diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm
index 087f13cc2..e655bb1b3 100644
--- a/lib/Slic3r/GUI.pm
+++ b/lib/Slic3r/GUI.pm
@@ -14,6 +14,7 @@ use Slic3r::GUI::ConfigWizard;
 use Slic3r::GUI::Controller;
 use Slic3r::GUI::Controller::ManualControlDialog;
 use Slic3r::GUI::Controller::PrinterPanel;
+use Slic3r::GUI::GLShader;
 use Slic3r::GUI::MainFrame;
 use Slic3r::GUI::Notifier;
 use Slic3r::GUI::Plater;
diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm
index 374469a0e..374544dfe 100644
--- a/lib/Slic3r/GUI/3DScene.pm
+++ b/lib/Slic3r/GUI/3DScene.pm
@@ -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
@@ -82,7 +87,10 @@ 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 GIMBALL_LOCK_THETA_MAX => 150;
+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
@@ -130,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;
     
@@ -178,6 +191,22 @@ sub new {
     return $self;
 }
 
+sub Destroy {
+    my ($self) = @_;
+    $self->DestroyGL;
+    return $self->SUPER::Destroy;
+}
+
+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) = @_;
     
@@ -192,40 +221,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);
@@ -250,7 +304,22 @@ 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;
@@ -305,6 +374,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;
@@ -720,7 +790,22 @@ sub InitGL {
     return if $self->init;
     return unless $self->GetContext;
     $self->init(1);
-    
+
+    my $shader;
+    $shader = $self->{shader} = new Slic3r::GUI::GLShader
+        if (defined($ENV{'SLIC3R_EXPERIMENTAL'}) && $ENV{'SLIC3R_EXPERIMENTAL'} == 1);
+    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);
@@ -761,7 +846,14 @@ sub InitGL {
     glEnable(GL_COLOR_MATERIAL);
     glEnable(GL_MULTISAMPLE);
 }
- 
+
+sub DestroyGL {
+    my $self = shift;
+    if ($self->init && $self->GetContext) {
+        delete $self->{shader};
+    }
+}
+
 sub Render {
     my ($self, $dc) = @_;
     
@@ -798,11 +890,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();
@@ -829,6 +925,7 @@ sub Render {
         glFlush();
         glFinish();
         glEnable(GL_LIGHTING);
+        glEnable(GL_MULTISAMPLE);
     }
     
     # draw fixed background
@@ -928,7 +1025,7 @@ sub Render {
     
     # draw objects
     $self->draw_volumes;
-    
+
     # draw cutting plane
     if (defined $self->cutting_plane_z) {
         my $plane_z = $self->cutting_plane_z;
@@ -947,6 +1044,8 @@ sub Render {
         glEnable(GL_CULL_FACE);
         glDisable(GL_BLEND);
     }
+
+    $self->draw_active_object_annotations;
     
     glFlush();
  
@@ -963,10 +1062,58 @@ sub draw_volumes {
     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;
@@ -1017,16 +1164,39 @@ sub draw_volumes {
         if ($qverts_begin < $qverts_end) {
             glVertexPointer_c(3, GL_FLOAT, 0, $volume->qverts->verts_ptr);
             glNormalPointer_c(GL_FLOAT, 0, $volume->qverts->norms_ptr);
-            glDrawArrays(GL_QUADS, $qverts_begin / 3, ($qverts_end-$qverts_begin) / 3);
+            $qverts_begin /= 3;
+            $qverts_end /= 3;
+            my $nvertices = $qverts_end-$qverts_begin;
+            while ($nvertices > 0) {
+                my $nvertices_this = ($nvertices > 4096) ? 4096 : $nvertices;
+                glDrawArrays(GL_QUADS, $qverts_begin, $nvertices_this);
+                $qverts_begin += $nvertices_this;
+                $nvertices -= $nvertices_this;
+            }
         }
         
         if ($tverts_begin < $tverts_end) {
             glVertexPointer_c(3, GL_FLOAT, 0, $volume->tverts->verts_ptr);
             glNormalPointer_c(GL_FLOAT, 0, $volume->tverts->norms_ptr);
-            glDrawArrays(GL_TRIANGLES, $tverts_begin / 3, ($tverts_end-$tverts_begin) / 3);
+            $tverts_begin /= 3;
+            $tverts_end /= 3;
+            my $nvertices = $tverts_end-$tverts_begin;
+            while ($nvertices > 0) {
+                my $nvertices_this = ($nvertices > 4095) ? 4095 : $nvertices;
+                glDrawArrays(GL_TRIANGLES, $tverts_begin, $nvertices_this);
+                $tverts_begin += $nvertices_this;
+                $nvertices -= $nvertices_this;
+            }
         }
-        
+
+        glVertexPointer_c(3, 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);
@@ -1036,10 +1206,98 @@ sub draw_volumes {
         glColor3f(0, 0, 0);
         glVertexPointer_p(3, $self->cut_lines_vertices);
         glDrawArrays(GL_LINES, 0, $self->cut_lines_vertices->elements / 3);
+        glVertexPointer_c(3, GL_FLOAT, 0, 0);
     }
     glDisableClientState(GL_VERTEX_ARRAY);
 }
 
+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
 {
     my ($self, $comment) = @_;
@@ -1069,6 +1327,112 @@ sub _report_opengl_state
     }
 }
 
+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 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 = 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);
+
+    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 = vec3(LIGHT_FRONT_DIR);
+    halfVector = normalize(lightDir + eye);
+    NdotL = max(dot(normal, lightDir), 0.0);
+    intensity_tainted += NdotL * LIGHT_FRONT_DIFFUSE;
+    
+    // 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
+}
+
+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 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.;
+}
+
+FRAGMENT
+}
+
 # Container for object geometry and selection status.
 package Slic3r::GUI::3DScene::Volume;
 use Moo;
@@ -1076,6 +1440,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.
@@ -1097,6 +1463,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) = @_;
     
@@ -1122,8 +1508,6 @@ __PACKAGE__->mk_accessors(qw(
     color_by
     select_by
     drag_by
-    volumes_by_object
-    _objects_by_volumes
 ));
 
 sub new {
@@ -1133,14 +1517,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')) {
@@ -1152,6 +1534,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}) {
@@ -1174,16 +1569,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);
@@ -1191,15 +1588,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;
 }
 
@@ -1538,19 +1938,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;
diff --git a/lib/Slic3r/GUI/GLShader.pm b/lib/Slic3r/GUI/GLShader.pm
new file mode 100644
index 000000000..766fe0807
--- /dev/null
+++ b/lib/Slic3r/GUI/GLShader.pm
@@ -0,0 +1,184 @@
+############################################################
+#
+# Stripped down from the Perl OpenGL::Shader package by Vojtech Bubnik
+# to only support the GLSL shaders. The original source was not maintained
+# and did not install properly through the CPAN archive, and it was unnecessary
+# complex.
+#
+# Original copyright:
+#
+# Copyright 2007 Graphcomp - ALL RIGHTS RESERVED
+# Author: Bob "grafman" Free - grafman@graphcomp.com
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+############################################################
+
+package Slic3r::GUI::GLShader;
+use OpenGL(':all');
+
+# Avoid cloning this class by the worker threads.
+sub CLONE_SKIP { 1 }
+
+# Shader constructor
+sub new
+{
+  # Check for required OpenGL extensions
+  return undef if (OpenGL::glpCheckExtension('GL_ARB_shader_objects'));
+  return undef if (OpenGL::glpCheckExtension('GL_ARB_fragment_shader'));
+  return undef if (OpenGL::glpCheckExtension('GL_ARB_vertex_shader'));
+  return undef if (OpenGL::glpCheckExtension('GL_ARB_shading_language_100'));
+#  my $glsl_version = glGetString(GL_SHADING_LANGUAGE_VERSION);
+#  my $glsl_version_ARB = glGetString(GL_SHADING_LANGUAGE_VERSION_ARB );
+#  print "GLSL version: $glsl_version, ARB: $glsl_version_ARB\n";
+
+  my $this = shift;
+  my $class = ref($this) || $this;
+  my($type) = @_;
+  my $self = {type => uc($type)};
+  bless($self,$class);
+
+  # Get GL_SHADING_LANGUAGE_VERSION_ARB
+  my $shader_ver = glGetString(0x8B8C);
+  $shader_ver =~ m|([\d\.]+)|;
+  $self->{version} = $1 || '0';
+  print 
+  return $self;
+}
+
+# Shader destructor
+# Must be disabled first
+sub DESTROY
+{
+  my($self) = @_;
+
+  if ($self->{program})
+  {
+    glDetachObjectARB($self->{program},$self->{fragment_id}) if ($self->{fragment_id});
+    glDetachObjectARB($self->{program},$self->{vertex_id}) if ($self->{vertex_id});
+    glDeleteProgramsARB_p($self->{program});
+  }
+
+  glDeleteProgramsARB_p($self->{fragment_id}) if ($self->{fragment_id});
+  glDeleteProgramsARB_p($self->{vertex_id}) if ($self->{vertex_id});
+}
+
+
+# Load shader strings
+sub Load
+{
+  my($self,$fragment,$vertex) = @_;
+
+  # Load fragment code
+  if ($fragment)
+  {
+    $self->{fragment_id} = glCreateShaderObjectARB(GL_FRAGMENT_SHADER);
+    return undef if (!$self->{fragment_id});
+
+    glShaderSourceARB_p($self->{fragment_id}, $fragment);
+    #my $frag = glGetShaderSourceARB_p($self->{fragment_id});
+    #print STDERR "Loaded fragment:\n$frag\n";
+
+    glCompileShaderARB($self->{fragment_id});
+    my $stat = glGetInfoLogARB_p($self->{fragment_id});
+    return "Fragment shader: $stat" if ($stat);
+  }
+
+  # Load vertex code
+  if ($vertex)
+  {
+    $self->{vertex_id} = glCreateShaderObjectARB(GL_VERTEX_SHADER);
+    return undef if (!$self->{vertex_id});
+
+    glShaderSourceARB_p($self->{vertex_id}, $vertex);
+    #my $vert = glGetShaderSourceARB_p($self->{vertex_id});
+    #print STDERR "Loaded vertex:\n$vert\n";
+
+    glCompileShaderARB($self->{vertex_id});
+    $stat = glGetInfoLogARB_p($self->{vertex_id});
+    return "Vertex shader: $stat" if ($stat);
+  }
+
+
+  # Link shaders
+  my $sp = glCreateProgramObjectARB();
+  glAttachObjectARB($sp, $self->{fragment_id}) if ($fragment);
+  glAttachObjectARB($sp, $self->{vertex_id}) if ($vertex);
+  glLinkProgramARB($sp);
+  my $linked = glGetObjectParameterivARB_p($sp, GL_OBJECT_LINK_STATUS_ARB);
+  if (!$linked)
+  {
+    $stat = glGetInfoLogARB_p($sp);
+    #print STDERR "Load shader: $stat\n";
+    return "Link shader: $stat" if ($stat);
+    return 'Unable to link shader';
+  }
+
+  $self->{program} = $sp;
+
+  return '';
+}
+
+# Enable shader
+sub Enable
+{
+  my($self) = @_;
+  glUseProgramObjectARB($self->{program}) if ($self->{program});
+}
+
+# Disable shader
+sub Disable
+{
+  my($self) = @_;
+  glUseProgramObjectARB(0) if ($self->{program});
+}
+
+# Return shader vertex attribute ID
+sub MapAttr
+{
+  my($self,$attr) = @_;
+  return undef if (!$self->{program});
+  my $id = glGetAttribLocationARB_p($self->{program},$attr);
+  return undef if ($id < 0);
+  return $id;
+}
+
+# Return shader uniform variable ID
+sub Map
+{
+  my($self,$var) = @_;
+  return undef if (!$self->{program});
+  my $id = glGetUniformLocationARB_p($self->{program},$var);
+  return undef if ($id < 0);
+  return $id;
+}
+
+# Set shader vector
+sub SetVector
+{
+  my($self,$var,@values) = @_;
+
+  my $id = $self->Map($var);
+  return 'Unable to map $var' if (!defined($id));
+
+  my $count = scalar(@values);
+  eval('glUniform'.$count.'fARB($id,@values)');
+
+  return '';
+}
+
+# Set shader 4x4 matrix
+sub SetMatrix
+{
+  my($self,$var,$oga) = @_;
+
+  my $id = $self->Map($var);
+  return 'Unable to map $var' if (!defined($id));
+
+  glUniformMatrix4fvARB_c($id,1,0,$oga->ptr());
+  return '';
+}
+
+1;
+__END__
diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm
index ca7143e28..f08adf672 100644
--- a/lib/Slic3r/GUI/MainFrame.pm
+++ b/lib/Slic3r/GUI/MainFrame.pm
@@ -42,6 +42,11 @@ sub new {
     $self->_init_tabpanel;
     $self->_init_menubar;
     
+    # set default tooltip timer in msec
+    # SetAutoPop supposedly accepts long integers but some bug doesn't allow for larger values
+    # (SetAutoPop is not available on GTK.)
+    eval { Wx::ToolTip::SetAutoPop(32767) };
+    
     # initialize status bar
     $self->{statusbar} = Slic3r::GUI::ProgressStatusBar->new($self, -1);
     $self->{statusbar}->SetStatusText("Version $Slic3r::VERSION - Remember to check for updates at http://github.com/prusa3d/slic3r/releases");
@@ -292,7 +297,7 @@ sub _init_menubar {
     # View menu
     if (!$self->{no_plater}) {
         $self->{viewMenu} = Wx::Menu->new;
-        $self->_append_menu_item($self->{viewMenu}, "Default", 'Default View', sub { $self->select_view('default'); });
+        $self->_append_menu_item($self->{viewMenu}, "Iso"    , 'Iso View'    , sub { $self->select_view('iso'    ); });
         $self->_append_menu_item($self->{viewMenu}, "Top"    , 'Top View'    , sub { $self->select_view('top'    ); });
         $self->_append_menu_item($self->{viewMenu}, "Bottom" , 'Bottom View' , sub { $self->select_view('bottom' ); });
         $self->_append_menu_item($self->{viewMenu}, "Front"  , 'Front View'  , sub { $self->select_view('front'  ); });
diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm
index 54f66e221..ead1fc4fe 100644
--- a/lib/Slic3r/GUI/Plater.pm
+++ b/lib/Slic3r/GUI/Plater.pm
@@ -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 
+    :panel :sizer :toolbar :window wxTheApp :notebook :combobox wxNullBitmap);
+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,10 @@ 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}->AddTool(TB_LAYER_EDITING, 'Layer Editing', Wx::Bitmap->new($Slic3r::var->("delete.png"), wxBITMAP_TYPE_PNG), wxNullBitmap, 1, undef, 'Layer Editing')
+            if (defined($ENV{'SLIC3R_EXPERIMENTAL'}) && $ENV{'SLIC3R_EXPERIMENTAL'} == 1);
     } else {
         my %tbar_buttons = (
             add             => "Add…",
@@ -168,12 +174,17 @@ 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_$_"});
         }
+        if (defined($ENV{'SLIC3R_EXPERIMENTAL'}) && $ENV{'SLIC3R_EXPERIMENTAL'} == 1) {
+            $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 +267,11 @@ 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 {
+            my $state = $self->{canvas3D}->layer_editing_enabled;
+            $self->{htoolbar}->ToggleTool(TB_LAYER_EDITING, ! $state);
+            $self->on_layer_editing_toggled(! $state);
+        });
     } 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 +285,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 +481,12 @@ sub _on_select_preset {
 	$self->on_config_change($self->GetFrame->config);
 }
 
+sub on_layer_editing_toggled {
+    my ($self, $new_state) = @_;
+    $self->{canvas3D}->layer_editing_enabled($new_state);
+    $self->{canvas3D}->update;
+}
+
 sub GetFrame {
     my ($self) = @_;
     return &Wx::GetTopLevelParent($self);
@@ -1481,6 +1504,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 +1704,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 +1737,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);
diff --git a/lib/Slic3r/GUI/Plater/2D.pm b/lib/Slic3r/GUI/Plater/2D.pm
index 8fb8908e1..a24a5a6ff 100644
--- a/lib/Slic3r/GUI/Plater/2D.pm
+++ b/lib/Slic3r/GUI/Plater/2D.pm
@@ -173,7 +173,7 @@ sub repaint {
             
             # if sequential printing is enabled and we have more than one object, draw clearance area
             if ($self->{config}->complete_objects && (map @{$_->instances}, @{$self->{model}->objects}) > 1) {
-                my ($clearance) = @{offset([$thumbnail->convex_hull], (scale($self->{config}->extruder_clearance_radius) / 2), 1, JT_ROUND, scale(0.1))};
+                my ($clearance) = @{offset([$thumbnail->convex_hull], (scale($self->{config}->extruder_clearance_radius) / 2), JT_ROUND, scale(0.1))};
                 $dc->SetPen($self->{clearance_pen});
                 $dc->SetBrush($self->{transparent_brush});
                 $dc->DrawPolygon($self->scaled_points_to_pixel($clearance, 1), 0, 0);
@@ -185,7 +185,7 @@ sub repaint {
     if (@{$self->{objects}} && $self->{config}->skirts) {
         my @points = map @{$_->contour}, map @$_, map @{$_->instance_thumbnails}, @{$self->{objects}};
         if (@points >= 3) {
-            my ($convex_hull) = @{offset([convex_hull(\@points)], scale max($self->{config}->brim_width + $self->{config}->skirt_distance), 1, JT_ROUND, scale(0.1))};
+            my ($convex_hull) = @{offset([convex_hull(\@points)], scale max($self->{config}->brim_width + $self->{config}->skirt_distance), JT_ROUND, scale(0.1))};
             $dc->SetPen($self->{skirt_pen});
             $dc->SetBrush($self->{transparent_brush});
             $dc->DrawPolygon($self->scaled_points_to_pixel($convex_hull, 1), 0, 0);
diff --git a/lib/Slic3r/GUI/Plater/3D.pm b/lib/Slic3r/GUI/Plater/3D.pm
index 07818a2b5..ce56c67b4 100644
--- a/lib/Slic3r/GUI/Plater/3D.pm
+++ b/lib/Slic3r/GUI/Plater/3D.pm
@@ -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;
diff --git a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm
index 9e4b33976..7889b3d66 100644
--- a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm
+++ b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm
@@ -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],
diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm
index 4882233a6..51a58366c 100644
--- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm
+++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm
@@ -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;
diff --git a/lib/Slic3r/Geometry/Clipper.pm b/lib/Slic3r/Geometry/Clipper.pm
index 652abb8db..37fc89914 100644
--- a/lib/Slic3r/Geometry/Clipper.pm
+++ b/lib/Slic3r/Geometry/Clipper.pm
@@ -5,9 +5,9 @@ use warnings;
 require Exporter;
 our @ISA = qw(Exporter);
 our @EXPORT_OK = qw(offset offset_ex
-    diff_ex diff union_ex intersection_ex xor_ex JT_ROUND JT_MITER
-    JT_SQUARE is_counter_clockwise union_pt offset2 offset2_ex
-    intersection intersection_pl diff_pl union CLIPPER_OFFSET_SCALE
-    union_pt_chained diff_ppl intersection_ppl);
+    diff_ex diff union_ex intersection_ex JT_ROUND JT_MITER
+    JT_SQUARE is_counter_clockwise offset2 offset2_ex
+    intersection intersection_pl diff_pl union
+    union_pt_chained);
 
 1;
diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm
index 187ee84e5..47a58c3a0 100644
--- a/lib/Slic3r/Print.pm
+++ b/lib/Slic3r/Print.pm
@@ -41,8 +41,12 @@ sub size {
 sub process {
     my ($self) = @_;
     
+    $self->status_cb->(20, "Generating perimeters");
     $_->make_perimeters for @{$self->objects};
+    
+    $self->status_cb->(70, "Infilling layers");
     $_->infill for @{$self->objects};
+    
     $_->generate_support_material for @{$self->objects};
     $self->make_skirt;
     $self->make_brim;  # must come after make_skirt
@@ -276,7 +280,7 @@ sub make_skirt {
     my $distance = scale max($self->config->skirt_distance, $self->config->brim_width);
     for (my $i = $skirts; $i > 0; $i--) {
         $distance += scale $spacing;
-        my $loop = offset([$convex_hull], $distance, 1, JT_ROUND, scale(0.1))->[0];
+        my $loop = offset([$convex_hull], $distance, JT_ROUND, scale(0.1))->[0];
         my $eloop = Slic3r::ExtrusionLoop->new_from_paths(
             Slic3r::ExtrusionPath->new(
                 polyline        => Slic3r::Polygon->new(@$loop)->split_at_first_point,
@@ -369,7 +373,7 @@ sub make_brim {
         # -0.5 because islands are not represented by their centerlines
         # (first offset more, then step back - reverse order than the one used for 
         # perimeters because here we're offsetting outwards)
-        push @loops, @{offset2(\@islands, ($i + 0.5) * $flow->scaled_spacing, -1.0 * $flow->scaled_spacing, 100000, JT_SQUARE)};
+        push @loops, @{offset2(\@islands, ($i + 0.5) * $flow->scaled_spacing, -1.0 * $flow->scaled_spacing, JT_SQUARE)};
     }
     
     $self->brim->append(map Slic3r::ExtrusionLoop->new_from_paths(
diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm
index 8fd2bf4cf..720294cd0 100644
--- a/lib/Slic3r/Print/Object.pm
+++ b/lib/Slic3r/Print/Object.pm
@@ -7,7 +7,7 @@ use List::Util qw(min max sum first);
 use Slic3r::Flow ':roles';
 use Slic3r::Geometry qw(X Y Z PI scale unscale chained_path epsilon);
 use Slic3r::Geometry::Clipper qw(diff diff_ex intersection intersection_ex union union_ex 
-    offset offset_ex offset2 offset2_ex intersection_ppl CLIPPER_OFFSET_SCALE JT_MITER);
+    offset offset_ex offset2 offset2_ex JT_MITER);
 use Slic3r::Print::State ':steps';
 use Slic3r::Surface ':types';
 
@@ -45,223 +45,7 @@ sub slice {
     $self->set_step_started(STEP_SLICE);
     $self->print->status_cb->(10, "Processing triangulated mesh");
     
-    # init layers
-    {
-        $self->clear_layers;
-    
-        # make layers taking custom heights into account
-        my $id      = 0;
-        my $print_z = 0;
-        my $first_object_layer_height   = -1;
-        my $first_object_layer_distance = -1;
-    
-        # add raft layers
-        if ($self->config->raft_layers > 0) {
-            # Reserve object layers for the raft. Last layer of the raft is the contact layer.
-            $id += $self->config->raft_layers;
-        
-            # Raise first object layer Z by the thickness of the raft itself
-            # plus the extra distance required by the support material logic.
-            #FIXME The last raft layer is the contact layer, which shall be printed with a bridging flow for ease of separation. Currently it is not the case.
-            my $first_layer_height = $self->config->get_value('first_layer_height');
-            $print_z += $first_layer_height;
-            
-            # Use as large as possible layer height for the intermediate raft layers.
-            my $support_material_layer_height;
-            {
-                my @nozzle_diameters = (
-                    map $self->print->config->get_at('nozzle_diameter', $_),
-                        $self->config->support_material_extruder-1,
-                        $self->config->support_material_interface_extruder-1,
-                );
-                $support_material_layer_height = 0.75 * min(@nozzle_diameters);
-            }
-            $print_z += $support_material_layer_height * ($self->config->raft_layers - 1);
-        
-            # compute the average of all nozzles used for printing the object
-            #FIXME It is expected, that the 1st layer of the object is printed with a bridging flow over a full raft. Shall it not be vice versa?
-            my $nozzle_diameter;
-            {
-                my @nozzle_diameters = (
-                    map $self->print->config->get_at('nozzle_diameter', $_), @{$self->print->object_extruders}
-                );
-                $nozzle_diameter = sum(@nozzle_diameters)/@nozzle_diameters;
-            }
-            $first_object_layer_distance = $self->_support_material->contact_distance($self->config->layer_height, $nozzle_diameter);
-        
-            # force first layer print_z according to the contact distance
-            # (the loop below will raise print_z by such height)
-            $first_object_layer_height = $first_object_layer_distance - $self->config->support_material_contact_distance;
-        }
-    
-        # loop until we have at least one layer and the max slice_z reaches the object height
-        my $slice_z = 0;
-        my $height  = 0;
-        my $max_z   = unscale($self->size->z);
-        while (($slice_z - $height) <= $max_z) {
-            # assign the default height to the layer according to the general settings
-            $height = ($id == 0)
-                ? $self->config->get_value('first_layer_height')
-                : $self->config->layer_height;
-        
-            # look for an applicable custom range
-            if (my $range = first { $_->[0] <= $slice_z && $_->[1] > $slice_z } @{$self->layer_height_ranges}) {
-                $height = $range->[2];
-        
-                # if user set custom height to zero we should just skip the range and resume slicing over it
-                if ($height == 0) {
-                    $slice_z += $range->[1] - $range->[0];
-                    next;
-                }
-            }
-            
-            if ($first_object_layer_height != -1 && !@{$self->layers}) {
-                $height = $first_object_layer_height;
-                $print_z += ($first_object_layer_distance - $height);
-            }
-            
-            $print_z += $height;
-            $slice_z += $height/2;
-        
-            ### Slic3r::debugf "Layer %d: height = %s; slice_z = %s; print_z = %s\n", $id, $height, $slice_z, $print_z;
-        
-            $self->add_layer($id, $height, $print_z, $slice_z);
-            if ($self->layer_count >= 2) {
-                my $lc = $self->layer_count;
-                $self->get_layer($lc - 2)->set_upper_layer($self->get_layer($lc - 1));
-                $self->get_layer($lc - 1)->set_lower_layer($self->get_layer($lc - 2));
-            }
-            $id++;
-        
-            $slice_z += $height/2;   # add the other half layer
-        }
-    }
-    
-    # make sure all layers contain layer region objects for all regions
-    my $regions_count = $self->print->region_count;
-    foreach my $layer (@{ $self->layers }) {
-        $layer->region($_) for 0 .. ($regions_count-1);
-    }
-    
-    # get array of Z coordinates for slicing
-    my @z = map $_->slice_z, @{$self->layers};
-    
-    # slice all non-modifier volumes
-    for my $region_id (0..($self->region_count - 1)) {
-        my $expolygons_by_layer = $self->_slice_region($region_id, \@z, 0);
-        for my $layer_id (0..$#$expolygons_by_layer) {
-            my $layerm = $self->get_layer($layer_id)->regions->[$region_id];
-            $layerm->slices->clear;
-            foreach my $expolygon (@{ $expolygons_by_layer->[$layer_id] }) {
-                $layerm->slices->append(Slic3r::Surface->new(
-                    expolygon    => $expolygon,
-                    surface_type => S_TYPE_INTERNAL,
-                ));
-            }
-        }
-    }
-    
-    # then slice all modifier volumes
-    if ($self->region_count > 1) {
-        for my $region_id (0..$self->region_count) {
-            my $expolygons_by_layer = $self->_slice_region($region_id, \@z, 1);
-            
-            # loop through the other regions and 'steal' the slices belonging to this one
-            for my $other_region_id (0..$self->region_count) {
-                next if $other_region_id == $region_id;
-                
-                for my $layer_id (0..$#$expolygons_by_layer) {
-                    my $layerm = $self->get_layer($layer_id)->regions->[$region_id];
-                    my $other_layerm = $self->get_layer($layer_id)->regions->[$other_region_id];
-                    next if !defined $other_layerm;
-                    
-                    my $other_slices = [ map $_->p, @{$other_layerm->slices} ];  # Polygons
-                    my $my_parts = intersection_ex(
-                        $other_slices,
-                        [ map @$_, @{ $expolygons_by_layer->[$layer_id] } ],
-                    );
-                    next if !@$my_parts;
-                    
-                    # append new parts to our region
-                    foreach my $expolygon (@$my_parts) {
-                        $layerm->slices->append(Slic3r::Surface->new(
-                            expolygon    => $expolygon,
-                            surface_type => S_TYPE_INTERNAL,
-                        ));
-                    }
-                    
-                    # remove such parts from original region
-                    $other_layerm->slices->clear;
-                    $other_layerm->slices->append(Slic3r::Surface->new(
-                        expolygon    => $_,
-                        surface_type => S_TYPE_INTERNAL,
-                    )) for @{ diff_ex($other_slices, [ map @$_, @$my_parts ]) };
-                }
-            }
-        }
-    }
-    
-    # remove last layer(s) if empty
-    $self->delete_layer($self->layer_count - 1)
-        while $self->layer_count && (!map @{$_->slices}, @{$self->get_layer($self->layer_count - 1)->regions});
-    
-    foreach my $layer (@{ $self->layers }) {
-        # apply size compensation
-        if ($self->config->xy_size_compensation != 0) {
-            my $delta = scale($self->config->xy_size_compensation);
-            if (@{$layer->regions} == 1) {
-                # single region
-                my $layerm = $layer->regions->[0];
-                my $slices = [ map $_->p, @{$layerm->slices} ];
-                $layerm->slices->clear;
-                $layerm->slices->append(Slic3r::Surface->new(
-                    expolygon    => $_,
-                    surface_type => S_TYPE_INTERNAL,
-                )) for @{offset_ex($slices, $delta)};
-            } else {
-                if ($delta < 0) {
-                    # multiple regions, shrinking
-                    # we apply the offset to the combined shape, then intersect it
-                    # with the original slices for each region
-                    my $slices = union([ map $_->p, map @{$_->slices}, @{$layer->regions} ]);
-                    $slices = offset($slices, $delta);
-                    foreach my $layerm (@{$layer->regions}) {
-                        my $this_slices = intersection_ex(
-                            $slices,
-                            [ map $_->p, @{$layerm->slices} ],
-                        );
-                        $layerm->slices->clear;
-                        $layerm->slices->append(Slic3r::Surface->new(
-                            expolygon    => $_,
-                            surface_type => S_TYPE_INTERNAL,
-                        )) for @$this_slices;
-                    }
-                } else {
-                    # multiple regions, growing
-                    # this is an ambiguous case, since it's not clear how to grow regions where they are going to overlap
-                    # so we give priority to the first one and so on
-                    for my $i (0..$#{$layer->regions}) {
-                        my $layerm = $layer->regions->[$i];
-                        my $slices = offset_ex([ map $_->p, @{$layerm->slices} ], $delta);
-                        if ($i > 0) {
-                            $slices = diff_ex(
-                                [ map @$_, @$slices ],
-                                [ map $_->p, map @{$_->slices}, map $layer->regions->[$_], 0..($i-1) ],  # slices of already processed regions
-                            );
-                        }
-                        $layerm->slices->clear;
-                        $layerm->slices->append(Slic3r::Surface->new(
-                            expolygon    => $_,
-                            surface_type => S_TYPE_INTERNAL,
-                        )) for @$slices;
-                    }
-                }
-            }
-        }
-        
-        # Merge all regions' slices to get islands, chain them by a shortest path.
-        $layer->make_slices;
-    }
+    $self->_slice;
     
     # detect slicing errors
     my $warning_thrown = 0;
@@ -334,151 +118,15 @@ sub slice {
     $self->set_step_done(STEP_SLICE);
 }
 
-# called from slice()
-sub _slice_region {
-    my ($self, $region_id, $z, $modifier) = @_;
-
-    return [] if !@{$self->get_region_volumes($region_id)};
-
-    # compose mesh
-    my $mesh;
-    foreach my $volume_id (@{ $self->get_region_volumes($region_id) }) {
-        my $volume = $self->model_object->volumes->[$volume_id];
-        next if $volume->modifier && !$modifier;
-        next if !$volume->modifier && $modifier;
-        
-        if (defined $mesh) {
-            $mesh->merge($volume->mesh);
-        } else {
-            $mesh = $volume->mesh->clone;
-        }
-    }
-    return if !defined $mesh;
-
-    # transform mesh
-    # we ignore the per-instance transformations currently and only 
-    # consider the first one
-    $self->model_object->instances->[0]->transform_mesh($mesh, 1);
-
-    # align mesh to Z = 0 (it should be already aligned actually) and apply XY shift
-    $mesh->translate((map unscale(-$_), @{$self->_copies_shift}), -$self->model_object->bounding_box->z_min);
-    
-    # perform actual slicing
-    return $mesh->slice($z);
-}
-
 # 1) Merges typed region slices into stInternal type.
 # 2) Increases an "extra perimeters" counter at region slices where needed.
 # 3) Generates perimeters, gap fills and fill regions (fill regions of type stInternal).
 sub make_perimeters {
-    my $self = shift;
+    my ($self) = @_;
     
     # prerequisites
     $self->slice;
-    
-    return if $self->step_done(STEP_PERIMETERS);
-    $self->set_step_started(STEP_PERIMETERS);
-    $self->print->status_cb->(20, "Generating perimeters");
-    
-    # Merge region slices if they were split into types.
-    # FIXME this is using a safety offset, so the region slices will be slightly bigger with each iteration.
-    if ($self->typed_slices) {
-        $_->merge_slices for @{$self->layers};
-        $self->set_typed_slices(0);
-        $self->invalidate_step(STEP_PREPARE_INFILL);
-    }
-    
-    # compare each layer to the one below, and mark those slices needing
-    # one additional inner perimeter, like the top of domed objects-
-    
-    # this algorithm makes sure that at least one perimeter is overlapping
-    # but we don't generate any extra perimeter if fill density is zero, as they would be floating
-    # inside the object - infill_only_where_needed should be the method of choice for printing
-    # hollow objects
-    for my $region_id (0 .. ($self->print->region_count-1)) {
-        my $region = $self->print->regions->[$region_id];
-        my $region_perimeters = $region->config->perimeters;
-        
-        next if !$region->config->extra_perimeters;
-        next if $region_perimeters == 0;
-        next if $region->config->fill_density == 0;
-        
-        for my $i (0 .. ($self->layer_count - 2)) {
-            my $layerm                  = $self->get_layer($i)->get_region($region_id);
-            my $upper_layerm            = $self->get_layer($i+1)->get_region($region_id);
-            my $upper_layerm_polygons   = [ map $_->p, @{$upper_layerm->slices} ];
-            # Filter upper layer polygons in intersection_ppl by their bounding boxes?
-            # my $upper_layerm_poly_bboxes= [ map $_->bounding_box, @{$upper_layerm_polygons} ];
-            my $total_loop_length       = sum(map $_->length, @$upper_layerm_polygons) // 0;
-            
-            my $perimeter_spacing       = $layerm->flow(FLOW_ROLE_PERIMETER)->scaled_spacing;
-            my $ext_perimeter_flow      = $layerm->flow(FLOW_ROLE_EXTERNAL_PERIMETER);
-            my $ext_perimeter_width     = $ext_perimeter_flow->scaled_width;
-            my $ext_perimeter_spacing   = $ext_perimeter_flow->scaled_spacing;
-            
-            foreach my $slice (@{$layerm->slices}) {
-                while (1) {
-                    # compute the total thickness of perimeters
-                    my $perimeters_thickness = $ext_perimeter_width/2 + $ext_perimeter_spacing/2
-                        + ($region_perimeters-1 + $slice->extra_perimeters) * $perimeter_spacing;
-                    
-                    # define a critical area where we don't want the upper slice to fall into
-                    # (it should either lay over our perimeters or outside this area)
-                    my $critical_area_depth = $perimeter_spacing*1.5;
-                    my $critical_area = diff(
-                        offset($slice->expolygon->arrayref, -$perimeters_thickness),
-                        offset($slice->expolygon->arrayref, -($perimeters_thickness + $critical_area_depth)),
-                    );
-                    
-                    # check whether a portion of the upper slices falls inside the critical area
-                    my $intersection = intersection_ppl(
-                        $upper_layerm_polygons,
-                        $critical_area,
-                    );
-                    
-                    # only add an additional loop if at least 30% of the slice loop would benefit from it
-                    my $total_intersection_length = sum(map $_->length, @$intersection) // 0;
-                    last unless $total_intersection_length > $total_loop_length*0.3;
-                    
-                    if (0) {
-                        require "Slic3r/SVG.pm";
-                        Slic3r::SVG::output(
-                            "extra.svg",
-                            no_arrows   => 1,
-                            expolygons  => union_ex($critical_area),
-                            polylines   => [ map $_->split_at_first_point, map $_->p, @{$upper_layerm->slices} ],
-                        );
-                    }
-                    
-                    $slice->extra_perimeters($slice->extra_perimeters + 1);
-                }
-                Slic3r::debugf "  adding %d more perimeter(s) at layer %d\n",
-                    $slice->extra_perimeters, $layerm->layer->id
-                    if $slice->extra_perimeters > 0;
-            }
-        }
-    }
-    
-    Slic3r::parallelize(
-        threads => $self->print->config->threads,
-        items => sub { 0 .. ($self->layer_count - 1) },
-        thread_cb => sub {
-            my $q = shift;
-            while (defined (my $i = $q->dequeue)) {
-                $self->get_layer($i)->make_perimeters;
-            }
-        },
-        no_threads_cb => sub {
-            $_->make_perimeters for @{$self->layers};
-        },
-    );
-    
-    # simplify slices (both layer and region slices),
-    # we only need the max resolution for perimeters
-    ### This makes this method not-idempotent, so we keep it disabled for now.
-    ###$self->_simplify_slices(&Slic3r::SCALED_RESOLUTION);
-    
-    $self->set_step_done(STEP_PERIMETERS);
+    $self->_make_perimeters;
 }
 
 sub prepare_infill {
@@ -598,32 +246,7 @@ sub infill {
     
     # prerequisites
     $self->prepare_infill;
-    
-    return if $self->step_done(STEP_INFILL);
-    $self->set_step_started(STEP_INFILL);
-    $self->print->status_cb->(70, "Infilling layers");
-    
-    Slic3r::parallelize(
-        threads => $self->print->config->threads,
-        items => sub { 0..$#{$self->layers} },
-        thread_cb => sub {
-            my $q = shift;
-            while (defined (my $i = $q->dequeue)) {
-                $self->get_layer($i)->make_fills;
-            }
-        },
-        no_threads_cb => sub {
-            foreach my $layer (@{$self->layers}) {
-                $layer->make_fills;
-            }
-        },
-    );
-
-    ### we could free memory now, but this would make this step not idempotent
-    ### Vojtech: Cannot release the fill_surfaces, they are used by the support generator.
-    ### $_->fill_surfaces->clear for map @{$_->regions}, @{$object->layers};
-    
-    $self->set_step_done(STEP_INFILL);
+    $self->_infill;
 }
 
 sub generate_support_material {
@@ -863,7 +486,7 @@ sub discover_horizontal_shells {
                         # and it's not wanted in a hollow print even if it would make sense when
                         # obeying the solid shell count option strictly (DWIM!)
                         my $margin = $neighbor_layerm->flow(FLOW_ROLE_EXTERNAL_PERIMETER)->scaled_width;
-                        my $regularized = offset2($new_internal_solid, -$margin, +$margin, CLIPPER_OFFSET_SCALE, JT_MITER, 5);
+                        my $regularized = offset2($new_internal_solid, -$margin, +$margin, JT_MITER, 5);
                         my $too_narrow = diff(
                             $new_internal_solid,
                             $regularized,
@@ -893,7 +516,7 @@ sub discover_horizontal_shells {
                         # have the same angle, so the next shell would be grown even more and so on.
                         my $too_narrow = diff(
                             $new_internal_solid,
-                            offset2($new_internal_solid, -$margin, +$margin, CLIPPER_OFFSET_SCALE, JT_MITER, 5),
+                            offset2($new_internal_solid, -$margin, +$margin, JT_MITER, 5),
                             1,
                         );
                         
diff --git a/lib/Slic3r/Print/SupportMaterial.pm b/lib/Slic3r/Print/SupportMaterial.pm
index 9dbfe4003..b4caffc7d 100644
--- a/lib/Slic3r/Print/SupportMaterial.pm
+++ b/lib/Slic3r/Print/SupportMaterial.pm
@@ -8,7 +8,7 @@ use Slic3r::ExtrusionPath ':roles';
 use Slic3r::Flow ':roles';
 use Slic3r::Geometry qw(epsilon scale scaled_epsilon PI rad2deg deg2rad convex_hull);
 use Slic3r::Geometry::Clipper qw(offset diff union union_ex intersection offset_ex offset2
-    intersection_pl offset2_ex diff_pl CLIPPER_OFFSET_SCALE JT_MITER JT_ROUND);
+    intersection_pl offset2_ex diff_pl JT_MITER JT_ROUND);
 use Slic3r::Surface ':types';
 
 has 'print_config'      => (is => 'rw', required => 1);
@@ -299,9 +299,8 @@ sub contact_area {
                             offset(
                                 $diff, 
                                 $_,
-                                CLIPPER_OFFSET_SCALE,
                                 JT_ROUND,
-                                scale(0.05)*CLIPPER_OFFSET_SCALE),
+                                scale(0.05)),
                             $slices_margin
                         );
                     }
@@ -584,9 +583,8 @@ sub generate_base_layers {
                         $fillet_radius_scaled, 
                         -$fillet_radius_scaled,
                         # Use a geometric offsetting for filleting.
-                        CLIPPER_OFFSET_SCALE,
                         JT_ROUND,
-                        0.2*$fillet_radius_scaled*CLIPPER_OFFSET_SCALE),
+                        0.2*$fillet_radius_scaled),
                     $trim_polygons,
                     0); # don't apply the safety offset.
             }
diff --git a/xs/Build.PL b/xs/Build.PL
index 0a700c37a..51d1a014f 100644
--- a/xs/Build.PL
+++ b/xs/Build.PL
@@ -119,15 +119,19 @@ if (defined $ENV{BOOST_LIBRARYDIR}) {
 my $have_boost = 0;
 my @boost_libraries = qw(system thread log);  # we need these
 
-# check without explicit lib path (works on Linux)
-if (! $mswin) {
-    $have_boost = 1
-        if check_lib(
-            lib     => [ map "boost_${_}", @boost_libraries ],
-        );
+if (!$ENV{SLIC3R_STATIC}) {
+    # Dynamic linking of boost libraries.
+    push @cflags, qw(-DBOOST_LOG_DYN_LINK);
+    if (! $mswin) {
+        # Check without explicit lib path (works on Linux and OSX).
+        $have_boost = 1
+            if check_lib(
+                lib     => [ map "boost_${_}", @boost_libraries ],
+            );
+    }
 }
 
-if (!$ENV{SLIC3R_STATIC} && $have_boost) {
+if ($have_boost) {
     # The boost library was detected by check_lib on Linux.
     push @LIBS, map "-lboost_${_}", @boost_libraries;
 } else {
@@ -138,7 +142,6 @@ if (!$ENV{SLIC3R_STATIC} && $have_boost) {
         # Try to find the boost system library.
         my @files = glob "$path/${lib_prefix}system*$lib_ext";
         next if !@files;
-
         if ($files[0] =~ /\Q${lib_prefix}system\E([^.]*)\Q$lib_ext\E$/) {
             # Suffix contains the version number, the build type etc.
             my $suffix = $1;
@@ -212,6 +215,10 @@ if ($cpp_guess->is_gcc) {
     }
 }
 
+print "\n";
+print 'With @INC: ', join(', ', map "\"$_\"", @INC), "\n";
+print 'With @LIBS: ', join(', ', map "\"$_\"", @LIBS), "\n";
+
 my $build = Module::Build::WithXSpp->new(
     module_name     => 'Slic3r::XS',
     dist_abstract   => 'XS code for Slic3r',
diff --git a/xs/MANIFEST b/xs/MANIFEST
index 70d83afa7..8ea48192b 100644
--- a/xs/MANIFEST
+++ b/xs/MANIFEST
@@ -32,6 +32,8 @@ src/libslic3r/ExtrusionEntityCollection.cpp
 src/libslic3r/ExtrusionEntityCollection.hpp
 src/libslic3r/ExtrusionSimulator.cpp
 src/libslic3r/ExtrusionSimulator.hpp
+src/libslic3r/Fill/Fill.cpp
+src/libslic3r/Fill/Fill.hpp
 src/libslic3r/Fill/FillBase.cpp
 src/libslic3r/Fill/FillBase.hpp
 src/libslic3r/Fill/FillConcentric.cpp
@@ -54,6 +56,8 @@ src/libslic3r/GCodeSender.cpp
 src/libslic3r/GCodeSender.hpp
 src/libslic3r/GCodeWriter.cpp
 src/libslic3r/GCodeWriter.hpp
+src/libslic3r/GCode/Analyzer.cpp
+src/libslic3r/GCode/Analyzer.hpp
 src/libslic3r/GCode/PressureEqualizer.cpp
 src/libslic3r/GCode/PressureEqualizer.hpp
 src/libslic3r/Geometry.cpp
@@ -88,6 +92,10 @@ src/libslic3r/PrintConfig.cpp
 src/libslic3r/PrintConfig.hpp
 src/libslic3r/PrintObject.cpp
 src/libslic3r/PrintRegion.cpp
+src/libslic3r/Slicing.cpp
+src/libslic3r/Slicing.hpp
+src/libslic3r/SlicingAdaptive.cpp
+src/libslic3r/SlicingAdaptive.hpp
 src/libslic3r/SupportMaterial.cpp
 src/libslic3r/SupportMaterial.hpp
 src/libslic3r/Surface.cpp
@@ -99,6 +107,7 @@ src/libslic3r/SVG.hpp
 src/libslic3r/TriangleMesh.cpp
 src/libslic3r/TriangleMesh.hpp
 src/libslic3r/utils.cpp
+src/libslic3r/Utils.hpp
 src/perlglue.cpp
 src/poly2tri/common/shapes.cc
 src/poly2tri/common/shapes.h
diff --git a/xs/src/admesh/stl.h b/xs/src/admesh/stl.h
index d97760eb0..34e1907e7 100644
--- a/xs/src/admesh/stl.h
+++ b/xs/src/admesh/stl.h
@@ -26,7 +26,7 @@
 #include <stdio.h>
 #include <stdint.h>
 #include <stddef.h>
-#include <boost/predef/detail/endian_compat.h>
+#include <boost/detail/endian.hpp>
 
 #ifndef BOOST_LITTLE_ENDIAN
 #error "admesh works correctly on little endian machines only!"
diff --git a/xs/src/admesh/stlinit.c b/xs/src/admesh/stlinit.c
index de1760bd5..33293cd47 100644
--- a/xs/src/admesh/stlinit.c
+++ b/xs/src/admesh/stlinit.c
@@ -43,23 +43,8 @@ stl_open(stl_file *stl, char *file) {
 
 void
 stl_initialize(stl_file *stl) {
-  stl->error = 0;
-  stl->stats.degenerate_facets = 0;
-  stl->stats.edges_fixed  = 0;
-  stl->stats.facets_added = 0;
-  stl->stats.facets_removed = 0;
-  stl->stats.facets_reversed = 0;
-  stl->stats.normals_fixed = 0;
-  stl->stats.number_of_parts = 0;
-  stl->stats.original_num_facets = 0;
-  stl->stats.number_of_facets = 0;
-  stl->stats.facets_malloced = 0;
+  memset(stl, 0, sizeof(stl_file));
   stl->stats.volume = -1.0;
-
-  stl->neighbors_start = NULL;
-  stl->facet_start = NULL;
-  stl->v_indices = NULL;
-  stl->v_shared = NULL;
 }
 
 void
@@ -270,6 +255,7 @@ stl_read(stl_file *stl, int first_facet, int first) {
     rewind(stl->fp);
   }
 
+  char normal_buf[3][32];
   for(i = first_facet; i < stl->stats.number_of_facets; i++) {
     if(stl->stats.type == binary)
       /* Read a single facet from a binary .STL file */
@@ -287,17 +273,25 @@ stl_read(stl_file *stl, int first_facet, int first) {
       fscanf(stl->fp, "endsolid\n");
       fscanf(stl->fp, "solid%*[^\n]\n");  // name might contain spaces so %*s doesn't work and it also can be empty (just "solid")
       
-      if((fscanf(stl->fp, " facet normal %f %f %f\n", &facet.normal.x, &facet.normal.y, &facet.normal.z) + \
-          fscanf(stl->fp, " outer loop\n") + \
-          fscanf(stl->fp, " vertex %f %f %f\n", &facet.vertex[0].x, &facet.vertex[0].y,  &facet.vertex[0].z) + \
-          fscanf(stl->fp, " vertex %f %f %f\n", &facet.vertex[1].x, &facet.vertex[1].y,  &facet.vertex[1].z) + \
-          fscanf(stl->fp, " vertex %f %f %f\n", &facet.vertex[2].x, &facet.vertex[2].y,  &facet.vertex[2].z) + \
-          fscanf(stl->fp, " endloop\n") + \
+      if((fscanf(stl->fp, " facet normal %31s %31s %31s\n", normal_buf[0], normal_buf[1], normal_buf[2]) +
+          fscanf(stl->fp, " outer loop\n") +
+          fscanf(stl->fp, " vertex %f %f %f\n", &facet.vertex[0].x, &facet.vertex[0].y,  &facet.vertex[0].z) +
+          fscanf(stl->fp, " vertex %f %f %f\n", &facet.vertex[1].x, &facet.vertex[1].y,  &facet.vertex[1].z) +
+          fscanf(stl->fp, " vertex %f %f %f\n", &facet.vertex[2].x, &facet.vertex[2].y,  &facet.vertex[2].z) +
+          fscanf(stl->fp, " endloop\n") +
           fscanf(stl->fp, " endfacet\n")) != 12) {
         perror("Something is syntactically very wrong with this ASCII STL!");
         stl->error = 1;
         return;
       }
+      // The facet normal has been parsed as a single string as to workaround for not a numbers in the normal definition.
+	  if (sscanf(normal_buf[0], "%f", &facet.normal.x) != 1 ||
+		  sscanf(normal_buf[1], "%f", &facet.normal.y) != 1 ||
+		  sscanf(normal_buf[2], "%f", &facet.normal.z) != 1) {
+		  // Normal was mangled. Maybe denormals or "not a number" were stored?
+		  // Just reset the normal and silently ignore it.
+		  memset(&facet.normal, 0, sizeof(facet.normal));
+	  }
     }
 
 #if 0
diff --git a/xs/src/clipper.hpp b/xs/src/clipper.hpp
index c8009e64e..47de7b182 100644
--- a/xs/src/clipper.hpp
+++ b/xs/src/clipper.hpp
@@ -153,17 +153,35 @@ private:
     PolyNode* GetNextSiblingUp() const;
     void AddChild(PolyNode& child);
     friend class Clipper; //to access Index
-    friend class ClipperOffset; 
+    friend class ClipperOffset;
+    friend class PolyTree; //to implement the PolyTree::move operator
 };
 
 class PolyTree: public PolyNode
 { 
 public:
-    ~PolyTree(){Clear();};
+    PolyTree() {}
+    PolyTree(PolyTree &&src) { *this = std::move(src); }
+    virtual ~PolyTree(){Clear();};
+    PolyTree& operator=(PolyTree &&src) { 
+        AllNodes   = std::move(src.AllNodes);
+        Contour    = std::move(src.Contour);
+        Childs     = std::move(src.Childs);
+        Parent     = nullptr;
+        Index      = src.Index;
+        m_IsOpen   = src.m_IsOpen;
+        m_jointype = src.m_jointype;
+        m_endtype  = src.m_endtype;
+        for (size_t i = 0; i < Childs.size(); ++ i)
+          Childs[i]->Parent = this;
+        return *this; 
+    }
     PolyNode* GetFirst() const;
     void Clear();
     int Total() const;
 private:
+    PolyTree(const PolyTree &src) = delete;
+    PolyTree& operator=(const PolyTree &src) = delete;
     PolyNodes AllNodes;
     friend class Clipper; //to access AllNodes
 };
diff --git a/xs/src/libslic3r/BoundingBox.cpp b/xs/src/libslic3r/BoundingBox.cpp
index bffe1dbfb..75ece90bf 100644
--- a/xs/src/libslic3r/BoundingBox.cpp
+++ b/xs/src/libslic3r/BoundingBox.cpp
@@ -277,4 +277,26 @@ BoundingBoxBase<PointClass>::overlap(const BoundingBoxBase<PointClass> &other) c
 template bool BoundingBoxBase<Point>::overlap(const BoundingBoxBase<Point> &point) const;
 template bool BoundingBoxBase<Pointf>::overlap(const BoundingBoxBase<Pointf> &point) const;
 
+
+// Align a coordinate to a grid. The coordinate may be negative,
+// the aligned value will never be bigger than the original one.
+static inline coord_t _align_to_grid(const coord_t coord, const coord_t spacing) {
+    // Current C++ standard defines the result of integer division to be rounded to zero,
+    // for both positive and negative numbers. Here we want to round down for negative
+    // numbers as well.
+    coord_t aligned = (coord < 0) ?
+            ((coord - spacing + 1) / spacing) * spacing :
+            (coord / spacing) * spacing;
+    assert(aligned <= coord);
+    return aligned;
+}
+
+void BoundingBox::align_to_grid(const coord_t cell_size)
+{
+    if (this->defined) {
+        min.x = _align_to_grid(min.x, cell_size);
+        min.y = _align_to_grid(min.y, cell_size);
+    }
+}
+
 }
diff --git a/xs/src/libslic3r/BoundingBox.hpp b/xs/src/libslic3r/BoundingBox.hpp
index 5f676151c..232dada80 100644
--- a/xs/src/libslic3r/BoundingBox.hpp
+++ b/xs/src/libslic3r/BoundingBox.hpp
@@ -65,6 +65,9 @@ class BoundingBox : public BoundingBoxBase<Point>
     BoundingBox rotated(double angle, const Point &center) const;
     void rotate(double angle) { (*this) = this->rotated(angle); }
     void rotate(double angle, const Point &center) { (*this) = this->rotated(angle, center); }
+    // Align the min corner to a grid of cell_size x cell_size cells,
+    // to encompass the original bounding box.
+    void align_to_grid(const coord_t cell_size);
     
     BoundingBox() : BoundingBoxBase<Point>() {};
     BoundingBox(const Point &pmin, const Point &pmax) : BoundingBoxBase<Point>(pmin, pmax) {};
diff --git a/xs/src/libslic3r/BridgeDetector.cpp b/xs/src/libslic3r/BridgeDetector.cpp
index 7e54bcf69..fa27134dc 100644
--- a/xs/src/libslic3r/BridgeDetector.cpp
+++ b/xs/src/libslic3r/BridgeDetector.cpp
@@ -40,13 +40,13 @@ void BridgeDetector::initialize()
     this->angle = -1.;
 
     // Outset our bridge by an arbitrary amout; we'll use this outer margin for detecting anchors.
-    Polygons grown = offset(this->expolygons, float(this->spacing));
+    Polygons grown = offset(to_polygons(this->expolygons), float(this->spacing));
     
     // Detect possible anchoring edges of this bridging region.
     // Detect what edges lie on lower slices by turning bridge contour and holes
     // into polylines and then clipping them with each lower slice's contour.
     // Currently _edges are only used to set a candidate direction of the bridge (see bridge_direction_candidates()).
-    intersection(to_polylines(grown), this->lower_slices.contours(), &this->_edges);
+    this->_edges = intersection_pl(to_polylines(grown), this->lower_slices.contours());
     
     #ifdef SLIC3R_DEBUG
     printf("  bridge has " PRINTF_ZU " support(s)\n", this->_edges.size());
@@ -117,7 +117,7 @@ BridgeDetector::detect_angle()
         double total_length = 0;
         double max_length = 0;
         {
-            Lines clipped_lines = intersection(lines, clip_area);
+            Lines clipped_lines = intersection_ln(lines, clip_area);
             for (size_t i = 0; i < clipped_lines.size(); ++i) {
                 const Line &line = clipped_lines[i];
                 if (expolygons_contain(this->_anchor_regions, line.a) && expolygons_contain(this->_anchor_regions, line.b)) {
@@ -203,76 +203,72 @@ std::vector<double> BridgeDetector::bridge_direction_candidates() const
     return angles;
 }
 
-void
-BridgeDetector::coverage(double angle, Polygons* coverage) const
+Polygons BridgeDetector::coverage(double angle) const
 {
-    if (angle == -1) angle = this->angle;
-    if (angle == -1) return;
+    if (angle == -1)
+        angle = this->angle;
 
-    // Get anchors, convert them to Polygons and rotate them.
-    Polygons anchors = to_polygons(this->_anchor_regions);
-    polygons_rotate(anchors, PI/2.0 - angle);
-    
     Polygons covered;
-    for (ExPolygons::const_iterator it_expoly = this->expolygons.begin(); it_expoly != this->expolygons.end(); ++ it_expoly)
-    {
-        // Clone our expolygon and rotate it so that we work with vertical lines.
-        ExPolygon expolygon = *it_expoly;
-        expolygon.rotate(PI/2.0 - angle);
+
+    if (angle != -1) {
+
+        // Get anchors, convert them to Polygons and rotate them.
+        Polygons anchors = to_polygons(this->_anchor_regions);
+        polygons_rotate(anchors, PI/2.0 - angle);
         
-        /*  Outset the bridge expolygon by half the amount we used for detecting anchors;
-            we'll use this one to generate our trapezoids and be sure that their vertices
-            are inside the anchors and not on their contours leading to false negatives. */
-        ExPolygons grown = offset_ex(expolygon, 0.5f * float(this->spacing));
-        
-        // Compute trapezoids according to a vertical orientation
-        Polygons trapezoids;
-        for (ExPolygons::const_iterator it = grown.begin(); it != grown.end(); ++it)
-            it->get_trapezoids2(&trapezoids, PI/2.0);
-        
-        for (Polygons::iterator trapezoid = trapezoids.begin(); trapezoid != trapezoids.end(); ++trapezoid) {
-            Lines supported = intersection(trapezoid->lines(), anchors);
-            size_t n_supported = 0;
-            // not nice, we need a more robust non-numeric check
-            for (size_t i = 0; i < supported.size(); ++i)
-                if (supported[i].length() >= this->spacing)
-                    ++ n_supported;
-            if (n_supported >= 2) 
-                covered.push_back(STDMOVE(*trapezoid));
+        for (ExPolygons::const_iterator it_expoly = this->expolygons.begin(); it_expoly != this->expolygons.end(); ++ it_expoly)
+        {
+            // Clone our expolygon and rotate it so that we work with vertical lines.
+            ExPolygon expolygon = *it_expoly;
+            expolygon.rotate(PI/2.0 - angle);
+            
+            /*  Outset the bridge expolygon by half the amount we used for detecting anchors;
+                we'll use this one to generate our trapezoids and be sure that their vertices
+                are inside the anchors and not on their contours leading to false negatives. */
+            ExPolygons grown = offset_ex(expolygon, 0.5f * float(this->spacing));
+            
+            // Compute trapezoids according to a vertical orientation
+            Polygons trapezoids;
+            for (ExPolygons::const_iterator it = grown.begin(); it != grown.end(); ++it)
+                it->get_trapezoids2(&trapezoids, PI/2.0);
+            
+            for (Polygons::iterator trapezoid = trapezoids.begin(); trapezoid != trapezoids.end(); ++trapezoid) {
+                Lines supported = intersection_ln(trapezoid->lines(), anchors);
+                size_t n_supported = 0;
+                // not nice, we need a more robust non-numeric check
+                for (size_t i = 0; i < supported.size(); ++i)
+                    if (supported[i].length() >= this->spacing)
+                        ++ n_supported;
+                if (n_supported >= 2) 
+                    covered.push_back(STDMOVE(*trapezoid));
+            }
         }
+
+        // Unite the trapezoids before rotation, as the rotation creates tiny gaps and intersections between the trapezoids
+        // instead of exact overlaps.
+        covered = union_(covered);
+
+        // Intersect trapezoids with actual bridge area to remove extra margins and append it to result.
+        polygons_rotate(covered, -(PI/2.0 - angle));
+    	covered = intersection(covered, to_polygons(this->expolygons));
+
+        /*
+        if (0) {
+            my @lines = map @{$_->lines}, @$trapezoids;
+            $_->rotate(-(PI/2 - $angle), [0,0]) for @lines;
+            
+            require "Slic3r/SVG.pm";
+            Slic3r::SVG::output(
+                "coverage_" . rad2deg($angle) . ".svg",
+                expolygons          => [$self->expolygon],
+                green_expolygons    => $self->_anchor_regions,
+                red_expolygons      => $coverage,
+                lines               => \@lines,
+            );
+        }
+        */
     }
-
-    // Unite the trapezoids before rotation, as the rotation creates tiny gaps and intersections between the trapezoids
-    // instead of exact overlaps.
-    covered = union_(covered);
-
-    // Intersect trapezoids with actual bridge area to remove extra margins and append it to result.
-    polygons_rotate(covered, -(PI/2.0 - angle));
-    intersection(covered, to_polygons(this->expolygons), coverage);
-
-    /*
-    if (0) {
-        my @lines = map @{$_->lines}, @$trapezoids;
-        $_->rotate(-(PI/2 - $angle), [0,0]) for @lines;
-        
-        require "Slic3r/SVG.pm";
-        Slic3r::SVG::output(
-            "coverage_" . rad2deg($angle) . ".svg",
-            expolygons          => [$self->expolygon],
-            green_expolygons    => $self->_anchor_regions,
-            red_expolygons      => $coverage,
-            lines               => \@lines,
-        );
-    }
-    */
-}
-
-Polygons
-BridgeDetector::coverage(double angle) const
-{
-    Polygons pp;
-    this->coverage(angle, &pp);
-    return pp;
+    return covered;
 }
 
 /*  This method returns the bridge edges (as polylines) that are not supported
@@ -288,9 +284,7 @@ BridgeDetector::unsupported_edges(double angle, Polylines* unsupported) const
 
     for (ExPolygons::const_iterator it_expoly = this->expolygons.begin(); it_expoly != this->expolygons.end(); ++ it_expoly) {    
         // get unsupported bridge edges (both contour and holes)
-        Polylines unuspported_polylines;
-        diff(to_polylines(*it_expoly), grown_lower, &unuspported_polylines);
-        Lines unsupported_lines = to_lines(unuspported_polylines);
+        Lines unsupported_lines = to_lines(diff_pl(to_polylines(*it_expoly), grown_lower));
         /*  Split into individual segments and filter out edges parallel to the bridging angle
             TODO: angle tolerance should probably be based on segment length and flow width,
             so that we build supports whenever there's a chance that at least one or two bridge
diff --git a/xs/src/libslic3r/BridgeDetector.hpp b/xs/src/libslic3r/BridgeDetector.hpp
index 825ce10b6..b04db63e4 100644
--- a/xs/src/libslic3r/BridgeDetector.hpp
+++ b/xs/src/libslic3r/BridgeDetector.hpp
@@ -32,7 +32,6 @@ public:
     BridgeDetector(ExPolygon _expolygon, const ExPolygonCollection &_lower_slices, coord_t _extrusion_width);
     BridgeDetector(const ExPolygons &_expolygons, const ExPolygonCollection &_lower_slices, coord_t _extrusion_width);
     bool detect_angle();
-    void coverage(double angle, Polygons* coverage) const;
     Polygons coverage(double angle = -1) const;
     void unsupported_edges(double angle, Polylines* unsupported) const;
     Polylines unsupported_edges(double angle = -1) const;
diff --git a/xs/src/libslic3r/ClipperUtils.cpp b/xs/src/libslic3r/ClipperUtils.cpp
index f2cc7eb38..7625cba49 100644
--- a/xs/src/libslic3r/ClipperUtils.cpp
+++ b/xs/src/libslic3r/ClipperUtils.cpp
@@ -9,65 +9,157 @@
 
 #include <Shiny/Shiny.h>
 
+// Factor to convert from coord_t (which is int32) to an int64 type used by the Clipper library
+// for general offsetting (the offset(), offset2(), offset_ex() functions) and for the safety offset,
+// which is optionally executed by other functions (union, intersection, diff).
+// By the way, is the scalling for offset needed at all?
+#define CLIPPER_OFFSET_POWER_OF_2 17
+// 2^17=131072
+#define CLIPPER_OFFSET_SCALE (1 << CLIPPER_OFFSET_POWER_OF_2)
+#define CLIPPER_OFFSET_SCALE_ROUNDING_DELTA ((1 << (CLIPPER_OFFSET_POWER_OF_2 - 1)) - 1)
+
 namespace Slic3r {
 
+#ifdef CLIPPER_UTILS_DEBUG
+bool clipper_export_enabled = false;
+// For debugging the Clipper library, for providing bug reports to the Clipper author.
+bool export_clipper_input_polygons_bin(const char *path, const ClipperLib::Paths &input_subject, const ClipperLib::Paths &input_clip)
+{
+    FILE *pfile = fopen(path, "wb");
+    if (pfile == NULL)
+        return false;
+
+    uint32_t sz = uint32_t(input_subject.size());
+    fwrite(&sz, 1, sizeof(sz), pfile);
+    for (size_t i = 0; i < input_subject.size(); ++i) {
+        const ClipperLib::Path &path = input_subject[i];
+        sz = uint32_t(path.size());
+        ::fwrite(&sz, 1, sizeof(sz), pfile);
+        ::fwrite(path.data(), sizeof(ClipperLib::IntPoint), sz, pfile);
+    }
+    sz = uint32_t(input_clip.size());
+    ::fwrite(&sz, 1, sizeof(sz), pfile);
+    for (size_t i = 0; i < input_clip.size(); ++i) {
+        const ClipperLib::Path &path = input_clip[i];
+        sz = uint32_t(path.size());
+        ::fwrite(&sz, 1, sizeof(sz), pfile);
+        ::fwrite(path.data(), sizeof(ClipperLib::IntPoint), sz, pfile);
+    }
+    ::fclose(pfile);
+    return true;
+
+err:
+    ::fclose(pfile);
+    return false;
+}
+#endif /* CLIPPER_UTILS_DEBUG */
+
+void scaleClipperPolygon(ClipperLib::Path &polygon)
+{
+    PROFILE_FUNC();
+    for (ClipperLib::Path::iterator pit = polygon.begin(); pit != polygon.end(); ++pit) {
+        pit->X <<= CLIPPER_OFFSET_POWER_OF_2;
+        pit->Y <<= CLIPPER_OFFSET_POWER_OF_2;
+    }
+}
+
+void scaleClipperPolygons(ClipperLib::Paths &polygons)
+{
+    PROFILE_FUNC();
+    for (ClipperLib::Paths::iterator it = polygons.begin(); it != polygons.end(); ++it)
+        for (ClipperLib::Path::iterator pit = (*it).begin(); pit != (*it).end(); ++pit) {
+            pit->X <<= CLIPPER_OFFSET_POWER_OF_2;
+            pit->Y <<= CLIPPER_OFFSET_POWER_OF_2;
+        }
+}
+
+void unscaleClipperPolygon(ClipperLib::Path &polygon)
+{
+    PROFILE_FUNC();
+    for (ClipperLib::Path::iterator pit = polygon.begin(); pit != polygon.end(); ++pit) {
+        pit->X += CLIPPER_OFFSET_SCALE_ROUNDING_DELTA;
+        pit->Y += CLIPPER_OFFSET_SCALE_ROUNDING_DELTA;
+        pit->X >>= CLIPPER_OFFSET_POWER_OF_2;
+        pit->Y >>= CLIPPER_OFFSET_POWER_OF_2;
+    }
+}
+
+void unscaleClipperPolygons(ClipperLib::Paths &polygons)
+{
+    PROFILE_FUNC();
+    for (ClipperLib::Paths::iterator it = polygons.begin(); it != polygons.end(); ++it)
+        for (ClipperLib::Path::iterator pit = (*it).begin(); pit != (*it).end(); ++pit) {
+            pit->X += CLIPPER_OFFSET_SCALE_ROUNDING_DELTA;
+            pit->Y += CLIPPER_OFFSET_SCALE_ROUNDING_DELTA;
+            pit->X >>= CLIPPER_OFFSET_POWER_OF_2;
+            pit->Y >>= CLIPPER_OFFSET_POWER_OF_2;
+        }
+}
+
 //-----------------------------------------------------------
 // legacy code from Clipper documentation
-void AddOuterPolyNodeToExPolygons(ClipperLib::PolyNode& polynode, Slic3r::ExPolygons* expolygons)
+void AddOuterPolyNodeToExPolygons(ClipperLib::PolyNode& polynode, ExPolygons* expolygons)
 {  
   size_t cnt = expolygons->size();
   expolygons->resize(cnt + 1);
-  ClipperPath_to_Slic3rMultiPoint(polynode.Contour, &(*expolygons)[cnt].contour);
+  (*expolygons)[cnt].contour = ClipperPath_to_Slic3rPolygon(polynode.Contour);
   (*expolygons)[cnt].holes.resize(polynode.ChildCount());
   for (int i = 0; i < polynode.ChildCount(); ++i)
   {
-    ClipperPath_to_Slic3rMultiPoint(polynode.Childs[i]->Contour, &(*expolygons)[cnt].holes[i]);
+    (*expolygons)[cnt].holes[i] = ClipperPath_to_Slic3rPolygon(polynode.Childs[i]->Contour);
     //Add outer polygons contained by (nested within) holes ...
     for (int j = 0; j < polynode.Childs[i]->ChildCount(); ++j)
       AddOuterPolyNodeToExPolygons(*polynode.Childs[i]->Childs[j], expolygons);
   }
 }
  
-void PolyTreeToExPolygons(ClipperLib::PolyTree& polytree, Slic3r::ExPolygons* expolygons)
+ExPolygons
+PolyTreeToExPolygons(ClipperLib::PolyTree& polytree)
 {
-    PROFILE_FUNC();
-    expolygons->clear();
+    ExPolygons retval;
     for (int i = 0; i < polytree.ChildCount(); ++i)
-        AddOuterPolyNodeToExPolygons(*polytree.Childs[i], expolygons);
+        AddOuterPolyNodeToExPolygons(*polytree.Childs[i], &retval);
+    return retval;
 }
 //-----------------------------------------------------------
 
-template <class T>
-void
-ClipperPath_to_Slic3rMultiPoint(const ClipperLib::Path &input, T* output)
+Slic3r::Polygon ClipperPath_to_Slic3rPolygon(const ClipperLib::Path &input)
 {
-    PROFILE_FUNC();
-    output->points.clear();
-    output->points.reserve(input.size());
+    Polygon retval;
     for (ClipperLib::Path::const_iterator pit = input.begin(); pit != input.end(); ++pit)
-        output->points.push_back(Slic3r::Point( (*pit).X, (*pit).Y ));
-}
-template void ClipperPath_to_Slic3rMultiPoint<Slic3r::Polygon>(const ClipperLib::Path &input, Slic3r::Polygon* output);
-
-template <class T>
-void
-ClipperPaths_to_Slic3rMultiPoints(const ClipperLib::Paths &input, T* output)
-{
-    PROFILE_FUNC();
-    output->clear();
-    output->reserve(input.size());
-    for (ClipperLib::Paths::const_iterator it = input.begin(); it != input.end(); ++it) {
-        typename T::value_type p;
-        ClipperPath_to_Slic3rMultiPoint(*it, &p);
-        output->push_back(p);
-    }
+        retval.points.push_back(Point( (*pit).X, (*pit).Y ));
+    return retval;
 }
 
-void
-ClipperPaths_to_Slic3rExPolygons(const ClipperLib::Paths &input, Slic3r::ExPolygons* output)
+Slic3r::Polyline ClipperPath_to_Slic3rPolyline(const ClipperLib::Path &input)
 {
-    PROFILE_FUNC();
+    Polyline retval;
+    for (ClipperLib::Path::const_iterator pit = input.begin(); pit != input.end(); ++pit)
+        retval.points.push_back(Point( (*pit).X, (*pit).Y ));
+    return retval;
+}
 
+Slic3r::Polygons ClipperPaths_to_Slic3rPolygons(const ClipperLib::Paths &input)
+{
+    Slic3r::Polygons retval;
+    retval.reserve(input.size());
+    for (ClipperLib::Paths::const_iterator it = input.begin(); it != input.end(); ++it)
+        retval.push_back(ClipperPath_to_Slic3rPolygon(*it));
+    return retval;
+}
+
+Slic3r::Polylines ClipperPaths_to_Slic3rPolylines(const ClipperLib::Paths &input)
+{
+    Slic3r::Polylines retval;
+    retval.reserve(input.size());
+    for (ClipperLib::Paths::const_iterator it = input.begin(); it != input.end(); ++it)
+        retval.push_back(ClipperPath_to_Slic3rPolyline(*it));
+    return retval;
+}
+
+ExPolygons
+ClipperPaths_to_Slic3rExPolygons(const ClipperLib::Paths &input)
+{
     // init Clipper
     ClipperLib::Clipper clipper;
     clipper.Clear();
@@ -78,213 +170,87 @@ ClipperPaths_to_Slic3rExPolygons(const ClipperLib::Paths &input, Slic3r::ExPolyg
     clipper.Execute(ClipperLib::ctUnion, polytree, ClipperLib::pftEvenOdd, ClipperLib::pftEvenOdd);  // offset results work with both EvenOdd and NonZero
     
     // write to ExPolygons object
-    output->clear();
-    PolyTreeToExPolygons(polytree, output);
+    return PolyTreeToExPolygons(polytree);
 }
 
-void
-Slic3rMultiPoint_to_ClipperPath(const Slic3r::MultiPoint &input, ClipperLib::Path* output)
+ClipperLib::Path
+Slic3rMultiPoint_to_ClipperPath(const MultiPoint &input)
 {
-    PROFILE_FUNC();
-
-    output->clear();
-    output->reserve(input.points.size());
-    for (Slic3r::Points::const_iterator pit = input.points.begin(); pit != input.points.end(); ++pit)
-        output->push_back(ClipperLib::IntPoint( (*pit).x, (*pit).y ));
+    ClipperLib::Path retval;
+    for (Points::const_iterator pit = input.points.begin(); pit != input.points.end(); ++pit)
+        retval.push_back(ClipperLib::IntPoint( (*pit).x, (*pit).y ));
+    return retval;
 }
 
-void
-Slic3rMultiPoint_to_ClipperPath_reversed(const Slic3r::MultiPoint &input, ClipperLib::Path* output)
+ClipperLib::Path
+Slic3rMultiPoint_to_ClipperPath_reversed(const Slic3r::MultiPoint &input)
 {
-    PROFILE_FUNC();
-
-    output->clear();
-    output->reserve(input.points.size());
+    ClipperLib::Path output;
+    output.reserve(input.points.size());
     for (Slic3r::Points::const_reverse_iterator pit = input.points.rbegin(); pit != input.points.rend(); ++pit)
-        output->push_back(ClipperLib::IntPoint( (*pit).x, (*pit).y ));
+        output.push_back(ClipperLib::IntPoint( (*pit).x, (*pit).y ));
+    return output;
 }
 
-template <class T>
-void
-Slic3rMultiPoints_to_ClipperPaths(const T &input, ClipperLib::Paths* output)
+ClipperLib::Paths Slic3rMultiPoints_to_ClipperPaths(const Polygons &input)
 {
-    PROFILE_FUNC();
-
-    output->clear();
-    output->reserve(input.size());
-    for (typename T::const_iterator it = input.begin(); it != input.end(); ++it) {
-        // std::vector< IntPoint >, IntPoint is a pair of int64_t
-        ClipperLib::Path p;
-        Slic3rMultiPoint_to_ClipperPath(*it, &p);
-        output->push_back(p);
-    }
+    ClipperLib::Paths retval;
+    for (Polygons::const_iterator it = input.begin(); it != input.end(); ++it)
+        retval.push_back(Slic3rMultiPoint_to_ClipperPath(*it));
+    return retval;
 }
 
-void
-scaleClipperPolygon(ClipperLib::Path &polygon, const double scale)
+ClipperLib::Paths Slic3rMultiPoints_to_ClipperPaths(const Polylines &input)
 {
-    PROFILE_FUNC();
-
-    for (ClipperLib::Path::iterator pit = polygon.begin(); pit != polygon.end(); ++pit) {
-        //FIXME multiplication of int64_t by double!
-        // Replace by bit shifts?
-        (*pit).X *= scale;
-        (*pit).Y *= scale;
-    }
+    ClipperLib::Paths retval;
+    for (Polylines::const_iterator it = input.begin(); it != input.end(); ++it)
+        retval.push_back(Slic3rMultiPoint_to_ClipperPath(*it));
+    return retval;
 }
 
-void
-scaleClipperPolygons(ClipperLib::Paths &polygons, const double scale)
+ClipperLib::Paths _offset(ClipperLib::Paths &&input, ClipperLib::EndType endType, const float delta, ClipperLib::JoinType joinType, double miterLimit)
 {
-    PROFILE_FUNC();
-
-    for (ClipperLib::Paths::iterator it = polygons.begin(); it != polygons.end(); ++it) {
-        for (ClipperLib::Path::iterator pit = (*it).begin(); pit != (*it).end(); ++pit) {
-            //FIXME multiplication of int64_t by double!
-            // Replace by bit shifts?
-            (*pit).X *= scale;
-            (*pit).Y *= scale;
-        }
-    }
-}
-
-void
-offset(const Slic3r::Polygons &polygons, ClipperLib::Paths* retval, const float delta,
-    double scale, ClipperLib::JoinType joinType, double miterLimit)
-{
-    PROFILE_FUNC();
-    // read input
-    ClipperLib::Paths input;
-    Slic3rMultiPoints_to_ClipperPaths(polygons, &input);
-    
     // scale input
-    scaleClipperPolygons(input, scale);
+    scaleClipperPolygons(input);
     
     // perform offset
     ClipperLib::ClipperOffset co;
-    if (joinType == jtRound) {
+    if (joinType == jtRound)
         co.ArcTolerance = miterLimit;
-    } else {
+    else
         co.MiterLimit = miterLimit;
-    }
-    {
-        PROFILE_BLOCK(offset_AddPaths);
-        co.AddPaths(input, joinType, ClipperLib::etClosedPolygon);
-    }
-    {
-        PROFILE_BLOCK(offset_Execute);
-        co.Execute(*retval, (delta*scale));
-    }
+    co.AddPaths(input, joinType, endType);
+    ClipperLib::Paths retval;
+    co.Execute(retval, delta * float(CLIPPER_OFFSET_SCALE));
     
     // unscale output
-    scaleClipperPolygons(*retval, 1/scale);
+    unscaleClipperPolygons(retval);
+    return retval;
 }
 
-void
-offset(const Slic3r::Polygons &polygons, Slic3r::Polygons* retval, const float delta,
-    double scale, ClipperLib::JoinType joinType, double miterLimit)
+ClipperLib::Paths _offset(ClipperLib::Path &&input, ClipperLib::EndType endType, const float delta, ClipperLib::JoinType joinType, double miterLimit)
 {
-    // perform offset
-    ClipperLib::Paths output;
-    offset(polygons, &output, delta, scale, joinType, miterLimit);
-    
-    // convert into ExPolygons
-    ClipperPaths_to_Slic3rMultiPoints(output, retval);
-}
-
-Slic3r::Polygons
-offset(const Slic3r::Polygons &polygons, const float delta,
-    double scale, ClipperLib::JoinType joinType, double miterLimit)
-{
-    Slic3r::Polygons pp;
-    offset(polygons, &pp, delta, scale, joinType, miterLimit);
-    return pp;
-}
-
-void
-offset(const Slic3r::Polylines &polylines, ClipperLib::Paths* retval, const float delta,
-    double scale, ClipperLib::JoinType joinType, double miterLimit)
-{
-    // read input
-    ClipperLib::Paths input;
-    Slic3rMultiPoints_to_ClipperPaths(polylines, &input);
-    
-    // scale input
-    scaleClipperPolygons(input, scale);
-    
-    // perform offset
-    ClipperLib::ClipperOffset co;
-    if (joinType == jtRound) {
-        co.ArcTolerance = miterLimit;
-    } else {
-        co.MiterLimit = miterLimit;
-    }
-    co.AddPaths(input, joinType, ClipperLib::etOpenButt);
-    co.Execute(*retval, (delta*scale));
-    
-    // unscale output
-    scaleClipperPolygons(*retval, 1/scale);
-}
-
-void
-offset(const Slic3r::Polylines &polylines, Slic3r::Polygons* retval, const float delta,
-    double scale, ClipperLib::JoinType joinType, double miterLimit)
-{
-    // perform offset
-    ClipperLib::Paths output;
-    offset(polylines, &output, delta, scale, joinType, miterLimit);
-    
-    // convert into ExPolygons
-    ClipperPaths_to_Slic3rMultiPoints(output, retval);
-}
-
-void
-offset(const Slic3r::Surface &surface, Slic3r::Surfaces* retval, const float delta,
-    double scale, ClipperLib::JoinType joinType, double miterLimit)
-{
-    // perform offset
-    Slic3r::ExPolygons expp;
-    offset(surface.expolygon, &expp, delta, scale, joinType, miterLimit);
-    
-    // clone the input surface for each expolygon we got
-    retval->clear();
-    retval->reserve(expp.size());
-    for (ExPolygons::iterator it = expp.begin(); it != expp.end(); ++it) {
-        Surface s = surface;  // clone
-        s.expolygon = *it;
-        retval->push_back(s);
-    }
-}
-
-void
-offset(const Slic3r::Polygons &polygons, Slic3r::ExPolygons* retval, const float delta,
-    double scale, ClipperLib::JoinType joinType, double miterLimit)
-{
-    // perform offset
-    ClipperLib::Paths output;
-    offset(polygons, &output, delta, scale, joinType, miterLimit);
-    
-    // convert into ExPolygons
-    ClipperPaths_to_Slic3rExPolygons(output, retval);
+    ClipperLib::Paths paths;
+    paths.push_back(std::move(input));
+	return _offset(std::move(paths), endType, delta, joinType, miterLimit);
 }
 
 // This is a safe variant of the polygon offset, tailored for a single ExPolygon:
 // a single polygon with multiple non-overlapping holes.
 // Each contour and hole is offsetted separately, then the holes are subtracted from the outer contours.
-void offset(const Slic3r::ExPolygon &expolygon, ClipperLib::Paths* retval, const float delta,
-    double scale, ClipperLib::JoinType joinType, double miterLimit)
+ClipperLib::Paths _offset(const Slic3r::ExPolygon &expolygon, const float delta,
+    ClipperLib::JoinType joinType, double miterLimit)
 {
 //    printf("new ExPolygon offset\n");
     // 1) Offset the outer contour.
-    const float delta_scaled = float(delta * scale);
+    const float delta_scaled = delta * float(CLIPPER_OFFSET_SCALE);
     ClipperLib::Paths contours;
     {
-        ClipperLib::Path input;
-        Slic3rMultiPoint_to_ClipperPath(expolygon.contour, &input);
-        scaleClipperPolygon(input, scale);
+        ClipperLib::Path input = Slic3rMultiPoint_to_ClipperPath(expolygon.contour);
+        scaleClipperPolygon(input);
         ClipperLib::ClipperOffset co;
         if (joinType == jtRound)
-            co.ArcTolerance = miterLimit;
+            co.ArcTolerance = miterLimit * double(CLIPPER_OFFSET_SCALE);
         else
             co.MiterLimit = miterLimit;
         co.AddPath(input, joinType, ClipperLib::etClosedPolygon);
@@ -296,12 +262,11 @@ void offset(const Slic3r::ExPolygon &expolygon, ClipperLib::Paths* retval, const
     {
         holes.reserve(expolygon.holes.size());
         for (Polygons::const_iterator it_hole = expolygon.holes.begin(); it_hole != expolygon.holes.end(); ++ it_hole) {
-            ClipperLib::Path input;
-            Slic3rMultiPoint_to_ClipperPath_reversed(*it_hole, &input);
-            scaleClipperPolygon(input, scale);
+            ClipperLib::Path input = Slic3rMultiPoint_to_ClipperPath_reversed(*it_hole);
+            scaleClipperPolygon(input);
             ClipperLib::ClipperOffset co;
             if (joinType == jtRound)
-                co.ArcTolerance = miterLimit;
+                co.ArcTolerance = miterLimit * double(CLIPPER_OFFSET_SCALE);
             else
                 co.MiterLimit = miterLimit;
             co.AddPath(input, joinType, ClipperLib::etClosedPolygon);
@@ -318,47 +283,22 @@ void offset(const Slic3r::ExPolygon &expolygon, ClipperLib::Paths* retval, const
         clipper.Clear();
         clipper.AddPaths(contours, ClipperLib::ptSubject, true);
         clipper.AddPaths(holes, ClipperLib::ptClip, true);
-        clipper.Execute(ClipperLib::ctDifference, *retval, ClipperLib::pftNonZero, ClipperLib::pftNonZero);
+        clipper.Execute(ClipperLib::ctDifference, output, ClipperLib::pftNonZero, ClipperLib::pftNonZero);
     }
     
     // 4) Unscale the output.
-    scaleClipperPolygons(*retval, 1/scale);
-}
-
-Slic3r::Polygons offset(const Slic3r::ExPolygon &expolygon, const float delta,
-    double scale, ClipperLib::JoinType joinType, double miterLimit)
-{
-    // perform offset
-    ClipperLib::Paths output;
-    offset(expolygon, &output, delta, scale, joinType, miterLimit);
-    
-    // convert into ExPolygons
-    Slic3r::Polygons retval;
-    ClipperPaths_to_Slic3rMultiPoints(output, &retval);
-    return retval;
-}
-
-Slic3r::ExPolygons offset_ex(const Slic3r::ExPolygon &expolygon, const float delta,
-    double scale, ClipperLib::JoinType joinType, double miterLimit)
-{
-    // perform offset
-    ClipperLib::Paths output;
-    offset(expolygon, &output, delta, scale, joinType, miterLimit);
-    
-    // convert into ExPolygons
-    Slic3r::ExPolygons retval;
-    ClipperPaths_to_Slic3rExPolygons(output, &retval);
-    return retval;
+    unscaleClipperPolygons(output);
+    return output;
 }
 
 // This is a safe variant of the polygon offset, tailored for a single ExPolygon:
 // a single polygon with multiple non-overlapping holes.
 // Each contour and hole is offsetted separately, then the holes are subtracted from the outer contours.
-void offset(const Slic3r::ExPolygons &expolygons, ClipperLib::Paths* retval, const float delta,
-    double scale, ClipperLib::JoinType joinType, double miterLimit)
+ClipperLib::Paths _offset(const Slic3r::ExPolygons &expolygons, const float delta,
+    ClipperLib::JoinType joinType, double miterLimit)
 {
 //    printf("new ExPolygon offset\n");
-    const float delta_scaled = float(delta * scale);
+    const float delta_scaled = delta * float(CLIPPER_OFFSET_SCALE);
     ClipperLib::Paths contours;
     ClipperLib::Paths holes;
     contours.reserve(expolygons.size());
@@ -372,12 +312,11 @@ void offset(const Slic3r::ExPolygons &expolygons, ClipperLib::Paths* retval, con
     for (Slic3r::ExPolygons::const_iterator it_expoly = expolygons.begin(); it_expoly != expolygons.end(); ++ it_expoly) {
         // 1) Offset the outer contour.
         {
-            ClipperLib::Path input;
-            Slic3rMultiPoint_to_ClipperPath(it_expoly->contour, &input);
-            scaleClipperPolygon(input, scale);
+            ClipperLib::Path input = Slic3rMultiPoint_to_ClipperPath(it_expoly->contour);
+            scaleClipperPolygon(input);
             ClipperLib::ClipperOffset co;
             if (joinType == jtRound)
-                co.ArcTolerance = miterLimit;
+                co.ArcTolerance = miterLimit * double(CLIPPER_OFFSET_SCALE);
             else
                 co.MiterLimit = miterLimit;
             co.AddPath(input, joinType, ClipperLib::etClosedPolygon);
@@ -389,12 +328,11 @@ void offset(const Slic3r::ExPolygons &expolygons, ClipperLib::Paths* retval, con
         // 2) Offset the holes one by one, collect the results.
         {
             for (Polygons::const_iterator it_hole = it_expoly->holes.begin(); it_hole != it_expoly->holes.end(); ++ it_hole) {
-                ClipperLib::Path input;
-                Slic3rMultiPoint_to_ClipperPath_reversed(*it_hole, &input);
-                scaleClipperPolygon(input, scale);
+                ClipperLib::Path input = Slic3rMultiPoint_to_ClipperPath_reversed(*it_hole);
+                scaleClipperPolygon(input);
                 ClipperLib::ClipperOffset co;
                 if (joinType == jtRound)
-                    co.ArcTolerance = miterLimit;
+                    co.ArcTolerance = miterLimit * double(CLIPPER_OFFSET_SCALE);
                 else
                     co.MiterLimit = miterLimit;
                 co.AddPath(input, joinType, ClipperLib::etClosedPolygon);
@@ -412,74 +350,23 @@ void offset(const Slic3r::ExPolygons &expolygons, ClipperLib::Paths* retval, con
         clipper.Clear();
         clipper.AddPaths(contours, ClipperLib::ptSubject, true);
         clipper.AddPaths(holes, ClipperLib::ptClip, true);
-        clipper.Execute(ClipperLib::ctDifference, *retval, ClipperLib::pftNonZero, ClipperLib::pftNonZero);
+        clipper.Execute(ClipperLib::ctDifference, output, ClipperLib::pftNonZero, ClipperLib::pftNonZero);
     }
     
     // 4) Unscale the output.
-    scaleClipperPolygons(*retval, 1/scale);
+    unscaleClipperPolygons(output);
+    return output;
 }
 
-Slic3r::Polygons offset(const Slic3r::ExPolygons &expolygons, const float delta,
-    double scale, ClipperLib::JoinType joinType, double miterLimit)
+ClipperLib::Paths
+_offset2(const Polygons &polygons, const float delta1, const float delta2,
+    const ClipperLib::JoinType joinType, const double miterLimit)
 {
-    // perform offset
-    ClipperLib::Paths output;
-    offset(expolygons, &output, delta, scale, joinType, miterLimit);
-    
-    // convert into ExPolygons
-    Slic3r::Polygons retval;
-    ClipperPaths_to_Slic3rMultiPoints(output, &retval);
-    return retval;
-}
-
-Slic3r::ExPolygons offset_ex(const Slic3r::ExPolygons &expolygons, const float delta,
-    double scale, ClipperLib::JoinType joinType, double miterLimit)
-{
-    // perform offset
-    ClipperLib::Paths output;
-    offset(expolygons, &output, delta, scale, joinType, miterLimit);
-    
-    // convert into ExPolygons
-    Slic3r::ExPolygons retval;
-    ClipperPaths_to_Slic3rExPolygons(output, &retval);
-    return retval;
-}
-
-Slic3r::ExPolygons
-offset_ex(const Slic3r::Polygons &polygons, const float delta,
-    double scale, ClipperLib::JoinType joinType, double miterLimit)
-{
-    Slic3r::ExPolygons expp;
-    offset(polygons, &expp, delta, scale, joinType, miterLimit);
-    return expp;
-}
-
-void
-offset2(const Slic3r::Polygons &polygons, ClipperLib::Paths* retval, const float delta1,
-    const float delta2, const double scale, const ClipperLib::JoinType joinType, const double miterLimit)
-{
-    if (delta1 * delta2 >= 0) {
-        // Both deltas are the same signum
-        offset(polygons, retval, delta1 + delta2, scale, joinType, miterLimit);
-        return;
-    }
-#ifdef CLIPPER_UTILS_DEBUG
-    BoundingBox bbox = get_extents(polygons);
-    coordf_t stroke_width = scale_(0.005);
-    static int iRun = 0;
-    ++ iRun;
-    bool flipY = false;
-    SVG svg(debug_out_path("offset2-%d.svg", iRun), bbox, scale_(1.), flipY);
-    for (Slic3r::Polygons::const_iterator it = polygons.begin(); it != polygons.end(); ++ it)
-        svg.draw(it->lines(), "gray", stroke_width);
-#endif /* CLIPPER_UTILS_DEBUG */
-
     // read input
-    ClipperLib::Paths input;
-    Slic3rMultiPoints_to_ClipperPaths(polygons, &input);
+    ClipperLib::Paths input = Slic3rMultiPoints_to_ClipperPaths(polygons);
     
     // scale input
-    scaleClipperPolygons(input, scale);
+    scaleClipperPolygons(input);
     
     // prepare ClipperOffset object
     ClipperLib::ClipperOffset co;
@@ -492,75 +379,49 @@ offset2(const Slic3r::Polygons &polygons, ClipperLib::Paths* retval, const float
     // perform first offset
     ClipperLib::Paths output1;
     co.AddPaths(input, joinType, ClipperLib::etClosedPolygon);
-    co.Execute(output1, (delta1*scale));
-#ifdef CLIPPER_UTILS_DEBUG
-    svg.draw(output1, 1./CLIPPER_OFFSET_SCALE, "red", stroke_width);
-#endif /* CLIPPER_UTILS_DEBUG */
+    co.Execute(output1, delta1 * float(CLIPPER_OFFSET_SCALE));
     
     // perform second offset
     co.Clear();
     co.AddPaths(output1, joinType, ClipperLib::etClosedPolygon);
-    co.Execute(*retval, (delta2*scale));
-#ifdef CLIPPER_UTILS_DEBUG
-    svg.draw(*retval, 1./CLIPPER_OFFSET_SCALE, "green", stroke_width);
-#endif /* CLIPPER_UTILS_DEBUG */
-
+    ClipperLib::Paths retval;
+    co.Execute(retval, delta2 * float(CLIPPER_OFFSET_SCALE));
+    
     // unscale output
-    scaleClipperPolygons(*retval, 1/scale);
+    unscaleClipperPolygons(retval);
+    return retval;
 }
 
-void
-offset2(const Slic3r::Polygons &polygons, Slic3r::Polygons* retval, const float delta1,
-    const float delta2, const double scale, const ClipperLib::JoinType joinType, const double miterLimit)
+Polygons
+offset2(const Polygons &polygons, const float delta1, const float delta2,
+    const ClipperLib::JoinType joinType, const double miterLimit)
 {
     // perform offset
-    ClipperLib::Paths output;
-    offset2(polygons, &output, delta1, delta2, scale, joinType, miterLimit);
+    ClipperLib::Paths output = _offset2(polygons, delta1, delta2, joinType, miterLimit);
     
     // convert into ExPolygons
-    ClipperPaths_to_Slic3rMultiPoints(output, retval);
+    return ClipperPaths_to_Slic3rPolygons(output);
 }
 
-Slic3r::Polygons
-offset2(const Slic3r::Polygons &polygons, const float delta1,
-    const float delta2, const double scale, const ClipperLib::JoinType joinType, const double miterLimit)
-{
-    Slic3r::Polygons pp;
-    offset2(polygons, &pp, delta1, delta2, scale, joinType, miterLimit);
-    return pp;
-}
-
-void
-offset2(const Slic3r::Polygons &polygons, Slic3r::ExPolygons* retval, const float delta1,
-    const float delta2, const double scale, const ClipperLib::JoinType joinType, const double miterLimit)
+ExPolygons
+offset2_ex(const Polygons &polygons, const float delta1, const float delta2,
+    const ClipperLib::JoinType joinType, const double miterLimit)
 {
     // perform offset
-    ClipperLib::Paths output;
-    offset2(polygons, &output, delta1, delta2, scale, joinType, miterLimit);
+    ClipperLib::Paths output = _offset2(polygons, delta1, delta2, joinType, miterLimit);
     
     // convert into ExPolygons
-    ClipperPaths_to_Slic3rExPolygons(output, retval);
-}
-
-Slic3r::ExPolygons
-offset2_ex(const Slic3r::Polygons &polygons, const float delta1,
-    const float delta2, const double scale, const ClipperLib::JoinType joinType, const double miterLimit)
-{
-    Slic3r::ExPolygons expp;
-    offset2(polygons, &expp, delta1, delta2, scale, joinType, miterLimit);
-    return expp;
+    return ClipperPaths_to_Slic3rExPolygons(output);
 }
 
 template <class T>
-void _clipper_do(const ClipperLib::ClipType clipType, const Slic3r::Polygons &subject, 
-    const Slic3r::Polygons &clip, T* retval, const ClipperLib::PolyFillType fillType, const bool safety_offset_)
+T
+_clipper_do(const ClipperLib::ClipType clipType, const Polygons &subject, 
+    const Polygons &clip, const ClipperLib::PolyFillType fillType, const bool safety_offset_)
 {
-    PROFILE_BLOCK(_clipper_do_polygons);
-
     // read input
-    ClipperLib::Paths input_subject, input_clip;
-    Slic3rMultiPoints_to_ClipperPaths(subject, &input_subject);
-    Slic3rMultiPoints_to_ClipperPaths(clip,    &input_clip);
+    ClipperLib::Paths input_subject = Slic3rMultiPoints_to_ClipperPaths(subject);
+    ClipperLib::Paths input_clip    = Slic3rMultiPoints_to_ClipperPaths(clip);
     
     // perform safety offset
     if (safety_offset_) {
@@ -576,29 +437,23 @@ void _clipper_do(const ClipperLib::ClipType clipType, const Slic3r::Polygons &su
     clipper.Clear();
     
     // add polygons
-    {
-        PROFILE_BLOCK(_clipper_do_polygons_AddPaths);
-        clipper.AddPaths(input_subject, ClipperLib::ptSubject, true);
-        clipper.AddPaths(input_clip, ClipperLib::ptClip, true);
-    }
+    clipper.AddPaths(input_subject, ClipperLib::ptSubject, true);
+    clipper.AddPaths(input_clip,    ClipperLib::ptClip,    true);
     
     // perform operation
-    { 
-        PROFILE_BLOCK(_clipper_do_polygons_Execute);
-        clipper.Execute(clipType, *retval, fillType, fillType);
-    }
+    T retval;
+    clipper.Execute(clipType, retval, fillType, fillType);
+    return retval;
 }
 
-void _clipper_do(const ClipperLib::ClipType clipType, const Slic3r::Polylines &subject, 
-    const Slic3r::Polygons &clip, ClipperLib::PolyTree* retval, const ClipperLib::PolyFillType fillType,
+ClipperLib::PolyTree
+_clipper_do_pl(const ClipperLib::ClipType clipType, const Polylines &subject, 
+    const Polygons &clip, const ClipperLib::PolyFillType fillType,
     const bool safety_offset_)
 {
-    PROFILE_BLOCK(_clipper_do_polylines);
-
     // read input
-    ClipperLib::Paths input_subject, input_clip;
-    Slic3rMultiPoints_to_ClipperPaths(subject, &input_subject);
-    Slic3rMultiPoints_to_ClipperPaths(clip,    &input_clip);
+    ClipperLib::Paths input_subject = Slic3rMultiPoints_to_ClipperPaths(subject);
+    ClipperLib::Paths input_clip    = Slic3rMultiPoints_to_ClipperPaths(clip);
     
     // perform safety offset
     if (safety_offset_) safety_offset(&input_clip);
@@ -608,302 +463,127 @@ void _clipper_do(const ClipperLib::ClipType clipType, const Slic3r::Polylines &s
     clipper.Clear();
     
     // add polygons
-    {
-        PROFILE_BLOCK(_clipper_do_polylines_AddPaths);
-        clipper.AddPaths(input_subject, ClipperLib::ptSubject, false);
-        clipper.AddPaths(input_clip,    ClipperLib::ptClip,    true);
-    }
+    clipper.AddPaths(input_subject, ClipperLib::ptSubject, false);
+    clipper.AddPaths(input_clip,    ClipperLib::ptClip,    true);
     
     // perform operation
-    {
-        PROFILE_BLOCK(_clipper_do_polylines_Execute);
-        clipper.Execute(clipType, *retval, fillType, fillType);
-    }
+    ClipperLib::PolyTree retval;
+    clipper.Execute(clipType, retval, fillType, fillType);
+    return retval;
 }
 
-void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject, 
-    const Slic3r::Polygons &clip, Slic3r::Polygons* retval, bool safety_offset_)
+Polygons
+_clipper(ClipperLib::ClipType clipType, const Polygons &subject, 
+    const Polygons &clip, bool safety_offset_)
+{
+    return ClipperPaths_to_Slic3rPolygons(_clipper_do<ClipperLib::Paths>(clipType, subject, clip, ClipperLib::pftNonZero, safety_offset_));
+}
+
+ExPolygons
+_clipper_ex(ClipperLib::ClipType clipType, const Polygons &subject, 
+    const Polygons &clip, bool safety_offset_)
+{
+    ClipperLib::PolyTree polytree = _clipper_do<ClipperLib::PolyTree>(clipType, subject, clip, ClipperLib::pftNonZero, safety_offset_);
+    return PolyTreeToExPolygons(polytree);
+}
+
+Polylines
+_clipper_pl(ClipperLib::ClipType clipType, const Polylines &subject, 
+    const Polygons &clip, bool safety_offset_)
 {
-    PROFILE_FUNC();
-    // perform operation
     ClipperLib::Paths output;
-    _clipper_do<ClipperLib::Paths>(clipType, subject, clip, &output, ClipperLib::pftNonZero, safety_offset_);
-    
-    // convert into Polygons
-    ClipperPaths_to_Slic3rMultiPoints(output, retval);
+    ClipperLib::PolyTreeToPaths(_clipper_do_pl(clipType, subject, clip, ClipperLib::pftNonZero, safety_offset_), output);
+    return ClipperPaths_to_Slic3rPolylines(output);
 }
 
-void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject, 
-    const Slic3r::Polygons &clip, Slic3r::ExPolygons* retval, bool safety_offset_)
-{
-    PROFILE_FUNC();
-    // perform operation
-    ClipperLib::PolyTree polytree;
-    _clipper_do<ClipperLib::PolyTree>(clipType, subject, clip, &polytree, ClipperLib::pftNonZero, safety_offset_);
-    
-    // convert into ExPolygons
-    PolyTreeToExPolygons(polytree, retval);
-}
-
-void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polylines &subject, 
-    const Slic3r::Polygons &clip, Slic3r::Polylines* retval, bool safety_offset_)
-{
-    PROFILE_FUNC();
-    // perform operation
-    ClipperLib::PolyTree polytree;
-    _clipper_do(clipType, subject, clip, &polytree, ClipperLib::pftNonZero, safety_offset_);
-    
-    // convert into Polylines
-    ClipperLib::Paths output;
-    ClipperLib::PolyTreeToPaths(polytree, output);
-    ClipperPaths_to_Slic3rMultiPoints(output, retval);
-}
-
-void _clipper(ClipperLib::ClipType clipType, const Slic3r::Lines &subject, 
-    const Slic3r::Polygons &clip, Slic3r::Lines* retval, bool safety_offset_)
-{
-    // convert Lines to Polylines
-    Slic3r::Polylines polylines;
-    polylines.reserve(subject.size());
-    for (Slic3r::Lines::const_iterator line = subject.begin(); line != subject.end(); ++line)
-        polylines.push_back(*line);
-    
-    // perform operation
-    _clipper(clipType, polylines, clip, &polylines, safety_offset_);
-    
-    // convert Polylines to Lines
-    for (Slic3r::Polylines::const_iterator polyline = polylines.begin(); polyline != polylines.end(); ++polyline)
-        retval->push_back(*polyline);
-}
-
-void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject, 
-    const Slic3r::Polygons &clip, Slic3r::Polylines* retval, bool safety_offset_)
+Polylines
+_clipper_pl(ClipperLib::ClipType clipType, const Polygons &subject, 
+    const Polygons &clip, bool safety_offset_)
 {
     // transform input polygons into polylines
-    Slic3r::Polylines polylines;
+    Polylines polylines;
     polylines.reserve(subject.size());
-    for (Slic3r::Polygons::const_iterator polygon = subject.begin(); polygon != subject.end(); ++polygon)
+    for (Polygons::const_iterator polygon = subject.begin(); polygon != subject.end(); ++polygon)
         polylines.push_back(*polygon);  // implicit call to split_at_first_point()
     
     // perform clipping
-    _clipper(clipType, polylines, clip, retval, safety_offset_);
+    Polylines retval = _clipper_pl(clipType, polylines, clip, safety_offset_);
     
     /* If the split_at_first_point() call above happens to split the polygon inside the clipping area
        we would get two consecutive polylines instead of a single one, so we go through them in order
        to recombine continuous polylines. */
-    for (size_t i = 0; i < retval->size(); ++i) {
-        for (size_t j = i+1; j < retval->size(); ++j) {
-            if ((*retval)[i].points.back().coincides_with((*retval)[j].points.front())) {
+    for (size_t i = 0; i < retval.size(); ++i) {
+        for (size_t j = i+1; j < retval.size(); ++j) {
+            if (retval[i].points.back().coincides_with(retval[j].points.front())) {
                 /* If last point of i coincides with first point of j,
                    append points of j to i and delete j */
-                (*retval)[i].points.insert((*retval)[i].points.end(), (*retval)[j].points.begin()+1, (*retval)[j].points.end());
-                retval->erase(retval->begin() + j);
+                retval[i].points.insert(retval[i].points.end(), retval[j].points.begin()+1, retval[j].points.end());
+                retval.erase(retval.begin() + j);
                 --j;
-            } else if ((*retval)[i].points.front().coincides_with((*retval)[j].points.back())) {
+            } else if (retval[i].points.front().coincides_with(retval[j].points.back())) {
                 /* If first point of i coincides with last point of j,
                    prepend points of j to i and delete j */
-                (*retval)[i].points.insert((*retval)[i].points.begin(), (*retval)[j].points.begin(), (*retval)[j].points.end()-1);
-                retval->erase(retval->begin() + j);
+                retval[i].points.insert(retval[i].points.begin(), retval[j].points.begin(), retval[j].points.end()-1);
+                retval.erase(retval.begin() + j);
                 --j;
-            } else if ((*retval)[i].points.front().coincides_with((*retval)[j].points.front())) {
+            } else if (retval[i].points.front().coincides_with(retval[j].points.front())) {
                 /* Since Clipper does not preserve orientation of polylines, 
                    also check the case when first point of i coincides with first point of j. */
-                (*retval)[j].reverse();
-                (*retval)[i].points.insert((*retval)[i].points.begin(), (*retval)[j].points.begin(), (*retval)[j].points.end()-1);
-                retval->erase(retval->begin() + j);
+                retval[j].reverse();
+                retval[i].points.insert(retval[i].points.begin(), retval[j].points.begin(), retval[j].points.end()-1);
+                retval.erase(retval.begin() + j);
                 --j;
-            } else if ((*retval)[i].points.back().coincides_with((*retval)[j].points.back())) {
+            } else if (retval[i].points.back().coincides_with(retval[j].points.back())) {
                 /* Since Clipper does not preserve orientation of polylines, 
                    also check the case when last point of i coincides with last point of j. */
-                (*retval)[j].reverse();
-                (*retval)[i].points.insert((*retval)[i].points.end(), (*retval)[j].points.begin()+1, (*retval)[j].points.end());
-                retval->erase(retval->begin() + j);
+                retval[j].reverse();
+                retval[i].points.insert(retval[i].points.end(), retval[j].points.begin()+1, retval[j].points.end());
+                retval.erase(retval.begin() + j);
                 --j;
             }
         }
-    }   
-}
-
-template <class SubjectType, class ResultType>
-void diff(const SubjectType &subject, const Slic3r::Polygons &clip, ResultType* retval, bool safety_offset_)
-{
-    _clipper(ClipperLib::ctDifference, subject, clip, retval, safety_offset_);
-}
-template void diff<Slic3r::Polygons, Slic3r::ExPolygons>(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::ExPolygons* retval, bool safety_offset_);
-template void diff<Slic3r::Polygons, Slic3r::Polygons>(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::Polygons* retval, bool safety_offset_);
-template void diff<Slic3r::Polygons, Slic3r::Polylines>(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::Polylines* retval, bool safety_offset_);
-template void diff<Slic3r::Polylines, Slic3r::Polylines>(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, Slic3r::Polylines* retval, bool safety_offset_);
-template void diff<Slic3r::Lines, Slic3r::Lines>(const Slic3r::Lines &subject, const Slic3r::Polygons &clip, Slic3r::Lines* retval, bool safety_offset_);
-
-template <class SubjectType, class ResultType>
-void diff(const SubjectType &subject, const Slic3r::ExPolygons &clip, ResultType* retval, bool safety_offset_)
-{
-    Slic3r::Polygons pp;
-    for (Slic3r::ExPolygons::const_iterator ex = clip.begin(); ex != clip.end(); ++ex) {
-        Slic3r::Polygons ppp = *ex;
-        pp.insert(pp.end(), ppp.begin(), ppp.end());
     }
-    diff(subject, pp, retval, safety_offset_);
-}
-template void diff<Slic3r::Polygons, Slic3r::ExPolygons>(const Slic3r::Polygons &subject, const Slic3r::ExPolygons &clip, Slic3r::ExPolygons* retval, bool safety_offset_);
-
-template <class ResultType>
-void diff(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, ResultType* retval, bool safety_offset_)
-{
-    Slic3r::Polygons pp;
-    for (Slic3r::ExPolygons::const_iterator ex = subject.begin(); ex != subject.end(); ++ex) {
-        Slic3r::Polygons ppp = *ex;
-        pp.insert(pp.end(), ppp.begin(), ppp.end());
-    }
-    diff(pp, clip, retval, safety_offset_);
-}
-
-Slic3r::Polygons
-diff(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_)
-{
-    Slic3r::Polygons pp;
-    diff(subject, clip, &pp, safety_offset_);
-    return pp;
-}
-
-template <class SubjectType, class ClipType>
-Slic3r::ExPolygons
-diff_ex(const SubjectType &subject, const ClipType &clip, bool safety_offset_)
-{
-    Slic3r::ExPolygons expp;
-    diff(subject, clip, &expp, safety_offset_);
-    return expp;
-}
-template Slic3r::ExPolygons diff_ex<Slic3r::Polygons, Slic3r::Polygons>(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_);
-template Slic3r::ExPolygons diff_ex<Slic3r::Polygons, Slic3r::ExPolygons>(const Slic3r::Polygons &subject, const Slic3r::ExPolygons &clip, bool safety_offset_);
-template Slic3r::ExPolygons diff_ex<Slic3r::ExPolygons, Slic3r::ExPolygons>(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, bool safety_offset_);
-
-template <class SubjectType, class ResultType>
-void intersection(const SubjectType &subject, const Slic3r::Polygons &clip, ResultType* retval, bool safety_offset_)
-{
-    _clipper(ClipperLib::ctIntersection, subject, clip, retval, safety_offset_);
-}
-template void intersection<Slic3r::Polygons, Slic3r::ExPolygons>(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::ExPolygons* retval, bool safety_offset_);
-template void intersection<Slic3r::Polygons, Slic3r::Polygons>(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::Polygons* retval, bool safety_offset_);
-template void intersection<Slic3r::Polygons, Slic3r::Polylines>(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::Polylines* retval, bool safety_offset_);
-template void intersection<Slic3r::Polylines, Slic3r::Polylines>(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, Slic3r::Polylines* retval, bool safety_offset_);
-template void intersection<Slic3r::Lines, Slic3r::Lines>(const Slic3r::Lines &subject, const Slic3r::Polygons &clip, Slic3r::Lines* retval, bool safety_offset_);
-
-template <class SubjectType>
-SubjectType intersection(const SubjectType &subject, const Slic3r::Polygons &clip, bool safety_offset_)
-{
-    SubjectType pp;
-    intersection(subject, clip, &pp, safety_offset_);
-    return pp;
-}
-
-template Slic3r::Polygons intersection<Slic3r::Polygons>(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_);
-template Slic3r::Polylines intersection<Slic3r::Polylines>(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, bool safety_offset_);
-template Slic3r::Lines intersection<Slic3r::Lines>(const Slic3r::Lines &subject, const Slic3r::Polygons &clip, bool safety_offset_);
-
-Slic3r::ExPolygons
-intersection_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_)
-{
-    Slic3r::ExPolygons expp;
-    intersection(subject, clip, &expp, safety_offset_);
-    return expp;
-}
-
-template <class SubjectType>
-bool intersects(const SubjectType &subject, const Slic3r::Polygons &clip, bool safety_offset_)
-{
-    SubjectType retval;
-    intersection(subject, clip, &retval, safety_offset_);
-    return !retval.empty();
-}
-template bool intersects<Slic3r::Polygons>(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_);
-template bool intersects<Slic3r::Polylines>(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, bool safety_offset_);
-template bool intersects<Slic3r::Lines>(const Slic3r::Lines &subject, const Slic3r::Polygons &clip, bool safety_offset_);
-
-void xor_(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::ExPolygons* retval, 
-    bool safety_offset_)
-{
-    _clipper(ClipperLib::ctXor, subject, clip, retval, safety_offset_);
-}
-
-template <class T>
-void union_(const Slic3r::Polygons &subject, T* retval, bool safety_offset_)
-{
-    Slic3r::Polygons p;
-    _clipper(ClipperLib::ctUnion, subject, p, retval, safety_offset_);
-}
-template void union_<Slic3r::ExPolygons>(const Slic3r::Polygons &subject, Slic3r::ExPolygons* retval, bool safety_offset_);
-template void union_<Slic3r::Polygons>(const Slic3r::Polygons &subject, Slic3r::Polygons* retval, bool safety_offset_);
-
-Slic3r::Polygons
-union_(const Slic3r::Polygons &subject, bool safety_offset)
-{
-    Polygons pp;
-    union_(subject, &pp, safety_offset);
-    return pp;
-}
-
-Slic3r::ExPolygons
-union_ex(const Slic3r::Polygons &subject, bool safety_offset)
-{
-    ExPolygons expp;
-    union_(subject, &expp, safety_offset);
-    return expp;
-}
-
-Slic3r::ExPolygons
-union_ex(const Slic3r::Surfaces &subject, bool safety_offset)
-{
-    Polygons pp;
-    for (Slic3r::Surfaces::const_iterator s = subject.begin(); s != subject.end(); ++s) {
-        Polygons spp = *s;
-        pp.insert(pp.end(), spp.begin(), spp.end());
-    }
-    return union_ex(pp, safety_offset);
-}
-
-void union_(const Slic3r::Polygons &subject1, const Slic3r::Polygons &subject2, Slic3r::Polygons* retval, bool safety_offset)
-{
-    Polygons pp = subject1;
-    pp.insert(pp.end(), subject2.begin(), subject2.end());
-    union_(pp, retval, safety_offset);
-}
-
-Slic3r::Polygons
-union_(const Slic3r::ExPolygons &subject1, const Slic3r::ExPolygons &subject2, bool safety_offset)
-{
-    Polygons pp;
-    for (Slic3r::ExPolygons::const_iterator it = subject1.begin(); it != subject1.end(); ++it) {
-        Polygons spp = *it;
-        pp.insert(pp.end(), spp.begin(), spp.end());
-    }
-    for (Slic3r::ExPolygons::const_iterator it = subject2.begin(); it != subject2.end(); ++it) {
-        Polygons spp = *it;
-        pp.insert(pp.end(), spp.begin(), spp.end());
-    }
-    Polygons retval;
-    union_(pp, &retval, safety_offset);
     return retval;
 }
 
-void union_pt(const Slic3r::Polygons &subject, ClipperLib::PolyTree* retval, bool safety_offset_)
+Lines
+_clipper_ln(ClipperLib::ClipType clipType, const Lines &subject, const Polygons &clip,
+    bool safety_offset_)
 {
-    Slic3r::Polygons clip;
-    _clipper_do<ClipperLib::PolyTree>(ClipperLib::ctUnion, subject, clip, retval, ClipperLib::pftEvenOdd, safety_offset_);
+    // convert Lines to Polylines
+    Polylines polylines;
+    polylines.reserve(subject.size());
+    for (Lines::const_iterator line = subject.begin(); line != subject.end(); ++line)
+        polylines.push_back(*line);
+    
+    // perform operation
+    polylines = _clipper_pl(clipType, polylines, clip, safety_offset_);
+    
+    // convert Polylines to Lines
+    Lines retval;
+    for (Polylines::const_iterator polyline = polylines.begin(); polyline != polylines.end(); ++polyline)
+        retval.push_back(*polyline);
+    return retval;
 }
 
-void union_pt_chained(const Slic3r::Polygons &subject, Slic3r::Polygons* retval, bool safety_offset_)
+ClipperLib::PolyTree
+union_pt(const Polygons &subject, bool safety_offset_)
 {
-    ClipperLib::PolyTree pt;
-    union_pt(subject, &pt, safety_offset_);
-    if (&subject == retval)
-        // It is safe to use the same variable for input and output, because this function makes
-        // a temporary copy of the results.
-        retval->clear();
-    traverse_pt(pt.Childs, retval);
+    return _clipper_do<ClipperLib::PolyTree>(ClipperLib::ctUnion, subject, Polygons(), ClipperLib::pftEvenOdd, safety_offset_);
 }
 
-static void traverse_pt(ClipperLib::PolyNodes &nodes, Slic3r::Polygons* retval)
+Polygons
+union_pt_chained(const Polygons &subject, bool safety_offset_)
+{
+    ClipperLib::PolyTree polytree = union_pt(subject, safety_offset_);
+    
+    Polygons retval;
+    traverse_pt(polytree.Childs, &retval);
+    return retval;
+}
+
+void
+traverse_pt(ClipperLib::PolyNodes &nodes, Polygons* retval)
 {
     /* use a nearest neighbor search to order these children
        TODO: supply start_near to chained_path() too? */
@@ -924,22 +604,18 @@ static void traverse_pt(ClipperLib::PolyNodes &nodes, Slic3r::Polygons* retval)
     for (ClipperLib::PolyNodes::iterator it = ordered_nodes.begin(); it != ordered_nodes.end(); ++it) {
         // traverse the next depth
         traverse_pt((*it)->Childs, retval);
-        
-        Polygon p;
-        ClipperPath_to_Slic3rMultiPoint((*it)->Contour, &p);
-        retval->push_back(p);
+        retval->push_back(ClipperPath_to_Slic3rPolygon((*it)->Contour));
         if ((*it)->IsHole()) retval->back().reverse();  // ccw
     }
 }
 
-void simplify_polygons(const Slic3r::Polygons &subject, Slic3r::Polygons* retval, bool preserve_collinear)
+Polygons
+simplify_polygons(const Polygons &subject, bool preserve_collinear)
 {
-    PROFILE_FUNC();
-
     // convert into Clipper polygons
-    ClipperLib::Paths input_subject, output;
-    Slic3rMultiPoints_to_ClipperPaths(subject, &input_subject);
+    ClipperLib::Paths input_subject = Slic3rMultiPoints_to_ClipperPaths(subject);
     
+    ClipperLib::Paths output;
     if (preserve_collinear) {
         ClipperLib::Clipper c;
         c.PreserveCollinear(true);
@@ -951,23 +627,18 @@ void simplify_polygons(const Slic3r::Polygons &subject, Slic3r::Polygons* retval
     }
     
     // convert into Slic3r polygons
-    ClipperPaths_to_Slic3rMultiPoints(output, retval);
+    return ClipperPaths_to_Slic3rPolygons(output);
 }
 
-void simplify_polygons(const Slic3r::Polygons &subject, Slic3r::ExPolygons* retval, bool preserve_collinear)
+ExPolygons
+simplify_polygons_ex(const Polygons &subject, bool preserve_collinear)
 {
-    PROFILE_FUNC();
-
     if (!preserve_collinear) {
-        Polygons polygons;
-        simplify_polygons(subject, &polygons, preserve_collinear);
-        union_(polygons, retval);
-        return;
+        return union_ex(simplify_polygons(subject, preserve_collinear));
     }
     
     // convert into Clipper polygons
-    ClipperLib::Paths input_subject;
-    Slic3rMultiPoints_to_ClipperPaths(subject, &input_subject);
+    ClipperLib::Paths input_subject = Slic3rMultiPoints_to_ClipperPaths(subject);
     
     ClipperLib::PolyTree polytree;
     
@@ -978,7 +649,7 @@ void simplify_polygons(const Slic3r::Polygons &subject, Slic3r::ExPolygons* retv
     c.Execute(ClipperLib::ctUnion, polytree, ClipperLib::pftNonZero, ClipperLib::pftNonZero);
     
     // convert into ExPolygons
-    PolyTreeToExPolygons(polytree, retval);
+    return PolyTreeToExPolygons(polytree);
 }
 
 void safety_offset(ClipperLib::Paths* paths)
@@ -986,43 +657,65 @@ void safety_offset(ClipperLib::Paths* paths)
     PROFILE_FUNC();
 
     // scale input
-    scaleClipperPolygons(*paths, CLIPPER_OFFSET_SCALE);
+    scaleClipperPolygons(*paths);
     
     // perform offset (delta = scale 1e-05)
     ClipperLib::ClipperOffset co;
-    co.MiterLimit = 2;
-    {
-        PROFILE_BLOCK(safety_offset_AddPaths);
-        co.AddPaths(*paths, ClipperLib::jtMiter, ClipperLib::etClosedPolygon);
+#ifdef CLIPPER_UTILS_DEBUG
+    if (clipper_export_enabled) {
+        static int iRun = 0;
+        export_clipper_input_polygons_bin(debug_out_path("safety_offset-polygons-%d", ++iRun).c_str(), *paths, ClipperLib::Paths());
     }
-    {
-        PROFILE_BLOCK(safety_offset_Execute);
-        co.Execute(*paths, 10.0 * CLIPPER_OFFSET_SCALE);
+#endif /* CLIPPER_UTILS_DEBUG */
+    ClipperLib::Paths out;
+    for (size_t i = 0; i < paths->size(); ++ i) {
+        ClipperLib::Path &path = (*paths)[i];
+        co.Clear();
+        co.MiterLimit = 2;
+        bool ccw = ClipperLib::Orientation(path);
+        if (! ccw)
+            std::reverse(path.begin(), path.end());
+        {
+            PROFILE_BLOCK(safety_offset_AddPaths);
+            co.AddPath((*paths)[i], ClipperLib::jtMiter, ClipperLib::etClosedPolygon);
+        }
+        {
+            PROFILE_BLOCK(safety_offset_Execute);
+            // offset outside by 10um
+            ClipperLib::Paths out_this;
+            co.Execute(out_this, ccw ? 10.f * float(CLIPPER_OFFSET_SCALE) : -10.f * float(CLIPPER_OFFSET_SCALE));
+            if (! ccw) {
+                // Reverse the resulting contours once again.
+                for (ClipperLib::Paths::iterator it = out_this.begin(); it != out_this.end(); ++ it)
+                    std::reverse(it->begin(), it->end());
+            }
+            if (out.empty())
+                out = std::move(out_this);
+            else
+                std::move(std::begin(out_this), std::end(out_this), std::back_inserter(out));
+        }
     }
+    *paths = std::move(out);
     
     // unscale output
-    scaleClipperPolygons(*paths, 1.0/CLIPPER_OFFSET_SCALE);
+    unscaleClipperPolygons(*paths);
 }
 
 Polygons top_level_islands(const Slic3r::Polygons &polygons)
 {
-    ClipperLib::Paths input;
-    Slic3rMultiPoints_to_ClipperPaths(polygons, &input);   
     // init Clipper
     ClipperLib::Clipper clipper;
     clipper.Clear();
     // perform union
-    clipper.AddPaths(input, ClipperLib::ptSubject, true);
+    clipper.AddPaths(Slic3rMultiPoints_to_ClipperPaths(polygons), ClipperLib::ptSubject, true);
     ClipperLib::PolyTree polytree;
     clipper.Execute(ClipperLib::ctUnion, polytree, ClipperLib::pftEvenOdd, ClipperLib::pftEvenOdd); 
     // Convert only the top level islands to the output.
     Polygons out;
     out.reserve(polytree.ChildCount());
-    for (int i = 0; i < polytree.ChildCount(); ++i) {
-        out.push_back(Polygon());
-        ClipperPath_to_Slic3rMultiPoint(polytree.Childs[i]->Contour, &out.back());
-    }
+    for (int i = 0; i < polytree.ChildCount(); ++i)
+        out.push_back(ClipperPath_to_Slic3rPolygon(polytree.Childs[i]->Contour));
     return out;
 }
 
-} // namespace Slic3r
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/xs/src/libslic3r/ClipperUtils.hpp b/xs/src/libslic3r/ClipperUtils.hpp
index 188867fe5..d6060386a 100644
--- a/xs/src/libslic3r/ClipperUtils.hpp
+++ b/xs/src/libslic3r/ClipperUtils.hpp
@@ -14,151 +14,197 @@ using ClipperLib::jtSquare;
 
 namespace Slic3r {
 
-// Factor to convert from coord_t (which is int32) to an int64 type used by the Clipper library.
-//FIXME Vojtech: Better to use a power of 2 coefficient and to use bit shifts for scaling.
-// How about 2^17=131072?
-// By the way, is the scalling needed at all? Cura runs all the computation with a fixed point precision of 1um, while Slic3r scales to 1nm,
-// further scaling by 10e5 brings us to 
-#define CLIPPER_OFFSET_SCALE 100000.0
-
 //-----------------------------------------------------------
 // legacy code from Clipper documentation
 void AddOuterPolyNodeToExPolygons(ClipperLib::PolyNode& polynode, Slic3r::ExPolygons& expolygons);
 void PolyTreeToExPolygons(ClipperLib::PolyTree& polytree, Slic3r::ExPolygons& expolygons);
 //-----------------------------------------------------------
 
-void Slic3rMultiPoint_to_ClipperPath(const Slic3r::MultiPoint &input, ClipperLib::Path* output);
-template <class T>
-void Slic3rMultiPoints_to_ClipperPaths(const T &input, ClipperLib::Paths* output);
-template <class T>
-void ClipperPath_to_Slic3rMultiPoint(const ClipperLib::Path &input, T* output);
-template <class T>
-void ClipperPaths_to_Slic3rMultiPoints(const ClipperLib::Paths &input, T* output);
-void ClipperPaths_to_Slic3rExPolygons(const ClipperLib::Paths &input, Slic3r::ExPolygons* output);
-
-void scaleClipperPolygons(ClipperLib::Paths &polygons, const double scale);
+ClipperLib::Path   Slic3rMultiPoint_to_ClipperPath(const Slic3r::MultiPoint &input);
+ClipperLib::Paths  Slic3rMultiPoints_to_ClipperPaths(const Polygons &input);
+ClipperLib::Paths  Slic3rMultiPoints_to_ClipperPaths(const Polylines &input);
+Slic3r::Polygon    ClipperPath_to_Slic3rPolygon(const ClipperLib::Path &input);
+Slic3r::Polyline   ClipperPath_to_Slic3rPolyline(const ClipperLib::Path &input);
+Slic3r::Polygons   ClipperPaths_to_Slic3rPolygons(const ClipperLib::Paths &input);
+Slic3r::Polylines  ClipperPaths_to_Slic3rPolylines(const ClipperLib::Paths &input);
+Slic3r::ExPolygons ClipperPaths_to_Slic3rExPolygons(const ClipperLib::Paths &input);
 
 // offset Polygons
-void offset(const Slic3r::Polygons &polygons, ClipperLib::Paths* retval, const float delta,
-    double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter, 
-    double miterLimit = 3);
-void offset(const Slic3r::Polygons &polygons, Slic3r::Polygons* retval, const float delta,
-    double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter, 
-    double miterLimit = 3);
-Slic3r::Polygons offset(const Slic3r::Polygons &polygons, const float delta,
-    double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter, 
-    double miterLimit = 3);
-
-// This is a safe variant of the polygon offset, tailored for a single ExPolygon:
-// a single polygon with multiple non-overlapping holes.
-// Each contour and hole is offsetted separately, then the holes are subtracted from the outer contours.
-void offset(const Slic3r::ExPolygon &expolygon, ClipperLib::Paths* retval, const float delta,
-    double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
-    double miterLimit = 3);
-void offset(const Slic3r::ExPolygons &expolygons, ClipperLib::Paths* retval, const float delta,
-    double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
-    double miterLimit = 3);
-Slic3r::Polygons offset(const Slic3r::ExPolygon &expolygon, const float delta,
-    double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
-    double miterLimit = 3);
-Slic3r::Polygons offset(const Slic3r::ExPolygons &expolygons, const float delta,
-    double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
-    double miterLimit = 3);
-Slic3r::ExPolygons offset_ex(const Slic3r::ExPolygon &expolygon, const float delta,
-    double scale, ClipperLib::JoinType joinType, double miterLimit);
-Slic3r::ExPolygons offset_ex(const Slic3r::ExPolygons &expolygons, const float delta,
-    double scale, ClipperLib::JoinType joinType, double miterLimit);
+ClipperLib::Paths _offset(ClipperLib::Path &&input, ClipperLib::EndType endType, const float delta, ClipperLib::JoinType joinType, double miterLimit);
+ClipperLib::Paths _offset(ClipperLib::Paths &&input, ClipperLib::EndType endType, const float delta, ClipperLib::JoinType joinType, double miterLimit);
+inline Slic3r::Polygons offset(const Slic3r::Polygon &polygon, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter,  double miterLimit = 3)
+    { return ClipperPaths_to_Slic3rPolygons(_offset(Slic3rMultiPoint_to_ClipperPath(polygon), ClipperLib::etClosedPolygon, delta, joinType, miterLimit)); }
+inline Slic3r::Polygons offset(const Slic3r::Polygons &polygons, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3)
+    { return ClipperPaths_to_Slic3rPolygons(_offset(Slic3rMultiPoints_to_ClipperPaths(polygons), ClipperLib::etClosedPolygon, delta, joinType, miterLimit)); }
 
 // offset Polylines
-void offset(const Slic3r::Polylines &polylines, ClipperLib::Paths* retval, const float delta,
-    double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtSquare, 
-    double miterLimit = 3);
-void offset(const Slic3r::Polylines &polylines, Slic3r::Polygons* retval, const float delta,
-    double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtSquare, 
-    double miterLimit = 3);
-void offset(const Slic3r::Surface &surface, Slic3r::Surfaces* retval, const float delta,
-    double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtSquare, 
-    double miterLimit = 3);
+inline Slic3r::Polygons offset(const Slic3r::Polyline &polyline, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtSquare, double miterLimit = 3)
+    { return ClipperPaths_to_Slic3rPolygons(_offset(Slic3rMultiPoint_to_ClipperPath(polyline), ClipperLib::etOpenButt, delta, joinType, miterLimit)); }
+inline Slic3r::Polygons offset(const Slic3r::Polylines &polylines, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtSquare, double miterLimit = 3)
+    { return ClipperPaths_to_Slic3rPolygons(_offset(Slic3rMultiPoints_to_ClipperPaths(polylines), ClipperLib::etOpenButt, delta, joinType, miterLimit)); }
 
-void offset(const Slic3r::Polygons &polygons, Slic3r::ExPolygons* retval, const float delta,
-    double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter, 
-    double miterLimit = 3);
-Slic3r::ExPolygons offset_ex(const Slic3r::Polygons &polygons, const float delta,
-    double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter, 
-    double miterLimit = 3);
+// offset expolygons and surfaces
+ClipperLib::Paths _offset(const Slic3r::ExPolygon &expolygon, const float delta, ClipperLib::JoinType joinType, double miterLimit);
+ClipperLib::Paths _offset(const Slic3r::ExPolygons &expolygons, const float delta, ClipperLib::JoinType joinType, double miterLimit);
+inline Slic3r::Polygons offset(const Slic3r::ExPolygon &expolygon, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3)
+    { return ClipperPaths_to_Slic3rPolygons(_offset(expolygon, delta, joinType, miterLimit)); }
+inline Slic3r::Polygons offset(const Slic3r::ExPolygons &expolygons, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3)
+    { return ClipperPaths_to_Slic3rPolygons(_offset(expolygons, delta, joinType, miterLimit)); }
+inline Slic3r::ExPolygons offset_ex(const Slic3r::Polygon &polygon, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3)
+    { return ClipperPaths_to_Slic3rExPolygons(_offset(Slic3rMultiPoint_to_ClipperPath(polygon), ClipperLib::etClosedPolygon, delta, joinType, miterLimit)); }    
+inline Slic3r::ExPolygons offset_ex(const Slic3r::Polygons &polygons, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3)
+    { return ClipperPaths_to_Slic3rExPolygons(_offset(Slic3rMultiPoints_to_ClipperPaths(polygons), ClipperLib::etClosedPolygon, delta, joinType, miterLimit)); }
+inline Slic3r::ExPolygons offset_ex(const Slic3r::ExPolygon &expolygon, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3)
+    { return ClipperPaths_to_Slic3rExPolygons(_offset(expolygon, delta, joinType, miterLimit)); }
+inline Slic3r::ExPolygons offset_ex(const Slic3r::ExPolygons &expolygons, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3)
+    { return ClipperPaths_to_Slic3rExPolygons(_offset(expolygons, delta, joinType, miterLimit)); }
 
-void offset2(const Slic3r::Polygons &polygons, ClipperLib::Paths* retval, const float delta1,
-    const float delta2, double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter, 
-    double miterLimit = 3);
-void offset2(const Slic3r::Polygons &polygons, Slic3r::Polygons* retval, const float delta1,
-    const float delta2, double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter, 
+ClipperLib::Paths _offset2(const Slic3r::Polygons &polygons, const float delta1,
+    const float delta2, ClipperLib::JoinType joinType = ClipperLib::jtMiter, 
     double miterLimit = 3);
 Slic3r::Polygons offset2(const Slic3r::Polygons &polygons, const float delta1,
-    const float delta2, double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter, 
-    double miterLimit = 3);
-void offset2(const Slic3r::Polygons &polygons, Slic3r::ExPolygons* retval, const float delta1,
-    const float delta2, double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter, 
+    const float delta2, ClipperLib::JoinType joinType = ClipperLib::jtMiter, 
     double miterLimit = 3);
 Slic3r::ExPolygons offset2_ex(const Slic3r::Polygons &polygons, const float delta1,
-    const float delta2, double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter, 
+    const float delta2, ClipperLib::JoinType joinType = ClipperLib::jtMiter, 
     double miterLimit = 3);
 
-template <class T>
-void _clipper_do(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject, 
-    const Slic3r::Polygons &clip, T* retval, bool safety_offset_);
-void _clipper_do(ClipperLib::ClipType clipType, const Slic3r::Polylines &subject, 
-    const Slic3r::Polygons &clip, ClipperLib::Paths* retval, bool safety_offset_);
-void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject, 
-    const Slic3r::Polygons &clip, Slic3r::Polygons* retval, bool safety_offset_);
-void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject, 
-    const Slic3r::Polygons &clip, Slic3r::ExPolygons* retval, bool safety_offset_);
-void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polylines &subject, 
-    const Slic3r::Polygons &clip, Slic3r::Polylines* retval);
-void _clipper(ClipperLib::ClipType clipType, const Slic3r::Lines &subject, 
-    const Slic3r::Polygons &clip, Slic3r::Lines* retval);
+Slic3r::Polygons _clipper(ClipperLib::ClipType clipType,
+    const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false);
+Slic3r::ExPolygons _clipper_ex(ClipperLib::ClipType clipType,
+    const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false);
+Slic3r::Polylines _clipper_pl(ClipperLib::ClipType clipType,
+    const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false);
+Slic3r::Polylines _clipper_pl(ClipperLib::ClipType clipType,
+    const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false);
+Slic3r::Lines _clipper_ln(ClipperLib::ClipType clipType,
+    const Slic3r::Lines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false);
 
-template <class SubjectType, class ResultType>
-void diff(const SubjectType &subject, const Slic3r::Polygons &clip, ResultType* retval, bool safety_offset_ = false);
+// diff
+inline Slic3r::Polygons
+diff(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false)
+{
+    return _clipper(ClipperLib::ctDifference, subject, clip, safety_offset_);
+}
 
-template <class SubjectType, class ResultType>
-void diff(const SubjectType &subject, const Slic3r::ExPolygons &clip, ResultType* retval, bool safety_offset_ = false);
+inline Slic3r::ExPolygons
+diff_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false)
+{
+    return _clipper_ex(ClipperLib::ctDifference, subject, clip, safety_offset_);
+}
 
-Slic3r::Polygons diff(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false);
+inline Slic3r::ExPolygons
+diff_ex(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, bool safety_offset_ = false)
+{
+    return _clipper_ex(ClipperLib::ctDifference, to_polygons(subject), to_polygons(clip), safety_offset_);
+}
 
-template <class SubjectType, class ClipType>
-Slic3r::ExPolygons diff_ex(const SubjectType &subject, const ClipType &clip, bool safety_offset_ = false);
+inline Slic3r::Polygons
+diff(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, bool safety_offset_ = false)
+{
+    return _clipper(ClipperLib::ctDifference, to_polygons(subject), to_polygons(clip), safety_offset_);
+}
 
-template <class SubjectType, class ResultType>
-void intersection(const SubjectType &subject, const Slic3r::Polygons &clip, ResultType* retval, bool safety_offset_ = false);
+inline Slic3r::Polylines
+diff_pl(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false)
+{
+    return _clipper_pl(ClipperLib::ctDifference, subject, clip, safety_offset_);
+}
 
-template <class SubjectType>
-SubjectType intersection(const SubjectType &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false);
+inline Slic3r::Polylines
+diff_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false)
+{
+    return _clipper_pl(ClipperLib::ctDifference, subject, clip, safety_offset_);
+}
 
-Slic3r::ExPolygons
-intersection_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false);
+inline Slic3r::Lines
+diff_ln(const Slic3r::Lines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false)
+{
+    return _clipper_ln(ClipperLib::ctDifference, subject, clip, safety_offset_);
+}
 
-template <class SubjectType>
-bool intersects(const SubjectType &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false);
+// intersection
+inline Slic3r::Polygons
+intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false)
+{
+    return _clipper(ClipperLib::ctIntersection, subject, clip, safety_offset_);
+}
 
-void xor_(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::ExPolygons* retval, 
-    bool safety_offset_ = false);
+inline Slic3r::ExPolygons
+intersection_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false)
+{
+    return _clipper_ex(ClipperLib::ctIntersection, subject, clip, safety_offset_);
+}
 
-template <class T>
-void union_(const Slic3r::Polygons &subject, T* retval, bool safety_offset_ = false);
+inline Slic3r::ExPolygons
+intersection_ex(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, bool safety_offset_ = false)
+{
+    return _clipper_ex(ClipperLib::ctIntersection, to_polygons(subject), to_polygons(clip), safety_offset_);
+}
 
-Slic3r::Polygons union_(const Slic3r::Polygons &subject, bool safety_offset = false);
-Slic3r::ExPolygons union_ex(const Slic3r::Polygons &subject, bool safety_offset = false);
-Slic3r::ExPolygons union_ex(const Slic3r::Surfaces &subject, bool safety_offset = false);
+inline Slic3r::Polygons
+intersection(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, bool safety_offset_ = false)
+{
+    return _clipper(ClipperLib::ctIntersection, to_polygons(subject), to_polygons(clip), safety_offset_);
+}
 
-void union_(const Slic3r::Polygons &subject1, const Slic3r::Polygons &subject2, Slic3r::Polygons* retval, bool safety_offset = false);
-Slic3r::Polygons union_(const Slic3r::ExPolygons &subject1, const Slic3r::ExPolygons &subject2, bool safety_offset = false);
+inline Slic3r::Polylines
+intersection_pl(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false)
+{
+    return _clipper_pl(ClipperLib::ctIntersection, subject, clip, safety_offset_);
+}
 
-void union_pt(const Slic3r::Polygons &subject, ClipperLib::PolyTree* retval, bool safety_offset_ = false);
-void union_pt_chained(const Slic3r::Polygons &subject, Slic3r::Polygons* retval, bool safety_offset_ = false);
-static void traverse_pt(ClipperLib::PolyNodes &nodes, Slic3r::Polygons* retval);
+inline Slic3r::Polylines
+intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false)
+{
+    return _clipper_pl(ClipperLib::ctIntersection, subject, clip, safety_offset_);
+}
 
-void simplify_polygons(const Slic3r::Polygons &subject, Slic3r::Polygons* retval, bool preserve_collinear = false);
-void simplify_polygons(const Slic3r::Polygons &subject, Slic3r::ExPolygons* retval, bool preserve_collinear = false);
+inline Slic3r::Lines
+intersection_ln(const Slic3r::Lines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false)
+{
+    return _clipper_ln(ClipperLib::ctIntersection, subject, clip, safety_offset_);
+}
+
+// union
+inline Slic3r::Polygons
+union_(const Slic3r::Polygons &subject, bool safety_offset_ = false)
+{
+    return _clipper(ClipperLib::ctUnion, subject, Slic3r::Polygons(), safety_offset_);
+}
+
+inline Slic3r::Polygons
+union_(const Slic3r::Polygons &subject, const Slic3r::Polygons &subject2, bool safety_offset_ = false)
+{
+    return _clipper(ClipperLib::ctUnion, subject, subject2, safety_offset_);
+}
+
+inline Slic3r::ExPolygons
+union_ex(const Slic3r::Polygons &subject, bool safety_offset_ = false)
+{
+    return _clipper_ex(ClipperLib::ctUnion, subject, Slic3r::Polygons(), safety_offset_);
+}
+
+inline Slic3r::ExPolygons
+union_ex(const Slic3r::ExPolygons &subject, bool safety_offset_ = false)
+{
+    return _clipper_ex(ClipperLib::ctUnion, to_polygons(subject), Slic3r::Polygons(), safety_offset_);
+}
+
+inline Slic3r::ExPolygons
+union_ex(const Slic3r::Surfaces &subject, bool safety_offset_ = false)
+{
+    return _clipper_ex(ClipperLib::ctUnion, to_polygons(subject), Slic3r::Polygons(), safety_offset_);
+}
+
+
+ClipperLib::PolyTree union_pt(const Slic3r::Polygons &subject, bool safety_offset_ = false);
+Slic3r::Polygons union_pt_chained(const Slic3r::Polygons &subject, bool safety_offset_ = false);
+void traverse_pt(ClipperLib::PolyNodes &nodes, Slic3r::Polygons* retval);
+
+/* OTHER */
+Slic3r::Polygons simplify_polygons(const Slic3r::Polygons &subject, bool preserve_collinear = false);
+Slic3r::ExPolygons simplify_polygons_ex(const Slic3r::Polygons &subject, bool preserve_collinear = false);
 
 void safety_offset(ClipperLib::Paths* paths);
 
@@ -166,4 +212,4 @@ Polygons top_level_islands(const Slic3r::Polygons &polygons);
 
 }
 
-#endif
+#endif
\ No newline at end of file
diff --git a/xs/src/libslic3r/Config.hpp b/xs/src/libslic3r/Config.hpp
index 3fbd82060..6c8fa9f27 100644
--- a/xs/src/libslic3r/Config.hpp
+++ b/xs/src/libslic3r/Config.hpp
@@ -1,6 +1,7 @@
 #ifndef slic3r_Config_hpp_
 #define slic3r_Config_hpp_
 
+#include <assert.h>
 #include <map>
 #include <climits>
 #include <cstdio>
@@ -73,11 +74,8 @@ class ConfigOptionVector : public ConfigOptionVectorBase
     };
     
     T get_at(size_t i) const {
-        try {
-            return this->values.at(i);
-        } catch (const std::out_of_range& oor) {
-            return this->values.front();
-        }
+        assert(! this->values.empty());
+        return (i < this->values.size()) ? this->values[i] : this->values.front();
     };
 };
 
diff --git a/xs/src/libslic3r/EdgeGrid.cpp b/xs/src/libslic3r/EdgeGrid.cpp
index 67c438f8e..4d1432722 100644
--- a/xs/src/libslic3r/EdgeGrid.cpp
+++ b/xs/src/libslic3r/EdgeGrid.cpp
@@ -1,6 +1,7 @@
 #include <algorithm>
 #include <vector>
 #include <float.h>
+#include <unordered_map>
 
 #ifdef SLIC3R_GUI
 #include <wx/image.h>
@@ -109,7 +110,6 @@ void EdgeGrid::Grid::create(const ExPolygonCollection &expolygons, coord_t resol
 void EdgeGrid::Grid::create_from_m_contours(coord_t resolution)
 {
 	// 1) Measure the bounding box.
-	m_bbox.defined = false;
 	for (size_t i = 0; i < m_contours.size(); ++ i) {
 		const Slic3r::Points &pts = *m_contours[i];
 		for (size_t j = 0; j < pts.size(); ++ j)
@@ -1118,7 +1118,7 @@ float EdgeGrid::Grid::signed_distance_bilinear(const Point &pt) const
 
 	return f;
 }
-
+ 
 bool EdgeGrid::Grid::signed_distance_edges(const Point &pt, coord_t search_radius, coordf_t &result_min_dist, bool *pon_segment) const {
 	BoundingBox bbox;
 	bbox.min = bbox.max = Point(pt.x - m_bbox.min.x, pt.y - m_bbox.min.y);
@@ -1222,6 +1222,114 @@ bool EdgeGrid::Grid::signed_distance(const Point &pt, coord_t search_radius, coo
 	return true;
 }
 
+Polygons EdgeGrid::Grid::contours_simplified() const
+{
+	typedef std::unordered_multimap<Point, int, PointHash> EndPointMapType;
+	// 1) Collect the lines.
+	std::vector<Line> lines;
+	EndPointMapType start_point_to_line_idx;
+	for (int r = 0; r <= int(m_rows); ++ r) {
+		for (int c = 0; c <= int(m_cols); ++ c) {
+			bool left    = cell_inside_or_crossing(r  , c-1);
+			bool top     = cell_inside_or_crossing(r-1, c  );
+			bool current = cell_inside_or_crossing(r  , c  );
+			if (left != current) {
+				lines.push_back(
+					left ? 
+						Line(Point(c, r+1), Point(c, r  )) : 
+						Line(Point(c, r  ), Point(c, r+1)));
+				start_point_to_line_idx.insert(std::pair<Point, int>(lines.back().a, int(lines.size()) - 1));
+			}
+			if (top != current) {
+				lines.push_back(
+					top ? 
+						Line(Point(c  , r), Point(c+1, r)) :
+						Line(Point(c+1, r), Point(c  , r))); 
+				start_point_to_line_idx.insert(std::pair<Point, int>(lines.back().a, int(lines.size()) - 1));
+			}
+		}
+	}
+
+	// 2) Chain the lines.
+	std::vector<char> line_processed(lines.size(), false);
+	Polygons out;
+	for (int i_candidate = 0; i_candidate < int(lines.size()); ++ i_candidate) {
+		if (line_processed[i_candidate])
+			continue;
+		Polygon poly;
+		line_processed[i_candidate] = true;
+		poly.points.push_back(lines[i_candidate].b);
+		int i_line_current = i_candidate;
+		for (;;) {
+			std::pair<EndPointMapType::iterator,EndPointMapType::iterator> line_range = 
+				start_point_to_line_idx.equal_range(lines[i_line_current].b);
+			// The interval has to be non empty, there shall be at least one line continuing the current one.
+			assert(line_range.first != line_range.second);
+			int i_next = -1;
+			for (EndPointMapType::iterator it = line_range.first; it != line_range.second; ++ it) {
+				if (it->second == i_candidate) {
+					// closing the loop.
+					goto end_of_poly;
+				}
+				if (line_processed[it->second])
+					continue;
+				if (i_next == -1) {
+					i_next = it->second;
+				} else {
+					// This is a corner, where two lines meet exactly. Pick the line, which encloses a smallest angle with
+					// the current edge.
+					const Line &line_current = lines[i_line_current];
+					const Line &line_next = lines[it->second];
+					const Vector v1 = line_current.vector();
+					const Vector v2 = line_next.vector();
+					int64_t cross = int64_t(v1.x) * int64_t(v2.y) - int64_t(v2.x) * int64_t(v1.y);
+					if (cross > 0) {
+						// This has to be a convex right angle. There is no better next line.
+						i_next = it->second;
+						break;
+					}
+				}
+			}
+			line_processed[i_next] = true;
+			i_line_current = i_next;
+			poly.points.push_back(lines[i_line_current].b);
+		}
+	end_of_poly:
+		out.push_back(std::move(poly));
+	}
+
+	// 3) Scale the polygons back into world, shrink slightly and remove collinear points.
+	for (size_t i = 0; i < out.size(); ++ i) {
+		Polygon &poly = out[i];
+		for (size_t j = 0; j < poly.points.size(); ++ j) {
+			Point &p = poly.points[j];
+			p.x *= m_resolution;
+			p.y *= m_resolution;
+			p.x += m_bbox.min.x;
+			p.y += m_bbox.min.y;
+		}
+		// Shrink the contour slightly, so if the same contour gets discretized and simplified again, one will get the same result.
+		// Remove collineaer points.
+		Points pts;
+		pts.reserve(poly.points.size());
+		coord_t downscale = 5;
+		for (size_t j = 0; j < poly.points.size(); ++ j) {
+			size_t j0 = (j == 0) ? poly.points.size() - 1 : j - 1;
+			size_t j2 = (j + 1 == poly.points.size()) ? 0 : j + 1;
+			Point  v  = poly.points[j2] - poly.points[j0];
+			if (v.x != 0 && v.y != 0) {
+				// This is a corner point. Copy it to the output contour.
+				Point p = poly.points[j];
+				p.y += (v.x < 0) ? downscale : -downscale;
+				p.x += (v.y > 0) ? downscale : -downscale;
+				pts.push_back(p);
+			} 
+		}
+		poly.points = std::move(pts);
+	}
+	return out;
+}
+
 #ifdef SLIC3R_GUI
 void EdgeGrid::save_png(const EdgeGrid::Grid &grid, const BoundingBox &bbox, coord_t resolution, const char *path)
 {
diff --git a/xs/src/libslic3r/EdgeGrid.hpp b/xs/src/libslic3r/EdgeGrid.hpp
index 626a430cd..2473ce423 100644
--- a/xs/src/libslic3r/EdgeGrid.hpp
+++ b/xs/src/libslic3r/EdgeGrid.hpp
@@ -12,11 +12,14 @@
 namespace Slic3r {
 namespace EdgeGrid {
 
-struct Grid
+class Grid
 {
+public:
 	Grid();
 	~Grid();
 
+	void set_bbox(const BoundingBox &bbox) { m_bbox = bbox; }
+
 	void create(const Polygons &polygons, coord_t resolution);
 	void create(const ExPolygon &expoly, coord_t resolution);
 	void create(const ExPolygons &expolygons, coord_t resolution);
@@ -54,6 +57,9 @@ struct Grid
 	const size_t		rows() const { return m_rows; }
 	const size_t		cols() const { return m_cols; }
 
+	// For supports: Contours enclosing the rasterized edges.
+	Polygons 			contours_simplified() const;
+
 protected:
 	struct Cell {
 		Cell() : begin(0), end(0) {}
@@ -65,6 +71,18 @@ protected:
 #if 0
 	bool line_cell_intersect(const Point &p1, const Point &p2, const Cell &cell);
 #endif
+	bool cell_inside_or_crossing(int r, int c) const
+	{
+		if (r < 0 || r >= m_rows ||
+			c < 0 || c >= m_cols)
+			// The cell is outside the domain. Hoping that the contours were correctly oriented, so
+			// there is a CCW outmost contour so the out of domain cells are outside.
+			return false;
+		const Cell &cell = m_cells[r * m_cols + c];
+		return 
+			(cell.begin < cell.end) || 
+			(! m_signed_distance_field.empty() && m_signed_distance_field[r * (m_cols + 1) + c] <= 0.f);
+	}
 
 	// Bounding box around the contours.
 	BoundingBox 								m_bbox;
diff --git a/xs/src/libslic3r/ExPolygon.cpp b/xs/src/libslic3r/ExPolygon.cpp
index fbb616ebb..06ba28ed2 100644
--- a/xs/src/libslic3r/ExPolygon.cpp
+++ b/xs/src/libslic3r/ExPolygon.cpp
@@ -99,9 +99,7 @@ ExPolygon::contains(const Line &line) const
 bool
 ExPolygon::contains(const Polyline &polyline) const
 {
-    Polylines pl_out;
-    diff((Polylines)polyline, *this, &pl_out);
-    return pl_out.empty();
+    return diff_pl((Polylines)polyline, *this).empty();
 }
 
 bool
@@ -115,8 +113,7 @@ ExPolygon::contains(const Polylines &polylines) const
     svg.draw_outline(*this);
     svg.draw(polylines, "blue");
     #endif
-    Polylines pl_out;
-    diff(polylines, *this, &pl_out);
+    Polylines pl_out = diff_pl(polylines, *this);
     #if 0
     svg.draw(pl_out, "red");
     #endif
@@ -162,8 +159,7 @@ ExPolygon::overlaps(const ExPolygon &other) const
     svg.draw_outline(*this);
     svg.draw_outline(other, "blue");
     #endif
-    Polylines pl_out;
-    intersection((Polylines)other, *this, &pl_out);
+    Polylines pl_out = intersection_pl((Polylines)other, *this);
     #if 0
     svg.draw(pl_out, "red");
     #endif
@@ -396,11 +392,8 @@ ExPolygon::get_trapezoids2(Polygons* polygons) const
         poly[3].y = bb.max.y;
         
         // intersect with this expolygon
-        Polygons trapezoids;
-        intersection<Polygons,Polygons>(poly, *this, &trapezoids);
-        
         // append results to return value
-        polygons->insert(polygons->end(), trapezoids.begin(), trapezoids.end());
+        polygons_append(*polygons, intersection(poly, to_polygons(*this)));
     }
 }
 
@@ -434,16 +427,13 @@ ExPolygon::triangulate_pp(Polygons* polygons) const
     // convert polygons
     std::list<TPPLPoly> input;
     
-    Polygons pp = *this;
-    simplify_polygons(pp, &pp, true);
-    ExPolygons expp;
-    union_(pp, &expp);
+    ExPolygons expp = union_ex(simplify_polygons(to_polygons(*this), true));
     
     for (ExPolygons::const_iterator ex = expp.begin(); ex != expp.end(); ++ex) {
         // contour
         {
             TPPLPoly p;
-            p.Init(ex->contour.points.size());
+            p.Init(int(ex->contour.points.size()));
             //printf(PRINTF_ZU "\n0\n", ex->contour.points.size());
             for (Points::const_iterator point = ex->contour.points.begin(); point != ex->contour.points.end(); ++point) {
                 p[ point-ex->contour.points.begin() ].x = point->x;
@@ -480,8 +470,8 @@ ExPolygon::triangulate_pp(Polygons* polygons) const
         Polygon p;
         p.points.resize(num_points);
         for (long i = 0; i < num_points; ++i) {
-            p.points[i].x = (*poly)[i].x;
-            p.points[i].y = (*poly)[i].y;
+            p.points[i].x = coord_t((*poly)[i].x);
+            p.points[i].y = coord_t((*poly)[i].y);
         }
         polygons->push_back(p);
     }
@@ -490,8 +480,7 @@ ExPolygon::triangulate_pp(Polygons* polygons) const
 void
 ExPolygon::triangulate_p2t(Polygons* polygons) const
 {
-    ExPolygons expp;
-    simplify_polygons(*this, &expp, true);
+    ExPolygons expp = simplify_polygons_ex(*this, true);
     
     for (ExPolygons::const_iterator ex = expp.begin(); ex != expp.end(); ++ex) {
         // TODO: prevent duplicate points
diff --git a/xs/src/libslic3r/ExPolygon.hpp b/xs/src/libslic3r/ExPolygon.hpp
index 7f76cfe4e..7d1a609ea 100644
--- a/xs/src/libslic3r/ExPolygon.hpp
+++ b/xs/src/libslic3r/ExPolygon.hpp
@@ -293,37 +293,37 @@ extern bool        remove_sticks(ExPolygon &poly);
 #include <boost/polygon/polygon.hpp>
 namespace boost { namespace polygon {
     template <>
-        struct polygon_traits<ExPolygon> {
+        struct polygon_traits<Slic3r::ExPolygon> {
         typedef coord_t coordinate_type;
-        typedef Points::const_iterator iterator_type;
-        typedef Point point_type;
+        typedef Slic3r::Points::const_iterator iterator_type;
+        typedef Slic3r::Point point_type;
 
         // Get the begin iterator
-        static inline iterator_type begin_points(const ExPolygon& t) {
+        static inline iterator_type begin_points(const Slic3r::ExPolygon& t) {
             return t.contour.points.begin();
         }
 
         // Get the end iterator
-        static inline iterator_type end_points(const ExPolygon& t) {
+        static inline iterator_type end_points(const Slic3r::ExPolygon& t) {
             return t.contour.points.end();
         }
 
         // Get the number of sides of the polygon
-        static inline std::size_t size(const ExPolygon& t) {
+        static inline std::size_t size(const Slic3r::ExPolygon& t) {
             return t.contour.points.size();
         }
 
         // Get the winding direction of the polygon
-        static inline winding_direction winding(const ExPolygon& t) {
+        static inline winding_direction winding(const Slic3r::ExPolygon& t) {
             return unknown_winding;
         }
     };
 
     template <>
-    struct polygon_mutable_traits<ExPolygon> {
+    struct polygon_mutable_traits<Slic3r::ExPolygon> {
         //expects stl style iterators
         template <typename iT>
-        static inline ExPolygon& set_points(ExPolygon& expolygon, iT input_begin, iT input_end) {
+        static inline Slic3r::ExPolygon& set_points(Slic3r::ExPolygon& expolygon, iT input_begin, iT input_end) {
             expolygon.contour.points.assign(input_begin, input_end);
             // skip last point since Boost will set last point = first point
             expolygon.contour.points.pop_back();
@@ -333,27 +333,27 @@ namespace boost { namespace polygon {
     
     
     template <>
-    struct geometry_concept<ExPolygon> { typedef polygon_with_holes_concept type; };
+    struct geometry_concept<Slic3r::ExPolygon> { typedef polygon_with_holes_concept type; };
 
     template <>
-    struct polygon_with_holes_traits<ExPolygon> {
-        typedef Polygons::const_iterator iterator_holes_type;
-        typedef Polygon hole_type;
-        static inline iterator_holes_type begin_holes(const ExPolygon& t) {
+    struct polygon_with_holes_traits<Slic3r::ExPolygon> {
+        typedef Slic3r::Polygons::const_iterator iterator_holes_type;
+        typedef Slic3r::Polygon hole_type;
+        static inline iterator_holes_type begin_holes(const Slic3r::ExPolygon& t) {
             return t.holes.begin();
         }
-        static inline iterator_holes_type end_holes(const ExPolygon& t) {
+        static inline iterator_holes_type end_holes(const Slic3r::ExPolygon& t) {
             return t.holes.end();
         }
-        static inline unsigned int size_holes(const ExPolygon& t) {
+        static inline unsigned int size_holes(const Slic3r::ExPolygon& t) {
             return (int)t.holes.size();
         }
     };
 
     template <>
-    struct polygon_with_holes_mutable_traits<ExPolygon> {
+    struct polygon_with_holes_mutable_traits<Slic3r::ExPolygon> {
          template <typename iT>
-         static inline ExPolygon& set_holes(ExPolygon& t, iT inputBegin, iT inputEnd) {
+         static inline Slic3r::ExPolygon& set_holes(Slic3r::ExPolygon& t, iT inputBegin, iT inputEnd) {
               t.holes.assign(inputBegin, inputEnd);
               return t;
          }
@@ -361,32 +361,32 @@ namespace boost { namespace polygon {
     
     //first we register CPolygonSet as a polygon set
     template <>
-    struct geometry_concept<ExPolygons> { typedef polygon_set_concept type; };
+    struct geometry_concept<Slic3r::ExPolygons> { typedef polygon_set_concept type; };
 
     //next we map to the concept through traits
     template <>
-    struct polygon_set_traits<ExPolygons> {
+    struct polygon_set_traits<Slic3r::ExPolygons> {
         typedef coord_t coordinate_type;
-        typedef ExPolygons::const_iterator iterator_type;
-        typedef ExPolygons operator_arg_type;
+        typedef Slic3r::ExPolygons::const_iterator iterator_type;
+        typedef Slic3r::ExPolygons operator_arg_type;
 
-        static inline iterator_type begin(const ExPolygons& polygon_set) {
+        static inline iterator_type begin(const Slic3r::ExPolygons& polygon_set) {
             return polygon_set.begin();
         }
 
-        static inline iterator_type end(const ExPolygons& polygon_set) {
+        static inline iterator_type end(const Slic3r::ExPolygons& polygon_set) {
             return polygon_set.end();
         }
 
         //don't worry about these, just return false from them
-        static inline bool clean(const ExPolygons& polygon_set) { return false; }
-        static inline bool sorted(const ExPolygons& polygon_set) { return false; }
+        static inline bool clean(const Slic3r::ExPolygons& polygon_set) { return false; }
+        static inline bool sorted(const Slic3r::ExPolygons& polygon_set) { return false; }
     };
 
     template <>
-    struct polygon_set_mutable_traits<ExPolygons> {
+    struct polygon_set_mutable_traits<Slic3r::ExPolygons> {
         template <typename input_iterator_type>
-        static inline void set(ExPolygons& expolygons, input_iterator_type input_begin, input_iterator_type input_end) {
+        static inline void set(Slic3r::ExPolygons& expolygons, input_iterator_type input_begin, input_iterator_type input_end) {
             expolygons.assign(input_begin, input_end);
         }
     };
diff --git a/xs/src/libslic3r/ExtrusionEntity.cpp b/xs/src/libslic3r/ExtrusionEntity.cpp
index 624cdccbc..9f59ca386 100644
--- a/xs/src/libslic3r/ExtrusionEntity.cpp
+++ b/xs/src/libslic3r/ExtrusionEntity.cpp
@@ -13,19 +13,13 @@ namespace Slic3r {
 void
 ExtrusionPath::intersect_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const
 {
-    // perform clipping
-    Polylines clipped;
-    intersection<Polylines,Polylines>(this->polyline, collection, &clipped);
-    return this->_inflate_collection(clipped, retval);
+    this->_inflate_collection(intersection_pl(this->polyline, collection), retval);
 }
 
 void
 ExtrusionPath::subtract_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const
 {
-    // perform clipping
-    Polylines clipped;
-    diff<Polylines,Polylines>(this->polyline, collection, &clipped);
-    return this->_inflate_collection(clipped, retval);
+    this->_inflate_collection(diff_pl(this->polyline, collection), retval);
 }
 
 void
@@ -58,9 +52,7 @@ ExtrusionPath::_inflate_collection(const Polylines &polylines, ExtrusionEntityCo
 
 void ExtrusionPath::polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const
 {
-    Polygons tmp;
-    offset(this->polyline, &tmp, scale_(this->width/2) + scaled_epsilon);
-    polygons_append(out, STDMOVE(tmp));
+    polygons_append(out, offset(this->polyline, float(scale_(this->width/2)) + scaled_epsilon));
 }
 
 void ExtrusionPath::polygons_covered_by_spacing(Polygons &out, const float scaled_epsilon) const
@@ -68,9 +60,7 @@ void ExtrusionPath::polygons_covered_by_spacing(Polygons &out, const float scale
     // Instantiating the Flow class to get the line spacing.
     // Don't know the nozzle diameter, setting to zero. It shall not matter it shall be optimized out by the compiler.
     Flow flow(this->width, this->height, 0.f, this->is_bridge());
-    Polygons tmp;
-    offset(this->polyline, &tmp, 0.5f * flow.scaled_spacing() + scaled_epsilon);
-    polygons_append(out, STDMOVE(tmp));
+    polygons_append(out, offset(this->polyline, 0.5f * float(flow.scaled_spacing()) + scaled_epsilon));
 }
 
 bool
diff --git a/xs/src/libslic3r/ExtrusionEntity.hpp b/xs/src/libslic3r/ExtrusionEntity.hpp
index 7ce053a10..dfc28d035 100644
--- a/xs/src/libslic3r/ExtrusionEntity.hpp
+++ b/xs/src/libslic3r/ExtrusionEntity.hpp
@@ -194,6 +194,26 @@ class ExtrusionLoop : public ExtrusionEntity
     Polyline as_polyline() const { return this->polygon().split_at_first_point(); }
 };
 
+inline void extrusion_paths_append(ExtrusionPaths &dst, Polylines &polylines, ExtrusionRole role, double mm3_per_mm, float width, float height)
+{
+    dst.reserve(dst.size() + polylines.size());
+    for (Polylines::const_iterator it_polyline = polylines.begin(); it_polyline != polylines.end(); ++ it_polyline) {
+        dst.push_back(ExtrusionPath(role, mm3_per_mm, width, height));
+        dst.back().polyline = *it_polyline;
+    }
+}
+
+#if SLIC3R_CPPVER >= 11
+inline void extrusion_paths_append(ExtrusionPaths &dst, Polylines &&polylines, ExtrusionRole role, double mm3_per_mm, float width, float height)
+{
+    dst.reserve(dst.size() + polylines.size());
+    for (Polylines::const_iterator it_polyline = polylines.begin(); it_polyline != polylines.end(); ++ it_polyline) {
+        dst.push_back(ExtrusionPath(role, mm3_per_mm, width, height));
+        dst.back().polyline = std::move(*it_polyline);
+    }
+}
+#endif // SLIC3R_CPPVER >= 11
+
 inline void extrusion_entities_append_paths(ExtrusionEntitiesPtr &dst, Polylines &polylines, ExtrusionRole role, double mm3_per_mm, float width, float height)
 {
     dst.reserve(dst.size() + polylines.size());
diff --git a/xs/src/libslic3r/ExtrusionSimulator.cpp b/xs/src/libslic3r/ExtrusionSimulator.cpp
index 0c86f073e..3752caed4 100644
--- a/xs/src/libslic3r/ExtrusionSimulator.cpp
+++ b/xs/src/libslic3r/ExtrusionSimulator.cpp
@@ -803,7 +803,7 @@ void gcode_spread_points(
 				const Cell &cell = cells[i];
 				acc[cell.idx.y()][cell.idx.x()] = (1.f - cell.fraction_covered) * cell.volume + cell.fraction_covered * cell.area * height_avg;
 			}
-		} else if (simulationType == ExtrusionSimulationSpreadExcess) {
+		} else if (simulationType == Slic3r::ExtrusionSimulationSpreadExcess) {
 			// The volume under the circle does not fit.
 			// 1) Fill the underfilled cells and remove them from the list.
 			float volume_borrowed_total = 0.;
diff --git a/xs/src/libslic3r/ExtrusionSimulator.hpp b/xs/src/libslic3r/ExtrusionSimulator.hpp
index 8b956ec11..040406766 100644
--- a/xs/src/libslic3r/ExtrusionSimulator.hpp
+++ b/xs/src/libslic3r/ExtrusionSimulator.hpp
@@ -13,7 +13,7 @@ enum ExtrusionSimulationType
     ExtrusionSimulationDontSpread,
     ExtrisopmSimulationSpreadNotOverfilled,
     ExtrusionSimulationSpreadFull,
-    ExtrusionSimulationSpreadExcess,
+    ExtrusionSimulationSpreadExcess
 };
 
 // An opaque class, to keep the boost stuff away from the header.
diff --git a/xs/src/libslic3r/Fill/Fill3DHoneycomb.cpp b/xs/src/libslic3r/Fill/Fill3DHoneycomb.cpp
index c37328c69..c763952d6 100644
--- a/xs/src/libslic3r/Fill/Fill3DHoneycomb.cpp
+++ b/xs/src/libslic3r/Fill/Fill3DHoneycomb.cpp
@@ -168,7 +168,7 @@ void Fill3DHoneycomb::_fill_surface_single(
         it->translate(bb.min.x, bb.min.y);
 
     // clip pattern to boundaries
-    intersection(polylines, (Polygons)expolygon, &polylines);
+    polylines = intersection_pl(polylines, (Polygons)expolygon);
 
     // connect lines
     if (! params.dont_connect && ! polylines.empty()) { // prevent calling leftmost_point() on empty collections
diff --git a/xs/src/libslic3r/Fill/FillBase.cpp b/xs/src/libslic3r/Fill/FillBase.cpp
index d19615aa9..7258e11fe 100644
--- a/xs/src/libslic3r/Fill/FillBase.cpp
+++ b/xs/src/libslic3r/Fill/FillBase.cpp
@@ -45,8 +45,7 @@ Fill* Fill::new_from_type(const std::string &type)
 Polylines Fill::fill_surface(const Surface *surface, const FillParams &params)
 {
     // Perform offset.
-    Slic3r::ExPolygons expp;
-    offset(surface->expolygon, &expp, -0.5*scale_(this->spacing));
+    Slic3r::ExPolygons expp = offset_ex(surface->expolygon, float(-0.5*scale_(this->spacing)));
     // Create the infills for each of the regions.
     Polylines polylines_out;
     for (size_t i = 0; i < expp.size(); ++ i)
diff --git a/xs/src/libslic3r/Fill/FillConcentric.cpp b/xs/src/libslic3r/Fill/FillConcentric.cpp
index 27914866d..156e3478a 100644
--- a/xs/src/libslic3r/Fill/FillConcentric.cpp
+++ b/xs/src/libslic3r/Fill/FillConcentric.cpp
@@ -33,7 +33,7 @@ void FillConcentric::_fill_surface_single(
 
     // generate paths from the outermost to the innermost, to avoid
     // adhesion problems of the first central tiny loops
-    union_pt_chained(loops, &loops, false);
+    loops = union_pt_chained(loops, false);
     
     // split paths using a nearest neighbor search
     size_t iPathFirst = polylines_out.size();
diff --git a/xs/src/libslic3r/Fill/FillHoneycomb.cpp b/xs/src/libslic3r/Fill/FillHoneycomb.cpp
index b264d80cc..22dea85da 100644
--- a/xs/src/libslic3r/Fill/FillHoneycomb.cpp
+++ b/xs/src/libslic3r/Fill/FillHoneycomb.cpp
@@ -93,7 +93,7 @@ void FillHoneycomb::_fill_surface_single(
             Polylines p;
             for (Polygons::iterator it = polygons.begin(); it != polygons.end(); ++ it)
                 p.push_back((Polyline)(*it));
-            intersection(p, (Polygons)expolygon, &paths);
+            paths = intersection_pl(p, to_polygons(expolygon));
         }
 
         // connect paths
@@ -122,7 +122,7 @@ void FillHoneycomb::_fill_surface_single(
         }
         
         // clip paths again to prevent connection segments from crossing the expolygon boundaries
-        intersection(paths, to_polygons(offset_ex(expolygon, SCALED_EPSILON)), &paths);
+        paths = intersection_pl(paths, to_polygons(offset_ex(expolygon, SCALED_EPSILON)));
         // Move the polylines to the output, avoid a deep copy.
         size_t j = polylines_out.size();
         polylines_out.resize(j + paths.size(), Polyline());
diff --git a/xs/src/libslic3r/Fill/FillPlanePath.cpp b/xs/src/libslic3r/Fill/FillPlanePath.cpp
index 34e155a06..f71ef95a1 100644
--- a/xs/src/libslic3r/Fill/FillPlanePath.cpp
+++ b/xs/src/libslic3r/Fill/FillPlanePath.cpp
@@ -44,7 +44,7 @@ void FillPlanePath::_fill_surface_single(
                 coord_t(floor(it->x * distance_between_lines + 0.5)), 
                 coord_t(floor(it->y * distance_between_lines + 0.5))));
 //      intersection(polylines_src, offset((Polygons)expolygon, scale_(0.02)), &polylines);
-        intersection(polylines, (Polygons)expolygon, &polylines);
+        polylines = intersection_pl(polylines, to_polygons(expolygon));
 
 /*        
         if (1) {
diff --git a/xs/src/libslic3r/Fill/FillRectilinear.cpp b/xs/src/libslic3r/Fill/FillRectilinear.cpp
index 5559a5437..991adc0b3 100644
--- a/xs/src/libslic3r/Fill/FillRectilinear.cpp
+++ b/xs/src/libslic3r/Fill/FillRectilinear.cpp
@@ -63,7 +63,7 @@ void FillRectilinear::_fill_surface_single(
         pts.push_back(it->a);
         pts.push_back(it->b);
     }
-    Polylines polylines = intersection(polylines_src, offset((Polygons)expolygon, scale_(0.02)), false);
+    Polylines polylines = intersection_pl(polylines_src, offset(to_polygons(expolygon), scale_(0.02)), false);
 
     // FIXME Vojtech: This is only performed for horizontal lines, not for the vertical lines!
     const float INFILL_OVERLAP_OVER_SPACING = 0.3f;
diff --git a/xs/src/libslic3r/Fill/FillRectilinear2.cpp b/xs/src/libslic3r/Fill/FillRectilinear2.cpp
index 766f389b0..3f7232e06 100644
--- a/xs/src/libslic3r/Fill/FillRectilinear2.cpp
+++ b/xs/src/libslic3r/Fill/FillRectilinear2.cpp
@@ -372,11 +372,9 @@ public:
         bool sticks_removed = remove_sticks(polygons_src);
 //        if (sticks_removed) printf("Sticks removed!\n");
         polygons_outer = offset(polygons_src, aoffset1,
-            CLIPPER_OFFSET_SCALE,
             ClipperLib::jtMiter,
             mitterLimit);
         polygons_inner = offset(polygons_outer, aoffset2 - aoffset1,
-            CLIPPER_OFFSET_SCALE,
             ClipperLib::jtMiter,
             mitterLimit);
 		// Filter out contours with zero area or small area, contours with 2 points only.
diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp
index ee76e75d9..e23e53465 100644
--- a/xs/src/libslic3r/GCode.cpp
+++ b/xs/src/libslic3r/GCode.cpp
@@ -315,8 +315,7 @@ GCode::change_layer(const Layer &layer)
     
     // avoid computing islands and overhangs if they're not needed
     if (this->config.avoid_crossing_perimeters) {
-        ExPolygons islands;
-        union_(layer.slices, &islands, true);
+        ExPolygons islands = union_ex(layer.slices, true);
         this->avoid_crossing_perimeters.init_layer_mp(islands);
     }
     
diff --git a/xs/src/libslic3r/Layer.cpp b/xs/src/libslic3r/Layer.cpp
index d3399d46e..929460169 100644
--- a/xs/src/libslic3r/Layer.cpp
+++ b/xs/src/libslic3r/Layer.cpp
@@ -105,7 +105,7 @@ Layer::make_slices()
         FOREACH_LAYERREGION(this, layerm) {
             polygons_append(slices_p, to_polygons((*layerm)->slices));
         }
-        union_(slices_p, &slices);
+        slices = union_ex(slices_p);
     }
     
     this->slices.expolygons.clear();
@@ -132,15 +132,11 @@ Layer::merge_slices()
     if (this->regions.size() == 1) {
         // Optimization, also more robust. Don't merge classified pieces of layerm->slices,
         // but use the non-split islands of a layer. For a single region print, these shall be equal.
-        this->regions.front()->slices.surfaces.clear();
-        surfaces_append(this->regions.front()->slices.surfaces, this->slices.expolygons, stInternal);
+        this->regions.front()->slices.set(this->slices.expolygons, stInternal);
     } else {
         FOREACH_LAYERREGION(this, layerm) {
-            ExPolygons expp;
             // without safety offset, artifacts are generated (GH #2494)
-            union_(to_polygons(STDMOVE((*layerm)->slices.surfaces)), &expp, true);
-            (*layerm)->slices.surfaces.clear();
-            surfaces_append((*layerm)->slices.surfaces, expp, stInternal);
+            (*layerm)->slices.set(union_ex(to_polygons(STDMOVE((*layerm)->slices.surfaces)), true), stInternal);
         }
     }
 }
@@ -223,7 +219,7 @@ Layer::make_perimeters()
                 }
                 // merge the surfaces assigned to each group
                 for (std::map<unsigned short,Surfaces>::const_iterator it = slices.begin(); it != slices.end(); ++it)
-                    surfaces_append(new_slices.surfaces, union_ex(it->second, true), it->second.front());
+                    new_slices.append(union_ex(it->second, true), it->second.front());
             }
             
             // make perimeters
@@ -236,8 +232,7 @@ Layer::make_perimeters()
                     // Separate the fill surfaces.
                     ExPolygons expp = intersection_ex(to_polygons(fill_surfaces), (*l)->slices);
                     (*l)->fill_expolygons = expp;
-                    (*l)->fill_surfaces.surfaces.clear();
-                    surfaces_append((*l)->fill_surfaces.surfaces, STDMOVE(expp), fill_surfaces.surfaces.front());
+                    (*l)->fill_surfaces.set(STDMOVE(expp), fill_surfaces.surfaces.front());
                 }
             }
         }
diff --git a/xs/src/libslic3r/Layer.hpp b/xs/src/libslic3r/Layer.hpp
index e59f0c9e1..a5ba3f804 100644
--- a/xs/src/libslic3r/Layer.hpp
+++ b/xs/src/libslic3r/Layer.hpp
@@ -11,9 +11,6 @@
 
 namespace Slic3r {
 
-typedef std::pair<coordf_t,coordf_t> t_layer_height_range;
-typedef std::map<t_layer_height_range,coordf_t> t_layer_height_ranges;
-
 class Layer;
 class PrintRegion;
 class PrintObject;
diff --git a/xs/src/libslic3r/LayerRegion.cpp b/xs/src/libslic3r/LayerRegion.cpp
index 7c84629e2..f0063114a 100644
--- a/xs/src/libslic3r/LayerRegion.cpp
+++ b/xs/src/libslic3r/LayerRegion.cpp
@@ -52,8 +52,7 @@ void LayerRegion::slices_to_fill_surfaces_clipped()
     Polygons fill_boundaries = to_polygons(this->fill_expolygons);
     this->fill_surfaces.surfaces.clear();
     for (Surfaces::const_iterator surface = this->slices.surfaces.begin(); surface != this->slices.surfaces.end(); ++ surface)
-        surfaces_append(
-            this->fill_surfaces.surfaces,
+        this->fill_surfaces.append(
             intersection_ex(to_polygons(surface->expolygon), fill_boundaries),
             surface->surface_type);
 }
@@ -91,9 +90,9 @@ LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollection*
     g.process();
 }
 
-//#define EXTERNAL_SURFACES_OFFSET_PARAMETERS CLIPPER_OFFSET_SCALE, ClipperLib::jtMiter, 3.
-//#define EXTERNAL_SURFACES_OFFSET_PARAMETERS CLIPPER_OFFSET_SCALE, ClipperLib::jtMiter, 1.5
-#define EXTERNAL_SURFACES_OFFSET_PARAMETERS CLIPPER_OFFSET_SCALE, ClipperLib::jtSquare, 0.
+//#define EXTERNAL_SURFACES_OFFSET_PARAMETERS ClipperLib::jtMiter, 3.
+//#define EXTERNAL_SURFACES_OFFSET_PARAMETERS ClipperLib::jtMiter, 1.5
+#define EXTERNAL_SURFACES_OFFSET_PARAMETERS ClipperLib::jtSquare, 0.
 
 void
 LayerRegion::process_external_surfaces(const Layer* lower_layer)
@@ -194,7 +193,7 @@ LayerRegion::process_external_surfaces(const Layer* lower_layer)
                         break;
                     }
                 // Grown by 3mm.
-                Polygons polys = offset(bridges[i].expolygon, float(margin), EXTERNAL_SURFACES_OFFSET_PARAMETERS);
+                Polygons polys = offset(to_polygons(bridges[i].expolygon), float(margin), EXTERNAL_SURFACES_OFFSET_PARAMETERS);
                 if (idx_island == -1) {
                     printf("Bridge did not fall into the source region!\r\n");
                 } else {
@@ -262,9 +261,7 @@ LayerRegion::process_external_surfaces(const Layer* lower_layer)
                 BridgeDetector bd(
                     initial,
                     lower_layer->slices,
-                    //FIXME parameters are not correct!
-                    // flow(FlowRole role, bool bridge = false, double width = -1) const;
-                    this->flow(frInfill, true, this->layer()->height).scaled_width()
+                    this->flow(frInfill, true).scaled_width()
                 );
                 #ifdef SLIC3R_DEBUG
                 printf("Processing bridge at layer " PRINTF_ZU ":\n", this->layer()->id());
diff --git a/xs/src/libslic3r/Line.hpp b/xs/src/libslic3r/Line.hpp
index 42e811449..6c40b062f 100644
--- a/xs/src/libslic3r/Line.hpp
+++ b/xs/src/libslic3r/Line.hpp
@@ -76,20 +76,20 @@ class Linef3
     void scale(double factor);
 };
 
-}
+} // namespace Slic3r
 
 // start Boost
 #include <boost/polygon/polygon.hpp>
 namespace boost { namespace polygon {
     template <>
-    struct geometry_concept<Line> { typedef segment_concept type; };
+    struct geometry_concept<Slic3r::Line> { typedef segment_concept type; };
 
     template <>
-    struct segment_traits<Line> {
+    struct segment_traits<Slic3r::Line> {
         typedef coord_t coordinate_type;
-        typedef Point point_type;
+        typedef Slic3r::Point point_type;
     
-        static inline point_type get(const Line& line, direction_1d dir) {
+        static inline point_type get(const Slic3r::Line& line, direction_1d dir) {
             return dir.to_int() ? line.b : line.a;
         }
     };
diff --git a/xs/src/libslic3r/Model.hpp b/xs/src/libslic3r/Model.hpp
index b2a9d0b53..963bd7c85 100644
--- a/xs/src/libslic3r/Model.hpp
+++ b/xs/src/libslic3r/Model.hpp
@@ -6,6 +6,7 @@
 #include "Layer.hpp"
 #include "Point.hpp"
 #include "TriangleMesh.hpp"
+#include "Slicing.hpp"
 #include <map>
 #include <string>
 #include <utility>
diff --git a/xs/src/libslic3r/MotionPlanner.cpp b/xs/src/libslic3r/MotionPlanner.cpp
index 21afb37c4..9fc1695c0 100644
--- a/xs/src/libslic3r/MotionPlanner.cpp
+++ b/xs/src/libslic3r/MotionPlanner.cpp
@@ -142,7 +142,7 @@ MotionPlanner::shortest_path(const Point &from, const Point &to)
     {
         // grow our environment slightly in order for simplify_by_visibility()
         // to work best by considering moves on boundaries valid as well
-        ExPolygonCollection grown_env(offset_ex(env.env, +SCALED_EPSILON));
+        ExPolygonCollection grown_env(offset_ex(env.env.expolygons, +SCALED_EPSILON));
         
         if (island_idx == -1) {
             /*  If 'from' or 'to' are not inside our env, they were connected using the 
@@ -155,12 +155,12 @@ MotionPlanner::shortest_path(const Point &from, const Point &to)
             if (!grown_env.contains(from)) {
                 // delete second point while the line connecting first to third crosses the
                 // boundaries as many times as the current first to second
-                while (polyline.points.size() > 2 && intersection((Lines)Line(from, polyline.points[2]), grown_env).size() == 1) {
+                while (polyline.points.size() > 2 && intersection_ln((Lines)Line(from, polyline.points[2]), grown_env).size() == 1) {
                     polyline.points.erase(polyline.points.begin() + 1);
                 }
             }
             if (!grown_env.contains(to)) {
-                while (polyline.points.size() > 2 && intersection((Lines)Line(*(polyline.points.end() - 3), to), grown_env).size() == 1) {
+                while (polyline.points.size() > 2 && intersection_ln((Lines)Line(*(polyline.points.end() - 3), to), grown_env).size() == 1) {
                     polyline.points.erase(polyline.points.end() - 2);
                 }
             }
@@ -294,7 +294,7 @@ MotionPlannerEnv::nearest_env_point(const Point &from, const Point &to) const
         size_t result = from.nearest_waypoint_index(pp, to);
         
         // as we assume 'from' is outside env, any node will require at least one crossing
-        if (intersection((Lines)Line(from, pp[result]), this->island).size() > 1) {
+        if (intersection_ln((Lines)Line(from, pp[result]), this->island).size() > 1) {
             // discard result
             pp.erase(pp.begin() + result);
         } else {
diff --git a/xs/src/libslic3r/PerimeterGenerator.cpp b/xs/src/libslic3r/PerimeterGenerator.cpp
index acdff127b..730e5e501 100644
--- a/xs/src/libslic3r/PerimeterGenerator.cpp
+++ b/xs/src/libslic3r/PerimeterGenerator.cpp
@@ -54,8 +54,7 @@ PerimeterGenerator::process()
     for (Surfaces::const_iterator surface = this->slices->surfaces.begin();
         surface != this->slices->surfaces.end(); ++surface) {
         // detect how many perimeters must be generated for this island
-        signed short loop_number = this->config->perimeters + surface->extra_perimeters;
-        loop_number--;  // 0-indexed loops
+        const int loop_number = this->config->perimeters + surface->extra_perimeters -1;  // 0-indexed loops
         
         Polygons gaps;
         
@@ -67,7 +66,7 @@ PerimeterGenerator::process()
             ThickPolylines thin_walls;
             
             // we loop one time more than needed in order to find gaps after the last perimeter was applied
-            for (signed short i = 0; i <= loop_number+1; ++i) {  // outer loop is 0
+            for (int i = 0; i <= loop_number+1; ++i) {  // outer loop is 0
                 Polygons offsets;
                 if (i == 0) {
                     // the minimum thickness of a single loop is:
@@ -170,16 +169,16 @@ PerimeterGenerator::process()
             }
             
             // nest loops: holes first
-            for (signed short d = 0; d <= loop_number; ++d) {
+            for (int d = 0; d <= loop_number; ++d) {
                 PerimeterGeneratorLoops &holes_d = holes[d];
                 
                 // loop through all holes having depth == d
-                for (signed short i = 0; i < holes_d.size(); ++i) {
+                for (int i = 0; i < (int)holes_d.size(); ++i) {
                     const PerimeterGeneratorLoop &loop = holes_d[i];
                     
                     // find the hole loop that contains this one, if any
-                    for (signed short t = d+1; t <= loop_number; ++t) {
-                        for (signed short j = 0; j < holes[t].size(); ++j) {
+                    for (int t = d+1; t <= loop_number; ++t) {
+                        for (int j = 0; j < (int)holes[t].size(); ++j) {
                             PerimeterGeneratorLoop &candidate_parent = holes[t][j];
                             if (candidate_parent.polygon.contains(loop.polygon.first_point())) {
                                 candidate_parent.children.push_back(loop);
@@ -191,8 +190,8 @@ PerimeterGenerator::process()
                     }
                     
                     // if no hole contains this hole, find the contour loop that contains it
-                    for (signed short t = loop_number; t >= 0; --t) {
-                        for (signed short j = 0; j < contours[t].size(); ++j) {
+                    for (int t = loop_number; t >= 0; --t) {
+                        for (int j = 0; j < (int)contours[t].size(); ++j) {
                             PerimeterGeneratorLoop &candidate_parent = contours[t][j];
                             if (candidate_parent.polygon.contains(loop.polygon.first_point())) {
                                 candidate_parent.children.push_back(loop);
@@ -207,16 +206,16 @@ PerimeterGenerator::process()
             }
         
             // nest contour loops
-            for (signed short d = loop_number; d >= 1; --d) {
+            for (int d = loop_number; d >= 1; --d) {
                 PerimeterGeneratorLoops &contours_d = contours[d];
                 
                 // loop through all contours having depth == d
-                for (signed short i = 0; i < contours_d.size(); ++i) {
+                for (int i = 0; i < (int)contours_d.size(); ++i) {
                     const PerimeterGeneratorLoop &loop = contours_d[i];
                 
                     // find the contour loop that contains it
-                    for (signed short t = d-1; t >= 0; --t) {
-                        for (signed short j = 0; j < contours[t].size(); ++j) {
+                    for (int t = d-1; t >= 0; --t) {
+                        for (int j = 0; j < contours[t].size(); ++j) {
                             PerimeterGeneratorLoop &candidate_parent = contours[t][j];
                             if (candidate_parent.polygon.contains(loop.polygon.first_point())) {
                                 candidate_parent.children.push_back(loop);
@@ -315,8 +314,7 @@ PerimeterGenerator::process()
             coord_t min_perimeter_infill_spacing = ispacing * (1 - INSET_OVERLAP_TOLERANCE);
             
             // append infill areas to fill_surfaces
-            surfaces_append(
-                this->fill_surfaces->surfaces, 
+            this->fill_surfaces->append(
                 offset2_ex(
                     pp,
                     -inset -min_perimeter_infill_spacing/2,
@@ -354,36 +352,24 @@ PerimeterGenerator::_traverse_loops(const PerimeterGeneratorLoops &loops,
         if (this->config->overhangs && this->layer_id > 0
             && !(this->object_config->support_material && this->object_config->support_material_contact_distance.value == 0)) {
             // get non-overhang paths by intersecting this loop with the grown lower slices
-            {
-                Polylines polylines;
-                intersection((Polygons)loop->polygon, this->_lower_slices_p, &polylines);
-                
-                for (Polylines::const_iterator polyline = polylines.begin(); polyline != polylines.end(); ++polyline) {
-                    ExtrusionPath path(role);
-                    path.polyline   = *polyline;
-                    path.mm3_per_mm = is_external ? this->_ext_mm3_per_mm           : this->_mm3_per_mm;
-                    path.width      = is_external ? this->ext_perimeter_flow.width  : this->perimeter_flow.width;
-                    path.height     = this->layer_height;
-                    paths.push_back(path);
-                }
-            }
+            extrusion_paths_append(
+                paths,
+                intersection_pl(loop->polygon, this->_lower_slices_p),
+                role,
+                is_external ? this->_ext_mm3_per_mm           : this->_mm3_per_mm,
+                is_external ? this->ext_perimeter_flow.width  : this->perimeter_flow.width,
+                this->layer_height);
             
             // get overhang paths by checking what parts of this loop fall 
             // outside the grown lower slices (thus where the distance between
             // the loop centerline and original lower slices is >= half nozzle diameter
-            {
-                Polylines polylines;
-                diff((Polygons)loop->polygon, this->_lower_slices_p, &polylines);
-                
-                for (Polylines::const_iterator polyline = polylines.begin(); polyline != polylines.end(); ++polyline) {
-                    ExtrusionPath path(erOverhangPerimeter);
-                    path.polyline   = *polyline;
-                    path.mm3_per_mm = this->_mm3_per_mm_overhang;
-                    path.width      = this->overhang_flow.width;
-                    path.height     = this->overhang_flow.height;
-                    paths.push_back(path);
-                }
-            }
+            extrusion_paths_append(
+                paths,
+                diff_pl(loop->polygon, this->_lower_slices_p),
+                erOverhangPerimeter,
+                this->_mm3_per_mm_overhang,
+                this->overhang_flow.width,
+                this->overhang_flow.height);
             
             // reapply the nearest point search for starting point
             // We allow polyline reversal because Clipper may have randomly
@@ -459,7 +445,7 @@ PerimeterGenerator::_variable_width(const ThickPolylines &polylines, ExtrusionRo
         ExtrusionPath path(role);
         ThickLines lines = p->thicklines();
         
-        for (size_t i = 0; i < lines.size(); ++i) {
+        for (int i = 0; i < (int)lines.size(); ++i) {
             const ThickLine& line = lines[i];
             
             const coordf_t line_len = line.length();
diff --git a/xs/src/libslic3r/Point.cpp b/xs/src/libslic3r/Point.cpp
index 8b71746e0..2ee5dabdd 100644
--- a/xs/src/libslic3r/Point.cpp
+++ b/xs/src/libslic3r/Point.cpp
@@ -12,12 +12,6 @@ Point::Point(double x, double y)
     this->y = lrint(y);
 }
 
-bool
-Point::operator==(const Point& rhs) const
-{
-    return this->coincides_with(rhs);
-}
-
 std::string
 Point::wkt() const
 {
diff --git a/xs/src/libslic3r/Point.hpp b/xs/src/libslic3r/Point.hpp
index 2cf046133..0405ec078 100644
--- a/xs/src/libslic3r/Point.hpp
+++ b/xs/src/libslic3r/Point.hpp
@@ -36,7 +36,7 @@ class Point
     static Point new_scale(coordf_t x, coordf_t y) {
         return Point(scale_(x), scale_(y));
     };
-    bool operator==(const Point& rhs) const;
+    bool operator==(const Point& rhs) const { return this->x == rhs.x && this->y == rhs.y; }
     std::string wkt() const;
     std::string dump_perl() const;
     void scale(double factor);
@@ -70,6 +70,12 @@ inline Point operator+(const Point& point1, const Point& point2) { return Point(
 inline Point operator-(const Point& point1, const Point& point2) { return Point(point1.x - point2.x, point1.y - point2.y); }
 inline Point operator*(double scalar, const Point& point2) { return Point(scalar * point2.x, scalar * point2.y); }
 
+struct PointHash {
+    size_t operator()(const Point &pt) const {
+        return std::hash<coord_t>()(pt.x) ^ std::hash<coord_t>()(pt.y);
+    }
+};
+
 class Point3 : public Point
 {
     public:
@@ -105,6 +111,9 @@ class Pointf
 inline Pointf operator+(const Pointf& point1, const Pointf& point2) { return Pointf(point1.x + point2.x, point1.y + point2.y); }
 inline Pointf operator-(const Pointf& point1, const Pointf& point2) { return Pointf(point1.x - point2.x, point1.y - point2.y); }
 inline Pointf operator*(double scalar, const Pointf& point2) { return Pointf(scalar * point2.x, scalar * point2.y); }
+inline Pointf operator*(const Pointf& point2, double scalar) { return Pointf(scalar * point2.x, scalar * point2.y); }
+inline coordf_t cross(const Pointf &v1, const Pointf &v2) { return v1.x * v2.y - v1.y * v2.x; }
+inline coordf_t dot(const Pointf &v1, const Pointf &v2) { return v1.x * v1.y + v2.x * v2.y; }
 
 class Pointf3 : public Pointf
 {
@@ -122,7 +131,7 @@ class Pointf3 : public Pointf
     Vectorf3 vector_to(const Pointf3 &point) const;
 };
 
-}
+} // namespace Slic3r
 
 // start Boost
 #include <boost/version.hpp>
@@ -146,28 +155,28 @@ namespace boost { namespace polygon {
 #endif
 
     template <>
-    struct geometry_concept<Point> { typedef point_concept type; };
+    struct geometry_concept<Slic3r::Point> { typedef point_concept type; };
    
     template <>
-    struct point_traits<Point> {
+    struct point_traits<Slic3r::Point> {
         typedef coord_t coordinate_type;
     
-        static inline coordinate_type get(const Point& point, orientation_2d orient) {
+        static inline coordinate_type get(const Slic3r::Point& point, orientation_2d orient) {
             return (orient == HORIZONTAL) ? point.x : point.y;
         }
     };
     
     template <>
-    struct point_mutable_traits<Point> {
+    struct point_mutable_traits<Slic3r::Point> {
         typedef coord_t coordinate_type;
-        static inline void set(Point& point, orientation_2d orient, coord_t value) {
+        static inline void set(Slic3r::Point& point, orientation_2d orient, coord_t value) {
             if (orient == HORIZONTAL)
                 point.x = value;
             else
                 point.y = value;
         }
-        static inline Point construct(coord_t x_value, coord_t y_value) {
-            Point retval;
+        static inline Slic3r::Point construct(coord_t x_value, coord_t y_value) {
+            Slic3r::Point retval;
             retval.x = x_value;
             retval.y = y_value; 
             return retval;
diff --git a/xs/src/libslic3r/Polygon.cpp b/xs/src/libslic3r/Polygon.cpp
index 056c285ec..60ccd792d 100644
--- a/xs/src/libslic3r/Polygon.cpp
+++ b/xs/src/libslic3r/Polygon.cpp
@@ -112,9 +112,7 @@ double Polygon::area() const
 bool
 Polygon::is_counter_clockwise() const
 {
-    ClipperLib::Path p;
-    Slic3rMultiPoint_to_ClipperPath(*this, &p);
-    return ClipperLib::Orientation(p);
+    return ClipperLib::Orientation(Slic3rMultiPoint_to_ClipperPath(*this));
 }
 
 bool
@@ -190,8 +188,7 @@ Polygon::simplify(double tolerance) const
     
     Polygons pp;
     pp.push_back(p);
-    simplify_polygons(pp, &pp);
-    return pp;
+    return simplify_polygons(pp);
 }
 
 void
diff --git a/xs/src/libslic3r/Polygon.hpp b/xs/src/libslic3r/Polygon.hpp
index 966264fb2..b0a7ea40a 100644
--- a/xs/src/libslic3r/Polygon.hpp
+++ b/xs/src/libslic3r/Polygon.hpp
@@ -142,43 +142,43 @@ inline Polylines to_polylines(Polygons &&polys)
 #include <boost/polygon/polygon.hpp>
 namespace boost { namespace polygon {
     template <>
-    struct geometry_concept<Polygon>{ typedef polygon_concept type; };
+    struct geometry_concept<Slic3r::Polygon>{ typedef polygon_concept type; };
 
     template <>
-    struct polygon_traits<Polygon> {
+    struct polygon_traits<Slic3r::Polygon> {
         typedef coord_t coordinate_type;
-        typedef Points::const_iterator iterator_type;
-        typedef Point point_type;
+        typedef Slic3r::Points::const_iterator iterator_type;
+        typedef Slic3r::Point point_type;
 
         // Get the begin iterator
-        static inline iterator_type begin_points(const Polygon& t) {
+        static inline iterator_type begin_points(const Slic3r::Polygon& t) {
             return t.points.begin();
         }
 
         // Get the end iterator
-        static inline iterator_type end_points(const Polygon& t) {
+        static inline iterator_type end_points(const Slic3r::Polygon& t) {
             return t.points.end();
         }
 
         // Get the number of sides of the polygon
-        static inline std::size_t size(const Polygon& t) {
+        static inline std::size_t size(const Slic3r::Polygon& t) {
             return t.points.size();
         }
 
         // Get the winding direction of the polygon
-        static inline winding_direction winding(const Polygon& t) {
+        static inline winding_direction winding(const Slic3r::Polygon& t) {
             return unknown_winding;
         }
     };
 
     template <>
-    struct polygon_mutable_traits<Polygon> {
+    struct polygon_mutable_traits<Slic3r::Polygon> {
         // expects stl style iterators
         template <typename iT>
-        static inline Polygon& set_points(Polygon& polygon, iT input_begin, iT input_end) {
+        static inline Slic3r::Polygon& set_points(Slic3r::Polygon& polygon, iT input_begin, iT input_end) {
             polygon.points.clear();
             while (input_begin != input_end) {
-                polygon.points.push_back(Point());
+                polygon.points.push_back(Slic3r::Point());
                 boost::polygon::assign(polygon.points.back(), *input_begin);
                 ++input_begin;
             }
@@ -189,32 +189,32 @@ namespace boost { namespace polygon {
     };
     
     template <>
-    struct geometry_concept<Polygons> { typedef polygon_set_concept type; };
+    struct geometry_concept<Slic3r::Polygons> { typedef polygon_set_concept type; };
 
     //next we map to the concept through traits
     template <>
-    struct polygon_set_traits<Polygons> {
+    struct polygon_set_traits<Slic3r::Polygons> {
         typedef coord_t coordinate_type;
-        typedef Polygons::const_iterator iterator_type;
-        typedef Polygons operator_arg_type;
+        typedef Slic3r::Polygons::const_iterator iterator_type;
+        typedef Slic3r::Polygons operator_arg_type;
 
-        static inline iterator_type begin(const Polygons& polygon_set) {
+        static inline iterator_type begin(const Slic3r::Polygons& polygon_set) {
             return polygon_set.begin();
         }
 
-        static inline iterator_type end(const Polygons& polygon_set) {
+        static inline iterator_type end(const Slic3r::Polygons& polygon_set) {
             return polygon_set.end();
         }
 
         //don't worry about these, just return false from them
-        static inline bool clean(const Polygons& polygon_set) { return false; }
-        static inline bool sorted(const Polygons& polygon_set) { return false; }
+        static inline bool clean(const Slic3r::Polygons& polygon_set) { return false; }
+        static inline bool sorted(const Slic3r::Polygons& polygon_set) { return false; }
     };
 
     template <>
-    struct polygon_set_mutable_traits<Polygons> {
+    struct polygon_set_mutable_traits<Slic3r::Polygons> {
         template <typename input_iterator_type>
-        static inline void set(Polygons& polygons, input_iterator_type input_begin, input_iterator_type input_end) {
+        static inline void set(Slic3r::Polygons& polygons, input_iterator_type input_begin, input_iterator_type input_end) {
           polygons.assign(input_begin, input_end);
         }
     };
diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
index 3ef964339..3583c627b 100644
--- a/xs/src/libslic3r/Print.cpp
+++ b/xs/src/libslic3r/Print.cpp
@@ -608,20 +608,15 @@ Print::validate() const
                 object->model_object()->instances.front()->transform_polygon(&convex_hull);
                 
                 // grow convex hull with the clearance margin
-                {
-                    Polygons grown_hull;
-                    offset(convex_hull, &grown_hull, scale_(this->config.extruder_clearance_radius.value)/2, 1, jtRound, scale_(0.1));
-                    convex_hull = grown_hull.front();
-                }
+                convex_hull = offset(convex_hull, scale_(this->config.extruder_clearance_radius.value)/2, jtRound, scale_(0.1)).front();
                 
                 // now we check that no instance of convex_hull intersects any of the previously checked object instances
                 for (Points::const_iterator copy = object->_shifted_copies.begin(); copy != object->_shifted_copies.end(); ++copy) {
                     Polygon p = convex_hull;
                     p.translate(*copy);
-                    if (intersects(a, p))
+                    if (! intersection(a, p).empty())
                         return "Some objects are too close; your extruder will collide with them.";
-                    
-                    union_(a, p, &a);
+                    polygons_append(a, p);
                 }
             }
         }
diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp
index 30b1f40cc..f97f888cd 100644
--- a/xs/src/libslic3r/Print.hpp
+++ b/xs/src/libslic3r/Print.hpp
@@ -12,7 +12,7 @@
 #include "Layer.hpp"
 #include "Model.hpp"
 #include "PlaceholderParser.hpp"
-
+#include "Slicing.hpp"
 
 namespace Slic3r {
 
@@ -78,6 +78,10 @@ public:
     std::map< size_t,std::vector<int> > region_volumes;
     PrintObjectConfig config;
     t_layer_height_ranges layer_height_ranges;
+
+    // Profile of increasing z to a layer height, to be linearly interpolated when calculating the layers.
+    // The pairs of <z, layer_height> are packed into a 1D array to simplify handling by the Perl XS.
+    std::vector<coordf_t> layer_height_profile;
     
     // this is set to true when LayerRegion->slices is split in top/internal/bottom
     // so that next call to make_perimeters() performs a union() before computing loops
@@ -136,13 +140,26 @@ public:
     bool invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys);
     bool invalidate_step(PrintObjectStep step);
     bool invalidate_all_steps();
-    
+
+    // Process layer_height_ranges, the raft layers and first layer thickness into layer_height_profile.
+    // The layer_height_profile may be later modified interactively by the user to refine layers at sloping surfaces.
+    void update_layer_height_profile();
+
+    // Collect the slicing parameters, to be used by variable layer thickness algorithm,
+    // by the interactive layer height editor and by the printing process itself.
+    // The slicing parameters are dependent on various configuration values
+    // (layer height, first layer height, raft settings, print nozzle diameter etc).
+    SlicingParameters slicing_parameters() const;
+
+    void _slice();
     bool has_support_material() const;
     void detect_surfaces_type();
     void process_external_surfaces();
     void discover_vertical_shells();
     void bridge_over_infill();
-    
+    void _make_perimeters();
+    void _infill();
+
 private:
     Print* _print;
     ModelObject* _model_object;
@@ -152,6 +169,8 @@ private:
         // parameter
     PrintObject(Print* print, ModelObject* model_object, const BoundingBoxf3 &modobj_bbox);
     ~PrintObject() {}
+
+    std::vector<ExPolygons> _slice_region(size_t region_id, const std::vector<float> &z, bool modifier);
 };
 
 typedef std::vector<PrintObject*> PrintObjectPtrs;
diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp
index 59dcea879..746cf7f1d 100644
--- a/xs/src/libslic3r/PrintConfig.cpp
+++ b/xs/src/libslic3r/PrintConfig.cpp
@@ -1,4 +1,5 @@
 #include "PrintConfig.hpp"
+#include <boost/thread.hpp>
 
 namespace Slic3r {
 
@@ -1290,9 +1291,11 @@ PrintConfigDef::PrintConfigDef()
     def->cli = "threads|j=i";
     def->readonly = true;
     def->min = 1;
-    def->max = 16;
-    def->default_value = new ConfigOptionInt(2);
-
+    {
+        unsigned int threads = boost::thread::hardware_concurrency();
+        def->default_value = new ConfigOptionInt(threads > 0 ? threads : 2);
+    }
+    
     def = this->add("toolchange_gcode", coString);
     def->label = "Tool change G-code";
     def->tooltip = "This custom code is inserted right before every extruder change. Note that you can use placeholder variables for all Slic3r settings as well as [previous_extruder] and [next_extruder].";
diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp
index 3808a545e..8d6ddaf21 100644
--- a/xs/src/libslic3r/PrintObject.cpp
+++ b/xs/src/libslic3r/PrintObject.cpp
@@ -2,10 +2,23 @@
 #include "BoundingBox.hpp"
 #include "ClipperUtils.hpp"
 #include "Geometry.hpp"
-#include "SVG.hpp"
+
+#include <boost/log/trivial.hpp>
 
 #include <Shiny/Shiny.h>
 
+// #define SLIC3R_DEBUG
+
+// Make assert active if SLIC3R_DEBUG
+#ifdef SLIC3R_DEBUG
+    #undef NDEBUG
+    #define DEBUG
+    #define _DEBUG
+    #include "SVG.hpp"
+    #undef assert 
+    #include <cassert>
+#endif
+
 namespace Slic3r {
 
 PrintObject::PrintObject(Print* print, ModelObject* model_object, const BoundingBoxf3 &modobj_bbox)
@@ -323,7 +336,8 @@ PrintObject::has_support_material() const
 // If a part of a region is of S_TYPE_BOTTOM and S_TYPE_TOP, the S_TYPE_BOTTOM wins.
 void PrintObject::detect_surfaces_type()
 {
-//    Slic3r::debugf "Detecting solid surfaces...\n";
+    BOOST_LOG_TRIVIAL(info) << "Detecting solid surfaces...";
+
     for (int idx_region = 0; idx_region < this->_print->regions.size(); ++ idx_region) {
 #ifdef SLIC3R_DEBUG_SLICE_PROCESSING
         for (int idx_layer = 0; idx_layer < int(this->layer_count()); ++ idx_layer) {
@@ -435,7 +449,7 @@ void PrintObject::detect_surfaces_type()
             {
                 Polygons topbottom = to_polygons(top);
                 polygons_append(topbottom, to_polygons(bottom));
-                surfaces_append(layerm->slices.surfaces, 
+                layerm->slices.append(
 #if 0
                     offset2_ex(diff(layerm_slices_surfaces, topbottom, true), -offset, offset),
 #else
@@ -444,8 +458,8 @@ void PrintObject::detect_surfaces_type()
                     stInternal);
             }
 
-            surfaces_append(layerm->slices.surfaces, STDMOVE(top));
-            surfaces_append(layerm->slices.surfaces, STDMOVE(bottom));
+            layerm->slices.append(STDMOVE(top));
+            layerm->slices.append(STDMOVE(bottom));
             
 //            Slic3r::debugf "  layer %d has %d bottom, %d top and %d internal surfaces\n",
 //                $layerm->layer->id, scalar(@bottom), scalar(@top), scalar(@internal) if $Slic3r::debug;
@@ -469,6 +483,8 @@ void PrintObject::detect_surfaces_type()
 void
 PrintObject::process_external_surfaces()
 {
+    BOOST_LOG_TRIVIAL(info) << "Processing external surfaces...";
+
     FOREACH_REGION(this->_print, region) {
         size_t region_id = region - this->_print->regions.begin();
         
@@ -497,6 +513,8 @@ PrintObject::discover_vertical_shells()
 {
     PROFILE_FUNC();
 
+    BOOST_LOG_TRIVIAL(info) << "Discovering vertical shells...";
+
     const SurfaceType surfaces_bottom[2] = { stBottom, stBottomBridge };
 
     for (size_t idx_region = 0; idx_region < this->_print->regions.size(); ++ idx_region) {
@@ -692,8 +710,7 @@ PrintObject::discover_vertical_shells()
 #if 1
             // Intentionally inflate a bit more than how much the region has been shrunk, 
             // so there will be some overlap between this solid infill and the other infill regions (mainly the sparse infill).
-            shell = offset2(shell, - 0.5f * min_perimeter_infill_spacing, 0.8f * min_perimeter_infill_spacing,
-                CLIPPER_OFFSET_SCALE, ClipperLib::jtSquare);
+            shell = offset2(shell, - 0.5f * min_perimeter_infill_spacing, 0.8f * min_perimeter_infill_spacing, ClipperLib::jtSquare);
             if (shell.empty())
                 continue;
 #else
@@ -705,7 +722,7 @@ PrintObject::discover_vertical_shells()
             // get a triangle in $too_narrow; if we grow it below then the shell
             // would have a different shape from the external surface and we'd still
             // have the same angle, so the next shell would be grown even more and so on.
-            Polygons too_narrow = diff(shell, offset2(shell, -margin, margin, CLIPPER_OFFSET_SCALE, ClipperLib::jtMiter, 5.), true);
+            Polygons too_narrow = diff(shell, offset2(shell, -margin, margin, ClipperLib::jtMiter, 5.), true);
             if (! too_narrow.empty()) {
                 // grow the collapsing parts and add the extra area to  the neighbor layer 
                 // as well as to our original surfaces so that we support this 
@@ -754,9 +771,9 @@ PrintObject::discover_vertical_shells()
             // Assign resulting internal surfaces to layer.
             const SurfaceType surfaceTypesKeep[] = { stTop, stBottom, stBottomBridge };
             layerm->fill_surfaces.keep_types(surfaceTypesKeep, sizeof(surfaceTypesKeep)/sizeof(SurfaceType));
-            layerm->fill_surfaces.append(stInternal     , new_internal);
-            layerm->fill_surfaces.append(stInternalVoid , new_internal_void);
-            layerm->fill_surfaces.append(stInternalSolid, new_internal_solid);
+            layerm->fill_surfaces.append(new_internal,       stInternal);
+            layerm->fill_surfaces.append(new_internal_void,  stInternalVoid);
+            layerm->fill_surfaces.append(new_internal_solid, stInternalSolid);
 
 #ifdef SLIC3R_DEBUG_SLICE_PROCESSING
             layerm->export_region_slices_to_svg_debug("4_discover_vertical_shells");
@@ -775,6 +792,8 @@ PrintObject::discover_vertical_shells()
 void
 PrintObject::bridge_over_infill()
 {
+    BOOST_LOG_TRIVIAL(info) << "Bridge over infill...";
+
     FOREACH_REGION(this->_print, region) {
         size_t region_id = region - this->_print->regions.begin();
         
@@ -846,7 +865,7 @@ PrintObject::bridge_over_infill()
             #endif
             
             // compute the remaning internal solid surfaces as difference
-            ExPolygons not_to_bridge = diff_ex(internal_solid, to_bridge, true);
+            ExPolygons not_to_bridge = diff_ex(internal_solid, to_polygons(to_bridge), true);
             to_bridge = intersection_ex(to_polygons(to_bridge), internal_solid, true);
             
             // build the new collection of fill_surfaces
@@ -902,4 +921,326 @@ PrintObject::bridge_over_infill()
     }
 }
 
+SlicingParameters PrintObject::slicing_parameters() const
+{
+    return SlicingParameters::create_from_config(
+        this->print()->config, this->config, 
+        unscale(this->size.z), this->print()->object_extruders());
 }
+
+void PrintObject::update_layer_height_profile()
+{
+    if (this->layer_height_profile.empty()) {
+        if (0)
+//        if (this->layer_height_profile.empty())
+            this->layer_height_profile = layer_height_profile_adaptive(this->slicing_parameters(), this->layer_height_ranges,
+                this->model_object()->volumes);
+        else
+            this->layer_height_profile = layer_height_profile_from_ranges(this->slicing_parameters(), this->layer_height_ranges);
+    }
+}
+
+// 1) Decides Z positions of the layers,
+// 2) Initializes layers and their regions
+// 3) Slices the object meshes
+// 4) Slices the modifier meshes and reclassifies the slices of the object meshes by the slices of the modifier meshes
+// 5) Applies size compensation (offsets the slices in XY plane)
+// 6) Replaces bad slices by the slices reconstructed from the upper/lower layer
+// Resulting expolygons of layer regions are marked as Internal.
+//
+// this should be idempotent
+void PrintObject::_slice()
+{
+    SlicingParameters slicing_params = this->slicing_parameters();
+
+    // 1) Initialize layers and their slice heights.
+    std::vector<float> slice_zs;
+    {
+        this->clear_layers();
+        // Object layers (pairs of bottom/top Z coordinate), without the raft.
+        this->update_layer_height_profile();
+        std::vector<coordf_t> object_layers = generate_object_layers(slicing_params, this->layer_height_profile);
+        // Reserve object layers for the raft. Last layer of the raft is the contact layer.
+        int id = int(slicing_params.raft_layers());
+        slice_zs.reserve(object_layers.size());
+        Layer *prev = nullptr;
+        for (size_t i_layer = 0; i_layer < object_layers.size(); i_layer += 2) {
+            coordf_t lo = object_layers[i_layer];
+            coordf_t hi = object_layers[i_layer + 1];
+            coordf_t slice_z = 0.5 * (lo + hi);
+            Layer *layer = this->add_layer(id ++, hi - lo, hi + slicing_params.object_print_z_min, slice_z);
+            slice_zs.push_back(float(slice_z));
+            if (prev != nullptr) {
+                prev->upper_layer = layer;
+                layer->lower_layer = prev;
+            }
+            // Make sure all layers contain layer region objects for all regions.
+            for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id)
+                layer->add_region(this->print()->regions[region_id]);
+            prev = layer;
+        }
+    }
+    
+    if (this->print()->regions.size() == 1) {
+        // Optimized for a single region. Slice the single non-modifier mesh.
+        std::vector<ExPolygons> expolygons_by_layer = this->_slice_region(0, slice_zs, false);
+        for (size_t layer_id = 0; layer_id < expolygons_by_layer.size(); ++ layer_id)
+            this->layers[layer_id]->regions.front()->slices.append(std::move(expolygons_by_layer[layer_id]), stInternal);
+    } else {
+        // Slice all non-modifier volumes.
+        for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) {
+            std::vector<ExPolygons> expolygons_by_layer = this->_slice_region(region_id, slice_zs, false);
+            for (size_t layer_id = 0; layer_id < expolygons_by_layer.size(); ++ layer_id)
+                this->layers[layer_id]->regions[region_id]->slices.append(std::move(expolygons_by_layer[layer_id]), stInternal);
+        }
+        // Slice all modifier volumes.
+        for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) {
+            std::vector<ExPolygons> expolygons_by_layer = this->_slice_region(region_id, slice_zs, true);
+            // loop through the other regions and 'steal' the slices belonging to this one
+            for (size_t other_region_id = 0; other_region_id < this->print()->regions.size(); ++ other_region_id) {
+                if (region_id == other_region_id)
+                    continue;
+                for (size_t layer_id = 0; layer_id < expolygons_by_layer.size(); ++ layer_id) {
+                    Layer       *layer = layers[layer_id];
+                    LayerRegion *layerm = layer->regions[region_id];
+                    LayerRegion *other_layerm = layer->regions[other_region_id];
+                    if (layerm == nullptr || other_layerm == nullptr)
+                        continue;
+                    Polygons other_slices = to_polygons(other_layerm->slices);
+                    ExPolygons my_parts = intersection_ex(other_slices, to_polygons(expolygons_by_layer[layer_id]));
+                    if (my_parts.empty())
+                        continue;
+                    // Remove such parts from original region.
+                    other_layerm->slices.set(diff_ex(other_slices, to_polygons(my_parts)), stInternal);
+                    // Append new parts to our region.
+                    layerm->slices.append(std::move(my_parts), stInternal);
+                }
+            }
+        }
+    }
+    
+    // remove last layer(s) if empty
+    while (! this->layers.empty()) {
+        const Layer *layer = this->layers.back();
+        for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id)
+            if (layer->regions[region_id] != nullptr && ! layer->regions[region_id]->slices.empty())
+                // Non empty layer.
+                goto end;
+        this->delete_layer(int(this->layers.size()) - 1);
+    }
+end:
+    ;
+
+    for (size_t layer_id = 0; layer_id < layers.size(); ++ layer_id) {
+        Layer *layer = this->layers[layer_id];
+        // apply size compensation
+        if (this->config.xy_size_compensation.value != 0.) {
+            float delta = float(scale_(this->config.xy_size_compensation.value));
+            if (layer->regions.size() == 1) {
+                // single region
+                LayerRegion *layerm = layer->regions.front();
+                layerm->slices.set(offset_ex(to_expolygons(std::move(layerm->slices.surfaces)), delta), stInternal);
+            } else {
+                if (delta < 0) {
+                    // multiple regions, shrinking
+                    // we apply the offset to the combined shape, then intersect it
+                    // with the original slices for each region
+                    Polygons region_slices;
+                    for (size_t region_id = 0; region_id < layer->regions.size(); ++ region_id)
+                        polygons_append(region_slices, layer->regions[region_id]->slices.surfaces);
+                    Polygons slices = offset(union_(region_slices), delta);
+                    for (size_t region_id = 0; region_id < layer->regions.size(); ++ region_id) {
+                        LayerRegion *layerm = layer->regions[region_id];
+                        layerm->slices.set(std::move(intersection_ex(slices, to_polygons(std::move(layerm->slices.surfaces)))), stInternal);
+                    }
+                } else {
+                    // multiple regions, growing
+                    // this is an ambiguous case, since it's not clear how to grow regions where they are going to overlap
+                    // so we give priority to the first one and so on
+                    Polygons processed;
+                    for (size_t region_id = 0;; ++ region_id) {
+                        LayerRegion *layerm = layer->regions[region_id];
+                        ExPolygons slices = offset_ex(to_expolygons(layerm->slices.surfaces), delta);
+                        if (region_id > 0)
+                            // Trim by the slices of already processed regions.
+                            slices = diff_ex(to_polygons(std::move(slices)), processed);
+                        if (region_id + 1 == layer->regions.size()) {
+                            layerm->slices.set(std::move(slices), stInternal);
+                            break;
+                        }
+                        polygons_append(processed, slices);
+                        layerm->slices.set(std::move(slices), stInternal);
+                    }
+                }
+            }
+        }
+        
+        // Merge all regions' slices to get islands, chain them by a shortest path.
+        layer->make_slices();
+    }
+}
+
+std::vector<ExPolygons> PrintObject::_slice_region(size_t region_id, const std::vector<float> &z, bool modifier)
+{
+    std::vector<ExPolygons> layers;
+    assert(region_id < this->region_volumes.size());
+    std::vector<int> &volumes = this->region_volumes[region_id];
+    if (! volumes.empty()) {
+        // Compose mesh.
+        //FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them.
+        TriangleMesh mesh;
+        for (std::vector<int>::const_iterator it_volume = volumes.begin(); it_volume != volumes.end(); ++ it_volume) {
+            ModelVolume *volume = this->model_object()->volumes[*it_volume];
+            if (volume->modifier == modifier)
+                mesh.merge(volume->mesh);
+        }
+        if (mesh.stl.stats.number_of_facets > 0) {
+            // transform mesh
+            // we ignore the per-instance transformations currently and only 
+            // consider the first one
+            this->model_object()->instances.front()->transform_mesh(&mesh, true);
+            // align mesh to Z = 0 (it should be already aligned actually) and apply XY shift
+            mesh.translate(- unscale(this->_copies_shift.x), - unscale(this->_copies_shift.y), -this->model_object()->bounding_box().min.z);
+            // perform actual slicing
+            TriangleMeshSlicer mslicer(&mesh);
+            mslicer.slice(z, &layers);
+        }
+    }
+    return layers;
+}
+
+void
+PrintObject::_make_perimeters()
+{
+    if (this->state.is_done(posPerimeters)) return;
+    this->state.set_started(posPerimeters);
+    
+    // merge slices if they were split into types
+    if (this->typed_slices) {
+        FOREACH_LAYER(this, layer_it)
+            (*layer_it)->merge_slices();
+        this->typed_slices = false;
+        this->state.invalidate(posPrepareInfill);
+    }
+    
+    // compare each layer to the one below, and mark those slices needing
+    // one additional inner perimeter, like the top of domed objects-
+    
+    // this algorithm makes sure that at least one perimeter is overlapping
+    // but we don't generate any extra perimeter if fill density is zero, as they would be floating
+    // inside the object - infill_only_where_needed should be the method of choice for printing
+    // hollow objects
+    FOREACH_REGION(this->_print, region_it) {
+        size_t region_id = region_it - this->_print->regions.begin();
+        const PrintRegion &region = **region_it;
+        
+        
+        if (!region.config.extra_perimeters
+            || region.config.perimeters == 0
+            || region.config.fill_density == 0
+            || this->layer_count() < 2) continue;
+        
+        for (int i = 0; i < int(this->layer_count()) - 1; ++i) {
+            LayerRegion &layerm                     = *this->get_layer(i)->get_region(region_id);
+            const LayerRegion &upper_layerm         = *this->get_layer(i+1)->get_region(region_id);
+            const Polygons upper_layerm_polygons    = upper_layerm.slices;
+            
+            // Filter upper layer polygons in intersection_ppl by their bounding boxes?
+            // my $upper_layerm_poly_bboxes= [ map $_->bounding_box, @{$upper_layerm_polygons} ];
+            double total_loop_length = 0;
+            for (Polygons::const_iterator it = upper_layerm_polygons.begin(); it != upper_layerm_polygons.end(); ++it)
+                total_loop_length += it->length();
+            
+            const coord_t perimeter_spacing     = layerm.flow(frPerimeter).scaled_spacing();
+            const Flow ext_perimeter_flow       = layerm.flow(frExternalPerimeter);
+            const coord_t ext_perimeter_width   = ext_perimeter_flow.scaled_width();
+            const coord_t ext_perimeter_spacing = ext_perimeter_flow.scaled_spacing();
+            
+            for (Surfaces::iterator slice = layerm.slices.surfaces.begin();
+                slice != layerm.slices.surfaces.end(); ++slice) {
+                while (true) {
+                    // compute the total thickness of perimeters
+                    const coord_t perimeters_thickness = ext_perimeter_width/2 + ext_perimeter_spacing/2
+                        + (region.config.perimeters-1 + region.config.extra_perimeters) * perimeter_spacing;
+                    
+                    // define a critical area where we don't want the upper slice to fall into
+                    // (it should either lay over our perimeters or outside this area)
+                    const coord_t critical_area_depth = perimeter_spacing * 1.5;
+                    const Polygons critical_area = diff(
+                        offset(slice->expolygon, -perimeters_thickness),
+                        offset(slice->expolygon, -(perimeters_thickness + critical_area_depth))
+                    );
+                    
+                    // check whether a portion of the upper slices falls inside the critical area
+                    const Polylines intersection = intersection_pl(
+                        to_polylines(upper_layerm_polygons),
+                        critical_area
+                    );
+                    
+                    // only add an additional loop if at least 30% of the slice loop would benefit from it
+                    {
+                        double total_intersection_length = 0;
+                        for (Polylines::const_iterator it = intersection.begin(); it != intersection.end(); ++it)
+                            total_intersection_length += it->length();
+                        if (total_intersection_length <= total_loop_length*0.3) break;
+                    }
+                    
+                    /*
+                    if (0) {
+                        require "Slic3r/SVG.pm";
+                        Slic3r::SVG::output(
+                            "extra.svg",
+                            no_arrows   => 1,
+                            expolygons  => union_ex($critical_area),
+                            polylines   => [ map $_->split_at_first_point, map $_->p, @{$upper_layerm->slices} ],
+                        );
+                    }
+                    */
+                    
+                    slice->extra_perimeters++;
+                }
+                
+                #ifdef DEBUG
+                    if (slice->extra_perimeters > 0)
+                        printf("  adding %d more perimeter(s) at layer %zu\n", slice->extra_perimeters, i);
+                #endif
+            }
+        }
+    }
+    
+    parallelize<Layer*>(
+        std::queue<Layer*>(std::deque<Layer*>(this->layers.begin(), this->layers.end())),  // cast LayerPtrs to std::queue<Layer*>
+        boost::bind(&Slic3r::Layer::make_perimeters, _1),
+        this->_print->config.threads.value
+    );
+    
+    /*
+        simplify slices (both layer and region slices),
+        we only need the max resolution for perimeters
+    ### This makes this method not-idempotent, so we keep it disabled for now.
+    ###$self->_simplify_slices(&Slic3r::SCALED_RESOLUTION);
+    */
+    
+    this->state.set_done(posPerimeters);
+}
+
+void
+PrintObject::_infill()
+{
+    if (this->state.is_done(posInfill)) return;
+    this->state.set_started(posInfill);
+    
+    parallelize<Layer*>(
+        std::queue<Layer*>(std::deque<Layer*>(this->layers.begin(), this->layers.end())),  // cast LayerPtrs to std::queue<Layer*>
+        boost::bind(&Slic3r::Layer::make_fills, _1),
+        this->_print->config.threads.value
+    );
+    
+    /*  we could free memory now, but this would make this step not idempotent
+    ### $_->fill_surfaces->clear for map @{$_->regions}, @{$object->layers};
+    */
+    
+    this->state.set_done(posInfill);
+}
+
+} // namespace Slic3r
diff --git a/xs/src/libslic3r/Slicing.cpp b/xs/src/libslic3r/Slicing.cpp
new file mode 100644
index 000000000..0de781c64
--- /dev/null
+++ b/xs/src/libslic3r/Slicing.cpp
@@ -0,0 +1,585 @@
+#include "Slicing.hpp"
+#include "SlicingAdaptive.hpp"
+#include "PrintConfig.hpp"
+#include "Model.hpp"
+
+// #define SLIC3R_DEBUG
+
+// Make assert active if SLIC3R_DEBUG
+#ifdef SLIC3R_DEBUG
+    #undef NDEBUG
+    #define DEBUG
+    #define _DEBUG
+    #include "SVG.hpp"
+    #undef assert 
+    #include <cassert>
+#endif
+
+namespace Slic3r
+{
+
+SlicingParameters SlicingParameters::create_from_config(
+	const PrintConfig 		&print_config, 
+	const PrintObjectConfig &object_config,
+	coordf_t				 object_height,
+	const std::set<size_t>  &object_extruders)
+{
+    coordf_t first_layer_height                      = (object_config.first_layer_height.value <= 0) ? 
+        object_config.layer_height.value : 
+        object_config.first_layer_height.get_abs_value(object_config.layer_height.value);
+    coordf_t support_material_extruder_dmr           = print_config.nozzle_diameter.get_at(object_config.support_material_extruder.value - 1);
+    coordf_t support_material_interface_extruder_dmr = print_config.nozzle_diameter.get_at(object_config.support_material_interface_extruder.value - 1);
+    bool     soluble_interface                       = object_config.support_material_contact_distance.value == 0.;
+
+    SlicingParameters params;
+    params.layer_height = object_config.layer_height.value;
+    params.first_object_layer_height = first_layer_height;
+    params.object_print_z_min = 0.;
+    params.object_print_z_max = object_height;
+    params.base_raft_layers = object_config.raft_layers.value;
+
+    if (params.base_raft_layers > 0) {
+		params.interface_raft_layers = (params.base_raft_layers + 1) / 2;
+        params.base_raft_layers -= params.interface_raft_layers;
+        // Use as large as possible layer height for the intermediate raft layers.
+        params.base_raft_layer_height       = std::max(params.layer_height, 0.75 * support_material_extruder_dmr);
+        params.interface_raft_layer_height  = std::max(params.layer_height, 0.75 * support_material_interface_extruder_dmr);
+        params.contact_raft_layer_height_bridging = false;
+        params.first_object_layer_bridging  = false;
+        #if 1
+        params.contact_raft_layer_height    = std::max(params.layer_height, 0.75 * support_material_interface_extruder_dmr);
+        if (! soluble_interface) {
+            // Compute the average of all nozzles used for printing the object over a raft.
+            //FIXME It is expected, that the 1st layer of the object is printed with a bridging flow over a full raft. Shall it not be vice versa?
+            coordf_t average_object_extruder_dmr = 0.;
+            if (! object_extruders.empty()) {
+                for (std::set<size_t>::const_iterator it_extruder = object_extruders.begin(); it_extruder != object_extruders.end(); ++ it_extruder)
+                    average_object_extruder_dmr += print_config.nozzle_diameter.get_at(*it_extruder);
+                average_object_extruder_dmr /= coordf_t(object_extruders.size());
+            }
+            params.first_object_layer_height   = average_object_extruder_dmr;
+            params.first_object_layer_bridging = true;
+        }
+        #else
+        params.contact_raft_layer_height    = soluble_interface ? support_material_interface_extruder_dmr : 0.75 * support_material_interface_extruder_dmr;
+        params.contact_raft_layer_height_bridging = ! soluble_interface;
+        ...
+        #endif
+    }
+
+    if (params.has_raft()) {
+        // Raise first object layer Z by the thickness of the raft itself plus the extra distance required by the support material logic.
+        //FIXME The last raft layer is the contact layer, which shall be printed with a bridging flow for ease of separation. Currently it is not the case.
+		coordf_t print_z = first_layer_height + object_config.support_material_contact_distance.value;
+		if (params.raft_layers() == 1) {
+			params.contact_raft_layer_height = first_layer_height;
+		} else {
+			print_z +=
+				// Number of the base raft layers is decreased by the first layer, which has already been added to print_z.
+				coordf_t(params.base_raft_layers - 1) * params.base_raft_layer_height +
+				// Number of the interface raft layers is decreased by the contact layer.
+				coordf_t(params.interface_raft_layers - 1) * params.interface_raft_layer_height +
+				params.contact_raft_layer_height;
+		}
+        params.object_print_z_min = print_z;
+        params.object_print_z_max += print_z;
+    }
+
+    params.min_layer_height = std::min(params.layer_height, first_layer_height);
+    params.max_layer_height = std::max(params.layer_height, first_layer_height);
+
+    //FIXME add it to the print configuration
+    params.min_layer_height = 0.05;
+
+    // Calculate the maximum layer height as 0.75 from the minimum nozzle diameter.
+    if (! object_extruders.empty()) {
+        coordf_t min_object_extruder_dmr = 1000000.;
+        for (std::set<size_t>::const_iterator it_extruder = object_extruders.begin(); it_extruder != object_extruders.end(); ++ it_extruder)
+            min_object_extruder_dmr = std::min(min_object_extruder_dmr, print_config.nozzle_diameter.get_at(*it_extruder));
+        // Allow excessive maximum layer height higher than 0.75 * min_object_extruder_dmr 
+        params.max_layer_height = std::max(std::max(params.layer_height, first_layer_height), 0.75 * min_object_extruder_dmr);
+    }
+
+    return params;
+}
+
+// Convert layer_height_ranges to layer_height_profile. Both are referenced to z=0, meaning the raft layers are not accounted for
+// in the height profile and the printed object may be lifted by the raft thickness at the time of the G-code generation.
+std::vector<coordf_t> layer_height_profile_from_ranges(
+	const SlicingParameters 	&slicing_params,
+	const t_layer_height_ranges &layer_height_ranges)
+{
+    // 1) If there are any height ranges, trim one by the other to make them non-overlapping. Insert the 1st layer if fixed.
+    std::vector<std::pair<t_layer_height_range,coordf_t>> ranges_non_overlapping;
+    ranges_non_overlapping.reserve(layer_height_ranges.size() * 4);
+    if (slicing_params.first_object_layer_height_fixed())
+        ranges_non_overlapping.push_back(std::pair<t_layer_height_range,coordf_t>(
+            t_layer_height_range(0., slicing_params.first_object_layer_height), 
+            slicing_params.first_object_layer_height));
+    // The height ranges are sorted lexicographically by low / high layer boundaries.
+    for (t_layer_height_ranges::const_iterator it_range = layer_height_ranges.begin(); it_range != layer_height_ranges.end(); ++ it_range) {
+        coordf_t lo = it_range->first.first;
+        coordf_t hi = std::min(it_range->first.second, slicing_params.object_print_z_height());
+        coordf_t height = it_range->second;
+        if (! ranges_non_overlapping.empty())
+            // Trim current low with the last high.
+            lo = std::max(lo, ranges_non_overlapping.back().first.second);
+        if (lo + EPSILON < hi)
+            // Ignore too narrow ranges.
+            ranges_non_overlapping.push_back(std::pair<t_layer_height_range,coordf_t>(t_layer_height_range(lo, hi), height));
+    }
+
+    // 2) Convert the trimmed ranges to a height profile, fill in the undefined intervals between z=0 and z=slicing_params.object_print_z_max()
+    // with slicing_params.layer_height
+    std::vector<coordf_t> layer_height_profile;
+    for (std::vector<std::pair<t_layer_height_range,coordf_t>>::const_iterator it_range = ranges_non_overlapping.begin(); it_range != ranges_non_overlapping.end(); ++ it_range) {
+        coordf_t lo = it_range->first.first;
+        coordf_t hi = it_range->first.second;
+        coordf_t height = it_range->second;
+        coordf_t last_z      = layer_height_profile.empty() ? 0. : layer_height_profile[layer_height_profile.size() - 2];
+        coordf_t last_height = layer_height_profile.empty() ? 0. : layer_height_profile[layer_height_profile.size() - 1];
+        if (lo > last_z + EPSILON) {
+            // Insert a step of normal layer height.
+            layer_height_profile.push_back(last_z);
+            layer_height_profile.push_back(slicing_params.layer_height);
+            layer_height_profile.push_back(lo);
+            layer_height_profile.push_back(slicing_params.layer_height);
+        }
+        // Insert a step of the overriden layer height.
+        layer_height_profile.push_back(lo);
+        layer_height_profile.push_back(height);
+        layer_height_profile.push_back(hi);
+        layer_height_profile.push_back(height);
+    }
+
+    coordf_t last_z      = layer_height_profile.empty() ? 0. : layer_height_profile[layer_height_profile.size() - 2];
+    coordf_t last_height = layer_height_profile.empty() ? 0. : layer_height_profile[layer_height_profile.size() - 1];
+    if (last_z < slicing_params.object_print_z_height()) {
+        // Insert a step of normal layer height up to the object top.
+        layer_height_profile.push_back(last_z);
+        layer_height_profile.push_back(slicing_params.layer_height);
+        layer_height_profile.push_back(slicing_params.object_print_z_height());
+        layer_height_profile.push_back(slicing_params.layer_height);
+    }
+
+   	return layer_height_profile;
+}
+
+// Based on the work of @platsch
+// Fill layer_height_profile by heights ensuring a prescribed maximum cusp height.
+std::vector<coordf_t> layer_height_profile_adaptive(
+    const SlicingParameters     &slicing_params,
+    const t_layer_height_ranges &layer_height_ranges,
+    const ModelVolumePtrs		&volumes)
+{
+    // 1) Initialize the SlicingAdaptive class with the object meshes.
+    SlicingAdaptive as;
+    as.set_slicing_parameters(slicing_params);
+    for (ModelVolumePtrs::const_iterator it = volumes.begin(); it != volumes.end(); ++ it)
+        if (! (*it)->modifier)
+            as.add_mesh(&(*it)->mesh);
+    as.prepare();
+
+    // 2) Generate layers using the algorithm of @platsch 
+    // loop until we have at least one layer and the max slice_z reaches the object height
+    //FIXME make it configurable
+    // Cusp value: A maximum allowed distance from a corner of a rectangular extrusion to a chrodal line, in mm.
+    const coordf_t cusp_value = 0.2; // $self->config->get_value('cusp_value');
+
+    std::vector<coordf_t> layer_height_profile;
+    layer_height_profile.push_back(0.);
+    layer_height_profile.push_back(slicing_params.first_object_layer_height);
+    if (slicing_params.first_object_layer_height_fixed()) {
+        layer_height_profile.push_back(slicing_params.first_object_layer_height);
+        layer_height_profile.push_back(slicing_params.first_object_layer_height);
+    }
+    coordf_t slice_z = slicing_params.first_object_layer_height;
+    coordf_t height  = slicing_params.first_object_layer_height;
+    coordf_t cusp_height = 0.;
+    int current_facet = 0;
+    while ((slice_z - height) <= slicing_params.object_print_z_height()) {
+        height = 999;
+        // Slic3r::debugf "\n Slice layer: %d\n", $id;
+        // determine next layer height
+        coordf_t cusp_height = as.cusp_height(slice_z, cusp_value, current_facet);
+        // check for horizontal features and object size
+        /*
+        if($self->config->get_value('match_horizontal_surfaces')) {
+            my $horizontal_dist = $adaptive_slicing[$region_id]->horizontal_facet_distance(scale $slice_z+$cusp_height, $min_height);
+            if(($horizontal_dist < $min_height) && ($horizontal_dist > 0)) {
+                Slic3r::debugf "Horizontal feature ahead, distance: %f\n", $horizontal_dist;
+                # can we shrink the current layer a bit?
+                if($cusp_height-($min_height-$horizontal_dist) > $min_height) {
+                    # yes we can
+                    $cusp_height = $cusp_height-($min_height-$horizontal_dist);
+                    Slic3r::debugf "Shrink layer height to %f\n", $cusp_height;
+                }else{
+                    # no, current layer would become too thin
+                    $cusp_height = $cusp_height+$horizontal_dist;
+                    Slic3r::debugf "Widen layer height to %f\n", $cusp_height;
+                }
+            }
+        }
+        */
+        height = std::min(cusp_height, height);
+
+        // apply z-gradation
+        /*
+        my $gradation = $self->config->get_value('adaptive_slicing_z_gradation');
+        if($gradation > 0) {
+            $height = $height - unscale((scale($height)) % (scale($gradation)));
+        }
+        */
+    
+        // look for an applicable custom range
+        /*
+        if (my $range = first { $_->[0] <= $slice_z && $_->[1] > $slice_z } @{$self->layer_height_ranges}) {
+            $height = $range->[2];
+    
+            # if user set custom height to zero we should just skip the range and resume slicing over it
+            if ($height == 0) {
+                $slice_z += $range->[1] - $range->[0];
+                next;
+            }
+        }
+        */
+        
+        layer_height_profile.push_back(slice_z);
+        layer_height_profile.push_back(height);
+        slice_z += height;
+        layer_height_profile.push_back(slice_z);
+        layer_height_profile.push_back(height);
+    }
+
+    coordf_t last = std::max(slicing_params.first_object_layer_height, layer_height_profile[layer_height_profile.size() - 2]);
+    layer_height_profile.push_back(last);
+    layer_height_profile.push_back(slicing_params.first_object_layer_height);
+    layer_height_profile.push_back(slicing_params.object_print_z_height());
+    layer_height_profile.push_back(slicing_params.first_object_layer_height);
+
+    return layer_height_profile;
+}
+
+template <typename T>
+static inline T clamp(const T low, const T high, const T value)
+{
+    return std::max(low, std::min(high, value));
+}
+
+template <typename T>
+static inline T lerp(const T a, const T b, const T t)
+{
+    assert(t >= T(-EPSILON) && t <= T(1.+EPSILON));
+    return (1. - t) * a + t * b;
+}
+
+void adjust_layer_height_profile(
+    const SlicingParameters     &slicing_params,
+    std::vector<coordf_t> 		&layer_height_profile,
+    coordf_t 					 z,
+    coordf_t 					 layer_thickness_delta,
+    coordf_t 					 band_width,
+    int 						 action)
+{
+     // Constrain the profile variability by the 1st layer height.
+    std::pair<coordf_t, coordf_t> z_span_variable = 
+        std::pair<coordf_t, coordf_t>(
+            slicing_params.first_object_layer_height_fixed() ? slicing_params.first_object_layer_height : 0.,
+            slicing_params.object_print_z_height());
+    if (z < z_span_variable.first || z > z_span_variable.second)
+        return;
+
+	assert(layer_height_profile.size() >= 2);
+
+    // 1) Get the current layer thickness at z.
+    coordf_t current_layer_height = slicing_params.layer_height;
+    for (size_t i = 0; i < layer_height_profile.size(); i += 2) {
+        if (i + 2 == layer_height_profile.size()) {
+            current_layer_height = layer_height_profile[i + 1];
+            break;
+        } else if (layer_height_profile[i + 2] > z) {
+            coordf_t z1 = layer_height_profile[i];
+            coordf_t h1 = layer_height_profile[i + 1];
+            coordf_t z2 = layer_height_profile[i + 2];
+            coordf_t h2 = layer_height_profile[i + 3];
+            current_layer_height = lerp(h1, h2, (z - z1) / (z2 - z1));
+			break;
+        }
+    }
+
+    // 2) Is it possible to apply the delta?
+    switch (action) {
+        case 0:
+        default:
+            if (layer_thickness_delta > 0) {
+                if (current_layer_height >= slicing_params.max_layer_height - EPSILON)
+                    return;
+                layer_thickness_delta = std::min(layer_thickness_delta, slicing_params.max_layer_height - current_layer_height);
+            } else {
+                if (current_layer_height <= slicing_params.min_layer_height + EPSILON)
+                    return;
+                layer_thickness_delta = std::max(layer_thickness_delta, slicing_params.min_layer_height - current_layer_height);
+            }
+            break;
+        case 1:
+            layer_thickness_delta = std::abs(layer_thickness_delta);
+            layer_thickness_delta = std::min(layer_thickness_delta, std::abs(slicing_params.layer_height - current_layer_height));
+            if (layer_thickness_delta < EPSILON)
+                return;
+            break;
+    }
+
+    // 3) Densify the profile inside z +- band_width/2, remove duplicate Zs from the height profile inside the band.
+	coordf_t lo = std::max(z_span_variable.first,  z - 0.5 * band_width);
+	coordf_t hi = std::min(z_span_variable.second, z + 0.5 * band_width);
+    coordf_t z_step = 0.1;
+    size_t i = 0;
+    while (i < layer_height_profile.size() && layer_height_profile[i] < lo)
+        i += 2;
+    i -= 2;
+
+    std::vector<double> profile_new;
+    profile_new.reserve(layer_height_profile.size());
+	assert(i >= 0 && i + 1 < layer_height_profile.size());
+	profile_new.insert(profile_new.end(), layer_height_profile.begin(), layer_height_profile.begin() + i + 2);
+    coordf_t zz = lo;
+    while (zz < hi) {
+        size_t next = i + 2;
+        coordf_t z1 = layer_height_profile[i];
+        coordf_t h1 = layer_height_profile[i + 1];
+        coordf_t height = h1;
+        if (next < layer_height_profile.size()) {
+            coordf_t z2 = layer_height_profile[next];
+            coordf_t h2 = layer_height_profile[next + 1];
+            height = lerp(h1, h2, (zz - z1) / (z2 - z1));
+        }
+        // Adjust height by layer_thickness_delta.
+        coordf_t weight = std::abs(zz - z) < 0.5 * band_width ? (0.5 + 0.5 * cos(2. * M_PI * (zz - z) / band_width)) : 0.;
+        coordf_t height_new = height;
+        switch (action) {
+            case 0:
+            default:
+                height += weight * layer_thickness_delta;
+                break;
+            case 1:
+            {
+                coordf_t delta = height - slicing_params.layer_height;
+                coordf_t step  = weight * layer_thickness_delta;
+                step = (std::abs(delta) > step) ?
+                    (delta > 0) ? -step : step :
+                    -delta;
+                height += step;
+                break;
+            }
+        }
+        // Avoid entering a too short segment.
+        if (profile_new[profile_new.size() - 2] + EPSILON < zz) {
+            profile_new.push_back(zz);
+            profile_new.push_back(clamp(slicing_params.min_layer_height, slicing_params.max_layer_height, height));
+        }
+        zz += z_step;
+        i = next;
+        while (i < layer_height_profile.size() && layer_height_profile[i] < zz)
+            i += 2;
+        i -= 2;
+    }
+
+    i += 2;
+	if (i < layer_height_profile.size()) {
+        if (profile_new[profile_new.size() - 2] + z_step < layer_height_profile[i]) {
+            profile_new.push_back(profile_new[profile_new.size() - 2] + z_step);
+            profile_new.push_back(layer_height_profile[i + 1]);
+        }
+		profile_new.insert(profile_new.end(), layer_height_profile.begin() + i, layer_height_profile.end());
+    }
+    layer_height_profile = std::move(profile_new);
+
+	assert(layer_height_profile.size() > 2);
+	assert(layer_height_profile.size() % 2 == 0);
+	assert(layer_height_profile[0] == 0.);
+#ifdef _DEBUG
+	for (size_t i = 2; i < layer_height_profile.size(); i += 2)
+		assert(layer_height_profile[i - 2] <= layer_height_profile[i]);
+	for (size_t i = 1; i < layer_height_profile.size(); i += 2) {
+		assert(layer_height_profile[i] > slicing_params.min_layer_height - EPSILON);
+		assert(layer_height_profile[i] < slicing_params.max_layer_height + EPSILON);
+	}
+#endif /* _DEBUG */
+}
+
+// Produce object layers as pairs of low / high layer boundaries, stored into a linear vector.
+std::vector<coordf_t> generate_object_layers(
+	const SlicingParameters 	&slicing_params,
+	const std::vector<coordf_t> &layer_height_profile)
+{
+    coordf_t print_z = 0;
+    coordf_t height  = 0;
+
+    std::vector<coordf_t> out;
+
+    if (slicing_params.first_object_layer_height_fixed()) {
+        out.push_back(0);
+        print_z = slicing_params.first_object_layer_height;
+        out.push_back(print_z);
+    }
+
+    size_t idx_layer_height_profile = 0;
+    // loop until we have at least one layer and the max slice_z reaches the object height
+    coordf_t slice_z = print_z + 0.5 * slicing_params.min_layer_height;
+    while (slice_z < slicing_params.object_print_z_height()) {
+        height = slicing_params.min_layer_height;
+        if (idx_layer_height_profile < layer_height_profile.size()) {
+            size_t next = idx_layer_height_profile + 2;
+            for (;;) {
+                if (next >= layer_height_profile.size() || slice_z < layer_height_profile[next])
+                    break;
+                idx_layer_height_profile = next;
+                next += 2;
+            }
+            coordf_t z1 = layer_height_profile[idx_layer_height_profile];
+            coordf_t h1 = layer_height_profile[idx_layer_height_profile + 1];
+            height = h1;
+            if (next < layer_height_profile.size()) {
+                coordf_t z2 = layer_height_profile[next];
+                coordf_t h2 = layer_height_profile[next + 1];
+                height = lerp(h1, h2, (slice_z - z1) / (z2 - z1));
+                assert(height >= slicing_params.min_layer_height - EPSILON && height <= slicing_params.max_layer_height + EPSILON);
+            }
+        }
+        slice_z = print_z + 0.5 * height;
+        if (slice_z >= slicing_params.object_print_z_height())
+            break;
+        assert(height > slicing_params.min_layer_height - EPSILON);
+        assert(height < slicing_params.max_layer_height + EPSILON);
+        out.push_back(print_z);
+        print_z += height;
+        slice_z = print_z + 0.5 * slicing_params.min_layer_height;
+        out.push_back(print_z);
+    }
+
+    //FIXME Adjust the last layer to align with the top object layer exactly?
+    return out;
+}
+
+int generate_layer_height_texture(
+	const SlicingParameters 	&slicing_params,
+	const std::vector<coordf_t> &layers,
+	void *data, int rows, int cols, bool level_of_detail_2nd_level)
+{
+// https://github.com/aschn/gnuplot-colorbrewer
+    std::vector<Point3> palette_raw;
+    palette_raw.push_back(Point3(0x0B2, 0x018, 0x02B));
+    palette_raw.push_back(Point3(0x0D6, 0x060, 0x04D));
+    palette_raw.push_back(Point3(0x0F4, 0x0A5, 0x082));
+    palette_raw.push_back(Point3(0x0FD, 0x0DB, 0x0C7));
+    palette_raw.push_back(Point3(0x0D1, 0x0E5, 0x0F0));
+    palette_raw.push_back(Point3(0x092, 0x0C5, 0x0DE));
+    palette_raw.push_back(Point3(0x043, 0x093, 0x0C3));
+    palette_raw.push_back(Point3(0x021, 0x066, 0x0AC));
+
+    // Clear the main texture and the 2nd LOD level.
+    memset(data, 0, rows * cols * 5);
+    // 2nd LOD level data start
+    unsigned char *data1 = reinterpret_cast<unsigned char*>(data) + rows * cols * 4;
+    int ncells  = std::min((cols-1) * rows, int(ceil(16. * (slicing_params.object_print_z_height() / slicing_params.min_layer_height))));
+    int ncells1 = ncells / 2;
+    int cols1   = cols / 2;
+    coordf_t z_to_cell = coordf_t(ncells-1) / slicing_params.object_print_z_height();
+    coordf_t cell_to_z = slicing_params.object_print_z_height() / coordf_t(ncells-1);
+    coordf_t z_to_cell1 = coordf_t(ncells1-1) / slicing_params.object_print_z_height();
+    coordf_t cell_to_z1 = slicing_params.object_print_z_height() / coordf_t(ncells1-1);
+    // for color scaling
+	coordf_t hscale = 2.f * std::max(slicing_params.max_layer_height - slicing_params.layer_height, slicing_params.layer_height - slicing_params.min_layer_height);
+	if (hscale == 0)
+		// All layers have the same height. Provide some height scale to avoid division by zero.
+		hscale = slicing_params.layer_height;
+    for (size_t idx_layer = 0; idx_layer < layers.size(); idx_layer += 2) {
+        coordf_t lo  = layers[idx_layer];
+		coordf_t hi  = layers[idx_layer + 1];
+        coordf_t mid = 0.5f * (lo + hi);
+		assert(mid <= slicing_params.object_print_z_height());
+		coordf_t h = hi - lo;
+		hi = std::min(hi, slicing_params.object_print_z_height());
+        int cell_first = clamp(0, ncells-1, int(ceil(lo * z_to_cell)));
+        int cell_last  = clamp(0, ncells-1, int(floor(hi * z_to_cell)));
+        for (int cell = cell_first; cell <= cell_last; ++ cell) {
+            coordf_t idxf = (0.5 * hscale + (h - slicing_params.layer_height)) * coordf_t(palette_raw.size()) / hscale;
+            int idx1 = clamp(0, int(palette_raw.size() - 1), int(floor(idxf)));
+            int idx2 = std::min(int(palette_raw.size() - 1), idx1 + 1);
+			coordf_t t = idxf - coordf_t(idx1);
+            const Point3 &color1 = palette_raw[idx1];
+            const Point3 &color2 = palette_raw[idx2];
+
+            coordf_t z = cell_to_z * coordf_t(cell);
+			assert(z >= lo && z <= hi);
+            // Intensity profile to visualize the layers.
+            coordf_t intensity = cos(M_PI * 0.7 * (mid - z) / h);
+
+            // Color mapping from layer height to RGB.
+            Pointf3 color(
+                intensity * lerp(coordf_t(color1.x), coordf_t(color2.x), t), 
+                intensity * lerp(coordf_t(color1.y), coordf_t(color2.y), t),
+                intensity * lerp(coordf_t(color1.z), coordf_t(color2.z), t));
+
+            int row = cell / (cols - 1);
+            int col = cell - row * (cols - 1);
+			assert(row >= 0 && row < rows);
+			assert(col >= 0 && col < cols);
+            unsigned char *ptr = (unsigned char*)data + (row * cols + col) * 4;
+            ptr[0] = clamp<int>(0, 255, int(floor(color.x + 0.5)));
+            ptr[1] = clamp<int>(0, 255, int(floor(color.y + 0.5)));
+            ptr[2] = clamp<int>(0, 255, int(floor(color.z + 0.5)));
+            ptr[3] = 255;
+            if (col == 0 && row > 0) {
+                // Duplicate the first value in a row as a last value of the preceding row.
+                ptr[-4] = ptr[0];
+                ptr[-3] = ptr[1];
+                ptr[-2] = ptr[2];
+                ptr[-1] = ptr[3];
+            }
+        }
+        if (level_of_detail_2nd_level) {
+            cell_first = clamp(0, ncells1-1, int(ceil(lo * z_to_cell1))); 
+            cell_last  = clamp(0, ncells1-1, int(floor(hi * z_to_cell1)));
+            for (int cell = cell_first; cell <= cell_last; ++ cell) {
+                coordf_t idxf = (0.5 * hscale + (h - slicing_params.layer_height)) * coordf_t(palette_raw.size()) / hscale;
+                int idx1 = clamp(0, int(palette_raw.size() - 1), int(floor(idxf)));
+                int idx2 = std::min(int(palette_raw.size() - 1), idx1 + 1);
+    			coordf_t t = idxf - coordf_t(idx1);
+                const Point3 &color1 = palette_raw[idx1];
+                const Point3 &color2 = palette_raw[idx2];
+
+                coordf_t z = cell_to_z1 * coordf_t(cell);
+                assert(z >= lo && z <= hi);
+
+                // Color mapping from layer height to RGB.
+                Pointf3 color(
+                    lerp(coordf_t(color1.x), coordf_t(color2.x), t), 
+                    lerp(coordf_t(color1.y), coordf_t(color2.y), t),
+                    lerp(coordf_t(color1.z), coordf_t(color2.z), t));
+
+                int row = cell / (cols1 - 1);
+                int col = cell - row * (cols1 - 1);
+    			assert(row >= 0 && row < rows/2);
+    			assert(col >= 0 && col < cols/2);
+                unsigned char *ptr = data1 + (row * cols1 + col) * 4;
+                ptr[0] = clamp<int>(0, 255, int(floor(color.x + 0.5)));
+                ptr[1] = clamp<int>(0, 255, int(floor(color.y + 0.5)));
+                ptr[2] = clamp<int>(0, 255, int(floor(color.z + 0.5)));
+                ptr[3] = 255;
+                if (col == 0 && row > 0) {
+                    // Duplicate the first value in a row as a last value of the preceding row.
+                    ptr[-4] = ptr[0];
+                    ptr[-3] = ptr[1];
+                    ptr[-2] = ptr[2];
+                    ptr[-1] = ptr[3];
+                }
+            }
+        }
+    }
+
+    // Returns number of cells of the 0th LOD level.
+    return ncells;
+}
+
+}; // namespace Slic3r
diff --git a/xs/src/libslic3r/Slicing.hpp b/xs/src/libslic3r/Slicing.hpp
new file mode 100644
index 000000000..349be2e4a
--- /dev/null
+++ b/xs/src/libslic3r/Slicing.hpp
@@ -0,0 +1,112 @@
+// Based on implementation by @platsch
+
+#ifndef slic3r_Slicing_hpp_
+#define slic3r_Slicing_hpp_
+
+#include <set>
+#include <vector>
+
+#include "libslic3r.h"
+namespace Slic3r
+{
+
+class PrintConfig;
+class PrintObjectConfig;
+class ModelVolume;
+typedef std::vector<ModelVolume*> ModelVolumePtrs;
+
+// Parameters to guide object slicing and support generation.
+// The slicing parameters account for a raft and whether the 1st object layer is printed with a normal or a bridging flow
+// (using a normal flow over a soluble support, using a bridging flow over a non-soluble support).
+struct SlicingParameters
+{
+	SlicingParameters() { memset(this, 0, sizeof(SlicingParameters)); }
+
+    static SlicingParameters create_from_config(
+        const PrintConfig       &print_config, 
+        const PrintObjectConfig &object_config,
+        coordf_t                 object_height,
+        const std::set<size_t>  &object_extruders);
+
+    // Has any raft layers?
+    bool        has_raft() const { return raft_layers() > 0; }
+    size_t      raft_layers() const { return base_raft_layers + interface_raft_layers; }
+
+    // Is the 1st object layer height fixed, or could it be varied?
+    bool        first_object_layer_height_fixed()  const { return ! has_raft() || first_object_layer_bridging; }
+
+    // Height of the object to be printed. This value does not contain the raft height.
+    coordf_t    object_print_z_height() const { return object_print_z_max - object_print_z_min; }
+
+    // Number of raft layers.
+    size_t      base_raft_layers;
+    // Number of interface layers including the contact layer.
+    size_t      interface_raft_layers;
+
+    // Layer heights of the raft (base, interface and a contact layer).
+    coordf_t    base_raft_layer_height;
+    coordf_t    interface_raft_layer_height;
+    coordf_t    contact_raft_layer_height;
+    bool        contact_raft_layer_height_bridging;
+
+	// The regular layer height, applied for all but the first layer, if not overridden by layer ranges
+	// or by the variable layer thickness table.
+    coordf_t    layer_height;
+
+    // Thickness of the first layer. This is either the first print layer thickness if printed without a raft,
+    // or a bridging flow thickness if printed over a non-soluble raft,
+    // or a normal layer height if printed over a soluble raft.
+    coordf_t    first_object_layer_height;
+
+    // If the object is printed over a non-soluble raft, the first layer may be printed with a briding flow.
+    bool 		first_object_layer_bridging;
+
+    // Minimum / maximum layer height, to be used for the automatic adaptive layer height algorithm,
+    // or by an interactive layer height editor.
+    coordf_t    min_layer_height;
+    coordf_t    max_layer_height;
+
+    // Bottom and top of the printed object.
+    // If printed without a raft, object_print_z_min = 0 and object_print_z_max = object height.
+    // Otherwise object_print_z_min is equal to the raft height.
+    coordf_t 	object_print_z_min;
+    coordf_t 	object_print_z_max;
+};
+
+typedef std::pair<coordf_t,coordf_t> t_layer_height_range;
+typedef std::map<t_layer_height_range,coordf_t> t_layer_height_ranges;
+
+extern std::vector<coordf_t> layer_height_profile_from_ranges(
+    const SlicingParameters     &slicing_params,
+    const t_layer_height_ranges &layer_height_ranges);
+
+extern std::vector<coordf_t> layer_height_profile_adaptive(
+    const SlicingParameters     &slicing_params,
+    const t_layer_height_ranges &layer_height_ranges,
+    const ModelVolumePtrs       &volumes);
+
+extern void adjust_layer_height_profile(
+    const SlicingParameters     &slicing_params,
+    std::vector<coordf_t>       &layer_height_profile,
+    coordf_t                     z,
+    coordf_t                     layer_thickness_delta, 
+    coordf_t                     band_width,
+    int                          action);
+
+// Produce object layers as pairs of low / high layer boundaries, stored into a linear vector.
+// The object layers are based at z=0, ignoring the raft layers.
+extern std::vector<coordf_t> generate_object_layers(
+    const SlicingParameters     &slicing_params,
+    const std::vector<coordf_t> &layer_height_profile);
+
+// Produce a 1D texture packed into a 2D texture describing in the RGBA format
+// the planned object layers.
+// Returns number of cells used by the texture of the 0th LOD level.
+extern int generate_layer_height_texture(
+    const SlicingParameters     &slicing_params,
+    const std::vector<coordf_t> &layers,
+    void *data, int rows, int cols, bool level_of_detail_2nd_level);
+
+}; // namespace Slic3r
+
+#endif /* slic3r_Slicing_hpp_ */
diff --git a/xs/src/libslic3r/SlicingAdaptive.cpp b/xs/src/libslic3r/SlicingAdaptive.cpp
new file mode 100644
index 000000000..ff0da7636
--- /dev/null
+++ b/xs/src/libslic3r/SlicingAdaptive.cpp
@@ -0,0 +1,140 @@
+#include "libslic3r.h"
+#include "TriangleMesh.hpp"
+#include "SlicingAdaptive.hpp"
+
+namespace Slic3r
+{
+
+void SlicingAdaptive::clear()
+{
+	m_meshes.clear();
+	m_faces.clear();
+	m_face_normal_z.clear();
+}
+
+std::pair<float, float> face_z_span(const stl_facet *f)
+{
+	return std::pair<float, float>(
+		std::min(std::min(f->vertex[0].z, f->vertex[1].z), f->vertex[2].z),
+		std::max(std::max(f->vertex[0].z, f->vertex[1].z), f->vertex[2].z));
+}
+
+void SlicingAdaptive::prepare()
+{
+	// 1) Collect faces of all meshes.
+	int nfaces_total = 0;
+	for (std::vector<const TriangleMesh*>::const_iterator it_mesh = m_meshes.begin(); it_mesh != m_meshes.end(); ++ it_mesh)
+		nfaces_total += (*it_mesh)->stl.stats.number_of_facets;
+	m_faces.reserve(nfaces_total);
+	for (std::vector<const TriangleMesh*>::const_iterator it_mesh = m_meshes.begin(); it_mesh != m_meshes.end(); ++ it_mesh)
+		for (int i = 0; i < (*it_mesh)->stl.stats.number_of_facets; ++ i)
+			m_faces.push_back((*it_mesh)->stl.facet_start + i);
+
+	// 2) Sort faces lexicographically by their Z span.
+	std::sort(m_faces.begin(), m_faces.end(), [](const stl_facet *f1, const stl_facet *f2) {
+		std::pair<float, float> span1 = face_z_span(f1);
+		std::pair<float, float> span2 = face_z_span(f2);
+		return span1 < span2;
+	});
+
+	// 3) Generate Z components of the facet normals.
+	m_face_normal_z.assign(m_faces.size(), 0.f);
+    for (size_t iface = 0; iface < m_faces.size(); ++ iface)
+    	m_face_normal_z[iface] = m_faces[iface]->normal.z;
+}
+
+float SlicingAdaptive::cusp_height(float z, float cusp_value, int &current_facet)
+{
+	float height = m_slicing_params.max_layer_height;
+	bool first_hit = false;
+	
+	// find all facets intersecting the slice-layer
+	int ordered_id = current_facet;
+	for (; ordered_id < int(m_faces.size()); ++ ordered_id) {
+		std::pair<float, float> zspan = face_z_span(m_faces[ordered_id]);
+		// facet's minimum is higher than slice_z -> end loop
+		if (zspan.first >= z)
+			break;
+		// facet's maximum is higher than slice_z -> store the first event for next cusp_height call to begin at this point
+		if (zspan.second > z) {
+			// first event?
+			if (! first_hit) {
+				first_hit = true;
+				current_facet = ordered_id;
+			}
+			// skip touching facets which could otherwise cause small cusp values
+			if (zspan.second <= z + EPSILON)
+				continue;
+			// compute cusp-height for this facet and store minimum of all heights
+			float normal_z = m_face_normal_z[ordered_id];
+			height = std::min(height, (normal_z == 0.f) ? 9999.f : std::abs(cusp_value / normal_z));
+		}
+	}
+
+	// lower height limit due to printer capabilities
+	height = std::max(height, float(m_slicing_params.min_layer_height));
+
+	// check for sloped facets inside the determined layer and correct height if necessary
+	if (height > m_slicing_params.min_layer_height) {
+		for (; ordered_id < int(m_faces.size()); ++ ordered_id) {
+			std::pair<float, float> zspan = face_z_span(m_faces[ordered_id]);
+			// facet's minimum is higher than slice_z + height -> end loop
+			if (zspan.first >= z + height)
+				break;
+
+			// skip touching facets which could otherwise cause small cusp values
+			if (zspan.second <= z + EPSILON)
+				continue;
+
+			// Compute cusp-height for this facet and check against height.
+			float normal_z = m_face_normal_z[ordered_id];
+			float cusp = (normal_z == 0) ? 9999 : abs(cusp_value / normal_z);
+			
+			float z_diff = zspan.first - z;
+
+			// handle horizontal facets
+			if (m_face_normal_z[ordered_id] > 0.999) {
+				// Slic3r::debugf "cusp computation, height is reduced from %f", $height;
+				height = z_diff;
+				// Slic3r::debugf "to %f due to near horizontal facet\n", $height;
+			} else if (cusp > z_diff) {
+				if (cusp < height) {
+					// Slic3r::debugf "cusp computation, height is reduced from %f", $height;
+					height = cusp;
+					// Slic3r::debugf "to %f due to new cusp height\n", $height;
+				}
+			} else {
+				// Slic3r::debugf "cusp computation, height is reduced from %f", $height;
+				height = z_diff;
+				// Slic3r::debugf "to z-diff: %f\n", $height;
+			}
+		}
+		// lower height limit due to printer capabilities again
+		height = std::max(height, float(m_slicing_params.min_layer_height));
+	}
+	
+//	Slic3r::debugf "cusp computation, layer-bottom at z:%f, cusp_value:%f, resulting layer height:%f\n", unscale $z, $cusp_value, $height;	
+	return height; 
+}
+
+// Returns the distance to the next horizontal facet in Z-dir 
+// to consider horizontal object features in slice thickness
+float SlicingAdaptive::horizontal_facet_distance(float z)
+{
+	for (size_t i = 0; i < m_faces.size(); ++ i) {
+		std::pair<float, float> zspan = face_z_span(m_faces[i]);
+		// facet's minimum is higher than max forward distance -> end loop
+		if (zspan.first > z + m_slicing_params.max_layer_height)
+			break;
+		// min_z == max_z -> horizontal facet
+		if (zspan.first > z && zspan.first == zspan.second)
+			return zspan.first - z;
+	}
+	
+	// objects maximum?
+	return (z + m_slicing_params.max_layer_height > m_slicing_params.object_print_z_height()) ? 
+		std::max<float>(m_slicing_params.object_print_z_height() - z, 0.f) :
+		m_slicing_params.max_layer_height;
+}
+
+}; // namespace Slic3r
diff --git a/xs/src/libslic3r/SlicingAdaptive.hpp b/xs/src/libslic3r/SlicingAdaptive.hpp
new file mode 100644
index 000000000..bfd081d81
--- /dev/null
+++ b/xs/src/libslic3r/SlicingAdaptive.hpp
@@ -0,0 +1,36 @@
+// Based on implementation by @platsch
+
+#ifndef slic3r_SlicingAdaptive_hpp_
+#define slic3r_SlicingAdaptive_hpp_
+
+#include "Slicing.hpp"
+#include "admesh/stl.h"
+
+namespace Slic3r
+{
+
+class TriangleMesh;
+
+class SlicingAdaptive
+{
+public:
+	void clear();
+	void set_slicing_parameters(SlicingParameters params) { m_slicing_params = params; }
+	void add_mesh(const TriangleMesh *mesh) { m_meshes.push_back(mesh); }
+	void prepare();
+	float cusp_height(float z, float cusp_value, int &current_facet);
+	float horizontal_facet_distance(float z);
+
+protected:
+	SlicingParameters 					m_slicing_params;
+
+	std::vector<const TriangleMesh*>	m_meshes;
+	// Collected faces of all meshes, sorted by raising Z of the bottom most face.
+	std::vector<const stl_facet*>		m_faces;
+	// Z component of face normals, normalized.
+	std::vector<float>					m_face_normal_z;
+};
+
+}; // namespace Slic3r
+
+#endif /* slic3r_SlicingAdaptive_hpp_ */
diff --git a/xs/src/libslic3r/SupportMaterial.cpp b/xs/src/libslic3r/SupportMaterial.cpp
index 29b016568..8bf9e9fc7 100644
--- a/xs/src/libslic3r/SupportMaterial.cpp
+++ b/xs/src/libslic3r/SupportMaterial.cpp
@@ -5,11 +5,12 @@
 #include "Print.hpp"
 #include "SupportMaterial.hpp"
 #include "Fill/FillBase.hpp"
+#include "EdgeGrid.hpp"
 
 #include <cmath>
-#include <cassert>
 #include <memory>
 #include <boost/log/trivial.hpp>
+#include <unordered_set>
 
 // #define SLIC3R_DEBUG
 
@@ -19,6 +20,8 @@
     #include "SVG.hpp"
 #endif
 
+#include <cassert>
+
 namespace Slic3r {
 
 // Increment used to reach MARGIN in steps to avoid trespassing thin objects
@@ -241,16 +244,10 @@ void PrintObjectSupportMaterial::generate(PrintObject &object)
 
     // Determine the bottom contact surfaces of the supports over the top surfaces of the object.
     // Depending on whether the support is soluble or not, the contact layer thickness is decided.
-    MyLayersPtr bottom_contacts = this->bottom_contact_layers(object, top_contacts, layer_storage);
-
-#ifdef SLIC3R_DEBUG
-    for (MyLayersPtr::const_iterator it = bottom_contacts.begin(); it != bottom_contacts.end(); ++ it) {
-        const MyLayer &layer = *(*it);
-        ::Slic3r::SVG svg(debug_out_path("support-bottom-contacts-%d-%lf.svg", iRun, layer.print_z), get_extents(layer.polygons));
-        Slic3r::ExPolygons expolys = union_ex(layer.polygons, false);
-        svg.draw(expolys);
-    }
-#endif /* SLIC3R_DEBUG */
+    std::vector<Polygons> layer_support_areas;
+    MyLayersPtr bottom_contacts = this->bottom_contact_layers_and_layer_support_areas(
+        object, top_contacts, layer_storage,
+        layer_support_areas);
 
     BOOST_LOG_TRIVIAL(info) << "Support generator - Trimming top contacts by bottom contacts";
 
@@ -271,7 +268,7 @@ void PrintObjectSupportMaterial::generate(PrintObject &object)
     BOOST_LOG_TRIVIAL(info) << "Support generator - Creating base layers";
 
     // Fill in intermediate layers between the top / bottom support contact layers, trimmed by the object.
-    this->generate_base_layers(object, bottom_contacts, top_contacts, intermediate_layers);
+    this->generate_base_layers(object, bottom_contacts, top_contacts, intermediate_layers, layer_support_areas);
 
 #ifdef SLIC3R_DEBUG
     for (MyLayersPtr::const_iterator it = intermediate_layers.begin(); it != intermediate_layers.end(); ++ it) {
@@ -437,7 +434,8 @@ Polygons collect_slices_outer(const Layer &layer)
 }
 
 // Find the top contact surfaces of the support or the raft.
-PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_layers(const PrintObject &object, MyLayerStorage &layer_storage) const
+PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_layers(
+    const PrintObject &object, MyLayerStorage &layer_storage) const
 {
 #ifdef SLIC3R_DEBUG
     static int iRun = 0;
@@ -577,7 +575,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
                         // workaround for Clipper bug, see Slic3r::Polygon::clip_as_polyline()
                         for (Polylines::iterator it = overhang_perimeters.begin(); it != overhang_perimeters.end(); ++ it)
                             it->points[0].x += 1;
-                        diff(overhang_perimeters, lower_grown_slices, &overhang_perimeters);
+                        overhang_perimeters = diff_pl(overhang_perimeters, lower_grown_slices);
                         
                         // only consider straight overhangs
                         // only consider overhangs having endpoints inside layer's slices
@@ -590,13 +588,9 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
                             if (it->is_straight()) {
                                 it->extend_start(fw);
                                 it->extend_end(fw);
-                                if (layer.slices.contains(it->first_point()) && layer.slices.contains(it->last_point())) {
+                                if (layer.slices.contains(it->first_point()) && layer.slices.contains(it->last_point()))
                                     // Offset a polyline into a polygon.
-                                    Polylines tmp; tmp.push_back(*it);
-                                    Polygons out;
-                                    offset(tmp, &out, 0.5f * w + 10.f);
-                                    polygons_append(bridged_perimeters, out);
-                                }
+                                    polygons_append(bridged_perimeters, offset(*it, 0.5f * w + 10.f));
                             }
                         }
                         bridged_perimeters = union_(bridged_perimeters);
@@ -613,13 +607,9 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
 
                         Polygons unsupported_bridge_polygons;                        
                         for (Polylines::const_iterator it = layerm.unsupported_bridge_edges.polylines.begin(); 
-                            it != layerm.unsupported_bridge_edges.polylines.end(); ++ it) {
+                            it != layerm.unsupported_bridge_edges.polylines.end(); ++ it)
                             // Offset a polyline into a polygon.
-                            Polylines tmp; tmp.push_back(*it);
-                            Polygons out;
-                            offset(tmp, &out, scale_(SUPPORT_MATERIAL_MARGIN));
-                            polygons_append(unsupported_bridge_polygons, out);
-                        }
+                            polygons_append(unsupported_bridge_polygons, offset(*it, scale_(SUPPORT_MATERIAL_MARGIN)));
                         polygons_append(diff_polygons, intersection(unsupported_bridge_polygons, bridges));
                     } else {
                         // just remove bridged areas
@@ -664,10 +654,9 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
                             offset(
                                 diff_polygons,
                                 SUPPORT_MATERIAL_MARGIN / NUM_MARGIN_STEPS,
-                                CLIPPER_OFFSET_SCALE,
                                 ClipperLib::jtRound,
                                 // round mitter limit
-                                scale_(0.05) * CLIPPER_OFFSET_SCALE),
+                                scale_(0.05)),
                             slices_margin);
                     }
                 }
@@ -753,14 +742,188 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
     return contact_out;
 }
 
-PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_contact_layers(
-    const PrintObject &object, const MyLayersPtr &top_contacts, MyLayerStorage &layer_storage) const
+#if 0
+typedef std::unordered_set<Point,PointHash> PointHashMap;
+
+void fillet(Polygon &poly, PointHashMap &new_points_hash_map)
 {
+    if (poly.points.size() < 3)
+        // an invalid contour will not be modified.
+        return;
+
+    // Flag describing a contour point.
+    std::vector<char> point_flag(std::vector<char>(poly.points.size(), 0));
+
+    // Does a point belong to new points?
+    for (size_t i = 0; i < poly.points.size(); ++ i)
+        if (new_points_hash_map.find(poly.points[i]) != new_points_hash_map.end())
+            // Mark the point as from the new contour.
+            point_flag[i] = 1;
+
+    // Mark the intersection points between the old and new contours.
+    size_t j = poly.points.size() - 1;
+    bool has_some = false;
+    for (size_t i = 0; i < poly.points.size(); j = i, ++ i)
+        if ((point_flag[i] ^ point_flag[j]) & 1) {
+            point_flag[(point_flag[i] & 1) ? j : i] |= 2;
+            has_some = true;
+        }
+    if (! has_some)
+        return;
+
+#ifdef SLIC3R_DEBUG
+    static int iRun = 0;
+    ++ iRun;
+    {
+        FILE *pfile = ::fopen(debug_out_path("fillet-in-%d.bin", iRun).c_str(), "wb");
+        size_t cnt = poly.points.size();
+        ::fwrite(&cnt, 1, sizeof(cnt), pfile);
+        ::fwrite(poly.points.data(), cnt, sizeof(Point), pfile);
+        cnt = new_points_hash_map.size();
+        ::fwrite(&cnt, 1, sizeof(cnt), pfile);
+        for (PointHashMap::iterator it = new_points_hash_map.begin(); it != new_points_hash_map.end(); ++ it) {
+            const Point &pt = *it;
+            ::fwrite(&pt, 1, sizeof(Point), pfile);
+        }
+        ::fclose(pfile);
+    }
+    ::Slic3r::SVG svg(debug_out_path("fillet-%d.svg", iRun), get_extents(poly));
+    svg.draw(poly, "black", scale_(0.05));
+    for (size_t i = 0; i < poly.points.size(); ++ i) {
+        const Point &pt1 = poly.points[i];
+        const Point &pt2 = poly.points[(i+1)%poly.points.size()];
+        if (new_points_hash_map.find(pt1) != new_points_hash_map.end())
+            svg.draw(Line(pt1, pt2), "red", scale_(0.035));
+        if (new_points_hash_map.find(pt1) != new_points_hash_map.end() &&
+            new_points_hash_map.find(pt2) != new_points_hash_map.end())
+            svg.draw(Line(pt1, pt2), "red", scale_(0.05));
+    }
+#endif
+
+    // Mark a range of points around the intersection points.
+    const double rounding_range = scale_(1.5);
+    std::vector<Pointf> pts;
+    pts.reserve(poly.points.size());
+    for (int i = 0; i < int(poly.points.size()); ++ i) {
+        if (point_flag[i] & 2) {
+            point_flag[i] |= 4;
+            // Extend a filetting span left / right from i by an Euclidian distance of rounding_range.
+            double d = 0.f;
+            const Point *pt = &poly.points[i];
+            for (int j = 1; j < int(poly.points.size()); ++ j) {
+                int idx = (i + j) % poly.points.size();
+                const Point *pt2 = &poly.points[idx];
+                d += pt->distance_to(*pt2);
+                if (d > rounding_range)
+                    break;
+                point_flag[idx] |= 4;
+                //pt = pt2;
+            }
+            for (int j = 1; j < int(poly.points.size()); ++ j) {
+                int idx = (i + int(poly.points.size()) - j) % poly.points.size();
+                const Point *pt2 = &poly.points[idx];
+                d += pt->distance_to(*pt2);
+                if (d > rounding_range)
+                    break;
+                point_flag[idx] |= 4;
+                //pt = pt2;
+            }
+        }
+        pts.push_back(Pointf(poly.points[i].x, poly.points[i].y));
+    }
+
+    //FIXME avoid filetting over long edges. Insert new points into long edges at the ends of the filetting interval.
+
+    // Perform the filetting over the marked vertices.
+    std::vector<Pointf> pts2(pts);
+    double laplacian_weight = 0.5;
+    for (size_t i_round = 0; i_round < 5; ++ i_round) {
+        for (size_t i = 0; i < int(pts.size()); ++ i) {
+            if (point_flag[i] & 4) {
+                size_t prev = (i == 0) ? pts.size() - 1 : i - 1;
+                size_t next = (i + 1 == pts.size()) ? 0 : i + 1;
+                Pointf &p0 = pts[prev];
+                Pointf &p1 = pts[i];
+                Pointf &p2 = pts[next];
+                // Is the point reflex?
+                coordf_t c = cross(p1 - p0, p2 - p1);
+                if (c < 0)
+                    // The point is reflex, perform Laplacian smoothing.
+                    pts2[i] = (1. - laplacian_weight) * pts[i] + (0.5 * laplacian_weight) * (pts[prev] + pts[next]);
+            }
+        }
+        pts.swap(pts2);
+    }
+
+    // Mark vertices representing short edges for removal.
+
+    // Convert the filetted points back, remove points marked for removal.
+    j = 0;
+    for (size_t i = 0; i < poly.points.size(); ++ i) {
+        if (point_flag[i] & 8)
+            // Remove this point.
+            continue;
+        if (point_flag[i] & 4)
+            // Update the point coordinates.
+            poly.points[i] = Point(pts[i].x, pts[i].y);
+        if (j < i)
+            poly.points[j] = poly.points[i];
+        ++ j;
+    }
+    if (j < poly.points.size())
+        poly.points.erase(poly.points.begin() + j, poly.points.end());
+
+#ifdef SLIC3R_DEBUG
+    svg.draw_outline(poly, "blue", scale_(0.025));
+#endif /* SLIC3R_DEBUG */
+}
+
+void fillet(Polygons &polygons, PointHashMap &new_points_hash_map)
+{
+    for (Polygons::iterator it = polygons.begin(); it != polygons.end(); ++ it)
+        fillet(*it, new_points_hash_map);
+}
+
+void union_and_fillet(Polygons &polygons, size_t n_polygons_old)
+{
+    if (n_polygons_old == polygons.size())
+        // No new polygons.
+        return;
+
+    // Fill in the new_points hash table with points of new contours.
+    PointHashMap new_points;
+    for (size_t i = n_polygons_old; i < polygons.size(); ++ i) {
+        const Polygon &poly = polygons[i];
+        for (size_t j = 0; j < poly.points.size(); ++ j)
+            new_points.insert(poly.points[j]);
+    }
+    // Merge the newly added regions. Don't use the safety offset, the offset has been added already.
+    polygons = union_(polygons, false);
+    // Fillet transition between the old and new points.
+    fillet(polygons, new_points);
+}
+#endif
+
+// Collect
+PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_contact_layers_and_layer_support_areas(
+    const PrintObject &object, const MyLayersPtr &top_contacts, MyLayerStorage &layer_storage,
+    std::vector<Polygons> &layer_support_areas) const
+{
+#ifdef SLIC3R_DEBUG
+    static int iRun = 0;
+    ++ iRun; 
+#endif /* SLIC3R_DEBUG */
+
+    // Allocate empty surface areas, one per object layer.
+    layer_support_areas.assign(object.total_layer_count(), Polygons());
+
     // find object top surfaces
     // we'll use them to clip our support and detect where does it stick
     MyLayersPtr bottom_contacts;
-    if (! m_object_config->support_material_buildplate_only.value && ! top_contacts.empty())
+
+    if (! top_contacts.empty()) 
     {
+        // There is some support to be built, if there are non-empty top surfaces detected.
         // Sum of unsupported contact areas above the current layer.print_z.
         Polygons  projection;
         // Last top contact layer visited when collecting the projection of contact areas.
@@ -768,56 +931,116 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
         for (int layer_id = int(object.total_layer_count()) - 2; layer_id >= 0; -- layer_id) {
             BOOST_LOG_TRIVIAL(trace) << "Support generator - bottom_contact_layers - layer " << layer_id;
             const Layer &layer = *object.get_layer(layer_id);
-            Polygons top = collect_region_slices_by_type(layer, stTop);
-            if (top.empty())
-                continue;
+            // Top surfaces of this layer, to be used to stop the surface volume from growing down.
+            Polygons top;
+            if (! m_object_config->support_material_buildplate_only)
+                top = collect_region_slices_by_type(layer, stTop);
             size_t projection_size_old = projection.size();
             // Collect projections of all contact areas above or at the same level as this top surface.
             for (; contact_idx >= 0 && top_contacts[contact_idx]->print_z >= layer.print_z; -- contact_idx) {
+                Polygons polygons_new;
                 // Contact surfaces are expanded away from the object, trimmed by the object.
                 // Use a slight positive offset to overlap the touching regions.
-                polygons_append(projection, offset(top_contacts[contact_idx]->polygons, SCALED_EPSILON));
+                polygons_append(polygons_new, offset(top_contacts[contact_idx]->polygons, SCALED_EPSILON));
+                size_t size1 = polygons_new.size();
                 // These are the overhang surfaces. They are touching the object and they are not expanded away from the object.
                 // Use a slight positive offset to overlap the touching regions.
-                polygons_append(projection, offset(*top_contacts[contact_idx]->aux_polygons, SCALED_EPSILON));
+                polygons_append(polygons_new, offset(*top_contacts[contact_idx]->aux_polygons, SCALED_EPSILON));
+#if 0
+                union_and_fillet(polygons_new, size1);
+#else
+                union_(polygons_new);
+#endif
+                polygons_append(projection, std::move(polygons_new));
             }
             if (projection.empty())
                 continue;
-            if (projection_size_old < projection.size()) {
-                // Merge the newly added regions. Don't use the safety offset, the offset has been added already.
-                projection = union_(projection, false);
+#if 0
+            union_and_fillet(projection, projection_size_old);
+#else
+            union_(projection);
+#endif
+    #ifdef SLIC3R_DEBUG
+            {
+                BoundingBox bbox = get_extents(projection);
+                bbox.merge(get_extents(top));
+                ::Slic3r::SVG svg(debug_out_path("support-bottom-layers-raw-%d-%lf.svg", iRun, layer.print_z), bbox);
+                svg.draw(union_ex(top, false), "blue", 0.5f);
+                svg.draw(union_ex(projection, true), "red", 0.5f);
+                svg.draw(layer.slices.expolygons, "green", 0.5f);
             }
+    #endif /* SLIC3R_DEBUG */
+
             // Now find whether any projection of the contact surfaces above layer.print_z not yet supported by any 
             // top surfaces above layer.print_z falls onto this top surface. 
             // touching are the contact surfaces supported exclusively by this top surfaaces.
             // Don't use a safety offset as it has been applied during insertion of polygons.
-            Polygons touching = intersection(top, projection, false);
-            if (touching.empty())
-                continue;
-            // Allocate a new bottom contact layer.
-            MyLayer &layer_new = layer_allocate(layer_storage, sltBottomContact);
-            bottom_contacts.push_back(&layer_new);
-            // Grow top surfaces so that interface and support generation are generated
-            // with some spacing from object - it looks we don't need the actual
-            // top shapes so this can be done here
-            layer_new.height  = m_soluble_interface ? 
-                // Align the interface layer with the object's layer height.
-                object.get_layer(layer_id + 1)->height :
-                // Place a bridge flow interface layer over the top surface.
-                m_support_material_interface_flow.nozzle_diameter;
-            layer_new.print_z = layer.print_z + layer_new.height + 
-                (m_soluble_interface ? 0. : m_object_config->support_material_contact_distance.value);
-            layer_new.bottom_z = layer.print_z;
-            layer_new.idx_object_layer_below = layer_id;
-            layer_new.bridging = ! m_soluble_interface;
-            //FIXME how much to inflate the top surface?
-            layer_new.polygons = offset(touching, float(m_support_material_flow.scaled_width()));
-            // Remove the areas that touched from the projection that will continue on next, lower, top surfaces.
-            projection = diff(projection, touching);
-        }
-    }
+            if (! top.empty()) {
+                Polygons touching = intersection(top, projection, false);
+                if (! touching.empty()) {
+                    // Allocate a new bottom contact layer.
+                    MyLayer &layer_new = layer_allocate(layer_storage, sltBottomContact);
+                    bottom_contacts.push_back(&layer_new);
+                    // Grow top surfaces so that interface and support generation are generated
+                    // with some spacing from object - it looks we don't need the actual
+                    // top shapes so this can be done here
+                    layer_new.height  = m_soluble_interface ? 
+                        // Align the interface layer with the object's layer height.
+                        object.get_layer(layer_id + 1)->height :
+                        // Place a bridge flow interface layer over the top surface.
+                        m_support_material_interface_flow.nozzle_diameter;
+                    layer_new.print_z = layer.print_z + layer_new.height + 
+                        (m_soluble_interface ? 0. : m_object_config->support_material_contact_distance.value);
+                    layer_new.bottom_z = layer.print_z;
+                    layer_new.idx_object_layer_below = layer_id;
+                    layer_new.bridging = ! m_soluble_interface;
+                    //FIXME how much to inflate the top surface?
+                    layer_new.polygons = offset(touching, float(m_support_material_flow.scaled_width()));
+        #ifdef SLIC3R_DEBUG
+                    {
+                        ::Slic3r::SVG svg(debug_out_path("support-bottom-contacts-%d-%lf.svg", iRun, layer_new.print_z), get_extents(layer_new.polygons));
+                        Slic3r::ExPolygons expolys = union_ex(layer_new.polygons, false);
+                        svg.draw(expolys);
+                    }
+        #endif /* SLIC3R_DEBUG */
+                }
+            } // ! top.empty()
+
+            remove_sticks(projection);
+            remove_degenerate(projection);
+
+            // Create an EdgeGrid, initialize it with projection, initialize signed distance field.
+            Slic3r::EdgeGrid::Grid grid;
+            coord_t grid_resolution = scale_(1.5f);
+            BoundingBox bbox = get_extents(projection);
+            bbox.offset(20);
+            bbox.align_to_grid(grid_resolution);
+			grid.set_bbox(bbox);
+			grid.create(projection, grid_resolution);
+            grid.calculate_sdf();
+
+            // Extract a bounding contour from the grid.
+            Polygons projection_simplified = grid.contours_simplified();
+#ifdef SLIC3R_DEBUG
+            {
+                BoundingBox bbox = get_extents(projection);
+                bbox.merge(get_extents(projection_simplified));
+
+                ::Slic3r::SVG svg(debug_out_path("support-bottom-contacts-simplified-%d-%d.svg", iRun, layer_id), bbox);
+                svg.draw(union_ex(projection, false), "blue", 0.5);
+                svg.draw(union_ex(projection_simplified, false), "red", 0.5);
+            }
+#endif /* SLIC3R_DEBUG */
+            projection = std::move(projection_simplified);
+
+            // Remove the areas that touched from the projection that will continue on next, lower, top surfaces. 
+            // projection = diff(projection, touching);
+            projection = diff(projection, to_polygons(layer.slices.expolygons), true);
+            layer_support_areas[layer_id] = projection;
+        }
+        std::reverse(bottom_contacts.begin(), bottom_contacts.end());
+    } // ! top_contacts.empty()
 
-	std::reverse(bottom_contacts.begin(), bottom_contacts.end());
     return bottom_contacts;
 }
 
@@ -952,7 +1175,8 @@ void PrintObjectSupportMaterial::generate_base_layers(
     const PrintObject   &object,
     const MyLayersPtr   &bottom_contacts,
     const MyLayersPtr   &top_contacts,
-    MyLayersPtr         &intermediate_layers) const
+    MyLayersPtr         &intermediate_layers,
+    std::vector<Polygons> &layer_support_areas) const
 {
 #ifdef SLIC3R_DEBUG
     static int iRun = 0;
@@ -965,25 +1189,50 @@ void PrintObjectSupportMaterial::generate_base_layers(
     // coordf_t fillet_radius_scaled = scale_(m_object_config->support_material_spacing);
     int idx_top_contact_above = int(top_contacts.size()) - 1;
     int idx_bottom_contact_overlapping = int(bottom_contacts.size()) - 1;
+    int idx_object_layer_above = int(object.total_layer_count()) - 1;
     for (int idx_intermediate = int(intermediate_layers.size()) - 1; idx_intermediate >= 0; -- idx_intermediate)
     {
+        BOOST_LOG_TRIVIAL(trace) << "Support generator - generate_base_layers - creating layer " << 
+            idx_intermediate << " of " << intermediate_layers.size();
         MyLayer &layer_intermediate = *intermediate_layers[idx_intermediate];
 
-        // New polygons for layer_intermediate.
-        Polygons polygons_new;
-
         // Find a top_contact layer touching the layer_intermediate from above, if any, and collect its polygons into polygons_new.
         while (idx_top_contact_above >= 0 && top_contacts[idx_top_contact_above]->bottom_z > layer_intermediate.print_z + EPSILON)
             -- idx_top_contact_above;
-		if (idx_top_contact_above >= 0 && top_contacts[idx_top_contact_above]->print_z > layer_intermediate.print_z)
-            polygons_append(polygons_new, top_contacts[idx_top_contact_above]->polygons);
- 
+
+        // New polygons for layer_intermediate.
+        Polygons polygons_new;
+
+#if 0
         // Add polygons projected from the intermediate layer above.
         if (idx_intermediate + 1 < int(intermediate_layers.size()))
             polygons_append(polygons_new, intermediate_layers[idx_intermediate+1]->polygons);
 
+		if (idx_top_contact_above >= 0 && top_contacts[idx_top_contact_above]->print_z > layer_intermediate.print_z) {
+            // Contact surfaces are expanded away from the object, trimmed by the object.
+            // Use a slight positive offset to overlap the touching regions.
+            Polygons polygons_new2;
+            polygons_append(polygons_new2, offset(top_contacts[idx_top_contact_above]->polygons, SCALED_EPSILON));
+            size_t size2 = polygons_new2.size();
+            // These are the overhang surfaces. They are touching the object and they are not expanded away from the object.
+            // Use a slight positive offset to overlap the touching regions.
+            polygons_append(polygons_new2, offset(*top_contacts[idx_top_contact_above]->aux_polygons, SCALED_EPSILON));
+            union_and_fillet(polygons_new2, size2);
+            if (! polygons_new2.empty()) {
+                size_t polygons_size_old = polygons_new.size();
+                polygons_append(polygons_new, std::move(polygons_new2));
+                union_and_fillet(polygons_new, polygons_size_old);
+            }
+        }
+#else
+        // Use the precomputed layer_support_areas.
+        while (idx_object_layer_above > 0 && object.get_layer(idx_object_layer_above - 1)->print_z > layer_intermediate.print_z - EPSILON)
+            -- idx_object_layer_above;
+        polygons_new = layer_support_areas[idx_object_layer_above];
+#endif
+ 
         // Polygons to trim polygons_new.
-        Polygons polygons_trimming;
+        Polygons polygons_trimming; 
 
         // Find the first top_contact layer intersecting with this layer.
         int idx_top_contact_overlapping = idx_top_contact_above;
@@ -1004,7 +1253,7 @@ void PrintObjectSupportMaterial::generate_base_layers(
             -- idx_bottom_contact_overlapping;
         // Collect all the top_contact layer intersecting with this layer.
         for (int i = idx_bottom_contact_overlapping; i >= 0; -- i) {
-            MyLayer &layer_bottom_overlapping = *bottom_contacts[idx_bottom_contact_overlapping];
+			MyLayer &layer_bottom_overlapping = *bottom_contacts[i];
             if (layer_bottom_overlapping.print_z < layer_intermediate.print_z - layer_intermediate.height + EPSILON)
                 break; 
             polygons_append(polygons_trimming, layer_bottom_overlapping.polygons);
@@ -1038,9 +1287,8 @@ void PrintObjectSupportMaterial::generate_base_layers(
                     $fillet_radius_scaled, 
                     -$fillet_radius_scaled,
                     # Use a geometric offsetting for filleting.
-                    CLIPPER_OFFSET_SCALE,
                     JT_ROUND,
-                    0.2*$fillet_radius_scaled*CLIPPER_OFFSET_SCALE),
+                    0.2*$fillet_radius_scaled),
                 $trim_polygons,
                 false); // don't apply the safety offset.
         }
@@ -1063,6 +1311,9 @@ void PrintObjectSupportMaterial::generate_base_layers(
     size_t idx_object_layer_overlapping = 0;
     // For all intermediate support layers:
     for (MyLayersPtr::iterator it_layer = intermediate_layers.begin(); it_layer != intermediate_layers.end(); ++ it_layer) {
+        BOOST_LOG_TRIVIAL(trace) << "Support generator - generate_base_layers - trimmming layer " << 
+            (it_layer - intermediate_layers.begin()) << " of " << intermediate_layers.size();
+
         MyLayer &layer_intermediate = *(*it_layer);
         if (layer_intermediate.polygons.empty())
             // Empty support layer, nothing to trim.
@@ -1337,10 +1588,8 @@ void LoopInterfaceProcessor::generate(MyLayerExtruded &top_contact_layer, const
         // Positions of the loop centers.
         Polygons circles;
         Polygons overhang_with_margin = offset(overhang_polygons, 0.5f * flow.scaled_width());
-        for (Polygons::const_iterator it_contact = top_contact_polygons.begin(); it_contact != top_contact_polygons.end(); ++ it_contact) {
-            Polylines tmp;
-            tmp.push_back(it_contact->split_at_first_point());
-            if (! intersection(tmp, overhang_with_margin).empty()) {
+        for (Polygons::const_iterator it_contact = top_contact_polygons.begin(); it_contact != top_contact_polygons.end(); ++ it_contact)
+            if (! intersection_pl(it_contact->split_at_first_point(), overhang_with_margin).empty()) {
                 external_loops.push_back(*it_contact);
                 Points positions_new = it_contact->equally_spaced_points(circle_distance);
                 for (Points::const_iterator it_center = positions_new.begin(); it_center != positions_new.end(); ++ it_center) {
@@ -1350,7 +1599,6 @@ void LoopInterfaceProcessor::generate(MyLayerExtruded &top_contact_layer, const
                         circle_new.points[i].translate(*it_center);
                 }
             }
-        }
         // Apply a pattern to the loop.
         loops0 = diff(external_loops, circles);
     }
@@ -1369,7 +1617,7 @@ void LoopInterfaceProcessor::generate(MyLayerExtruded &top_contact_layer, const
         loop_lines.reserve(loop_polygons.size());
         for (Polygons::const_iterator it = loop_polygons.begin(); it != loop_polygons.end(); ++ it)
             loop_lines.push_back(it->split_at_first_point());
-        loop_lines = intersection(loop_lines, offset(overhang_polygons, scale_(SUPPORT_MATERIAL_MARGIN)));
+        loop_lines = intersection_pl(loop_lines, offset(overhang_polygons, scale_(SUPPORT_MATERIAL_MARGIN)));
     }
     
     // add the contact infill area to the interface area
@@ -1377,9 +1625,7 @@ void LoopInterfaceProcessor::generate(MyLayerExtruded &top_contact_layer, const
     // extrusions are left inside the circles; however it creates
     // a very large gap between loops and contact_infill_polygons, so maybe another
     // solution should be found to achieve both goals
-    Polygons thick_loop_lines;
-    offset(loop_lines, &thick_loop_lines, float(circle_radius * 1.1));
-    top_contact_layer.layer->polygons = diff(top_contact_layer.layer->polygons, std::move(thick_loop_lines));
+    top_contact_layer.layer->polygons = diff(top_contact_layer.layer->polygons, offset(loop_lines, float(circle_radius * 1.1)));
 
     // Transform loops into ExtrusionPath objects.
     extrusion_entities_append_paths(
@@ -1598,7 +1844,7 @@ void PrintObjectSupportMaterial::generate_toolpaths(
                 // TODO: use brim ordering algorithm
                 Polygons to_infill_polygons = to_polygons(to_infill);
                 // TODO: use offset2_ex()
-                to_infill = offset_ex(to_infill_polygons, - flow.scaled_spacing());
+                to_infill = offset_ex(to_infill, - flow.scaled_spacing());
                 extrusion_entities_append_paths(
                     support_layer.support_fills.entities, 
                     to_polylines(STDMOVE(to_infill_polygons)),
diff --git a/xs/src/libslic3r/SupportMaterial.hpp b/xs/src/libslic3r/SupportMaterial.hpp
index 31a7899ce..abcaf676a 100644
--- a/xs/src/libslic3r/SupportMaterial.hpp
+++ b/xs/src/libslic3r/SupportMaterial.hpp
@@ -163,7 +163,9 @@ private:
 	// Generate bottom contact layers supporting the top contact layers.
 	// For a soluble interface material synchronize the layer heights with the object, 
 	// otherwise set the layer height to a bridging flow of a support interface nozzle.
-	MyLayersPtr bottom_contact_layers(const PrintObject &object, const MyLayersPtr &top_contacts, MyLayerStorage &layer_storage) const;
+	MyLayersPtr bottom_contact_layers_and_layer_support_areas(
+		const PrintObject &object, const MyLayersPtr &top_contacts, MyLayerStorage &layer_storage,
+		std::vector<Polygons> &layer_support_areas) const;
 
 	// Trim the top_contacts layers with the bottom_contacts layers if they overlap, so there would not be enough vertical space for both of them.
 	void trim_top_contacts_by_bottom_contacts(const PrintObject &object, const MyLayersPtr &bottom_contacts, MyLayersPtr &top_contacts) const;
@@ -180,7 +182,8 @@ private:
 	    const PrintObject   &object,
 	    const MyLayersPtr   &bottom_contacts,
 	    const MyLayersPtr   &top_contacts,
-	    MyLayersPtr         &intermediate_layers) const;
+	    MyLayersPtr         &intermediate_layers,
+	    std::vector<Polygons> &layer_support_areas) const;
 
     Polygons generate_raft_base(
 	    const PrintObject   &object,
diff --git a/xs/src/libslic3r/SurfaceCollection.cpp b/xs/src/libslic3r/SurfaceCollection.cpp
index 804190548..cfeae4ccc 100644
--- a/xs/src/libslic3r/SurfaceCollection.cpp
+++ b/xs/src/libslic3r/SurfaceCollection.cpp
@@ -196,19 +196,6 @@ SurfaceCollection::remove_types(const SurfaceType *types, int ntypes)
         surfaces.erase(surfaces.begin() + j, surfaces.end());
 }
 
-void
-SurfaceCollection::append(const SurfaceCollection &coll)
-{
-    this->surfaces.insert(this->surfaces.end(), coll.surfaces.begin(), coll.surfaces.end());
-}
-
-void 
-SurfaceCollection::append(const SurfaceType surfaceType, const Slic3r::ExPolygons &expoly)
-{
-    for (Slic3r::ExPolygons::const_iterator it = expoly.begin(); it != expoly.end(); ++ it)
-        this->surfaces.push_back(Slic3r::Surface(surfaceType, *it));
-}
-
 void SurfaceCollection::export_to_svg(const char *path, bool show_labels) 
 {
     BoundingBox bbox;
diff --git a/xs/src/libslic3r/SurfaceCollection.hpp b/xs/src/libslic3r/SurfaceCollection.hpp
index e013bf891..22c26936e 100644
--- a/xs/src/libslic3r/SurfaceCollection.hpp
+++ b/xs/src/libslic3r/SurfaceCollection.hpp
@@ -28,8 +28,27 @@ class SurfaceCollection
     void remove_type(const SurfaceType type);
     void remove_types(const SurfaceType *types, int ntypes);
     void filter_by_type(SurfaceType type, Polygons* polygons);
-    void append(const SurfaceCollection &coll);
-    void append(const SurfaceType surfaceType, const ExPolygons &expoly);
+
+    void clear() { surfaces.clear(); }
+    bool empty() const { return surfaces.empty(); }
+
+    void set(const SurfaceCollection &coll) { surfaces = coll.surfaces; }
+    void set(SurfaceCollection &&coll) { surfaces = std::move(coll.surfaces); }
+    void set(const ExPolygons &src, SurfaceType surfaceType) { clear(); this->append(src, surfaceType); }
+    void set(const ExPolygons &src, const Surface &surfaceTempl) { clear(); this->append(src, surfaceTempl); }
+    void set(const Surfaces &src) { clear(); this->append(src); }
+    void set(ExPolygons &&src, SurfaceType surfaceType) { clear(); this->append(std::move(src), surfaceType); }
+    void set(ExPolygons &&src, const Surface &surfaceTempl) { clear(); this->append(std::move(src), surfaceTempl); }
+    void set(Surfaces &&src) { clear(); this->append(std::move(src)); }
+
+    void append(const SurfaceCollection &coll) { this->append(coll.surfaces); }
+    void append(SurfaceCollection &&coll) { this->append(std::move(coll.surfaces)); }
+    void append(const ExPolygons &src, SurfaceType surfaceType) { surfaces_append(this->surfaces, src, surfaceType); }
+    void append(const ExPolygons &src, const Surface &surfaceTempl) { surfaces_append(this->surfaces, src, surfaceTempl); }
+    void append(const Surfaces &src) { surfaces_append(this->surfaces, src); }
+    void append(ExPolygons &&src, SurfaceType surfaceType) { surfaces_append(this->surfaces, std::move(src), surfaceType); }
+    void append(ExPolygons &&src, const Surface &surfaceTempl) { surfaces_append(this->surfaces, std::move(src), surfaceTempl); }
+    void append(Surfaces &&src) { surfaces_append(this->surfaces, std::move(src)); }
 
     // For debugging purposes:
     void export_to_svg(const char *path, bool show_labels);
diff --git a/xs/src/libslic3r/TriangleMesh.cpp b/xs/src/libslic3r/TriangleMesh.cpp
index 5737747a2..71e34f3b8 100644
--- a/xs/src/libslic3r/TriangleMesh.cpp
+++ b/xs/src/libslic3r/TriangleMesh.cpp
@@ -2,8 +2,8 @@
 #include "ClipperUtils.hpp"
 #include "Geometry.hpp"
 #include <cmath>
-#include <queue>
 #include <deque>
+#include <queue>
 #include <set>
 #include <vector>
 #include <map>
@@ -193,12 +193,17 @@ void TriangleMesh::scale(const Pointf3 &versor)
 
 void TriangleMesh::translate(float x, float y, float z)
 {
+    if (x == 0.f && y == 0.f && z == 0.f)
+        return;
     stl_translate_relative(&(this->stl), x, y, z);
     stl_invalidate_shared_vertices(&this->stl);
 }
 
 void TriangleMesh::rotate(float angle, const Axis &axis)
 {
+    if (angle == 0.f)
+        return;
+
     // admesh uses degrees
     angle = Slic3r::Geometry::rad2deg(angle);
     
@@ -265,6 +270,8 @@ void TriangleMesh::align_to_origin()
 
 void TriangleMesh::rotate(double angle, Point* center)
 {
+    if (angle == 0.)
+        return;
     this->translate(-center->x, -center->y, 0);
     stl_rotate_z(&(this->stl), (float)angle);
     this->translate(+center->x, +center->y, 0);
@@ -363,10 +370,7 @@ TriangleMesh::horizontal_projection() const
     }
     
     // the offset factor was tuned using groovemount.stl
-    offset(pp, &pp, 0.01 / SCALING_FACTOR);
-    ExPolygons retval;
-    union_(pp, &retval, true);
-    return retval;
+    return union_ex(offset(pp, 0.01 / SCALING_FACTOR), true);
 }
 
 Polygon
@@ -403,7 +407,7 @@ TriangleMesh::require_shared_vertices()
 }
 
 void
-TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<Polygons>* layers)
+TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<Polygons>* layers) const
 {
     /*
        This method gets called with a list of unscaled Z coordinates and outputs
@@ -427,58 +431,66 @@ TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<Polygons>* la
         
         At the end, we free the tables generated by analyze() as we don't 
         need them anymore.
-        FUTURE: parallelize slice_facet() and make_loops()
         
         NOTE: this method accepts a vector of floats because the mesh coordinate
         type is float.
     */
     
     std::vector<IntersectionLines> lines(z.size());
-    
-    for (int facet_idx = 0; facet_idx < this->mesh->stl.stats.number_of_facets; facet_idx++) {
-        stl_facet* facet = &this->mesh->stl.facet_start[facet_idx];
-        
-        // find facet extents
-        float min_z = fminf(facet->vertex[0].z, fminf(facet->vertex[1].z, facet->vertex[2].z));
-        float max_z = fmaxf(facet->vertex[0].z, fmaxf(facet->vertex[1].z, facet->vertex[2].z));
-        
-        #ifdef SLIC3R_TRIANGLEMESH_DEBUG
-        printf("\n==> FACET %d (%f,%f,%f - %f,%f,%f - %f,%f,%f):\n", facet_idx,
-            facet->vertex[0].x, facet->vertex[0].y, facet->vertex[0].z,
-            facet->vertex[1].x, facet->vertex[1].y, facet->vertex[1].z,
-            facet->vertex[2].x, facet->vertex[2].y, facet->vertex[2].z);
-        printf("z: min = %.2f, max = %.2f\n", min_z, max_z);
-        #endif
-        
-        // find layer extents
-        std::vector<float>::const_iterator min_layer, max_layer;
-        min_layer = std::lower_bound(z.begin(), z.end(), min_z); // first layer whose slice_z is >= min_z
-        max_layer = std::upper_bound(z.begin() + (min_layer - z.begin()), z.end(), max_z) - 1; // last layer whose slice_z is <= max_z
-        #ifdef SLIC3R_TRIANGLEMESH_DEBUG
-        printf("layers: min = %d, max = %d\n", (int)(min_layer - z.begin()), (int)(max_layer - z.begin()));
-        #endif
-        
-        for (std::vector<float>::const_iterator it = min_layer; it != max_layer + 1; ++it) {
-            std::vector<float>::size_type layer_idx = it - z.begin();
-            this->slice_facet(*it / SCALING_FACTOR, *facet, facet_idx, min_z, max_z, &lines[layer_idx]);
-        }
+    {
+        boost::mutex lines_mutex;
+        parallelize<int>(
+            0,
+            this->mesh->stl.stats.number_of_facets-1,
+            boost::bind(&TriangleMeshSlicer::_slice_do, this, _1, &lines, &lines_mutex, z)
+        );
     }
     
     // v_scaled_shared could be freed here
     
     // build loops
     layers->resize(z.size());
-    for (std::vector<IntersectionLines>::iterator it = lines.begin(); it != lines.end(); ++it) {
-        size_t layer_idx = it - lines.begin();
-        #ifdef SLIC3R_TRIANGLEMESH_DEBUG
-        printf("Layer " PRINTF_ZU ":\n", layer_idx);
-        #endif
-        this->make_loops(*it, &(*layers)[layer_idx]);
+    parallelize<size_t>(
+        0,
+        lines.size()-1,
+        boost::bind(&TriangleMeshSlicer::_make_loops_do, this, _1, &lines, layers)
+    );
+}
+
+void
+TriangleMeshSlicer::_slice_do(size_t facet_idx, std::vector<IntersectionLines>* lines, boost::mutex* lines_mutex, 
+    const std::vector<float> &z) const
+{
+    const stl_facet &facet = this->mesh->stl.facet_start[facet_idx];
+    
+    // find facet extents
+    const float min_z = fminf(facet.vertex[0].z, fminf(facet.vertex[1].z, facet.vertex[2].z));
+    const float max_z = fmaxf(facet.vertex[0].z, fmaxf(facet.vertex[1].z, facet.vertex[2].z));
+    
+    #ifdef SLIC3R_DEBUG
+    printf("\n==> FACET %d (%f,%f,%f - %f,%f,%f - %f,%f,%f):\n", facet_idx,
+        facet.vertex[0].x, facet.vertex[0].y, facet.vertex[0].z,
+        facet.vertex[1].x, facet.vertex[1].y, facet.vertex[1].z,
+        facet.vertex[2].x, facet.vertex[2].y, facet.vertex[2].z);
+    printf("z: min = %.2f, max = %.2f\n", min_z, max_z);
+    #endif
+    
+    // find layer extents
+    std::vector<float>::const_iterator min_layer, max_layer;
+    min_layer = std::lower_bound(z.begin(), z.end(), min_z); // first layer whose slice_z is >= min_z
+    max_layer = std::upper_bound(z.begin() + (min_layer - z.begin()), z.end(), max_z) - 1; // last layer whose slice_z is <= max_z
+    #ifdef SLIC3R_DEBUG
+    printf("layers: min = %d, max = %d\n", (int)(min_layer - z.begin()), (int)(max_layer - z.begin()));
+    #endif
+    
+    for (std::vector<float>::const_iterator it = min_layer; it != max_layer + 1; ++it) {
+        std::vector<float>::size_type layer_idx = it - z.begin();
+        this->slice_facet(*it / SCALING_FACTOR, facet, facet_idx, min_z, max_z, &(*lines)[layer_idx], lines_mutex);
     }
 }
 
 void
-TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<ExPolygons>* layers)
+TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<ExPolygons>* layers) const
 {
     std::vector<Polygons> layers_p;
     this->slice(z, &layers_p);
@@ -495,7 +507,9 @@ TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<ExPolygons>*
 }
 
 void
-TriangleMeshSlicer::slice_facet(float slice_z, const stl_facet &facet, const int &facet_idx, const float &min_z, const float &max_z, std::vector<IntersectionLine>* lines) const
+TriangleMeshSlicer::slice_facet(float slice_z, const stl_facet &facet, const int &facet_idx,
+    const float &min_z, const float &max_z, std::vector<IntersectionLine>* lines,
+    boost::mutex* lines_mutex) const
 {
     std::vector<IntersectionPoint> points;
     std::vector< std::vector<IntersectionPoint>::size_type > points_on_layer;
@@ -547,7 +561,12 @@ TriangleMeshSlicer::slice_facet(float slice_z, const stl_facet &facet, const int
             line.b.y    = b->y;
             line.a_id   = a_id;
             line.b_id   = b_id;
-            lines->push_back(line);
+            if (lines_mutex != NULL) {
+                boost::lock_guard<boost::mutex> l(*lines_mutex);
+                lines->push_back(line);
+            } else {
+                lines->push_back(line);
+            }
             
             found_horizontal_edge = true;
             
@@ -600,13 +619,24 @@ TriangleMeshSlicer::slice_facet(float slice_z, const stl_facet &facet, const int
         line.b_id       = points[0].point_id;
         line.edge_a_id  = points[1].edge_id;
         line.edge_b_id  = points[0].edge_id;
-        lines->push_back(line);
+        if (lines_mutex != NULL) {
+            boost::lock_guard<boost::mutex> l(*lines_mutex);
+            lines->push_back(line);
+        } else {
+            lines->push_back(line);
+        }
         return;
     }
 }
 
 void
-TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygons* loops)
+TriangleMeshSlicer::_make_loops_do(size_t i, std::vector<IntersectionLines>* lines, std::vector<Polygons>* layers) const
+{
+    this->make_loops((*lines)[i], &(*layers)[i]);
+}
+
+void
+TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygons* loops) const
 {
     /*
     SVG svg("lines.svg");
@@ -707,6 +737,7 @@ TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygons* l
                     for (IntersectionLinePtrs::const_iterator lineptr = loop.begin(); lineptr != loop.end(); ++lineptr) {
                         p.points.push_back((*lineptr)->a);
                     }
+                    
                     loops->push_back(p);
                     
                     #ifdef SLIC3R_TRIANGLEMESH_DEBUG
@@ -746,7 +777,7 @@ class _area_comp {
 };
 
 void
-TriangleMeshSlicer::make_expolygons_simple(std::vector<IntersectionLine> &lines, ExPolygons* slices)
+TriangleMeshSlicer::make_expolygons_simple(std::vector<IntersectionLine> &lines, ExPolygons* slices) const
 {
     Polygons loops;
     this->make_loops(lines, &loops);
@@ -780,7 +811,7 @@ TriangleMeshSlicer::make_expolygons_simple(std::vector<IntersectionLine> &lines,
 }
 
 void
-TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slices)
+TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slices) const
 {
     /*
         Input loops are not suitable for evenodd nor nonzero fill types, as we might get
@@ -818,17 +849,15 @@ TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slices)
            of the loops, since the Orientation() function provided by Clipper
            would do the same, thus repeating the calculation */
         Polygons::const_iterator loop = loops.begin() + *loop_idx;
-        if (area[*loop_idx] > +EPSILON) {
+        if (area[*loop_idx] > +EPSILON)
             p_slices.push_back(*loop);
-        } else if (area[*loop_idx] < -EPSILON) {
-            diff(p_slices, *loop, &p_slices);
-        }
+        else if (area[*loop_idx] < -EPSILON)
+            p_slices = diff(p_slices, *loop);
     }
 
     // perform a safety offset to merge very close facets (TODO: find test case for this)
     double safety_offset = scale_(0.0499);
-    ExPolygons ex_slices;
-    offset2(p_slices, &ex_slices, +safety_offset, -safety_offset);
+    ExPolygons ex_slices = offset2_ex(p_slices, +safety_offset, -safety_offset);
     
     #ifdef SLIC3R_TRIANGLEMESH_DEBUG
     size_t holes_count = 0;
@@ -840,11 +869,11 @@ TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slices)
     #endif
     
     // append to the supplied collection
-    slices->insert(slices->end(), ex_slices.begin(), ex_slices.end());
+    expolygons_append(*slices, ex_slices);
 }
 
 void
-TriangleMeshSlicer::make_expolygons(std::vector<IntersectionLine> &lines, ExPolygons* slices)
+TriangleMeshSlicer::make_expolygons(std::vector<IntersectionLine> &lines, ExPolygons* slices) const
 {
     Polygons pp;
     this->make_loops(lines, &pp);
@@ -852,7 +881,7 @@ TriangleMeshSlicer::make_expolygons(std::vector<IntersectionLine> &lines, ExPoly
 }
 
 void
-TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower)
+TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower) const
 {
     IntersectionLines upper_lines, lower_lines;
     
@@ -1004,7 +1033,6 @@ TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower)
     
     stl_get_size(&(upper->stl));
     stl_get_size(&(lower->stl));
-    
 }
 
 TriangleMeshSlicer::TriangleMeshSlicer(TriangleMesh* _mesh) : mesh(_mesh), v_scaled_shared(NULL)
diff --git a/xs/src/libslic3r/TriangleMesh.hpp b/xs/src/libslic3r/TriangleMesh.hpp
index cf129809a..ff332ed66 100644
--- a/xs/src/libslic3r/TriangleMesh.hpp
+++ b/xs/src/libslic3r/TriangleMesh.hpp
@@ -4,6 +4,7 @@
 #include "libslic3r.h"
 #include <admesh/stl.h>
 #include <vector>
+#include <boost/thread.hpp>
 #include "BoundingBox.hpp"
 #include "Line.hpp"
 #include "Point.hpp"
@@ -88,19 +89,23 @@ class TriangleMeshSlicer
     TriangleMesh* mesh;
     TriangleMeshSlicer(TriangleMesh* _mesh);
     ~TriangleMeshSlicer();
-    void slice(const std::vector<float> &z, std::vector<Polygons>* layers);
-    void slice(const std::vector<float> &z, std::vector<ExPolygons>* layers);
-    void slice_facet(float slice_z, const stl_facet &facet, const int &facet_idx, const float &min_z, const float &max_z, std::vector<IntersectionLine>* lines) const;
-    void cut(float z, TriangleMesh* upper, TriangleMesh* lower);
+    void slice(const std::vector<float> &z, std::vector<Polygons>* layers) const;
+    void slice(const std::vector<float> &z, std::vector<ExPolygons>* layers) const;
+    void slice_facet(float slice_z, const stl_facet &facet, const int &facet_idx,
+        const float &min_z, const float &max_z, std::vector<IntersectionLine>* lines,
+        boost::mutex* lines_mutex = NULL) const;
+    void cut(float z, TriangleMesh* upper, TriangleMesh* lower) const;
     
     private:
     typedef std::vector< std::vector<int> > t_facets_edges;
     t_facets_edges facets_edges;
     stl_vertex* v_scaled_shared;
-    void make_loops(std::vector<IntersectionLine> &lines, Polygons* loops);
-    void make_expolygons(const Polygons &loops, ExPolygons* slices);
-    void make_expolygons_simple(std::vector<IntersectionLine> &lines, ExPolygons* slices);
-    void make_expolygons(std::vector<IntersectionLine> &lines, ExPolygons* slices);
+    void _slice_do(size_t facet_idx, std::vector<IntersectionLines>* lines, boost::mutex* lines_mutex, const std::vector<float> &z) const;
+    void _make_loops_do(size_t i, std::vector<IntersectionLines>* lines, std::vector<Polygons>* layers) const;
+    void make_loops(std::vector<IntersectionLine> &lines, Polygons* loops) const;
+    void make_expolygons(const Polygons &loops, ExPolygons* slices) const;
+    void make_expolygons_simple(std::vector<IntersectionLine> &lines, ExPolygons* slices) const;
+    void make_expolygons(std::vector<IntersectionLine> &lines, ExPolygons* slices) const;
 };
 
 }
diff --git a/xs/src/libslic3r/libslic3r.h b/xs/src/libslic3r/libslic3r.h
index 5aa7866b8..89db6d81e 100644
--- a/xs/src/libslic3r/libslic3r.h
+++ b/xs/src/libslic3r/libslic3r.h
@@ -4,10 +4,14 @@
 // this needs to be included early for MSVC (listing it in Build.PL is not enough)
 #include <ostream>
 #include <iostream>
+#include <math.h>
+#include <queue>
 #include <sstream>
 #include <cstdio>
 #include <stdint.h>
 #include <stdarg.h>
+#include <vector>
+#include <boost/thread.hpp>
 
 #define SLIC3R_FORK_NAME "Slic3r Prusa Edition"
 #define SLIC3R_VERSION "1.31.6"
@@ -40,13 +44,6 @@
 typedef long coord_t;
 typedef double coordf_t;
 
-namespace Slic3r {
-
-enum Axis { X=0, Y, Z };
-
-}
-using namespace Slic3r;
-
 /* Implementation of CONFESS("foo"): */
 #ifdef _MSC_VER
 	#define CONFESS(...) confess_at(__FILE__, __LINE__, __FUNCTION__, __VA_ARGS__)
@@ -91,4 +88,55 @@ inline std::string debug_out_path(const char *name, ...)
 // Write slices as SVG images into out directory during the 2D processing of the slices.
 // #define SLIC3R_DEBUG_SLICE_PROCESSING
 
+namespace Slic3r {
+
+enum Axis { X=0, Y, Z };
+
+template <class T>
+inline void append_to(std::vector<T> &dst, const std::vector<T> &src)
+{
+    dst.insert(dst.end(), src.begin(), src.end());
+}
+
+template <class T> void
+_parallelize_do(std::queue<T>* queue, boost::mutex* queue_mutex, boost::function<void(T)> func)
+{
+    //std::cout << "THREAD STARTED: " << boost::this_thread::get_id() << std::endl;
+    while (true) {
+        T i;
+        {
+            boost::lock_guard<boost::mutex> l(*queue_mutex);
+            if (queue->empty()) return;
+            i = queue->front();
+            queue->pop();
+        }
+        //std::cout << "  Thread " << boost::this_thread::get_id() << " processing item " << i << std::endl;
+        func(i);
+        boost::this_thread::interruption_point();
+    }
+}
+
+template <class T> void
+parallelize(std::queue<T> queue, boost::function<void(T)> func,
+    int threads_count = boost::thread::hardware_concurrency())
+{
+    if (threads_count == 0) threads_count = 2;
+    boost::mutex queue_mutex;
+    boost::thread_group workers;
+    for (int i = 0; i < fminf(threads_count, queue.size()); i++)
+        workers.add_thread(new boost::thread(&_parallelize_do<T>, &queue, &queue_mutex, func));
+    workers.join_all();
+}
+
+template <class T> void
+parallelize(T start, T end, boost::function<void(T)> func,
+    int threads_count = boost::thread::hardware_concurrency())
+{
+    std::queue<T> queue;
+    for (T i = start; i <= end; ++i) queue.push(i);
+    parallelize(queue, func, threads_count);
+}
+
+} // namespace Slic3r
+
 #endif
diff --git a/xs/src/libslic3r/utils.cpp b/xs/src/libslic3r/utils.cpp
index 0f298e954..b66d58489 100644
--- a/xs/src/libslic3r/utils.cpp
+++ b/xs/src/libslic3r/utils.cpp
@@ -1,3 +1,30 @@
+#include <boost/log/core.hpp>
+#include <boost/log/trivial.hpp>
+#include <boost/log/expressions.hpp>
+
+namespace Slic3r {
+
+static boost::log::trivial::severity_level logSeverity = boost::log::trivial::fatal;
+
+void set_logging_level(unsigned int level)
+{
+    switch (level) {
+    case 0: logSeverity = boost::log::trivial::fatal; break;
+    case 1: logSeverity = boost::log::trivial::error; break;
+    case 2: logSeverity = boost::log::trivial::warning; break;
+    case 3: logSeverity = boost::log::trivial::info; break;
+    case 4: logSeverity = boost::log::trivial::debug; break;
+    default: logSeverity = boost::log::trivial::trace; break;
+    }
+
+    boost::log::core::get()->set_filter
+    (
+        boost::log::trivial::severity >= logSeverity
+    );
+}
+
+} // namespace Slic3r
+
 #ifdef SLIC3R_HAS_BROKEN_CROAK
 
 // Some Strawberry Perl builds (mainly the latest 64bit builds) have a broken mechanism
@@ -66,30 +93,3 @@ confess_at(const char *file, int line, const char *func,
 }
 
 #endif
-
-#include <boost/log/core.hpp>
-#include <boost/log/trivial.hpp>
-#include <boost/log/expressions.hpp>
-
-namespace Slic3r {
-
-static boost::log::trivial::severity_level logSeverity = boost::log::trivial::fatal;
-
-void set_logging_level(unsigned int level)
-{
-    switch (level) {
-    case 0: logSeverity = boost::log::trivial::fatal; break;
-    case 1: logSeverity = boost::log::trivial::error; break;
-    case 2: logSeverity = boost::log::trivial::warning; break;
-    case 3: logSeverity = boost::log::trivial::info; break;
-    case 4: logSeverity = boost::log::trivial::debug; break;
-    default: logSeverity = boost::log::trivial::trace; break;
-    }
-
-    boost::log::core::get()->set_filter
-    (
-        boost::log::trivial::severity >= logSeverity
-    );
-}
-
-} // namespace Slic3r
diff --git a/xs/src/perlglue.cpp b/xs/src/perlglue.cpp
index 1544a2608..cae956fe3 100644
--- a/xs/src/perlglue.cpp
+++ b/xs/src/perlglue.cpp
@@ -532,8 +532,7 @@ SV*
 polynode2perl(const ClipperLib::PolyNode& node)
 {
     HV* hv = newHV();
-    Slic3r::Polygon p;
-    ClipperPath_to_Slic3rMultiPoint(node.Contour, &p);
+    Slic3r::Polygon p = ClipperPath_to_Slic3rPolygon(node.Contour);
     if (node.IsHole()) {
         (void)hv_stores( hv, "hole", Slic3r::perl_to_SV_clone_ref(p) );
     } else {
diff --git a/xs/src/xsinit.h b/xs/src/xsinit.h
index 6b50bc2e4..79abc2f54 100644
--- a/xs/src/xsinit.h
+++ b/xs/src/xsinit.h
@@ -31,6 +31,7 @@
 #include <ostream>
 #include <iostream>
 #include <sstream>
+#include <libslic3r.h>
 
 #ifdef SLIC3RXS
 extern "C" {
@@ -42,15 +43,18 @@ extern "C" {
 #undef do_close
 #undef bind
 #undef seed
+#undef push
+#undef pop
 #ifdef _MSC_VER
     // Undef some of the macros set by Perl <xsinit.h>, which cause compilation errors on Win32
-    #undef send
     #undef connect
+    #undef seek
+    #undef send
+    #undef write
 #endif /* _MSC_VER */
 }
 #endif
 
-#include <libslic3r.h>
 #include <ClipperUtils.hpp>
 #include <Config.hpp>
 #include <ExPolygon.hpp>
@@ -163,4 +167,6 @@ SV* polynode2perl(const ClipperLib::PolyNode& node);
 #endif
 #endif
 
+using namespace Slic3r;
+
 #endif
diff --git a/xs/t/11_clipper.t b/xs/t/11_clipper.t
index 8709c0108..321d3048c 100644
--- a/xs/t/11_clipper.t
+++ b/xs/t/11_clipper.t
@@ -5,7 +5,7 @@ use warnings;
 
 use List::Util qw(sum);
 use Slic3r::XS;
-use Test::More tests => 23;
+use Test::More tests => 16;
 
 my $square = Slic3r::Polygon->new(  # ccw
     [200, 100],
@@ -121,41 +121,6 @@ if (0) {  # Clipper does not preserve polyline orientation
     is_deeply $result->[0]->pp, [[200,150], [100,150]], 'clipped line orientation is preserved';
 }
 
-if (0) {  # Clipper does not preserve polyline orientation
-    my $result = Slic3r::Geometry::Clipper::intersection_ppl([$hole_in_square], [$square]);
-    is_deeply $result->[0]->pp, $hole_in_square->split_at_first_point->pp,
-        'intersection_ppl - clipping cw polygon as polyline preserves winding order';
-}
-
-{
-    my $square2 = $square->clone;
-    $square2->translate(50,50);
-    {
-        my $result = Slic3r::Geometry::Clipper::intersection_ppl([$square2], [$square]);
-        is scalar(@$result), 1, 'intersection_ppl - result contains a single line';
-        is scalar(@{$result->[0]}), 3, 'intersection_ppl - result contains expected number of points';
-        # Clipper does not preserve polyline orientation so we only check the middle point
-        ###ok $result->[0][0]->coincides_with(Slic3r::Point->new(150,200)), 'intersection_ppl - expected point order';
-        ok $result->[0][1]->coincides_with(Slic3r::Point->new(150,150)), 'intersection_ppl - expected point order';
-        ###ok $result->[0][2]->coincides_with(Slic3r::Point->new(200,150)), 'intersection_ppl - expected point order';
-    }
-}
-
-{
-    my $square2 = $square->clone;
-    $square2->reverse;
-    $square2->translate(50,50);
-    {
-        my $result = Slic3r::Geometry::Clipper::intersection_ppl([$square2], [$square]);
-        is scalar(@$result), 1, 'intersection_ppl - result contains a single line';
-        is scalar(@{$result->[0]}), 3, 'intersection_ppl - result contains expected number of points';
-        # Clipper does not preserve polyline orientation so we only check the middle point
-        ###ok $result->[0][0]->coincides_with(Slic3r::Point->new(200,150)), 'intersection_ppl - expected point order';
-        ok $result->[0][1]->coincides_with(Slic3r::Point->new(150,150)), 'intersection_ppl - expected point order';
-        ###ok $result->[0][2]->coincides_with(Slic3r::Point->new(150,200)), 'intersection_ppl - expected point order';
-    }
-}
-
 {
     # Clipper bug #96 (our issue #2028)
     my $subject = Slic3r::Polyline->new(
@@ -168,17 +133,6 @@ if (0) {  # Clipper does not preserve polyline orientation
     is scalar(@$result), 1, 'intersection_pl - result is not empty';
 }
 
-{
-    my $subject = Slic3r::Polygon->new(
-        [44730000,31936670],[55270000,31936670],[55270000,25270000],[74730000,25270000],[74730000,44730000],[68063296,44730000],[68063296,55270000],[74730000,55270000],[74730000,74730000],[55270000,74730000],[55270000,68063296],[44730000,68063296],[44730000,74730000],[25270000,74730000],[25270000,55270000],[31936670,55270000],[31936670,44730000],[25270000,44730000],[25270000,25270000],[44730000,25270000]
-    );
-    my $clip = [
-        Slic3r::Polygon->new([75200000,45200000],[54800000,45200000],[54800000,24800000],[75200000,24800000]),
-    ];
-    my $result = Slic3r::Geometry::Clipper::intersection_ppl([$subject], $clip);
-    is scalar(@$result), 1, 'intersection_ppl - result is not empty';
-}
-
 {
     # Clipper bug #122
     my $subject = [
diff --git a/xs/xsp/BoundingBox.xsp b/xs/xsp/BoundingBox.xsp
index f6e35e0df..aec5be564 100644
--- a/xs/xsp/BoundingBox.xsp
+++ b/xs/xsp/BoundingBox.xsp
@@ -30,7 +30,8 @@
     long y_min() %code{% RETVAL = THIS->min.y; %};
     long y_max() %code{% RETVAL = THIS->max.y; %};
     std::string serialize() %code{% char buf[2048]; sprintf(buf, "%ld,%ld;%ld,%ld", THIS->min.x, THIS->min.y, THIS->max.x, THIS->max.y); RETVAL = buf; %};
-  
+    bool defined() %code{% RETVAL = THIS->defined; %};
+
 %{
 
 BoundingBox*
@@ -69,7 +70,8 @@ new_from_points(CLASS, points)
     void set_y_min(double val) %code{% THIS->min.y = val; %};
     void set_y_max(double val) %code{% THIS->max.y = val; %};
     std::string serialize() %code{% char buf[2048]; sprintf(buf, "%lf,%lf;%lf,%lf", THIS->min.x, THIS->min.y, THIS->max.x, THIS->max.y); RETVAL = buf; %};
-
+    bool defined() %code{% RETVAL = THIS->defined; %};
+    
 %{
 
 BoundingBoxf*
@@ -106,4 +108,5 @@ new_from_points(CLASS, points)
     double z_min() %code{% RETVAL = THIS->min.z; %};
     double z_max() %code{% RETVAL = THIS->max.z; %};
     std::string serialize() %code{% char buf[2048]; sprintf(buf, "%lf,%lf,%lf;%lf,%lf,%lf", THIS->min.x, THIS->min.y, THIS->min.z, THIS->max.x, THIS->max.y, THIS->max.z); RETVAL = buf; %};
+    bool defined() %code{% RETVAL = THIS->defined; %};
 };
diff --git a/xs/xsp/Clipper.xsp b/xs/xsp/Clipper.xsp
index 7a33ea0c4..7457c8540 100644
--- a/xs/xsp/Clipper.xsp
+++ b/xs/xsp/Clipper.xsp
@@ -16,58 +16,53 @@ _constant()
     JT_MITER        = jtMiter
     JT_ROUND        = jtRound
     JT_SQUARE       = jtSquare
-    CLIPPER_OFFSET_SCALE = CLIPPER_OFFSET_SCALE
   CODE:
     RETVAL = ix;
   OUTPUT: RETVAL
 
 Polygons
-offset(polygons, delta, scale = CLIPPER_OFFSET_SCALE, joinType = ClipperLib::jtMiter, miterLimit = 3)
+offset(polygons, delta, joinType = ClipperLib::jtMiter, miterLimit = 3)
     Polygons                polygons
     const float             delta
-    double                  scale
     ClipperLib::JoinType    joinType
     double                  miterLimit
     CODE:
-        offset(polygons, &RETVAL, delta, scale, joinType, miterLimit);
+        RETVAL = offset(polygons, delta, joinType, miterLimit);
     OUTPUT:
         RETVAL
 
 ExPolygons
-offset_ex(polygons, delta, scale = CLIPPER_OFFSET_SCALE, joinType = ClipperLib::jtMiter, miterLimit = 3)
+offset_ex(polygons, delta, joinType = ClipperLib::jtMiter, miterLimit = 3)
     Polygons                polygons
     const float             delta
-    double                  scale
     ClipperLib::JoinType    joinType
     double                  miterLimit
     CODE:
-        offset(polygons, &RETVAL, delta, scale, joinType, miterLimit);
+        RETVAL = offset_ex(polygons, delta, joinType, miterLimit);
     OUTPUT:
         RETVAL
 
 Polygons
-offset2(polygons, delta1, delta2, scale = CLIPPER_OFFSET_SCALE, joinType = ClipperLib::jtMiter, miterLimit = 3)
+offset2(polygons, delta1, delta2, joinType = ClipperLib::jtMiter, miterLimit = 3)
     Polygons                polygons
     const float             delta1
     const float             delta2
-    double                  scale
     ClipperLib::JoinType    joinType
     double                  miterLimit
     CODE:
-        offset2(polygons, &RETVAL, delta1, delta2, scale, joinType, miterLimit);
+        RETVAL = offset2(polygons, delta1, delta2, joinType, miterLimit);
     OUTPUT:
         RETVAL
 
 ExPolygons
-offset2_ex(polygons, delta1, delta2, scale = CLIPPER_OFFSET_SCALE, joinType = ClipperLib::jtMiter, miterLimit = 3)
+offset2_ex(polygons, delta1, delta2, joinType = ClipperLib::jtMiter, miterLimit = 3)
     Polygons                polygons
     const float             delta1
     const float             delta2
-    double                  scale
     ClipperLib::JoinType    joinType
     double                  miterLimit
     CODE:
-        offset2(polygons, &RETVAL, delta1, delta2, scale, joinType, miterLimit);
+        RETVAL = offset2_ex(polygons, delta1, delta2, joinType, miterLimit);
     OUTPUT:
         RETVAL
 
@@ -77,7 +72,7 @@ diff(subject, clip, safety_offset = false)
     Polygons    clip
     bool        safety_offset
     CODE:
-        diff(subject, clip, &RETVAL, safety_offset);
+        RETVAL = diff(subject, clip, safety_offset);
     OUTPUT:
         RETVAL
 
@@ -87,7 +82,7 @@ diff_ex(subject, clip, safety_offset = false)
     Polygons    clip
     bool        safety_offset
     CODE:
-        diff(subject, clip, &RETVAL, safety_offset);
+        RETVAL = diff_ex(subject, clip, safety_offset);
     OUTPUT:
         RETVAL
 
@@ -96,16 +91,7 @@ diff_pl(subject, clip)
     Polylines   subject
     Polygons    clip
     CODE:
-        diff(subject, clip, &RETVAL);
-    OUTPUT:
-        RETVAL
-
-Polylines
-diff_ppl(subject, clip)
-    Polygons    subject
-    Polygons    clip
-    CODE:
-        diff(subject, clip, &RETVAL);
+        RETVAL = diff_pl(subject, clip);
     OUTPUT:
         RETVAL
 
@@ -115,7 +101,7 @@ intersection(subject, clip, safety_offset = false)
     Polygons                    clip
     bool                        safety_offset
     CODE:
-        intersection(subject, clip, &RETVAL, safety_offset);
+        RETVAL = intersection(subject, clip, safety_offset);
     OUTPUT:
         RETVAL
 
@@ -125,7 +111,7 @@ intersection_ex(subject, clip, safety_offset = false)
     Polygons                    clip
     bool                        safety_offset
     CODE:
-        intersection(subject, clip, &RETVAL, safety_offset);
+        RETVAL = intersection_ex(subject, clip, safety_offset);
     OUTPUT:
         RETVAL
 
@@ -134,26 +120,7 @@ intersection_pl(subject, clip)
     Polylines                   subject
     Polygons                    clip
     CODE:
-        intersection(subject, clip, &RETVAL);
-    OUTPUT:
-        RETVAL
-
-Polylines
-intersection_ppl(subject, clip)
-    Polygons                    subject
-    Polygons                    clip
-    CODE:
-        intersection(subject, clip, &RETVAL);
-    OUTPUT:
-        RETVAL
-
-ExPolygons
-xor_ex(subject, clip, safety_offset = false)
-    Polygons                    subject
-    Polygons                    clip
-    bool                        safety_offset
-    CODE:
-        xor_(subject, clip, &RETVAL, safety_offset);
+        RETVAL = intersection_pl(subject, clip);
     OUTPUT:
         RETVAL
 
@@ -162,7 +129,7 @@ union(subject, safety_offset = false)
     Polygons    subject
     bool        safety_offset
     CODE:
-        union_(subject, &RETVAL, safety_offset);
+        RETVAL = union_(subject, safety_offset);
     OUTPUT:
         RETVAL
 
@@ -171,20 +138,7 @@ union_ex(subject, safety_offset = false)
     Polygons                    subject
     bool                        safety_offset
     CODE:
-        union_(subject, &RETVAL, safety_offset);
-    OUTPUT:
-        RETVAL
-
-SV*
-union_pt(subject, safety_offset = false)
-    Polygons                    subject
-    bool                        safety_offset
-    CODE:
-        // perform operation
-        ClipperLib::PolyTree polytree;
-        union_pt(subject, &polytree, safety_offset);
-        
-        RETVAL = polynode_children_2_perl(polytree);
+        RETVAL = union_ex(subject, safety_offset);
     OUTPUT:
         RETVAL
 
@@ -193,7 +147,7 @@ union_pt_chained(subject, safety_offset = false)
     Polygons                    subject
     bool                        safety_offset
     CODE:
-        union_pt_chained(subject, &RETVAL, safety_offset);
+        RETVAL = union_pt_chained(subject, safety_offset);
     OUTPUT:
         RETVAL
 
@@ -201,7 +155,7 @@ Polygons
 simplify_polygons(subject)
     Polygons                    subject
     CODE:
-        simplify_polygons(subject, &RETVAL);
+        RETVAL = simplify_polygons(subject);
     OUTPUT:
         RETVAL
 
diff --git a/xs/xsp/Polyline.xsp b/xs/xsp/Polyline.xsp
index 31bd4045e..60d7c6aca 100644
--- a/xs/xsp/Polyline.xsp
+++ b/xs/xsp/Polyline.xsp
@@ -80,13 +80,12 @@ Polyline::rotate(angle, center_sv)
         THIS->rotate(angle, center);
 
 Polygons
-Polyline::grow(delta, scale = CLIPPER_OFFSET_SCALE, joinType = ClipperLib::jtSquare, miterLimit = 3)
+Polyline::grow(delta, joinType = ClipperLib::jtSquare, miterLimit = 3)
     const float             delta
-    double                  scale
     ClipperLib::JoinType    joinType
     double                  miterLimit
     CODE:
-        offset(*THIS, &RETVAL, delta, scale, joinType, miterLimit);
+        RETVAL = offset(*THIS, delta, joinType, miterLimit);
     OUTPUT:
         RETVAL
 
diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp
index ad90eb32e..7461b9d91 100644
--- a/xs/xsp/Print.xsp
+++ b/xs/xsp/Print.xsp
@@ -3,6 +3,7 @@
 %{
 #include <xsinit.h>
 #include "libslic3r/Print.hpp"
+#include "libslic3r/Slicing.hpp"
 #include "libslic3r/PlaceholderParser.hpp"
 %}
 
@@ -58,6 +59,8 @@ _constant()
     Points copies();
     t_layer_height_ranges layer_height_ranges()
         %code%{ RETVAL = THIS->layer_height_ranges; %};
+    std::vector<double> layer_height_profile()
+        %code%{ RETVAL = THIS->layer_height_profile; %};
     Ref<Point3> size()
         %code%{ RETVAL = &THIS->size; %};
     Clone<BoundingBox> bounding_box();
@@ -82,6 +85,8 @@ _constant()
     bool reload_model_instances();
     void set_layer_height_ranges(t_layer_height_ranges layer_height_ranges)
         %code%{ THIS->layer_height_ranges = layer_height_ranges; %};
+    void set_layer_height_profile(std::vector<double> profile)
+        %code%{ THIS->layer_height_profile = profile; %};
 
     size_t total_layer_count();
     size_t layer_count();
@@ -106,11 +111,31 @@ _constant()
         %code%{ THIS->state.set_done(step); %};
     void set_step_started(PrintObjectStep step)
         %code%{ THIS->state.set_started(step); %};
-    
+
+    void _slice();
     void detect_surfaces_type();
     void process_external_surfaces();
     void discover_vertical_shells();
     void bridge_over_infill();
+    void _make_perimeters();
+    void _infill();
+
+    void adjust_layer_height_profile(coordf_t z, coordf_t layer_thickness_delta, coordf_t band_width, int action)
+        %code%{ 
+            THIS->update_layer_height_profile(); 
+            adjust_layer_height_profile(
+                THIS->slicing_parameters(), THIS->layer_height_profile, z, layer_thickness_delta, band_width, action);
+        %};
+    
+    int generate_layer_height_texture(void *data, int rows, int cols, bool level_of_detail_2nd_level = true)
+        %code%{ 
+            THIS->update_layer_height_profile();
+            SlicingParameters slicing_params = THIS->slicing_parameters(); 
+            RETVAL = generate_layer_height_texture(
+                slicing_params, 
+                generate_object_layers(slicing_params, THIS->layer_height_profile), 
+                data, rows, cols, level_of_detail_2nd_level); 
+        %};
     
     int ptr()
         %code%{ RETVAL = (int)(intptr_t)THIS; %};
diff --git a/xs/xsp/Surface.xsp b/xs/xsp/Surface.xsp
index 597ed1b5d..379774f0a 100644
--- a/xs/xsp/Surface.xsp
+++ b/xs/xsp/Surface.xsp
@@ -83,13 +83,12 @@ Surface::polygons()
         RETVAL
 
 Surfaces
-Surface::offset(delta, scale = CLIPPER_OFFSET_SCALE, joinType = ClipperLib::jtMiter, miterLimit = 3)
+Surface::offset(delta, joinType = ClipperLib::jtMiter, miterLimit = 3)
     const float             delta
-    double                  scale
     ClipperLib::JoinType    joinType
     double                  miterLimit
     CODE:
-        offset(*THIS, &RETVAL, delta, scale, joinType, miterLimit);
+        surfaces_append(RETVAL, offset_ex(THIS->expolygon, delta, joinType, miterLimit), *THIS);
     OUTPUT:
         RETVAL