From beb1baa0960470d63ddea10df321cf6cffd8b615 Mon Sep 17 00:00:00 2001
From: Alessandro Ranellucci <aar@cpan.org>
Date: Mon, 30 Dec 2013 18:28:41 +0100
Subject: [PATCH 01/19] Incomplete work for refactoring regions and flows

---
 lib/Slic3r/Config.pm                          |  16 +++
 lib/Slic3r/Extruder.pm                        |  19 +--
 lib/Slic3r/Flow.pm                            | 115 ++++++++--------
 lib/Slic3r/GUI/Plater.pm                      |   1 -
 lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm |   7 +-
 lib/Slic3r/GUI/SkeinPanel.pm                  |   5 +-
 lib/Slic3r/Layer/Region.pm                    |  74 ++++------
 lib/Slic3r/Model.pm                           |   3 +-
 lib/Slic3r/Print.pm                           | 130 ++++++++++++++----
 lib/Slic3r/Print/Object.pm                    |   8 +-
 lib/Slic3r/Print/Region.pm                    |  58 +++++++-
 slic3r.pl                                     |   5 +-
 xs/src/PrintConfig.hpp                        |   4 +
 13 files changed, 297 insertions(+), 148 deletions(-)

diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm
index 0db6e69e1..bd924448b 100644
--- a/lib/Slic3r/Config.pm
+++ b/lib/Slic3r/Config.pm
@@ -175,6 +175,22 @@ sub setenv {
     }
 }
 
+sub equals {
+    my ($self, $other) = @_;
+    return @{ $self->diff($other) } == 0;
+}
+
+sub diff {
+    my ($self, $other) = @_;
+    
+    my @diff = ();
+    foreach my $opt_key (sort @{$self->get_keys}) {
+        push @diff, $opt_key
+            if !$other->has($opt_key) || $other->serialize($opt_key) ne $self->serialize($opt_key);
+    }
+    return [@diff];
+}
+
 # this method is idempotent by design
 sub validate {
     my $self = shift;
diff --git a/lib/Slic3r/Extruder.pm b/lib/Slic3r/Extruder.pm
index 9444f6916..1025c3459 100644
--- a/lib/Slic3r/Extruder.pm
+++ b/lib/Slic3r/Extruder.pm
@@ -1,6 +1,12 @@
 package Slic3r::Extruder;
 use Moo;
 
+require Exporter;
+our @ISA = qw(Exporter);
+our @EXPORT_OK   = qw(EXTRUDER_ROLE_PERIMETER EXTRUDER_ROLE_INFILL EXTRUDER_ROLE_SUPPORT_MATERIAL
+                    EXTRUDER_ROLE_SUPPORT_MATERIAL_INTERFACE);
+our %EXPORT_TAGS = (roles => \@EXPORT_OK);
+
 use Slic3r::Geometry qw(PI scale);
 
 use constant OPTIONS => [qw(
@@ -14,7 +20,6 @@ has 'id'    => (is => 'rw', required => 1);
 has $_      => (is => 'ro', required => 1) for @{&OPTIONS};
 has 'config'=> (is => 'ro', required => 1);
 
-has 'bridge_flow'               => (is => 'lazy');
 has 'E'                         => (is => 'rw', default => sub {0} );
 has 'absolute_E'                => (is => 'rw', default => sub {0} );
 has 'retracted'                 => (is => 'rw', default => sub {0} );
@@ -23,14 +28,10 @@ has 'e_per_mm3'                 => (is => 'lazy');
 has 'retract_speed_mm_min'      => (is => 'lazy');
 has '_mm3_per_mm_cache'         => (is => 'ro', default => sub {{}});
 
-sub _build_bridge_flow {
-    my $self = shift;
-    
-    return Slic3r::Flow::Bridge->new(
-        nozzle_diameter     => $self->nozzle_diameter,
-        bridge_flow_ratio   => $self->config->bridge_flow_ratio,
-    );
-}
+use constant EXTRUDER_ROLE_PERIMETER                    => 1;
+use constant EXTRUDER_ROLE_INFILL                       => 2;
+use constant EXTRUDER_ROLE_SUPPORT_MATERIAL             => 3;
+use constant EXTRUDER_ROLE_SUPPORT_MATERIAL_INTERFACE   => 4;
 
 sub _build_e_per_mm3 {
     my $self = shift;
diff --git a/lib/Slic3r/Flow.pm b/lib/Slic3r/Flow.pm
index a02f7b34e..0a9747fa8 100644
--- a/lib/Slic3r/Flow.pm
+++ b/lib/Slic3r/Flow.pm
@@ -1,48 +1,73 @@
 package Slic3r::Flow;
 use Moo;
 
-use Slic3r::Geometry qw(PI scale);
+require Exporter;
+our @ISA = qw(Exporter);
+our @EXPORT_OK   = qw(FLOW_ROLE_PERIMETER FLOW_ROLE_INFILL FLOW_ROLE_SOLID_INFILL FLOW_ROLE_TOP_SOLID_INFILL
+                        FLOW_ROLE_SUPPORT_MATERIAL FLOW_ROLE_SUPPORT_MATERIAL_INTERFACE);
+our %EXPORT_TAGS = (roles => \@EXPORT_OK);
 
-has 'nozzle_diameter'   => (is => 'ro', required => 1);
-has 'layer_height'      => (is => 'ro', required => 1);
-has 'role'              => (is => 'ro', default => sub { '' });
+use Slic3r::Geometry qw(PI);
 
-has 'width'             => (is => 'rwp', builder => 1);
-has 'spacing'           => (is => 'lazy');
+has 'width'             => (is => 'ro');
+has 'spacing'           => (is => 'ro');
 has 'scaled_width'      => (is => 'lazy');
 has 'scaled_spacing'    => (is => 'lazy');
 
-sub BUILD {
-    my $self = shift;
+use constant FLOW_ROLE_PERIMETER                    => 1;
+use constant FLOW_ROLE_INFILL                       => 2;
+use constant FLOW_ROLE_SOLID_INFILL                 => 3;
+use constant FLOW_ROLE_TOP_SOLID_INFILL             => 4;
+use constant FLOW_ROLE_SUPPORT_MATERIAL             => 5;
+use constant FLOW_ROLE_SUPPORT_MATERIAL_INTERFACE   => 6;
+
+sub BUILDARGS {
+    my ($self, %args) = @_;
     
-    if ($self->width =~ /^(\d+(?:\.\d+)?)%$/) {
-        $self->_set_width($self->layer_height * $1 / 100);
+    # the constructor can take two sets of arguments:
+    # - width (only absolute value), spacing
+    # - width (abs/%/0), role, nozzle_diameter, layer_height, bridge_flow_ratio
+    #   (if bridge_flow_ratio == 0, we return a non-bridge flow)
+    
+    if (exists $args{role}) {
+        if ($args{width} eq '0') {
+            $args{width} = $self->_width(@args{qw(role nozzle_diameter layer_height bridge_flow_ratio)});
+        } elsif ($args{width} =~ /^(\d+(?:\.\d+)?)%$/) {
+            $args{width} = $args{layer_height} * $1 / 100;
+        }
+        $args{spacing} = $self->_spacing(@args{qw(width nozzle_diameter layer_height bridge_flow_ratio)});
+        %args = @args{qw(width spacing)};
     }
-    $self->_set_width($self->_build_width) if $self->width == 0; # auto
+    
+    return {%args};
 }
 
-sub _build_width {
-    my $self = shift;
+sub _width {
+    my ($self, $role, $nozzle_diameter, $layer_height, $bridge_flow_ratio) = @_;
+    
+    if ($bridge_flow_ratio > 0) {
+        return sqrt($bridge_flow_ratio * ($nozzle_diameter**2));
+    }
     
     # here we calculate a sane default by matching the flow speed (at the nozzle) and the feed rate
-    my $volume = ($self->nozzle_diameter**2) * PI/4;
-    my $shape_threshold = $self->nozzle_diameter * $self->layer_height + ($self->layer_height**2) * PI/4;
+    my $volume = ($nozzle_diameter**2) * PI/4;
+    my $shape_threshold = $nozzle_diameter * $layer_height + ($layer_height**2) * PI/4;
     my $width;
     if ($volume >= $shape_threshold) {
         # rectangle with semicircles at the ends
-        $width = (($self->nozzle_diameter**2) * PI + ($self->layer_height**2) * (4 - PI)) / (4 * $self->layer_height);
+        $width = (($nozzle_diameter**2) * PI + ($layer_height**2) * (4 - PI)) / (4 * $layer_height);
     } else {
         # rectangle with squished semicircles at the ends
-        $width = $self->nozzle_diameter * ($self->nozzle_diameter/$self->layer_height - 4/PI + 1);
+        $width = $nozzle_diameter * ($nozzle_diameter/$layer_height - 4/PI + 1);
     }
     
-    my $min = $self->nozzle_diameter * 1.05;
+    my $min = $nozzle_diameter * 1.05;
     my $max;
-    if ($self->role eq 'perimeter' || $self->role eq 'support_material') {
-        $min = $max = $self->nozzle_diameter;
-    } elsif ($self->role ne 'infill') {
+    if ($role == FLOW_ROLE_PERIMETER || $role == FLOW_ROLE_SUPPORT_MATERIAL) {
+        $min = $max = $nozzle_diameter;
+    } elsif ($role != FLOW_ROLE_INFILL) {
         # do not limit width for sparse infill so that we use full native flow for it
-        $max = $self->nozzle_diameter * 1.7;
+        $max = $nozzle_diameter * 1.7;
     }
     $width = $max if defined($max) && $width > $max;
     $width = $min if $width < $min;
@@ -50,59 +75,41 @@ sub _build_width {
     return $width;
 }
 
-sub _build_spacing {
-    my $self = shift;
+sub _spacing {
+    my ($self, $width, $nozzle_diameter, $layer_height, $bridge_flow_ratio) = @_;
+    
+    if ($bridge_flow_ratio > 0) {
+        return $width + 0.05;
+    }
     
     my $min_flow_spacing;
-    if ($self->width >= ($self->nozzle_diameter + $self->layer_height)) {
+    if ($width >= ($nozzle_diameter + $layer_height)) {
         # rectangle with semicircles at the ends
-        $min_flow_spacing = $self->width - $self->layer_height * (1 - PI/4);
+        $min_flow_spacing = $width - $layer_height * (1 - PI/4);
     } else {
         # rectangle with shrunk semicircles at the ends
-        $min_flow_spacing = $self->nozzle_diameter * (1 - PI/4) + $self->width * PI/4;
+        $min_flow_spacing = $nozzle_diameter * (1 - PI/4) + $width * PI/4;
     }
-    return $self->width - &Slic3r::OVERLAP_FACTOR * ($self->width - $min_flow_spacing);
+    return $width - &Slic3r::OVERLAP_FACTOR * ($width - $min_flow_spacing);
 }
 
 sub clone {
     my $self = shift;
     
     return (ref $self)->new(
-        nozzle_diameter => $self->nozzle_diameter,
-        layer_height    => $self->layer_height,
-        @_,
+        width   => $self->width,
+        spacing => $self->spacing,
     );
 }
 
 sub _build_scaled_width {
     my $self = shift;
-    return scale $self->width;
+    return Slic3r::Geometry::scale($self->width);
 }
 
 sub _build_scaled_spacing {
     my $self = shift;
-    return scale $self->spacing;
-}
-
-
-package Slic3r::Flow::Bridge;
-use Moo;
-extends 'Slic3r::Flow';
-
-# layer_height is not required in this case
-has '+layer_height'         => (is => 'ro', required => 0);
-has 'bridge_flow_ratio'     => (is => 'ro', required => 1);
-
-use Slic3r::Geometry qw(PI);
-
-sub _build_width {
-    my $self = shift;
-    return sqrt($self->bridge_flow_ratio * ($self->nozzle_diameter**2));
-}
-
-sub _build_spacing {
-    my $self = shift;
-    return $self->width + 0.05;
+    return Slic3r::Geometry::scale($self->spacing);
 }
 
 1;
diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm
index ed85250a2..e0b0fcf4a 100644
--- a/lib/Slic3r/GUI/Plater.pm
+++ b/lib/Slic3r/GUI/Plater.pm
@@ -638,7 +638,6 @@ sub split_object {
             input_file              => $current_model_object->input_file,
             config                  => $current_model_object->config->clone,
             layer_height_ranges     => $current_model_object->layer_height_ranges,  # TODO: clone this
-            material_mapping        => $current_model_object->material_mapping,     # TODO: clone this
         );
         $model_object->add_volume(
             mesh        => $mesh,
diff --git a/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm b/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm
index 30a5dadeb..e681d7238 100644
--- a/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm
+++ b/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm
@@ -324,7 +324,12 @@ sub Closing {
     my $self = shift;
     
     # save mappings into the plater object
-    $self->model_object->material_mapping($self->{mapping});
+    foreach my $volume (@{$self->model_object}) {
+        if (defined $volume->material_id) {
+            my $config = $self->model_object->model->materials->{ $volume->material_id }->config;
+            $config->set('extruder', $self->{mapping}{ $volume->material_id });
+        }
+    }
 }
 
 1;
diff --git a/lib/Slic3r/GUI/SkeinPanel.pm b/lib/Slic3r/GUI/SkeinPanel.pm
index ee11147bd..7750794c0 100644
--- a/lib/Slic3r/GUI/SkeinPanel.pm
+++ b/lib/Slic3r/GUI/SkeinPanel.pm
@@ -146,7 +146,10 @@ sub quick_slice {
         }
         $model->center_instances_around_point($config->print_center);
         
-        $print->add_model_object($_) for @{ $model->objects };
+        foreach my $model_object (@{$model->objects}) {
+            $print->auto_assign_extruders($model_object);
+            $print->add_model_object($model_object);
+        }
         $print->validate;
 
         # select output file
diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm
index 53f40a659..e065f561e 100644
--- a/lib/Slic3r/Layer/Region.pm
+++ b/lib/Slic3r/Layer/Region.pm
@@ -3,6 +3,7 @@ use Moo;
 
 use List::Util qw(sum first);
 use Slic3r::ExtrusionPath ':roles';
+use Slic3r::Flow ':roles';
 use Slic3r::Geometry qw(PI A B scale unscale chained_path points_coincide);
 use Slic3r::Geometry::Clipper qw(union_ex diff_ex intersection_ex 
     offset offset2 offset2_ex union_pt diff intersection
@@ -17,10 +18,6 @@ has 'layer' => (
     handles     => [qw(id slice_z print_z height flow config)],
 );
 has 'region'            => (is => 'ro', required => 1, handles => [qw(extruders)]);
-has 'perimeter_flow'    => (is => 'rw');
-has 'infill_flow'       => (is => 'rw');
-has 'solid_infill_flow' => (is => 'rw');
-has 'top_infill_flow'   => (is => 'rw');
 has 'infill_area_threshold' => (is => 'lazy');
 has 'overhang_width'    => (is => 'lazy');
 
@@ -40,43 +37,26 @@ has 'perimeters' => (is => 'rw', default => sub { Slic3r::ExtrusionPath::Collect
 # ordered collection of extrusion paths to fill surfaces
 has 'fills' => (is => 'rw', default => sub { Slic3r::ExtrusionPath::Collection->new });
 
-sub BUILD {
-    my $self = shift;
-    $self->_update_flows;
-}
-
-sub _trigger_layer {
-    my $self = shift;
-    $self->_update_flows;
-}
-
-sub _update_flows {
-    my $self = shift;
-    return if !$self->region;
-    
-    if ($self->id == 0) {
-        for (qw(perimeter infill solid_infill top_infill)) {
-            my $method = "${_}_flow";
-            $self->$method
-                ($self->region->first_layer_flows->{$_} || $self->region->flows->{$_});
-        } 
-    } else {
-        $self->perimeter_flow($self->region->flows->{perimeter});
-        $self->infill_flow($self->region->flows->{infill});
-        $self->solid_infill_flow($self->region->flows->{solid_infill});
-        $self->top_infill_flow($self->region->flows->{top_infill});
-    }
-}
-
 sub _build_overhang_width {
     my $self = shift;
-    my $threshold_rad = PI/2 - atan2($self->perimeter_flow->width / $self->height / 2, 1);
+    my $threshold_rad = PI/2 - atan2($self->flow(FLOW_ROLE_PERIMETER)->width / $self->height / 2, 1);
     return scale($self->height * ((cos $threshold_rad) / (sin $threshold_rad)));
 }
 
 sub _build_infill_area_threshold {
     my $self = shift;
-    return $self->solid_infill_flow->scaled_spacing ** 2;
+    return $self->flow(FLOW_ROLE_SOLID_INFILL)->scaled_spacing ** 2;
+}
+
+sub flow {
+    my ($self, $role, $bridge, $width) = @_;
+    return $self->region->flow(
+        $role,
+        $self->layer->height,
+        $bridge // 0,
+        $self->layer->id == 0,
+        $width,
+    );
 }
 
 # build polylines from lines
@@ -145,10 +125,11 @@ sub _merge_loops {
 sub make_perimeters {
     my $self = shift;
     
-    my $pwidth              = $self->perimeter_flow->scaled_width;
-    my $pspacing            = $self->perimeter_flow->scaled_spacing;
-    my $ispacing            = $self->solid_infill_flow->scaled_spacing;
-    my $gap_area_threshold  = $self->perimeter_flow->scaled_width ** 2;
+    my $perimeter_flow      = $self->flow(FLOW_ROLE_PERIMETER);
+    my $pwidth              = $perimeter_flow->scaled_width;
+    my $pspacing            = $perimeter_flow->scaled_spacing;
+    my $ispacing            = $self->flow(FLOW_ROLE_SOLID_INFILL)->scaled_spacing;
+    my $gap_area_threshold  = $pwidth ** 2;
     
     $self->perimeters->clear;
     $self->fill_surfaces->clear;
@@ -284,7 +265,7 @@ sub make_perimeters {
             push @loops, Slic3r::ExtrusionLoop->new(
                 polygon         => $polygon,
                 role            => $role,
-                flow_spacing    => $self->perimeter_flow->spacing,
+                flow_spacing    => $perimeter_flow->spacing,
             );
         }
         return @loops;
@@ -311,7 +292,7 @@ sub make_perimeters {
             next if $p->length <= $pspacing * 2;
             my %params = (
                 role            => EXTR_ROLE_EXTERNAL_PERIMETER,
-                flow_spacing    => $self->perimeter_flow->spacing,
+                flow_spacing    => $perimeter_flow->spacing,
             );
             push @paths, $p->isa('Slic3r::Polygon')
                 ? Slic3r::ExtrusionLoop->new(polygon  => $p, %params)
@@ -345,10 +326,10 @@ sub _fill_gaps {
     # we could try with 1.5*$w for example, but that doesn't work well for zigzag fill
     # because it tends to create very sparse points along the gap when the infill direction
     # is not parallel to the gap (1.5*$w thus may only work well with a straight line)
-    my $w = $self->perimeter_flow->width;
+    my $w = $self->flow(FLOW_ROLE_PERIMETER)->width;
     my @widths = ($w, 0.4 * $w);  # worth trying 0.2 too?
     foreach my $width (@widths) {
-        my $flow = $self->perimeter_flow->clone(width => $width);
+        my $flow = $self->flow(FLOW_ROLE_PERIMETER, 0, $width);
         
         # extract the gaps having this width
         my @this_width = map @{$_->offset_ex(+0.5*$flow->scaled_width)},
@@ -499,7 +480,10 @@ sub process_external_surfaces {
 sub _detect_bridge_direction {
     my ($self, $expolygon, $lower_layer) = @_;
     
-    my $grown = $expolygon->offset_ex(+$self->perimeter_flow->scaled_width);
+    my $perimeter_flow  = $self->flow(FLOW_ROLE_PERIMETER);
+    my $infill_flow     = $self->flow(FLOW_ROLE_INFILL);
+    
+    my $grown = $expolygon->offset_ex(+$perimeter_flow->scaled_width);
     my @lower = @{$lower_layer->slices};       # expolygons
     
     # detect what edges lie on lower slices
@@ -554,7 +538,7 @@ sub _detect_bridge_direction {
         }
     } elsif (@edges) {
         # inset the bridge expolygon; we'll use this one to clip our test lines
-        my $inset = $expolygon->offset_ex($self->infill_flow->scaled_width);
+        my $inset = $expolygon->offset_ex($infill_flow->scaled_width);
         
         # detect anchors as intersection between our bridge expolygon and the lower slices
         my $anchors = intersection_ex(
@@ -568,7 +552,7 @@ sub _detect_bridge_direction {
         # endpoints within anchors
         my %directions = ();  # angle => score
         my $angle_increment = PI/36; # 5°
-        my $line_increment = $self->infill_flow->scaled_width;
+        my $line_increment = $infill_flow->scaled_width;
         for (my $angle = 0; $angle <= PI; $angle += $angle_increment) {
             # rotate everything - the center point doesn't matter
             $_->rotate($angle, [0,0]) for @$inset, @$anchors;
diff --git a/lib/Slic3r/Model.pm b/lib/Slic3r/Model.pm
index d9b5ef463..401deb47e 100644
--- a/lib/Slic3r/Model.pm
+++ b/lib/Slic3r/Model.pm
@@ -44,7 +44,6 @@ sub add_object {
             input_file          => $object->input_file,
             config              => $object->config,
             layer_height_ranges => $object->layer_height_ranges,    # TODO: clone!
-            material_mapping    => $object->material_mapping,       # TODO: clone!
         );
         
         foreach my $volume (@{$object->volumes}) {
@@ -324,6 +323,7 @@ use Moo;
 
 has 'model'         => (is => 'ro', weak_ref => 1, required => 1);
 has 'attributes'    => (is => 'rw', default => sub { {} });
+has 'config'        => (is => 'rw', default => sub { Slic3r::Config->new });
 
 package Slic3r::Model::Object;
 use Moo;
@@ -338,7 +338,6 @@ has 'volumes'               => (is => 'ro', default => sub { [] });
 has 'instances'             => (is => 'rw');
 has 'config'                => (is => 'rw', default => sub { Slic3r::Config->new });
 has 'layer_height_ranges'   => (is => 'rw', default => sub { [] }); # [ z_min, z_max, layer_height ]
-has 'material_mapping'      => (is => 'rw', default => sub { {} }); # { material_id => region_idx }
 has '_bounding_box'         => (is => 'rw');
 
 sub add_volume {
diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm
index 59d90af7d..ef32677ea 100644
--- a/lib/Slic3r/Print.pm
+++ b/lib/Slic3r/Print.pm
@@ -17,8 +17,6 @@ has 'objects'                => (is => 'rw', default => sub {[]});
 has 'status_cb'              => (is => 'rw');
 has 'extruders'              => (is => 'rw', default => sub {[]});
 has 'regions'                => (is => 'rw', default => sub {[]});
-has 'support_material_flow'  => (is => 'rw');
-has 'first_layer_support_material_flow' => (is => 'rw');
 has 'has_support_material'   => (is => 'lazy');
 has '_state'                 => (is => 'ro', default => sub { Slic3r::Print::State->new });
 
@@ -88,30 +86,37 @@ sub add_model_object {
     my $self = shift;
     my ($object, $obj_idx) = @_;
     
-    # read the material mapping provided by the model object, if any
-    my %matmap = %{ $object->material_mapping || {} };
-    $_-- for values %matmap;  # extruders in the mapping are 1-indexed but we want 0-indexed
-    
     my %volumes = ();           # region_id => [ volume_id, ... ]
     foreach my $volume_id (0..$#{$object->volumes}) {
         my $volume = $object->volumes->[$volume_id];
         
-        # determine what region should this volume be mapped to
-        my $region_id;
+        # get the config applied to this volume
+        my $config;
         if (defined $volume->material_id) {
-            if (!exists $matmap{ $volume->material_id }) {
-                # there's no mapping between this material and a region
-                $matmap{ $volume->material_id } = scalar(@{ $self->regions });
-            }
-            $region_id = $matmap{ $volume->material_id };
+            my $config = $object->model->materials->{ $volume->material_id }->config;
         } else {
-            $region_id = 0;
+            $config = Slic3r::Config->new;
         }
+        
+        # find an existing print region with the same config
+        my $region_id;
+        foreach my $i (0..$#{$self->regions}) {
+            my $region = $self->regions->[$i];
+            if ($config->equals($region->config)) {
+                $region_id = $i;
+                last;
+            }
+        }
+        
+        # if no region exists with the same config, create a new one
+        if (!defined $region_id) {
+            push @{$self->regions}, Slic3r::Print::Region->new(config => $config->clone);
+            $region_id = $#{$self->regions};
+        }
+        
+        # assign volume to region
         $volumes{$region_id} //= [];
         push @{ $volumes{$region_id} }, $volume_id;
-        
-        # instantiate region if it does not exist
-        $self->regions->[$region_id] //= Slic3r::Print::Region->new;
     }
     
     # initialize print object
@@ -632,7 +637,7 @@ sub make_skirt {
     my @extruded_length = ();  # for each extruder
     
     # TODO: use each extruder's own flow
-    my $spacing = $self->objects->[0]->layers->[0]->regions->[0]->perimeter_flow->spacing;
+    my $spacing = $self->flow(FLOW_ROLE_SUPPORT_MATERIAL)->spacing;
     
     my $first_layer_height = $self->config->get_value('first_layer_height');
     my @extruders_e_per_mm = ();
@@ -673,7 +678,7 @@ sub make_brim {
     
     $self->brim->clear;  # method must be idempotent
     
-    my $flow = $self->objects->[0]->layers->[0]->regions->[0]->perimeter_flow;
+    my $flow = $self->flow(FLOW_ROLE_SUPPORT_MATERIAL);
     
     my $grow_distance = $flow->scaled_width / 2;
     my @islands = (); # array of polygons
@@ -747,15 +752,24 @@ sub write_gcode {
     for (qw(nozzle_diameter filament_diameter extrusion_multiplier)) {
         printf $fh "; %s = %s\n", $_, $self->config->$_->[0];
     }
-    printf $fh "; perimeters extrusion width = %.2fmm\n", $self->regions->[0]->flows->{perimeter}->width;
-    printf $fh "; infill extrusion width = %.2fmm\n", $self->regions->[0]->flows->{infill}->width;
-    printf $fh "; solid infill extrusion width = %.2fmm\n", $self->regions->[0]->flows->{solid_infill}->width;
-    printf $fh "; top infill extrusion width = %.2fmm\n", $self->regions->[0]->flows->{top_infill}->width;
-    printf $fh "; support material extrusion width = %.2fmm\n", $self->support_material_flow->width
-        if $self->support_material_flow;
-    printf $fh "; first layer extrusion width = %.2fmm\n", $self->regions->[0]->first_layer_flows->{perimeter}->width
-        if $self->regions->[0]->first_layer_flows->{perimeter};
-    print  $fh "\n";
+    
+    for my $region_id (0..$#{$self->regions}) {
+        printf $fh "; perimeters extrusion width = %.2fmm\n",
+            $self->regions->[$region_id]->flow(FLOW_ROLE_PERIMETER)->width;
+        printf $fh "; infill extrusion width = %.2fmm\n",
+            $self->regions->[$region_id]->flow(FLOW_ROLE_INFILL)->width;
+        printf $fh "; solid infill extrusion width = %.2fmm\n",
+            $self->regions->[$region_id]->flow(FLOW_ROLE_SOLID_INFILL)->width;
+        printf $fh "; top infill extrusion width = %.2fmm\n",
+            $self->regions->[$region_id]->flow(FLOW_ROLE_TOP_SOLID_INFILL)->width;
+        printf $fh "; support material extrusion width = %.2fmm\n",
+            $self->flow(FLOW_ROLE_SUPPORT_MATERIAL)->width
+            if $self->support_material_flow;
+        printf $fh "; first layer extrusion width = %.2fmm\n",
+            $self->flow(FLOW_ROLE_SUPPORT_MATERIAL, 0, 1)->width
+            if $self->regions->[0]->first_layer_flows->{perimeter};
+        print  $fh "\n";
+    }
     
     # set up our extruder object
     my $gcodegen = Slic3r::GCode->new(
@@ -1012,4 +1026,64 @@ sub invalidate_step {
             keys %Slic3r::Print::State::prereqs;
 }
 
+# This method assigns extruders to the volumes having a material
+# but not having extruders set in the material config.
+sub auto_assign_extruders {
+    my ($self, $model_object) = @_;
+    
+    my $extruders = scalar @{ $self->config->nozzle_diameter };
+    foreach my $i (0..$#{$model_object->volumes}) {
+        my $volume = $model_object->volumes->[$i];
+        if (defined $volume->material_id) {
+            my $material = $model_object->model->materials->{ $volume->material_id };
+            my $config = $material->config;
+            $config->set_ifndef('perimeters_extruder', $i);
+            $config->set_ifndef('infill_extruder', $i);
+            $config->set_ifndef('support_material_extruder', $i);
+            $config->set_ifndef('support_material_interface_extruder', $i);
+        }
+    }
+}
+
+sub flow {
+    my ($self, $role, $layer_height, $bridge, $first_layer, $width) = @_;
+    
+    $bridge         //= 0;
+    $first_layer    //= 0;
+    
+    # use the supplied custom width, if any
+    my $config_width = $width;
+    if (!defined $config_width) {
+        # get extrusion width from configuration
+        # (might be an absolute value, or a percent value, or zero for auto)
+        if ($first_layer) {
+            $config_width = $self->config->first_layer_extrusion_width;
+        } elsif ($role == FLOW_ROLE_SUPPORT_MATERIAL || $role == FLOW_ROLE_SUPPORT_MATERIAL_INTERFACE) {
+            $config_width = $self->config->support_material_extrusion_width;
+        } else {
+            die "Unknown role $role";
+        }
+    }
+    
+    # get the configured nozzle_diameter for the extruder associated
+    # to the flow role requested
+    my $extruder;  # 1-based
+    if ($role == FLOW_ROLE_SUPPORT_MATERIAL) {
+        $config_width = $self->config->support_material_extruder;
+    } elsif ($role == FLOW_ROLE_SUPPORT_MATERIAL_INTERFACE) {
+        $config_width = $self->config->support_material_interface_extruder;
+    } else {
+        die "Unknown role $role";
+    }
+    my $nozzle_diameter = $self->config->nozzle_diameter->[$extruder-1];
+    
+    return Slic3r::Flow->new(
+        width               => $config_width,
+        role                => $role,
+        nozzle_diameter     => $nozzle_diameter,
+        layer_height        => $layer_height,
+        bridge_flow_ratio   => ($bridge ? $self->config->bridge_flow_ratio : 0),
+    );
+}
+
 1;
diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm
index d7d8f00db..f1edc98f8 100644
--- a/lib/Slic3r/Print/Object.pm
+++ b/lib/Slic3r/Print/Object.pm
@@ -861,9 +861,11 @@ sub generate_support_material {
     return unless ($self->config->support_material || $self->config->raft_layers > 0)
         && $self->layer_count >= 2;
     
-    Slic3r::Print::SupportMaterial
-        ->new(config => $self->config, flow => $self->print->support_material_flow)
-        ->generate($self);
+    my $s = Slic3r::Print::SupportMaterial->new(
+        config  => $self->config,
+        flow    => $self->print->support_material_flow,
+    );
+    $s->generate($self);
 }
 
 sub _simplify_slices {
diff --git a/lib/Slic3r/Print/Region.pm b/lib/Slic3r/Print/Region.pm
index 253fde2ba..0d3b844f4 100644
--- a/lib/Slic3r/Print/Region.pm
+++ b/lib/Slic3r/Print/Region.pm
@@ -1,8 +1,60 @@
 package Slic3r::Print::Region;
 use Moo;
 
-has 'extruders'         => (is => 'rw', default => sub { {} }); # by role
-has 'flows'             => (is => 'rw', default => sub { {} }); # by role
-has 'first_layer_flows' => (is => 'rw', default => sub { {} }); # by role
+use Slic3r::Extruder ':roles';
+use Slic3r::Flow ':roles';
+
+# A Print::Region object represents a group of volumes to print
+# sharing the same config (including the same assigned extruder(s))
+
+has 'print'             => (is => 'ro', required => 1, weak_ref => 1);
+has 'config'            => (is => 'ro', required => 1);
+
+sub flow {
+    my ($self, $role, $layer_height, $bridge, $first_layer, $width) = @_;
+    
+    $bridge         //= 0;
+    $first_layer    //= 0;
+    
+    # use the supplied custom width, if any
+    my $config_width = $width;
+    if (!defined $config_width) {
+        # get extrusion width from configuration
+        # (might be an absolute value, or a percent value, or zero for auto)
+        if ($first_layer) {
+            $config_width = $self->config->first_layer_extrusion_width;
+        } elsif ($role == FLOW_ROLE_PERIMETER) {
+            $config_width = $self->config->perimeter_extrusion_width;
+        } elsif ($role == FLOW_ROLE_INFILL) {
+            $config_width = $self->config->infill_extrusion_width;
+        } elsif ($role == FLOW_ROLE_SOLID_INFILL) {
+            $config_width = $self->config->solid_infill_extrusion_width;
+        } elsif ($role == FLOW_ROLE_TOP_SOLID_INFILL) {
+            $config_width = $self->config->top_infill_extrusion_width;
+        } else {
+            die "Unknown role $role";
+        }
+    }
+    
+    # get the configured nozzle_diameter for the extruder associated
+    # to the flow role requested
+    my $extruder;  # 1-based
+    if ($role == FLOW_ROLE_PERIMETER) {
+        $config_width = $self->config->perimeter_extruder;
+    } elsif ($role == FLOW_ROLE_INFILL || $role == FLOW_ROLE_SOLID_INFILL || $role == FLOW_ROLE_TOP_SOLID_INFILL) {
+        $config_width = $self->config->infill_extruder;
+    } else {
+        die "Unknown role $role";
+    }
+    my $nozzle_diameter = $self->print->config->nozzle_diameter->[$extruder-1];
+    
+    return Slic3r::Flow->new(
+        width               => $config_width,
+        role                => $role,
+        nozzle_diameter     => $nozzle_diameter,
+        layer_height        => $layer_height,
+        bridge_flow_ratio   => ($bridge ? $self->config->bridge_flow_ratio : 0),
+    );
+}
 
 1;
diff --git a/slic3r.pl b/slic3r.pl
index c55bd19a6..0be1219d0 100755
--- a/slic3r.pl
+++ b/slic3r.pl
@@ -159,7 +159,10 @@ if (@ARGV) {  # slicing from command line
                 printf "=> %s\n", $message;
             },
         );
-        $print->add_model_object($_) for @{$model->objects};
+        foreach my $model_object (@{$model->objects}) {
+            $print->auto_assign_extruders($model_object);
+            $print->add_model_object($model_object);
+        }
         undef $model;  # free memory
         $print->validate;
         if ($opt{export_svg}) {
diff --git a/xs/src/PrintConfig.hpp b/xs/src/PrintConfig.hpp
index 4dd732150..a6bd71ec7 100644
--- a/xs/src/PrintConfig.hpp
+++ b/xs/src/PrintConfig.hpp
@@ -761,6 +761,7 @@ class PrintConfig : public StaticConfig
         Options["infill_extruder"].label = "Infill extruder";
         Options["infill_extruder"].tooltip = "The extruder to use when printing infill.";
         Options["infill_extruder"].cli = "infill-extruder=i";
+        Options["infill_extruder"].shortcut.push_back("extruder");
 
         Options["infill_extrusion_width"].type = coFloatOrPercent;
         Options["infill_extrusion_width"].label = "Infill";
@@ -878,6 +879,7 @@ class PrintConfig : public StaticConfig
         Options["perimeter_extruder"].tooltip = "The extruder to use when printing perimeters.";
         Options["perimeter_extruder"].cli = "perimeter-extruder=i";
         Options["perimeter_extruder"].aliases.push_back("perimeters_extruder");
+        Options["perimeter_extruder"].shortcut.push_back("extruder");
 
         Options["perimeter_extrusion_width"].type = coFloatOrPercent;
         Options["perimeter_extrusion_width"].label = "Perimeters";
@@ -1139,6 +1141,7 @@ class PrintConfig : public StaticConfig
         Options["support_material_extruder"].label = "Support material extruder";
         Options["support_material_extruder"].tooltip = "The extruder to use when printing support material. This affects brim and raft too.";
         Options["support_material_extruder"].cli = "support-material-extruder=i";
+        Options["support_material_extruder"].shortcut.push_back("extruder");
 
         Options["support_material_extrusion_width"].type = coFloatOrPercent;
         Options["support_material_extrusion_width"].label = "Support material";
@@ -1150,6 +1153,7 @@ class PrintConfig : public StaticConfig
         Options["support_material_interface_extruder"].label = "Support material interface extruder";
         Options["support_material_interface_extruder"].tooltip = "The extruder to use when printing support material interface. This affects raft too.";
         Options["support_material_interface_extruder"].cli = "support-material-interface-extruder=i";
+        Options["support_material_interface_extruder"].shortcut.push_back("extruder");
 
         Options["support_material_interface_layers"].type = coInt;
         Options["support_material_interface_layers"].label = "Interface layers";

From 83326845dd9d6d7fbceb5c03a487e4c4f77f5e83 Mon Sep 17 00:00:00 2001
From: Alessandro Ranellucci <aar@cpan.org>
Date: Tue, 31 Dec 2013 14:33:03 +0100
Subject: [PATCH 02/19] More incomplete work

---
 lib/Slic3r/Layer/Region.pm |   3 +-
 lib/Slic3r/Print.pm        | 141 +++++++++++--------------------------
 lib/Slic3r/Print/Object.pm |  27 ++++++-
 lib/Slic3r/Print/Region.pm |   2 +-
 xs/src/Config.cpp          |   7 ++
 xs/src/PrintConfig.hpp     |  12 ++--
 xs/t/15_config.t           |   4 +-
 7 files changed, 85 insertions(+), 111 deletions(-)

diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm
index e065f561e..1f27d143c 100644
--- a/lib/Slic3r/Layer/Region.pm
+++ b/lib/Slic3r/Layer/Region.pm
@@ -14,8 +14,7 @@ has 'layer' => (
     is          => 'ro',
     weak_ref    => 1,
     required    => 1,
-    trigger     => 1,
-    handles     => [qw(id slice_z print_z height flow config)],
+    handles     => [qw(id slice_z print_z height config)],
 );
 has 'region'            => (is => 'ro', required => 1, handles => [qw(extruders)]);
 has 'infill_area_threshold' => (is => 'lazy');
diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm
index ef32677ea..98060342b 100644
--- a/lib/Slic3r/Print.pm
+++ b/lib/Slic3r/Print.pm
@@ -5,6 +5,7 @@ use File::Basename qw(basename fileparse);
 use File::Spec;
 use List::Util qw(min max first);
 use Slic3r::ExtrusionPath ':roles';
+use Slic3r::Flow ':roles';
 use Slic3r::Geometry qw(X Y Z X1 Y1 X2 Y2 MIN MAX PI scale unscale move_points chained_path
     convex_hull);
 use Slic3r::Geometry::Clipper qw(diff_ex union_ex union_pt intersection_ex intersection offset
@@ -96,6 +97,7 @@ sub add_model_object {
             my $config = $object->model->materials->{ $volume->material_id }->config;
         } else {
             $config = Slic3r::Config->new;
+            $config->set('extruder', 0);
         }
         
         # find an existing print region with the same config
@@ -110,7 +112,10 @@ sub add_model_object {
         
         # if no region exists with the same config, create a new one
         if (!defined $region_id) {
-            push @{$self->regions}, Slic3r::Print::Region->new(config => $config->clone);
+            push @{$self->regions}, Slic3r::Print::Region->new(
+                print  => $self,
+                config => $config->clone,
+            );
             $region_id = $#{$self->regions};
         }
         
@@ -233,15 +238,19 @@ sub validate {
 sub init_extruders {
     my $self = shift;
     
-    # map regions to extruders (ghetto mapping for now)
-    my %extruder_mapping = map { $_ => $_ } 0..$#{$self->regions};
-    
     # initialize all extruder(s) we need
-    my @used_extruders = (
-        0,
-        (map $self->config->get("${_}_extruder")-1, qw(perimeter infill support_material support_material_interface)),
-        (values %extruder_mapping),
-    );
+    my @used_extruders = ();
+    foreach my $region (@{$self->regions}) {
+        push @used_extruders,
+            map $region->config->get("${_}_extruder")-1,
+            qw(perimeter infill);
+    }
+    foreach my $object (@{$self->objects}) {
+        push @used_extruders,
+            map $object->config->get("${_}_extruder")-1,
+            qw(support_material support_material_interface);
+    }
+    
     for my $extruder_id (keys %{{ map {$_ => 1} @used_extruders }}) {
         $self->extruders->[$extruder_id] = Slic3r::Extruder->new(
             config => $self->config,
@@ -251,51 +260,8 @@ sub init_extruders {
         );
     }
     
-    # calculate regions' flows
-    for my $region_id (0 .. $#{$self->regions}) {
-        my $region = $self->regions->[$region_id];
-        
-        # per-role extruders and flows
-        for (qw(perimeter infill solid_infill top_infill)) {
-            my $extruder_name = $_;
-            $extruder_name =~ s/^(?:solid|top)_//;
-            $region->extruders->{$_} = ($self->regions_count > 1)
-                ? $self->extruders->[$extruder_mapping{$region_id}]
-                : $self->extruders->[$self->config->get("${extruder_name}_extruder")-1];
-            $region->flows->{$_} = $region->extruders->{$_}->make_flow(
-                layer_height => $self->config->layer_height,
-                width => $self->config->get("${_}_extrusion_width") || $self->config->extrusion_width,
-                role  => $_,
-            );
-            $region->first_layer_flows->{$_} = $region->extruders->{$_}->make_flow(
-                layer_height    => $self->config->get_value('first_layer_height'),
-                width           => $self->config->first_layer_extrusion_width,
-                role            => $_,
-            ) if $self->config->first_layer_extrusion_width;
-        }
-    }
-    
-    # calculate support material flow
-    # Note: we should calculate a different flow for support material interface
-    # TODO: support material layers have their own variable layer heights, so we
-    # probably need a DynamicFlow object that calculates flow on the fly
-    # (or the Flow object must support a mutable layer_height)
-    if ($self->has_support_material) {
-        my $extruder = $self->extruders->[$self->config->support_material_extruder-1];
-        $self->support_material_flow($extruder->make_flow(
-            layer_height => $self->config->layer_height,  # WRONG!
-            width => $self->config->support_material_extrusion_width || $self->config->extrusion_width,
-            role  => 'support_material',
-        ));
-        $self->first_layer_support_material_flow($extruder->make_flow(
-            layer_height    => $self->config->get_value('first_layer_height'),
-            width           => $self->config->first_layer_extrusion_width,
-            role            => 'support_material',
-        ));
-    }
-    
     # enforce tall skirt if using ooze_prevention
-    # NOTE: this is not idempotent (i.e. switching ooze_prevention off will not revert skirt settings)
+    # FIXME: this is not idempotent (i.e. switching ooze_prevention off will not revert skirt settings)
     if ($self->config->ooze_prevention && @{$self->extruders} > 1) {
         $self->config->set('skirt_height', -1);
         $self->config->set('skirts', 1) if $self->config->skirts == 0;
@@ -636,8 +602,17 @@ sub make_skirt {
     
     my @extruded_length = ();  # for each extruder
     
+    # skirt may be printed on several layers, having distinct layer heights,
+    # but loops must be aligned so can't vary width/spacing
     # TODO: use each extruder's own flow
-    my $spacing = $self->flow(FLOW_ROLE_SUPPORT_MATERIAL)->spacing;
+    my $flow = Slic3r::Flow->new(
+        width               => ($self->config->first_layer_extrusion_width || $self->config->perimeter_extrusion_width),
+        role                => FLOW_ROLE_PERIMETER,
+        nozzle_diameter     => $self->config->nozzle_diameter->[0],
+        layer_height        => $self->config->get_abs_value('first_layer_height'),
+        bridge_flow_ratio   => 0,
+    );
+    my $spacing = $flow->spacing;
     
     my $first_layer_height = $self->config->get_value('first_layer_height');
     my @extruders_e_per_mm = ();
@@ -678,7 +653,14 @@ sub make_brim {
     
     $self->brim->clear;  # method must be idempotent
     
-    my $flow = $self->flow(FLOW_ROLE_SUPPORT_MATERIAL);
+    # brim is only printed on first layer and uses support material extruder
+    my $flow = Slic3r::Flow->new(
+        width               => ($self->config->first_layer_extrusion_width || $self->config->perimeter_extrusion_width),
+        role                => FLOW_ROLE_PERIMETER,
+        nozzle_diameter     => $self->config->nozzle_diameter->[ $self->config->support_material_extruder-1 ],
+        layer_height        => $self->config->get_abs_value('first_layer_height'),
+        bridge_flow_ratio   => 0,
+    );
     
     my $grow_distance = $flow->scaled_width / 2;
     my @islands = (); # array of polygons
@@ -763,11 +745,11 @@ sub write_gcode {
         printf $fh "; top infill extrusion width = %.2fmm\n",
             $self->regions->[$region_id]->flow(FLOW_ROLE_TOP_SOLID_INFILL)->width;
         printf $fh "; support material extrusion width = %.2fmm\n",
-            $self->flow(FLOW_ROLE_SUPPORT_MATERIAL)->width
-            if $self->support_material_flow;
+            $self->objects->[0]->support_material_flow->width
+            if $self->has_support_material;
         printf $fh "; first layer extrusion width = %.2fmm\n",
-            $self->flow(FLOW_ROLE_SUPPORT_MATERIAL, 0, 1)->width
-            if $self->regions->[0]->first_layer_flows->{perimeter};
+            $self->regions->[$region_id]->flow(FLOW_ROLE_PERIMETER, 0, 1)->width
+            if ($self->regions->[$region_id]->config->first_layer_extrusion_width != 0);
         print  $fh "\n";
     }
     
@@ -1045,45 +1027,4 @@ sub auto_assign_extruders {
     }
 }
 
-sub flow {
-    my ($self, $role, $layer_height, $bridge, $first_layer, $width) = @_;
-    
-    $bridge         //= 0;
-    $first_layer    //= 0;
-    
-    # use the supplied custom width, if any
-    my $config_width = $width;
-    if (!defined $config_width) {
-        # get extrusion width from configuration
-        # (might be an absolute value, or a percent value, or zero for auto)
-        if ($first_layer) {
-            $config_width = $self->config->first_layer_extrusion_width;
-        } elsif ($role == FLOW_ROLE_SUPPORT_MATERIAL || $role == FLOW_ROLE_SUPPORT_MATERIAL_INTERFACE) {
-            $config_width = $self->config->support_material_extrusion_width;
-        } else {
-            die "Unknown role $role";
-        }
-    }
-    
-    # get the configured nozzle_diameter for the extruder associated
-    # to the flow role requested
-    my $extruder;  # 1-based
-    if ($role == FLOW_ROLE_SUPPORT_MATERIAL) {
-        $config_width = $self->config->support_material_extruder;
-    } elsif ($role == FLOW_ROLE_SUPPORT_MATERIAL_INTERFACE) {
-        $config_width = $self->config->support_material_interface_extruder;
-    } else {
-        die "Unknown role $role";
-    }
-    my $nozzle_diameter = $self->config->nozzle_diameter->[$extruder-1];
-    
-    return Slic3r::Flow->new(
-        width               => $config_width,
-        role                => $role,
-        nozzle_diameter     => $nozzle_diameter,
-        layer_height        => $layer_height,
-        bridge_flow_ratio   => ($bridge ? $self->config->bridge_flow_ratio : 0),
-    );
-}
-
 1;
diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm
index f1edc98f8..b1ef32fad 100644
--- a/lib/Slic3r/Print/Object.pm
+++ b/lib/Slic3r/Print/Object.pm
@@ -2,6 +2,7 @@ package Slic3r::Print::Object;
 use Moo;
 
 use List::Util qw(min max sum first);
+use Slic3r::Flow ':roles';
 use Slic3r::Geometry qw(X Y Z PI scale unscale deg2rad rad2deg scaled_epsilon chained_path);
 use Slic3r::Geometry::Clipper qw(diff diff_ex intersection intersection_ex union union_ex 
     offset offset_ex offset2 offset2_ex CLIPPER_OFFSET_SCALE JT_MITER);
@@ -300,7 +301,7 @@ sub make_perimeters {
             for my $layer_id (0 .. $self->layer_count-2) {
                 my $layerm          = $self->layers->[$layer_id]->regions->[$region_id];
                 my $upper_layerm    = $self->layers->[$layer_id+1]->regions->[$region_id];
-                my $perimeter_spacing       = $layerm->perimeter_flow->scaled_spacing;
+                my $perimeter_spacing       = $layerm->flow(FLOW_ROLE_PERIMETER)->scaled_spacing;
                 
                 my $overlap = $perimeter_spacing;  # one perimeter
                 
@@ -862,8 +863,9 @@ sub generate_support_material {
         && $self->layer_count >= 2;
     
     my $s = Slic3r::Print::SupportMaterial->new(
-        config  => $self->config,
-        flow    => $self->print->support_material_flow,
+        config          => $self->config,
+        flow            => $self->support_material_flow,
+        interface_flow  => $self->support_material_flow(FLOW_ROLE_SUPPORT_MATERIAL_INTERFACE),
     );
     $s->generate($self);
 }
@@ -877,4 +879,23 @@ sub _simplify_slices {
     }
 }
 
+sub support_material_flow {
+    my ($self, $role) = @_;
+    
+    $role //= FLOW_ROLE_SUPPORT_MATERIAL;
+    my $extruder = ($role == FLOW_ROLE_SUPPORT_MATERIAL)
+        ? $self->config->support_material_extruder
+        : $self->config->support_material_interface_extruder;
+    
+    # we use a bogus layer_height because we use the same flow for all
+    # support material layers
+    return Slic3r::Flow->new(
+        width               => $self->config->support_material_extrusion_width,
+        role                => $role,
+        nozzle_diameter     => $self->print->config->nozzle_diameter->[$extruder-1],
+        layer_height        => $self->config->layer_height,
+        bridge_flow_ratio   => 0,
+    );
+}
+
 1;
diff --git a/lib/Slic3r/Print/Region.pm b/lib/Slic3r/Print/Region.pm
index 0d3b844f4..559fce3c5 100644
--- a/lib/Slic3r/Print/Region.pm
+++ b/lib/Slic3r/Print/Region.pm
@@ -21,7 +21,7 @@ sub flow {
     if (!defined $config_width) {
         # get extrusion width from configuration
         # (might be an absolute value, or a percent value, or zero for auto)
-        if ($first_layer) {
+        if ($first_layer && $self->config->first_layer_extrusion_width != 0) {
             $config_width = $self->config->first_layer_extrusion_width;
         } elsif ($role == FLOW_ROLE_PERIMETER) {
             $config_width = $self->config->perimeter_extrusion_width;
diff --git a/xs/src/Config.cpp b/xs/src/Config.cpp
index 15ff4340c..828795c0c 100644
--- a/xs/src/Config.cpp
+++ b/xs/src/Config.cpp
@@ -131,6 +131,13 @@ ConfigBase::set(t_config_option_key opt_key, SV* value) {
     ConfigOption* opt = this->option(opt_key, true);
     if (opt == NULL) CONFESS("Trying to set non-existing option");
     
+    ConfigOptionDef* optdef = &(*this->def)[opt_key];
+    if (!optdef->shortcut.empty()) {
+        for (std::vector<t_config_option_key>::iterator it = optdef->shortcut.begin(); it != optdef->shortcut.end(); ++it)
+            this->set(*it, value);
+        return;
+    }
+    
     if (ConfigOptionFloat* optv = dynamic_cast<ConfigOptionFloat*>(opt)) {
         optv->value = SvNV(value);
     } else if (ConfigOptionFloats* optv = dynamic_cast<ConfigOptionFloats*>(opt)) {
diff --git a/xs/src/PrintConfig.hpp b/xs/src/PrintConfig.hpp
index a6bd71ec7..17af9b4ee 100644
--- a/xs/src/PrintConfig.hpp
+++ b/xs/src/PrintConfig.hpp
@@ -576,6 +576,14 @@ class PrintConfig : public StaticConfig
         Options["extra_perimeters"].cli = "extra-perimeters!";
         Options["extra_perimeters"].scope = "object";
 
+        Options["extruder"].type = coInt;
+        Options["extruder"].label = "Extruder";
+        Options["extruder"].cli = "extruder=i";
+        Options["extruder"].shortcut.push_back("perimeter_extruder");
+        Options["extruder"].shortcut.push_back("infill_extruder");
+        Options["extruder"].shortcut.push_back("support_material_extruder");
+        Options["extruder"].shortcut.push_back("support_material_interface_extruder");
+
         Options["extruder_clearance_height"].type = coFloat;
         Options["extruder_clearance_height"].label = "Height";
         Options["extruder_clearance_height"].tooltip = "Set this to the vertical distance between your nozzle tip and (usually) the X carriage rods. In other words, this is the height of the clearance cylinder around your extruder, and it represents the maximum depth the extruder can peek before colliding with other printed objects.";
@@ -761,7 +769,6 @@ class PrintConfig : public StaticConfig
         Options["infill_extruder"].label = "Infill extruder";
         Options["infill_extruder"].tooltip = "The extruder to use when printing infill.";
         Options["infill_extruder"].cli = "infill-extruder=i";
-        Options["infill_extruder"].shortcut.push_back("extruder");
 
         Options["infill_extrusion_width"].type = coFloatOrPercent;
         Options["infill_extrusion_width"].label = "Infill";
@@ -879,7 +886,6 @@ class PrintConfig : public StaticConfig
         Options["perimeter_extruder"].tooltip = "The extruder to use when printing perimeters.";
         Options["perimeter_extruder"].cli = "perimeter-extruder=i";
         Options["perimeter_extruder"].aliases.push_back("perimeters_extruder");
-        Options["perimeter_extruder"].shortcut.push_back("extruder");
 
         Options["perimeter_extrusion_width"].type = coFloatOrPercent;
         Options["perimeter_extrusion_width"].label = "Perimeters";
@@ -1141,7 +1147,6 @@ class PrintConfig : public StaticConfig
         Options["support_material_extruder"].label = "Support material extruder";
         Options["support_material_extruder"].tooltip = "The extruder to use when printing support material. This affects brim and raft too.";
         Options["support_material_extruder"].cli = "support-material-extruder=i";
-        Options["support_material_extruder"].shortcut.push_back("extruder");
 
         Options["support_material_extrusion_width"].type = coFloatOrPercent;
         Options["support_material_extrusion_width"].label = "Support material";
@@ -1153,7 +1158,6 @@ class PrintConfig : public StaticConfig
         Options["support_material_interface_extruder"].label = "Support material interface extruder";
         Options["support_material_interface_extruder"].tooltip = "The extruder to use when printing support material interface. This affects raft too.";
         Options["support_material_interface_extruder"].cli = "support-material-interface-extruder=i";
-        Options["support_material_interface_extruder"].shortcut.push_back("extruder");
 
         Options["support_material_interface_layers"].type = coInt;
         Options["support_material_interface_layers"].label = "Interface layers";
diff --git a/xs/t/15_config.t b/xs/t/15_config.t
index 9a9943d3a..699f115f8 100644
--- a/xs/t/15_config.t
+++ b/xs/t/15_config.t
@@ -4,7 +4,7 @@ use strict;
 use warnings;
 
 use Slic3r::XS;
-use Test::More tests => 78;
+use Test::More tests => 79;
 
 foreach my $config (Slic3r::Config->new, Slic3r::Config::Print->new) {
     $config->set('layer_height', 0.3);
@@ -88,6 +88,8 @@ foreach my $config (Slic3r::Config->new, Slic3r::Config::Print->new) {
 {
     my $config = Slic3r::Config->new;
     $config->set('perimeters', 2);
+    $config->set('solid_layers', 2);
+    is $config->get('top_solid_layers'), 2, 'shortcut';
     
     # test that no crash happens when using set_deserialize() with a key that hasn't been set() yet
     $config->set_deserialize('filament_diameter', '3');

From 51b976721d7c321afaa8f021516c4a3bf847ea4c Mon Sep 17 00:00:00 2001
From: Alessandro Ranellucci <aar@cpan.org>
Date: Tue, 31 Dec 2013 15:52:37 +0100
Subject: [PATCH 03/19] Split PrintConfig into PrintObjectConfig and
 PrintRegionConfig

---
 xs/src/PrintConfig.cpp |   2 +-
 xs/src/PrintConfig.hpp | 859 +++++++++++++++++++++--------------------
 2 files changed, 449 insertions(+), 412 deletions(-)

diff --git a/xs/src/PrintConfig.cpp b/xs/src/PrintConfig.cpp
index 76cc9ef49..711740897 100644
--- a/xs/src/PrintConfig.cpp
+++ b/xs/src/PrintConfig.cpp
@@ -2,6 +2,6 @@
 
 namespace Slic3r {
 
-t_optiondef_map PrintConfig::PrintConfigDef = PrintConfig::build_def();
+t_optiondef_map PrintConfigDef::def = PrintConfigDef::build_def();
 
 }
diff --git a/xs/src/PrintConfig.hpp b/xs/src/PrintConfig.hpp
index 17af9b4ee..38eb09ba1 100644
--- a/xs/src/PrintConfig.hpp
+++ b/xs/src/PrintConfig.hpp
@@ -37,417 +37,10 @@ template<> inline t_config_enum_values ConfigOptionEnum<InfillPattern>::get_enum
     return keys_map;
 }
 
-class PrintConfig : public StaticConfig
+class PrintConfigDef
 {
     public:
-    static t_optiondef_map PrintConfigDef;
-    
-    ConfigOptionBool                avoid_crossing_perimeters;
-    ConfigOptionPoint               bed_size;
-    ConfigOptionInt                 bed_temperature;
-    ConfigOptionInt                 bottom_solid_layers;
-    ConfigOptionFloat               bridge_acceleration;
-    ConfigOptionInt                 bridge_fan_speed;
-    ConfigOptionFloat               bridge_flow_ratio;
-    ConfigOptionFloat               bridge_speed;
-    ConfigOptionFloat               brim_width;
-    ConfigOptionBool                complete_objects;
-    ConfigOptionBool                cooling;
-    ConfigOptionFloat               default_acceleration;
-    ConfigOptionInt                 disable_fan_first_layers;
-    ConfigOptionInt                 duplicate;
-    ConfigOptionFloat               duplicate_distance;
-    ConfigOptionPoint               duplicate_grid;
-    ConfigOptionString              end_gcode;
-    ConfigOptionFloatOrPercent      external_perimeter_speed;
-    ConfigOptionBool                external_perimeters_first;
-    ConfigOptionBool                extra_perimeters;
-    ConfigOptionFloat               extruder_clearance_height;
-    ConfigOptionFloat               extruder_clearance_radius;
-    ConfigOptionPoints              extruder_offset;
-    ConfigOptionString              extrusion_axis;
-    ConfigOptionFloats              extrusion_multiplier;
-    ConfigOptionFloatOrPercent      extrusion_width;
-    ConfigOptionBool                fan_always_on;
-    ConfigOptionInt                 fan_below_layer_time;
-    ConfigOptionFloats              filament_diameter;
-    ConfigOptionInt                 fill_angle;
-    ConfigOptionFloat               fill_density;
-    ConfigOptionEnum<InfillPattern> fill_pattern;
-    ConfigOptionFloat               first_layer_acceleration;
-    ConfigOptionInt                 first_layer_bed_temperature;
-    ConfigOptionFloatOrPercent      first_layer_extrusion_width;
-    ConfigOptionFloatOrPercent      first_layer_height;
-    ConfigOptionFloatOrPercent      first_layer_speed;
-    ConfigOptionInts                first_layer_temperature;
-    ConfigOptionBool                g0;
-    ConfigOptionFloat               gap_fill_speed;
-    ConfigOptionBool                gcode_arcs;
-    ConfigOptionBool                gcode_comments;
-    ConfigOptionEnum<GCodeFlavor>   gcode_flavor;
-    ConfigOptionFloat               infill_acceleration;
-    ConfigOptionInt                 infill_every_layers;
-    ConfigOptionInt                 infill_extruder;
-    ConfigOptionFloatOrPercent      infill_extrusion_width;
-    ConfigOptionBool                infill_first;
-    ConfigOptionBool                infill_only_where_needed;
-    ConfigOptionFloat               infill_speed;
-    ConfigOptionString              layer_gcode;
-    ConfigOptionFloat               layer_height;
-    ConfigOptionInt                 max_fan_speed;
-    ConfigOptionInt                 min_fan_speed;
-    ConfigOptionInt                 min_print_speed;
-    ConfigOptionFloat               min_skirt_length;
-    ConfigOptionString              notes;
-    ConfigOptionFloats              nozzle_diameter;
-    ConfigOptionBool                only_retract_when_crossing_perimeters;
-    ConfigOptionBool                ooze_prevention;
-    ConfigOptionString              output_filename_format;
-    ConfigOptionBool                overhangs;
-    ConfigOptionFloat               perimeter_acceleration;
-    ConfigOptionInt                 perimeter_extruder;
-    ConfigOptionFloatOrPercent      perimeter_extrusion_width;
-    ConfigOptionFloat               perimeter_speed;
-    ConfigOptionInt                 perimeters;
-    ConfigOptionStrings             post_process;
-    ConfigOptionPoint               print_center;
-    ConfigOptionInt                 raft_layers;
-    ConfigOptionBool                randomize_start;
-    ConfigOptionFloat               resolution;
-    ConfigOptionFloats              retract_before_travel;
-    ConfigOptionBools               retract_layer_change;
-    ConfigOptionFloats              retract_length;
-    ConfigOptionFloats              retract_length_toolchange;
-    ConfigOptionFloats              retract_lift;
-    ConfigOptionFloats              retract_restart_extra;
-    ConfigOptionFloats              retract_restart_extra_toolchange;
-    ConfigOptionInts                retract_speed;
-    ConfigOptionInt                 rotate;
-    ConfigOptionFloat               scale;
-    ConfigOptionFloat               skirt_distance;
-    ConfigOptionInt                 skirt_height;
-    ConfigOptionInt                 skirts;
-    ConfigOptionInt                 slowdown_below_layer_time;
-    ConfigOptionFloatOrPercent      small_perimeter_speed;
-    ConfigOptionEnum<InfillPattern> solid_fill_pattern;
-    ConfigOptionFloat               solid_infill_below_area;
-    ConfigOptionInt                 solid_infill_every_layers;
-    ConfigOptionFloatOrPercent      solid_infill_extrusion_width;
-    ConfigOptionFloatOrPercent      solid_infill_speed;
-    ConfigOptionInt                 solid_layers;
-    ConfigOptionBool                spiral_vase;
-    ConfigOptionInt                 standby_temperature_delta;
-    ConfigOptionString              start_gcode;
-    ConfigOptionBool                start_perimeters_at_concave_points;
-    ConfigOptionBool                start_perimeters_at_non_overhang;
-    ConfigOptionBool                support_material;
-    ConfigOptionInt                 support_material_angle;
-    ConfigOptionInt                 support_material_enforce_layers;
-    ConfigOptionInt                 support_material_extruder;
-    ConfigOptionFloatOrPercent      support_material_extrusion_width;
-    ConfigOptionInt                 support_material_interface_extruder;
-    ConfigOptionInt                 support_material_interface_layers;
-    ConfigOptionFloat               support_material_interface_spacing;
-    ConfigOptionEnum<InfillPattern> support_material_pattern;
-    ConfigOptionFloat               support_material_spacing;
-    ConfigOptionFloat               support_material_speed;
-    ConfigOptionInt                 support_material_threshold;
-    ConfigOptionInts                temperature;
-    ConfigOptionBool                thin_walls;
-    ConfigOptionInt                 threads;
-    ConfigOptionString              toolchange_gcode;
-    ConfigOptionFloatOrPercent      top_infill_extrusion_width;
-    ConfigOptionFloatOrPercent      top_solid_infill_speed;
-    ConfigOptionInt                 top_solid_layers;
-    ConfigOptionFloat               travel_speed;
-    ConfigOptionBool                use_firmware_retraction;
-    ConfigOptionBool                use_relative_e_distances;
-    ConfigOptionFloat               vibration_limit;
-    ConfigOptionBools               wipe;
-    ConfigOptionFloat               z_offset;
-    
-    PrintConfig() {
-        this->def = &PrintConfig::PrintConfigDef;
-        
-        this->avoid_crossing_perimeters.value                    = false;
-        this->bed_size.point                                     = Pointf(200,200);
-        this->bed_temperature.value                              = 0;
-        this->bottom_solid_layers.value                          = 3;
-        this->bridge_acceleration.value                          = 0;
-        this->bridge_fan_speed.value                             = 100;
-        this->bridge_flow_ratio.value                            = 1;
-        this->bridge_speed.value                                 = 60;
-        this->brim_width.value                                   = 0;
-        this->complete_objects.value                             = false;
-        this->cooling.value                                      = true;
-        this->default_acceleration.value                         = 0;
-        this->disable_fan_first_layers.value                     = 1;
-        this->duplicate.value                                    = 1;
-        this->duplicate_distance.value                           = 6;
-        this->duplicate_grid.point                               = Pointf(1,1);
-        this->end_gcode.value                                    = "M104 S0 ; turn off temperature\nG28 X0  ; home X axis\nM84     ; disable motors\n";
-        this->external_perimeter_speed.value                     = 70;
-        this->external_perimeter_speed.percent                   = true;
-        this->external_perimeters_first.value                    = false;
-        this->extra_perimeters.value                             = true;
-        this->extruder_clearance_height.value                    = 20;
-        this->extruder_clearance_radius.value                    = 20;
-        this->extruder_offset.points.resize(1);
-        this->extruder_offset.points[0]                          = Pointf(0,0);
-        this->extrusion_axis.value                               = "E";
-        this->extrusion_multiplier.values.resize(1);
-        this->extrusion_multiplier.values[0]                     = 1;
-        this->extrusion_width.value                              = 0;
-        this->extrusion_width.percent                            = false;
-        this->fan_always_on.value                                = false;
-        this->fan_below_layer_time.value                         = 60;
-        this->filament_diameter.values.resize(1);
-        this->filament_diameter.values[0]                        = 3;
-        this->fill_angle.value                                   = 45;
-        this->fill_density.value                                 = 0.4;
-        this->fill_pattern.value                                 = ipHoneycomb;
-        this->first_layer_acceleration.value                     = 0;
-        this->first_layer_bed_temperature.value                  = 0;
-        this->first_layer_extrusion_width.value                  = 200;
-        this->first_layer_extrusion_width.percent                = true;
-        this->first_layer_height.value                           = 0.35;
-        this->first_layer_height.percent                         = false;
-        this->first_layer_speed.value                            = 30;
-        this->first_layer_speed.percent                          = true;
-        this->first_layer_temperature.values.resize(1);
-        this->first_layer_temperature.values[0]                  = 200;
-        this->g0.value                                           = false;
-        this->gap_fill_speed.value                               = 20;
-        this->gcode_arcs.value                                   = false;
-        this->gcode_comments.value                               = false;
-        this->gcode_flavor.value                                 = gcfRepRap;
-        this->infill_acceleration.value                          = 0;
-        this->infill_every_layers.value                          = 1;
-        this->infill_extruder.value                              = 1;
-        this->infill_extrusion_width.value                       = 0;
-        this->infill_extrusion_width.percent                     = false;
-        this->infill_first.value                                 = false;
-        this->infill_only_where_needed.value                     = false;
-        this->infill_speed.value                                 = 60;
-        this->layer_gcode.value                                  = "";
-        this->layer_height.value                                 = 0.4;
-        this->max_fan_speed.value                                = 100;
-        this->min_fan_speed.value                                = 35;
-        this->min_print_speed.value                              = 10;
-        this->min_skirt_length.value                             = 0;
-        this->notes.value                                        = "";
-        this->nozzle_diameter.values.resize(1);
-        this->nozzle_diameter.values[0]                          = 0.5;
-        this->only_retract_when_crossing_perimeters.value        = true;
-        this->ooze_prevention.value                              = false;
-        this->output_filename_format.value                       = "[input_filename_base].gcode";
-        this->overhangs.value                                    = true;
-        this->perimeter_acceleration.value                       = 0;
-        this->perimeter_extruder.value                           = 1;
-        this->perimeter_extrusion_width.value                    = 0;
-        this->perimeter_extrusion_width.percent                  = false;
-        this->perimeter_speed.value                              = 30;
-        this->perimeters.value                                   = 3;
-        this->print_center.point                                 = Pointf(100,100);
-        this->raft_layers.value                                  = 0;
-        this->randomize_start.value                              = false;
-        this->resolution.value                                   = 0;
-        this->retract_before_travel.values.resize(1);
-        this->retract_before_travel.values[0]                    = 2;
-        this->retract_layer_change.values.resize(1);
-        this->retract_layer_change.values[0]                     = true;
-        this->retract_length.values.resize(1);
-        this->retract_length.values[0]                           = 1;
-        this->retract_length_toolchange.values.resize(1);
-        this->retract_length_toolchange.values[0]                = 10;
-        this->retract_lift.values.resize(1);
-        this->retract_lift.values[0]                             = 0;
-        this->retract_restart_extra.values.resize(1);
-        this->retract_restart_extra.values[0]                    = 0;
-        this->retract_restart_extra_toolchange.values.resize(1);
-        this->retract_restart_extra_toolchange.values[0]         = 0;
-        this->retract_speed.values.resize(1);
-        this->retract_speed.values[0]                            = 30;
-        this->rotate.value                                       = 0;
-        this->scale.value                                        = 1;
-        this->skirt_distance.value                               = 6;
-        this->skirt_height.value                                 = 1;
-        this->skirts.value                                       = 1;
-        this->slowdown_below_layer_time.value                    = 30;
-        this->small_perimeter_speed.value                        = 30;
-        this->small_perimeter_speed.percent                      = false;
-        this->solid_fill_pattern.value                           = ipRectilinear;
-        this->solid_infill_below_area.value                      = 70;
-        this->solid_infill_every_layers.value                    = 0;
-        this->solid_infill_extrusion_width.value                 = 0;
-        this->solid_infill_extrusion_width.percent               = false;
-        this->solid_infill_speed.value                           = 60;
-        this->solid_infill_speed.percent                         = false;
-        this->spiral_vase.value                                  = false;
-        this->standby_temperature_delta.value                    = -5;
-        this->start_gcode.value                                  = "G28 ; home all axes\nG1 Z5 F5000 ; lift nozzle\n";
-        this->start_perimeters_at_concave_points.value           = false;
-        this->start_perimeters_at_non_overhang.value             = false;
-        this->support_material.value                             = false;
-        this->support_material_angle.value                       = 0;
-        this->support_material_enforce_layers.value              = 0;
-        this->support_material_extruder.value                    = 1;
-        this->support_material_extrusion_width.value             = 0;
-        this->support_material_extrusion_width.percent           = false;
-        this->support_material_interface_extruder.value          = 1;
-        this->support_material_interface_layers.value            = 3;
-        this->support_material_interface_spacing.value           = 0;
-        this->support_material_pattern.value                     = ipHoneycomb;
-        this->support_material_spacing.value                     = 2.5;
-        this->support_material_speed.value                       = 60;
-        this->support_material_threshold.value                   = 0;
-        this->temperature.values.resize(1);
-        this->temperature.values[0]                              = 200;
-        this->thin_walls.value                                   = true;
-        this->threads.value                                      = 2;
-        this->toolchange_gcode.value                             = "";
-        this->top_infill_extrusion_width.value                   = 0;
-        this->top_infill_extrusion_width.percent                 = false;
-        this->top_solid_infill_speed.value                       = 50;
-        this->top_solid_infill_speed.percent                     = false;
-        this->top_solid_layers.value                             = 3;
-        this->travel_speed.value                                 = 130;
-        this->use_firmware_retraction.value                      = false;
-        this->use_relative_e_distances.value                     = false;
-        this->vibration_limit.value                              = 0;
-        this->wipe.values.resize(1);
-        this->wipe.values[0]                                     = false;
-        this->z_offset.value                                     = 0;
-    };
-    
-    ConfigOption* option(const t_config_option_key opt_key, bool create = false) {
-        if (opt_key == "avoid_crossing_perimeters")                  return &this->avoid_crossing_perimeters;
-        if (opt_key == "bed_size")                                   return &this->bed_size;
-        if (opt_key == "bed_temperature")                            return &this->bed_temperature;
-        if (opt_key == "bottom_solid_layers")                        return &this->bottom_solid_layers;
-        if (opt_key == "bridge_acceleration")                        return &this->bridge_acceleration;
-        if (opt_key == "bridge_fan_speed")                           return &this->bridge_fan_speed;
-        if (opt_key == "bridge_flow_ratio")                          return &this->bridge_flow_ratio;
-        if (opt_key == "bridge_speed")                               return &this->bridge_speed;
-        if (opt_key == "brim_width")                                 return &this->brim_width;
-        if (opt_key == "complete_objects")                           return &this->complete_objects;
-        if (opt_key == "cooling")                                    return &this->cooling;
-        if (opt_key == "default_acceleration")                       return &this->default_acceleration;
-        if (opt_key == "disable_fan_first_layers")                   return &this->disable_fan_first_layers;
-        if (opt_key == "duplicate")                                  return &this->duplicate;
-        if (opt_key == "duplicate_distance")                         return &this->duplicate_distance;
-        if (opt_key == "duplicate_grid")                             return &this->duplicate_grid;
-        if (opt_key == "end_gcode")                                  return &this->end_gcode;
-        if (opt_key == "external_perimeter_speed")                   return &this->external_perimeter_speed;
-        if (opt_key == "external_perimeters_first")                  return &this->external_perimeters_first;
-        if (opt_key == "extra_perimeters")                           return &this->extra_perimeters;
-        if (opt_key == "extruder_clearance_height")                  return &this->extruder_clearance_height;
-        if (opt_key == "extruder_clearance_radius")                  return &this->extruder_clearance_radius;
-        if (opt_key == "extruder_offset")                            return &this->extruder_offset;
-        if (opt_key == "extrusion_axis")                             return &this->extrusion_axis;
-        if (opt_key == "extrusion_multiplier")                       return &this->extrusion_multiplier;
-        if (opt_key == "extrusion_width")                            return &this->extrusion_width;
-        if (opt_key == "fan_always_on")                              return &this->fan_always_on;
-        if (opt_key == "fan_below_layer_time")                       return &this->fan_below_layer_time;
-        if (opt_key == "filament_diameter")                          return &this->filament_diameter;
-        if (opt_key == "fill_angle")                                 return &this->fill_angle;
-        if (opt_key == "fill_density")                               return &this->fill_density;
-        if (opt_key == "fill_pattern")                               return &this->fill_pattern;
-        if (opt_key == "first_layer_acceleration")                   return &this->first_layer_acceleration;
-        if (opt_key == "first_layer_bed_temperature")                return &this->first_layer_bed_temperature;
-        if (opt_key == "first_layer_extrusion_width")                return &this->first_layer_extrusion_width;
-        if (opt_key == "first_layer_height")                         return &this->first_layer_height;
-        if (opt_key == "first_layer_speed")                          return &this->first_layer_speed;
-        if (opt_key == "first_layer_temperature")                    return &this->first_layer_temperature;
-        if (opt_key == "g0")                                         return &this->g0;
-        if (opt_key == "gap_fill_speed")                             return &this->gap_fill_speed;
-        if (opt_key == "gcode_arcs")                                 return &this->gcode_arcs;
-        if (opt_key == "gcode_comments")                             return &this->gcode_comments;
-        if (opt_key == "gcode_flavor")                               return &this->gcode_flavor;
-        if (opt_key == "infill_acceleration")                        return &this->infill_acceleration;
-        if (opt_key == "infill_every_layers")                        return &this->infill_every_layers;
-        if (opt_key == "infill_extruder")                            return &this->infill_extruder;
-        if (opt_key == "infill_extrusion_width")                     return &this->infill_extrusion_width;
-        if (opt_key == "infill_first")                               return &this->infill_first;
-        if (opt_key == "infill_only_where_needed")                   return &this->infill_only_where_needed;
-        if (opt_key == "infill_speed")                               return &this->infill_speed;
-        if (opt_key == "layer_gcode")                                return &this->layer_gcode;
-        if (opt_key == "layer_height")                               return &this->layer_height;
-        if (opt_key == "max_fan_speed")                              return &this->max_fan_speed;
-        if (opt_key == "min_fan_speed")                              return &this->min_fan_speed;
-        if (opt_key == "min_print_speed")                            return &this->min_print_speed;
-        if (opt_key == "min_skirt_length")                           return &this->min_skirt_length;
-        if (opt_key == "notes")                                      return &this->notes;
-        if (opt_key == "nozzle_diameter")                            return &this->nozzle_diameter;
-        if (opt_key == "only_retract_when_crossing_perimeters")      return &this->only_retract_when_crossing_perimeters;
-        if (opt_key == "ooze_prevention")                            return &this->ooze_prevention;
-        if (opt_key == "output_filename_format")                     return &this->output_filename_format;
-        if (opt_key == "overhangs")                                  return &this->overhangs;
-        if (opt_key == "perimeter_acceleration")                     return &this->perimeter_acceleration;
-        if (opt_key == "perimeter_extruder")                         return &this->perimeter_extruder;
-        if (opt_key == "perimeter_extrusion_width")                  return &this->perimeter_extrusion_width;
-        if (opt_key == "perimeter_speed")                            return &this->perimeter_speed;
-        if (opt_key == "perimeters")                                 return &this->perimeters;
-        if (opt_key == "post_process")                               return &this->post_process;
-        if (opt_key == "print_center")                               return &this->print_center;
-        if (opt_key == "raft_layers")                                return &this->raft_layers;
-        if (opt_key == "randomize_start")                            return &this->randomize_start;
-        if (opt_key == "resolution")                                 return &this->resolution;
-        if (opt_key == "retract_before_travel")                      return &this->retract_before_travel;
-        if (opt_key == "retract_layer_change")                       return &this->retract_layer_change;
-        if (opt_key == "retract_length")                             return &this->retract_length;
-        if (opt_key == "retract_length_toolchange")                  return &this->retract_length_toolchange;
-        if (opt_key == "retract_lift")                               return &this->retract_lift;
-        if (opt_key == "retract_restart_extra")                      return &this->retract_restart_extra;
-        if (opt_key == "retract_restart_extra_toolchange")           return &this->retract_restart_extra_toolchange;
-        if (opt_key == "retract_speed")                              return &this->retract_speed;
-        if (opt_key == "rotate")                                     return &this->rotate;
-        if (opt_key == "scale")                                      return &this->scale;
-        if (opt_key == "skirt_distance")                             return &this->skirt_distance;
-        if (opt_key == "skirt_height")                               return &this->skirt_height;
-        if (opt_key == "skirts")                                     return &this->skirts;
-        if (opt_key == "slowdown_below_layer_time")                  return &this->slowdown_below_layer_time;
-        if (opt_key == "small_perimeter_speed")                      return &this->small_perimeter_speed;
-        if (opt_key == "solid_fill_pattern")                         return &this->solid_fill_pattern;
-        if (opt_key == "solid_infill_below_area")                    return &this->solid_infill_below_area;
-        if (opt_key == "solid_infill_every_layers")                  return &this->solid_infill_every_layers;
-        if (opt_key == "solid_infill_extrusion_width")               return &this->solid_infill_extrusion_width;
-        if (opt_key == "solid_infill_speed")                         return &this->solid_infill_speed;
-        if (opt_key == "solid_layers")                               return &this->solid_layers;
-        if (opt_key == "spiral_vase")                                return &this->spiral_vase;
-        if (opt_key == "standby_temperature_delta")                  return &this->standby_temperature_delta;
-        if (opt_key == "start_gcode")                                return &this->start_gcode;
-        if (opt_key == "start_perimeters_at_concave_points")         return &this->start_perimeters_at_concave_points;
-        if (opt_key == "start_perimeters_at_non_overhang")           return &this->start_perimeters_at_non_overhang;
-        if (opt_key == "support_material")                           return &this->support_material;
-        if (opt_key == "support_material_angle")                     return &this->support_material_angle;
-        if (opt_key == "support_material_enforce_layers")            return &this->support_material_enforce_layers;
-        if (opt_key == "support_material_extruder")                  return &this->support_material_extruder;
-        if (opt_key == "support_material_extrusion_width")           return &this->support_material_extrusion_width;
-        if (opt_key == "support_material_interface_extruder")        return &this->support_material_interface_extruder;
-        if (opt_key == "support_material_interface_layers")          return &this->support_material_interface_layers;
-        if (opt_key == "support_material_interface_spacing")         return &this->support_material_interface_spacing;
-        if (opt_key == "support_material_pattern")                   return &this->support_material_pattern;
-        if (opt_key == "support_material_spacing")                   return &this->support_material_spacing;
-        if (opt_key == "support_material_speed")                     return &this->support_material_speed;
-        if (opt_key == "support_material_threshold")                 return &this->support_material_threshold;
-        if (opt_key == "temperature")                                return &this->temperature;
-        if (opt_key == "thin_walls")                                 return &this->thin_walls;
-        if (opt_key == "threads")                                    return &this->threads;
-        if (opt_key == "toolchange_gcode")                           return &this->toolchange_gcode;
-        if (opt_key == "top_infill_extrusion_width")                 return &this->top_infill_extrusion_width;
-        if (opt_key == "top_solid_infill_speed")                     return &this->top_solid_infill_speed;
-        if (opt_key == "top_solid_layers")                           return &this->top_solid_layers;
-        if (opt_key == "travel_speed")                               return &this->travel_speed;
-        if (opt_key == "use_firmware_retraction")                    return &this->use_firmware_retraction;
-        if (opt_key == "use_relative_e_distances")                   return &this->use_relative_e_distances;
-        if (opt_key == "vibration_limit")                            return &this->vibration_limit;
-        if (opt_key == "wipe")                                       return &this->wipe;
-        if (opt_key == "z_offset")                                   return &this->z_offset;
-        
-        if (create) throw "Attempt to create non-existing option in StaticConfig object";
-        return NULL;
-    };
+    static t_optiondef_map def;
     
     static t_optiondef_map build_def () {
         t_optiondef_map Options;
@@ -1301,15 +894,459 @@ class PrintConfig : public StaticConfig
     };
 };
 
+class PrintObjectConfig : public virtual StaticConfig
+{
+    public:
+    ConfigOptionFloatOrPercent      extrusion_width;
+    ConfigOptionFloatOrPercent      first_layer_height;
+    ConfigOptionFloat               layer_height;
+    ConfigOptionInt                 raft_layers;
+    ConfigOptionBool                support_material;
+    ConfigOptionInt                 support_material_angle;
+    ConfigOptionInt                 support_material_enforce_layers;
+    ConfigOptionInt                 support_material_extruder;
+    ConfigOptionFloatOrPercent      support_material_extrusion_width;
+    ConfigOptionInt                 support_material_interface_extruder;
+    ConfigOptionInt                 support_material_interface_layers;
+    ConfigOptionFloat               support_material_interface_spacing;
+    ConfigOptionEnum<InfillPattern> support_material_pattern;
+    ConfigOptionFloat               support_material_spacing;
+    ConfigOptionFloat               support_material_speed;
+    ConfigOptionInt                 support_material_threshold;
+    
+    PrintObjectConfig() {
+        this->def = &PrintConfigDef::def;
+        
+        this->extrusion_width.value                              = 0;
+        this->extrusion_width.percent                            = false;
+        this->first_layer_height.value                           = 0.35;
+        this->first_layer_height.percent                         = false;
+        this->layer_height.value                                 = 0.4;
+        this->raft_layers.value                                  = 0;
+        this->support_material.value                             = false;
+        this->support_material_angle.value                       = 0;
+        this->support_material_enforce_layers.value              = 0;
+        this->support_material_extruder.value                    = 1;
+        this->support_material_extrusion_width.value             = 0;
+        this->support_material_extrusion_width.percent           = false;
+        this->support_material_interface_extruder.value          = 1;
+        this->support_material_interface_layers.value            = 3;
+        this->support_material_interface_spacing.value           = 0;
+        this->support_material_pattern.value                     = ipHoneycomb;
+        this->support_material_spacing.value                     = 2.5;
+        this->support_material_speed.value                       = 60;
+        this->support_material_threshold.value                   = 0;
+    };
+    
+    ConfigOption* option(const t_config_option_key opt_key, bool create = false) {
+        if (opt_key == "extrusion_width")                            return &this->extrusion_width;
+        if (opt_key == "first_layer_height")                         return &this->first_layer_height;
+        if (opt_key == "layer_height")                               return &this->layer_height;
+        if (opt_key == "raft_layers")                                return &this->raft_layers;
+        if (opt_key == "support_material")                           return &this->support_material;
+        if (opt_key == "support_material_angle")                     return &this->support_material_angle;
+        if (opt_key == "support_material_enforce_layers")            return &this->support_material_enforce_layers;
+        if (opt_key == "support_material_extruder")                  return &this->support_material_extruder;
+        if (opt_key == "support_material_extrusion_width")           return &this->support_material_extrusion_width;
+        if (opt_key == "support_material_interface_extruder")        return &this->support_material_interface_extruder;
+        if (opt_key == "support_material_interface_layers")          return &this->support_material_interface_layers;
+        if (opt_key == "support_material_interface_spacing")         return &this->support_material_interface_spacing;
+        if (opt_key == "support_material_pattern")                   return &this->support_material_pattern;
+        if (opt_key == "support_material_spacing")                   return &this->support_material_spacing;
+        if (opt_key == "support_material_speed")                     return &this->support_material_speed;
+        if (opt_key == "support_material_threshold")                 return &this->support_material_threshold;
+        
+        if (create) throw "Attempt to create non-existing option in StaticConfig object";
+        return NULL;
+    };
+};
+
+class PrintRegionConfig : public virtual StaticConfig
+{
+    public:
+    ConfigOptionInt                 bottom_solid_layers;
+    ConfigOptionBool                extra_perimeters;
+    ConfigOptionInt                 fill_angle;
+    ConfigOptionFloat               fill_density;
+    ConfigOptionEnum<InfillPattern> fill_pattern;
+    ConfigOptionFloatOrPercent      first_layer_extrusion_width;
+    ConfigOptionInt                 infill_extruder;
+    ConfigOptionFloatOrPercent      infill_extrusion_width;
+    ConfigOptionInt                 infill_every_layers;
+    ConfigOptionBool                infill_only_where_needed;
+    ConfigOptionInt                 perimeter_extruder;
+    ConfigOptionFloatOrPercent      perimeter_extrusion_width;
+    ConfigOptionInt                 perimeters;
+    ConfigOptionEnum<InfillPattern> solid_fill_pattern;
+    ConfigOptionFloat               solid_infill_below_area;
+    ConfigOptionFloatOrPercent      solid_infill_extrusion_width;
+    ConfigOptionInt                 solid_infill_every_layers;
+    ConfigOptionInt                 solid_layers;
+    ConfigOptionBool                thin_walls;
+    ConfigOptionFloatOrPercent      top_infill_extrusion_width;
+    ConfigOptionInt                 top_solid_layers;
+    
+    PrintObjectConfig() {
+        this->def = &PrintConfigDef::def;
+        
+        this->bottom_solid_layers.value                          = 3;
+        this->extra_perimeters.value                             = true;
+        this->fill_angle.value                                   = 45;
+        this->fill_density.value                                 = 0.4;
+        this->fill_pattern.value                                 = ipHoneycomb;
+        this->first_layer_extrusion_width.value                  = 200;
+        this->first_layer_extrusion_width.percent                = true;
+        this->infill_extruder.value                              = 1;
+        this->infill_extrusion_width.value                       = 0;
+        this->infill_extrusion_width.percent                     = false;
+        this->infill_every_layers.value                          = 1;
+        this->infill_only_where_needed.value                     = false;
+        this->perimeter_extruder.value                           = 1;
+        this->perimeter_extrusion_width.value                    = 0;
+        this->perimeter_extrusion_width.percent                  = false;
+        this->perimeters.value                                   = 3;
+        this->solid_fill_pattern.value                           = ipRectilinear;
+        this->solid_infill_below_area.value                      = 70;
+        this->solid_infill_extrusion_width.value                 = 0;
+        this->solid_infill_extrusion_width.percent               = false;
+        this->solid_infill_every_layers.value                    = 0;
+        this->thin_walls.value                                   = true;
+        this->top_infill_extrusion_width.value                   = 0;
+        this->top_infill_extrusion_width.percent                 = false;
+        this->top_solid_layers.value                             = 3;
+    };
+    
+    ConfigOption* option(const t_config_option_key opt_key, bool create = false) {
+        if (opt_key == "bottom_solid_layers")                        return &this->bottom_solid_layers;
+        if (opt_key == "extra_perimeters")                           return &this->extra_perimeters;
+        if (opt_key == "fill_angle")                                 return &this->fill_angle;
+        if (opt_key == "fill_density")                               return &this->fill_density;
+        if (opt_key == "fill_pattern")                               return &this->fill_pattern;
+        if (opt_key == "first_layer_extrusion_width")                return &this->first_layer_extrusion_width;
+        if (opt_key == "infill_extruder")                            return &this->infill_extruder;
+        if (opt_key == "infill_extrusion_width")                     return &this->infill_extrusion_width;
+        if (opt_key == "infill_every_layers")                        return &this->infill_every_layers;
+        if (opt_key == "infill_only_where_needed")                   return &this->infill_only_where_needed;
+        if (opt_key == "perimeter_extruder")                         return &this->perimeter_extruder;
+        if (opt_key == "perimeter_extrusion_width")                  return &this->perimeter_extrusion_width;
+        if (opt_key == "perimeters")                                 return &this->perimeters;
+        if (opt_key == "solid_fill_pattern")                         return &this->solid_fill_pattern;
+        if (opt_key == "solid_infill_below_area")                    return &this->solid_infill_below_area;
+        if (opt_key == "solid_infill_extrusion_width")               return &this->solid_infill_extrusion_width;
+        if (opt_key == "solid_infill_every_layers")                  return &this->solid_infill_every_layers;
+        if (opt_key == "solid_layers")                               return &this->solid_layers;
+        if (opt_key == "thin_walls")                                 return &this->thin_walls;
+        if (opt_key == "top_infill_extrusion_width")                 return &this->top_infill_extrusion_width;
+        if (opt_key == "top_solid_layers")                           return &this->top_solid_layers;
+        
+        if (create) throw "Attempt to create non-existing option in StaticConfig object";
+        return NULL;
+    };
+};
+
+class PrintConfig : public virtual StaticConfig
+{
+    public:
+    ConfigOptionBool                avoid_crossing_perimeters;
+    ConfigOptionPoint               bed_size;
+    ConfigOptionInt                 bed_temperature;
+    ConfigOptionFloat               bridge_acceleration;
+    ConfigOptionInt                 bridge_fan_speed;
+    ConfigOptionFloat               bridge_flow_ratio;
+    ConfigOptionFloat               bridge_speed;
+    ConfigOptionFloat               brim_width;
+    ConfigOptionBool                complete_objects;
+    ConfigOptionBool                cooling;
+    ConfigOptionFloat               default_acceleration;
+    ConfigOptionInt                 disable_fan_first_layers;
+    ConfigOptionInt                 duplicate;
+    ConfigOptionFloat               duplicate_distance;
+    ConfigOptionPoint               duplicate_grid;
+    ConfigOptionString              end_gcode;
+    ConfigOptionFloatOrPercent      external_perimeter_speed;
+    ConfigOptionBool                external_perimeters_first;
+    ConfigOptionFloat               extruder_clearance_height;
+    ConfigOptionFloat               extruder_clearance_radius;
+    ConfigOptionPoints              extruder_offset;
+    ConfigOptionString              extrusion_axis;
+    ConfigOptionFloats              extrusion_multiplier;
+    ConfigOptionBool                fan_always_on;
+    ConfigOptionInt                 fan_below_layer_time;
+    ConfigOptionFloats              filament_diameter;
+    ConfigOptionFloat               first_layer_acceleration;
+    ConfigOptionInt                 first_layer_bed_temperature;
+    ConfigOptionFloatOrPercent      first_layer_speed;
+    ConfigOptionInts                first_layer_temperature;
+    ConfigOptionBool                g0;
+    ConfigOptionFloat               gap_fill_speed;
+    ConfigOptionBool                gcode_arcs;
+    ConfigOptionBool                gcode_comments;
+    ConfigOptionEnum<GCodeFlavor>   gcode_flavor;
+    ConfigOptionFloat               infill_acceleration;
+    ConfigOptionBool                infill_first;
+    ConfigOptionFloat               infill_speed;
+    ConfigOptionString              layer_gcode;
+    ConfigOptionInt                 max_fan_speed;
+    ConfigOptionInt                 min_fan_speed;
+    ConfigOptionInt                 min_print_speed;
+    ConfigOptionFloat               min_skirt_length;
+    ConfigOptionString              notes;
+    ConfigOptionFloats              nozzle_diameter;
+    ConfigOptionBool                only_retract_when_crossing_perimeters;
+    ConfigOptionBool                ooze_prevention;
+    ConfigOptionString              output_filename_format;
+    ConfigOptionBool                overhangs;
+    ConfigOptionFloat               perimeter_acceleration;
+    ConfigOptionFloat               perimeter_speed;
+    ConfigOptionStrings             post_process;
+    ConfigOptionPoint               print_center;
+    ConfigOptionBool                randomize_start;
+    ConfigOptionFloat               resolution;
+    ConfigOptionFloats              retract_before_travel;
+    ConfigOptionBools               retract_layer_change;
+    ConfigOptionFloats              retract_length;
+    ConfigOptionFloats              retract_length_toolchange;
+    ConfigOptionFloats              retract_lift;
+    ConfigOptionFloats              retract_restart_extra;
+    ConfigOptionFloats              retract_restart_extra_toolchange;
+    ConfigOptionInts                retract_speed;
+    ConfigOptionInt                 rotate;
+    ConfigOptionFloat               scale;
+    ConfigOptionFloat               skirt_distance;
+    ConfigOptionInt                 skirt_height;
+    ConfigOptionInt                 skirts;
+    ConfigOptionInt                 slowdown_below_layer_time;
+    ConfigOptionFloatOrPercent      small_perimeter_speed;
+    ConfigOptionFloatOrPercent      solid_infill_speed;
+    ConfigOptionBool                spiral_vase;
+    ConfigOptionInt                 standby_temperature_delta;
+    ConfigOptionString              start_gcode;
+    ConfigOptionBool                start_perimeters_at_concave_points;
+    ConfigOptionBool                start_perimeters_at_non_overhang;
+    ConfigOptionInts                temperature;
+    ConfigOptionInt                 threads;
+    ConfigOptionString              toolchange_gcode;
+    ConfigOptionFloatOrPercent      top_solid_infill_speed;
+    ConfigOptionFloat               travel_speed;
+    ConfigOptionBool                use_firmware_retraction;
+    ConfigOptionBool                use_relative_e_distances;
+    ConfigOptionFloat               vibration_limit;
+    ConfigOptionBools               wipe;
+    ConfigOptionFloat               z_offset;
+    
+    PrintConfig() {
+        this->def = &PrintConfig::PrintConfigDef;
+        
+        this->avoid_crossing_perimeters.value                    = false;
+        this->bed_size.point                                     = Pointf(200,200);
+        this->bed_temperature.value                              = 0;
+        this->bridge_acceleration.value                          = 0;
+        this->bridge_fan_speed.value                             = 100;
+        this->bridge_flow_ratio.value                            = 1;
+        this->bridge_speed.value                                 = 60;
+        this->brim_width.value                                   = 0;
+        this->complete_objects.value                             = false;
+        this->cooling.value                                      = true;
+        this->default_acceleration.value                         = 0;
+        this->disable_fan_first_layers.value                     = 1;
+        this->duplicate.value                                    = 1;
+        this->duplicate_distance.value                           = 6;
+        this->duplicate_grid.point                               = Pointf(1,1);
+        this->end_gcode.value                                    = "M104 S0 ; turn off temperature\nG28 X0  ; home X axis\nM84     ; disable motors\n";
+        this->external_perimeter_speed.value                     = 70;
+        this->external_perimeter_speed.percent                   = true;
+        this->external_perimeters_first.value                    = false;
+        this->extruder_clearance_height.value                    = 20;
+        this->extruder_clearance_radius.value                    = 20;
+        this->extruder_offset.points.resize(1);
+        this->extruder_offset.points[0]                          = Pointf(0,0);
+        this->extrusion_axis.value                               = "E";
+        this->extrusion_multiplier.values.resize(1);
+        this->extrusion_multiplier.values[0]                     = 1;
+        this->fan_always_on.value                                = false;
+        this->fan_below_layer_time.value                         = 60;
+        this->filament_diameter.values.resize(1);
+        this->filament_diameter.values[0]                        = 3;
+        this->first_layer_acceleration.value                     = 0;
+        this->first_layer_bed_temperature.value                  = 0;
+        this->first_layer_speed.value                            = 30;
+        this->first_layer_speed.percent                          = true;
+        this->first_layer_temperature.values.resize(1);
+        this->first_layer_temperature.values[0]                  = 200;
+        this->g0.value                                           = false;
+        this->gap_fill_speed.value                               = 20;
+        this->gcode_arcs.value                                   = false;
+        this->gcode_comments.value                               = false;
+        this->gcode_flavor.value                                 = gcfRepRap;
+        this->infill_acceleration.value                          = 0;
+        this->infill_first.value                                 = false;
+        this->infill_speed.value                                 = 60;
+        this->layer_gcode.value                                  = "";
+        this->max_fan_speed.value                                = 100;
+        this->min_fan_speed.value                                = 35;
+        this->min_print_speed.value                              = 10;
+        this->min_skirt_length.value                             = 0;
+        this->notes.value                                        = "";
+        this->nozzle_diameter.values.resize(1);
+        this->nozzle_diameter.values[0]                          = 0.5;
+        this->only_retract_when_crossing_perimeters.value        = true;
+        this->ooze_prevention.value                              = false;
+        this->output_filename_format.value                       = "[input_filename_base].gcode";
+        this->overhangs.value                                    = true;
+        this->perimeter_acceleration.value                       = 0;
+        this->perimeter_speed.value                              = 30;
+        this->print_center.point                                 = Pointf(100,100);
+        this->randomize_start.value                              = false;
+        this->resolution.value                                   = 0;
+        this->retract_before_travel.values.resize(1);
+        this->retract_before_travel.values[0]                    = 2;
+        this->retract_layer_change.values.resize(1);
+        this->retract_layer_change.values[0]                     = true;
+        this->retract_length.values.resize(1);
+        this->retract_length.values[0]                           = 1;
+        this->retract_length_toolchange.values.resize(1);
+        this->retract_length_toolchange.values[0]                = 10;
+        this->retract_lift.values.resize(1);
+        this->retract_lift.values[0]                             = 0;
+        this->retract_restart_extra.values.resize(1);
+        this->retract_restart_extra.values[0]                    = 0;
+        this->retract_restart_extra_toolchange.values.resize(1);
+        this->retract_restart_extra_toolchange.values[0]         = 0;
+        this->retract_speed.values.resize(1);
+        this->retract_speed.values[0]                            = 30;
+        this->rotate.value                                       = 0;
+        this->scale.value                                        = 1;
+        this->skirt_distance.value                               = 6;
+        this->skirt_height.value                                 = 1;
+        this->skirts.value                                       = 1;
+        this->slowdown_below_layer_time.value                    = 30;
+        this->small_perimeter_speed.value                        = 30;
+        this->small_perimeter_speed.percent                      = false;
+        this->solid_infill_speed.value                           = 60;
+        this->solid_infill_speed.percent                         = false;
+        this->spiral_vase.value                                  = false;
+        this->standby_temperature_delta.value                    = -5;
+        this->start_gcode.value                                  = "G28 ; home all axes\nG1 Z5 F5000 ; lift nozzle\n";
+        this->start_perimeters_at_concave_points.value           = false;
+        this->start_perimeters_at_non_overhang.value             = false;
+        this->temperature.values.resize(1);
+        this->temperature.values[0]                              = 200;
+        this->threads.value                                      = 2;
+        this->toolchange_gcode.value                             = "";
+        this->top_solid_infill_speed.value                       = 50;
+        this->top_solid_infill_speed.percent                     = false;
+        this->travel_speed.value                                 = 130;
+        this->use_firmware_retraction.value                      = false;
+        this->use_relative_e_distances.value                     = false;
+        this->vibration_limit.value                              = 0;
+        this->wipe.values.resize(1);
+        this->wipe.values[0]                                     = false;
+        this->z_offset.value                                     = 0;
+    };
+    
+    ConfigOption* option(const t_config_option_key opt_key, bool create = false) {
+        if (opt_key == "avoid_crossing_perimeters")                  return &this->avoid_crossing_perimeters;
+        if (opt_key == "bed_size")                                   return &this->bed_size;
+        if (opt_key == "bed_temperature")                            return &this->bed_temperature;
+        if (opt_key == "bridge_acceleration")                        return &this->bridge_acceleration;
+        if (opt_key == "bridge_fan_speed")                           return &this->bridge_fan_speed;
+        if (opt_key == "bridge_flow_ratio")                          return &this->bridge_flow_ratio;
+        if (opt_key == "bridge_speed")                               return &this->bridge_speed;
+        if (opt_key == "brim_width")                                 return &this->brim_width;
+        if (opt_key == "complete_objects")                           return &this->complete_objects;
+        if (opt_key == "cooling")                                    return &this->cooling;
+        if (opt_key == "default_acceleration")                       return &this->default_acceleration;
+        if (opt_key == "disable_fan_first_layers")                   return &this->disable_fan_first_layers;
+        if (opt_key == "duplicate")                                  return &this->duplicate;
+        if (opt_key == "duplicate_distance")                         return &this->duplicate_distance;
+        if (opt_key == "duplicate_grid")                             return &this->duplicate_grid;
+        if (opt_key == "end_gcode")                                  return &this->end_gcode;
+        if (opt_key == "external_perimeter_speed")                   return &this->external_perimeter_speed;
+        if (opt_key == "external_perimeters_first")                  return &this->external_perimeters_first;
+        if (opt_key == "extruder_clearance_height")                  return &this->extruder_clearance_height;
+        if (opt_key == "extruder_clearance_radius")                  return &this->extruder_clearance_radius;
+        if (opt_key == "extruder_offset")                            return &this->extruder_offset;
+        if (opt_key == "extrusion_axis")                             return &this->extrusion_axis;
+        if (opt_key == "extrusion_multiplier")                       return &this->extrusion_multiplier;
+        if (opt_key == "fan_always_on")                              return &this->fan_always_on;
+        if (opt_key == "fan_below_layer_time")                       return &this->fan_below_layer_time;
+        if (opt_key == "filament_diameter")                          return &this->filament_diameter;
+        if (opt_key == "first_layer_acceleration")                   return &this->first_layer_acceleration;
+        if (opt_key == "first_layer_bed_temperature")                return &this->first_layer_bed_temperature;
+        if (opt_key == "first_layer_speed")                          return &this->first_layer_speed;
+        if (opt_key == "first_layer_temperature")                    return &this->first_layer_temperature;
+        if (opt_key == "g0")                                         return &this->g0;
+        if (opt_key == "gap_fill_speed")                             return &this->gap_fill_speed;
+        if (opt_key == "gcode_arcs")                                 return &this->gcode_arcs;
+        if (opt_key == "gcode_comments")                             return &this->gcode_comments;
+        if (opt_key == "gcode_flavor")                               return &this->gcode_flavor;
+        if (opt_key == "infill_acceleration")                        return &this->infill_acceleration;
+        if (opt_key == "infill_first")                               return &this->infill_first;
+        if (opt_key == "infill_speed")                               return &this->infill_speed;
+        if (opt_key == "layer_gcode")                                return &this->layer_gcode;
+        if (opt_key == "max_fan_speed")                              return &this->max_fan_speed;
+        if (opt_key == "min_fan_speed")                              return &this->min_fan_speed;
+        if (opt_key == "min_print_speed")                            return &this->min_print_speed;
+        if (opt_key == "min_skirt_length")                           return &this->min_skirt_length;
+        if (opt_key == "notes")                                      return &this->notes;
+        if (opt_key == "nozzle_diameter")                            return &this->nozzle_diameter;
+        if (opt_key == "only_retract_when_crossing_perimeters")      return &this->only_retract_when_crossing_perimeters;
+        if (opt_key == "ooze_prevention")                            return &this->ooze_prevention;
+        if (opt_key == "output_filename_format")                     return &this->output_filename_format;
+        if (opt_key == "overhangs")                                  return &this->overhangs;
+        if (opt_key == "perimeter_acceleration")                     return &this->perimeter_acceleration;
+        if (opt_key == "perimeter_speed")                            return &this->perimeter_speed;
+        if (opt_key == "post_process")                               return &this->post_process;
+        if (opt_key == "print_center")                               return &this->print_center;
+        if (opt_key == "randomize_start")                            return &this->randomize_start;
+        if (opt_key == "resolution")                                 return &this->resolution;
+        if (opt_key == "retract_before_travel")                      return &this->retract_before_travel;
+        if (opt_key == "retract_layer_change")                       return &this->retract_layer_change;
+        if (opt_key == "retract_length")                             return &this->retract_length;
+        if (opt_key == "retract_length_toolchange")                  return &this->retract_length_toolchange;
+        if (opt_key == "retract_lift")                               return &this->retract_lift;
+        if (opt_key == "retract_restart_extra")                      return &this->retract_restart_extra;
+        if (opt_key == "retract_restart_extra_toolchange")           return &this->retract_restart_extra_toolchange;
+        if (opt_key == "retract_speed")                              return &this->retract_speed;
+        if (opt_key == "rotate")                                     return &this->rotate;
+        if (opt_key == "scale")                                      return &this->scale;
+        if (opt_key == "skirt_distance")                             return &this->skirt_distance;
+        if (opt_key == "skirt_height")                               return &this->skirt_height;
+        if (opt_key == "skirts")                                     return &this->skirts;
+        if (opt_key == "slowdown_below_layer_time")                  return &this->slowdown_below_layer_time;
+        if (opt_key == "small_perimeter_speed")                      return &this->small_perimeter_speed;
+        if (opt_key == "solid_infill_speed")                         return &this->solid_infill_speed;
+        if (opt_key == "spiral_vase")                                return &this->spiral_vase;
+        if (opt_key == "standby_temperature_delta")                  return &this->standby_temperature_delta;
+        if (opt_key == "start_gcode")                                return &this->start_gcode;
+        if (opt_key == "start_perimeters_at_concave_points")         return &this->start_perimeters_at_concave_points;
+        if (opt_key == "start_perimeters_at_non_overhang")           return &this->start_perimeters_at_non_overhang;
+        if (opt_key == "temperature")                                return &this->temperature;
+        if (opt_key == "threads")                                    return &this->threads;
+        if (opt_key == "toolchange_gcode")                           return &this->toolchange_gcode;
+        if (opt_key == "top_solid_infill_speed")                     return &this->top_solid_infill_speed;
+        if (opt_key == "travel_speed")                               return &this->travel_speed;
+        if (opt_key == "use_firmware_retraction")                    return &this->use_firmware_retraction;
+        if (opt_key == "use_relative_e_distances")                   return &this->use_relative_e_distances;
+        if (opt_key == "vibration_limit")                            return &this->vibration_limit;
+        if (opt_key == "wipe")                                       return &this->wipe;
+        if (opt_key == "z_offset")                                   return &this->z_offset;
+        
+        if (create) throw "Attempt to create non-existing option in StaticConfig object";
+        return NULL;
+    };
+};
+
 class DynamicPrintConfig : public DynamicConfig
 {
     public:
     DynamicPrintConfig() {
-        this->def = &PrintConfig::PrintConfigDef;
+        this->def = &PrintConfigDef::def;
     };
-
 };
 
+class FullPrintConfig : public PrintObjectConfig, public PrintRegionConfig, public PrintConfig {};
+
 }
 
 #endif

From 0883d0f4ebc56425280868ba2d77c09657c13907 Mon Sep 17 00:00:00 2001
From: Alessandro Ranellucci <aar@cpan.org>
Date: Wed, 1 Jan 2014 17:29:15 +0100
Subject: [PATCH 04/19] More work here and there

---
 lib/Slic3r/Print.pm        | 35 +++++++++++++++++++------------
 lib/Slic3r/Print/Object.pm | 10 +--------
 lib/Slic3r/Print/Region.pm |  2 +-
 xs/src/Config.cpp          | 22 +++++++++++++++++++
 xs/src/Config.hpp          |  1 +
 xs/src/PrintConfig.hpp     |  4 ++--
 xs/xsp/Config.xsp          | 43 +++++++++++++++++++++++++++++++-------
 7 files changed, 85 insertions(+), 32 deletions(-)

diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm
index 98060342b..4c9b9d79e 100644
--- a/lib/Slic3r/Print.pm
+++ b/lib/Slic3r/Print.pm
@@ -12,13 +12,14 @@ use Slic3r::Geometry::Clipper qw(diff_ex union_ex union_pt intersection_ex inter
     offset2 union union_pt_chained JT_ROUND JT_SQUARE);
 use Slic3r::Print::State ':steps';
 
-has 'config'                 => (is => 'rw', default => sub { Slic3r::Config->new_from_defaults }, trigger => \&init_config);
+has 'config'                 => (is => 'rw', default => sub { Slic3r::Config::Print->new }, trigger => \&init_config);
+has 'default_object_config'  => (is => 'rw', default => sub { Slic3r::Config::PrintObject->new });
+has 'default_region_config'  => (is => 'rw', default => sub { Slic3r::Config::PrintRegion->new });
 has 'extra_variables'        => (is => 'rw', default => sub {{}});
 has 'objects'                => (is => 'rw', default => sub {[]});
 has 'status_cb'              => (is => 'rw');
 has 'extruders'              => (is => 'rw', default => sub {[]});
 has 'regions'                => (is => 'rw', default => sub {[]});
-has 'has_support_material'   => (is => 'lazy');
 has '_state'                 => (is => 'ro', default => sub { Slic3r::Print::State->new });
 
 # ordered collection of extrusion paths to build skirt loops
@@ -70,11 +71,12 @@ sub apply_config {
     my ($self, $config) = @_;
     
     $self->config->apply($config);
+    $self->default_object_config->apply($config);
+    $self->default_region_config->apply($config);
     $self->init_config;
-    $_->init_config for @{$self->objects};
 }
 
-sub _build_has_support_material {
+sub has_support_material {
     my $self = shift;
     return (first { $_->config->support_material } @{$self->objects})
         || (first { $_->config->raft_layers > 0 } @{$self->objects})
@@ -91,13 +93,15 @@ sub add_model_object {
     foreach my $volume_id (0..$#{$object->volumes}) {
         my $volume = $object->volumes->[$volume_id];
         
-        # get the config applied to this volume
-        my $config;
+        # get the config applied to this volume: start from our global defaults
+        my $config = Slic3r::Config::RegionConfig->new;
+        $config->apply($self->default_region_config);
+        
+        # override the defaults with per-object config and then with per-material config
+        $config->apply($object->config);
         if (defined $volume->material_id) {
-            my $config = $object->model->materials->{ $volume->material_id }->config;
-        } else {
-            $config = Slic3r::Config->new;
-            $config->set('extruder', 0);
+            my $material_config = $object->model->materials->{ $volume->material_id }->config;
+            $config->apply($material_config);
         }
         
         # find an existing print region with the same config
@@ -112,10 +116,10 @@ sub add_model_object {
         
         # if no region exists with the same config, create a new one
         if (!defined $region_id) {
-            push @{$self->regions}, Slic3r::Print::Region->new(
+            push @{$self->regions}, my $r = Slic3r::Print::Region->new(
                 print  => $self,
-                config => $config->clone,
             );
+            $r->config->apply($config);
             $region_id = $#{$self->regions};
         }
         
@@ -130,9 +134,14 @@ sub add_model_object {
         model_object        => $object,
         region_volumes      => [ map $volumes{$_}, 0..$#{$self->regions} ],
         copies              => [ map Slic3r::Point->new_scale(@{ $_->offset }), @{ $object->instances } ],
-        config_overrides    => $object->config,
         layer_height_ranges => $object->layer_height_ranges,
     );
+    
+    # apply config to print object
+    $o->config->apply($self->default_object_config);
+    $o->config->apply($object->config);
+    
+    # store print object at the given position
     if (defined $obj_idx) {
         splice @{$self->objects}, $obj_idx, 0, $o;
     } else {
diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm
index b1ef32fad..2e9f3dfe0 100644
--- a/lib/Slic3r/Print/Object.pm
+++ b/lib/Slic3r/Print/Object.pm
@@ -13,8 +13,7 @@ has 'print'             => (is => 'ro', weak_ref => 1, required => 1);
 has 'model_object'      => (is => 'ro', required => 1);
 has 'region_volumes'    => (is => 'rw', default => sub { [] });  # by region_id
 has 'copies'            => (is => 'ro');  # Slic3r::Point objects in scaled G-code coordinates
-has 'config_overrides'  => (is => 'rw', default => sub { Slic3r::Config->new });
-has 'config'            => (is => 'rw');
+has 'config'            => (is => 'rw', required => 1);  # Slic3r::Config::PrintObject
 has 'layer_height_ranges' => (is => 'rw', default => sub { [] }); # [ z_min, z_max, layer_height ]
 
 has 'size'              => (is => 'rw'); # XYZ in scaled coordinates
@@ -28,8 +27,6 @@ has '_state'            => (is => 'ro', default => sub { Slic3r::Print::State->n
 sub BUILD {
     my $self = shift;
  	
- 	$self->init_config;
- 	
  	# translate meshes so that we work with smaller coordinates
  	{
  	    # compute the bounding box of the supplied meshes
@@ -97,11 +94,6 @@ sub delete_all_copies {
     $self->_trigger_copies;
 }
 
-sub init_config {
-    my $self = shift;
-    $self->config(Slic3r::Config->merge($self->print->config, $self->config_overrides));
-}
-
 sub layer_count {
     my $self = shift;
     return scalar @{ $self->layers };
diff --git a/lib/Slic3r/Print/Region.pm b/lib/Slic3r/Print/Region.pm
index 559fce3c5..2b5d445de 100644
--- a/lib/Slic3r/Print/Region.pm
+++ b/lib/Slic3r/Print/Region.pm
@@ -8,7 +8,7 @@ use Slic3r::Flow ':roles';
 # sharing the same config (including the same assigned extruder(s))
 
 has 'print'             => (is => 'ro', required => 1, weak_ref => 1);
-has 'config'            => (is => 'ro', required => 1);
+has 'config'            => (is => 'ro', required => 1);  # Slic3r::Config::RegionConfig
 
 sub flow {
     my ($self, $role, $layer_height, $bridge, $first_layer, $width) = @_;
diff --git a/xs/src/Config.cpp b/xs/src/Config.cpp
index 828795c0c..58c9343e2 100644
--- a/xs/src/Config.cpp
+++ b/xs/src/Config.cpp
@@ -35,6 +35,14 @@ ConfigBase::serialize(const t_config_option_key opt_key) {
 
 void
 ConfigBase::set_deserialize(const t_config_option_key opt_key, std::string str) {
+    if (this->def->count(opt_key) == 0) throw "Calling set_deserialize() on unknown option";
+    ConfigOptionDef* optdef = &(*this->def)[opt_key];
+    if (!optdef->shortcut.empty()) {
+        for (std::vector<t_config_option_key>::iterator it = optdef->shortcut.begin(); it != optdef->shortcut.end(); ++it)
+            this->set_deserialize(*it, str);
+        return;
+    }
+    
     ConfigOption* opt = this->option(opt_key, true);
     assert(opt != NULL);
     opt->deserialize(str);
@@ -61,6 +69,20 @@ ConfigBase::get_abs_value(const t_config_option_key opt_key) {
     }
 }
 
+double
+ConfigBase::get_abs_value(const t_config_option_key opt_key, double ratio_over) {
+    // get stored option value
+    ConfigOptionFloatOrPercent* opt = dynamic_cast<ConfigOptionFloatOrPercent*>(this->option(opt_key));
+    assert(opt != NULL);
+    
+    // compute absolute value
+    if (opt->percent) {
+        return ratio_over * opt->value / 100;
+    } else {
+        return opt->value;
+    }
+}
+
 #ifdef SLIC3RXS
 SV*
 ConfigBase::as_hash() {
diff --git a/xs/src/Config.hpp b/xs/src/Config.hpp
index e222651c8..9b9a7bf95 100644
--- a/xs/src/Config.hpp
+++ b/xs/src/Config.hpp
@@ -393,6 +393,7 @@ class ConfigBase
     std::string serialize(const t_config_option_key opt_key);
     void set_deserialize(const t_config_option_key opt_key, std::string str);
     double get_abs_value(const t_config_option_key opt_key);
+    double get_abs_value(const t_config_option_key opt_key, double ratio_over);
     
     #ifdef SLIC3RXS
     SV* as_hash();
diff --git a/xs/src/PrintConfig.hpp b/xs/src/PrintConfig.hpp
index 38eb09ba1..0c9678206 100644
--- a/xs/src/PrintConfig.hpp
+++ b/xs/src/PrintConfig.hpp
@@ -986,7 +986,7 @@ class PrintRegionConfig : public virtual StaticConfig
     ConfigOptionFloatOrPercent      top_infill_extrusion_width;
     ConfigOptionInt                 top_solid_layers;
     
-    PrintObjectConfig() {
+    PrintRegionConfig() {
         this->def = &PrintConfigDef::def;
         
         this->bottom_solid_layers.value                          = 3;
@@ -1135,7 +1135,7 @@ class PrintConfig : public virtual StaticConfig
     ConfigOptionFloat               z_offset;
     
     PrintConfig() {
-        this->def = &PrintConfig::PrintConfigDef;
+        this->def = &PrintConfigDef::def;
         
         this->avoid_crossing_perimeters.value                    = false;
         this->bed_size.point                                     = Pointf(200,200);
diff --git a/xs/xsp/Config.xsp b/xs/xsp/Config.xsp
index 3f053061d..80b00c116 100644
--- a/xs/xsp/Config.xsp
+++ b/xs/xsp/Config.xsp
@@ -15,15 +15,13 @@
     void set_deserialize(t_config_option_key opt_key, std::string str);
     std::string serialize(t_config_option_key opt_key);
     double get_abs_value(t_config_option_key opt_key);
+    double get_abs_value(t_config_option_key opt_key, double ratio_over);
     void apply(DynamicPrintConfig* other)
         %code{% THIS->apply(*other, true); %};
     void apply_static(PrintConfig* other)
         %code{% THIS->apply(*other, true); %};
     std::vector<std::string> get_keys()
         %code{% THIS->keys(&RETVAL); %};
-%{
-
-%}
 };
 
 %name{Slic3r::Config::Print} class PrintConfig {
@@ -36,13 +34,45 @@
     void set_deserialize(t_config_option_key opt_key, std::string str);
     std::string serialize(t_config_option_key opt_key);
     double get_abs_value(t_config_option_key opt_key);
+    double get_abs_value(t_config_option_key opt_key, double ratio_over);
     void apply_dynamic(DynamicPrintConfig* other)
         %code{% THIS->apply(*other, true); %};
     std::vector<std::string> get_keys()
         %code{% THIS->keys(&RETVAL); %};
-%{
+};
 
-%}
+%name{Slic3r::Config::Print} class PrintConfig {
+    PrintConfig();
+    ~PrintConfig();
+    bool has(t_config_option_key opt_key);
+    SV* as_hash();
+    SV* get(t_config_option_key opt_key);
+    void set(t_config_option_key opt_key, SV* value);
+    void set_deserialize(t_config_option_key opt_key, std::string str);
+    std::string serialize(t_config_option_key opt_key);
+    double get_abs_value(t_config_option_key opt_key);
+    double get_abs_value(t_config_option_key opt_key, double ratio_over);
+    void apply_dynamic(DynamicPrintConfig* other)
+        %code{% THIS->apply(*other, true); %};
+    std::vector<std::string> get_keys()
+        %code{% THIS->keys(&RETVAL); %};
+};
+
+%name{Slic3r::Config::Print} class PrintConfig {
+    PrintConfig();
+    ~PrintConfig();
+    bool has(t_config_option_key opt_key);
+    SV* as_hash();
+    SV* get(t_config_option_key opt_key);
+    void set(t_config_option_key opt_key, SV* value);
+    void set_deserialize(t_config_option_key opt_key, std::string str);
+    std::string serialize(t_config_option_key opt_key);
+    double get_abs_value(t_config_option_key opt_key);
+    double get_abs_value(t_config_option_key opt_key, double ratio_over);
+    void apply_dynamic(DynamicPrintConfig* other)
+        %code{% THIS->apply(*other, true); %};
+    std::vector<std::string> get_keys()
+        %code{% THIS->keys(&RETVAL); %};
 };
 
 %package{Slic3r::Config};
@@ -53,8 +83,7 @@ PROTOTYPES: DISABLE
 SV*
 print_config_def()
     CODE:
-        PrintConfig config;
-        t_optiondef_map* def = config.def;
+        t_optiondef_map* def = &PrintConfigDef::def;
         
         HV* options_hv = newHV();
         for (t_optiondef_map::iterator oit = def->begin(); oit != def->end(); ++oit) {

From e2f1040a76e9ad3e7d08e9588dc527895c28e017 Mon Sep 17 00:00:00 2001
From: Alessandro Ranellucci <aar@cpan.org>
Date: Thu, 2 Jan 2014 10:44:54 +0100
Subject: [PATCH 05/19] More work

---
 lib/Slic3r.pm              |  3 +++
 lib/Slic3r/Config.pm       | 27 +++++++++++++++++---
 lib/Slic3r/Layer/Region.pm | 10 ++++----
 lib/Slic3r/Print.pm        | 14 +++++------
 lib/Slic3r/Print/Object.pm | 44 ++++++++++++++++++--------------
 lib/Slic3r/Print/Region.pm |  8 +++---
 xs/src/PrintConfig.hpp     | 13 +++++++---
 xs/t/15_config.t           |  6 ++---
 xs/xsp/Config.xsp          | 51 ++++++++++++++++++++++++++++++--------
 xs/xsp/my.map              |  3 +++
 xs/xsp/typemap.xspt        |  3 +++
 11 files changed, 126 insertions(+), 56 deletions(-)

diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm
index 1a1fa6f70..8ab34ca34 100644
--- a/lib/Slic3r.pm
+++ b/lib/Slic3r.pm
@@ -132,7 +132,10 @@ sub thread_cleanup {
     # prevent destruction of shared objects
     no warnings 'redefine';
     *Slic3r::Config::DESTROY                = sub {};
+    *Slic3r::Config::Full::DESTROY          = sub {};
     *Slic3r::Config::Print::DESTROY         = sub {};
+    *Slic3r::Config::PrintObject::DESTROY   = sub {};
+    *Slic3r::Config::PrintRegion::DESTROY   = sub {};
     *Slic3r::ExPolygon::DESTROY             = sub {};
     *Slic3r::ExPolygon::Collection::DESTROY = sub {};
     *Slic3r::ExtrusionLoop::DESTROY         = sub {};
diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm
index bd924448b..9f5de4f4b 100644
--- a/lib/Slic3r/Config.pm
+++ b/lib/Slic3r/Config.pm
@@ -15,16 +15,25 @@ our $Options = print_config_def();
 {
     no strict 'refs';
     for my $opt_key (keys %$Options) {
-        *{$opt_key} = sub { $_[0]->get($opt_key) };
+        *{$opt_key} = sub { $_[0]->_get($opt_key) };
     }
 }
 
+sub _get {
+    my ($self, $opt_key) = @_;
+    use XXX;
+    if (!defined first { $_ eq $opt_key } @{$self->get_keys}) {
+        ZZZ $opt_key;
+    }
+    return $self->get($opt_key);
+}
+
 sub new_from_defaults {
     my $class = shift;
     my (@opt_keys) = @_;
     
     my $self = $class->new;
-    my $defaults = Slic3r::Config::Print->new;
+    my $defaults = Slic3r::Config::Full->new;
     if (@opt_keys) {
         $self->set($_, $defaults->get($_)) for @opt_keys;
     } else {
@@ -309,7 +318,7 @@ sub validate {
             && !$self->default_acceleration;
     
     # general validation, quick and dirty
-    foreach my $opt_key (keys %$Options) {
+    foreach my $opt_key (@{$self->get_keys}) {
         my $opt = $Options->{$opt_key};
         next unless defined $self->$opt_key;
         next unless defined $opt->{cli} && $opt->{cli} =~ /=(.+)$/;
@@ -431,4 +440,16 @@ sub read_ini {
     return $ini;
 }
 
+package Slic3r::Config::Print;
+use parent 'Slic3r::Config';
+
+package Slic3r::Config::PrintObject;
+use parent 'Slic3r::Config';
+
+package Slic3r::Config::PrintRegion;
+use parent 'Slic3r::Config';
+
+package Slic3r::Config::Full;
+use parent 'Slic3r::Config';
+
 1;
diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm
index 1f27d143c..ce22d9c88 100644
--- a/lib/Slic3r/Layer/Region.pm
+++ b/lib/Slic3r/Layer/Region.pm
@@ -14,9 +14,9 @@ has 'layer' => (
     is          => 'ro',
     weak_ref    => 1,
     required    => 1,
-    handles     => [qw(id slice_z print_z height config)],
+    handles     => [qw(id slice_z print_z height object print)],
 );
-has 'region'            => (is => 'ro', required => 1, handles => [qw(extruders)]);
+has 'region'            => (is => 'ro', required => 1, handles => [qw(config)]);
 has 'infill_area_threshold' => (is => 'lazy');
 has 'overhang_width'    => (is => 'lazy');
 
@@ -168,7 +168,7 @@ sub make_perimeters {
                     @offsets = @{offset2(\@last, -(1.5*$pspacing - 1), +(0.5*$pspacing - 1))};
                 
                     # look for gaps
-                    if ($self->config->gap_fill_speed > 0 && $self->config->fill_density > 0) {
+                    if ($self->print->config->gap_fill_speed > 0 && $self->config->fill_density > 0) {
                         my $diff = diff_ex(
                             offset(\@last, -0.5*$pspacing),
                             offset(\@offsets, +0.5*$pspacing),
@@ -277,8 +277,8 @@ sub make_perimeters {
     # we continue inwards after having finished the brim
     # TODO: add test for perimeter order
     @loops = reverse @loops
-        if $self->config->external_perimeters_first
-            || ($self->layer->id == 0 && $self->config->brim_width > 0);
+        if $self->print->config->external_perimeters_first
+            || ($self->layer->id == 0 && $self->print->config->brim_width > 0);
     
     # append perimeters
     $self->perimeters->append(@loops);
diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm
index 4c9b9d79e..d490597db 100644
--- a/lib/Slic3r/Print.pm
+++ b/lib/Slic3r/Print.pm
@@ -70,9 +70,9 @@ sub init_config {
 sub apply_config {
     my ($self, $config) = @_;
     
-    $self->config->apply($config);
-    $self->default_object_config->apply($config);
-    $self->default_region_config->apply($config);
+    $self->config->apply_dynamic($config);
+    $self->default_object_config->apply_dynamic($config);
+    $self->default_region_config->apply_dynamic($config);
     $self->init_config;
 }
 
@@ -94,14 +94,14 @@ sub add_model_object {
         my $volume = $object->volumes->[$volume_id];
         
         # get the config applied to this volume: start from our global defaults
-        my $config = Slic3r::Config::RegionConfig->new;
+        my $config = Slic3r::Config::PrintRegion->new;
         $config->apply($self->default_region_config);
         
         # override the defaults with per-object config and then with per-material config
-        $config->apply($object->config);
+        $config->apply_dynamic($object->config);
         if (defined $volume->material_id) {
             my $material_config = $object->model->materials->{ $volume->material_id }->config;
-            $config->apply($material_config);
+            $config->apply_dynamic($material_config);
         }
         
         # find an existing print region with the same config
@@ -139,7 +139,7 @@ sub add_model_object {
     
     # apply config to print object
     $o->config->apply($self->default_object_config);
-    $o->config->apply($object->config);
+    $o->config->apply_dynamic($object->config);
     
     # store print object at the given position
     if (defined $obj_idx) {
diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm
index 2e9f3dfe0..600a0dcc8 100644
--- a/lib/Slic3r/Print/Object.pm
+++ b/lib/Slic3r/Print/Object.pm
@@ -13,7 +13,7 @@ has 'print'             => (is => 'ro', weak_ref => 1, required => 1);
 has 'model_object'      => (is => 'ro', required => 1);
 has 'region_volumes'    => (is => 'rw', default => sub { [] });  # by region_id
 has 'copies'            => (is => 'ro');  # Slic3r::Point objects in scaled G-code coordinates
-has 'config'            => (is => 'rw', required => 1);  # Slic3r::Config::PrintObject
+has 'config'            => (is => 'ro', default => sub { Slic3r::Config::PrintObject->new });
 has 'layer_height_ranges' => (is => 'rw', default => sub { [] }); # [ z_min, z_max, layer_height ]
 
 has 'size'              => (is => 'rw'); # XYZ in scaled coordinates
@@ -273,8 +273,8 @@ sub slice {
     }
     
     # simplify slices if required
-    if ($self->config->resolution) {
-        $self->_simplify_slices(scale($self->config->resolution));
+    if ($self->print->config->resolution) {
+        $self->_simplify_slices(scale($self->print->config->resolution));
     }
 }
 
@@ -288,8 +288,11 @@ sub make_perimeters {
     # 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
-    if ($self->config->extra_perimeters && $self->config->perimeters > 0 && $self->config->fill_density > 0) {
-        for my $region_id (0 .. ($self->print->regions_count-1)) {
+    for my $region_id (0 .. ($self->print->regions_count-1)) {
+        my $region = $self->print->regions->[$region_id];
+        my $region_perimeters = $region->config->perimeters;
+        
+        if ($region->config->extra_perimeters && $region_perimeters > 0 && $region->config->fill_density > 0) {
             for my $layer_id (0 .. $self->layer_count-2) {
                 my $layerm          = $self->layers->[$layer_id]->regions->[$region_id];
                 my $upper_layerm    = $self->layers->[$layer_id+1]->regions->[$region_id];
@@ -298,7 +301,7 @@ sub make_perimeters {
                 my $overlap = $perimeter_spacing;  # one perimeter
                 
                 my $diff = diff(
-                    offset([ map @{$_->expolygon}, @{$layerm->slices} ], -($self->config->perimeters * $perimeter_spacing)),
+                    offset([ map @{$_->expolygon}, @{$layerm->slices} ], -($region_perimeters * $perimeter_spacing)),
                     offset([ map @{$_->expolygon}, @{$upper_layerm->slices} ], -$overlap),
                 );
                 next if !@$diff;
@@ -322,8 +325,8 @@ sub make_perimeters {
                         # of our slice
                         $extra_perimeters++;
                         my $hypothetical_perimeter = diff(
-                            offset($slice->expolygon->arrayref, -($perimeter_spacing * ($self->config->perimeters + $extra_perimeters-1))),
-                            offset($slice->expolygon->arrayref, -($perimeter_spacing * ($self->config->perimeters + $extra_perimeters))),
+                            offset($slice->expolygon->arrayref, -($perimeter_spacing * ($region_perimeters + $extra_perimeters-1))),
+                            offset($slice->expolygon->arrayref, -($perimeter_spacing * ($region_perimeters + $extra_perimeters))),
                         );
                         last CYCLE if !@$hypothetical_perimeter;  # no extra perimeter is possible
                         
@@ -339,7 +342,7 @@ sub make_perimeters {
     }
     
     Slic3r::parallelize(
-        threads => $self->config->threads,
+        threads => $self->print->config->threads,
         items => sub { 0 .. ($self->layer_count-1) },
         thread_cb => sub {
             my $q = shift;
@@ -376,7 +379,7 @@ sub detect_surfaces_type {
                 );
                 
                 # collapse very narrow parts (using the safety offset in the diff is not enough)
-                my $offset = $layerm->perimeter_flow->scaled_width / 10;
+                my $offset = $layerm->flow(FLOW_ROLE_PERIMETER)->scaled_width / 10;
                 return map Slic3r::Surface->new(expolygon => $_, surface_type => $result_type),
                     @{ offset2_ex($diff, -$offset, +$offset) };
             };
@@ -609,8 +612,8 @@ sub discover_horizontal_shells {
         for (my $i = 0; $i < $self->layer_count; $i++) {
             my $layerm = $self->layers->[$i]->regions->[$region_id];
             
-            if ($self->config->solid_infill_every_layers && $self->config->fill_density > 0
-                && ($i % $self->config->solid_infill_every_layers) == 0) {
+            if ($layerm->config->solid_infill_every_layers && $layerm->config->fill_density > 0
+                && ($i % $layerm->config->solid_infill_every_layers) == 0) {
                 $_->surface_type(S_TYPE_INTERNALSOLID) for @{$layerm->fill_surfaces->filter_by_type(S_TYPE_INTERNAL)};
             }
             
@@ -632,8 +635,8 @@ sub discover_horizontal_shells {
                 Slic3r::debugf "Layer %d has %s surfaces\n", $i, ($type == S_TYPE_TOP) ? 'top' : 'bottom';
                 
                 my $solid_layers = ($type == S_TYPE_TOP)
-                    ? $self->config->top_solid_layers
-                    : $self->config->bottom_solid_layers;
+                    ? $layerm->config->top_solid_layers
+                    : $layerm->config->bottom_solid_layers;
                 NEIGHBOR: for (my $n = ($type == S_TYPE_TOP) ? $i-1 : $i+1; 
                         abs($n - $i) <= $solid_layers-1; 
                         ($type == S_TYPE_TOP) ? $n-- : $n++) {
@@ -668,7 +671,7 @@ sub discover_horizontal_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.
-                        my $margin = 3 * $layerm->solid_infill_flow->scaled_width; # require at least this size
+                        my $margin = 3 * $layerm->flow(FLOW_ROLE_SOLID_INFILL)->scaled_width; # require at least this size
                         my $too_narrow = diff(
                             $new_internal_solid,
                             offset2($new_internal_solid, -$margin, +$margin, CLIPPER_OFFSET_SCALE, JT_MITER, 5),
@@ -677,7 +680,7 @@ sub discover_horizontal_shells {
                         
                         # if some parts are going to collapse, use a different strategy according to fill density
                         if (@$too_narrow) {
-                            if ($self->config->fill_density > 0) {
+                            if ($layerm->config->fill_density > 0) {
                                 # if we have internal infill, 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 
                                 # additional area in the next shell too
@@ -741,13 +744,16 @@ sub discover_horizontal_shells {
 # combine fill surfaces across layers
 sub combine_infill {
     my $self = shift;
-    return unless $self->config->infill_every_layers > 1 && $self->config->fill_density > 0;
-    my $every = $self->config->infill_every_layers;
+    
+    return unless defined first { $_->config->infill_every_layers > 1 && $_->config->fill_density > 0 } @{$self->print->regions};
     
     my $layer_count = $self->layer_count;
     my @layer_heights = map $self->layers->[$_]->height, 0 .. $layer_count-1;
     
     for my $region_id (0 .. ($self->print->regions_count-1)) {
+        my $region = $self->print->regions->[$region_id];
+        my $every = $region->config->infill_every_layers;
+        
         # limit the number of combined layers to the maximum height allowed by this regions' nozzle
         my $nozzle_diameter = $self->print->regions->[$region_id]->extruders->{infill}->nozzle_diameter;
         
@@ -805,7 +811,7 @@ sub combine_infill {
                      + $layerms[-1]->perimeter_flow->scaled_width / 2
                      # Because fill areas for rectilinear and honeycomb are grown 
                      # later to overlap perimeters, we need to counteract that too.
-                     + (($type == S_TYPE_INTERNALSOLID || $self->config->fill_pattern =~ /(rectilinear|honeycomb)/)
+                     + (($type == S_TYPE_INTERNALSOLID || $region->config->fill_pattern =~ /(rectilinear|honeycomb)/)
                        ? $layerms[-1]->solid_infill_flow->scaled_width * &Slic3r::INFILL_OVERLAP_OVER_SPACING
                        : 0)
                      )}, @$intersection;
diff --git a/lib/Slic3r/Print/Region.pm b/lib/Slic3r/Print/Region.pm
index 2b5d445de..4cb929e0b 100644
--- a/lib/Slic3r/Print/Region.pm
+++ b/lib/Slic3r/Print/Region.pm
@@ -8,7 +8,7 @@ use Slic3r::Flow ':roles';
 # sharing the same config (including the same assigned extruder(s))
 
 has 'print'             => (is => 'ro', required => 1, weak_ref => 1);
-has 'config'            => (is => 'ro', required => 1);  # Slic3r::Config::RegionConfig
+has 'config'            => (is => 'ro', default => sub { Slic3r::Config::PrintRegion->new});
 
 sub flow {
     my ($self, $role, $layer_height, $bridge, $first_layer, $width) = @_;
@@ -21,7 +21,7 @@ sub flow {
     if (!defined $config_width) {
         # get extrusion width from configuration
         # (might be an absolute value, or a percent value, or zero for auto)
-        if ($first_layer && $self->config->first_layer_extrusion_width != 0) {
+        if ($first_layer && $self->config->first_layer_extrusion_width ne '0') {
             $config_width = $self->config->first_layer_extrusion_width;
         } elsif ($role == FLOW_ROLE_PERIMETER) {
             $config_width = $self->config->perimeter_extrusion_width;
@@ -40,9 +40,9 @@ sub flow {
     # to the flow role requested
     my $extruder;  # 1-based
     if ($role == FLOW_ROLE_PERIMETER) {
-        $config_width = $self->config->perimeter_extruder;
+        $extruder = $self->config->perimeter_extruder;
     } elsif ($role == FLOW_ROLE_INFILL || $role == FLOW_ROLE_SOLID_INFILL || $role == FLOW_ROLE_TOP_SOLID_INFILL) {
-        $config_width = $self->config->infill_extruder;
+        $extruder = $self->config->infill_extruder;
     } else {
         die "Unknown role $role";
     }
diff --git a/xs/src/PrintConfig.hpp b/xs/src/PrintConfig.hpp
index 0c9678206..4643c8b70 100644
--- a/xs/src/PrintConfig.hpp
+++ b/xs/src/PrintConfig.hpp
@@ -956,7 +956,6 @@ class PrintObjectConfig : public virtual StaticConfig
         if (opt_key == "support_material_speed")                     return &this->support_material_speed;
         if (opt_key == "support_material_threshold")                 return &this->support_material_threshold;
         
-        if (create) throw "Attempt to create non-existing option in StaticConfig object";
         return NULL;
     };
 };
@@ -1039,7 +1038,6 @@ class PrintRegionConfig : public virtual StaticConfig
         if (opt_key == "top_infill_extrusion_width")                 return &this->top_infill_extrusion_width;
         if (opt_key == "top_solid_layers")                           return &this->top_solid_layers;
         
-        if (create) throw "Attempt to create non-existing option in StaticConfig object";
         return NULL;
     };
 };
@@ -1332,7 +1330,6 @@ class PrintConfig : public virtual StaticConfig
         if (opt_key == "wipe")                                       return &this->wipe;
         if (opt_key == "z_offset")                                   return &this->z_offset;
         
-        if (create) throw "Attempt to create non-existing option in StaticConfig object";
         return NULL;
     };
 };
@@ -1345,7 +1342,15 @@ class DynamicPrintConfig : public DynamicConfig
     };
 };
 
-class FullPrintConfig : public PrintObjectConfig, public PrintRegionConfig, public PrintConfig {};
+class FullPrintConfig : public PrintObjectConfig, public PrintRegionConfig, public PrintConfig {
+    ConfigOption* option(const t_config_option_key opt_key, bool create = false) {
+        ConfigOption* opt;
+        if ((opt = PrintObjectConfig::option(opt_key, create)) != NULL) return opt;
+        if ((opt = PrintRegionConfig::option(opt_key, create)) != NULL) return opt;
+        if ((opt = PrintConfig::option(opt_key, create)) != NULL) return opt;
+        return NULL;
+    };
+};
 
 }
 
diff --git a/xs/t/15_config.t b/xs/t/15_config.t
index 699f115f8..4e8f6e68a 100644
--- a/xs/t/15_config.t
+++ b/xs/t/15_config.t
@@ -6,7 +6,7 @@ use warnings;
 use Slic3r::XS;
 use Test::More tests => 79;
 
-foreach my $config (Slic3r::Config->new, Slic3r::Config::Print->new) {
+foreach my $config (Slic3r::Config->new, Slic3r::Config::Full->new) {
     $config->set('layer_height', 0.3);
     ok abs($config->get('layer_height') - 0.3) < 1e-4, 'set/get float';
     is $config->serialize('layer_height'), '0.3', 'serialize float';
@@ -94,13 +94,13 @@ foreach my $config (Slic3r::Config->new, Slic3r::Config::Print->new) {
     # test that no crash happens when using set_deserialize() with a key that hasn't been set() yet
     $config->set_deserialize('filament_diameter', '3');
     
-    my $config2 = Slic3r::Config::Print->new;
+    my $config2 = Slic3r::Config::Full->new;
     $config2->apply_dynamic($config);
     is $config2->get('perimeters'), 2, 'apply_dynamic';
 }
 
 {
-    my $config = Slic3r::Config::Print->new;
+    my $config = Slic3r::Config::Full->new;
     my $config2 = Slic3r::Config->new;
     $config2->apply_static($config);
     is $config2->get('perimeters'), Slic3r::Config::print_config_def()->{perimeters}{default}, 'apply_static and print_config_def';
diff --git a/xs/xsp/Config.xsp b/xs/xsp/Config.xsp
index 80b00c116..5c3966e13 100644
--- a/xs/xsp/Config.xsp
+++ b/xs/xsp/Config.xsp
@@ -15,7 +15,8 @@
     void set_deserialize(t_config_option_key opt_key, std::string str);
     std::string serialize(t_config_option_key opt_key);
     double get_abs_value(t_config_option_key opt_key);
-    double get_abs_value(t_config_option_key opt_key, double ratio_over);
+    %name{get_abs_value_over}
+        double get_abs_value(t_config_option_key opt_key, double ratio_over);
     void apply(DynamicPrintConfig* other)
         %code{% THIS->apply(*other, true); %};
     void apply_static(PrintConfig* other)
@@ -34,16 +35,17 @@
     void set_deserialize(t_config_option_key opt_key, std::string str);
     std::string serialize(t_config_option_key opt_key);
     double get_abs_value(t_config_option_key opt_key);
-    double get_abs_value(t_config_option_key opt_key, double ratio_over);
+    %name{get_abs_value_over}
+        double get_abs_value(t_config_option_key opt_key, double ratio_over);
     void apply_dynamic(DynamicPrintConfig* other)
         %code{% THIS->apply(*other, true); %};
     std::vector<std::string> get_keys()
         %code{% THIS->keys(&RETVAL); %};
 };
 
-%name{Slic3r::Config::Print} class PrintConfig {
-    PrintConfig();
-    ~PrintConfig();
+%name{Slic3r::Config::PrintRegion} class PrintRegionConfig {
+    PrintRegionConfig();
+    ~PrintRegionConfig();
     bool has(t_config_option_key opt_key);
     SV* as_hash();
     SV* get(t_config_option_key opt_key);
@@ -51,16 +53,19 @@
     void set_deserialize(t_config_option_key opt_key, std::string str);
     std::string serialize(t_config_option_key opt_key);
     double get_abs_value(t_config_option_key opt_key);
-    double get_abs_value(t_config_option_key opt_key, double ratio_over);
+    %name{get_abs_value_over}
+        double get_abs_value(t_config_option_key opt_key, double ratio_over);
+    void apply(PrintRegionConfig* other)
+        %code{% THIS->apply(*other, true); %};
     void apply_dynamic(DynamicPrintConfig* other)
         %code{% THIS->apply(*other, true); %};
     std::vector<std::string> get_keys()
         %code{% THIS->keys(&RETVAL); %};
 };
 
-%name{Slic3r::Config::Print} class PrintConfig {
-    PrintConfig();
-    ~PrintConfig();
+%name{Slic3r::Config::PrintObject} class PrintObjectConfig {
+    PrintObjectConfig();
+    ~PrintObjectConfig();
     bool has(t_config_option_key opt_key);
     SV* as_hash();
     SV* get(t_config_option_key opt_key);
@@ -68,7 +73,30 @@
     void set_deserialize(t_config_option_key opt_key, std::string str);
     std::string serialize(t_config_option_key opt_key);
     double get_abs_value(t_config_option_key opt_key);
-    double get_abs_value(t_config_option_key opt_key, double ratio_over);
+    %name{get_abs_value_over}
+        double get_abs_value(t_config_option_key opt_key, double ratio_over);
+    void apply(PrintObjectConfig* other)
+        %code{% THIS->apply(*other, true); %};
+    void apply_dynamic(DynamicPrintConfig* other)
+        %code{% THIS->apply(*other, true); %};
+    std::vector<std::string> get_keys()
+        %code{% THIS->keys(&RETVAL); %};
+};
+
+%name{Slic3r::Config::Full} class FullPrintConfig {
+    FullPrintConfig();
+    ~FullPrintConfig();
+    bool has(t_config_option_key opt_key);
+    SV* as_hash();
+    SV* get(t_config_option_key opt_key);
+    void set(t_config_option_key opt_key, SV* value);
+    void set_deserialize(t_config_option_key opt_key, std::string str);
+    std::string serialize(t_config_option_key opt_key);
+    double get_abs_value(t_config_option_key opt_key);
+    %name{get_abs_value_over}
+        double get_abs_value(t_config_option_key opt_key, double ratio_over);
+    void apply(PrintObjectConfig* other)
+        %code{% THIS->apply(*other, true); %};
     void apply_dynamic(DynamicPrintConfig* other)
         %code{% THIS->apply(*other, true); %};
     std::vector<std::string> get_keys()
@@ -83,7 +111,8 @@ PROTOTYPES: DISABLE
 SV*
 print_config_def()
     CODE:
-        t_optiondef_map* def = &PrintConfigDef::def;
+        FullPrintConfig config;
+        t_optiondef_map* def = config.def;
         
         HV* options_hv = newHV();
         for (t_optiondef_map::iterator oit = def->begin(); oit != def->end(); ++oit) {
diff --git a/xs/xsp/my.map b/xs/xsp/my.map
index 7c370ac10..666cbf616 100644
--- a/xs/xsp/my.map
+++ b/xs/xsp/my.map
@@ -2,7 +2,10 @@ std::vector<Points::size_type>  T_STD_VECTOR_INT
 t_config_option_key T_STD_STRING
 
 DynamicPrintConfig*  O_OBJECT
+PrintObjectConfig*  O_OBJECT
+PrintRegionConfig*  O_OBJECT
 PrintConfig*  O_OBJECT
+FullPrintConfig*  O_OBJECT
 ZTable*         O_OBJECT
 TriangleMesh*         O_OBJECT
 Point*         O_OBJECT
diff --git a/xs/xsp/typemap.xspt b/xs/xsp/typemap.xspt
index eb6302e7d..25c4c4d12 100644
--- a/xs/xsp/typemap.xspt
+++ b/xs/xsp/typemap.xspt
@@ -9,7 +9,10 @@
 %typemap{AV*};
 %typemap{Point*};
 %typemap{DynamicPrintConfig*};
+%typemap{PrintObjectConfig*};
+%typemap{PrintRegionConfig*};
 %typemap{PrintConfig*};
+%typemap{FullPrintConfig*};
 %typemap{ExPolygon*};
 %typemap{ExPolygonCollection*};
 %typemap{Line*};

From 5bf0942f4559d5458bc3a50b1da76cd1bd0e7a16 Mon Sep 17 00:00:00 2001
From: Alessandro Ranellucci <aar@cpan.org>
Date: Thu, 2 Jan 2014 17:24:23 +0100
Subject: [PATCH 06/19] Satisfy test suite and CLI

---
 lib/Slic3r/Config.pm                |  46 +++++--
 lib/Slic3r/Extruder.pm              |   4 +-
 lib/Slic3r/Fill.pm                  |  23 ++--
 lib/Slic3r/Flow.pm                  |   5 +-
 lib/Slic3r/GCode.pm                 | 187 ++++++++++++++--------------
 lib/Slic3r/GCode/ArcFitting.pm      |   2 +-
 lib/Slic3r/GCode/CoolingBuffer.pm   |   2 +-
 lib/Slic3r/GCode/Layer.pm           |  67 +++++-----
 lib/Slic3r/Print.pm                 | 109 ++++++----------
 lib/Slic3r/Print/Object.pm          |  16 ++-
 lib/Slic3r/Print/Region.pm          |   2 +-
 lib/Slic3r/Print/SupportMaterial.pm |  54 ++++----
 lib/Slic3r/Test.pm                  |  11 +-
 slic3r.pl                           |   2 +-
 t/cooling.t                         |  10 +-
 t/gcode.t                           |   5 +-
 t/shells.t                          |   3 +
 t/support.t                         |   8 +-
 xs/src/PrintConfig.hpp              |  15 ++-
 xs/xsp/Config.xsp                   |   1 +
 20 files changed, 300 insertions(+), 272 deletions(-)

diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm
index 9f5de4f4b..21fa2322b 100644
--- a/lib/Slic3r/Config.pm
+++ b/lib/Slic3r/Config.pm
@@ -15,18 +15,18 @@ our $Options = print_config_def();
 {
     no strict 'refs';
     for my $opt_key (keys %$Options) {
-        *{$opt_key} = sub { $_[0]->_get($opt_key) };
+        *{$opt_key} = sub { $_[0]->get($opt_key) };
     }
 }
 
-sub _get {
-    my ($self, $opt_key) = @_;
-    use XXX;
-    if (!defined first { $_ eq $opt_key } @{$self->get_keys}) {
-        ZZZ $opt_key;
-    }
-    return $self->get($opt_key);
-}
+# sub _get {
+#     my ($self, $opt_key) = @_;
+#     use XXX;
+#     if (!defined first { $_ eq $opt_key } @{$self->get_keys}) {
+#         ZZZ $opt_key;
+#     }
+#     return $self->get($opt_key);
+# }
 
 sub new_from_defaults {
     my $class = shift;
@@ -200,7 +200,8 @@ sub diff {
     return [@diff];
 }
 
-# this method is idempotent by design
+# this method is idempotent by design and only applies to ::DynamicConfig or ::Full
+# objects because it performs cross checks
 sub validate {
     my $self = shift;
     
@@ -317,6 +318,31 @@ sub validate {
         if ($self->perimeter_acceleration || $self->infill_acceleration || $self->bridge_acceleration || $self->first_layer_acceleration)
             && !$self->default_acceleration;
     
+    # --spiral-vase
+    if ($self->spiral_vase) {
+        # Note that we might want to have more than one perimeter on the bottom
+        # solid layers.
+        die "Can't make more than one perimeter when spiral vase mode is enabled\n"
+            if $self->perimeters > 1;
+        
+        die "Can't make less than one perimeter when spiral vase mode is enabled\n"
+            if $self->perimeters < 1;
+        
+        die "Spiral vase mode is not compatible with non-zero fill density\n"
+            if $self->fill_density > 0;
+        
+        die "Spiral vase mode is not compatible with top solid layers\n"
+            if $self->top_solid_layers > 0;
+        
+        die "Spiral vase mode is not compatible with support material\n"
+            if $self->support_material || $self->support_material_enforce_layers > 0;
+        
+        # This should be enforce automatically only on spiral layers and
+        # done on the others
+        die "Spiral vase mode is not compatible with retraction on layer change\n"
+            if defined first { $_ } @{ $self->retract_layer_change };
+    }
+    
     # general validation, quick and dirty
     foreach my $opt_key (@{$self->get_keys}) {
         my $opt = $Options->{$opt_key};
diff --git a/lib/Slic3r/Extruder.pm b/lib/Slic3r/Extruder.pm
index 1025c3459..9b8b1dc97 100644
--- a/lib/Slic3r/Extruder.pm
+++ b/lib/Slic3r/Extruder.pm
@@ -18,7 +18,7 @@ use constant OPTIONS => [qw(
 
 has 'id'    => (is => 'rw', required => 1);
 has $_      => (is => 'ro', required => 1) for @{&OPTIONS};
-has 'config'=> (is => 'ro', required => 1);
+has 'use_relative_e_distances'  => (is => 'ro', default => sub {0});
 
 has 'E'                         => (is => 'rw', default => sub {0} );
 has 'absolute_E'                => (is => 'rw', default => sub {0} );
@@ -56,7 +56,7 @@ sub scaled_wipe_distance {
 sub extrude {
     my ($self, $E) = @_;
     
-    $self->E(0) if $self->config->use_relative_e_distances;
+    $self->E(0) if $self->use_relative_e_distances;
     $self->absolute_E($self->absolute_E + $E);
     return $self->E($self->E + $E);
 }
diff --git a/lib/Slic3r/Fill.pm b/lib/Slic3r/Fill.pm
index 42cbea13f..7969431c5 100644
--- a/lib/Slic3r/Fill.pm
+++ b/lib/Slic3r/Fill.pm
@@ -1,6 +1,7 @@
 package Slic3r::Fill;
 use Moo;
 
+use Slic3r::ExtrusionPath ':roles';
 use Slic3r::Fill::ArchimedeanChords;
 use Slic3r::Fill::Base;
 use Slic3r::Fill::Concentric;
@@ -11,7 +12,7 @@ use Slic3r::Fill::Line;
 use Slic3r::Fill::OctagramSpiral;
 use Slic3r::Fill::PlanePath;
 use Slic3r::Fill::Rectilinear;
-use Slic3r::ExtrusionPath ':roles';
+use Slic3r::Flow ':roles';
 use Slic3r::Geometry qw(X Y PI scale chained_path);
 use Slic3r::Geometry::Clipper qw(union union_ex diff diff_ex intersection_ex offset offset2);
 use Slic3r::Surface ':types';
@@ -50,7 +51,10 @@ sub make_fill {
     my ($layerm) = @_;
     
     Slic3r::debugf "Filling layer %d:\n", $layerm->id;
-    my $fill_density = $layerm->config->fill_density;
+    
+    my $fill_density        = $layerm->config->fill_density;
+    my $infill_flow         = $layerm->flow(FLOW_ROLE_INFILL);
+    my $solid_infill_flow   = $layerm->flow(FLOW_ROLE_SOLID_INFILL);
     
     my @surfaces = ();
     
@@ -95,7 +99,7 @@ sub make_fill {
     # we are going to grow such regions by overlapping them with the void (if any)
     # TODO: detect and investigate whether there could be narrow regions without
     # any void neighbors
-    my $distance_between_surfaces = $layerm->solid_infill_flow->scaled_spacing * &Slic3r::INFILL_OVERLAP_OVER_SPACING;
+    my $distance_between_surfaces = $infill_flow->scaled_spacing * &Slic3r::INFILL_OVERLAP_OVER_SPACING;
     {
         my $collapsed = diff(
             [ map @{$_->expolygon}, @surfaces ],
@@ -133,11 +137,10 @@ sub make_fill {
         my $filler          = $layerm->config->fill_pattern;
         my $density         = $fill_density;
         my $flow            = ($surface->surface_type == S_TYPE_TOP)
-            ? $layerm->top_infill_flow
+            ? $layerm->flow(FLOW_ROLE_TOP_SOLID_INFILL)
             : $surface->is_solid
-                ? $layerm->solid_infill_flow
-                : $layerm->infill_flow;
-        my $flow_spacing    = $flow->spacing;
+                ? $solid_infill_flow
+                : $infill_flow;
         my $is_bridge       = $layerm->id > 0 && $surface->is_bridge;
         my $is_solid        = $surface->is_solid;
         
@@ -147,7 +150,7 @@ sub make_fill {
             $filler = $layerm->config->solid_fill_pattern;
             if ($is_bridge) {
                 $filler = 'rectilinear';
-                $flow_spacing = $layerm->extruders->{infill}->bridge_flow->spacing;
+                $flow = $layerm->flow(FLOW_ROLE_SOLID_INFILL, 1);
             } elsif ($surface->surface_type == S_TYPE_INTERNALSOLID) {
                 $filler = 'rectilinear';
             }
@@ -155,6 +158,8 @@ sub make_fill {
             next SURFACE unless $density > 0;
         }
         
+        my $flow_spacing = $flow->spacing;
+        
         my $f = $self->filler($filler);
         $f->layer_id($layerm->id);
         $f->angle($layerm->config->fill_angle);
@@ -166,7 +171,7 @@ sub make_fill {
         next unless @polylines;
         
         # ugly hack(tm) to get the right amount of flow (GCode.pm should be fixed)
-        $params->{flow_spacing} = $layerm->extruders->{infill}->bridge_flow->width if $is_bridge;
+        $params->{flow_spacing} = $flow->width if $is_bridge;
         
         # save into layer
         push @fills, my $collection = Slic3r::ExtrusionPath::Collection->new;
diff --git a/lib/Slic3r/Flow.pm b/lib/Slic3r/Flow.pm
index 0a9747fa8..3930dce52 100644
--- a/lib/Slic3r/Flow.pm
+++ b/lib/Slic3r/Flow.pm
@@ -36,7 +36,10 @@ sub BUILDARGS {
             $args{width} = $args{layer_height} * $1 / 100;
         }
         $args{spacing} = $self->_spacing(@args{qw(width nozzle_diameter layer_height bridge_flow_ratio)});
-        %args = @args{qw(width spacing)};
+        %args = (
+            width   => $args{width},
+            spacing => $args{spacing},
+        );
     }
     
     return {%args};
diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm
index da55942f6..66175260f 100644
--- a/lib/Slic3r/GCode.pm
+++ b/lib/Slic3r/GCode.pm
@@ -3,11 +3,12 @@ use Moo;
 
 use List::Util qw(min first);
 use Slic3r::ExtrusionPath ':roles';
+use Slic3r::Flow ':roles';
 use Slic3r::Geometry qw(epsilon scale unscale scaled_epsilon points_coincide PI X Y B);
 use Slic3r::Geometry::Clipper qw(union_ex);
 use Slic3r::Surface ':types';
 
-has 'config'             => (is => 'ro', required => 1);
+has 'print_config'       => (is => 'ro', default => sub { Slic3r::Config::Print->new });
 has 'extra_variables'    => (is => 'rw', default => sub {{}});
 has 'extruders'          => (is => 'ro', required => 1);
 has 'multiple_extruders' => (is => 'lazy');
@@ -16,6 +17,7 @@ has 'enable_loop_clipping' => (is => 'rw', default => sub {1});
 has 'enable_wipe'        => (is => 'lazy');   # at least one extruder has wipe enabled
 has 'layer_count'        => (is => 'ro', required => 1 );
 has 'layer'              => (is => 'rw');
+has 'region'             => (is => 'rw');
 has '_layer_islands'     => (is => 'rw');
 has '_upper_layer_islands'  => (is => 'rw');
 has '_layer_overhangs'   => (is => 'ro', default => sub { Slic3r::ExPolygon::Collection->new });
@@ -23,6 +25,8 @@ has 'shift_x'            => (is => 'rw', default => sub {0} );
 has 'shift_y'            => (is => 'rw', default => sub {0} );
 has 'z'                  => (is => 'rw');
 has 'speed'              => (is => 'rw');
+has '_extrusion_axis'    => (is => 'rw');
+has '_retract_lift'      => (is => 'rw');
 
 has 'speeds'             => (is => 'lazy');  # mm/min
 has 'external_mp'        => (is => 'rw');
@@ -38,12 +42,19 @@ has 'last_f'             => (is => 'rw', default => sub {""});
 has 'last_fan_speed'     => (is => 'rw', default => sub {0});
 has 'wipe_path'          => (is => 'rw');
 
+sub BUILD {
+    my ($self) = @_;
+    
+    $self->_extrusion_axis($self->print_config->get_extrusion_axis);
+    $self->_retract_lift($self->print_config->retract_lift->[0]);
+}
+
 sub _build_speeds {
     my $self = shift;
     return {
-        map { $_ => 60 * $self->config->get_value("${_}_speed") }
+        map { $_ => 60 * $self->print_config->get_value("${_}_speed") }
             qw(travel perimeter small_perimeter external_perimeter infill
-                solid_infill top_solid_infill support_material bridge gap_fill retract),
+                solid_infill top_solid_infill bridge gap_fill retract),
     };
 }
 
@@ -59,7 +70,6 @@ my %role_speeds = (
     &EXTR_ROLE_BRIDGE                       => 'bridge',
     &EXTR_ROLE_INTERNALBRIDGE               => 'solid_infill',
     &EXTR_ROLE_SKIRT                        => 'perimeter',
-    &EXTR_ROLE_SUPPORTMATERIAL              => 'support_material',
     &EXTR_ROLE_GAPFILL                      => 'gap_fill',
 );
 
@@ -97,29 +107,29 @@ sub change_layer {
     $self->_layer_islands($layer->islands);
     $self->_upper_layer_islands($layer->upper_layer ? $layer->upper_layer->islands : []);
     $self->_layer_overhangs->clear;
-    if ($layer->id > 0 && ($layer->config->overhangs || $self->config->start_perimeters_at_non_overhang)) {
+    if ($layer->id > 0 && ($self->print_config->overhangs || $self->print_config->start_perimeters_at_non_overhang)) {
         $self->_layer_overhangs->append(
             # clone ExPolygons because they come from Surface objects but will be used outside here
             map $_->expolygon, map @{$_->slices->filter_by_type(S_TYPE_BOTTOM)}, @{$layer->regions}
         );
     }
-    if ($self->config->avoid_crossing_perimeters) {
+    if ($self->print_config->avoid_crossing_perimeters) {
         $self->layer_mp(Slic3r::GCode::MotionPlanner->new(
             islands => union_ex([ map @$_, @{$layer->slices} ], 1),
         ));
     }
     
     my $gcode = "";
-    if ($self->config->gcode_flavor =~ /^(?:makerware|sailfish)$/) {
+    if ($self->print_config->gcode_flavor =~ /^(?:makerware|sailfish)$/) {
         $gcode .= sprintf "M73 P%s%s\n",
             int(99 * ($layer->id / ($self->layer_count - 1))),
-            ($self->config->gcode_comments ? ' ; update progress' : '');
+            ($self->print_config->gcode_comments ? ' ; update progress' : '');
     }
-    if ($self->config->first_layer_acceleration) {
+    if ($self->print_config->first_layer_acceleration) {
         if ($layer->id == 0) {
-            $gcode .= $self->set_acceleration($self->config->first_layer_acceleration);
+            $gcode .= $self->set_acceleration($self->print_config->first_layer_acceleration);
         } elsif ($layer->id == 1) {
-            $gcode .= $self->set_acceleration($self->config->default_acceleration);
+            $gcode .= $self->set_acceleration($self->print_config->default_acceleration);
         }
     }
     
@@ -133,7 +143,7 @@ sub move_z {
     
     my $gcode = "";
     
-    $z += $self->config->z_offset;
+    $z += $self->print_config->z_offset;
     my $current_z = $self->z;
     my $nominal_z = defined $current_z ? ($current_z - $self->lifted) : undef;
     
@@ -181,11 +191,11 @@ sub extrude_loop {
     # find candidate starting points
     # start looking for concave vertices not being overhangs
     my @concave = ();
-    if ($self->config->start_perimeters_at_concave_points) {
+    if ($self->print_config->start_perimeters_at_concave_points) {
         @concave = $polygon->concave_points;
     }
     my @candidates = ();
-    if ($self->config->start_perimeters_at_non_overhang) {
+    if ($self->print_config->start_perimeters_at_non_overhang) {
         @candidates = grep !$self->_layer_overhangs->contains_point($_), @concave;
     }
     if (!@candidates) {
@@ -193,7 +203,7 @@ sub extrude_loop {
         @candidates = @concave;
         if (!@candidates) {
             # if none, look for any non-overhang vertex
-            if ($self->config->start_perimeters_at_non_overhang) {
+            if ($self->print_config->start_perimeters_at_non_overhang) {
                 @candidates = grep !$self->_layer_overhangs->contains_point($_), @$polygon;
             }
             if (!@candidates) {
@@ -206,9 +216,9 @@ sub extrude_loop {
     # find the point of the loop that is closest to the current extruder position
     # or randomize if requested
     my $last_pos = $self->last_pos;
-    if ($self->config->randomize_start && $loop->role == EXTR_ROLE_CONTOUR_INTERNAL_PERIMETER) {
-        $last_pos = Slic3r::Point->new(scale $self->config->print_center->[X], scale $self->config->bed_size->[Y]);
-        $last_pos->rotate(rand(2*PI), $self->config->print_center);
+    if ($self->print_config->randomize_start && $loop->role == EXTR_ROLE_CONTOUR_INTERNAL_PERIMETER) {
+        $last_pos = Slic3r::Point->new(scale $self->print_config->print_center->[X], scale $self->print_config->bed_size->[Y]);
+        $last_pos->rotate(rand(2*PI), $self->print_config->print_center);
     }
     
     # split the loop at the starting point and make a path
@@ -224,7 +234,7 @@ sub extrude_loop {
     
     my @paths = ();
     # detect overhanging/bridging perimeters
-    if ($self->layer->config->overhangs && $extrusion_path->is_perimeter && $self->_layer_overhangs->count > 0) {
+    if ($self->layer->print->config->overhangs && $extrusion_path->is_perimeter && $self->_layer_overhangs->count > 0) {
         # get non-overhang paths by subtracting overhangs from the loop
         push @paths,
             map $_->clone,
@@ -234,7 +244,7 @@ sub extrude_loop {
         push @paths,
             map {
                 $_->role(EXTR_ROLE_OVERHANG_PERIMETER);
-                $_->flow_spacing($self->extruder->bridge_flow->width);
+                $_->flow_spacing($self->region->flow(FLOW_ROLE_PERIMETER, undef, 1)->width);
                 $_
             }
             map $_->clone,
@@ -259,7 +269,7 @@ sub extrude_loop {
     $self->wipe_path($extrusion_path->polyline->clone) if $self->enable_wipe;
     
     # make a little move inwards before leaving loop
-    if ($loop->role == EXTR_ROLE_EXTERNAL_PERIMETER && defined $self->layer && $self->layer->object->config->perimeters > 1) {
+    if ($loop->role == EXTR_ROLE_EXTERNAL_PERIMETER && defined $self->layer && $self->region->config->perimeters > 1) {
         # detect angle between last and first segment
         # the side depends on the original winding order of the polygon (left for contours, right for holes)
         my @points = $was_clockwise ? (-2, 1) : (1, -2);
@@ -299,13 +309,13 @@ sub extrude_path {
     
     # adjust acceleration
     my $acceleration;
-    if (!$self->config->first_layer_acceleration || $self->layer->id != 0) {
-        if ($self->config->perimeter_acceleration && $path->is_perimeter) {
-            $acceleration = $self->config->perimeter_acceleration;
-        } elsif ($self->config->infill_acceleration && $path->is_fill) {
-            $acceleration = $self->config->infill_acceleration;
-        } elsif ($self->config->infill_acceleration && $path->is_bridge) {
-            $acceleration = $self->config->bridge_acceleration;
+    if (!$self->print_config->first_layer_acceleration || $self->layer->id != 0) {
+        if ($self->print_config->perimeter_acceleration && $path->is_perimeter) {
+            $acceleration = $self->print_config->perimeter_acceleration;
+        } elsif ($self->print_config->infill_acceleration && $path->is_fill) {
+            $acceleration = $self->print_config->infill_acceleration;
+        } elsif ($self->print_config->infill_acceleration && $path->is_bridge) {
+            $acceleration = $self->print_config->bridge_acceleration;
         }
         $gcode .= $self->set_acceleration($acceleration) if $acceleration;
     }
@@ -322,15 +332,13 @@ sub extrude_path {
     
     # calculate extrusion length per distance unit
     my $e = $self->extruder->e_per_mm3 * $area;
-    $e = 0 if !$self->config->extrusion_axis;
+    $e = 0 if !$self->_extrusion_axis;
     
     # set speed
     $self->speed( $params{speed} || $role_speeds{$path->role} || die "Unknown role: " . $path->role );
     my $F = $self->speeds->{$self->speed} // $self->speed;
     if ($self->layer->id == 0) {
-        $F = $self->config->first_layer_speed =~ /^(\d+(?:\.\d+)?)%$/
-            ? sprintf("%.3f", $F * $1/100)
-            : $self->config->first_layer_speed * 60;
+        $F = $self->print_config->get_abs_value_over('first_layer_speed', $F/60) * 60;
     }
     
     # extrude arc or line
@@ -350,12 +358,12 @@ sub extrude_path {
             $gcode .= sprintf "G1 X%.3f Y%.3f",
                 ($point->x * &Slic3r::SCALING_FACTOR) + $self->shift_x - $self->extruder->extruder_offset->[X], 
                 ($point->y * &Slic3r::SCALING_FACTOR) + $self->shift_y - $self->extruder->extruder_offset->[Y];  #**
-            $gcode .= sprintf(" %s%.5f", $self->config->extrusion_axis, $E)
+            $gcode .= sprintf(" %s%.5f", $self->_extrusion_axis, $E)
                 if $E;
             $gcode .= " F$local_F"
                 if $local_F;
             $gcode .= " ; $description"
-                if $self->config->gcode_comments;
+                if $self->print_config->gcode_comments;
             $gcode .= "\n";
             
             # only include F in the first line
@@ -369,19 +377,14 @@ sub extrude_path {
     $gcode .= ";_BRIDGE_FAN_END\n" if $path->is_bridge;
     $self->last_pos($path->last_point);
     
-    if ($self->config->cooling) {
+    if ($self->print_config->cooling) {
         my $path_time = $path_length / $F * 60;
-        if ($self->layer->id == 0) {
-            $path_time = $self->config->first_layer_speed =~ /^(\d+(?:\.\d+)?)%$/
-                ? $path_time / ($1/100)
-                : $path_length / $self->config->first_layer_speed * 60;
-        }
         $self->elapsed_time($self->elapsed_time + $path_time);
     }
     
     # reset acceleration
-    $gcode .= $self->set_acceleration($self->config->default_acceleration)
-        if $acceleration && $self->config->default_acceleration;
+    $gcode .= $self->set_acceleration($self->print_config->default_acceleration)
+        if $acceleration && $self->print_config->default_acceleration;
     
     return $gcode;
 }
@@ -400,7 +403,7 @@ sub travel_to {
     # skip retraction if the travel move is contained in an island in the current layer
     # *and* in an island in the upper layer (so that the ooze will not be visible)
     if ($travel->length < scale $self->extruder->retract_before_travel
-        || ($self->config->only_retract_when_crossing_perimeters
+        || ($self->print_config->only_retract_when_crossing_perimeters
             && (first { $_->contains_line($travel) } @{$self->_upper_layer_islands})
             && (first { $_->contains_line($travel) } @{$self->_layer_islands}))
         || (defined $role && $role == EXTR_ROLE_SUPPORTMATERIAL && (first { $_->contains_line($travel) } @{$self->layer->support_islands}))
@@ -408,7 +411,7 @@ sub travel_to {
         $self->straight_once(0);
         $self->speed('travel');
         $gcode .= $self->G0($point, undef, 0, $comment || "");
-    } elsif (!$self->config->avoid_crossing_perimeters || $self->straight_once) {
+    } elsif (!$self->print_config->avoid_crossing_perimeters || $self->straight_once) {
         $self->straight_once(0);
         $gcode .= $self->retract;
         $self->speed('travel');
@@ -441,7 +444,7 @@ sub _plan {
     my @travel = @{$mp->shortest_path($self->last_pos, $point)->lines};
     
     # if the path is not contained in a single island we need to retract
-    my $need_retract = !$self->config->only_retract_when_crossing_perimeters;
+    my $need_retract = !$self->print_config->only_retract_when_crossing_perimeters;
     if (!$need_retract) {
         $need_retract = 1;
         foreach my $island (@{$self->_upper_layer_islands}) {
@@ -481,14 +484,14 @@ sub retract {
     if ($self->extruder->wipe && $self->wipe_path) {
         my @points = @{$self->wipe_path};
         $wipe_path = Slic3r::Polyline->new($self->last_pos, @{$self->wipe_path}[1..$#{$self->wipe_path}]);
-        $wipe_path->clip_end($wipe_path->length - $self->extruder->scaled_wipe_distance($self->config->travel_speed));
+        $wipe_path->clip_end($wipe_path->length - $self->extruder->scaled_wipe_distance($self->print_config->travel_speed));
     }
     
     # prepare moves
     my $retract = [undef, undef, -$length, $comment];
-    my $lift    = ($self->extruder->retract_lift == 0 || defined $params{move_z}) && !$self->lifted
+    my $lift    = ($self->_retract_lift == 0 || defined $params{move_z}) && !$self->lifted
         ? undef
-        : [undef, $self->z + $self->extruder->retract_lift, 0, 'lift plate during travel'];
+        : [undef, $self->z + $self->_retract_lift, 0, 'lift plate during travel'];
     
     # check that we have a positive wipe length
     if ($wipe_path) {
@@ -500,7 +503,7 @@ sub retract {
             my $segment_length = $line->length;
             # reduce retraction length a bit to avoid effective retraction speed to be greater than the configured one
             # due to rounding
-            my $e = $retract->[2] * ($segment_length / $self->extruder->scaled_wipe_distance($self->config->travel_speed)) * 0.95;
+            my $e = $retract->[2] * ($segment_length / $self->extruder->scaled_wipe_distance($self->print_config->travel_speed)) * 0.95;
             $retracted += $e;
             $gcode .= $self->G1($line->b, undef, $e, $retract->[3] . ";_WIPE");
         }
@@ -510,7 +513,7 @@ sub retract {
             $self->speed('retract');
             $gcode .= $self->G1(undef, undef, $retract->[2] - $retracted, $comment);
         }
-    } elsif ($self->config->use_firmware_retraction) {
+    } elsif ($self->print_config->use_firmware_retraction) {
         $gcode .= "G10 ; retract\n";
     } else {
         $self->speed('retract');
@@ -518,23 +521,23 @@ sub retract {
     }
     if (!$self->lifted) {
         $self->speed('travel');
-        if (defined $params{move_z} && $self->extruder->retract_lift > 0) {
-            my $travel = [undef, $params{move_z} + $self->extruder->retract_lift, 0, 'move to next layer (' . $self->layer->id . ') and lift'];
+        if (defined $params{move_z} && $self->_retract_lift > 0) {
+            my $travel = [undef, $params{move_z} + $self->_retract_lift, 0, 'move to next layer (' . $self->layer->id . ') and lift'];
             $gcode .= $self->G0(@$travel);
-            $self->lifted($self->extruder->retract_lift);
+            $self->lifted($self->_retract_lift);
         } elsif ($lift) {
             $gcode .= $self->G1(@$lift);
         }
     }
     $self->extruder->retracted($self->extruder->retracted + $length);
     $self->extruder->restart_extra($restart_extra);
-    $self->lifted($self->extruder->retract_lift) if $lift;
+    $self->lifted($self->_retract_lift) if $lift;
     
     # reset extrusion distance during retracts
     # this makes sure we leave sufficient precision in the firmware
     $gcode .= $self->reset_e;
     
-    $gcode .= "M103 ; extruder off\n" if $self->config->gcode_flavor eq 'makerware';
+    $gcode .= "M103 ; extruder off\n" if $self->print_config->gcode_flavor eq 'makerware';
     
     return $gcode;
 }
@@ -543,7 +546,7 @@ sub unretract {
     my ($self) = @_;
     
     my $gcode = "";
-    $gcode .= "M101 ; extruder on\n" if $self->config->gcode_flavor eq 'makerware';
+    $gcode .= "M101 ; extruder on\n" if $self->print_config->gcode_flavor eq 'makerware';
     
     if ($self->lifted) {
         $self->speed('travel');
@@ -554,15 +557,15 @@ sub unretract {
     my $to_unretract = $self->extruder->retracted + $self->extruder->restart_extra;
     if ($to_unretract) {
         $self->speed('retract');
-        if ($self->config->use_firmware_retraction) {
+        if ($self->print_config->use_firmware_retraction) {
             $gcode .= "G11 ; unretract\n";
-        } elsif ($self->config->extrusion_axis) {
+        } elsif ($self->_extrusion_axis) {
             # use G1 instead of G0 because G0 will blend the restart with the previous travel move
             $gcode .= sprintf "G1 %s%.5f F%.3f",
-                $self->config->extrusion_axis,
+                $self->_extrusion_axis,
                 $self->extruder->extrude($to_unretract),
                 $self->extruder->retract_speed_mm_min;
-            $gcode .= " ; compensate retraction" if $self->config->gcode_comments;
+            $gcode .= " ; compensate retraction" if $self->print_config->gcode_comments;
             $gcode .= "\n";
         }
         $self->extruder->retracted(0);
@@ -574,11 +577,11 @@ sub unretract {
 
 sub reset_e {
     my ($self) = @_;
-    return "" if $self->config->gcode_flavor =~ /^(?:mach3|makerware|sailfish)$/;
+    return "" if $self->print_config->gcode_flavor =~ /^(?:mach3|makerware|sailfish)$/;
     
     $self->extruder->E(0) if $self->extruder;
-    return sprintf "G92 %s0%s\n", $self->config->extrusion_axis, ($self->config->gcode_comments ? ' ; reset extrusion distance' : '')
-        if $self->config->extrusion_axis && !$self->config->use_relative_e_distances;
+    return sprintf "G92 %s0%s\n", $self->_extrusion_axis, ($self->print_config->gcode_comments ? ' ; reset extrusion distance' : '')
+        if $self->_extrusion_axis && !$self->print_config->use_relative_e_distances;
 }
 
 sub set_acceleration {
@@ -586,12 +589,12 @@ sub set_acceleration {
     return "" if !$acceleration;
     
     return sprintf "M204 S%s%s\n",
-        $acceleration, ($self->config->gcode_comments ? ' ; adjust acceleration' : '');
+        $acceleration, ($self->print_config->gcode_comments ? ' ; adjust acceleration' : '');
 }
 
 sub G0 {
     my $self = shift;
-    return $self->G1(@_) if !($self->config->g0 || $self->config->gcode_flavor eq 'mach3');
+    return $self->G1(@_) if !($self->print_config->g0 || $self->print_config->gcode_flavor eq 'mach3');
     return $self->_G0_G1("G0", @_);
 }
 
@@ -628,11 +631,11 @@ sub _Gx {
     $gcode .= sprintf " F%.3f", $F;
     
     # output extrusion distance
-    if ($e && $self->config->extrusion_axis) {
-        $gcode .= sprintf " %s%.5f", $self->config->extrusion_axis, $self->extruder->extrude($e);
+    if ($e && $self->_extrusion_axis) {
+        $gcode .= sprintf " %s%.5f", $self->_extrusion_axis, $self->extruder->extrude($e);
     }
     
-    $gcode .= " ; $comment" if $comment && $self->config->gcode_comments;
+    $gcode .= " ; $comment" if $comment && $self->print_config->gcode_comments;
     return "$gcode\n";
 }
 
@@ -653,8 +656,8 @@ sub set_extruder {
     $gcode .= $self->retract(toolchange => 1) if defined $self->extruder;
     
     # append custom toolchange G-code
-    if (defined $self->extruder && $self->config->toolchange_gcode) {
-        $gcode .= sprintf "%s\n", $self->replace_variables($self->config->toolchange_gcode, {
+    if (defined $self->extruder && $self->print_config->toolchange_gcode) {
+        $gcode .= sprintf "%s\n", $self->replace_variables($self->print_config->toolchange_gcode, {
             previous_extruder   => $self->extruder->id,
             next_extruder       => $extruder->id,
         });
@@ -669,24 +672,24 @@ sub set_extruder {
             ? $self->extruder->first_layer_temperature
             : $self->extruder->temperature;
         # we assume that heating is always slower than cooling, so no need to block
-        $gcode .= $self->set_temperature($temp + $self->config->standby_temperature_delta, 0);
+        $gcode .= $self->set_temperature($temp + $self->print_config->standby_temperature_delta, 0);
     }
     
     # set the new extruder
     $self->extruder($extruder);
     $gcode .= sprintf "%s%d%s\n", 
-        ($self->config->gcode_flavor eq 'makerware'
+        ($self->print_config->gcode_flavor eq 'makerware'
             ? 'M135 T'
-            : $self->config->gcode_flavor eq 'sailfish'
+            : $self->print_config->gcode_flavor eq 'sailfish'
                 ? 'M108 T'
                 : 'T'),
         $extruder->id,
-        ($self->config->gcode_comments ? ' ; change extruder' : '');
+        ($self->print_config->gcode_comments ? ' ; change extruder' : '');
     
     $gcode .= $self->reset_e;
     
     # set the new extruder to the operating temperature
-    if ($self->config->ooze_prevention) {
+    if ($self->print_config->ooze_prevention) {
         my $temp = defined $self->layer && $self->layer->id == 0
             ? $self->extruder->first_layer_temperature
             : $self->extruder->temperature;
@@ -702,18 +705,18 @@ sub set_fan {
     if ($self->last_fan_speed != $speed || $dont_save) {
         $self->last_fan_speed($speed) if !$dont_save;
         if ($speed == 0) {
-            my $code = $self->config->gcode_flavor eq 'teacup'
+            my $code = $self->print_config->gcode_flavor eq 'teacup'
                 ? 'M106 S0'
-                : $self->config->gcode_flavor =~ /^(?:makerware|sailfish)$/
+                : $self->print_config->gcode_flavor =~ /^(?:makerware|sailfish)$/
                     ? 'M127'
                     : 'M107';
-            return sprintf "$code%s\n", ($self->config->gcode_comments ? ' ; disable fan' : '');
+            return sprintf "$code%s\n", ($self->print_config->gcode_comments ? ' ; disable fan' : '');
         } else {
-            if ($self->config->gcode_flavor =~ /^(?:makerware|sailfish)$/) {
-                return sprintf "M126%s\n", ($self->config->gcode_comments ? ' ; enable fan' : '');
+            if ($self->print_config->gcode_flavor =~ /^(?:makerware|sailfish)$/) {
+                return sprintf "M126%s\n", ($self->print_config->gcode_comments ? ' ; enable fan' : '');
             } else {
-                return sprintf "M106 %s%d%s\n", ($self->config->gcode_flavor eq 'mach3' ? 'P' : 'S'),
-                    (255 * $speed / 100), ($self->config->gcode_comments ? ' ; enable fan' : '');
+                return sprintf "M106 %s%d%s\n", ($self->print_config->gcode_flavor eq 'mach3' ? 'P' : 'S'),
+                    (255 * $speed / 100), ($self->print_config->gcode_comments ? ' ; enable fan' : '');
             }
         }
     }
@@ -723,17 +726,17 @@ sub set_fan {
 sub set_temperature {
     my ($self, $temperature, $wait, $tool) = @_;
     
-    return "" if $wait && $self->config->gcode_flavor =~ /^(?:makerware|sailfish)$/;
+    return "" if $wait && $self->print_config->gcode_flavor =~ /^(?:makerware|sailfish)$/;
     
-    my ($code, $comment) = ($wait && $self->config->gcode_flavor ne 'teacup')
+    my ($code, $comment) = ($wait && $self->print_config->gcode_flavor ne 'teacup')
         ? ('M109', 'wait for temperature to be reached')
         : ('M104', 'set temperature');
     my $gcode = sprintf "$code %s%d %s; $comment\n",
-        ($self->config->gcode_flavor eq 'mach3' ? 'P' : 'S'), $temperature,
-        (defined $tool && ($self->multiple_extruders || $self->config->gcode_flavor =~ /^(?:makerware|sailfish)$/)) ? "T$tool " : "";
+        ($self->print_config->gcode_flavor eq 'mach3' ? 'P' : 'S'), $temperature,
+        (defined $tool && ($self->multiple_extruders || $self->print_config->gcode_flavor =~ /^(?:makerware|sailfish)$/)) ? "T$tool " : "";
     
     $gcode .= "M116 ; wait for temperature to be reached\n"
-        if $self->config->gcode_flavor eq 'teacup' && $wait;
+        if $self->print_config->gcode_flavor eq 'teacup' && $wait;
     
     return $gcode;
 }
@@ -741,21 +744,21 @@ sub set_temperature {
 sub set_bed_temperature {
     my ($self, $temperature, $wait) = @_;
     
-    my ($code, $comment) = ($wait && $self->config->gcode_flavor ne 'teacup')
-        ? (($self->config->gcode_flavor =~ /^(?:makerware|sailfish)$/ ? 'M109' : 'M190'), 'wait for bed temperature to be reached')
+    my ($code, $comment) = ($wait && $self->print_config->gcode_flavor ne 'teacup')
+        ? (($self->print_config->gcode_flavor =~ /^(?:makerware|sailfish)$/ ? 'M109' : 'M190'), 'wait for bed temperature to be reached')
         : ('M140', 'set bed temperature');
     my $gcode = sprintf "$code %s%d ; $comment\n",
-        ($self->config->gcode_flavor eq 'mach3' ? 'P' : 'S'), $temperature;
+        ($self->print_config->gcode_flavor eq 'mach3' ? 'P' : 'S'), $temperature;
     
     $gcode .= "M116 ; wait for bed temperature to be reached\n"
-        if $self->config->gcode_flavor eq 'teacup' && $wait;
+        if $self->print_config->gcode_flavor eq 'teacup' && $wait;
     
     return $gcode;
 }
 
 sub replace_variables {
     my ($self, $string, $extra) = @_;
-    return $self->config->replace_options($string, { %{$self->extra_variables}, %{ $extra || {} } });
+    return $self->print_config->replace_options($string, { %{$self->extra_variables}, %{ $extra || {} } });
 }
 
 1;
diff --git a/lib/Slic3r/GCode/ArcFitting.pm b/lib/Slic3r/GCode/ArcFitting.pm
index 52078bdc0..fe45bdb36 100644
--- a/lib/Slic3r/GCode/ArcFitting.pm
+++ b/lib/Slic3r/GCode/ArcFitting.pm
@@ -104,7 +104,7 @@ sub flush_path {
         $gcode .= sprintf " I%.3f J%.3f", map { unscale($arc_center->[$_] - $cur_path->[0][$_]) } (X,Y);
         
         my $E = 0;  # TODO: compute E using $length
-        $gcode .= sprintf(" %s%.5f", $self->config->extrusion_axis, $E)
+        $gcode .= sprintf(" %s%.5f", $self->config->get_extrusion_axis, $E)
             if $E;
         
         my $F = 0;  # TODO: extract F from original moves
diff --git a/lib/Slic3r/GCode/CoolingBuffer.pm b/lib/Slic3r/GCode/CoolingBuffer.pm
index d95b5fec1..91119fe3e 100644
--- a/lib/Slic3r/GCode/CoolingBuffer.pm
+++ b/lib/Slic3r/GCode/CoolingBuffer.pm
@@ -1,7 +1,7 @@
 package Slic3r::GCode::CoolingBuffer;
 use Moo;
 
-has 'config'    => (is => 'ro', required => 1);
+has 'config'    => (is => 'ro', required => 1);  # Slic3r::Config::Print
 has 'gcodegen'  => (is => 'ro', required => 1);
 has 'gcode'     => (is => 'rw', default => sub {""});
 has 'elapsed_time' => (is => 'rw', default => sub {0});
diff --git a/lib/Slic3r/GCode/Layer.pm b/lib/Slic3r/GCode/Layer.pm
index f888ddac2..f64b6a1ba 100644
--- a/lib/Slic3r/GCode/Layer.pm
+++ b/lib/Slic3r/GCode/Layer.pm
@@ -19,24 +19,24 @@ has '_last_obj_copy'                => (is => 'rw');
 sub _build_spiralvase {
     my $self = shift;
     
-    return $self->gcodegen->config->spiral_vase
-        ? Slic3r::GCode::SpiralVase->new(config => $self->gcodegen->config)
+    return $self->print->config->spiral_vase
+        ? Slic3r::GCode::SpiralVase->new(config => $self->print->config)
         : undef;
 }
 
 sub _build_vibration_limit {
     my $self = shift;
     
-    return $self->gcodegen->config->vibration_limit
-        ? Slic3r::GCode::VibrationLimit->new(config => $self->gcodegen->config)
+    return $self->print->config->vibration_limit
+        ? Slic3r::GCode::VibrationLimit->new(config => $self->print->config)
         : undef;
 }
 
 sub _build_arc_fitting {
     my $self = shift;
     
-    return $self->gcodegen->config->gcode_arcs
-        ? Slic3r::GCode::ArcFitting->new(config => $self->gcodegen->config)
+    return $self->print->config->gcode_arcs
+        ? Slic3r::GCode::ArcFitting->new(config => $self->print->config)
         : undef;
 }
 
@@ -45,44 +45,46 @@ sub process_layer {
     my ($layer, $object_copies) = @_;
     my $gcode = "";
     
+    my $object = $layer->object;
+    
     # check whether we're going to apply spiralvase logic
     my $spiralvase = defined $self->spiralvase
-        && ($layer->id > 0 || $self->gcodegen->config->brim_width == 0)
-        && ($layer->id >= $self->gcodegen->config->skirt_height && $self->gcodegen->config->skirt_height != -1)
-        && ($layer->id >= $self->gcodegen->config->bottom_solid_layers);
+        && ($layer->id > 0 || $self->print->config->brim_width == 0)
+        && ($layer->id >= $self->print->config->skirt_height && $self->print->config->skirt_height != -1)
+        && !defined(first { $_->config->bottom_solid_layers > $layer->id } @{$layer->regions});
     
     # if we're going to apply spiralvase to this layer, disable loop clipping
     $self->gcodegen->enable_loop_clipping(!$spiralvase);
     
     if (!$self->second_layer_things_done && $layer->id == 1) {
-        for my $t (grep $self->extruders->[$_], 0 .. $#{$self->gcodegen->config->temperature}) {
+        for my $t (grep $self->extruders->[$_], 0 .. $#{$self->print->config->temperature}) {
             $gcode .= $self->gcodegen->set_temperature($self->extruders->[$t]->temperature, 0, $t)
                 if $self->print->extruders->[$t]->temperature && $self->extruders->[$t]->temperature != $self->extruders->[$t]->first_layer_temperature;
         }
-        $gcode .= $self->gcodegen->set_bed_temperature($self->gcodegen->config->bed_temperature)
-            if $self->gcodegen->config->bed_temperature && $self->gcodegen->config->bed_temperature != $self->gcodegen->config->first_layer_bed_temperature;
+        $gcode .= $self->gcodegen->set_bed_temperature($self->print->config->bed_temperature)
+            if $self->print->config->bed_temperature && $self->print->config->bed_temperature != $self->print->config->first_layer_bed_temperature;
         $self->second_layer_things_done(1);
     }
     
     # set new layer - this will change Z and force a retraction if retract_layer_change is enabled
     $gcode .= $self->gcodegen->change_layer($layer);
-    $gcode .= $self->gcodegen->replace_variables($self->gcodegen->config->layer_gcode, {
+    $gcode .= $self->gcodegen->replace_variables($self->print->config->layer_gcode, {
         layer_num => $self->gcodegen->layer->id,
-    }) . "\n" if $self->gcodegen->config->layer_gcode;
+    }) . "\n" if $self->print->config->layer_gcode;
     
     # extrude skirt
-    if (((values %{$self->skirt_done}) < $self->gcodegen->config->skirt_height || $self->gcodegen->config->skirt_height == -1)
+    if (((values %{$self->skirt_done}) < $self->print->config->skirt_height || $self->print->config->skirt_height == -1)
         && !$self->skirt_done->{$layer->print_z}) {
         $self->gcodegen->set_shift(@{$self->shift});
         $gcode .= $self->gcodegen->set_extruder($self->extruders->[0]);
         # skip skirt if we have a large brim
-        if ($layer->id < $self->gcodegen->config->skirt_height || $self->gcodegen->config->skirt_height == -1) {
+        if ($layer->id < $self->print->config->skirt_height || $self->print->config->skirt_height == -1) {
             # distribute skirt loops across all extruders
             my @skirt_loops = @{$self->print->skirt};
             for my $i (0 .. $#skirt_loops) {
                 # when printing layers > 0 ignore 'min_skirt_length' and 
                 # just use the 'skirts' setting; also just use the current extruder
-                last if ($layer->id > 0) && ($i >= $self->gcodegen->config->skirts);
+                last if ($layer->id > 0) && ($i >= $self->print->config->skirts);
                 $gcode .= $self->gcodegen->set_extruder($self->extruders->[ ($i/@{$self->extruders}) % @{$self->extruders} ])
                     if $layer->id == 0;
                 $gcode .= $self->gcodegen->extrude_loop($skirt_loops[$i], 'skirt');
@@ -94,7 +96,7 @@ sub process_layer {
     
     # extrude brim
     if (!$self->brim_done) {
-        $gcode .= $self->gcodegen->set_extruder($self->extruders->[$self->gcodegen->config->support_material_extruder-1]);
+        $gcode .= $self->gcodegen->set_extruder($self->extruders->[$self->print->objects->[0]->config->support_material_extruder-1]);
         $self->gcodegen->set_shift(@{$self->shift});
         $gcode .= $self->gcodegen->extrude_loop($_, 'brim') for @{$self->print->brim};
         $self->brim_done(1);
@@ -109,15 +111,17 @@ sub process_layer {
         
         # extrude support material before other things because it might use a lower Z
         # and also because we avoid travelling on other things when printing it
-        if ($self->print->has_support_material && $layer->isa('Slic3r::Layer::Support')) {
+        if ($layer->isa('Slic3r::Layer::Support')) {
             if ($layer->support_interface_fills->count > 0) {
-                $gcode .= $self->gcodegen->set_extruder($self->extruders->[$self->gcodegen->config->support_material_interface_extruder-1]);
-                $gcode .= $self->gcodegen->extrude_path($_, 'support material interface') 
+                $gcode .= $self->gcodegen->set_extruder($self->extruders->[$object->config->support_material_interface_extruder-1]);
+                my %params = (speed => $object->config->support_material_speed*60);
+                $gcode .= $self->gcodegen->extrude_path($_, 'support material interface', %params) 
                     for @{$layer->support_interface_fills->chained_path_from($self->gcodegen->last_pos, 0)}; 
             }
             if ($layer->support_fills->count > 0) {
-                $gcode .= $self->gcodegen->set_extruder($self->extruders->[$self->gcodegen->config->support_material_extruder-1]);
-                $gcode .= $self->gcodegen->extrude_path($_, 'support material') 
+                $gcode .= $self->gcodegen->set_extruder($self->extruders->[$object->config->support_material_extruder-1]);
+                my %params = (speed => $object->config->support_material_speed*60);
+                $gcode .= $self->gcodegen->extrude_path($_, 'support material', %params) 
                     for @{$layer->support_fills->chained_path_from($self->gcodegen->last_pos, 0)};
             }
         }
@@ -126,16 +130,17 @@ sub process_layer {
         my @region_ids = 0 .. ($self->print->regions_count-1);
         if ($self->gcodegen->multiple_extruders) {
             my $last_extruder = $self->gcodegen->extruder;
-            my $best_region_id = first { $self->print->regions->[$_]->extruders->{perimeter} eq $last_extruder } @region_ids;
+            my $best_region_id = first { $self->print->regions->[$_]->config->perimeter_extruder-1 eq $last_extruder } @region_ids;
             @region_ids = ($best_region_id, grep $_ != $best_region_id, @region_ids) if $best_region_id;
         }
         
         foreach my $region_id (@region_ids) {
             my $layerm = $layer->regions->[$region_id] or next;
             my $region = $self->print->regions->[$region_id];
+            $self->gcodegen->region($region);
             
             my @islands = ();
-            if ($self->gcodegen->config->avoid_crossing_perimeters) {
+            if ($self->print->config->avoid_crossing_perimeters) {
                 push @islands, { perimeters => [], fills => [] }
                     for 1 .. (@{$layer->slices} || 1);  # make sure we have at least one island hash to avoid failure of the -1 subscript below
                 PERIMETER: foreach my $perimeter (@{$layerm->perimeters}) {
@@ -166,8 +171,8 @@ sub process_layer {
             foreach my $island (@islands) {
                 # give priority to infill if we were already using its extruder and it wouldn't
                 # be good for perimeters
-                if ($self->gcodegen->config->infill_first
-                    || ($self->gcodegen->multiple_extruders && $region->extruders->{infill} eq $self->gcodegen->extruder) && $region->extruders->{infill} ne $region->extruders->{perimeter}) {
+                if ($self->print->config->infill_first
+                    || ($self->gcodegen->multiple_extruders && $region->config->infill_extruder-1 == $self->gcodegen->extruder->id && $region->config->infill_extruder != $region->config->perimeter_extruder)) {
                     $gcode .= $self->_extrude_infill($island, $region);
                     $gcode .= $self->_extrude_perimeters($island, $region);
                 } else {
@@ -184,11 +189,11 @@ sub process_layer {
     
     # apply vibration limit if enabled
     $gcode = $self->vibration_limit->process($gcode)
-        if $self->gcodegen->config->vibration_limit != 0;
+        if $self->print->config->vibration_limit != 0;
     
     # apply arc fitting if enabled
     $gcode = $self->arc_fitting->process($gcode)
-        if $self->gcodegen->config->gcode_arcs;
+        if $self->print->config->gcode_arcs;
     
     return $gcode;
 }
@@ -200,7 +205,7 @@ sub _extrude_perimeters {
     return "" if !@{ $island->{perimeters} };
     
     my $gcode = "";
-    $gcode .= $self->gcodegen->set_extruder($region->extruders->{perimeter});
+    $gcode .= $self->gcodegen->set_extruder($self->extruders->[$region->config->perimeter_extruder-1]);
     $gcode .= $self->gcodegen->extrude($_, 'perimeter') for @{ $island->{perimeters} };
     return $gcode;
 }
@@ -212,7 +217,7 @@ sub _extrude_infill {
     return "" if !@{ $island->{fills} };
     
     my $gcode = "";
-    $gcode .= $self->gcodegen->set_extruder($region->extruders->{infill});
+    $gcode .= $self->gcodegen->set_extruder($self->extruders->[$region->config->infill_extruder-1]);
     for my $fill (@{ $island->{fills} }) {
         if ($fill->isa('Slic3r::ExtrusionPath::Collection')) {
             $gcode .= $self->gcodegen->extrude($_, 'fill') 
diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm
index d490597db..d44a06b6c 100644
--- a/lib/Slic3r/Print.pm
+++ b/lib/Slic3r/Print.pm
@@ -12,9 +12,9 @@ use Slic3r::Geometry::Clipper qw(diff_ex union_ex union_pt intersection_ex inter
     offset2 union union_pt_chained JT_ROUND JT_SQUARE);
 use Slic3r::Print::State ':steps';
 
-has 'config'                 => (is => 'rw', default => sub { Slic3r::Config::Print->new }, trigger => \&init_config);
-has 'default_object_config'  => (is => 'rw', default => sub { Slic3r::Config::PrintObject->new });
-has 'default_region_config'  => (is => 'rw', default => sub { Slic3r::Config::PrintRegion->new });
+has 'config'                 => (is => 'ro', default => sub { Slic3r::Config::Print->new });
+has 'default_object_config'  => (is => 'ro', default => sub { Slic3r::Config::PrintObject->new });
+has 'default_region_config'  => (is => 'ro', default => sub { Slic3r::Config::PrintRegion->new });
 has 'extra_variables'        => (is => 'rw', default => sub {{}});
 has 'objects'                => (is => 'rw', default => sub {[]});
 has 'status_cb'              => (is => 'rw');
@@ -28,52 +28,12 @@ has 'skirt' => (is => 'rw', default => sub { Slic3r::ExtrusionPath::Collection->
 # ordered collection of extrusion paths to build a brim
 has 'brim' => (is => 'rw', default => sub { Slic3r::ExtrusionPath::Collection->new });
 
-sub BUILD {
-    my $self = shift;
-    
-    # call this manually because the 'default' coderef doesn't trigger the trigger
-    $self->init_config;
-}
-
-# this method needs to be idempotent
-sub init_config {
-    my $self = shift;
-    
-    # legacy with existing config files
-    $self->config->set('first_layer_height', $self->config->layer_height)
-        if !$self->config->first_layer_height;
-    $self->config->set_ifndef('small_perimeter_speed',  $self->config->perimeter_speed);
-    $self->config->set_ifndef('bridge_speed',           $self->config->infill_speed);
-    $self->config->set_ifndef('solid_infill_speed',     $self->config->infill_speed);
-    $self->config->set_ifndef('top_solid_infill_speed', $self->config->solid_infill_speed);
-    $self->config->set_ifndef('top_solid_layers',       $self->config->solid_layers);
-    $self->config->set_ifndef('bottom_solid_layers',    $self->config->solid_layers);
-    
-    # G-code flavors
-    $self->config->set('extrusion_axis', 'A') if $self->config->gcode_flavor eq 'mach3';
-    $self->config->set('extrusion_axis', '')  if $self->config->gcode_flavor eq 'no-extrusion';
-    
-    # enforce some settings when spiral_vase is set
-    if ($self->config->spiral_vase) {
-        $self->config->set('perimeters', 1);
-        $self->config->set('fill_density', 0);
-        $self->config->set('top_solid_layers', 0);
-        $self->config->set('support_material', 0);
-        $self->config->set('support_material_enforce_layers', 0);
-        $self->config->set('retract_layer_change', [0]);  # TODO: only apply this to the spiral layers
-    }
-    
-    # force all retraction lift values to be the same
-    $self->config->set('retract_lift', [ map $self->config->retract_lift->[0], @{$self->config->retract_lift} ]);
-}
-
 sub apply_config {
     my ($self, $config) = @_;
     
     $self->config->apply_dynamic($config);
     $self->default_object_config->apply_dynamic($config);
     $self->default_region_config->apply_dynamic($config);
-    $self->init_config;
 }
 
 sub has_support_material {
@@ -261,11 +221,21 @@ sub init_extruders {
     }
     
     for my $extruder_id (keys %{{ map {$_ => 1} @used_extruders }}) {
+        # make sure print config contains a value for all extruders
+        my %extruder_config = ();
+        foreach my $opt_key (@{&Slic3r::Extruder::OPTIONS}) {
+            my $value = $self->config->get($opt_key);
+            if (!defined $value->[$extruder_id]) {
+                $value->[$extruder_id] = $value->[0];
+                $self->config->set($opt_key, $value);
+            }
+            $extruder_config{$opt_key} = $value->[$extruder_id];
+        }
+        
         $self->extruders->[$extruder_id] = Slic3r::Extruder->new(
-            config => $self->config,
             id => $extruder_id,
-            map { $_ => $self->config->get($_)->[$extruder_id] // $self->config->get($_)->[0] } #/
-                @{&Slic3r::Extruder::OPTIONS}
+            use_relative_e_distances => $self->config->use_relative_e_distances,
+            %extruder_config,
         );
     }
     
@@ -614,16 +584,17 @@ sub make_skirt {
     # skirt may be printed on several layers, having distinct layer heights,
     # but loops must be aligned so can't vary width/spacing
     # TODO: use each extruder's own flow
+    my $region0_config = $self->regions->[0]->config;
+    my $first_layer_height = $self->objects->[0]->config->get_value('first_layer_height');
     my $flow = Slic3r::Flow->new(
-        width               => ($self->config->first_layer_extrusion_width || $self->config->perimeter_extrusion_width),
+        width               => ($region0_config->first_layer_extrusion_width || $region0_config->perimeter_extrusion_width),
         role                => FLOW_ROLE_PERIMETER,
         nozzle_diameter     => $self->config->nozzle_diameter->[0],
-        layer_height        => $self->config->get_abs_value('first_layer_height'),
+        layer_height        => $first_layer_height,
         bridge_flow_ratio   => 0,
     );
     my $spacing = $flow->spacing;
     
-    my $first_layer_height = $self->config->get_value('first_layer_height');
     my @extruders_e_per_mm = ();
     my $extruder_idx = 0;
     
@@ -664,10 +635,10 @@ sub make_brim {
     
     # brim is only printed on first layer and uses support material extruder
     my $flow = Slic3r::Flow->new(
-        width               => ($self->config->first_layer_extrusion_width || $self->config->perimeter_extrusion_width),
+        width               => ($self->regions->[0]->config->first_layer_extrusion_width || $self->regions->[0]->config->perimeter_extrusion_width),
         role                => FLOW_ROLE_PERIMETER,
-        nozzle_diameter     => $self->config->nozzle_diameter->[ $self->config->support_material_extruder-1 ],
-        layer_height        => $self->config->get_abs_value('first_layer_height'),
+        nozzle_diameter     => $self->config->nozzle_diameter->[ $self->objects->[0]->config->support_material_extruder-1 ],
+        layer_height        => $self->objects->[0]->config->get_abs_value('first_layer_height'),
         bridge_flow_ratio   => 0,
     );
     
@@ -737,34 +708,28 @@ sub write_gcode {
     print $fh "; $_\n" foreach split /\R/, $self->config->notes;
     print $fh "\n" if $self->config->notes;
     
-    for (qw(layer_height perimeters top_solid_layers bottom_solid_layers fill_density perimeter_speed infill_speed travel_speed)) {
-        printf $fh "; %s = %s\n", $_, $self->config->$_;
-    }
-    for (qw(nozzle_diameter filament_diameter extrusion_multiplier)) {
-        printf $fh "; %s = %s\n", $_, $self->config->$_->[0];
-    }
-    
+    my $layer_height = $self->objects->[0]->config->layer_height;
     for my $region_id (0..$#{$self->regions}) {
         printf $fh "; perimeters extrusion width = %.2fmm\n",
-            $self->regions->[$region_id]->flow(FLOW_ROLE_PERIMETER)->width;
+            $self->regions->[$region_id]->flow(FLOW_ROLE_PERIMETER, $layer_height)->width;
         printf $fh "; infill extrusion width = %.2fmm\n",
-            $self->regions->[$region_id]->flow(FLOW_ROLE_INFILL)->width;
+            $self->regions->[$region_id]->flow(FLOW_ROLE_INFILL, $layer_height)->width;
         printf $fh "; solid infill extrusion width = %.2fmm\n",
-            $self->regions->[$region_id]->flow(FLOW_ROLE_SOLID_INFILL)->width;
+            $self->regions->[$region_id]->flow(FLOW_ROLE_SOLID_INFILL, $layer_height)->width;
         printf $fh "; top infill extrusion width = %.2fmm\n",
-            $self->regions->[$region_id]->flow(FLOW_ROLE_TOP_SOLID_INFILL)->width;
+            $self->regions->[$region_id]->flow(FLOW_ROLE_TOP_SOLID_INFILL, $layer_height)->width;
         printf $fh "; support material extrusion width = %.2fmm\n",
             $self->objects->[0]->support_material_flow->width
             if $self->has_support_material;
         printf $fh "; first layer extrusion width = %.2fmm\n",
-            $self->regions->[$region_id]->flow(FLOW_ROLE_PERIMETER, 0, 1)->width
-            if ($self->regions->[$region_id]->config->first_layer_extrusion_width != 0);
+            $self->regions->[$region_id]->flow(FLOW_ROLE_PERIMETER, $layer_height, 0, 1)->width
+            if ($self->regions->[$region_id]->config->first_layer_extrusion_width ne '0');
         print  $fh "\n";
     }
     
     # set up our extruder object
     my $gcodegen = Slic3r::GCode->new(
-        config              => $self->config,
+        print_config        => $self->config,
         extra_variables     => $self->extra_variables,
         extruders           => $self->extruders,    # we should only pass the *used* extruders (but maintain the Tx indices right!)
         layer_count         => $self->layer_count,
@@ -941,13 +906,11 @@ sub write_gcode {
             $extruder->absolute_E, $extruder->extruded_volume/1000;
     }
     
-    if ($self->config->gcode_comments) {
-        # append full config
-        print $fh "\n";
-        foreach my $opt_key (sort @{$self->config->get_keys}) {
-            next if $Slic3r::Config::Options->{$opt_key}{shortcut};
-            printf $fh "; %s = %s\n", $opt_key, $self->config->serialize($opt_key);
-        }
+    # append full config
+    print $fh "\n";
+    foreach my $opt_key (sort @{$self->config->get_keys}) {
+        next if $Slic3r::Config::Options->{$opt_key}{shortcut};
+        printf $fh "; %s = %s\n", $opt_key, $self->config->serialize($opt_key);
     }
     
     # close our gcode file
diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm
index 600a0dcc8..bf4c2efba 100644
--- a/lib/Slic3r/Print/Object.pm
+++ b/lib/Slic3r/Print/Object.pm
@@ -521,13 +521,16 @@ sub clip_fill_surfaces {
 
 sub bridge_over_infill {
     my $self = shift;
-    return if $self->config->fill_density == 1;
     
-    for my $layer_id (1..$#{$self->layers}) {
-        my $layer       = $self->layers->[$layer_id];
-        my $lower_layer = $self->layers->[$layer_id-1];
+    for my $region_id (0..$#{$self->print->regions}) {
+        my $fill_density = $self->print->regions->[$region_id]->config->fill_density;
+        next if $fill_density == 1 || $fill_density == 0;
         
-        foreach my $layerm (@{$layer->regions}) {
+        for my $layer_id (1..$#{$self->layers}) {
+            my $layer       = $self->layers->[$layer_id];
+            my $layerm      = $layer->regions->[$region_id];
+            my $lower_layer = $self->layers->[$layer_id-1];
+            
             # compute the areas needing bridge math 
             my @internal_solid = @{$layerm->fill_surfaces->filter_by_type(S_TYPE_INTERNALSOLID)};
             my @lower_internal = map @{$_->fill_surfaces->filter_by_type(S_TYPE_INTERNAL)}, @{$lower_layer->regions};
@@ -861,7 +864,8 @@ sub generate_support_material {
         && $self->layer_count >= 2;
     
     my $s = Slic3r::Print::SupportMaterial->new(
-        config          => $self->config,
+        print_config    => $self->print->config,
+        object_config   => $self->config,
         flow            => $self->support_material_flow,
         interface_flow  => $self->support_material_flow(FLOW_ROLE_SUPPORT_MATERIAL_INTERFACE),
     );
diff --git a/lib/Slic3r/Print/Region.pm b/lib/Slic3r/Print/Region.pm
index 4cb929e0b..ff05d5bc9 100644
--- a/lib/Slic3r/Print/Region.pm
+++ b/lib/Slic3r/Print/Region.pm
@@ -53,7 +53,7 @@ sub flow {
         role                => $role,
         nozzle_diameter     => $nozzle_diameter,
         layer_height        => $layer_height,
-        bridge_flow_ratio   => ($bridge ? $self->config->bridge_flow_ratio : 0),
+        bridge_flow_ratio   => ($bridge ? $self->print->config->bridge_flow_ratio : 0),
     );
 }
 
diff --git a/lib/Slic3r/Print/SupportMaterial.pm b/lib/Slic3r/Print/SupportMaterial.pm
index 291268bba..38949d22e 100644
--- a/lib/Slic3r/Print/SupportMaterial.pm
+++ b/lib/Slic3r/Print/SupportMaterial.pm
@@ -3,13 +3,15 @@ use Moo;
 
 use List::Util qw(sum min max);
 use Slic3r::ExtrusionPath ':roles';
+use Slic3r::Flow ':roles';
 use Slic3r::Geometry qw(scale scaled_epsilon PI rad2deg deg2rad);
 use Slic3r::Geometry::Clipper qw(offset diff union union_ex intersection offset_ex offset2
     intersection_pl);
 use Slic3r::Surface ':types';
 
-has 'config' => (is => 'rw', required => 1);
-has 'flow'   => (is => 'rw', required => 1);
+has 'print_config'  => (is => 'rw', required => 1);
+has 'object_config' => (is => 'rw', required => 1);
+has 'flow'          => (is => 'rw', required => 1);
 
 use constant DEBUG_CONTACT_ONLY => 0;
 
@@ -73,8 +75,8 @@ sub contact_area {
     
     # if user specified a custom angle threshold, convert it to radians
     my $threshold_rad;
-    if ($self->config->support_material_threshold) {
-        $threshold_rad = deg2rad($self->config->support_material_threshold + 1);  # +1 makes the threshold inclusive
+    if ($self->object_config->support_material_threshold) {
+        $threshold_rad = deg2rad($self->object_config->support_material_threshold + 1);  # +1 makes the threshold inclusive
         Slic3r::debugf "Threshold angle = %d°\n", rad2deg($threshold_rad);
     }
     
@@ -86,9 +88,9 @@ sub contact_area {
         # so $layer_id == 0 means first object layer
         # and $layer->id == 0 means first print layer (including raft)
         
-        if ($self->config->raft_layers == 0) {
+        if ($self->object_config->raft_layers == 0) {
             next if $layer_id == 0;
-        } elsif (!$self->config->support_material) {
+        } elsif (!$self->object_config->support_material) {
             # if we are only going to generate raft just check 
             # the 'overhangs' of the first object layer
             last if $layer_id > 0;
@@ -110,8 +112,8 @@ sub contact_area {
             
                 # If a threshold angle was specified, use a different logic for detecting overhangs.
                 if (defined $threshold_rad
-                    || $layer_id < $self->config->support_material_enforce_layers
-                    || $self->config->raft_layers > 0) {
+                    || $layer_id < $self->object_config->support_material_enforce_layers
+                    || $self->object_config->raft_layers > 0) {
                     my $d = defined $threshold_rad
                         ? scale $lower_layer->height * ((cos $threshold_rad) / (sin $threshold_rad))
                         : 0;
@@ -170,8 +172,8 @@ sub contact_area {
         # now apply the contact areas to the layer were they need to be made
         {
             # get the average nozzle diameter used on this layer
-            my @nozzle_diameters = map $_->nozzle_diameter,
-                map { $_->perimeter_flow, $_->solid_infill_flow }
+            my @nozzle_diameters = map $self->print_config->nozzle_diameter->[$_],
+                map { $_->config->perimeter_extruder-1, $_->config->infill_extruder-1 }
                 @{$layer->regions};
             my $nozzle_diameter = sum(@nozzle_diameters)/@nozzle_diameters;
             
@@ -179,7 +181,7 @@ sub contact_area {
             ###$contact_z = $layer->print_z - $layer->height;
             
             # ignore this contact area if it's too low
-            next if $contact_z < $self->config->get_value('first_layer_height');
+            next if $contact_z < $self->object_config->get_value('first_layer_height');
             
             $contact{$contact_z}  = [ @contact ];
             $overhang{$contact_z} = [ @overhang ];
@@ -242,25 +244,25 @@ sub support_layers_z {
     # determine layer height for any non-contact layer
     # we use max() to prevent many ultra-thin layers to be inserted in case
     # layer_height > nozzle_diameter * 0.75
-    my $nozzle_diameter = $self->flow->nozzle_diameter;
+    my $nozzle_diameter = $self->print_config->nozzle_diameter->[$self->object_config->support_material_extruder-1];
     my $support_material_height = max($max_object_layer_height, $nozzle_diameter * 0.75);
     
     my @z = sort { $a <=> $b } @$contact_z, @$top_z, (map $_ + $nozzle_diameter, @$top_z);
     
     # enforce first layer height
-    my $first_layer_height = $self->config->get_value('first_layer_height');
+    my $first_layer_height = $self->object_config->get_value('first_layer_height');
     shift @z while @z && $z[0] <= $first_layer_height;
     unshift @z, $first_layer_height;
     
     # add raft layers by dividing the space between first layer and
     # first contact layer evenly
-    if ($self->config->raft_layers > 1 && @z >= 2) {
+    if ($self->object_config->raft_layers > 1 && @z >= 2) {
         # $z[1] is last raft layer (contact layer for the first layer object)
-        my $height = ($z[1] - $z[0]) / ($self->config->raft_layers - 1);
+        my $height = ($z[1] - $z[0]) / ($self->object_config->raft_layers - 1);
         splice @z, 1, 0,
             map { int($_*100)/100 }
             map { $z[0] + $height * $_ }
-            0..($self->config->raft_layers - 1);
+            0..($self->object_config->raft_layers - 1);
     }
     
     for (my $i = $#z; $i >= 0; $i--) {
@@ -291,7 +293,7 @@ sub generate_interface_layers {
     
     # let's now generate interface layers below contact areas
     my %interface = ();  # layer_id => [ polygons ]
-    my $interface_layers = $self->config->support_material_interface_layers;
+    my $interface_layers = $self->object_config->support_material_interface_layers;
     for my $layer_id (0 .. $#$support_z) {
         my $z = $support_z->[$layer_id];
         my $this = $contact->{$z} // next;
@@ -339,7 +341,7 @@ sub generate_base_layers {
             # in case we have no interface layers, look at upper contact
             # (1 interface layer means we only have contact layer, so $interface->{$i+1} is empty)
             my @upper_contact = ();
-            if ($self->config->support_material_interface_layers <= 1) {
+            if ($self->object_config->support_material_interface_layers <= 1) {
                 @upper_contact = @{ $contact->{$support_z->[$i+1]} || [] };
             }
             
@@ -395,8 +397,8 @@ sub generate_toolpaths {
     Slic3r::debugf "Generating patterns\n";
     
     # prepare fillers
-    my $pattern = $self->config->support_material_pattern;
-    my @angles = ($self->config->support_material_angle);
+    my $pattern = $self->object_config->support_material_pattern;
+    my @angles = ($self->object_config->support_material_angle);
     if ($pattern eq 'rectilinear-grid') {
         $pattern = 'rectilinear';
         push @angles, $angles[0] + 90;
@@ -407,10 +409,10 @@ sub generate_toolpaths {
         support     => $object->fill_maker->filler($pattern),
     );
     
-    my $interface_angle = $self->config->support_material_angle + 90;
-    my $interface_spacing = $self->config->support_material_interface_spacing + $flow->spacing;
+    my $interface_angle = $self->object_config->support_material_angle + 90;
+    my $interface_spacing = $self->object_config->support_material_interface_spacing + $flow->spacing;
     my $interface_density = $interface_spacing == 0 ? 1 : $flow->spacing / $interface_spacing;
-    my $support_spacing = $self->config->support_material_spacing + $flow->spacing;
+    my $support_spacing = $self->object_config->support_material_spacing + $flow->spacing;
     my $support_density = $support_spacing == 0 ? 1 : $flow->spacing / $support_spacing;
     
     my $process_layer = sub {
@@ -441,7 +443,7 @@ sub generate_toolpaths {
         
         # contact
         my $contact_infill = [];
-        if ($self->config->support_material_interface_layers == 0) {
+        if ($self->object_config->support_material_interface_layers == 0) {
             # if no interface layers were requested we treat the contact layer
             # exactly as a generic base layer
             push @$base, @$contact;
@@ -561,7 +563,7 @@ sub generate_toolpaths {
             # base flange
             if ($layer_id == 0) {
                 $filler = $fillers{interface};
-                $filler->angle($self->config->support_material_angle + 90);
+                $filler->angle($self->object_config->support_material_angle + 90);
                 $density        = 0.5;
                 $flow_spacing   = $object->print->first_layer_support_material_flow->spacing;
             } else {
@@ -609,7 +611,7 @@ sub generate_toolpaths {
     };
     
     Slic3r::parallelize(
-        threads => $self->config->threads,
+        threads => $self->print_config->threads,
         items => [ 0 .. $#{$object->support_layers} ],
         thread_cb => sub {
             my $q = shift;
diff --git a/lib/Slic3r/Test.pm b/lib/Slic3r/Test.pm
index 3a3944318..2e88268a7 100644
--- a/lib/Slic3r/Test.pm
+++ b/lib/Slic3r/Test.pm
@@ -99,20 +99,21 @@ sub model {
 sub init_print {
     my ($model_name, %params) = @_;
     
-    my $config = Slic3r::Config->new_from_defaults;
+    my $config = Slic3r::Config->new;
     $config->apply($params{config}) if $params{config};
     $config->set('gcode_comments', 1) if $ENV{SLIC3R_TESTS_GCODE};
     
-    my $print = Slic3r::Print->new(config => $config);
+    my $print = Slic3r::Print->new;
+    $print->apply_config($config);
     
     $model_name = [$model_name] if ref($model_name) ne 'ARRAY';
     for my $model (map model($_, %params), @$model_name) {
         die "Unknown model in test" if !defined $model;
         if (defined $params{duplicate} && $params{duplicate} > 1) {
-            $model->duplicate($params{duplicate} // 1, $config->min_object_distance);
+            $model->duplicate($params{duplicate} // 1, $print->config->min_object_distance);
         }
-        $model->arrange_objects($config->min_object_distance);
-        $model->center_instances_around_point($config->print_center);
+        $model->arrange_objects($print->config->min_object_distance);
+        $model->center_instances_around_point($print->config->print_center);
         $print->add_model_object($_) for @{$model->objects};
     }
     $print->validate;
diff --git a/slic3r.pl b/slic3r.pl
index 0be1219d0..beea80373 100755
--- a/slic3r.pl
+++ b/slic3r.pl
@@ -153,12 +153,12 @@ if (@ARGV) {  # slicing from command line
         }
         
         my $print = Slic3r::Print->new(
-            config      => $config,
             status_cb   => sub {
                 my ($percent, $message) = @_;
                 printf "=> %s\n", $message;
             },
         );
+        $print->apply_config($config);
         foreach my $model_object (@{$model->objects}) {
             $print->auto_assign_extruders($model_object);
             $print->add_model_object($model_object);
diff --git a/t/cooling.t b/t/cooling.t
index 87d3dff39..6fc5cf71b 100644
--- a/t/cooling.t
+++ b/t/cooling.t
@@ -13,10 +13,14 @@ use Slic3r;
 use Slic3r::Test;
 
 sub buffer {
-    my $config = shift || Slic3r::Config->new_from_defaults;
+    my $config = shift || Slic3r::Config->new;
+    
+    my $print_config = Slic3r::Config::Print->new;
+    $print_config->apply_dynamic($config);
+    
     my $buffer = Slic3r::GCode::CoolingBuffer->new(
-        config      => $config,
-        gcodegen    => Slic3r::GCode->new(config => $config, layer_count => 10, extruders => []),
+        config      => $print_config,
+        gcodegen    => Slic3r::GCode->new(print_config => $print_config, layer_count => 10, extruders => []),
     );
     return $buffer;
 }
diff --git a/t/gcode.t b/t/gcode.t
index c8db2f816..bd5677fc7 100644
--- a/t/gcode.t
+++ b/t/gcode.t
@@ -14,9 +14,8 @@ use Slic3r::Test;
 
 {
     my $gcodegen = Slic3r::GCode->new(
-        config      => Slic3r::Config->new_from_defaults,
-        layer_count => 1,
-        extruders   => [],
+        layer_count     => 1,
+        extruders       => [],
     );
     $gcodegen->set_shift(10, 10);
     is_deeply $gcodegen->last_pos->arrayref, [scale -10, scale -10], 'last_pos is shifted correctly';
diff --git a/t/shells.t b/t/shells.t
index 10da90556..541c7cb75 100644
--- a/t/shells.t
+++ b/t/shells.t
@@ -140,6 +140,9 @@ use Slic3r::Test;
 
 {
     my $config = Slic3r::Config->new_from_defaults;
+    $config->set('perimeters', 1);
+    $config->set('fill_density', 0);
+    $config->set('top_solid_layers', 0);
     $config->set('spiral_vase', 1);
     $config->set('bottom_solid_layers', 0);
     $config->set('skirts', 0);
diff --git a/t/support.t b/t/support.t
index b7d1810df..bd82d0ce1 100644
--- a/t/support.t
+++ b/t/support.t
@@ -21,16 +21,16 @@ use Slic3r::Test;
     my $test = sub {
         my $print = Slic3r::Test::init_print('20mm_cube', config => $config);
         $print->init_extruders;
-        my $flow = $print->support_material_flow;
+        my $flow = $print->objects->[0]->support_material_flow;
         my $support_z = Slic3r::Print::SupportMaterial
-            ->new(config => $config, flow => $flow)
+            ->new(object_config => $print->objects->[0]->config, print_config => $print->config, flow => $flow)
             ->support_layers_z(\@contact_z, \@top_z, $config->layer_height);
         
         is $support_z->[0], $config->first_layer_height,
             'first layer height is honored';
         is scalar(grep { $support_z->[$_]-$support_z->[$_-1] <= 0 } 1..$#$support_z), 0,
             'no null or negative support layers';
-        is scalar(grep { $support_z->[$_]-$support_z->[$_-1] > $flow->nozzle_diameter + epsilon } 1..$#$support_z), 0,
+        is scalar(grep { $support_z->[$_]-$support_z->[$_-1] > $config->nozzle_diameter->[0] + epsilon } 1..$#$support_z), 0,
             'no layers thicker than nozzle diameter';
         
         my $wrong_top_spacing = 0;
@@ -40,7 +40,7 @@ use Slic3r::Test;
             
             # check that first support layer above this top surface is spaced with nozzle diameter
             $wrong_top_spacing = 1
-                if ($support_z->[$layer_id+1] - $support_z->[$layer_id]) != $flow->nozzle_diameter;
+                if ($support_z->[$layer_id+1] - $support_z->[$layer_id]) != $config->nozzle_diameter->[0];
         }
         ok !$wrong_top_spacing, 'layers above top surfaces are spaced correctly';
     };
diff --git a/xs/src/PrintConfig.hpp b/xs/src/PrintConfig.hpp
index 4643c8b70..cecd00a33 100644
--- a/xs/src/PrintConfig.hpp
+++ b/xs/src/PrintConfig.hpp
@@ -899,6 +899,7 @@ class PrintObjectConfig : public virtual StaticConfig
     public:
     ConfigOptionFloatOrPercent      extrusion_width;
     ConfigOptionFloatOrPercent      first_layer_height;
+    ConfigOptionBool                infill_only_where_needed;
     ConfigOptionFloat               layer_height;
     ConfigOptionInt                 raft_layers;
     ConfigOptionBool                support_material;
@@ -921,6 +922,7 @@ class PrintObjectConfig : public virtual StaticConfig
         this->extrusion_width.percent                            = false;
         this->first_layer_height.value                           = 0.35;
         this->first_layer_height.percent                         = false;
+        this->infill_only_where_needed.value                     = false;
         this->layer_height.value                                 = 0.4;
         this->raft_layers.value                                  = 0;
         this->support_material.value                             = false;
@@ -941,6 +943,7 @@ class PrintObjectConfig : public virtual StaticConfig
     ConfigOption* option(const t_config_option_key opt_key, bool create = false) {
         if (opt_key == "extrusion_width")                            return &this->extrusion_width;
         if (opt_key == "first_layer_height")                         return &this->first_layer_height;
+        if (opt_key == "infill_only_where_needed")                   return &this->infill_only_where_needed;
         if (opt_key == "layer_height")                               return &this->layer_height;
         if (opt_key == "raft_layers")                                return &this->raft_layers;
         if (opt_key == "support_material")                           return &this->support_material;
@@ -972,7 +975,6 @@ class PrintRegionConfig : public virtual StaticConfig
     ConfigOptionInt                 infill_extruder;
     ConfigOptionFloatOrPercent      infill_extrusion_width;
     ConfigOptionInt                 infill_every_layers;
-    ConfigOptionBool                infill_only_where_needed;
     ConfigOptionInt                 perimeter_extruder;
     ConfigOptionFloatOrPercent      perimeter_extrusion_width;
     ConfigOptionInt                 perimeters;
@@ -999,7 +1001,6 @@ class PrintRegionConfig : public virtual StaticConfig
         this->infill_extrusion_width.value                       = 0;
         this->infill_extrusion_width.percent                     = false;
         this->infill_every_layers.value                          = 1;
-        this->infill_only_where_needed.value                     = false;
         this->perimeter_extruder.value                           = 1;
         this->perimeter_extrusion_width.value                    = 0;
         this->perimeter_extrusion_width.percent                  = false;
@@ -1025,7 +1026,6 @@ class PrintRegionConfig : public virtual StaticConfig
         if (opt_key == "infill_extruder")                            return &this->infill_extruder;
         if (opt_key == "infill_extrusion_width")                     return &this->infill_extrusion_width;
         if (opt_key == "infill_every_layers")                        return &this->infill_every_layers;
-        if (opt_key == "infill_only_where_needed")                   return &this->infill_only_where_needed;
         if (opt_key == "perimeter_extruder")                         return &this->perimeter_extruder;
         if (opt_key == "perimeter_extrusion_width")                  return &this->perimeter_extrusion_width;
         if (opt_key == "perimeters")                                 return &this->perimeters;
@@ -1332,6 +1332,15 @@ class PrintConfig : public virtual StaticConfig
         
         return NULL;
     };
+    
+    std::string get_extrusion_axis() {
+        if (this->gcode_flavor == gcfMach3) {
+            return std::string("A");
+        } else if (this->gcode_flavor == gcfNoExtrusion) {
+            return std::string("");
+        }
+        return this->extrusion_axis;
+    }
 };
 
 class DynamicPrintConfig : public DynamicConfig
diff --git a/xs/xsp/Config.xsp b/xs/xsp/Config.xsp
index 5c3966e13..b7f6f3ec8 100644
--- a/xs/xsp/Config.xsp
+++ b/xs/xsp/Config.xsp
@@ -41,6 +41,7 @@
         %code{% THIS->apply(*other, true); %};
     std::vector<std::string> get_keys()
         %code{% THIS->keys(&RETVAL); %};
+    std::string get_extrusion_axis();
 };
 
 %name{Slic3r::Config::PrintRegion} class PrintRegionConfig {

From 385e0e0974ce4b8de6f80ca7ac31970943912977 Mon Sep 17 00:00:00 2001
From: Alessandro Ranellucci <aar@cpan.org>
Date: Thu, 2 Jan 2014 17:54:18 +0100
Subject: [PATCH 07/19] Refactoring: new Slic3r::Print::Simple class for
 non-interactive slicing (used in CLI and Quick Slice)

---
 lib/Slic3r.pm                       |   1 +
 lib/Slic3r/GUI/SkeinPanel.pm        |  72 ++++++------------
 lib/Slic3r/Print/Simple.pm          | 112 ++++++++++++++++++++++++++++
 lib/Slic3r/Print/SupportMaterial.pm |   2 +-
 slic3r.pl                           |  52 ++++---------
 5 files changed, 151 insertions(+), 88 deletions(-)
 create mode 100644 lib/Slic3r/Print/Simple.pm

diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm
index 8ab34ca34..1c5ffa74c 100644
--- a/lib/Slic3r.pm
+++ b/lib/Slic3r.pm
@@ -68,6 +68,7 @@ use Slic3r::Polyline;
 use Slic3r::Print;
 use Slic3r::Print::Object;
 use Slic3r::Print::Region;
+use Slic3r::Print::Simple;
 use Slic3r::Print::SupportMaterial;
 use Slic3r::Surface;
 use Slic3r::TriangleMesh;
diff --git a/lib/Slic3r/GUI/SkeinPanel.pm b/lib/Slic3r/GUI/SkeinPanel.pm
index 7750794c0..371698e0a 100644
--- a/lib/Slic3r/GUI/SkeinPanel.pm
+++ b/lib/Slic3r/GUI/SkeinPanel.pm
@@ -88,25 +88,15 @@ sub quick_slice {
     my $self = shift;
     my %params = @_;
     
-    my $process_dialog;
+    my $progress_dialog;
     eval {
         # validate configuration
         my $config = $self->config;
         $config->validate;
-
-        # confirm slicing of more than one copies
-        my $copies = $config->duplicate_grid->[X] * $config->duplicate_grid->[Y];
-        $copies = $config->duplicate if $config->duplicate > 1;
-        if ($copies > 1) {
-            my $confirmation = Wx::MessageDialog->new($self, "Are you sure you want to slice $copies copies?",
-                                                      'Multiple Copies', wxICON_QUESTION | wxOK | wxCANCEL);
-            return unless $confirmation->ShowModal == wxID_OK;
-        }
         
         # select input file
-        my $dir = $Slic3r::GUI::Settings->{recent}{skein_directory} || $Slic3r::GUI::Settings->{recent}{config_directory} || '';
-
         my $input_file;
+        my $dir = $Slic3r::GUI::Settings->{recent}{skein_directory} || $Slic3r::GUI::Settings->{recent}{config_directory} || '';
         if (!$params{reslice}) {
             my $dialog = Wx::FileDialog->new($self, 'Choose a file to slice (STL/OBJ/AMF):', $dir, "", MODEL_WILDCARD, wxFD_OPEN | wxFD_FILE_MUST_EXIST);
             if ($dialog->ShowModal != wxID_OK) {
@@ -133,31 +123,23 @@ sub quick_slice {
         $Slic3r::GUI::Settings->{recent}{skein_directory} = dirname($input_file);
         Slic3r::GUI->save_settings;
         
-        my $print = $self->init_print;
-        my $model = eval { Slic3r::Model->read_from_file($input_file) };
-        Slic3r::GUI::show_error($self, $@) if $@;
+        my $sprint = Slic3r::Print::Simple->new(
+            status_cb       => sub {
+                my ($percent, $message) = @_;
+                return if &Wx::wxVERSION_STRING !~ / 2\.(8\.|9\.[2-9])/;
+                $progress_dialog->Update($percent, "$message…");
+            },
+        );
         
-        if ($model->has_objects_with_no_instances) {
-            # apply a default position to all objects not having one
-            foreach my $object (@{$model->objects}) {
-                $object->add_instance(offset => [0,0]) if !defined $object->instances;
-            }
-            $model->arrange_objects($config->min_object_distance);
-        }
-        $model->center_instances_around_point($config->print_center);
+        $sprint->apply_config($config);
+        $sprint->set_model(Slic3r::Model->read_from_file($input_file));
         
-        foreach my $model_object (@{$model->objects}) {
-            $print->auto_assign_extruders($model_object);
-            $print->add_model_object($model_object);
-        }
-        $print->validate;
-
         # select output file
-        my $output_file = $main::opt{output};
+        my $output_file;
         if ($params{reslice}) {
             $output_file = $last_output_file if defined $last_output_file;
         } elsif ($params{save_as}) {
-            $output_file = $print->expanded_output_filepath($output_file);
+            $output_file = $sprint->expanded_output_filepath;
             $output_file =~ s/\.gcode$/.svg/i if $params{export_svg};
             my $dlg = Wx::FileDialog->new($self, 'Save ' . ($params{export_svg} ? 'SVG' : 'G-code') . ' file as:',
                 Slic3r::GUI->output_path(dirname($output_file)),
@@ -174,40 +156,32 @@ sub quick_slice {
         }
         
         # show processbar dialog
-        $process_dialog = Wx::ProgressDialog->new('Slicing…', "Processing $input_file_basename…", 
+        $progress_dialog = Wx::ProgressDialog->new('Slicing…', "Processing $input_file_basename…", 
             100, $self, 0);
-        $process_dialog->Pulse;
+        $progress_dialog->Pulse;
         
         {
             my @warnings = ();
             local $SIG{__WARN__} = sub { push @warnings, $_[0] };
-            my %export_params = (
-                output_file => $output_file,
-            );
-            $print->status_cb(sub {
-                my ($percent, $message) = @_;
-                if (&Wx::wxVERSION_STRING =~ / 2\.(8\.|9\.[2-9])/) {
-                    $process_dialog->Update($percent, "$message…");
-                }
-            });
+            
+            $sprint->output_file($output_file);
             if ($params{export_svg}) {
-                $print->export_svg(%export_params);
+                $sprint->export_svg;
             } else {
-                $print->process;
-                $print->export_gcode(%export_params);
+                $sprint->export_gcode;
             }
-            $print->status_cb(undef);
+            $sprint->status_cb(undef);
             Slic3r::GUI::warning_catcher($self)->($_) for @warnings;
         }
-        $process_dialog->Destroy;
-        undef $process_dialog;
+        $progress_dialog->Destroy;
+        undef $progress_dialog;
         
         my $message = "$input_file_basename was successfully sliced.";
         &Wx::wxTheApp->notify($message);
         Wx::MessageDialog->new($self, $message, 'Slicing Done!', 
             wxOK | wxICON_INFORMATION)->ShowModal;
     };
-    Slic3r::GUI::catch_error($self, sub { $process_dialog->Destroy if $process_dialog });
+    Slic3r::GUI::catch_error($self, sub { $progress_dialog->Destroy if $progress_dialog });
 }
 
 sub repair_stl {
diff --git a/lib/Slic3r/Print/Simple.pm b/lib/Slic3r/Print/Simple.pm
new file mode 100644
index 000000000..0bf497b7d
--- /dev/null
+++ b/lib/Slic3r/Print/Simple.pm
@@ -0,0 +1,112 @@
+package Slic3r::Print::Simple;
+use Moo;
+
+use Slic3r::Geometry qw(X Y);
+
+has '_print' => (
+    is      => 'ro',
+    default => sub { Slic3r::Print->new },
+    handles => [qw(apply_config extruders expanded_output_filepath)],
+);
+
+has 'duplicate' => (
+    is      => 'rw',
+    default => sub { 1 },
+);
+
+has 'scale' => (
+    is      => 'rw',
+    default => sub { 1 },
+);
+
+has 'rotate' => (
+    is      => 'rw',
+    default => sub { 0 },
+);
+
+has 'duplicate_grid' => (
+    is      => 'rw',
+    default => sub { [1,1] },
+);
+
+has 'status_cb' => (
+    is      => 'rw',
+    default => sub { sub {} },
+);
+
+has 'output_file' => (
+    is      => 'rw',
+);
+
+sub set_model {
+    my ($self, $model) = @_;
+    
+    # make method idempotent so that the object is reusable
+    $self->_print->delete_all_objects;
+    
+    my $need_arrange = $model->has_objects_with_no_instances;
+    if ($need_arrange) {
+        # apply a default position to all objects not having one
+        foreach my $object (@{$model->objects}) {
+            $object->add_instance(offset => [0,0]) if !defined $object->instances;
+        }
+    }
+    
+    # apply scaling and rotation supplied from command line if any
+    foreach my $instance (map @{$_->instances}, @{$model->objects}) {
+        $instance->scaling_factor($instance->scaling_factor * $self->scale);
+        $instance->rotation($instance->rotation + $self->rotate);
+    }
+    # TODO: --scale --rotate, --duplicate* shouldn't be stored in config
+    
+    if ($self->duplicate_grid->[X] > 1 || $self->duplicate_grid->[Y] > 1) {
+        $model->duplicate_objects_grid($self->duplicate_grid, $self->_print->config->duplicate_distance);
+    } elsif ($need_arrange) {
+        $model->duplicate_objects($self->duplicate, $self->_print->config->min_object_distance);
+    } elsif ($self->duplicate > 1) {
+        # if all input objects have defined position(s) apply duplication to the whole model
+        $model->duplicate($self->duplicate, $self->_print->config->min_object_distance);
+    }
+    $model->center_instances_around_point($self->_print->config->print_center);
+    
+    foreach my $model_object (@{$model->objects}) {
+        $self->_print->auto_assign_extruders($model_object);
+        $self->_print->add_model_object($model_object);
+    }
+}
+
+sub _before_export {
+    my ($self) = @_;
+    
+    $self->_print->status_cb($self->status_cb);
+    $self->_print->validate;
+}
+
+sub _after_export {
+    my ($self) = @_;
+    
+    $self->_print->status_cb(undef);
+}
+
+sub export_gcode {
+    my ($self) = @_;
+    
+    $self->_before_export;
+    
+    $self->_print->process;
+    $self->_print->export_gcode(output_file => $self->output_file);
+    
+    $self->_after_export;
+}
+
+sub export_svg {
+    my ($self) = @_;
+    
+    $self->_before_export;
+    
+    $self->_print->export_svg(output_file => $self->output_file);
+    
+    $self->_after_export;
+}
+
+1;
diff --git a/lib/Slic3r/Print/SupportMaterial.pm b/lib/Slic3r/Print/SupportMaterial.pm
index 38949d22e..0dae19e18 100644
--- a/lib/Slic3r/Print/SupportMaterial.pm
+++ b/lib/Slic3r/Print/SupportMaterial.pm
@@ -107,7 +107,7 @@ sub contact_area {
         } else {
             my $lower_layer = $object->layers->[$layer_id-1];
             foreach my $layerm (@{$layer->regions}) {
-                my $fw = $layerm->perimeter_flow->scaled_width;
+                my $fw = $layerm->flow(FLOW_ROLE_PERIMETER)->scaled_width;
                 my $diff;
             
                 # If a threshold angle was specified, use a different logic for detecting overhangs.
diff --git a/slic3r.pl b/slic3r.pl
index beea80373..e62c22b3a 100755
--- a/slic3r.pl
+++ b/slic3r.pl
@@ -12,7 +12,6 @@ use Getopt::Long qw(:config no_auto_abbrev);
 use List::Util qw(first);
 use POSIX qw(setlocale LC_NUMERIC);
 use Slic3r;
-use Slic3r::Geometry qw(X Y);
 use Time::HiRes qw(gettimeofday tv_interval);
 $|++;
 
@@ -122,55 +121,32 @@ if (@ARGV) {  # slicing from command line
             $model = Slic3r::Model->read_from_file($input_file);
         }
         
-        my $need_arrange = $model->has_objects_with_no_instances;
-        if ($need_arrange) {
-            # apply a default position to all objects not having one
-            foreach my $object (@{$model->objects}) {
-                $object->add_instance(offset => [0,0]) if !defined $object->instances;
-            }
-        }
-        
-        # apply scaling and rotation supplied from command line if any
-        foreach my $instance (map @{$_->instances}, @{$model->objects}) {
-            $instance->scaling_factor($instance->scaling_factor * $config->scale);
-            $instance->rotation($instance->rotation + $config->rotate);
-        }
-        # TODO: --scale --rotate, --duplicate* shouldn't be stored in config
-        
-        if ($config->duplicate_grid->[X] > 1 || $config->duplicate_grid->[Y] > 1) {
-            $model->duplicate_objects_grid($config->duplicate_grid, $config->duplicate_distance);
-        } elsif ($need_arrange) {
-            $model->duplicate_objects($config->duplicate, $config->min_object_distance);
-        } elsif ($config->duplicate > 1) {
-            # if all input objects have defined position(s) apply duplication to the whole model
-            $model->duplicate($config->duplicate, $config->min_object_distance);
-        }
-        $model->center_instances_around_point($config->print_center);
-        
         if ($opt{info}) {
             $model->print_info;
             next;
         }
         
-        my $print = Slic3r::Print->new(
-            status_cb   => sub {
+        my $sprint = Slic3r::Print::Simple->new(
+            scale           => $config->scale,
+            rotate          => $config->rotate,
+            duplicate       => $config->duplicate,
+            duplicate_grid  => $config->duplicate_grid,
+            status_cb       => sub {
                 my ($percent, $message) = @_;
                 printf "=> %s\n", $message;
             },
+            output_file     => $opt{output},
         );
-        $print->apply_config($config);
-        foreach my $model_object (@{$model->objects}) {
-            $print->auto_assign_extruders($model_object);
-            $print->add_model_object($model_object);
-        }
+        
+        $sprint->apply_config($config);
+        $sprint->set_model($model);
         undef $model;  # free memory
-        $print->validate;
+        
         if ($opt{export_svg}) {
-            $print->export_svg(output_file => $opt{output});
+            $sprint->export_svg;
         } else {
             my $t0 = [gettimeofday];
-            $print->process;
-            $print->export_gcode(output_file => $opt{output});
+            $sprint->export_gcode;
             
             # output some statistics
             {
@@ -180,7 +156,7 @@ if (@ARGV) {  # slicing from command line
             }
             print map sprintf("Filament required: %.1fmm (%.1fcm3)\n",
                 $_->absolute_E, $_->extruded_volume/1000),
-                @{$print->extruders};
+                @{$sprint->extruders};
         }
     }
 } else {

From bfa2ee2770b3f3a49d7256be86d2afd1b5423c83 Mon Sep 17 00:00:00 2001
From: Alessandro Ranellucci <aar@cpan.org>
Date: Thu, 2 Jan 2014 18:01:21 +0100
Subject: [PATCH 08/19] Remove scale, rotate, duplicate, duplicate_grid from
 config (but leave them in CLI)

---
 lib/Slic3r/Config.pm       | 11 +----------
 lib/Slic3r/Print/Simple.pm |  1 -
 slic3r.pl                  | 25 +++++++++++++++++--------
 xs/src/PrintConfig.hpp     | 31 -------------------------------
 4 files changed, 18 insertions(+), 50 deletions(-)

diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm
index 21fa2322b..41e5fa975 100644
--- a/lib/Slic3r/Config.pm
+++ b/lib/Slic3r/Config.pm
@@ -7,7 +7,7 @@ use List::Util qw(first);
 
 # cemetery of old config settings
 our @Ignore = qw(duplicate_x duplicate_y multiply_x multiply_y support_material_tool acceleration
-    adjust_overhang_flow standby_temperature);
+    adjust_overhang_flow standby_temperature scale rotate duplicate duplicate_grid);
 
 our $Options = print_config_def();
 
@@ -19,15 +19,6 @@ our $Options = print_config_def();
     }
 }
 
-# sub _get {
-#     my ($self, $opt_key) = @_;
-#     use XXX;
-#     if (!defined first { $_ eq $opt_key } @{$self->get_keys}) {
-#         ZZZ $opt_key;
-#     }
-#     return $self->get($opt_key);
-# }
-
 sub new_from_defaults {
     my $class = shift;
     my (@opt_keys) = @_;
diff --git a/lib/Slic3r/Print/Simple.pm b/lib/Slic3r/Print/Simple.pm
index 0bf497b7d..a6f756343 100644
--- a/lib/Slic3r/Print/Simple.pm
+++ b/lib/Slic3r/Print/Simple.pm
@@ -57,7 +57,6 @@ sub set_model {
         $instance->scaling_factor($instance->scaling_factor * $self->scale);
         $instance->rotation($instance->rotation + $self->rotate);
     }
-    # TODO: --scale --rotate, --duplicate* shouldn't be stored in config
     
     if ($self->duplicate_grid->[X] > 1 || $self->duplicate_grid->[Y] > 1) {
         $model->duplicate_objects_grid($self->duplicate_grid, $self->_print->config->duplicate_distance);
diff --git a/slic3r.pl b/slic3r.pl
index e62c22b3a..bb2f7280e 100755
--- a/slic3r.pl
+++ b/slic3r.pl
@@ -37,6 +37,11 @@ my %cli_options = ();
         'merge|m'               => \$opt{merge},
         'repair'                => \$opt{repair},
         'info'                  => \$opt{info},
+        
+        'scale=f'               => \$opt{scale},
+        'rotate=i'              => \$opt{rotate},
+        'duplicate=i'           => \$opt{duplicate},
+        'duplicate-grid=s'      => \$opt{duplicate_grid},
     );
     foreach my $opt_key (keys %{$Slic3r::Config::Options}) {
         my $cli = $Slic3r::Config::Options->{$opt_key}->{cli} or next;
@@ -126,11 +131,15 @@ if (@ARGV) {  # slicing from command line
             next;
         }
         
+        if (defined $opt{duplicate_grid}) {
+            $opt{duplicate_grid} = [ split /[,x]/, $opt{duplicate_grid}, 2 ];
+        }
+        
         my $sprint = Slic3r::Print::Simple->new(
-            scale           => $config->scale,
-            rotate          => $config->rotate,
-            duplicate       => $config->duplicate,
-            duplicate_grid  => $config->duplicate_grid,
+            scale           => $opt{scale}          // 1,
+            rotate          => $opt{rotate}         // 0,
+            duplicate       => $opt{duplicate}      // 1,
+            duplicate_grid  => $opt{duplicate_grid} // [1,1],
             status_cb       => sub {
                 my ($percent, $message) = @_;
                 printf "=> %s\n", $message;
@@ -394,11 +403,11 @@ $j
                         (mm, default: $config->{brim_width})
    
    Transform options:
-    --scale             Factor for scaling input object (default: $config->{scale})
-    --rotate            Rotation angle in degrees (0-360, default: $config->{rotate})
-    --duplicate         Number of items with auto-arrange (1+, default: $config->{duplicate})
+    --scale             Factor for scaling input object (default: 1)
+    --rotate            Rotation angle in degrees (0-360, default: 0)
+    --duplicate         Number of items with auto-arrange (1+, default: 1)
     --bed-size          Bed size, only used for auto-arrange (mm, default: $config->{bed_size}->[0],$config->{bed_size}->[1])
-    --duplicate-grid    Number of items with grid arrangement (default: $config->{duplicate_grid}->[0],$config->{duplicate_grid}->[1])
+    --duplicate-grid    Number of items with grid arrangement (default: 1,1)
     --duplicate-distance Distance in mm between copies (default: $config->{duplicate_distance})
    
    Sequential printing options:
diff --git a/xs/src/PrintConfig.hpp b/xs/src/PrintConfig.hpp
index cecd00a33..dd9f2633c 100644
--- a/xs/src/PrintConfig.hpp
+++ b/xs/src/PrintConfig.hpp
@@ -126,11 +126,6 @@ class PrintConfigDef
         Options["disable_fan_first_layers"].cli = "disable-fan-first-layers=i";
         Options["disable_fan_first_layers"].max = 1000;
 
-        Options["duplicate"].type = coInt;
-        Options["duplicate"].label = "Copies (autoarrange)";
-        Options["duplicate"].cli = "duplicate=i";
-        Options["duplicate"].min = 1;
-
         Options["duplicate_distance"].type = coFloat;
         Options["duplicate_distance"].label = "Distance between copies";
         Options["duplicate_distance"].tooltip = "Distance used for the auto-arrange feature of the plater.";
@@ -138,10 +133,6 @@ class PrintConfigDef
         Options["duplicate_distance"].cli = "duplicate-distance=f";
         Options["duplicate_distance"].aliases.push_back("multiply_distance");
 
-        Options["duplicate_grid"].type = coPoint;
-        Options["duplicate_grid"].label = "Copies (grid)";
-        Options["duplicate_grid"].cli = "duplicate-grid=s";
-
         Options["end_gcode"].type = coString;
         Options["end_gcode"].label = "End G-code";
         Options["end_gcode"].tooltip = "This end procedure is inserted at the end of the output file. Note that you can use placeholder variables for all Slic3r settings.";
@@ -584,16 +575,6 @@ class PrintConfigDef
         Options["retract_speed"].cli = "retract-speed=f@";
         Options["retract_speed"].max = 1000;
 
-        Options["rotate"].type = coInt;
-        Options["rotate"].label = "Rotate";
-        Options["rotate"].sidetext = "°";
-        Options["rotate"].cli = "rotate=i";
-        Options["rotate"].max = 359;
-
-        Options["scale"].type = coFloat;
-        Options["scale"].label = "Scale";
-        Options["scale"].cli = "scale=f";
-
         Options["skirt_distance"].type = coFloat;
         Options["skirt_distance"].label = "Distance from object";
         Options["skirt_distance"].tooltip = "Distance between skirt and object(s). Set this to zero to attach the skirt to the object(s) and get a brim for better adhesion.";
@@ -1057,9 +1038,7 @@ class PrintConfig : public virtual StaticConfig
     ConfigOptionBool                cooling;
     ConfigOptionFloat               default_acceleration;
     ConfigOptionInt                 disable_fan_first_layers;
-    ConfigOptionInt                 duplicate;
     ConfigOptionFloat               duplicate_distance;
-    ConfigOptionPoint               duplicate_grid;
     ConfigOptionString              end_gcode;
     ConfigOptionFloatOrPercent      external_perimeter_speed;
     ConfigOptionBool                external_perimeters_first;
@@ -1108,8 +1087,6 @@ class PrintConfig : public virtual StaticConfig
     ConfigOptionFloats              retract_restart_extra;
     ConfigOptionFloats              retract_restart_extra_toolchange;
     ConfigOptionInts                retract_speed;
-    ConfigOptionInt                 rotate;
-    ConfigOptionFloat               scale;
     ConfigOptionFloat               skirt_distance;
     ConfigOptionInt                 skirt_height;
     ConfigOptionInt                 skirts;
@@ -1147,9 +1124,7 @@ class PrintConfig : public virtual StaticConfig
         this->cooling.value                                      = true;
         this->default_acceleration.value                         = 0;
         this->disable_fan_first_layers.value                     = 1;
-        this->duplicate.value                                    = 1;
         this->duplicate_distance.value                           = 6;
-        this->duplicate_grid.point                               = Pointf(1,1);
         this->end_gcode.value                                    = "M104 S0 ; turn off temperature\nG28 X0  ; home X axis\nM84     ; disable motors\n";
         this->external_perimeter_speed.value                     = 70;
         this->external_perimeter_speed.percent                   = true;
@@ -1212,8 +1187,6 @@ class PrintConfig : public virtual StaticConfig
         this->retract_restart_extra_toolchange.values[0]         = 0;
         this->retract_speed.values.resize(1);
         this->retract_speed.values[0]                            = 30;
-        this->rotate.value                                       = 0;
-        this->scale.value                                        = 1;
         this->skirt_distance.value                               = 6;
         this->skirt_height.value                                 = 1;
         this->skirts.value                                       = 1;
@@ -1255,9 +1228,7 @@ class PrintConfig : public virtual StaticConfig
         if (opt_key == "cooling")                                    return &this->cooling;
         if (opt_key == "default_acceleration")                       return &this->default_acceleration;
         if (opt_key == "disable_fan_first_layers")                   return &this->disable_fan_first_layers;
-        if (opt_key == "duplicate")                                  return &this->duplicate;
         if (opt_key == "duplicate_distance")                         return &this->duplicate_distance;
-        if (opt_key == "duplicate_grid")                             return &this->duplicate_grid;
         if (opt_key == "end_gcode")                                  return &this->end_gcode;
         if (opt_key == "external_perimeter_speed")                   return &this->external_perimeter_speed;
         if (opt_key == "external_perimeters_first")                  return &this->external_perimeters_first;
@@ -1306,8 +1277,6 @@ class PrintConfig : public virtual StaticConfig
         if (opt_key == "retract_restart_extra")                      return &this->retract_restart_extra;
         if (opt_key == "retract_restart_extra_toolchange")           return &this->retract_restart_extra_toolchange;
         if (opt_key == "retract_speed")                              return &this->retract_speed;
-        if (opt_key == "rotate")                                     return &this->rotate;
-        if (opt_key == "scale")                                      return &this->scale;
         if (opt_key == "skirt_distance")                             return &this->skirt_distance;
         if (opt_key == "skirt_height")                               return &this->skirt_height;
         if (opt_key == "skirts")                                     return &this->skirts;

From 81663215c54ed97df49b93940fb89f2f77c75e58 Mon Sep 17 00:00:00 2001
From: Alessandro Ranellucci <aar@cpan.org>
Date: Thu, 2 Jan 2014 22:06:58 +0100
Subject: [PATCH 09/19] Adapt plater to the new split config

---
 lib/Slic3r/Config.pm                          | 23 +-----
 lib/Slic3r/GUI/Plater.pm                      | 10 ++-
 lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm | 66 ++++++++-------
 lib/Slic3r/Model.pm                           | 12 +--
 lib/Slic3r/Print.pm                           | 82 ++++++++++++++++++-
 xs/src/Config.hpp                             |  2 +-
 xs/src/Print.cpp                              |  7 ++
 xs/src/Print.hpp                              |  1 +
 xs/src/PrintConfig.hpp                        | 12 +--
 xs/xsp/Config.xsp                             |  2 +-
 xs/xsp/Print.xsp                              |  1 +
 11 files changed, 153 insertions(+), 65 deletions(-)

diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm
index 41e5fa975..ae166b6c5 100644
--- a/lib/Slic3r/Config.pm
+++ b/lib/Slic3r/Config.pm
@@ -76,7 +76,7 @@ sub load {
     my ($file) = @_;
     
     my $ini = __PACKAGE__->read_ini($file);
-    my $config = __PACKAGE__->new;
+    my $config = $class->new;
     foreach my $opt_key (keys %{$ini->{_}}) {
         ($opt_key, my $value) = _handle_legacy($opt_key, $ini->{_}{$opt_key});
         next if !defined $opt_key;
@@ -88,7 +88,7 @@ sub load {
 sub clone {
     my $self = shift;
     
-    my $new = __PACKAGE__->new;
+    my $new = (ref $self)->new;
     $new->apply($self);
     return $new;
 }
@@ -180,13 +180,14 @@ sub equals {
     return @{ $self->diff($other) } == 0;
 }
 
+# this will *ignore* options not present in both configs
 sub diff {
     my ($self, $other) = @_;
     
     my @diff = ();
     foreach my $opt_key (sort @{$self->get_keys}) {
         push @diff, $opt_key
-            if !$other->has($opt_key) || $other->serialize($opt_key) ne $self->serialize($opt_key);
+            if $other->has($opt_key) && $other->serialize($opt_key) ne $self->serialize($opt_key);
     }
     return [@diff];
 }
@@ -265,27 +266,11 @@ sub validate {
     die "Invalid value for --infill-every-layers\n"
         if $self->infill_every_layers !~ /^\d+$/ || $self->infill_every_layers < 1;
     
-    # --scale
-    die "Invalid value for --scale\n"
-        if $self->scale <= 0;
-    
     # --bed-size
     die "Invalid value for --bed-size\n"
         if !ref $self->bed_size 
             && (!$self->bed_size || $self->bed_size !~ /^\d+,\d+$/);
     
-    # --duplicate-grid
-    die "Invalid value for --duplicate-grid\n"
-        if !ref $self->duplicate_grid 
-            && (!$self->duplicate_grid || $self->duplicate_grid !~ /^\d+,\d+$/);
-    
-    # --duplicate
-    die "Invalid value for --duplicate or --duplicate-grid\n"
-        if !$self->duplicate || $self->duplicate < 1 || !$self->duplicate_grid
-            || (grep !$_, @{$self->duplicate_grid});
-    die "Use either --duplicate or --duplicate-grid (using both doesn't make sense)\n"
-        if $self->duplicate > 1 && $self->duplicate_grid && (grep $_ && $_ > 1, @{$self->duplicate_grid});
-    
     # --skirt-height
     die "Invalid value for --skirt-height\n"
         if $self->skirt_height < -1;  # -1 means as tall as the object
diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm
index e0b0fcf4a..1fb606c3c 100644
--- a/lib/Slic3r/GUI/Plater.pm
+++ b/lib/Slic3r/GUI/Plater.pm
@@ -754,11 +754,15 @@ sub export_gcode2 {
     } if $Slic3r::have_threads;
     
     my $print = $self->{print};
-    $print->apply_config($config);
-    $print->apply_extra_variables($extra_variables);
+    
     
     eval {
-        $print->config->validate;
+        # this will throw errors if config is not valid
+        $config->validate;
+        
+        $print->apply_config($config);
+        $print->apply_extra_variables($extra_variables);
+        
         $print->validate;
         
         {
diff --git a/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm b/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm
index e681d7238..b2f5c3ce0 100644
--- a/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm
+++ b/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm
@@ -114,7 +114,7 @@ sub update_optgroup {
     
     my $config = $self->model_object->config;
     my %categories = ();
-    foreach my $opt_key (keys %$config) {
+    foreach my $opt_key (@{$config->get_keys}) {
         my $category = $Slic3r::Config::Options->{$opt_key}{category};
         $categories{$category} ||= [];
         push @{$categories{$category}}, $opt_key;
@@ -288,31 +288,41 @@ sub new {
     # get unique materials used in this object
     $self->{materials} = [ $self->model_object->unique_materials ];
     
-    # build an OptionsGroup
-    $self->{mapping} = {
-        (map { $self->{materials}[$_] => $_+1 } 0..$#{ $self->{materials} }),   # defaults
-        %{$self->model_object->material_mapping},
-    };
-    my $optgroup = Slic3r::GUI::OptionsGroup->new(
-        parent      => $self,
-        title       => 'Extruders',
-        label_width => 300,
-        options => [
-            map {
-                my $i           = $_;
-                my $material_id = $self->{materials}[$i];
-                {
-                    opt_key     => "material_extruder_$_",
-                    type        => 'i',
-                    label       => $self->model_object->model->get_material_name($material_id),
-                    min         => 1,
-                    default     => $self->{mapping}{$material_id},
-                    on_change   => sub { $self->{mapping}{$material_id} = $_[0] },
-                }
-            } 0..$#{ $self->{materials} }
-        ],
-    );
-    $self->{sizer}->Add($optgroup->sizer, 0, wxEXPAND | wxALL, 10);
+    # get the current mapping
+    $self->{mapping} = {};
+    foreach my $material_id (@{ $self->{materials}}) {
+        my $config = $self->model_object->model->materials->{ $material_id }->config;
+        $self->{mapping}{$material_id} = ($config->perimeter_extruder // 0) + 1;
+    }
+    
+    if (@{$self->{materials}} > 0) {
+        # build an OptionsGroup
+        my $optgroup = Slic3r::GUI::OptionsGroup->new(
+            parent      => $self,
+            title       => 'Extruders',
+            label_width => 300,
+            options => [
+                map {
+                    my $i           = $_;
+                    my $material_id = $self->{materials}[$i];
+                    {
+                        opt_key     => "material_extruder_$_",
+                        type        => 'i',
+                        label       => $self->model_object->model->get_material_name($material_id),
+                        min         => 1,
+                        default     => $self->{mapping}{$material_id} // 1,
+                        on_change   => sub { $self->{mapping}{$material_id} = $_[0] },
+                    }
+                } 0..$#{ $self->{materials} }
+            ],
+        );
+        $self->{sizer}->Add($optgroup->sizer, 0, wxEXPAND | wxALL, 10);
+    } else {
+        my $label = Wx::StaticText->new($self, -1, "This object does not contain named materials.",
+            wxDefaultPosition, [-1, 25]);
+        $label->SetFont(Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
+        $self->{sizer}->Add($label, 0, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, 10);
+    }
     
     $self->SetSizer($self->{sizer});
     $self->{sizer}->SetSizeHints($self);
@@ -324,10 +334,10 @@ sub Closing {
     my $self = shift;
     
     # save mappings into the plater object
-    foreach my $volume (@{$self->model_object}) {
+    foreach my $volume (@{$self->model_object->volumes}) {
         if (defined $volume->material_id) {
             my $config = $self->model_object->model->materials->{ $volume->material_id }->config;
-            $config->set('extruder', $self->{mapping}{ $volume->material_id });
+            $config->set('extruder', $self->{mapping}{ $volume->material_id }-1);
         }
     }
 }
diff --git a/lib/Slic3r/Model.pm b/lib/Slic3r/Model.pm
index 401deb47e..3cbd97493 100644
--- a/lib/Slic3r/Model.pm
+++ b/lib/Slic3r/Model.pm
@@ -54,10 +54,11 @@ sub add_object {
             
             if (defined $volume->material_id) {
                 #  merge material attributes (should we rename materials in case of duplicates?)
-                $self->set_material($volume->material_id, {
-                    %{ $object->model->materials->{$volume->material_id} },
-                    %{ $self->materials->{$volume->material_id} || {} },
-                });
+                my %attributes = %{ $object->model->materials->{$volume->material_id}->attributes };
+                if (exists $self->materials->{$volume->material_id}) {
+                    %attributes = (%attributes, %{ $self->materials->{$volume->material_id}->attributes });
+                }
+                $self->set_material($volume->material_id, {%attributes});
             }
         }
         
@@ -478,7 +479,8 @@ sub unique_materials {
     my $self = shift;
     
     my %materials = ();
-    $materials{ $_->material_id // '_' } = 1 for @{$self->volumes};
+    $materials{ $_->material_id } = 1
+        for grep { defined $_->material_id } @{$self->volumes};
     return sort keys %materials;
 }
 
diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm
index d44a06b6c..c0ed2e11f 100644
--- a/lib/Slic3r/Print.pm
+++ b/lib/Slic3r/Print.pm
@@ -31,9 +31,87 @@ has 'brim' => (is => 'rw', default => sub { Slic3r::ExtrusionPath::Collection->n
 sub apply_config {
     my ($self, $config) = @_;
     
-    $self->config->apply_dynamic($config);
+    # handle changes to print config
+    my $print_diff = $self->config->diff($config);
+    if (@$print_diff) {
+        $self->config->apply_dynamic($config);
+        
+        # TODO: only invalidate changed steps
+        $self->_state->invalidate_all;
+    }
+    
+    # handle changes to object config defaults
     $self->default_object_config->apply_dynamic($config);
+    foreach my $object (@{$self->objects}) {
+        # we don't assume that $config contains a full ObjectConfig,
+        # so we base it on the current print-wise default
+        my $new = $self->default_object_config->clone;
+        
+        # we override the new config with object-specific options
+        $new->apply_dynamic($object->model_object->config);
+        
+        # check whether the new config is different from the current one
+        my $diff = $object->config->diff($new);
+        if (@$diff) {
+            $object->config->apply($new);
+            # TODO: only invalidate changed steps
+            $object->_state->invalidate_all;
+        }
+    }
+    
+    # handle changes to regions config defaults
     $self->default_region_config->apply_dynamic($config);
+    
+    # check whether after applying the new region config defaults to all existing regions
+    # they still have distinct configs; if not we need to re-add objects in order to 
+    # merge the now-equal regions
+    
+    # first compute the transformed region configs
+    my @new_region_configs = ();
+    foreach my $region_id (0..$#{$self->regions}) {
+        my $new = $self->default_region_config->clone;
+        foreach my $object (@{$self->objects}) {
+            foreach my $volume_id (@{ $object->region_volumes->[$region_id] }) {
+                my $volume = $object->model_object->volumes->[$volume_id];
+                next if !defined $volume->material_id;
+                my $material = $object->model_object->model->materials->{$volume->material_id};
+                $new->apply_dynamic($material->config);
+            }
+        }
+        push @new_region_configs, $new;
+    }
+    
+    # then find the first pair of identical configs
+    my $have_identical_configs = 0;
+    my $region_diff = [];
+    for my $i (0..$#new_region_configs) {
+        for my $j (($i+1)..$#new_region_configs) {
+            if ($new_region_configs[$i]->equals($new_region_configs[$j])) {
+                $have_identical_configs = 1;
+            }
+        }
+        my $diff = $self->regions->[$i]->config->diff($new_region_configs[$i]);
+        push @$region_diff, @$diff;
+    }
+    
+    if ($have_identical_configs) {
+        # okay, the current subdivision of regions does not make sense anymore.
+        # we need to remove all objects and re-add them
+        my @model_objects = map $_->model_object, @{$self->object};
+        $self->delete_all_objects;
+        $self->add_model_object($_) for @model_objects;
+    } elsif (@$region_diff > 0) {
+        # if there are no identical regions even after applying the change in 
+        # region config defaults, but at least one region config option changed,
+        # store the new region configs and invalidate
+        # the affected step(s)
+        foreach my $region_id (0..$#{$self->regions}) {
+            $self->regions->[$region_id]->config->apply($new_region_configs[$region_id]);
+        }
+        
+        # TODO: only invalidate changed steps
+        $_->_state->invalidate_all for @{$self->objects};
+    }
 }
 
 sub has_support_material {
@@ -991,7 +1069,7 @@ sub auto_assign_extruders {
         if (defined $volume->material_id) {
             my $material = $model_object->model->materials->{ $volume->material_id };
             my $config = $material->config;
-            $config->set_ifndef('perimeters_extruder', $i);
+            $config->set_ifndef('perimeter_extruder', $i);
             $config->set_ifndef('infill_extruder', $i);
             $config->set_ifndef('support_material_extruder', $i);
             $config->set_ifndef('support_material_interface_extruder', $i);
diff --git a/xs/src/Config.hpp b/xs/src/Config.hpp
index 9b9a7bf95..3af99a448 100644
--- a/xs/src/Config.hpp
+++ b/xs/src/Config.hpp
@@ -354,6 +354,7 @@ class ConfigOptionDef
     public:
     ConfigOptionType type;
     std::string label;
+    std::string full_label;
     std::string category;
     std::string tooltip;
     std::string sidetext;
@@ -361,7 +362,6 @@ class ConfigOptionDef
     std::string scope;
     t_config_option_key ratio_over;
     bool multiline;
-    bool full_label;
     bool full_width;
     bool readonly;
     int height;
diff --git a/xs/src/Print.cpp b/xs/src/Print.cpp
index f5dd9fe7d..539206c49 100644
--- a/xs/src/Print.cpp
+++ b/xs/src/Print.cpp
@@ -33,4 +33,11 @@ PrintState::invalidate(PrintStep step)
     this->_done.erase(step);
 }
 
+void
+PrintState::invalidate_all()
+{
+    this->_started.clear();
+    this->_done.clear();
+}
+
 }
diff --git a/xs/src/Print.hpp b/xs/src/Print.hpp
index a79d544df..9e93408b2 100644
--- a/xs/src/Print.hpp
+++ b/xs/src/Print.hpp
@@ -22,6 +22,7 @@ class PrintState
     void set_started(PrintStep step);
     void set_done(PrintStep step);
     void invalidate(PrintStep step);
+    void invalidate_all();
 };
 
 }
diff --git a/xs/src/PrintConfig.hpp b/xs/src/PrintConfig.hpp
index dd9f2633c..8ac01f504 100644
--- a/xs/src/PrintConfig.hpp
+++ b/xs/src/PrintConfig.hpp
@@ -61,7 +61,7 @@ class PrintConfigDef
         Options["bed_temperature"].tooltip = "Bed temperature for layers after the first one. Set this to zero to disable bed temperature control commands in the output.";
         Options["bed_temperature"].sidetext = "°C";
         Options["bed_temperature"].cli = "bed-temperature=i";
-        Options["bed_temperature"].full_label = true;
+        Options["bed_temperature"].full_label = "Bed temperature";
         Options["bed_temperature"].max = 300;
 
         Options["bottom_solid_layers"].type = coInt;
@@ -70,7 +70,7 @@ class PrintConfigDef
         Options["bottom_solid_layers"].tooltip = "Number of solid layers to generate on bottom surfaces.";
         Options["bottom_solid_layers"].cli = "bottom-solid-layers=i";
         Options["bottom_solid_layers"].scope = "object";
-        Options["bottom_solid_layers"].full_label = true;
+        Options["bottom_solid_layers"].full_label = "Bottom solid layers";
 
         Options["bridge_acceleration"].type = coFloat;
         Options["bridge_acceleration"].label = "Bridge";
@@ -346,7 +346,7 @@ class PrintConfigDef
         Options["infill_every_layers"].sidetext = "layers";
         Options["infill_every_layers"].cli = "infill-every-layers=i";
         Options["infill_every_layers"].scope = "object";
-        Options["infill_every_layers"].full_label = true;
+        Options["infill_every_layers"].full_label = "Combine infill every n layers";
         Options["infill_every_layers"].min = 1;
 
         Options["infill_extruder"].type = coInt;
@@ -715,7 +715,7 @@ class PrintConfigDef
         Options["support_material_enforce_layers"].sidetext = "layers";
         Options["support_material_enforce_layers"].cli = "support-material-enforce-layers=f";
         Options["support_material_enforce_layers"].scope = "object";
-        Options["support_material_enforce_layers"].full_label = true;
+        Options["support_material_enforce_layers"].full_label = "Enforce support for the first n layers";
 
         Options["support_material_extruder"].type = coInt;
         Options["support_material_extruder"].label = "Support material extruder";
@@ -789,7 +789,7 @@ class PrintConfigDef
         Options["temperature"].tooltip = "Extruder temperature for layers after the first one. Set this to zero to disable temperature control commands in the output.";
         Options["temperature"].sidetext = "°C";
         Options["temperature"].cli = "temperature=i@";
-        Options["temperature"].full_label = true;
+        Options["temperature"].full_label = "Temperature";
         Options["temperature"].max = 400;
 
         Options["thin_walls"].type = coBool;
@@ -835,7 +835,7 @@ class PrintConfigDef
         Options["top_solid_layers"].tooltip = "Number of solid layers to generate on top surfaces.";
         Options["top_solid_layers"].cli = "top-solid-layers=i";
         Options["top_solid_layers"].scope = "object";
-        Options["top_solid_layers"].full_label = true;
+        Options["top_solid_layers"].full_label = "Top solid layers";
 
         Options["travel_speed"].type = coFloat;
         Options["travel_speed"].label = "Travel";
diff --git a/xs/xsp/Config.xsp b/xs/xsp/Config.xsp
index b7f6f3ec8..18deda295 100644
--- a/xs/xsp/Config.xsp
+++ b/xs/xsp/Config.xsp
@@ -142,6 +142,7 @@ print_config_def()
             }
             (void)hv_stores( hv, "type",        newSVpv(opt_type, 0) );
             (void)hv_stores( hv, "label",       newSVpvn(optdef->label.c_str(), optdef->label.length()) );
+            (void)hv_stores( hv, "full_label",  newSVpvn(optdef->full_label.c_str(), optdef->full_label.length()) );
             (void)hv_stores( hv, "category",    newSVpvn(optdef->category.c_str(), optdef->category.length()) );
             (void)hv_stores( hv, "tooltip",     newSVpvn(optdef->tooltip.c_str(), optdef->tooltip.length()) );
             (void)hv_stores( hv, "sidetext",    newSVpvn(optdef->sidetext.c_str(), optdef->sidetext.length()) );
@@ -149,7 +150,6 @@ print_config_def()
             (void)hv_stores( hv, "scope",       newSVpvn(optdef->scope.c_str(), optdef->scope.length()) );
             (void)hv_stores( hv, "ratio_over",  newSVpvn(optdef->ratio_over.c_str(), optdef->ratio_over.length()) );
             (void)hv_stores( hv, "multiline",   newSViv(optdef->multiline ? 1 : 0) );
-            (void)hv_stores( hv, "full_label",  newSViv(optdef->full_label ? 1 : 0) );
             (void)hv_stores( hv, "full_width",  newSViv(optdef->full_width ? 1 : 0) );
             (void)hv_stores( hv, "readonly",    newSViv(optdef->readonly ? 1 : 0) );
             (void)hv_stores( hv, "height",      newSViv(optdef->height) );
diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp
index 0eef527ea..25f52a71c 100644
--- a/xs/xsp/Print.xsp
+++ b/xs/xsp/Print.xsp
@@ -13,6 +13,7 @@
     void set_started(PrintStep step);
     void set_done(PrintStep step);
     void invalidate(PrintStep step);
+    void invalidate_all();
 %{
 
 %}

From b9793b3f12ab5792232badd633098f41a14f4a5e Mon Sep 17 00:00:00 2001
From: Alessandro Ranellucci <aar@cpan.org>
Date: Fri, 3 Jan 2014 00:34:30 +0100
Subject: [PATCH 10/19] Moved first_layer_extrusion_width to PrintConfig

---
 lib/Slic3r/Print.pm                 |  7 +++----
 lib/Slic3r/Print/Object.pm          | 17 ++++++++++++----
 lib/Slic3r/Print/Region.pm          |  4 ++--
 lib/Slic3r/Print/SupportMaterial.pm | 31 ++++++++++++++++-------------
 xs/src/Config.hpp                   |  2 +-
 xs/src/PrintConfig.hpp              |  8 ++++----
 6 files changed, 40 insertions(+), 29 deletions(-)

diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm
index c0ed2e11f..d5884c10c 100644
--- a/lib/Slic3r/Print.pm
+++ b/lib/Slic3r/Print.pm
@@ -662,10 +662,9 @@ sub make_skirt {
     # skirt may be printed on several layers, having distinct layer heights,
     # but loops must be aligned so can't vary width/spacing
     # TODO: use each extruder's own flow
-    my $region0_config = $self->regions->[0]->config;
     my $first_layer_height = $self->objects->[0]->config->get_value('first_layer_height');
     my $flow = Slic3r::Flow->new(
-        width               => ($region0_config->first_layer_extrusion_width || $region0_config->perimeter_extrusion_width),
+        width               => ($self->config->first_layer_extrusion_width || $self->regions->[0]->config->perimeter_extrusion_width),
         role                => FLOW_ROLE_PERIMETER,
         nozzle_diameter     => $self->config->nozzle_diameter->[0],
         layer_height        => $first_layer_height,
@@ -713,7 +712,7 @@ sub make_brim {
     
     # brim is only printed on first layer and uses support material extruder
     my $flow = Slic3r::Flow->new(
-        width               => ($self->regions->[0]->config->first_layer_extrusion_width || $self->regions->[0]->config->perimeter_extrusion_width),
+        width               => ($self->config->first_layer_extrusion_width || $self->regions->[0]->config->perimeter_extrusion_width),
         role                => FLOW_ROLE_PERIMETER,
         nozzle_diameter     => $self->config->nozzle_diameter->[ $self->objects->[0]->config->support_material_extruder-1 ],
         layer_height        => $self->objects->[0]->config->get_abs_value('first_layer_height'),
@@ -801,7 +800,7 @@ sub write_gcode {
             if $self->has_support_material;
         printf $fh "; first layer extrusion width = %.2fmm\n",
             $self->regions->[$region_id]->flow(FLOW_ROLE_PERIMETER, $layer_height, 0, 1)->width
-            if ($self->regions->[$region_id]->config->first_layer_extrusion_width ne '0');
+            if $self->regions->[$region_id]->config->first_layer_extrusion_width;
         print  $fh "\n";
     }
     
diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm
index bf4c2efba..84dc33b36 100644
--- a/lib/Slic3r/Print/Object.pm
+++ b/lib/Slic3r/Print/Object.pm
@@ -863,11 +863,20 @@ sub generate_support_material {
     return unless ($self->config->support_material || $self->config->raft_layers > 0)
         && $self->layer_count >= 2;
     
+    my $first_layer_flow = Slic3r::Flow->new(
+        width               => ($self->config->first_layer_extrusion_width || $self->config->support_material_extrusion_width),
+        role                => FLOW_ROLE_SUPPORT_MATERIAL,
+        nozzle_diameter     => $self->print->config->nozzle_diameter->[ $self->config->support_material_extruder-1 ],
+        layer_height        => $self->config->get_abs_value('first_layer_height'),
+        bridge_flow_ratio   => 0,
+    );
+    
     my $s = Slic3r::Print::SupportMaterial->new(
-        print_config    => $self->print->config,
-        object_config   => $self->config,
-        flow            => $self->support_material_flow,
-        interface_flow  => $self->support_material_flow(FLOW_ROLE_SUPPORT_MATERIAL_INTERFACE),
+        print_config        => $self->print->config,
+        object_config       => $self->config,
+        first_layer_flow    => $first_layer_flow,
+        flow                => $self->support_material_flow,
+        interface_flow      => $self->support_material_flow(FLOW_ROLE_SUPPORT_MATERIAL_INTERFACE),
     );
     $s->generate($self);
 }
diff --git a/lib/Slic3r/Print/Region.pm b/lib/Slic3r/Print/Region.pm
index ff05d5bc9..c587f515f 100644
--- a/lib/Slic3r/Print/Region.pm
+++ b/lib/Slic3r/Print/Region.pm
@@ -21,8 +21,8 @@ sub flow {
     if (!defined $config_width) {
         # get extrusion width from configuration
         # (might be an absolute value, or a percent value, or zero for auto)
-        if ($first_layer && $self->config->first_layer_extrusion_width ne '0') {
-            $config_width = $self->config->first_layer_extrusion_width;
+        if ($first_layer && $self->print->config->first_layer_extrusion_width) {
+            $config_width = $self->print->config->first_layer_extrusion_width;
         } elsif ($role == FLOW_ROLE_PERIMETER) {
             $config_width = $self->config->perimeter_extrusion_width;
         } elsif ($role == FLOW_ROLE_INFILL) {
diff --git a/lib/Slic3r/Print/SupportMaterial.pm b/lib/Slic3r/Print/SupportMaterial.pm
index 0dae19e18..b6d793ee1 100644
--- a/lib/Slic3r/Print/SupportMaterial.pm
+++ b/lib/Slic3r/Print/SupportMaterial.pm
@@ -9,9 +9,11 @@ use Slic3r::Geometry::Clipper qw(offset diff union union_ex intersection offset_
     intersection_pl);
 use Slic3r::Surface ':types';
 
-has 'print_config'  => (is => 'rw', required => 1);
-has 'object_config' => (is => 'rw', required => 1);
-has 'flow'          => (is => 'rw', required => 1);
+has 'print_config'      => (is => 'rw', required => 1);
+has 'object_config'     => (is => 'rw', required => 1);
+has 'flow'              => (is => 'rw', required => 1);
+has 'first_layer_flow'  => (is => 'rw', required => 1);
+has 'interface_flow'    => (is => 'rw', required => 1);
 
 use constant DEBUG_CONTACT_ONLY => 0;
 
@@ -385,11 +387,12 @@ sub clip_with_object {
 sub generate_toolpaths {
     my ($self, $object, $overhang, $contact, $interface, $base) = @_;
     
-    my $flow = $self->flow;
+    my $flow            = $self->flow;
+    my $interface_flow  = $self->interface_flow;
     
     # shape of contact area
     my $contact_loops   = 1;
-    my $circle_radius   = 1.5 * $flow->scaled_width;
+    my $circle_radius   = 1.5 * $interface_flow->scaled_width;
     my $circle_distance = 3 * $circle_radius;
     my $circle          = Slic3r::Polygon->new(map [ $circle_radius * cos $_, $circle_radius * sin $_ ],
                             (5*PI/3, 4*PI/3, PI, 2*PI/3, PI/3, 0));
@@ -410,8 +413,8 @@ sub generate_toolpaths {
     );
     
     my $interface_angle = $self->object_config->support_material_angle + 90;
-    my $interface_spacing = $self->object_config->support_material_interface_spacing + $flow->spacing;
-    my $interface_density = $interface_spacing == 0 ? 1 : $flow->spacing / $interface_spacing;
+    my $interface_spacing = $self->object_config->support_material_interface_spacing + $interface_flow->spacing;
+    my $interface_density = $interface_spacing == 0 ? 1 : $interface_flow->spacing / $interface_spacing;
     my $support_spacing = $self->object_config->support_material_spacing + $flow->spacing;
     my $support_density = $support_spacing == 0 ? 1 : $flow->spacing / $support_spacing;
     
@@ -452,11 +455,11 @@ sub generate_toolpaths {
             my @loops0 = ();
             {
                 # find centerline of the external loop of the contours
-                my @external_loops = @{offset($contact, -$flow->scaled_width/2)};
+                my @external_loops = @{offset($contact, -$interface_flow->scaled_width/2)};
                 
                 # only consider the loops facing the overhang
                 {
-                    my $overhang_with_margin = offset($overhang, +$flow->scaled_width/2);
+                    my $overhang_with_margin = offset($overhang, +$interface_flow->scaled_width/2);
                     @external_loops = grep {
                         @{intersection_pl(
                             [ $_->split_at_first_point ],
@@ -476,8 +479,8 @@ sub generate_toolpaths {
             # make more loops
             my @loops = @loops0;
             for my $i (2..$contact_loops) {
-                my $d = ($i-1) * $flow->scaled_spacing;
-                push @loops, @{offset2(\@loops0, -$d -0.5*$flow->scaled_spacing, +0.5*$flow->scaled_spacing)};
+                my $d = ($i-1) * $interface_flow->scaled_spacing;
+                push @loops, @{offset2(\@loops0, -$d -0.5*$interface_flow->scaled_spacing, +0.5*$interface_flow->scaled_spacing)};
             }
             
             # clip such loops to the side oriented towards the object
@@ -500,7 +503,7 @@ sub generate_toolpaths {
             @loops = map Slic3r::ExtrusionPath->new(
                 polyline        => $_,
                 role            => EXTR_ROLE_SUPPORTMATERIAL,
-                flow_spacing    => $flow->spacing,
+                flow_spacing    => $interface_flow->spacing,
             ), @loops;
             
             $layer->support_interface_fills->append(@loops);
@@ -534,7 +537,7 @@ sub generate_toolpaths {
                 my ($params, @p) = $fillers{interface}->fill_surface(
                     Slic3r::Surface->new(expolygon => $expolygon, surface_type => S_TYPE_INTERNAL),
                     density         => $interface_density,
-                    flow_spacing    => $flow->spacing,
+                    flow_spacing    => $interface_flow->spacing,
                     complete        => 1,
                 );
                 
@@ -565,7 +568,7 @@ sub generate_toolpaths {
                 $filler = $fillers{interface};
                 $filler->angle($self->object_config->support_material_angle + 90);
                 $density        = 0.5;
-                $flow_spacing   = $object->print->first_layer_support_material_flow->spacing;
+                $flow_spacing   = $self->first_layer_flow->spacing;
             } else {
                 # draw a perimeter all around support infill
                 # TODO: use brim ordering algorithm
diff --git a/xs/src/Config.hpp b/xs/src/Config.hpp
index 3af99a448..9306a18b2 100644
--- a/xs/src/Config.hpp
+++ b/xs/src/Config.hpp
@@ -374,7 +374,7 @@ class ConfigOptionDef
     std::vector<std::string> enum_labels;
     t_config_enum_values enum_keys_map;
     
-    ConfigOptionDef() : multiline(false), full_label(false), full_width(false), readonly(false),
+    ConfigOptionDef() : multiline(false), full_width(false), readonly(false),
                         height(-1), width(-1), min(INT_MIN), max(INT_MAX) {};
 };
 
diff --git a/xs/src/PrintConfig.hpp b/xs/src/PrintConfig.hpp
index 8ac01f504..05380ab16 100644
--- a/xs/src/PrintConfig.hpp
+++ b/xs/src/PrintConfig.hpp
@@ -952,7 +952,6 @@ class PrintRegionConfig : public virtual StaticConfig
     ConfigOptionInt                 fill_angle;
     ConfigOptionFloat               fill_density;
     ConfigOptionEnum<InfillPattern> fill_pattern;
-    ConfigOptionFloatOrPercent      first_layer_extrusion_width;
     ConfigOptionInt                 infill_extruder;
     ConfigOptionFloatOrPercent      infill_extrusion_width;
     ConfigOptionInt                 infill_every_layers;
@@ -976,8 +975,6 @@ class PrintRegionConfig : public virtual StaticConfig
         this->fill_angle.value                                   = 45;
         this->fill_density.value                                 = 0.4;
         this->fill_pattern.value                                 = ipHoneycomb;
-        this->first_layer_extrusion_width.value                  = 200;
-        this->first_layer_extrusion_width.percent                = true;
         this->infill_extruder.value                              = 1;
         this->infill_extrusion_width.value                       = 0;
         this->infill_extrusion_width.percent                     = false;
@@ -1003,7 +1000,6 @@ class PrintRegionConfig : public virtual StaticConfig
         if (opt_key == "fill_angle")                                 return &this->fill_angle;
         if (opt_key == "fill_density")                               return &this->fill_density;
         if (opt_key == "fill_pattern")                               return &this->fill_pattern;
-        if (opt_key == "first_layer_extrusion_width")                return &this->first_layer_extrusion_width;
         if (opt_key == "infill_extruder")                            return &this->infill_extruder;
         if (opt_key == "infill_extrusion_width")                     return &this->infill_extrusion_width;
         if (opt_key == "infill_every_layers")                        return &this->infill_every_layers;
@@ -1052,6 +1048,7 @@ class PrintConfig : public virtual StaticConfig
     ConfigOptionFloats              filament_diameter;
     ConfigOptionFloat               first_layer_acceleration;
     ConfigOptionInt                 first_layer_bed_temperature;
+    ConfigOptionFloatOrPercent      first_layer_extrusion_width;
     ConfigOptionFloatOrPercent      first_layer_speed;
     ConfigOptionInts                first_layer_temperature;
     ConfigOptionBool                g0;
@@ -1142,6 +1139,8 @@ class PrintConfig : public virtual StaticConfig
         this->filament_diameter.values[0]                        = 3;
         this->first_layer_acceleration.value                     = 0;
         this->first_layer_bed_temperature.value                  = 0;
+        this->first_layer_extrusion_width.value                  = 200;
+        this->first_layer_extrusion_width.percent                = true;
         this->first_layer_speed.value                            = 30;
         this->first_layer_speed.percent                          = true;
         this->first_layer_temperature.values.resize(1);
@@ -1242,6 +1241,7 @@ class PrintConfig : public virtual StaticConfig
         if (opt_key == "filament_diameter")                          return &this->filament_diameter;
         if (opt_key == "first_layer_acceleration")                   return &this->first_layer_acceleration;
         if (opt_key == "first_layer_bed_temperature")                return &this->first_layer_bed_temperature;
+        if (opt_key == "first_layer_extrusion_width")                return &this->first_layer_extrusion_width;
         if (opt_key == "first_layer_speed")                          return &this->first_layer_speed;
         if (opt_key == "first_layer_temperature")                    return &this->first_layer_temperature;
         if (opt_key == "g0")                                         return &this->g0;

From 52ce6e4a7bfc8b223f68199cb79f910e26491914 Mon Sep 17 00:00:00 2001
From: Alessandro Ranellucci <aar@cpan.org>
Date: Fri, 3 Jan 2014 10:44:36 +0100
Subject: [PATCH 11/19] Some initial work for refactoring Print->extruders

---
 lib/Slic3r/Print.pm        | 20 ++++++++++++--------
 lib/Slic3r/Print/Object.pm |  2 +-
 2 files changed, 13 insertions(+), 9 deletions(-)

diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm
index d5884c10c..f7283954c 100644
--- a/lib/Slic3r/Print.pm
+++ b/lib/Slic3r/Print.pm
@@ -18,7 +18,6 @@ has 'default_region_config'  => (is => 'ro', default => sub { Slic3r::Config::Pr
 has 'extra_variables'        => (is => 'rw', default => sub {{}});
 has 'objects'                => (is => 'rw', default => sub {[]});
 has 'status_cb'              => (is => 'rw');
-has 'extruders'              => (is => 'rw', default => sub {[]});
 has 'regions'                => (is => 'rw', default => sub {[]});
 has '_state'                 => (is => 'ro', default => sub { Slic3r::Print::State->new });
 
@@ -282,8 +281,9 @@ sub validate {
     }
 }
 
-sub init_extruders {
-    my $self = shift;
+# 0-based indices of used extruders
+sub extruders {
+    my ($self) = @_;
     
     # initialize all extruder(s) we need
     my @used_extruders = ();
@@ -298,7 +298,15 @@ sub init_extruders {
             qw(support_material support_material_interface);
     }
     
-    for my $extruder_id (keys %{{ map {$_ => 1} @used_extruders }}) {
+    my %h = map { $_ => 1 } @used_extruders;
+    return [ sort keys %h ];
+}
+
+sub init_extruders {
+    my $self = shift;
+    
+    # initialize all extruder(s) we need
+    for my $extruder_id (@{$self->extruders}) {
         # make sure print config contains a value for all extruders
         my %extruder_config = ();
         foreach my $opt_key (@{&Slic3r::Extruder::OPTIONS}) {
@@ -845,10 +853,6 @@ sub write_gcode {
         }
     }
     
-    # always start with first extruder
-    # TODO: make sure we select the first *used* extruder
-    print $fh $gcodegen->set_extruder($self->extruders->[0]);
-    
     # initialize a motion planner for object-to-object travel moves
     if ($self->config->avoid_crossing_perimeters) {
         my $distance_from_objects = 1;
diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm
index 84dc33b36..fd07d73e5 100644
--- a/lib/Slic3r/Print/Object.pm
+++ b/lib/Slic3r/Print/Object.pm
@@ -758,7 +758,7 @@ sub combine_infill {
         my $every = $region->config->infill_every_layers;
         
         # limit the number of combined layers to the maximum height allowed by this regions' nozzle
-        my $nozzle_diameter = $self->print->regions->[$region_id]->extruders->{infill}->nozzle_diameter;
+        my $nozzle_diameter = $self->print->config->nozzle_diameter->[ $region->config->infill_extruder-1 ];
         
         # define the combinations
         my @combine = ();   # layer_id => thickness in layers

From 8ed738d3f7dcc5ae3d47415eff331f0f1e874da0 Mon Sep 17 00:00:00 2001
From: Alessandro Ranellucci <aar@cpan.org>
Date: Fri, 3 Jan 2014 18:27:46 +0100
Subject: [PATCH 12/19] More incomplete work for Flow/Extruder refactoring

---
 lib/Slic3r.pm                       |   1 -
 lib/Slic3r/Extruder.pm              |  56 ++++++--------
 lib/Slic3r/ExtrusionLoop.pm         |   3 +-
 lib/Slic3r/Fill.pm                  |  12 +--
 lib/Slic3r/Fill/Honeycomb.pm        |   6 +-
 lib/Slic3r/Fill/Rectilinear.pm      |  15 ++--
 lib/Slic3r/Flow.pm                  | 115 ++++++++++++++++++++--------
 lib/Slic3r/GCode.pm                 |  37 ++++-----
 lib/Slic3r/GCode/Layer.pm           |  12 +--
 lib/Slic3r/Layer/Region.pm          |  19 ++---
 lib/Slic3r/Print.pm                 |  48 +++++-------
 lib/Slic3r/Print/Object.pm          |   4 +-
 lib/Slic3r/Print/Region.pm          |   2 +-
 lib/Slic3r/Print/SupportMaterial.pm |  49 ++++++------
 t/arcs.t                            |   6 +-
 t/fill.t                            |   4 +-
 xs/lib/Slic3r/XS.pm                 |  12 +--
 xs/src/ExtrusionEntity.cpp          |   3 +-
 xs/src/ExtrusionEntity.hpp          |   3 +-
 xs/src/PrintConfig.hpp              |   3 +
 xs/t/07_extrusionpath.t             |   1 +
 xs/t/08_extrusionloop.t             |   1 +
 xs/t/12_extrusionpathcollection.t   |   6 +-
 xs/t/15_config.t                    |  15 +++-
 xs/xsp/Config.xsp                   |   2 +-
 xs/xsp/ExtrusionLoop.xsp            |  24 ++----
 xs/xsp/ExtrusionPath.xsp            |  24 ++----
 27 files changed, 250 insertions(+), 233 deletions(-)

diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm
index 1c5ffa74c..710e33e19 100644
--- a/lib/Slic3r.pm
+++ b/lib/Slic3r.pm
@@ -77,7 +77,6 @@ our $build = eval "use Slic3r::Build; 1";
 use constant SCALING_FACTOR         => 0.000001;
 use constant RESOLUTION             => 0.0125;
 use constant SCALED_RESOLUTION      => RESOLUTION / SCALING_FACTOR;
-use constant OVERLAP_FACTOR         => 1;
 use constant SMALL_PERIMETER_LENGTH => (6.5 / SCALING_FACTOR) * 2 * PI;
 use constant LOOP_CLIPPING_LENGTH_OVER_SPACING      => 0.15;
 use constant INFILL_OVERLAP_OVER_SPACING  => 0.45;
diff --git a/lib/Slic3r/Extruder.pm b/lib/Slic3r/Extruder.pm
index 9b8b1dc97..2a8371cef 100644
--- a/lib/Slic3r/Extruder.pm
+++ b/lib/Slic3r/Extruder.pm
@@ -26,13 +26,26 @@ has 'retracted'                 => (is => 'rw', default => sub {0} );
 has 'restart_extra'             => (is => 'rw', default => sub {0} );
 has 'e_per_mm3'                 => (is => 'lazy');
 has 'retract_speed_mm_min'      => (is => 'lazy');
-has '_mm3_per_mm_cache'         => (is => 'ro', default => sub {{}});
 
 use constant EXTRUDER_ROLE_PERIMETER                    => 1;
 use constant EXTRUDER_ROLE_INFILL                       => 2;
 use constant EXTRUDER_ROLE_SUPPORT_MATERIAL             => 3;
 use constant EXTRUDER_ROLE_SUPPORT_MATERIAL_INTERFACE   => 4;
 
+sub new_from_config {
+    my ($class, $config, $extruder_id) = @_;
+    
+    my %conf = (
+        id => $extruder_id,
+        use_relative_e_distances => $config->use_relative_e_distances,
+    );
+    foreach my $opt_key (@{&OPTIONS}) {
+        my $value = $config->get($opt_key);
+        $conf{$opt_key} = $value->[$extruder_id] // $value->[0];
+    }
+    return $class->new(%conf);
+}
+
 sub _build_e_per_mm3 {
     my $self = shift;
     return $self->extrusion_multiplier * (4 / (($self->filament_diameter ** 2) * PI));
@@ -43,6 +56,15 @@ sub _build_retract_speed_mm_min {
     return $self->retract_speed * 60;
 }
 
+sub reset {
+    my ($self) = @_;
+    
+    $self->E(0);
+    $self->absolute_E(0);
+    $self->retracted(0);
+    $self->restart_extra(0);
+}
+
 sub scaled_wipe_distance {
     my ($self, $travel_speed) = @_;
     
@@ -66,37 +88,9 @@ sub extruded_volume {
     return $self->absolute_E * ($self->filament_diameter**2) * PI/4;
 }
 
-sub make_flow {
-    my $self = shift;
-    return Slic3r::Flow->new(nozzle_diameter => $self->nozzle_diameter, @_);
-}
-
-sub mm3_per_mm {
-    my $self = shift;
-    my ($s, $h) = @_;
-    
-    my $cache_key = "${s}_${h}";
-    if (!exists $self->_mm3_per_mm_cache->{$cache_key}) {
-        my $w_threshold = $h + $self->nozzle_diameter;
-        my $s_threshold = $w_threshold - &Slic3r::OVERLAP_FACTOR * ($w_threshold - ($w_threshold - $h * (1 - PI/4)));
-        
-        if ($s >= $s_threshold) {
-            # rectangle with semicircles at the ends
-            my $w = $s + &Slic3r::OVERLAP_FACTOR * $h * (1 - PI/4);
-            $self->_mm3_per_mm_cache->{$cache_key} = $w * $h + ($h**2) / 4 * (PI - 4);
-        } else {
-            # rectangle with shrunk semicircles at the ends
-            my $w = ($s + $self->nozzle_diameter * &Slic3r::OVERLAP_FACTOR * (PI/4 - 1)) / (1 + &Slic3r::OVERLAP_FACTOR * (PI/4 - 1));
-            $self->_mm3_per_mm_cache->{$cache_key} = $self->nozzle_diameter * $h * (1 - PI/4) + $h * $w * PI/4;
-        }
-    }
-    return $self->_mm3_per_mm_cache->{$cache_key};
-}
-
 sub e_per_mm {
-    my $self = shift;
-    my ($s, $h) = @_;
-    return $self->mm3_per_mm($s, $h) * $self->e_per_mm3;
+    my ($self, $mm3_per_mm) = @_;
+    return $mm3_per_mm * $self->e_per_mm3;
 }
 
 1;
diff --git a/lib/Slic3r/ExtrusionLoop.pm b/lib/Slic3r/ExtrusionLoop.pm
index 57f380eec..7123b3a77 100644
--- a/lib/Slic3r/ExtrusionLoop.pm
+++ b/lib/Slic3r/ExtrusionLoop.pm
@@ -8,8 +8,7 @@ sub split_at {
     return Slic3r::ExtrusionPath->new(
         polyline        => $self->polygon->split_at(@_),
         role            => $self->role,
-        flow_spacing    => $self->flow_spacing,
-        height          => $self->height,
+        mm3_per_mm      => $self->mm3_per_mm,
     );
 }
 
diff --git a/lib/Slic3r/Fill.pm b/lib/Slic3r/Fill.pm
index 7969431c5..45508169f 100644
--- a/lib/Slic3r/Fill.pm
+++ b/lib/Slic3r/Fill.pm
@@ -158,20 +158,17 @@ sub make_fill {
             next SURFACE unless $density > 0;
         }
         
-        my $flow_spacing = $flow->spacing;
-        
         my $f = $self->filler($filler);
         $f->layer_id($layerm->id);
         $f->angle($layerm->config->fill_angle);
         my ($params, @polylines) = $f->fill_surface(
             $surface,
-            density         => $density,
-            flow_spacing    => $flow_spacing,
+            density => $density,
+            flow    => $flow,
         );
         next unless @polylines;
         
-        # ugly hack(tm) to get the right amount of flow (GCode.pm should be fixed)
-        $params->{flow_spacing} = $flow->width if $is_bridge;
+        my $mm3_per_mm = $params->{flow}->mm3_per_mm($surface->thickness);
         
         # save into layer
         push @fills, my $collection = Slic3r::ExtrusionPath::Collection->new;
@@ -187,8 +184,7 @@ sub make_fill {
                         : $is_solid
                             ? (($surface->surface_type == S_TYPE_TOP) ? EXTR_ROLE_TOPSOLIDFILL : EXTR_ROLE_SOLIDFILL)
                             : EXTR_ROLE_FILL),
-                height => $surface->thickness,
-                flow_spacing => $params->{flow_spacing} || (warn "Warning: no flow_spacing was returned by the infill engine, please report this to the developer\n"),
+                mm3_per_mm => $mm3_per_mm,
             ), @polylines,
         );
         push @fills_ordering_points, $polylines[0]->first_point;
diff --git a/lib/Slic3r/Fill/Honeycomb.pm b/lib/Slic3r/Fill/Honeycomb.pm
index caaa15407..1e6df2742 100644
--- a/lib/Slic3r/Fill/Honeycomb.pm
+++ b/lib/Slic3r/Fill/Honeycomb.pm
@@ -17,11 +17,11 @@ sub fill_surface {
     my $rotate_vector = $self->infill_direction($surface);
     
     # cache hexagons math
-    my $cache_id = sprintf "d%s_s%s", $params{density}, $params{flow_spacing};
+    my $cache_id = sprintf "d%s_s%s", $params{density}, $params{flow}->spacing;
     my $m;
     if (!($m = $self->cache->{$cache_id})) {
         $m = $self->cache->{$cache_id} = {};
-        my $min_spacing = scale $params{flow_spacing};
+        my $min_spacing = $params{flow}->scaled_spacing;
         $m->{distance} = $min_spacing / $params{density};
         $m->{hex_side} = $m->{distance} / (sqrt(3)/2);
         $m->{hex_width} = $m->{distance} * 2;  # $m->{hex_width} == $m->{hex_side} * sqrt(3);
@@ -120,7 +120,7 @@ sub fill_surface {
         )};
     }
     
-    return { flow_spacing => $params{flow_spacing} }, @paths;
+    return { flow => $params{flow} }, @paths;
 }
 
 1;
diff --git a/lib/Slic3r/Fill/Rectilinear.pm b/lib/Slic3r/Fill/Rectilinear.pm
index 335bf5b23..39fa5b941 100644
--- a/lib/Slic3r/Fill/Rectilinear.pm
+++ b/lib/Slic3r/Fill/Rectilinear.pm
@@ -17,8 +17,8 @@ sub fill_surface {
     my $rotate_vector = $self->infill_direction($surface);
     $self->rotate_points($expolygon, $rotate_vector);
     
-    my $flow_spacing        = $params{flow_spacing};
-    my $min_spacing         = scale $params{flow_spacing};
+    my $flow                = $params{flow};
+    my $min_spacing         = $flow->scaled_spacing;
     my $line_spacing        = $min_spacing / $params{density};
     my $line_oscillation    = $line_spacing - $min_spacing;
     my $is_line_pattern     = $self->isa('Slic3r::Fill::Line');
@@ -30,7 +30,12 @@ sub fill_surface {
             width       => $bounding_box->size->[X],
             distance    => $line_spacing,
         );
-        $flow_spacing = unscale $line_spacing;
+        $flow = Slic3r::Flow->new_from_spacing(
+            spacing             => unscale($line_spacing),
+            nozzle_diameter     => $flow->nozzle_diameter,
+            layer_height        => $surface->thickness,
+            bridge              => $flow->bridge,
+        );
     } else {
         # extend bounding box so that our pattern will be aligned with other layers
         $bounding_box->extents->[X][MIN] -= $bounding_box->x_min % $line_spacing;
@@ -62,7 +67,7 @@ sub fill_surface {
     
     # connect lines
     unless ($params{dont_connect} || !@polylines) {  # prevent calling leftmost_point() on empty collections
-        my ($expolygon_off) = @{$expolygon->offset_ex(scale $params{flow_spacing}/2)};
+        my ($expolygon_off) = @{$expolygon->offset_ex($min_spacing/2)};
         my $collection = Slic3r::Polyline::Collection->new(@polylines);
         @polylines = ();
         
@@ -97,7 +102,7 @@ sub fill_surface {
     # paths must be rotated back
     $self->rotate_points_back(\@polylines, $rotate_vector);
     
-    return { flow_spacing => $flow_spacing }, @polylines;
+    return { flow => $flow }, @polylines;
 }
 
 1;
diff --git a/lib/Slic3r/Flow.pm b/lib/Slic3r/Flow.pm
index 3930dce52..5b1502a73 100644
--- a/lib/Slic3r/Flow.pm
+++ b/lib/Slic3r/Flow.pm
@@ -9,8 +9,10 @@ our %EXPORT_TAGS = (roles => \@EXPORT_OK);
 
 use Slic3r::Geometry qw(PI);
 
-has 'width'             => (is => 'ro');
-has 'spacing'           => (is => 'ro');
+has 'width'             => (is => 'ro', required => 1);
+has 'spacing'           => (is => 'ro', required => 1);
+has 'nozzle_diameter'   => (is => 'ro', required => 1);
+has 'bridge'            => (is => 'ro', default => sub {0});
 has 'scaled_width'      => (is => 'lazy');
 has 'scaled_spacing'    => (is => 'lazy');
 
@@ -21,32 +23,67 @@ use constant FLOW_ROLE_TOP_SOLID_INFILL             => 4;
 use constant FLOW_ROLE_SUPPORT_MATERIAL             => 5;
 use constant FLOW_ROLE_SUPPORT_MATERIAL_INTERFACE   => 6;
 
-sub BUILDARGS {
-    my ($self, %args) = @_;
+use constant BRIDGE_EXTRA_SPACING   => 0.05;
+use constant OVERLAP_FACTOR         => 1;
+
+sub new_from_width {
+    my ($class, %args) = @_;
     
-    # the constructor can take two sets of arguments:
-    # - width (only absolute value), spacing
-    # - width (abs/%/0), role, nozzle_diameter, layer_height, bridge_flow_ratio
-    #   (if bridge_flow_ratio == 0, we return a non-bridge flow)
-    
-    if (exists $args{role}) {
-        if ($args{width} eq '0') {
-            $args{width} = $self->_width(@args{qw(role nozzle_diameter layer_height bridge_flow_ratio)});
-        } elsif ($args{width} =~ /^(\d+(?:\.\d+)?)%$/) {
-            $args{width} = $args{layer_height} * $1 / 100;
-        }
-        $args{spacing} = $self->_spacing(@args{qw(width nozzle_diameter layer_height bridge_flow_ratio)});
-        %args = (
-            width   => $args{width},
-            spacing => $args{spacing},
-        );
+    if ($args{width} eq '0') {
+        $args{width} = _width(@args{qw(role nozzle_diameter layer_height bridge_flow_ratio)});
+    } elsif ($args{width} =~ /^(\d+(?:\.\d+)?)%$/) {
+        $args{width} = $args{layer_height} * $1 / 100;
     }
     
-    return {%args};
+    return $class->new(
+        width           => $args{width},
+        spacing         => _spacing(@args{qw(width nozzle_diameter layer_height bridge_flow_ratio)}),
+        nozzle_diameter => $args{nozzle_diameter},
+        bridge          => ($args{bridge_flow_ratio} > 0) ? 1 : 0,
+    );
+}
+
+sub new_from_spacing {
+    my ($class, %args) = @_;
+    
+    return $class->new(
+        width           => _width_from_spacing(@args{qw(spacing nozzle_diameter layer_height bridge)}),
+        spacing         => $args{spacing},
+        nozzle_diameter => $args{nozzle_diameter},
+        bridge          => $args{bridge},
+    );
+}
+
+sub clone {
+    my $self = shift;
+    
+    return (ref $self)->new(
+        width           => $self->width,
+        spacing         => $self->spacing,
+        nozzle_diameter => $self->nozzle_diameter,
+        bridge          => $self->bridge,
+    );
+}
+
+sub mm3_per_mm {
+    my ($self, $h) = @_;
+    
+    my $w = $self->width;
+    my $s = $self->spacing;
+    
+    if ($self->bridge) {
+        return ($s**2) * PI/4;
+    } elsif ($w >= ($self->nozzle_diameter + $h)) {
+        # rectangle with semicircles at the ends
+        return $w * $h + ($h**2) / 4 * (PI - 4);
+    } else {
+        # rectangle with shrunk semicircles at the ends
+        return $self->nozzle_diameter * $h * (1 - PI/4) + $h * $w * PI/4;
+    }
 }
 
 sub _width {
-    my ($self, $role, $nozzle_diameter, $layer_height, $bridge_flow_ratio) = @_;
+    my ($role, $nozzle_diameter, $layer_height, $bridge_flow_ratio) = @_;
     
     if ($bridge_flow_ratio > 0) {
         return sqrt($bridge_flow_ratio * ($nozzle_diameter**2));
@@ -78,11 +115,30 @@ sub _width {
     return $width;
 }
 
+sub _width_from_spacing {
+    my ($s, $nozzle_diameter, $h, $bridge) = @_;
+    
+    if ($bridge) {
+        return $s - BRIDGE_EXTRA_SPACING;
+    }
+    
+    my $w_threshold = $h + $nozzle_diameter;
+    my $s_threshold = $w_threshold - OVERLAP_FACTOR * ($w_threshold - ($w_threshold - $h * (1 - PI/4)));
+    
+    if ($s >= $s_threshold) {
+        # rectangle with semicircles at the ends
+        return $s + OVERLAP_FACTOR * $h * (1 - PI/4);
+    } else {
+        # rectangle with shrunk semicircles at the ends
+        return ($s + $nozzle_diameter * OVERLAP_FACTOR * (PI/4 - 1)) / (1 + OVERLAP_FACTOR * (PI/4 - 1));
+    }
+}
+
 sub _spacing {
-    my ($self, $width, $nozzle_diameter, $layer_height, $bridge_flow_ratio) = @_;
+    my ($width, $nozzle_diameter, $layer_height, $bridge_flow_ratio) = @_;
     
     if ($bridge_flow_ratio > 0) {
-        return $width + 0.05;
+        return $width + BRIDGE_EXTRA_SPACING;
     }
     
     my $min_flow_spacing;
@@ -93,16 +149,7 @@ sub _spacing {
         # rectangle with shrunk semicircles at the ends
         $min_flow_spacing = $nozzle_diameter * (1 - PI/4) + $width * PI/4;
     }
-    return $width - &Slic3r::OVERLAP_FACTOR * ($width - $min_flow_spacing);
-}
-
-sub clone {
-    my $self = shift;
-    
-    return (ref $self)->new(
-        width   => $self->width,
-        spacing => $self->spacing,
-    );
+    return $width - OVERLAP_FACTOR * ($width - $min_flow_spacing);
 }
 
 sub _build_scaled_width {
diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm
index 66175260f..a8f7526ad 100644
--- a/lib/Slic3r/GCode.pm
+++ b/lib/Slic3r/GCode.pm
@@ -10,8 +10,6 @@ use Slic3r::Surface ':types';
 
 has 'print_config'       => (is => 'ro', default => sub { Slic3r::Config::Print->new });
 has 'extra_variables'    => (is => 'rw', default => sub {{}});
-has 'extruders'          => (is => 'ro', required => 1);
-has 'multiple_extruders' => (is => 'lazy');
 has 'standby_points'     => (is => 'rw');
 has 'enable_loop_clipping' => (is => 'rw', default => sub {1});
 has 'enable_wipe'        => (is => 'lazy');   # at least one extruder has wipe enabled
@@ -27,6 +25,7 @@ has 'z'                  => (is => 'rw');
 has 'speed'              => (is => 'rw');
 has '_extrusion_axis'    => (is => 'rw');
 has '_retract_lift'      => (is => 'rw');
+has 'extruders'          => (is => 'ro', default => sub {[]});
 
 has 'speeds'             => (is => 'lazy');  # mm/min
 has 'external_mp'        => (is => 'rw');
@@ -49,6 +48,14 @@ sub BUILD {
     $self->_retract_lift($self->print_config->retract_lift->[0]);
 }
 
+sub set_extruders {
+    my ($self, $extruder_ids) = @_;
+    
+    foreach my $i (@$extruder_ids) {
+        $self->extruders->[$i] = Slic3r::Extruder->new_from_config($self->print_config, $i);
+    }
+}
+
 sub _build_speeds {
     my $self = shift;
     return {
@@ -73,7 +80,7 @@ my %role_speeds = (
     &EXTR_ROLE_GAPFILL                      => 'gap_fill',
 );
 
-sub _build_multiple_extruders {
+sub multiple_extruders {
     my $self = shift;
     return @{$self->extruders} > 1;
 }
@@ -320,18 +327,8 @@ sub extrude_path {
         $gcode .= $self->set_acceleration($acceleration) if $acceleration;
     }
     
-    my $area;  # mm^3 of extrudate per mm of tool movement 
-    if ($path->is_bridge) {
-        my $s = $path->flow_spacing;
-        $area = ($s**2) * PI/4;
-    } else {
-        my $s = $path->flow_spacing;
-        my $h = (defined $path->height && $path->height != -1) ? $path->height : $self->layer->height;
-        $area = $self->extruder->mm3_per_mm($s, $h);
-    }
-    
     # calculate extrusion length per distance unit
-    my $e = $self->extruder->e_per_mm3 * $area;
+    my $e = $self->extruder->e_per_mm3 * $path->mm3_per_mm;
     $e = 0 if !$self->_extrusion_axis;
     
     # set speed
@@ -640,14 +637,14 @@ sub _Gx {
 }
 
 sub set_extruder {
-    my ($self, $extruder) = @_;
+    my ($self, $extruder_id) = @_;
     
     # return nothing if this extruder was already selected
-    return "" if (defined $self->extruder) && ($self->extruder->id == $extruder->id);
+    return "" if (defined $self->extruder) && ($self->extruder->id == $extruder_id);
     
     # if we are running a single-extruder setup, just set the extruder and return nothing
     if (!$self->multiple_extruders) {
-        $self->extruder($extruder);
+        $self->extruder($self->extruders->[$extruder_id]);
         return "";
     }
     
@@ -659,7 +656,7 @@ sub set_extruder {
     if (defined $self->extruder && $self->print_config->toolchange_gcode) {
         $gcode .= sprintf "%s\n", $self->replace_variables($self->print_config->toolchange_gcode, {
             previous_extruder   => $self->extruder->id,
-            next_extruder       => $extruder->id,
+            next_extruder       => $extruder_id,
         });
     }
     
@@ -676,14 +673,14 @@ sub set_extruder {
     }
     
     # set the new extruder
-    $self->extruder($extruder);
+    $self->extruder($self->extruders->[$extruder_id]);
     $gcode .= sprintf "%s%d%s\n", 
         ($self->print_config->gcode_flavor eq 'makerware'
             ? 'M135 T'
             : $self->print_config->gcode_flavor eq 'sailfish'
                 ? 'M108 T'
                 : 'T'),
-        $extruder->id,
+        $extruder_id,
         ($self->print_config->gcode_comments ? ' ; change extruder' : '');
     
     $gcode .= $self->reset_e;
diff --git a/lib/Slic3r/GCode/Layer.pm b/lib/Slic3r/GCode/Layer.pm
index f64b6a1ba..96029eaab 100644
--- a/lib/Slic3r/GCode/Layer.pm
+++ b/lib/Slic3r/GCode/Layer.pm
@@ -85,7 +85,7 @@ sub process_layer {
                 # when printing layers > 0 ignore 'min_skirt_length' and 
                 # just use the 'skirts' setting; also just use the current extruder
                 last if ($layer->id > 0) && ($i >= $self->print->config->skirts);
-                $gcode .= $self->gcodegen->set_extruder($self->extruders->[ ($i/@{$self->extruders}) % @{$self->extruders} ])
+                $gcode .= $self->gcodegen->set_extruder(($i/@{$self->extruders}) % @{$self->extruders})
                     if $layer->id == 0;
                 $gcode .= $self->gcodegen->extrude_loop($skirt_loops[$i], 'skirt');
             }
@@ -96,7 +96,7 @@ sub process_layer {
     
     # extrude brim
     if (!$self->brim_done) {
-        $gcode .= $self->gcodegen->set_extruder($self->extruders->[$self->print->objects->[0]->config->support_material_extruder-1]);
+        $gcode .= $self->gcodegen->set_extruder($self->print->objects->[0]->config->support_material_extruder-1);
         $self->gcodegen->set_shift(@{$self->shift});
         $gcode .= $self->gcodegen->extrude_loop($_, 'brim') for @{$self->print->brim};
         $self->brim_done(1);
@@ -113,13 +113,13 @@ sub process_layer {
         # and also because we avoid travelling on other things when printing it
         if ($layer->isa('Slic3r::Layer::Support')) {
             if ($layer->support_interface_fills->count > 0) {
-                $gcode .= $self->gcodegen->set_extruder($self->extruders->[$object->config->support_material_interface_extruder-1]);
+                $gcode .= $self->gcodegen->set_extruder($object->config->support_material_interface_extruder-1);
                 my %params = (speed => $object->config->support_material_speed*60);
                 $gcode .= $self->gcodegen->extrude_path($_, 'support material interface', %params) 
                     for @{$layer->support_interface_fills->chained_path_from($self->gcodegen->last_pos, 0)}; 
             }
             if ($layer->support_fills->count > 0) {
-                $gcode .= $self->gcodegen->set_extruder($self->extruders->[$object->config->support_material_extruder-1]);
+                $gcode .= $self->gcodegen->set_extruder($object->config->support_material_extruder-1);
                 my %params = (speed => $object->config->support_material_speed*60);
                 $gcode .= $self->gcodegen->extrude_path($_, 'support material', %params) 
                     for @{$layer->support_fills->chained_path_from($self->gcodegen->last_pos, 0)};
@@ -205,7 +205,7 @@ sub _extrude_perimeters {
     return "" if !@{ $island->{perimeters} };
     
     my $gcode = "";
-    $gcode .= $self->gcodegen->set_extruder($self->extruders->[$region->config->perimeter_extruder-1]);
+    $gcode .= $self->gcodegen->set_extruder($region->config->perimeter_extruder-1);
     $gcode .= $self->gcodegen->extrude($_, 'perimeter') for @{ $island->{perimeters} };
     return $gcode;
 }
@@ -217,7 +217,7 @@ sub _extrude_infill {
     return "" if !@{ $island->{fills} };
     
     my $gcode = "";
-    $gcode .= $self->gcodegen->set_extruder($self->extruders->[$region->config->infill_extruder-1]);
+    $gcode .= $self->gcodegen->set_extruder($region->config->infill_extruder-1);
     for my $fill (@{ $island->{fills} }) {
         if ($fill->isa('Slic3r::ExtrusionPath::Collection')) {
             $gcode .= $self->gcodegen->extrude($_, 'fill') 
diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm
index ce22d9c88..53b3834d3 100644
--- a/lib/Slic3r/Layer/Region.pm
+++ b/lib/Slic3r/Layer/Region.pm
@@ -125,6 +125,7 @@ sub make_perimeters {
     my $self = shift;
     
     my $perimeter_flow      = $self->flow(FLOW_ROLE_PERIMETER);
+    my $mm3_per_mm          = $perimeter_flow->mm3_per_mm($self->height);
     my $pwidth              = $perimeter_flow->scaled_width;
     my $pspacing            = $perimeter_flow->scaled_spacing;
     my $ispacing            = $self->flow(FLOW_ROLE_SOLID_INFILL)->scaled_spacing;
@@ -264,7 +265,7 @@ sub make_perimeters {
             push @loops, Slic3r::ExtrusionLoop->new(
                 polygon         => $polygon,
                 role            => $role,
-                flow_spacing    => $perimeter_flow->spacing,
+                mm3_per_mm      => $mm3_per_mm,
             );
         }
         return @loops;
@@ -290,8 +291,8 @@ sub make_perimeters {
         for my $p (@p) {
             next if $p->length <= $pspacing * 2;
             my %params = (
-                role            => EXTR_ROLE_EXTERNAL_PERIMETER,
-                flow_spacing    => $perimeter_flow->spacing,
+                role        => EXTR_ROLE_EXTERNAL_PERIMETER,
+                mm3_per_mm  => $mm3_per_mm,
             );
             push @paths, $p->isa('Slic3r::Polygon')
                 ? Slic3r::ExtrusionLoop->new(polygon  => $p, %params)
@@ -339,8 +340,8 @@ sub _fill_gaps {
             # fill gaps using dynamic extrusion width, by treating them like thin polygons,
             # thus generating the skeleton and using it to fill them
             my %path_args = (
-                role            => EXTR_ROLE_SOLIDFILL,
-                flow_spacing    => $flow->spacing,
+                role        => EXTR_ROLE_SOLIDFILL,
+                mm3_per_mm  => $flow->mm3_per_mm($self->height),
             );
             $self->thin_fills->append(map {
                 $_->isa('Slic3r::Polygon')
@@ -360,9 +361,10 @@ sub _fill_gaps {
             foreach my $expolygon (@infill) {
                 my ($params, @paths) = $filler->fill_surface(
                     Slic3r::Surface->new(expolygon => $expolygon, surface_type => S_TYPE_INTERNALSOLID),
-                    density         => 1,
-                    flow_spacing    => $flow->spacing,
+                    density => 1,
+                    flow    => $flow,
                 );
+                my $mm3_per_mm = $params->{flow}->mm3_per_mm($self->height);
                 
                 # Split polylines into lines so that the chained_path() search
                 # at the final stage has more freedom and will choose starting
@@ -379,8 +381,7 @@ sub _fill_gaps {
                 @paths = map Slic3r::ExtrusionPath->new(
                     polyline        => Slic3r::Polyline->new(@$_),
                     role            => EXTR_ROLE_GAPFILL,
-                    height          => $self->height,
-                    flow_spacing    => $params->{flow_spacing},
+                    mm3_per_mm      => $mm3_per_mm,
                 ), @lines;
                 $_->simplify($flow->scaled_width/3) for @paths;
                 
diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm
index f7283954c..ebaba5ce1 100644
--- a/lib/Slic3r/Print.pm
+++ b/lib/Slic3r/Print.pm
@@ -305,26 +305,6 @@ sub extruders {
 sub init_extruders {
     my $self = shift;
     
-    # initialize all extruder(s) we need
-    for my $extruder_id (@{$self->extruders}) {
-        # make sure print config contains a value for all extruders
-        my %extruder_config = ();
-        foreach my $opt_key (@{&Slic3r::Extruder::OPTIONS}) {
-            my $value = $self->config->get($opt_key);
-            if (!defined $value->[$extruder_id]) {
-                $value->[$extruder_id] = $value->[0];
-                $self->config->set($opt_key, $value);
-            }
-            $extruder_config{$opt_key} = $value->[$extruder_id];
-        }
-        
-        $self->extruders->[$extruder_id] = Slic3r::Extruder->new(
-            id => $extruder_id,
-            use_relative_e_distances => $self->config->use_relative_e_distances,
-            %extruder_config,
-        );
-    }
-    
     # enforce tall skirt if using ooze_prevention
     # FIXME: this is not idempotent (i.e. switching ooze_prevention off will not revert skirt settings)
     if ($self->config->ooze_prevention && @{$self->extruders} > 1) {
@@ -671,7 +651,7 @@ sub make_skirt {
     # but loops must be aligned so can't vary width/spacing
     # TODO: use each extruder's own flow
     my $first_layer_height = $self->objects->[0]->config->get_value('first_layer_height');
-    my $flow = Slic3r::Flow->new(
+    my $flow = Slic3r::Flow->new_from_width(
         width               => ($self->config->first_layer_extrusion_width || $self->regions->[0]->config->perimeter_extrusion_width),
         role                => FLOW_ROLE_PERIMETER,
         nozzle_diameter     => $self->config->nozzle_diameter->[0],
@@ -679,6 +659,7 @@ sub make_skirt {
         bridge_flow_ratio   => 0,
     );
     my $spacing = $flow->spacing;
+    my $mm3_per_mm = $flow->mm3_per_mm($first_layer_height);
     
     my @extruders_e_per_mm = ();
     my $extruder_idx = 0;
@@ -692,13 +673,16 @@ sub make_skirt {
         $self->skirt->append(Slic3r::ExtrusionLoop->new(
             polygon         => Slic3r::Polygon->new(@$loop),
             role            => EXTR_ROLE_SKIRT,
-            flow_spacing    => $spacing,
+            mm3_per_mm      => $mm3_per_mm,
         ));
         
         if ($self->config->min_skirt_length > 0) {
-            $extruded_length[$extruder_idx]     ||= 0;
-            $extruders_e_per_mm[$extruder_idx]  ||= $self->extruders->[$extruder_idx]->e_per_mm($spacing, $first_layer_height);
-            $extruded_length[$extruder_idx]     += unscale $loop->length * $extruders_e_per_mm[$extruder_idx];
+            $extruded_length[$extruder_idx] ||= 0;
+            if (!$extruders_e_per_mm[$extruder_idx]) {
+                my $extruder = Slic3r::Extruder->new_from_config($self->config, $extruder_idx);
+                $extruders_e_per_mm[$extruder_idx] = $extruder->e_per_mm($mm3_per_mm);
+            }
+            $extruded_length[$extruder_idx] += unscale $loop->length * $extruders_e_per_mm[$extruder_idx];
             $i++ if defined first { ($extruded_length[$_] // 0) < $self->config->min_skirt_length } 0 .. $#{$self->extruders};
             if ($extruded_length[$extruder_idx] >= $self->config->min_skirt_length) {
                 if ($extruder_idx < $#{$self->extruders}) {
@@ -719,13 +703,15 @@ sub make_brim {
     $self->brim->clear;  # method must be idempotent
     
     # brim is only printed on first layer and uses support material extruder
-    my $flow = Slic3r::Flow->new(
+    my $first_layer_height = $self->objects->[0]->config->get_abs_value('first_layer_height');
+    my $flow = Slic3r::Flow->new_from_width(
         width               => ($self->config->first_layer_extrusion_width || $self->regions->[0]->config->perimeter_extrusion_width),
         role                => FLOW_ROLE_PERIMETER,
         nozzle_diameter     => $self->config->nozzle_diameter->[ $self->objects->[0]->config->support_material_extruder-1 ],
-        layer_height        => $self->objects->[0]->config->get_abs_value('first_layer_height'),
+        layer_height        => $first_layer_height,
         bridge_flow_ratio   => 0,
     );
+    my $mm3_per_mm = $flow->mm3_per_mm($first_layer_height);
     
     my $grow_distance = $flow->scaled_width / 2;
     my @islands = (); # array of polygons
@@ -768,7 +754,7 @@ sub make_brim {
     $self->brim->append(map Slic3r::ExtrusionLoop->new(
         polygon         => Slic3r::Polygon->new(@$_),
         role            => EXTR_ROLE_SKIRT,
-        flow_spacing    => $flow->spacing,
+        mm3_per_mm      => $mm3_per_mm,
     ), reverse @{union_pt_chained(\@loops)});
 }
 
@@ -816,9 +802,11 @@ sub write_gcode {
     my $gcodegen = Slic3r::GCode->new(
         print_config        => $self->config,
         extra_variables     => $self->extra_variables,
-        extruders           => $self->extruders,    # we should only pass the *used* extruders (but maintain the Tx indices right!)
         layer_count         => $self->layer_count,
     );
+    $gcodegen->set_extruders($self->extruders);
+    $gcodegen->set_extruder($self->extruders->[0]);
+    
     print $fh "G21 ; set units to millimeters\n" if $self->config->gcode_flavor ne 'makerware';
     print $fh $gcodegen->set_fan(0, 1) if $self->config->cooling && $self->config->disable_fan_first_layers;
     
@@ -833,7 +821,7 @@ sub write_gcode {
         
         return if $self->config->start_gcode =~ /M(?:109|104)/i;
         for my $t (0 .. $#{$self->extruders}) {
-            my $temp = $self->extruders->[$t]->first_layer_temperature;
+            my $temp = $self->config->first_layer_temperature->[$t] // $self->config->first_layer_temperature->[0];
             $temp += $self->config->standby_temperature_delta if $self->config->ooze_prevention;
             printf $fh $gcodegen->set_temperature($temp, $wait, $t) if $temp > 0;
         }
diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm
index fd07d73e5..2d9e9b53c 100644
--- a/lib/Slic3r/Print/Object.pm
+++ b/lib/Slic3r/Print/Object.pm
@@ -863,7 +863,7 @@ sub generate_support_material {
     return unless ($self->config->support_material || $self->config->raft_layers > 0)
         && $self->layer_count >= 2;
     
-    my $first_layer_flow = Slic3r::Flow->new(
+    my $first_layer_flow = Slic3r::Flow->new_from_width(
         width               => ($self->config->first_layer_extrusion_width || $self->config->support_material_extrusion_width),
         role                => FLOW_ROLE_SUPPORT_MATERIAL,
         nozzle_diameter     => $self->print->config->nozzle_diameter->[ $self->config->support_material_extruder-1 ],
@@ -900,7 +900,7 @@ sub support_material_flow {
     
     # we use a bogus layer_height because we use the same flow for all
     # support material layers
-    return Slic3r::Flow->new(
+    return Slic3r::Flow->new_from_width(
         width               => $self->config->support_material_extrusion_width,
         role                => $role,
         nozzle_diameter     => $self->print->config->nozzle_diameter->[$extruder-1],
diff --git a/lib/Slic3r/Print/Region.pm b/lib/Slic3r/Print/Region.pm
index c587f515f..472968478 100644
--- a/lib/Slic3r/Print/Region.pm
+++ b/lib/Slic3r/Print/Region.pm
@@ -48,7 +48,7 @@ sub flow {
     }
     my $nozzle_diameter = $self->print->config->nozzle_diameter->[$extruder-1];
     
-    return Slic3r::Flow->new(
+    return Slic3r::Flow->new_from_width(
         width               => $config_width,
         role                => $role,
         nozzle_diameter     => $nozzle_diameter,
diff --git a/lib/Slic3r/Print/SupportMaterial.pm b/lib/Slic3r/Print/SupportMaterial.pm
index b6d793ee1..a88728e67 100644
--- a/lib/Slic3r/Print/SupportMaterial.pm
+++ b/lib/Slic3r/Print/SupportMaterial.pm
@@ -500,10 +500,11 @@ sub generate_toolpaths {
             );
             
             # transform loops into ExtrusionPath objects
+            my $mm3_per_mm = $interface_flow->mm3_per_mm($layer->height);
             @loops = map Slic3r::ExtrusionPath->new(
-                polyline        => $_,
-                role            => EXTR_ROLE_SUPPORTMATERIAL,
-                flow_spacing    => $interface_flow->spacing,
+                polyline    => $_,
+                role        => EXTR_ROLE_SUPPORTMATERIAL,
+                mm3_per_mm  => $mm3_per_mm,
             ), @loops;
             
             $layer->support_interface_fills->append(@loops);
@@ -536,16 +537,16 @@ sub generate_toolpaths {
             foreach my $expolygon (@{union_ex($interface)}) {
                 my ($params, @p) = $fillers{interface}->fill_surface(
                     Slic3r::Surface->new(expolygon => $expolygon, surface_type => S_TYPE_INTERNAL),
-                    density         => $interface_density,
-                    flow_spacing    => $interface_flow->spacing,
-                    complete        => 1,
+                    density     => $interface_density,
+                    flow        => $interface_flow,
+                    complete    => 1,
                 );
+                my $mm3_per_mm = $params->{flow}->mm3_per_mm($layer->height);
                 
                 push @paths, map Slic3r::ExtrusionPath->new(
-                    polyline        => Slic3r::Polyline->new(@$_),
-                    role            => EXTR_ROLE_SUPPORTMATERIAL,
-                    height          => undef,
-                    flow_spacing    => $params->{flow_spacing},
+                    polyline    => Slic3r::Polyline->new(@$_),
+                    role        => EXTR_ROLE_SUPPORTMATERIAL,
+                    mm3_per_mm  => $mm3_per_mm,
                 ), @p;
             }
             
@@ -556,8 +557,8 @@ sub generate_toolpaths {
         if (@$base) {
             my $filler = $fillers{support};
             $filler->angle($angles[ ($layer_id) % @angles ]);
-            my $density         = $support_density;
-            my $flow_spacing    = $flow->spacing;
+            my $density     = $support_density;
+            my $base_flow   = $flow;
             
             # TODO: use offset2_ex()
             my $to_infill = union_ex($base, 1);
@@ -568,15 +569,15 @@ sub generate_toolpaths {
                 $filler = $fillers{interface};
                 $filler->angle($self->object_config->support_material_angle + 90);
                 $density        = 0.5;
-                $flow_spacing   = $self->first_layer_flow->spacing;
+                $base_flow      = $self->first_layer_flow;
             } else {
                 # draw a perimeter all around support infill
                 # TODO: use brim ordering algorithm
+                my $mm3_per_mm = $flow->mm3_per_mm($layer->height);
                 push @paths, map Slic3r::ExtrusionPath->new(
-                    polyline        => $_->split_at_first_point,
-                    role            => EXTR_ROLE_SUPPORTMATERIAL,
-                    height          => undef,
-                    flow_spacing    => $flow->spacing,
+                    polyline    => $_->split_at_first_point,
+                    role        => EXTR_ROLE_SUPPORTMATERIAL,
+                    mm3_per_mm  => $mm3_per_mm,
                 ), map @$_, @$to_infill;
                 
                 # TODO: use offset2_ex()
@@ -586,16 +587,16 @@ sub generate_toolpaths {
             foreach my $expolygon (@$to_infill) {
                 my ($params, @p) = $filler->fill_surface(
                     Slic3r::Surface->new(expolygon => $expolygon, surface_type => S_TYPE_INTERNAL),
-                    density         => $density,
-                    flow_spacing    => $flow_spacing,
-                    complete        => 1,
+                    density     => $density,
+                    flow        => $base_flow,
+                    complete    => 1,
                 );
+                my $mm3_per_mm = $params->{flow}->mm3_per_mm($layer->height);
                 
                 push @paths, map Slic3r::ExtrusionPath->new(
-                    polyline        => Slic3r::Polyline->new(@$_),
-                    role            => EXTR_ROLE_SUPPORTMATERIAL,
-                    height          => undef,
-                    flow_spacing    => $params->{flow_spacing},
+                    polyline    => Slic3r::Polyline->new(@$_),
+                    role        => EXTR_ROLE_SUPPORTMATERIAL,
+                    mm3_per_mm  => $mm3_per_mm,
                 ), @p;
             }
             
diff --git a/t/arcs.t b/t/arcs.t
index 92f83964b..2eac2acf6 100644
--- a/t/arcs.t
+++ b/t/arcs.t
@@ -21,7 +21,7 @@ use Slic3r::Geometry qw(scaled_epsilon scale X Y);
         [306517.1,219034.23], [286979.42,248012.49], [258001.16,267550.17], [222515.14,274714.47], 
         [187029.11,267550.17], [158050.85,248012.49], [138513.17,219034.23], [131348.87,183548.2], 
         [86948.77,175149.09], [119825.35,100585],
-    ), role => EXTR_ROLE_FILL, flow_spacing => 0.5);
+    ), role => EXTR_ROLE_FILL, mm3_per_mm => 0.5);
     
     my @paths = $path->detect_arcs(30);
     
@@ -42,12 +42,12 @@ use Slic3r::Geometry qw(scaled_epsilon scale X Y);
     my $path1 = Slic3r::ExtrusionPath->new(
         polyline    => Slic3r::Polyline->new(@points),
         role        => EXTR_ROLE_FILL,
-        flow_spacing => 0.5,
+        mm3_per_mm  => 0.5,
     );
     my $path2 = Slic3r::ExtrusionPath->new(
         polyline    => Slic3r::Polyline->new(reverse @points),
         role        => EXTR_ROLE_FILL,
-        flow_spacing => 0.5,
+        mm3_per_mm  => 0.5,
     );
     
     my @paths1 = $path1->detect_arcs(10, scale 1);
diff --git a/t/fill.t b/t/fill.t
index 2be4b87c1..424b206df 100644
--- a/t/fill.t
+++ b/t/fill.t
@@ -134,7 +134,7 @@ sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ }
 
 {
     my $collection = Slic3r::ExtrusionPath::Collection->new(
-        map Slic3r::ExtrusionPath->new(polyline => $_, role => 0),
+        map Slic3r::ExtrusionPath->new(polyline => $_, role => 0, mm3_per_mm => 1),
             Slic3r::Polyline->new([0,15], [0,18], [0,20]),
             Slic3r::Polyline->new([0,10], [0,8], [0,5]),
     );
@@ -146,7 +146,7 @@ sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ }
 
 {
     my $collection = Slic3r::ExtrusionPath::Collection->new(
-        map Slic3r::ExtrusionPath->new(polyline => $_, role => 0),
+        map Slic3r::ExtrusionPath->new(polyline => $_, role => 0, mm3_per_mm => 1),
             Slic3r::Polyline->new([15,0], [10,0], [4,0]),
             Slic3r::Polyline->new([10,5], [15,5], [20,5]),
     );
diff --git a/xs/lib/Slic3r/XS.pm b/xs/lib/Slic3r/XS.pm
index 9f761ba9a..9e0e678d8 100644
--- a/xs/lib/Slic3r/XS.pm
+++ b/xs/lib/Slic3r/XS.pm
@@ -97,8 +97,7 @@ sub new {
     return $class->_new(
         $args{polygon},      # required
         $args{role},         # required
-        $args{height}        // -1,
-        $args{flow_spacing}  // -1,
+        $args{mm3_per_mm}   // -1,
     );
 }
 
@@ -108,8 +107,7 @@ sub clone {
     return (ref $self)->_new(
         $args{polygon}       // $self->polygon,
         $args{role}          // $self->role,
-        $args{height}        // $self->height,
-        $args{flow_spacing}  // $self->flow_spacing,
+        $args{mm3_per_mm}    // $self->mm3_per_mm,
     );
 }
 
@@ -129,8 +127,7 @@ sub new {
     return $class->_new(
         $args{polyline},     # required
         $args{role},         # required
-        $args{height}        // -1,
-        $args{flow_spacing}  // -1,
+        $args{mm3_per_mm}   // -1,
     );
 }
 
@@ -140,8 +137,7 @@ sub clone {
     return (ref $self)->_new(
         $args{polyline}      // $self->polyline,
         $args{role}          // $self->role,
-        $args{height}        // $self->height,
-        $args{flow_spacing}  // $self->flow_spacing,
+        $args{mm3_per_mm}    // $self->mm3_per_mm,
     );
 }
 
diff --git a/xs/src/ExtrusionEntity.cpp b/xs/src/ExtrusionEntity.cpp
index 9c5cd5d31..c6641a716 100644
--- a/xs/src/ExtrusionEntity.cpp
+++ b/xs/src/ExtrusionEntity.cpp
@@ -116,8 +116,7 @@ ExtrusionLoop::split_at_index(int index) const
     ExtrusionPath* path = new ExtrusionPath();
     path->polyline      = *poly;
     path->role          = this->role;
-    path->height        = this->height;
-    path->flow_spacing  = this->flow_spacing;
+    path->mm3_per_mm    = this->mm3_per_mm;
     
     delete poly;
     return path;
diff --git a/xs/src/ExtrusionEntity.hpp b/xs/src/ExtrusionEntity.hpp
index c763ad77e..381fd1fd1 100644
--- a/xs/src/ExtrusionEntity.hpp
+++ b/xs/src/ExtrusionEntity.hpp
@@ -31,8 +31,7 @@ class ExtrusionEntity
     virtual ExtrusionEntity* clone() const = 0;
     virtual ~ExtrusionEntity() {};
     ExtrusionRole role;
-    double height;  // vertical thickness of the extrusion expressed in mm
-    double flow_spacing;
+    double mm3_per_mm;  // mm^3 of plastic per mm of linear head motion
     virtual void reverse() = 0;
     virtual Point* first_point() const = 0;
     virtual Point* last_point() const = 0;
diff --git a/xs/src/PrintConfig.hpp b/xs/src/PrintConfig.hpp
index 05380ab16..c5adff7c1 100644
--- a/xs/src/PrintConfig.hpp
+++ b/xs/src/PrintConfig.hpp
@@ -241,6 +241,7 @@ class PrintConfigDef
         Options["fill_pattern"].tooltip = "Fill pattern for general low-density infill.";
         Options["fill_pattern"].cli = "fill-pattern=s";
         Options["fill_pattern"].scope = "object";
+        Options["fill_pattern"].enum_keys_map = ConfigOptionEnum<InfillPattern>::get_enum_values();
         Options["fill_pattern"].enum_values.push_back("rectilinear");
         Options["fill_pattern"].enum_values.push_back("line");
         Options["fill_pattern"].enum_values.push_back("concentric");
@@ -320,6 +321,7 @@ class PrintConfigDef
         Options["gcode_flavor"].label = "G-code flavor";
         Options["gcode_flavor"].tooltip = "Some G/M-code commands, including temperature control and others, are not universal. Set this option to your printer's firmware to get a compatible output. The \"No extrusion\" flavor prevents Slic3r from exporting any extrusion value at all.";
         Options["gcode_flavor"].cli = "gcode-flavor=s";
+        Options["gcode_flavor"].enum_keys_map = ConfigOptionEnum<GCodeFlavor>::get_enum_values();
         Options["gcode_flavor"].enum_values.push_back("reprap");
         Options["gcode_flavor"].enum_values.push_back("teacup");
         Options["gcode_flavor"].enum_values.push_back("makerware");
@@ -613,6 +615,7 @@ class PrintConfigDef
         Options["solid_fill_pattern"].tooltip = "Fill pattern for top/bottom infill.";
         Options["solid_fill_pattern"].cli = "solid-fill-pattern=s";
         Options["solid_fill_pattern"].scope = "object";
+        Options["solid_fill_pattern"].enum_keys_map = ConfigOptionEnum<InfillPattern>::get_enum_values();
         Options["solid_fill_pattern"].enum_values.push_back("rectilinear");
         Options["solid_fill_pattern"].enum_values.push_back("concentric");
         Options["solid_fill_pattern"].enum_values.push_back("hilbertcurve");
diff --git a/xs/t/07_extrusionpath.t b/xs/t/07_extrusionpath.t
index 78e084ccd..1c2ed3f2d 100644
--- a/xs/t/07_extrusionpath.t
+++ b/xs/t/07_extrusionpath.t
@@ -15,6 +15,7 @@ my $points = [
 my $path = Slic3r::ExtrusionPath->new(
     polyline => Slic3r::Polyline->new(@$points),
     role     => Slic3r::ExtrusionPath::EXTR_ROLE_EXTERNAL_PERIMETER,
+    mm3_per_mm => 1,
 );
 isa_ok $path->polyline, 'Slic3r::Polyline::Ref', 'path polyline';
 is_deeply $path->polyline->pp, $points, 'path points roundtrip';
diff --git a/xs/t/08_extrusionloop.t b/xs/t/08_extrusionloop.t
index bd729361f..54ef72ca7 100644
--- a/xs/t/08_extrusionloop.t
+++ b/xs/t/08_extrusionloop.t
@@ -16,6 +16,7 @@ my $square = [
 my $loop = Slic3r::ExtrusionLoop->new(
     polygon  => Slic3r::Polygon->new(@$square),
     role     => Slic3r::ExtrusionPath::EXTR_ROLE_EXTERNAL_PERIMETER,
+    mm3_per_mm => 1,
 );
 isa_ok $loop->polygon, 'Slic3r::Polygon::Ref', 'loop polygon';
 is_deeply $loop->polygon->pp, $square, 'polygon points roundtrip';
diff --git a/xs/t/12_extrusionpathcollection.t b/xs/t/12_extrusionpathcollection.t
index e28c04b6d..4d5b9c589 100644
--- a/xs/t/12_extrusionpathcollection.t
+++ b/xs/t/12_extrusionpathcollection.t
@@ -15,11 +15,13 @@ my $points = [
 my $path = Slic3r::ExtrusionPath->new(
     polyline => Slic3r::Polyline->new(@$points),
     role     => Slic3r::ExtrusionPath::EXTR_ROLE_EXTERNAL_PERIMETER,
+    mm3_per_mm => 1,
 );
 
 my $loop = Slic3r::ExtrusionLoop->new(
     polygon => Slic3r::Polygon->new(@$points),
     role     => Slic3r::ExtrusionPath::EXTR_ROLE_FILL,
+    mm3_per_mm => 1,
 );
 
 my $collection = Slic3r::ExtrusionPath::Collection->new($path);
@@ -49,7 +51,7 @@ is scalar(@{$collection->[1]}), 1, 'appended collection was duplicated';
 
 {
     my $collection = Slic3r::ExtrusionPath::Collection->new(
-        map Slic3r::ExtrusionPath->new(polyline => $_, role => 0),
+        map Slic3r::ExtrusionPath->new(polyline => $_, role => 0, mm3_per_mm => 1),
             Slic3r::Polyline->new([0,15], [0,18], [0,20]),
             Slic3r::Polyline->new([0,10], [0,8], [0,5]),
     );
@@ -65,7 +67,7 @@ is scalar(@{$collection->[1]}), 1, 'appended collection was duplicated';
 
 {
     my $collection = Slic3r::ExtrusionPath::Collection->new(
-        map Slic3r::ExtrusionPath->new(polyline => $_, role => 0),
+        map Slic3r::ExtrusionPath->new(polyline => $_, role => 0, mm3_per_mm => 1),
             Slic3r::Polyline->new([15,0], [10,0], [4,0]),
             Slic3r::Polyline->new([10,5], [15,5], [20,5]),
     );
diff --git a/xs/t/15_config.t b/xs/t/15_config.t
index 4e8f6e68a..0d2080602 100644
--- a/xs/t/15_config.t
+++ b/xs/t/15_config.t
@@ -4,7 +4,7 @@ use strict;
 use warnings;
 
 use Slic3r::XS;
-use Test::More tests => 79;
+use Test::More tests => 82;
 
 foreach my $config (Slic3r::Config->new, Slic3r::Config::Full->new) {
     $config->set('layer_height', 0.3);
@@ -49,6 +49,9 @@ foreach my $config (Slic3r::Config->new, Slic3r::Config::Full->new) {
     $config->set_deserialize('gcode_flavor', 'mach3');
     is $config->get('gcode_flavor'), 'mach3', 'deserialize enum';
     
+    $config->set_deserialize('fill_pattern', 'line');
+    is $config->get('fill_pattern'), 'line', 'deserialize enum';
+    
     $config->set('extruder_offset', [[10,20],[30,45]]);
     is_deeply $config->get('extruder_offset'), [[10,20],[30,45]], 'set/get points';
     is $config->serialize('extruder_offset'), '10x20,30x45', 'serialize points';
@@ -106,4 +109,14 @@ foreach my $config (Slic3r::Config->new, Slic3r::Config::Full->new) {
     is $config2->get('perimeters'), Slic3r::Config::print_config_def()->{perimeters}{default}, 'apply_static and print_config_def';
 }
 
+{
+    my $config = Slic3r::Config->new;
+    $config->set('fill_pattern', 'line');
+
+    my $config2 = Slic3r::Config->new;
+    $config2->set('fill_pattern', 'hilbertcurve');
+    
+    is $config->get('fill_pattern'), 'line', 'no interferences between DynamicConfig objects';
+}
+
 __END__
diff --git a/xs/xsp/Config.xsp b/xs/xsp/Config.xsp
index 18deda295..743b38ca1 100644
--- a/xs/xsp/Config.xsp
+++ b/xs/xsp/Config.xsp
@@ -19,7 +19,7 @@
         double get_abs_value(t_config_option_key opt_key, double ratio_over);
     void apply(DynamicPrintConfig* other)
         %code{% THIS->apply(*other, true); %};
-    void apply_static(PrintConfig* other)
+    void apply_static(FullPrintConfig* other)
         %code{% THIS->apply(*other, true); %};
     std::vector<std::string> get_keys()
         %code{% THIS->keys(&RETVAL); %};
diff --git a/xs/xsp/ExtrusionLoop.xsp b/xs/xsp/ExtrusionLoop.xsp
index 9bef01d42..fbf4dfca3 100644
--- a/xs/xsp/ExtrusionLoop.xsp
+++ b/xs/xsp/ExtrusionLoop.xsp
@@ -28,18 +28,16 @@
 %{
 
 ExtrusionLoop*
-_new(CLASS, polygon_sv, role, height, flow_spacing)
+_new(CLASS, polygon_sv, role, mm3_per_mm)
     char*           CLASS;
     SV*             polygon_sv;
     ExtrusionRole   role;
-    double          height;
-    double          flow_spacing;
+    double          mm3_per_mm;
     CODE:
         RETVAL = new ExtrusionLoop ();
         RETVAL->polygon.from_SV_check(polygon_sv);
         RETVAL->role            = role;
-        RETVAL->height          = height;
-        RETVAL->flow_spacing    = flow_spacing;
+        RETVAL->mm3_per_mm      = mm3_per_mm;
     OUTPUT:
         RETVAL
 
@@ -66,22 +64,12 @@ ExtrusionLoop::role(...)
         RETVAL
 
 double
-ExtrusionLoop::height(...)
+ExtrusionLoop::mm3_per_mm(...)
     CODE:
         if (items > 1) {
-            THIS->height = (double)SvNV(ST(1));
+            THIS->mm3_per_mm = (double)SvNV(ST(1));
         }
-        RETVAL = THIS->height;
-    OUTPUT:
-        RETVAL
-
-double
-ExtrusionLoop::flow_spacing(...)
-    CODE:
-        if (items > 1) {
-            THIS->flow_spacing = (double)SvNV(ST(1));
-        }
-        RETVAL = THIS->flow_spacing;
+        RETVAL = THIS->mm3_per_mm;
     OUTPUT:
         RETVAL
 
diff --git a/xs/xsp/ExtrusionPath.xsp b/xs/xsp/ExtrusionPath.xsp
index b4248214c..9d0abd70d 100644
--- a/xs/xsp/ExtrusionPath.xsp
+++ b/xs/xsp/ExtrusionPath.xsp
@@ -33,18 +33,16 @@
 %{
 
 ExtrusionPath*
-_new(CLASS, polyline_sv, role, height, flow_spacing)
+_new(CLASS, polyline_sv, role, mm3_per_mm)
     char*           CLASS;
     SV*             polyline_sv;
     ExtrusionRole   role;
-    double          height;
-    double          flow_spacing;
+    double          mm3_per_mm;
     CODE:
         RETVAL = new ExtrusionPath ();
         RETVAL->polyline.from_SV_check(polyline_sv);
         RETVAL->role            = role;
-        RETVAL->height          = height;
-        RETVAL->flow_spacing    = flow_spacing;
+        RETVAL->mm3_per_mm      = mm3_per_mm;
     OUTPUT:
         RETVAL
 
@@ -71,22 +69,12 @@ ExtrusionPath::role(...)
         RETVAL
 
 double
-ExtrusionPath::height(...)
+ExtrusionPath::mm3_per_mm(...)
     CODE:
         if (items > 1) {
-            THIS->height = (double)SvNV(ST(1));
+            THIS->mm3_per_mm = (double)SvNV(ST(1));
         }
-        RETVAL = THIS->height;
-    OUTPUT:
-        RETVAL
-
-double
-ExtrusionPath::flow_spacing(...)
-    CODE:
-        if (items > 1) {
-            THIS->flow_spacing = (double)SvNV(ST(1));
-        }
-        RETVAL = THIS->flow_spacing;
+        RETVAL = THIS->mm3_per_mm;
     OUTPUT:
         RETVAL
 

From a2cbb261cbef9b95b7e900fac3408fc3513fe52b Mon Sep 17 00:00:00 2001
From: Alessandro Ranellucci <aar@cpan.org>
Date: Fri, 3 Jan 2014 20:02:00 +0100
Subject: [PATCH 13/19] More work for refactoring Flow/Extruder

---
 lib/Slic3r.pm                  |  2 +-
 lib/Slic3r/Fill.pm             |  4 +++-
 lib/Slic3r/Fill/Concentric.pm  | 16 +++++++++++-----
 lib/Slic3r/Fill/PlanePath.pm   |  5 +++--
 lib/Slic3r/Fill/Rectilinear.pm |  2 +-
 lib/Slic3r/GCode.pm            |  6 +++---
 lib/Slic3r/Print.pm            | 10 +++++++++-
 lib/Slic3r/Print/Simple.pm     |  3 ++-
 slic3r.pl                      |  5 ++---
 t/fill.t                       | 16 +++++++++++++---
 10 files changed, 48 insertions(+), 21 deletions(-)

diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm
index 710e33e19..f2bea526c 100644
--- a/lib/Slic3r.pm
+++ b/lib/Slic3r.pm
@@ -78,7 +78,7 @@ use constant SCALING_FACTOR         => 0.000001;
 use constant RESOLUTION             => 0.0125;
 use constant SCALED_RESOLUTION      => RESOLUTION / SCALING_FACTOR;
 use constant SMALL_PERIMETER_LENGTH => (6.5 / SCALING_FACTOR) * 2 * PI;
-use constant LOOP_CLIPPING_LENGTH_OVER_SPACING      => 0.15;
+use constant LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER => 0.15;
 use constant INFILL_OVERLAP_OVER_SPACING  => 0.45;
 use constant EXTERNAL_INFILL_MARGIN => 3;
 
diff --git a/lib/Slic3r/Fill.pm b/lib/Slic3r/Fill.pm
index 45508169f..c128d0eb3 100644
--- a/lib/Slic3r/Fill.pm
+++ b/lib/Slic3r/Fill.pm
@@ -168,7 +168,9 @@ sub make_fill {
         );
         next unless @polylines;
         
-        my $mm3_per_mm = $params->{flow}->mm3_per_mm($surface->thickness);
+        my $h = $surface->thickness;
+        $h = $layerm->height if $h == -1;
+        my $mm3_per_mm = $params->{flow}->mm3_per_mm($h);
         
         # save into layer
         push @fills, my $collection = Slic3r::ExtrusionPath::Collection->new;
diff --git a/lib/Slic3r/Fill/Concentric.pm b/lib/Slic3r/Fill/Concentric.pm
index 5649a8aa7..0bb1f5061 100644
--- a/lib/Slic3r/Fill/Concentric.pm
+++ b/lib/Slic3r/Fill/Concentric.pm
@@ -15,16 +15,22 @@ sub fill_surface {
     my $expolygon = $surface->expolygon;
     my $bounding_box = $expolygon->bounding_box;
     
-    my $min_spacing = scale $params{flow_spacing};
+    my $flow = $params{flow};
+    my $min_spacing = $flow->scaled_spacing;
     my $distance = $min_spacing / $params{density};
     
-    my $flow_spacing = $params{flow_spacing};
+    my $flow_spacing = $flow->spacing;
     if ($params{density} == 1 && !$params{dont_adjust}) {
         $distance = $self->adjust_solid_spacing(
             width       => $bounding_box->size->[X],
             distance    => $distance,
         );
-        $flow_spacing = unscale $distance;
+        $flow = Slic3r::Flow->new_from_spacing(
+            spacing             => unscale($distance),
+            nozzle_diameter     => $flow->nozzle_diameter,
+            layer_height        => $surface->thickness,
+            bridge              => $flow->bridge,
+        );
     }
     
     # compensate the overlap which is good for rectilinear but harmful for concentric
@@ -48,11 +54,11 @@ sub fill_surface {
     }
     
     # clip the paths to avoid the extruder to get exactly on the first point of the loop
-    my $clip_length = scale $flow_spacing * &Slic3r::LOOP_CLIPPING_LENGTH_OVER_SPACING;
+    my $clip_length = scale($flow->nozzle_diameter) * &Slic3r::LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER;
     $_->clip_end($clip_length) for @paths;
     
     # TODO: return ExtrusionLoop objects to get better chained paths
-    return { flow_spacing => $flow_spacing, no_sort => 1 }, @paths;
+    return { flow => $flow, no_sort => 1 }, @paths;
 }
 
 1;
diff --git a/lib/Slic3r/Fill/PlanePath.pm b/lib/Slic3r/Fill/PlanePath.pm
index 0797fd10e..d393f9049 100644
--- a/lib/Slic3r/Fill/PlanePath.pm
+++ b/lib/Slic3r/Fill/PlanePath.pm
@@ -27,7 +27,8 @@ sub fill_surface {
     my $rotate_vector = $self->infill_direction($surface);
     $self->rotate_points($expolygon, $rotate_vector);
     
-    my $distance_between_lines = scale $params{flow_spacing} / $params{density} * $self->multiplier;
+    my $flow = $params{flow};
+    my $distance_between_lines = $flow->scaled_spacing / $params{density} * $self->multiplier;
     my $bounding_box = $expolygon->bounding_box;
     
     (ref $self) =~ /::([^:]+)$/;
@@ -54,7 +55,7 @@ sub fill_surface {
     # paths must be rotated back
     $self->rotate_points_back(\@paths, $rotate_vector);
     
-    return { flow_spacing => $params{flow_spacing} }, @paths;
+    return { flow => $flow }, @paths;
 }
 
 1;
diff --git a/lib/Slic3r/Fill/Rectilinear.pm b/lib/Slic3r/Fill/Rectilinear.pm
index 39fa5b941..acf7c1090 100644
--- a/lib/Slic3r/Fill/Rectilinear.pm
+++ b/lib/Slic3r/Fill/Rectilinear.pm
@@ -17,7 +17,7 @@ sub fill_surface {
     my $rotate_vector = $self->infill_direction($surface);
     $self->rotate_points($expolygon, $rotate_vector);
     
-    my $flow                = $params{flow};
+    my $flow                = $params{flow} or die "No flow supplied to fill_surface()";
     my $min_spacing         = $flow->scaled_spacing;
     my $line_spacing        = $min_spacing / $params{density};
     my $line_oscillation    = $line_spacing - $min_spacing;
diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm
index a8f7526ad..b1c05d95d 100644
--- a/lib/Slic3r/GCode.pm
+++ b/lib/Slic3r/GCode.pm
@@ -235,7 +235,7 @@ sub extrude_loop {
     # clip the path to avoid the extruder to get exactly on the first point of the loop;
     # if polyline was shorter than the clipping distance we'd get a null polyline, so
     # we discard it in that case
-    $extrusion_path->clip_end(scale $extrusion_path->flow_spacing * &Slic3r::LOOP_CLIPPING_LENGTH_OVER_SPACING)
+    $extrusion_path->clip_end(scale($self->extruder->nozzle_diameter) * &Slic3r::LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER)
         if $self->enable_loop_clipping;
     return '' if !@{$extrusion_path->polyline};
     
@@ -251,7 +251,7 @@ sub extrude_loop {
         push @paths,
             map {
                 $_->role(EXTR_ROLE_OVERHANG_PERIMETER);
-                $_->flow_spacing($self->region->flow(FLOW_ROLE_PERIMETER, undef, 1)->width);
+                $_->mm3_per_mm($self->region->flow(FLOW_ROLE_PERIMETER, undef, 1)->mm3_per_mm(undef));
                 $_
             }
             map $_->clone,
@@ -287,7 +287,7 @@ sub extrude_loop {
         # we make sure we don't exceed the segment length because we don't know
         # the rotation of the second segment so we might cross the object boundary
         my $first_segment = Slic3r::Line->new(@{$extrusion_path->polyline}[0,1]);
-        my $distance = min(scale $extrusion_path->flow_spacing, $first_segment->length);
+        my $distance = min(scale($self->extruder->nozzle_diameter), $first_segment->length);
         my $point = $first_segment->point_at($distance);
         $point->rotate($angle, $extrusion_path->first_point);
         
diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm
index ebaba5ce1..f31e6665a 100644
--- a/lib/Slic3r/Print.pm
+++ b/lib/Slic3r/Print.pm
@@ -19,6 +19,8 @@ has 'extra_variables'        => (is => 'rw', default => sub {{}});
 has 'objects'                => (is => 'rw', default => sub {[]});
 has 'status_cb'              => (is => 'rw');
 has 'regions'                => (is => 'rw', default => sub {[]});
+has 'total_used_filament'    => (is => 'rw');
+has 'total_extruded_volume'  => (is => 'rw');
 has '_state'                 => (is => 'ro', default => sub { Slic3r::Print::State->new });
 
 # ordered collection of extrusion paths to build skirt loops
@@ -970,7 +972,13 @@ sub write_gcode {
     print $fh $gcodegen->set_fan(0);
     printf $fh "%s\n", $gcodegen->replace_variables($self->config->end_gcode);
     
-    foreach my $extruder (@{$self->extruders}) {
+    $self->total_used_filament(0);
+    $self->total_extruded_volume(0);
+    foreach my $extruder_id (@{$self->extruders}) {
+        my $extruder = $gcodegen->extruders->[$extruder_id];
+        $self->total_used_filament($self->total_used_filament + $extruder->absolute_E);
+        $self->total_extruded_volume($self->total_extruded_volume + $extruder->extruded_volume);
+        
         printf $fh "; filament used = %.1fmm (%.1fcm3)\n",
             $extruder->absolute_E, $extruder->extruded_volume/1000;
     }
diff --git a/lib/Slic3r/Print/Simple.pm b/lib/Slic3r/Print/Simple.pm
index a6f756343..ded003063 100644
--- a/lib/Slic3r/Print/Simple.pm
+++ b/lib/Slic3r/Print/Simple.pm
@@ -6,7 +6,8 @@ use Slic3r::Geometry qw(X Y);
 has '_print' => (
     is      => 'ro',
     default => sub { Slic3r::Print->new },
-    handles => [qw(apply_config extruders expanded_output_filepath)],
+    handles => [qw(apply_config extruders expanded_output_filepath
+                    total_used_filament total_extruded_volume)],
 );
 
 has 'duplicate' => (
diff --git a/slic3r.pl b/slic3r.pl
index bb2f7280e..e27e7d323 100755
--- a/slic3r.pl
+++ b/slic3r.pl
@@ -163,9 +163,8 @@ if (@ARGV) {  # slicing from command line
                 printf "Done. Process took %d minutes and %.3f seconds\n", 
                     int($duration/60), ($duration - int($duration/60)*60);  # % truncates to integer
             }
-            print map sprintf("Filament required: %.1fmm (%.1fcm3)\n",
-                $_->absolute_E, $_->extruded_volume/1000),
-                @{$sprint->extruders};
+            printf "Filament required: %.1fmm (%.1fcm3)\n",
+                $sprint->total_used_filament, $sprint->total_extruded_volume/1000;
         }
     }
 } else {
diff --git a/t/fill.t b/t/fill.t
index 424b206df..8e112484a 100644
--- a/t/fill.t
+++ b/t/fill.t
@@ -43,9 +43,14 @@ sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ }
         surface_type    => S_TYPE_TOP,
         expolygon       => $expolygon,
     );
+    my $flow = Slic3r::Flow->new(
+        width           => 0.69,
+        spacing         => 0.69,
+        nozzle_diameter => 0.50,
+    );
     foreach my $angle (0, 45) {
         $surface->expolygon->rotate(Slic3r::Geometry::deg2rad($angle), [0,0]);
-        my ($params, @paths) = $filler->fill_surface($surface, flow_spacing => 0.69, density => 0.4);
+        my ($params, @paths) = $filler->fill_surface($surface, flow => $flow, density => 0.4);
         is scalar @paths, 1, 'one continuous path';
     }
 }
@@ -62,14 +67,19 @@ sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ }
             surface_type    => S_TYPE_BOTTOM,
             expolygon       => $expolygon,
         );
+        my $flow = Slic3r::Flow->new(
+            width           => $flow_spacing,
+            spacing         => $flow_spacing,
+            nozzle_diameter => $flow_spacing,
+        );
         my ($params, @paths) = $filler->fill_surface(
             $surface,
-            flow_spacing    => $flow_spacing,
+            flow            => $flow,
             density         => $density // 1,
         );
         
         # check whether any part was left uncovered
-        my @grown_paths = map @{Slic3r::Polyline->new(@$_)->grow(scale $params->{flow_spacing}/2)}, @paths;
+        my @grown_paths = map @{Slic3r::Polyline->new(@$_)->grow(scale $params->{flow}->spacing/2)}, @paths;
         my $uncovered = diff_ex([ @$expolygon ], [ @grown_paths ], 1);
         
         # ignore very small dots

From 07b9b12475ddcc206252abb43cb457db06dd334b Mon Sep 17 00:00:00 2001
From: Alessandro Ranellucci <aar@cpan.org>
Date: Sun, 5 Jan 2014 00:36:33 +0100
Subject: [PATCH 14/19] Make tests pass

---
 lib/Slic3r/Extruder.pm              |  3 +--
 lib/Slic3r/Flow.pm                  |  4 +--
 lib/Slic3r/GCode.pm                 | 35 ++++++++++--------------
 lib/Slic3r/GCode/Layer.pm           | 16 ++++++-----
 lib/Slic3r/Print.pm                 | 16 ++++++-----
 lib/Slic3r/Print/Object.pm          | 13 ++++-----
 lib/Slic3r/Print/Region.pm          |  2 +-
 lib/Slic3r/Print/SupportMaterial.pm |  4 +--
 t/combineinfill.t                   | 35 ++++++++++++++++++++++--
 t/retraction.t                      | 18 ++++++-------
 t/support.t                         |  8 +++++-
 xs/src/Config.cpp                   | 32 ++++++++++++++++++----
 xs/src/Config.hpp                   | 41 +++++++++++++++++++----------
 xs/src/PrintConfig.hpp              |  4 +--
 xs/t/15_config.t                    |  5 +++-
 xs/xsp/Config.xsp                   |  5 ++++
 16 files changed, 159 insertions(+), 82 deletions(-)

diff --git a/lib/Slic3r/Extruder.pm b/lib/Slic3r/Extruder.pm
index 2a8371cef..514aec7c7 100644
--- a/lib/Slic3r/Extruder.pm
+++ b/lib/Slic3r/Extruder.pm
@@ -40,8 +40,7 @@ sub new_from_config {
         use_relative_e_distances => $config->use_relative_e_distances,
     );
     foreach my $opt_key (@{&OPTIONS}) {
-        my $value = $config->get($opt_key);
-        $conf{$opt_key} = $value->[$extruder_id] // $value->[0];
+        $conf{$opt_key} = $config->get_at($opt_key, $extruder_id);
     }
     return $class->new(%conf);
 }
diff --git a/lib/Slic3r/Flow.pm b/lib/Slic3r/Flow.pm
index 5b1502a73..f07be98f5 100644
--- a/lib/Slic3r/Flow.pm
+++ b/lib/Slic3r/Flow.pm
@@ -72,7 +72,7 @@ sub mm3_per_mm {
     my $s = $self->spacing;
     
     if ($self->bridge) {
-        return ($s**2) * PI/4;
+        return ($w**2) * PI/4;
     } elsif ($w >= ($self->nozzle_diameter + $h)) {
         # rectangle with semicircles at the ends
         return $w * $h + ($h**2) / 4 * (PI - 4);
@@ -140,7 +140,7 @@ sub _spacing {
     if ($bridge_flow_ratio > 0) {
         return $width + BRIDGE_EXTRA_SPACING;
     }
-    
+    use XXX; ZZZ "here" if !defined $nozzle_diameter;
     my $min_flow_spacing;
     if ($width >= ($nozzle_diameter + $layer_height)) {
         # rectangle with semicircles at the ends
diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm
index b1c05d95d..78324f3bd 100644
--- a/lib/Slic3r/GCode.pm
+++ b/lib/Slic3r/GCode.pm
@@ -12,7 +12,7 @@ has 'print_config'       => (is => 'ro', default => sub { Slic3r::Config::Print-
 has 'extra_variables'    => (is => 'rw', default => sub {{}});
 has 'standby_points'     => (is => 'rw');
 has 'enable_loop_clipping' => (is => 'rw', default => sub {1});
-has 'enable_wipe'        => (is => 'lazy');   # at least one extruder has wipe enabled
+has 'enable_wipe'        => (is => 'rw', default => sub {0});   # at least one extruder has wipe enabled
 has 'layer_count'        => (is => 'ro', required => 1 );
 has 'layer'              => (is => 'rw');
 has 'region'             => (is => 'rw');
@@ -25,14 +25,13 @@ has 'z'                  => (is => 'rw');
 has 'speed'              => (is => 'rw');
 has '_extrusion_axis'    => (is => 'rw');
 has '_retract_lift'      => (is => 'rw');
-has 'extruders'          => (is => 'ro', default => sub {[]});
-
+has 'extruders'          => (is => 'ro', default => sub {{}});
+has 'extruder'           => (is => 'rw');
 has 'speeds'             => (is => 'lazy');  # mm/min
 has 'external_mp'        => (is => 'rw');
 has 'layer_mp'           => (is => 'rw');
 has 'new_object'         => (is => 'rw', default => sub {0});
 has 'straight_once'      => (is => 'rw', default => sub {1});
-has 'extruder'           => (is => 'rw');
 has 'elapsed_time'       => (is => 'rw', default => sub {0} );  # seconds
 has 'lifted'             => (is => 'rw', default => sub {0} );
 has 'last_pos'           => (is => 'rw', default => sub { Slic3r::Point->new(0,0) } );
@@ -52,7 +51,8 @@ sub set_extruders {
     my ($self, $extruder_ids) = @_;
     
     foreach my $i (@$extruder_ids) {
-        $self->extruders->[$i] = Slic3r::Extruder->new_from_config($self->print_config, $i);
+        $self->extruders->{$i} = my $e = Slic3r::Extruder->new_from_config($self->print_config, $i);
+        $self->enable_wipe(1) if $e->wipe;
     }
 }
 
@@ -82,12 +82,7 @@ my %role_speeds = (
 
 sub multiple_extruders {
     my $self = shift;
-    return @{$self->extruders} > 1;
-}
-
-sub _build_enable_wipe {
-    my $self = shift;
-    return (first { $_->wipe } @{$self->extruders}) ? 1 : 0;
+    return (keys %{$self->extruders}) > 1;
 }
 
 sub set_shift {
@@ -248,14 +243,12 @@ sub extrude_loop {
             @{$extrusion_path->subtract_expolygons($self->_layer_overhangs)};
         
         # get overhang paths by intersecting overhangs with the loop
-        push @paths,
-            map {
-                $_->role(EXTR_ROLE_OVERHANG_PERIMETER);
-                $_->mm3_per_mm($self->region->flow(FLOW_ROLE_PERIMETER, undef, 1)->mm3_per_mm(undef));
-                $_
-            }
-            map $_->clone,
-            @{$extrusion_path->intersect_expolygons($self->_layer_overhangs)};
+        foreach my $path (@{$extrusion_path->intersect_expolygons($self->_layer_overhangs)}) {
+            $path = $path->clone;
+            $path->role(EXTR_ROLE_OVERHANG_PERIMETER);
+            $path->mm3_per_mm($self->region->flow(FLOW_ROLE_PERIMETER, undef, 1)->mm3_per_mm(undef));
+            push @paths, $path;
+        }
         
         # reapply the nearest point search for starting point
         # (clone because the collection gets DESTROY'ed)
@@ -644,7 +637,7 @@ sub set_extruder {
     
     # if we are running a single-extruder setup, just set the extruder and return nothing
     if (!$self->multiple_extruders) {
-        $self->extruder($self->extruders->[$extruder_id]);
+        $self->extruder($self->extruders->{$extruder_id});
         return "";
     }
     
@@ -673,7 +666,7 @@ sub set_extruder {
     }
     
     # set the new extruder
-    $self->extruder($self->extruders->[$extruder_id]);
+    $self->extruder($self->extruders->{$extruder_id});
     $gcode .= sprintf "%s%d%s\n", 
         ($self->print_config->gcode_flavor eq 'makerware'
             ? 'M135 T'
diff --git a/lib/Slic3r/GCode/Layer.pm b/lib/Slic3r/GCode/Layer.pm
index 96029eaab..08de360ec 100644
--- a/lib/Slic3r/GCode/Layer.pm
+++ b/lib/Slic3r/GCode/Layer.pm
@@ -4,8 +4,8 @@ use Moo;
 use List::Util qw(first);
 use Slic3r::Geometry qw(X Y unscale);
 
-has 'print'                         => (is => 'ro', required => 1, handles => [qw(extruders)]);
-has 'gcodegen'                      => (is => 'ro', required => 1);
+has 'print'                         => (is => 'ro', required => 1);
+has 'gcodegen'                      => (is => 'ro', required => 1, handles => [qw(extruders)]);
 has 'shift'                         => (is => 'ro', default => sub { [0,0] });
 
 has 'spiralvase'                    => (is => 'lazy');
@@ -57,9 +57,10 @@ sub process_layer {
     $self->gcodegen->enable_loop_clipping(!$spiralvase);
     
     if (!$self->second_layer_things_done && $layer->id == 1) {
-        for my $t (grep $self->extruders->[$_], 0 .. $#{$self->print->config->temperature}) {
-            $gcode .= $self->gcodegen->set_temperature($self->extruders->[$t]->temperature, 0, $t)
-                if $self->print->extruders->[$t]->temperature && $self->extruders->[$t]->temperature != $self->extruders->[$t]->first_layer_temperature;
+        for my $extruder_id (sort keys %{$self->extruders}) {
+            my $extruder = $self->extruders->{$extruder_id};
+            $gcode .= $self->gcodegen->set_temperature($extruder->temperature, 0, $extruder->id)
+                if $extruder->temperature && $extruder->temperature != $extruder->first_layer_temperature;
         }
         $gcode .= $self->gcodegen->set_bed_temperature($self->print->config->bed_temperature)
             if $self->print->config->bed_temperature && $self->print->config->bed_temperature != $self->print->config->first_layer_bed_temperature;
@@ -76,7 +77,8 @@ sub process_layer {
     if (((values %{$self->skirt_done}) < $self->print->config->skirt_height || $self->print->config->skirt_height == -1)
         && !$self->skirt_done->{$layer->print_z}) {
         $self->gcodegen->set_shift(@{$self->shift});
-        $gcode .= $self->gcodegen->set_extruder($self->extruders->[0]);
+        my @extruder_ids = sort keys %{$self->extruders};
+        $gcode .= $self->gcodegen->set_extruder($extruder_ids[0]);
         # skip skirt if we have a large brim
         if ($layer->id < $self->print->config->skirt_height || $self->print->config->skirt_height == -1) {
             # distribute skirt loops across all extruders
@@ -85,7 +87,7 @@ sub process_layer {
                 # when printing layers > 0 ignore 'min_skirt_length' and 
                 # just use the 'skirts' setting; also just use the current extruder
                 last if ($layer->id > 0) && ($i >= $self->print->config->skirts);
-                $gcode .= $self->gcodegen->set_extruder(($i/@{$self->extruders}) % @{$self->extruders})
+                $gcode .= $self->gcodegen->set_extruder(($i/@extruder_ids) % @extruder_ids)
                     if $layer->id == 0;
                 $gcode .= $self->gcodegen->extrude_loop($skirt_loops[$i], 'skirt');
             }
diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm
index f31e6665a..e24070354 100644
--- a/lib/Slic3r/Print.pm
+++ b/lib/Slic3r/Print.pm
@@ -709,7 +709,7 @@ sub make_brim {
     my $flow = Slic3r::Flow->new_from_width(
         width               => ($self->config->first_layer_extrusion_width || $self->regions->[0]->config->perimeter_extrusion_width),
         role                => FLOW_ROLE_PERIMETER,
-        nozzle_diameter     => $self->config->nozzle_diameter->[ $self->objects->[0]->config->support_material_extruder-1 ],
+        nozzle_diameter     => $self->config->get_at('nozzle_diameter', $self->objects->[0]->config->support_material_extruder-1),
         layer_height        => $first_layer_height,
         bridge_flow_ratio   => 0,
     );
@@ -807,7 +807,6 @@ sub write_gcode {
         layer_count         => $self->layer_count,
     );
     $gcodegen->set_extruders($self->extruders);
-    $gcodegen->set_extruder($self->extruders->[0]);
     
     print $fh "G21 ; set units to millimeters\n" if $self->config->gcode_flavor ne 'makerware';
     print $fh $gcodegen->set_fan(0, 1) if $self->config->cooling && $self->config->disable_fan_first_layers;
@@ -822,8 +821,8 @@ sub write_gcode {
         my ($wait) = @_;
         
         return if $self->config->start_gcode =~ /M(?:109|104)/i;
-        for my $t (0 .. $#{$self->extruders}) {
-            my $temp = $self->config->first_layer_temperature->[$t] // $self->config->first_layer_temperature->[0];
+        for my $t (@{$self->extruders}) {
+            my $temp = $self->config->get_at('first_layer_temperature', $t);
             $temp += $self->config->standby_temperature_delta if $self->config->ooze_prevention;
             printf $fh $gcodegen->set_temperature($temp, $wait, $t) if $temp > 0;
         }
@@ -873,9 +872,9 @@ sub write_gcode {
         if (@skirt_points) {
             my $outer_skirt = convex_hull(\@skirt_points);
             my @skirts = ();
-            foreach my $extruder (@{$self->extruders}) {
+            foreach my $extruder_id (@{$self->extruders}) {
                 push @skirts, my $s = $outer_skirt->clone;
-                $s->translate(map scale($_), @{$extruder->extruder_offset});
+                $s->translate(map scale($_), @{$self->config->get_at('extruder_offset', $extruder_id)});
             }
             my $convex_hull = convex_hull([ map @$_, @skirts ]);
             $gcodegen->standby_points([ map $_->clone, map @$_, map $_->subdivide(scale 10), @{offset([$convex_hull], scale 3)} ]);
@@ -888,6 +887,9 @@ sub write_gcode {
         gcodegen    => $gcodegen,
     );
     
+    # set initial extruder only after custom start G-code
+    print $fh $gcodegen->set_extruder($self->extruders->[0]);
+    
     # do all objects for each layer
     if ($self->config->complete_objects) {
         # print objects from the smallest to the tallest to avoid collisions
@@ -975,7 +977,7 @@ sub write_gcode {
     $self->total_used_filament(0);
     $self->total_extruded_volume(0);
     foreach my $extruder_id (@{$self->extruders}) {
-        my $extruder = $gcodegen->extruders->[$extruder_id];
+        my $extruder = $gcodegen->extruders->{$extruder_id};
         $self->total_used_filament($self->total_used_filament + $extruder->absolute_E);
         $self->total_extruded_volume($self->total_extruded_volume + $extruder->extruded_volume);
         
diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm
index 2d9e9b53c..bf196c100 100644
--- a/lib/Slic3r/Print/Object.pm
+++ b/lib/Slic3r/Print/Object.pm
@@ -758,7 +758,7 @@ sub combine_infill {
         my $every = $region->config->infill_every_layers;
         
         # limit the number of combined layers to the maximum height allowed by this regions' nozzle
-        my $nozzle_diameter = $self->print->config->nozzle_diameter->[ $region->config->infill_extruder-1 ];
+        my $nozzle_diameter = $self->print->config->get_at('nozzle_diameter', $region->config->infill_extruder-1);
         
         # define the combinations
         my @combine = ();   # layer_id => thickness in layers
@@ -810,12 +810,12 @@ sub combine_infill {
                 # so let's remove those areas from all layers
                 
                  my @intersection_with_clearance = map @{$_->offset(
-                       $layerms[-1]->solid_infill_flow->scaled_width    / 2
-                     + $layerms[-1]->perimeter_flow->scaled_width / 2
+                       $layerms[-1]->flow(FLOW_ROLE_SOLID_INFILL)->scaled_width    / 2
+                     + $layerms[-1]->flow(FLOW_ROLE_PERIMETER)->scaled_width / 2
                      # Because fill areas for rectilinear and honeycomb are grown 
                      # later to overlap perimeters, we need to counteract that too.
                      + (($type == S_TYPE_INTERNALSOLID || $region->config->fill_pattern =~ /(rectilinear|honeycomb)/)
-                       ? $layerms[-1]->solid_infill_flow->scaled_width * &Slic3r::INFILL_OVERLAP_OVER_SPACING
+                       ? $layerms[-1]->flow(FLOW_ROLE_SOLID_INFILL)->scaled_width * &Slic3r::INFILL_OVERLAP_OVER_SPACING
                        : 0)
                      )}, @$intersection;
 
@@ -866,7 +866,8 @@ sub generate_support_material {
     my $first_layer_flow = Slic3r::Flow->new_from_width(
         width               => ($self->config->first_layer_extrusion_width || $self->config->support_material_extrusion_width),
         role                => FLOW_ROLE_SUPPORT_MATERIAL,
-        nozzle_diameter     => $self->print->config->nozzle_diameter->[ $self->config->support_material_extruder-1 ],
+        nozzle_diameter     => $self->print->config->nozzle_diameter->[ $self->config->support_material_extruder-1 ]
+                                // $self->print->config->nozzle_diameter->[0],
         layer_height        => $self->config->get_abs_value('first_layer_height'),
         bridge_flow_ratio   => 0,
     );
@@ -903,7 +904,7 @@ sub support_material_flow {
     return Slic3r::Flow->new_from_width(
         width               => $self->config->support_material_extrusion_width,
         role                => $role,
-        nozzle_diameter     => $self->print->config->nozzle_diameter->[$extruder-1],
+        nozzle_diameter     => $self->print->config->nozzle_diameter->[$extruder-1] // $self->print->config->nozzle_diameter->[0],
         layer_height        => $self->config->layer_height,
         bridge_flow_ratio   => 0,
     );
diff --git a/lib/Slic3r/Print/Region.pm b/lib/Slic3r/Print/Region.pm
index 472968478..657aa34d3 100644
--- a/lib/Slic3r/Print/Region.pm
+++ b/lib/Slic3r/Print/Region.pm
@@ -46,7 +46,7 @@ sub flow {
     } else {
         die "Unknown role $role";
     }
-    my $nozzle_diameter = $self->print->config->nozzle_diameter->[$extruder-1];
+    my $nozzle_diameter = $self->print->config->get_at('nozzle_diameter', $extruder-1);
     
     return Slic3r::Flow->new_from_width(
         width               => $config_width,
diff --git a/lib/Slic3r/Print/SupportMaterial.pm b/lib/Slic3r/Print/SupportMaterial.pm
index a88728e67..2c533d8d9 100644
--- a/lib/Slic3r/Print/SupportMaterial.pm
+++ b/lib/Slic3r/Print/SupportMaterial.pm
@@ -174,7 +174,7 @@ sub contact_area {
         # now apply the contact areas to the layer were they need to be made
         {
             # get the average nozzle diameter used on this layer
-            my @nozzle_diameters = map $self->print_config->nozzle_diameter->[$_],
+            my @nozzle_diameters = map $self->print_config->get_at('nozzle_diameter', $_),
                 map { $_->config->perimeter_extruder-1, $_->config->infill_extruder-1 }
                 @{$layer->regions};
             my $nozzle_diameter = sum(@nozzle_diameters)/@nozzle_diameters;
@@ -246,7 +246,7 @@ sub support_layers_z {
     # determine layer height for any non-contact layer
     # we use max() to prevent many ultra-thin layers to be inserted in case
     # layer_height > nozzle_diameter * 0.75
-    my $nozzle_diameter = $self->print_config->nozzle_diameter->[$self->object_config->support_material_extruder-1];
+    my $nozzle_diameter = $self->print_config->get_at('nozzle_diameter', $self->object_config->support_material_extruder-1);
     my $support_material_height = max($max_object_layer_height, $nozzle_diameter * 0.75);
     
     my @z = sort { $a <=> $b } @$contact_z, @$top_z, (map $_ + $nozzle_diameter, @$top_z);
diff --git a/t/combineinfill.t b/t/combineinfill.t
index bd80b0d30..62e22e6b2 100644
--- a/t/combineinfill.t
+++ b/t/combineinfill.t
@@ -11,10 +11,41 @@ use List::Util qw(first);
 use Slic3r;
 use Slic3r::Test;
 
-plan skip_all => 'this test is currently disabled';  # needs to be adapted to the new API
-plan tests => 3;
+plan tests => 2;
 
 {
+    my $config = Slic3r::Config->new_from_defaults;
+    $config->set('layer_height', 0.2);
+    $config->set('first_layer_height', 0.2);
+    $config->set('nozzle_diameter', [0.5]);
+    $config->set('infill_every_layers', 2);
+    $config->set('infill_extruder', 2);
+    $config->set('top_solid_layers', 0);
+    $config->set('bottom_solid_layers', 0);
+    my $print = Slic3r::Test::init_print('20mm_cube', config => $config);
+    ok my $gcode = Slic3r::Test::gcode($print), "infill_every_layers does not crash";
+    
+    my $tool = undef;
+    my %layer_infill = ();  # layer_z => has_infill
+    Slic3r::GCode::Reader->new->parse($gcode, sub {
+        my ($self, $cmd, $args, $info) = @_;
+        
+        if ($cmd =~ /^T(\d+)/) {
+            $tool = $1;
+        } elsif ($cmd eq 'G1' && $info->{extruding} && $info->{dist_XY} > 0) {
+            $layer_infill{$self->Z} //= 0;
+            if ($tool == $config->infill_extruder-1) {
+                $layer_infill{$self->Z} = 1;
+            }
+        }
+    });
+    my $layers_with_infill = grep $_,  values %layer_infill;
+    $layers_with_infill--; # first layer is never combined
+    is $layers_with_infill, scalar(keys %layer_infill)/2, 'infill is only present in correct number of layers';
+}
+
+# the following needs to be adapted to the new API
+if (0) {
     my $config = Slic3r::Config->new_from_defaults;
     $config->set('skirts', 0);
     $config->set('solid_layers', 0);
diff --git a/t/retraction.t b/t/retraction.t
index 636359149..3737f63cd 100644
--- a/t/retraction.t
+++ b/t/retraction.t
@@ -41,8 +41,8 @@ my $test = sub {
         
         if ($info->{dist_Z}) {
             # lift move or lift + change layer
-            if (_eq($info->{dist_Z}, $print->extruders->[$tool]->retract_lift)
-                || (_eq($info->{dist_Z}, $conf->layer_height + $print->extruders->[$tool]->retract_lift) && $print->extruders->[$tool]->retract_lift > 0)) {
+            if (_eq($info->{dist_Z}, $print->config->get_at('retract_lift', $tool))
+                || (_eq($info->{dist_Z}, $conf->layer_height + $print->config->get_at('retract_lift', $tool)) && $print->config->get_at('retract_lift', $tool) > 0)) {
                 fail 'only lifting while retracted' if !$retracted[$tool] && !($conf->g0 && $info->{retracting});
                 fail 'double lift' if $lifted;
                 $lifted = 1;
@@ -50,8 +50,8 @@ my $test = sub {
             if ($info->{dist_Z} < 0) {
                 fail 'going down only after lifting' if !$lifted;
                 fail 'going down by the same amount of the lift or by the amount needed to get to next layer'
-                    if !_eq($info->{dist_Z}, -$print->extruders->[$tool]->retract_lift)
-                        && !_eq($info->{dist_Z}, -$print->extruders->[$tool]->retract_lift + $conf->layer_height);
+                    if !_eq($info->{dist_Z}, -$print->config->get_at('retract_lift', $tool))
+                        && !_eq($info->{dist_Z}, -$print->config->get_at('retract_lift', $tool) + $conf->layer_height);
                 $lifted = 0;
             }
             fail 'move Z at travel speed' if ($args->{F} // $self->F) != $conf->travel_speed * 60;
@@ -59,9 +59,9 @@ my $test = sub {
         if ($info->{retracting}) {
             $retracted[$tool] = 1;
             $retracted_length[$tool] += -$info->{dist_E};
-            if (_eq($retracted_length[$tool], $print->extruders->[$tool]->retract_length)) {
+            if (_eq($retracted_length[$tool], $print->config->get_at('retract_length', $tool))) {
                 # okay
-            } elsif (_eq($retracted_length[$tool], $print->extruders->[$tool]->retract_length_toolchange)) {
+            } elsif (_eq($retracted_length[$tool], $print->config->get_at('retract_length_toolchange', $tool))) {
                 $wait_for_toolchange = 1;
             } else {
                 fail 'retracted by the correct amount';
@@ -72,9 +72,9 @@ my $test = sub {
         if ($info->{extruding}) {
             fail 'only extruding while not lifted' if $lifted;
             if ($retracted[$tool]) {
-                my $expected_amount = $retracted_length[$tool] + $print->extruders->[$tool]->retract_restart_extra;
+                my $expected_amount = $retracted_length[$tool] + $print->config->get_at('retract_restart_extra', $tool);
                 if ($changed_tool && $toolchange_count[$tool] > 1) {
-                    $expected_amount = $print->extruders->[$tool]->retract_length_toolchange + $print->extruders->[$tool]->retract_restart_extra_toolchange;
+                    $expected_amount = $print->config->get_at('retract_length_toolchange', $tool) + $print->config->get_at('retract_restart_extra_toolchange', $tool);
                     $changed_tool = 0;
                 }
                 fail 'unretracted by the correct amount'
@@ -83,7 +83,7 @@ my $test = sub {
                 $retracted_length[$tool] = 0;
             }
         }
-        if ($info->{travel} && $info->{dist_XY} >= $print->extruders->[$tool]->retract_before_travel) {
+        if ($info->{travel} && $info->{dist_XY} >= $print->config->get_at('retract_before_travel', $tool)) {
             fail 'retracted before long travel move' if !$retracted[$tool];
         }
     });
diff --git a/t/support.t b/t/support.t
index bd82d0ce1..a1e7173d9 100644
--- a/t/support.t
+++ b/t/support.t
@@ -23,7 +23,13 @@ use Slic3r::Test;
         $print->init_extruders;
         my $flow = $print->objects->[0]->support_material_flow;
         my $support_z = Slic3r::Print::SupportMaterial
-            ->new(object_config => $print->objects->[0]->config, print_config => $print->config, flow => $flow)
+            ->new(
+                object_config       => $print->objects->[0]->config,
+                print_config        => $print->config,
+                flow                => $flow,
+                interface_flow      => $flow,
+                first_layer_flow    => $flow,
+            )
             ->support_layers_z(\@contact_z, \@top_z, $config->layer_height);
         
         is $support_z->[0], $config->first_layer_height,
diff --git a/xs/src/Config.cpp b/xs/src/Config.cpp
index 58c9343e2..32c6d6074 100644
--- a/xs/src/Config.cpp
+++ b/xs/src/Config.cpp
@@ -130,9 +130,9 @@ ConfigBase::get(t_config_option_key opt_key) {
         return optv->point.to_SV_pureperl();
     } else if (ConfigOptionPoints* optv = dynamic_cast<ConfigOptionPoints*>(opt)) {
         AV* av = newAV();
-        av_fill(av, optv->points.size()-1);
-        for (Pointfs::iterator it = optv->points.begin(); it != optv->points.end(); ++it)
-            av_store(av, it - optv->points.begin(), it->to_SV_pureperl());
+        av_fill(av, optv->values.size()-1);
+        for (Pointfs::iterator it = optv->values.begin(); it != optv->values.end(); ++it)
+            av_store(av, it - optv->values.begin(), it->to_SV_pureperl());
         return newRV_noinc((SV*)av);
     } else if (ConfigOptionBool* optv = dynamic_cast<ConfigOptionBool*>(opt)) {
         return newSViv(optv->value ? 1 : 0);
@@ -148,6 +148,28 @@ ConfigBase::get(t_config_option_key opt_key) {
     }
 }
 
+SV*
+ConfigBase::get_at(t_config_option_key opt_key, size_t i) {
+    ConfigOption* opt = this->option(opt_key);
+    if (opt == NULL) return &PL_sv_undef;
+    
+    if (ConfigOptionFloats* optv = dynamic_cast<ConfigOptionFloats*>(opt)) {
+        return newSVnv(optv->get_at(i));
+    } else if (ConfigOptionInts* optv = dynamic_cast<ConfigOptionInts*>(opt)) {
+        return newSViv(optv->get_at(i));
+    } else if (ConfigOptionStrings* optv = dynamic_cast<ConfigOptionStrings*>(opt)) {
+        // we don't serialize() because that would escape newlines
+        std::string val = optv->get_at(i);
+        return newSVpvn(val.c_str(), val.length());
+    } else if (ConfigOptionPoints* optv = dynamic_cast<ConfigOptionPoints*>(opt)) {
+        return optv->get_at(i).to_SV_pureperl();
+    } else if (ConfigOptionBools* optv = dynamic_cast<ConfigOptionBools*>(opt)) {
+        return newSViv(optv->get_at(i) ? 1 : 0);
+    } else {
+        return &PL_sv_undef;
+    }
+}
+
 void
 ConfigBase::set(t_config_option_key opt_key, SV* value) {
     ConfigOption* opt = this->option(opt_key, true);
@@ -193,14 +215,14 @@ ConfigBase::set(t_config_option_key opt_key, SV* value) {
     } else if (ConfigOptionPoint* optv = dynamic_cast<ConfigOptionPoint*>(opt)) {
         optv->point.from_SV(value);
     } else if (ConfigOptionPoints* optv = dynamic_cast<ConfigOptionPoints*>(opt)) {
-        optv->points.clear();
+        optv->values.clear();
         AV* av = (AV*)SvRV(value);
         const size_t len = av_len(av)+1;
         for (size_t i = 0; i < len; i++) {
             SV** elem = av_fetch(av, i, 0);
             Pointf point;
             point.from_SV(*elem);
-            optv->points.push_back(point);
+            optv->values.push_back(point);
         }
     } else if (ConfigOptionBool* optv = dynamic_cast<ConfigOptionBool*>(opt)) {
         optv->value = SvTRUE(value);
diff --git a/xs/src/Config.hpp b/xs/src/Config.hpp
index 9306a18b2..64229cb2c 100644
--- a/xs/src/Config.hpp
+++ b/xs/src/Config.hpp
@@ -8,6 +8,7 @@
 #include <cstdio>
 #include <cstdlib>
 #include <iostream>
+#include <stdexcept>
 #include <string>
 #include <vector>
 #include "Point.hpp"
@@ -24,6 +25,22 @@ class ConfigOption {
     virtual void deserialize(std::string str) = 0;
 };
 
+template <class T>
+class ConfigOptionVector
+{
+    public:
+    virtual ~ConfigOptionVector() {};
+    std::vector<T> values;
+    
+    T get_at(size_t i) {
+        try {
+            return this->values.at(i);
+        } catch (const std::out_of_range& oor) {
+            return this->values.front();
+        }
+    };
+};
+
 class ConfigOptionFloat : public ConfigOption
 {
     public:
@@ -43,10 +60,9 @@ class ConfigOptionFloat : public ConfigOption
     };
 };
 
-class ConfigOptionFloats : public ConfigOption
+class ConfigOptionFloats : public ConfigOption, public ConfigOptionVector<double>
 {
     public:
-    std::vector<double> values;
     
     std::string serialize() {
         std::ostringstream ss;
@@ -86,10 +102,9 @@ class ConfigOptionInt : public ConfigOption
     };
 };
 
-class ConfigOptionInts : public ConfigOption
+class ConfigOptionInts : public ConfigOption, public ConfigOptionVector<int>
 {
     public:
-    std::vector<int> values;
     
     std::string serialize() {
         std::ostringstream ss;
@@ -144,10 +159,9 @@ class ConfigOptionString : public ConfigOption
 };
 
 // semicolon-separated strings
-class ConfigOptionStrings : public ConfigOption
+class ConfigOptionStrings : public ConfigOption, public ConfigOptionVector<std::string>
 {
     public:
-    std::vector<std::string> values;
     
     std::string serialize() {
         std::ostringstream ss;
@@ -215,15 +229,14 @@ class ConfigOptionPoint : public ConfigOption
     };
 };
 
-class ConfigOptionPoints : public ConfigOption
+class ConfigOptionPoints : public ConfigOption, public ConfigOptionVector<Pointf>
 {
     public:
-    Pointfs points;
     
     std::string serialize() {
         std::ostringstream ss;
-        for (Pointfs::const_iterator it = this->points.begin(); it != this->points.end(); ++it) {
-            if (it - this->points.begin() != 0) ss << ",";
+        for (Pointfs::const_iterator it = this->values.begin(); it != this->values.end(); ++it) {
+            if (it - this->values.begin() != 0) ss << ",";
             ss << it->x;
             ss << "x";
             ss << it->y;
@@ -232,13 +245,13 @@ class ConfigOptionPoints : public ConfigOption
     };
     
     void deserialize(std::string str) {
-        this->points.clear();
+        this->values.clear();
         std::istringstream is(str);
         std::string point_str;
         while (std::getline(is, point_str, ',')) {
             Pointf point;
             sscanf(point_str.c_str(), "%lfx%lf", &point.x, &point.y);
-            this->points.push_back(point);
+            this->values.push_back(point);
         }
     };
 };
@@ -260,10 +273,9 @@ class ConfigOptionBool : public ConfigOption
     };
 };
 
-class ConfigOptionBools : public ConfigOption
+class ConfigOptionBools : public ConfigOption, public ConfigOptionVector<bool>
 {
     public:
-    std::vector<bool> values;
     
     std::string serialize() {
         std::ostringstream ss;
@@ -398,6 +410,7 @@ class ConfigBase
     #ifdef SLIC3RXS
     SV* as_hash();
     SV* get(t_config_option_key opt_key);
+    SV* get_at(t_config_option_key opt_key, size_t i);
     void set(t_config_option_key opt_key, SV* value);
     #endif
 };
diff --git a/xs/src/PrintConfig.hpp b/xs/src/PrintConfig.hpp
index c5adff7c1..cb44a007f 100644
--- a/xs/src/PrintConfig.hpp
+++ b/xs/src/PrintConfig.hpp
@@ -1131,8 +1131,8 @@ class PrintConfig : public virtual StaticConfig
         this->external_perimeters_first.value                    = false;
         this->extruder_clearance_height.value                    = 20;
         this->extruder_clearance_radius.value                    = 20;
-        this->extruder_offset.points.resize(1);
-        this->extruder_offset.points[0]                          = Pointf(0,0);
+        this->extruder_offset.values.resize(1);
+        this->extruder_offset.values[0]                          = Pointf(0,0);
         this->extrusion_axis.value                               = "E";
         this->extrusion_multiplier.values.resize(1);
         this->extrusion_multiplier.values[0]                     = 1;
diff --git a/xs/t/15_config.t b/xs/t/15_config.t
index 0d2080602..c80f14408 100644
--- a/xs/t/15_config.t
+++ b/xs/t/15_config.t
@@ -4,7 +4,7 @@ use strict;
 use warnings;
 
 use Slic3r::XS;
-use Test::More tests => 82;
+use Test::More tests => 88;
 
 foreach my $config (Slic3r::Config->new, Slic3r::Config::Full->new) {
     $config->set('layer_height', 0.3);
@@ -75,6 +75,9 @@ foreach my $config (Slic3r::Config->new, Slic3r::Config::Full->new) {
     
     $config->set('wipe', [1,0]);
     is_deeply $config->get('wipe'), [1,0], 'set/get bools';
+    is $config->get_at('wipe', 0), 1, 'get_at bools';
+    is $config->get_at('wipe', 1), 0, 'get_at bools';
+    is $config->get_at('wipe', 9), 1, 'get_at bools';
     is $config->serialize('wipe'), '1,0', 'serialize bools';
     $config->set_deserialize('wipe', '0,1,1');
     is_deeply $config->get('wipe'), [0,1,1], 'deserialize bools';
diff --git a/xs/xsp/Config.xsp b/xs/xsp/Config.xsp
index 743b38ca1..79d9bd269 100644
--- a/xs/xsp/Config.xsp
+++ b/xs/xsp/Config.xsp
@@ -11,6 +11,7 @@
     bool has(t_config_option_key opt_key);
     SV* as_hash();
     SV* get(t_config_option_key opt_key);
+    SV* get_at(t_config_option_key opt_key, int i);
     void set(t_config_option_key opt_key, SV* value);
     void set_deserialize(t_config_option_key opt_key, std::string str);
     std::string serialize(t_config_option_key opt_key);
@@ -31,6 +32,7 @@
     bool has(t_config_option_key opt_key);
     SV* as_hash();
     SV* get(t_config_option_key opt_key);
+    SV* get_at(t_config_option_key opt_key, int i);
     void set(t_config_option_key opt_key, SV* value);
     void set_deserialize(t_config_option_key opt_key, std::string str);
     std::string serialize(t_config_option_key opt_key);
@@ -50,6 +52,7 @@
     bool has(t_config_option_key opt_key);
     SV* as_hash();
     SV* get(t_config_option_key opt_key);
+    SV* get_at(t_config_option_key opt_key, int i);
     void set(t_config_option_key opt_key, SV* value);
     void set_deserialize(t_config_option_key opt_key, std::string str);
     std::string serialize(t_config_option_key opt_key);
@@ -70,6 +73,7 @@
     bool has(t_config_option_key opt_key);
     SV* as_hash();
     SV* get(t_config_option_key opt_key);
+    SV* get_at(t_config_option_key opt_key, int i);
     void set(t_config_option_key opt_key, SV* value);
     void set_deserialize(t_config_option_key opt_key, std::string str);
     std::string serialize(t_config_option_key opt_key);
@@ -90,6 +94,7 @@
     bool has(t_config_option_key opt_key);
     SV* as_hash();
     SV* get(t_config_option_key opt_key);
+    SV* get_at(t_config_option_key opt_key, int i);
     void set(t_config_option_key opt_key, SV* value);
     void set_deserialize(t_config_option_key opt_key, std::string str);
     std::string serialize(t_config_option_key opt_key);

From 4e76a9e4c9bd2fdc621b27013cf047e817365650 Mon Sep 17 00:00:00 2001
From: Alessandro Ranellucci <aar@cpan.org>
Date: Mon, 30 Dec 2013 19:00:32 +0100
Subject: [PATCH 15/19] Parse options from AMF metadata

---
 lib/Slic3r/Format/AMF/Parser.pm | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/lib/Slic3r/Format/AMF/Parser.pm b/lib/Slic3r/Format/AMF/Parser.pm
index d392a8915..d67687f70 100644
--- a/lib/Slic3r/Format/AMF/Parser.pm
+++ b/lib/Slic3r/Format/AMF/Parser.pm
@@ -97,6 +97,12 @@ sub end_element {
     } elsif ($data->{LocalName} eq 'material') {
         $self->{_material} = undef;
     } elsif ($data->{LocalName} eq 'metadata' && $self->{_material}) {
+        if ($self->{_material_metadata_type} =~ /^slic3r\.(.+)/) {
+            my $opt_key = $1;
+            if (exists $Slic3r::Config::Options->{$opt_key}) {
+                $self->{_material}->set_deserialize($opt_key, $self->{_material}->attributes->{$opt_key});
+            }
+        }
         $self->{_material_metadata_type} = undef;
     } elsif ($data->{LocalName} eq 'constellation') {
         $self->{_constellation} = undef;

From 87342d324caa3e745325aff1508821b0dfd8abe8 Mon Sep 17 00:00:00 2001
From: Alessandro Ranellucci <aar@cpan.org>
Date: Sun, 5 Jan 2014 01:25:39 +0100
Subject: [PATCH 16/19] Fix typo

---
 lib/Slic3r/Format/AMF/Parser.pm | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/Slic3r/Format/AMF/Parser.pm b/lib/Slic3r/Format/AMF/Parser.pm
index d67687f70..c99d71800 100644
--- a/lib/Slic3r/Format/AMF/Parser.pm
+++ b/lib/Slic3r/Format/AMF/Parser.pm
@@ -100,7 +100,7 @@ sub end_element {
         if ($self->{_material_metadata_type} =~ /^slic3r\.(.+)/) {
             my $opt_key = $1;
             if (exists $Slic3r::Config::Options->{$opt_key}) {
-                $self->{_material}->set_deserialize($opt_key, $self->{_material}->attributes->{$opt_key});
+                $self->{_material}->config->set_deserialize($opt_key, $self->{_material}->attributes->{$opt_key});
             }
         }
         $self->{_material_metadata_type} = undef;

From 036badf932291520ca7d3f7d9f8bad1c69a947aa Mon Sep 17 00:00:00 2001
From: Alessandro Ranellucci <aar@cpan.org>
Date: Sun, 5 Jan 2014 13:16:13 +0100
Subject: [PATCH 17/19] Ported Flow to XS

---
 lib/Slic3r/Flow.pm  | 167 ++------------------------------------------
 lib/Slic3r/GCode.pm |   2 +-
 xs/MANIFEST         |   4 ++
 xs/lib/Slic3r/XS.pm |  28 ++++++++
 xs/src/Config.cpp   |  32 ++++-----
 xs/src/Config.hpp   |  36 ++++++----
 xs/src/Flow.cpp     | 109 +++++++++++++++++++++++++++++
 xs/src/Flow.hpp     |  48 +++++++++++++
 xs/src/Point.cpp    |   4 +-
 xs/src/Point.hpp    |   6 +-
 xs/src/myinit.h     |   2 +
 xs/t/15_config.t    |   5 +-
 xs/t/16_flow.t      |  29 ++++++++
 xs/xsp/Flow.xsp     |  80 +++++++++++++++++++++
 xs/xsp/my.map       |   2 +
 xs/xsp/typemap.xspt |   7 ++
 16 files changed, 360 insertions(+), 201 deletions(-)
 create mode 100644 xs/src/Flow.cpp
 create mode 100644 xs/src/Flow.hpp
 create mode 100644 xs/t/16_flow.t
 create mode 100644 xs/xsp/Flow.xsp

diff --git a/lib/Slic3r/Flow.pm b/lib/Slic3r/Flow.pm
index f07be98f5..a7c8312c5 100644
--- a/lib/Slic3r/Flow.pm
+++ b/lib/Slic3r/Flow.pm
@@ -1,165 +1,12 @@
 package Slic3r::Flow;
-use Moo;
+use strict;
+use warnings;
 
-require Exporter;
-our @ISA = qw(Exporter);
-our @EXPORT_OK   = qw(FLOW_ROLE_PERIMETER FLOW_ROLE_INFILL FLOW_ROLE_SOLID_INFILL FLOW_ROLE_TOP_SOLID_INFILL
-                        FLOW_ROLE_SUPPORT_MATERIAL FLOW_ROLE_SUPPORT_MATERIAL_INTERFACE);
+use parent qw(Exporter);
+
+our @EXPORT_OK = qw(FLOW_ROLE_PERIMETER FLOW_ROLE_INFILL FLOW_ROLE_SOLID_INFILL
+    FLOW_ROLE_TOP_SOLID_INFILL FLOW_ROLE_SUPPORT_MATERIAL 
+    FLOW_ROLE_SUPPORT_MATERIAL_INTERFACE);
 our %EXPORT_TAGS = (roles => \@EXPORT_OK);
 
-use Slic3r::Geometry qw(PI);
-
-has 'width'             => (is => 'ro', required => 1);
-has 'spacing'           => (is => 'ro', required => 1);
-has 'nozzle_diameter'   => (is => 'ro', required => 1);
-has 'bridge'            => (is => 'ro', default => sub {0});
-has 'scaled_width'      => (is => 'lazy');
-has 'scaled_spacing'    => (is => 'lazy');
-
-use constant FLOW_ROLE_PERIMETER                    => 1;
-use constant FLOW_ROLE_INFILL                       => 2;
-use constant FLOW_ROLE_SOLID_INFILL                 => 3;
-use constant FLOW_ROLE_TOP_SOLID_INFILL             => 4;
-use constant FLOW_ROLE_SUPPORT_MATERIAL             => 5;
-use constant FLOW_ROLE_SUPPORT_MATERIAL_INTERFACE   => 6;
-
-use constant BRIDGE_EXTRA_SPACING   => 0.05;
-use constant OVERLAP_FACTOR         => 1;
-
-sub new_from_width {
-    my ($class, %args) = @_;
-    
-    if ($args{width} eq '0') {
-        $args{width} = _width(@args{qw(role nozzle_diameter layer_height bridge_flow_ratio)});
-    } elsif ($args{width} =~ /^(\d+(?:\.\d+)?)%$/) {
-        $args{width} = $args{layer_height} * $1 / 100;
-    }
-    
-    return $class->new(
-        width           => $args{width},
-        spacing         => _spacing(@args{qw(width nozzle_diameter layer_height bridge_flow_ratio)}),
-        nozzle_diameter => $args{nozzle_diameter},
-        bridge          => ($args{bridge_flow_ratio} > 0) ? 1 : 0,
-    );
-}
-
-sub new_from_spacing {
-    my ($class, %args) = @_;
-    
-    return $class->new(
-        width           => _width_from_spacing(@args{qw(spacing nozzle_diameter layer_height bridge)}),
-        spacing         => $args{spacing},
-        nozzle_diameter => $args{nozzle_diameter},
-        bridge          => $args{bridge},
-    );
-}
-
-sub clone {
-    my $self = shift;
-    
-    return (ref $self)->new(
-        width           => $self->width,
-        spacing         => $self->spacing,
-        nozzle_diameter => $self->nozzle_diameter,
-        bridge          => $self->bridge,
-    );
-}
-
-sub mm3_per_mm {
-    my ($self, $h) = @_;
-    
-    my $w = $self->width;
-    my $s = $self->spacing;
-    
-    if ($self->bridge) {
-        return ($w**2) * PI/4;
-    } elsif ($w >= ($self->nozzle_diameter + $h)) {
-        # rectangle with semicircles at the ends
-        return $w * $h + ($h**2) / 4 * (PI - 4);
-    } else {
-        # rectangle with shrunk semicircles at the ends
-        return $self->nozzle_diameter * $h * (1 - PI/4) + $h * $w * PI/4;
-    }
-}
-
-sub _width {
-    my ($role, $nozzle_diameter, $layer_height, $bridge_flow_ratio) = @_;
-    
-    if ($bridge_flow_ratio > 0) {
-        return sqrt($bridge_flow_ratio * ($nozzle_diameter**2));
-    }
-    
-    # here we calculate a sane default by matching the flow speed (at the nozzle) and the feed rate
-    my $volume = ($nozzle_diameter**2) * PI/4;
-    my $shape_threshold = $nozzle_diameter * $layer_height + ($layer_height**2) * PI/4;
-    my $width;
-    if ($volume >= $shape_threshold) {
-        # rectangle with semicircles at the ends
-        $width = (($nozzle_diameter**2) * PI + ($layer_height**2) * (4 - PI)) / (4 * $layer_height);
-    } else {
-        # rectangle with squished semicircles at the ends
-        $width = $nozzle_diameter * ($nozzle_diameter/$layer_height - 4/PI + 1);
-    }
-    
-    my $min = $nozzle_diameter * 1.05;
-    my $max;
-    if ($role == FLOW_ROLE_PERIMETER || $role == FLOW_ROLE_SUPPORT_MATERIAL) {
-        $min = $max = $nozzle_diameter;
-    } elsif ($role != FLOW_ROLE_INFILL) {
-        # do not limit width for sparse infill so that we use full native flow for it
-        $max = $nozzle_diameter * 1.7;
-    }
-    $width = $max if defined($max) && $width > $max;
-    $width = $min if $width < $min;
-    
-    return $width;
-}
-
-sub _width_from_spacing {
-    my ($s, $nozzle_diameter, $h, $bridge) = @_;
-    
-    if ($bridge) {
-        return $s - BRIDGE_EXTRA_SPACING;
-    }
-    
-    my $w_threshold = $h + $nozzle_diameter;
-    my $s_threshold = $w_threshold - OVERLAP_FACTOR * ($w_threshold - ($w_threshold - $h * (1 - PI/4)));
-    
-    if ($s >= $s_threshold) {
-        # rectangle with semicircles at the ends
-        return $s + OVERLAP_FACTOR * $h * (1 - PI/4);
-    } else {
-        # rectangle with shrunk semicircles at the ends
-        return ($s + $nozzle_diameter * OVERLAP_FACTOR * (PI/4 - 1)) / (1 + OVERLAP_FACTOR * (PI/4 - 1));
-    }
-}
-
-sub _spacing {
-    my ($width, $nozzle_diameter, $layer_height, $bridge_flow_ratio) = @_;
-    
-    if ($bridge_flow_ratio > 0) {
-        return $width + BRIDGE_EXTRA_SPACING;
-    }
-    use XXX; ZZZ "here" if !defined $nozzle_diameter;
-    my $min_flow_spacing;
-    if ($width >= ($nozzle_diameter + $layer_height)) {
-        # rectangle with semicircles at the ends
-        $min_flow_spacing = $width - $layer_height * (1 - PI/4);
-    } else {
-        # rectangle with shrunk semicircles at the ends
-        $min_flow_spacing = $nozzle_diameter * (1 - PI/4) + $width * PI/4;
-    }
-    return $width - OVERLAP_FACTOR * ($width - $min_flow_spacing);
-}
-
-sub _build_scaled_width {
-    my $self = shift;
-    return Slic3r::Geometry::scale($self->width);
-}
-
-sub _build_scaled_spacing {
-    my $self = shift;
-    return Slic3r::Geometry::scale($self->spacing);
-}
-
 1;
diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm
index 78324f3bd..1d1952309 100644
--- a/lib/Slic3r/GCode.pm
+++ b/lib/Slic3r/GCode.pm
@@ -246,7 +246,7 @@ sub extrude_loop {
         foreach my $path (@{$extrusion_path->intersect_expolygons($self->_layer_overhangs)}) {
             $path = $path->clone;
             $path->role(EXTR_ROLE_OVERHANG_PERIMETER);
-            $path->mm3_per_mm($self->region->flow(FLOW_ROLE_PERIMETER, undef, 1)->mm3_per_mm(undef));
+            $path->mm3_per_mm($self->region->flow(FLOW_ROLE_PERIMETER, -1, 1)->mm3_per_mm(-1));
             push @paths, $path;
         }
         
diff --git a/xs/MANIFEST b/xs/MANIFEST
index ef37e40cb..fc52e2497 100644
--- a/xs/MANIFEST
+++ b/xs/MANIFEST
@@ -22,6 +22,8 @@ src/ExtrusionEntity.cpp
 src/ExtrusionEntity.hpp
 src/ExtrusionEntityCollection.cpp
 src/ExtrusionEntityCollection.hpp
+src/Flow.cpp
+src/Flow.hpp
 src/Geometry.cpp
 src/Geometry.hpp
 src/Line.cpp
@@ -65,6 +67,7 @@ t/12_extrusionpathcollection.t
 t/13_polylinecollection.t
 t/14_geometry.t
 t/15_config.t
+t/16_flow.t
 xsp/Clipper.xsp
 xsp/Config.xsp
 xsp/ExPolygon.xsp
@@ -72,6 +75,7 @@ xsp/ExPolygonCollection.xsp
 xsp/ExtrusionEntityCollection.xsp
 xsp/ExtrusionLoop.xsp
 xsp/ExtrusionPath.xsp
+xsp/Flow.xsp
 xsp/Geometry.xsp
 xsp/Line.xsp
 xsp/my.map
diff --git a/xs/lib/Slic3r/XS.pm b/xs/lib/Slic3r/XS.pm
index 9e0e678d8..489aeb0e8 100644
--- a/xs/lib/Slic3r/XS.pm
+++ b/xs/lib/Slic3r/XS.pm
@@ -146,6 +146,34 @@ our @ISA = 'Slic3r::ExtrusionPath';
 
 sub DESTROY {}
 
+package Slic3r::Flow;
+
+sub new {
+    my ($class, %args) = @_;
+    
+    my $self = $class->_new(
+        @args{qw(width spacing nozzle_diameter)},
+    );
+    $self->set_bridge($args{bridge} // 0);
+    return $self;
+}
+
+sub new_from_width {
+    my ($class, %args) = @_;
+    
+    return $class->_new_from_width(
+        @args{qw(role width nozzle_diameter layer_height bridge_flow_ratio)},
+    );
+}
+
+sub new_from_spacing {
+    my ($class, %args) = @_;
+    
+    return $class->_new_from_spacing(
+        @args{qw(spacing nozzle_diameter layer_height bridge)},
+    );
+}
+
 package Slic3r::Surface;
 
 sub new {
diff --git a/xs/src/Config.cpp b/xs/src/Config.cpp
index 32c6d6074..20be9a3c2 100644
--- a/xs/src/Config.cpp
+++ b/xs/src/Config.cpp
@@ -50,22 +50,18 @@ ConfigBase::set_deserialize(const t_config_option_key opt_key, std::string str)
 
 double
 ConfigBase::get_abs_value(const t_config_option_key opt_key) {
-    // get option definition
-    assert(this->def->count(opt_key) != 0);
-    ConfigOptionDef* def = &(*this->def)[opt_key];
-    assert(def->type == coFloatOrPercent);
-    
-    // get stored option value
-    ConfigOptionFloatOrPercent* opt = dynamic_cast<ConfigOptionFloatOrPercent*>(this->option(opt_key));
-    assert(opt != NULL);
-    
-    // compute absolute value
-    if (opt->percent) {
-        ConfigOptionFloat* optbase = dynamic_cast<ConfigOptionFloat*>(this->option(def->ratio_over));
-        if (optbase == NULL) throw "ratio_over option not found";
-        return optbase->value * opt->value / 100;
+    ConfigOption* opt = this->option(opt_key, false);
+    if (ConfigOptionFloatOrPercent* optv = dynamic_cast<ConfigOptionFloatOrPercent*>(opt)) {
+        // get option definition
+        assert(this->def->count(opt_key) != 0);
+        ConfigOptionDef* def = &(*this->def)[opt_key];
+        
+        // compute absolute value over the absolute value of the base option
+        return optv->get_abs_value(this->get_abs_value(def->ratio_over));
+    } else if (ConfigOptionFloat* optv = dynamic_cast<ConfigOptionFloat*>(opt)) {
+        return optv->value;
     } else {
-        return opt->value;
+        throw "Not a valid option type for get_abs_value()";
     }
 }
 
@@ -76,11 +72,7 @@ ConfigBase::get_abs_value(const t_config_option_key opt_key, double ratio_over)
     assert(opt != NULL);
     
     // compute absolute value
-    if (opt->percent) {
-        return ratio_over * opt->value / 100;
-    } else {
-        return opt->value;
-    }
+    return opt->get_abs_value(ratio_over);
 }
 
 #ifdef SLIC3RXS
diff --git a/xs/src/Config.hpp b/xs/src/Config.hpp
index 64229cb2c..71f945349 100644
--- a/xs/src/Config.hpp
+++ b/xs/src/Config.hpp
@@ -21,7 +21,7 @@ typedef std::vector<std::string> t_config_option_keys;
 class ConfigOption {
     public:
     virtual ~ConfigOption() {};
-    virtual std::string serialize() = 0;
+    virtual std::string serialize() const = 0;
     virtual void deserialize(std::string str) = 0;
 };
 
@@ -49,7 +49,7 @@ class ConfigOptionFloat : public ConfigOption
     
     operator double() const { return this->value; };
     
-    std::string serialize() {
+    std::string serialize() const {
         std::ostringstream ss;
         ss << this->value;
         return ss.str();
@@ -64,7 +64,7 @@ class ConfigOptionFloats : public ConfigOption, public ConfigOptionVector<double
 {
     public:
     
-    std::string serialize() {
+    std::string serialize() const {
         std::ostringstream ss;
         for (std::vector<double>::const_iterator it = this->values.begin(); it != this->values.end(); ++it) {
             if (it - this->values.begin() != 0) ss << ",";
@@ -91,7 +91,7 @@ class ConfigOptionInt : public ConfigOption
     
     operator int() const { return this->value; };
     
-    std::string serialize() {
+    std::string serialize() const {
         std::ostringstream ss;
         ss << this->value;
         return ss.str();
@@ -106,7 +106,7 @@ class ConfigOptionInts : public ConfigOption, public ConfigOptionVector<int>
 {
     public:
     
-    std::string serialize() {
+    std::string serialize() const {
         std::ostringstream ss;
         for (std::vector<int>::const_iterator it = this->values.begin(); it != this->values.end(); ++it) {
             if (it - this->values.begin() != 0) ss << ",";
@@ -133,7 +133,7 @@ class ConfigOptionString : public ConfigOption
     
     operator std::string() const { return this->value; };
     
-    std::string serialize() {
+    std::string serialize() const {
         std::string str = this->value;
         
         // s/\R/\\n/g
@@ -163,7 +163,7 @@ class ConfigOptionStrings : public ConfigOption, public ConfigOptionVector<std::
 {
     public:
     
-    std::string serialize() {
+    std::string serialize() const {
         std::ostringstream ss;
         for (std::vector<std::string>::const_iterator it = this->values.begin(); it != this->values.end(); ++it) {
             if (it - this->values.begin() != 0) ss << ";";
@@ -189,7 +189,15 @@ class ConfigOptionFloatOrPercent : public ConfigOption
     bool percent;
     ConfigOptionFloatOrPercent() : value(0), percent(false) {};
     
-    std::string serialize() {
+    double get_abs_value(double ratio_over) const {
+        if (this->percent) {
+            return ratio_over * this->value / 100;
+        } else {
+            return this->value;
+        }
+    };
+    
+    std::string serialize() const {
         std::ostringstream ss;
         ss << this->value;
         std::string s(ss.str());
@@ -216,7 +224,7 @@ class ConfigOptionPoint : public ConfigOption
     
     operator Pointf() const { return this->point; };
     
-    std::string serialize() {
+    std::string serialize() const {
         std::ostringstream ss;
         ss << this->point.x;
         ss << ",";
@@ -233,7 +241,7 @@ class ConfigOptionPoints : public ConfigOption, public ConfigOptionVector<Pointf
 {
     public:
     
-    std::string serialize() {
+    std::string serialize() const {
         std::ostringstream ss;
         for (Pointfs::const_iterator it = this->values.begin(); it != this->values.end(); ++it) {
             if (it - this->values.begin() != 0) ss << ",";
@@ -264,7 +272,7 @@ class ConfigOptionBool : public ConfigOption
     
     operator bool() const { return this->value; };
     
-    std::string serialize() {
+    std::string serialize() const {
         return std::string(this->value ? "1" : "0");
     };
     
@@ -277,7 +285,7 @@ class ConfigOptionBools : public ConfigOption, public ConfigOptionVector<bool>
 {
     public:
     
-    std::string serialize() {
+    std::string serialize() const {
         std::ostringstream ss;
         for (std::vector<bool>::const_iterator it = this->values.begin(); it != this->values.end(); ++it) {
             if (it - this->values.begin() != 0) ss << ",";
@@ -306,7 +314,7 @@ class ConfigOptionEnum : public ConfigOption
     
     operator T() const { return this->value; };
     
-    std::string serialize() {
+    std::string serialize() const {
         t_config_enum_values enum_keys_map = ConfigOptionEnum<T>::get_enum_values();
         for (t_config_enum_values::iterator it = enum_keys_map.begin(); it != enum_keys_map.end(); ++it) {
             if (it->second == static_cast<int>(this->value)) return it->first;
@@ -333,7 +341,7 @@ class ConfigOptionEnumGeneric : public ConfigOption
     
     operator int() const { return this->value; };
     
-    std::string serialize() {
+    std::string serialize() const {
         for (t_config_enum_values::iterator it = this->keys_map->begin(); it != this->keys_map->end(); ++it) {
             if (it->second == this->value) return it->first;
         }
diff --git a/xs/src/Flow.cpp b/xs/src/Flow.cpp
new file mode 100644
index 000000000..e5e7dc2be
--- /dev/null
+++ b/xs/src/Flow.cpp
@@ -0,0 +1,109 @@
+#include "Flow.hpp"
+#include <cmath>
+
+namespace Slic3r {
+
+Flow
+Flow::new_from_config_width(FlowRole role, const ConfigOptionFloatOrPercent &width, float nozzle_diameter, float height, float bridge_flow_ratio) {
+    float w;
+    if (!width.percent && width.value == 0) {
+        w = Flow::_width(role, nozzle_diameter, height, bridge_flow_ratio);
+    } else {
+        w = width.get_abs_value(height);
+    }
+    
+    Flow flow(w, Flow::_spacing(w, nozzle_diameter, height, bridge_flow_ratio), nozzle_diameter);
+    if (bridge_flow_ratio > 0) flow.bridge = true;
+    return flow;
+}
+
+Flow
+Flow::new_from_spacing(float spacing, float nozzle_diameter, float height, bool bridge) {
+    float w = Flow::_width_from_spacing(spacing, nozzle_diameter, height, bridge);
+    Flow flow(w, spacing, nozzle_diameter);
+    flow.bridge = bridge;
+    return flow;
+}
+
+double
+Flow::mm3_per_mm(float h) {
+    if (this->bridge) {
+        return (this->width * this->width) * PI/4.0;
+    } else if (this->width >= (this->nozzle_diameter + h)) {
+        // rectangle with semicircles at the ends
+        return this->width * h + (h*h) / 4.0 * (PI-4.0);
+    } else {
+        // rectangle with shrunk semicircles at the ends
+        return this->nozzle_diameter * h * (1 - PI/4.0) + h * this->width * PI/4.0;
+    }
+}
+
+float
+Flow::_width(FlowRole role, float nozzle_diameter, float height, float bridge_flow_ratio) {
+    if (bridge_flow_ratio > 0) {
+        return sqrt(bridge_flow_ratio * (nozzle_diameter*nozzle_diameter));
+    }
+    
+    // here we calculate a sane default by matching the flow speed (at the nozzle) and the feed rate
+    float volume = (nozzle_diameter*nozzle_diameter) * PI/4.0;
+    float shape_threshold = nozzle_diameter * height + (height*height) * PI/4.0;
+    float width;
+    if (volume >= shape_threshold) {
+        // rectangle with semicircles at the ends
+        width = ((nozzle_diameter*nozzle_diameter) * PI + (height*height) * (4.0 - PI)) / (4.0 * height);
+    } else {
+        // rectangle with squished semicircles at the ends
+        width = nozzle_diameter * (nozzle_diameter/height - 4.0/PI + 1);
+    }
+    
+    float min = nozzle_diameter * 1.05;
+    float max = -1;
+    if (role == frPerimeter || role == frSupportMaterial) {
+        min = max = nozzle_diameter;
+    } else if (role != frInfill) {
+        // do not limit width for sparse infill so that we use full native flow for it
+        max = nozzle_diameter * 1.7;
+    }
+    if (max != -1 && width > max) width = max;
+    if (width < min) width = min;
+    
+    return width;
+}
+
+
+float
+Flow::_width_from_spacing(float spacing, float nozzle_diameter, float height, bool bridge) {
+    if (bridge) {
+        return spacing - BRIDGE_EXTRA_SPACING;
+    }
+    
+    float w_threshold = height + nozzle_diameter;
+    float s_threshold = w_threshold - OVERLAP_FACTOR * (w_threshold - (w_threshold - height * (1 - PI/4.0)));
+    
+    if (spacing >= s_threshold) {
+        // rectangle with semicircles at the ends
+        return spacing + OVERLAP_FACTOR * height * (1 - PI/4.0);
+    } else {
+        // rectangle with shrunk semicircles at the ends
+        return (spacing + nozzle_diameter * OVERLAP_FACTOR * (PI/4.0 - 1)) / (1 + OVERLAP_FACTOR * (PI/4.0 - 1));
+    }
+}
+
+float
+Flow::_spacing(float width, float nozzle_diameter, float height, float bridge_flow_ratio) {
+    if (bridge_flow_ratio > 0) {
+        return width + BRIDGE_EXTRA_SPACING;
+    }
+    
+    float min_flow_spacing;
+    if (width >= (nozzle_diameter + height)) {
+        // rectangle with semicircles at the ends
+        min_flow_spacing = width - height * (1 - PI/4.0);
+    } else {
+        // rectangle with shrunk semicircles at the ends
+        min_flow_spacing = nozzle_diameter * (1 - PI/4.0) + width * PI/4.0;
+    }
+    return width - OVERLAP_FACTOR * (width - min_flow_spacing);
+}
+
+}
diff --git a/xs/src/Flow.hpp b/xs/src/Flow.hpp
new file mode 100644
index 000000000..e98173dc9
--- /dev/null
+++ b/xs/src/Flow.hpp
@@ -0,0 +1,48 @@
+#ifndef slic3r_Flow_hpp_
+#define slic3r_Flow_hpp_
+
+#include <myinit.h>
+#include "Config.hpp"
+#include "ExtrusionEntity.hpp"
+
+namespace Slic3r {
+
+#define BRIDGE_EXTRA_SPACING 0.05
+#define OVERLAP_FACTOR 1.0
+
+enum FlowRole {
+    frPerimeter,
+    frInfill,
+    frSolidInfill,
+    frTopSolidInfill,
+    frSupportMaterial,
+    frSupportMaterialInterface,
+};
+
+class Flow
+{
+    public:
+    float width;
+    float spacing;
+    float nozzle_diameter;
+    bool bridge;
+    coord_t scaled_width;
+    coord_t scaled_spacing;
+    
+    Flow(float _w, float _s, float _nd): width(_w), spacing(_s), nozzle_diameter(_nd), bridge(false) {
+        this->scaled_width   = scale_(this->width);
+        this->scaled_spacing = scale_(this->spacing);
+    };
+    double mm3_per_mm(float h);
+    static Flow new_from_config_width(FlowRole role, const ConfigOptionFloatOrPercent &width, float nozzle_diameter, float height, float bridge_flow_ratio);
+    static Flow new_from_spacing(float spacing, float nozzle_diameter, float height, bool bridge);
+    
+    private:
+    static float _width(FlowRole role, float nozzle_diameter, float height, float bridge_flow_ratio);
+    static float _width_from_spacing(float spacing, float nozzle_diameter, float height, bool bridge);
+    static float _spacing(float width, float nozzle_diameter, float height, float bridge_flow_ratio);
+};
+
+}
+
+#endif
diff --git a/xs/src/Point.cpp b/xs/src/Point.cpp
index fbb3b06c6..d35b3f0f8 100644
--- a/xs/src/Point.cpp
+++ b/xs/src/Point.cpp
@@ -23,8 +23,8 @@ Point::rotate(double angle, Point* center)
 {
     double cur_x = (double)this->x;
     double cur_y = (double)this->y;
-    this->x = (long)round( (double)center->x + cos(angle) * (cur_x - (double)center->x) - sin(angle) * (cur_y - (double)center->y) );
-    this->y = (long)round( (double)center->y + cos(angle) * (cur_y - (double)center->y) + sin(angle) * (cur_x - (double)center->x) );
+    this->x = (coord_t)round( (double)center->x + cos(angle) * (cur_x - (double)center->x) - sin(angle) * (cur_y - (double)center->y) );
+    this->y = (coord_t)round( (double)center->y + cos(angle) * (cur_y - (double)center->y) + sin(angle) * (cur_x - (double)center->x) );
 }
 
 bool
diff --git a/xs/src/Point.hpp b/xs/src/Point.hpp
index 507d082c9..385f99cdb 100644
--- a/xs/src/Point.hpp
+++ b/xs/src/Point.hpp
@@ -17,9 +17,9 @@ typedef std::vector<Pointf> Pointfs;
 class Point
 {
     public:
-    long x;
-    long y;
-    explicit Point(long _x = 0, long _y = 0): x(_x), y(_y) {};
+    coord_t x;
+    coord_t y;
+    explicit Point(coord_t _x = 0, coord_t _y = 0): x(_x), y(_y) {};
     void scale(double factor);
     void translate(double x, double y);
     void rotate(double angle, Point* center);
diff --git a/xs/src/myinit.h b/xs/src/myinit.h
index 1ef522b44..fcbaca5c0 100644
--- a/xs/src/myinit.h
+++ b/xs/src/myinit.h
@@ -20,8 +20,10 @@ extern "C" {
 
 #define EPSILON 1e-4
 #define SCALING_FACTOR 0.000001
+#define PI 3.141592653589793238
 #define scale_(val) (val / SCALING_FACTOR)
 #define unscale(val) (val * SCALING_FACTOR)
+typedef long coord_t;
 
 namespace Slic3r {}
 using namespace Slic3r;
diff --git a/xs/t/15_config.t b/xs/t/15_config.t
index c80f14408..ed60c3e6d 100644
--- a/xs/t/15_config.t
+++ b/xs/t/15_config.t
@@ -4,7 +4,7 @@ use strict;
 use warnings;
 
 use Slic3r::XS;
-use Test::More tests => 88;
+use Test::More tests => 89;
 
 foreach my $config (Slic3r::Config->new, Slic3r::Config::Full->new) {
     $config->set('layer_height', 0.3);
@@ -110,6 +110,9 @@ foreach my $config (Slic3r::Config->new, Slic3r::Config::Full->new) {
     my $config2 = Slic3r::Config->new;
     $config2->apply_static($config);
     is $config2->get('perimeters'), Slic3r::Config::print_config_def()->{perimeters}{default}, 'apply_static and print_config_def';
+    
+    $config->set('top_solid_infill_speed', 70);
+    is $config->get_abs_value('top_solid_infill_speed'), 70, 'get_abs_value() works when ratio_over references a floatOrPercent option';
 }
 
 {
diff --git a/xs/t/16_flow.t b/xs/t/16_flow.t
new file mode 100644
index 000000000..e19430200
--- /dev/null
+++ b/xs/t/16_flow.t
@@ -0,0 +1,29 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Slic3r::XS;
+use Test::More tests => 2;
+
+{
+    my $flow = Slic3r::Flow->new_from_width(
+        role => Slic3r::Flow::FLOW_ROLE_PERIMETER,
+        width               => '1',
+        nozzle_diameter     => 0.5,
+        layer_height        => 0.3, 
+        bridge_flow_ratio   => 1,
+    );
+    isa_ok $flow, 'Slic3r::Flow', 'new_from_width';
+}
+
+{
+    my $flow = Slic3r::Flow->new(
+        width               => 1,
+        spacing             => 0.95,
+        nozzle_diameter     => 0.5,
+    );
+    isa_ok $flow, 'Slic3r::Flow', 'new';
+}
+
+__END__
diff --git a/xs/xsp/Flow.xsp b/xs/xsp/Flow.xsp
new file mode 100644
index 000000000..4ff5427fc
--- /dev/null
+++ b/xs/xsp/Flow.xsp
@@ -0,0 +1,80 @@
+%module{Slic3r::XS};
+
+%{
+#include <myinit.h>
+#include "Flow.hpp"
+%}
+
+%name{Slic3r::Flow} class Flow {
+    ~Flow();
+    %name{_new} Flow(float width, float spacing, float nozzle_diameter);
+    void set_bridge(bool bridge)
+        %code{% THIS->bridge = bridge; %};
+    Flow* clone()
+        %code{% const char* CLASS = "Slic3r::Flow"; RETVAL = new Flow(*THIS); %};
+    
+    float width()
+        %code{% RETVAL = THIS->width; %};
+    float spacing()
+        %code{% RETVAL = THIS->spacing; %};
+    float nozzle_diameter()
+        %code{% RETVAL = THIS->nozzle_diameter; %};
+    bool bridge()
+        %code{% RETVAL = THIS->bridge; %};
+    long scaled_width()
+        %code{% RETVAL = THIS->scaled_width; %};
+    long scaled_spacing()
+        %code{% RETVAL = THIS->scaled_spacing; %};
+    
+    double mm3_per_mm(float height);
+%{
+
+Flow*
+_new_from_width(CLASS, role, width, nozzle_diameter, height, bridge_flow_ratio)
+    char*           CLASS;
+    FlowRole        role;
+    std::string     width;
+    float           nozzle_diameter;
+    float           height;
+    float           bridge_flow_ratio;
+    CODE:
+        ConfigOptionFloatOrPercent optwidth;
+        optwidth.deserialize(width);
+        RETVAL = new Flow(Flow::new_from_config_width(role, optwidth, nozzle_diameter, height, bridge_flow_ratio));
+    OUTPUT:
+        RETVAL
+
+Flow*
+_new_from_spacing(CLASS, spacing, nozzle_diameter, height, bridge)
+    char*           CLASS;
+    float           spacing;
+    float           nozzle_diameter;
+    float           height;
+    bool            bridge;
+    CODE:
+        RETVAL = new Flow(Flow::new_from_spacing(spacing, nozzle_diameter, height, bridge));
+    OUTPUT:
+        RETVAL
+
+%}
+};
+
+%package{Slic3r::Flow};
+%{
+
+IV
+_constant()
+  ALIAS:
+    FLOW_ROLE_PERIMETER                     = frPerimeter
+    FLOW_ROLE_INFILL                        = frInfill
+    FLOW_ROLE_SOLID_INFILL                  = frSolidInfill
+    FLOW_ROLE_TOP_SOLID_INFILL              = frTopSolidInfill
+    FLOW_ROLE_SUPPORT_MATERIAL              = frSupportMaterial
+    FLOW_ROLE_SUPPORT_MATERIAL_INTERFACE    = frSupportMaterialInterface
+  PROTOTYPE:
+  CODE:
+    RETVAL = ix;
+  OUTPUT: RETVAL
+
+%}
+
diff --git a/xs/xsp/my.map b/xs/xsp/my.map
index 666cbf616..6a02d9432 100644
--- a/xs/xsp/my.map
+++ b/xs/xsp/my.map
@@ -18,11 +18,13 @@ ExPolygonCollection*    O_OBJECT
 ExtrusionEntityCollection*    O_OBJECT
 ExtrusionPath*  O_OBJECT
 ExtrusionLoop*  O_OBJECT
+Flow*           O_OBJECT
 PrintState*  O_OBJECT
 Surface*        O_OBJECT
 SurfaceCollection*      O_OBJECT
 
 ExtrusionRole     T_UV
+FlowRole     T_UV
 PrintStep     T_UV
 SurfaceType     T_UV
 ClipperLib::JoinType		T_UV
diff --git a/xs/xsp/typemap.xspt b/xs/xsp/typemap.xspt
index 25c4c4d12..46a037526 100644
--- a/xs/xsp/typemap.xspt
+++ b/xs/xsp/typemap.xspt
@@ -15,6 +15,7 @@
 %typemap{FullPrintConfig*};
 %typemap{ExPolygon*};
 %typemap{ExPolygonCollection*};
+%typemap{Flow*};
 %typemap{Line*};
 %typemap{Polyline*};
 %typemap{Polygon*};
@@ -43,6 +44,12 @@
     $CVar = (ExtrusionRole)SvUV($PerlVar);
   %};
 };
+%typemap{FlowRole}{parsed}{
+  %cpp_type{FlowRole};
+  %precall_code{%
+    $CVar = (FlowRole)SvUV($PerlVar);
+  %};
+};
 %typemap{PrintStep}{parsed}{
   %cpp_type{PrintStep};
   %precall_code{%

From 2754ddf2155343f1a5b26bbc80c44a80175b4779 Mon Sep 17 00:00:00 2001
From: Alessandro Ranellucci <aar@cpan.org>
Date: Sun, 5 Jan 2014 14:04:32 +0100
Subject: [PATCH 18/19] Fix positioning of loded AMF plates

---
 lib/Slic3r/Format/AMF/Parser.pm |  2 +-
 lib/Slic3r/GUI/Plater.pm        | 66 ++++++++++++++++++---------------
 2 files changed, 37 insertions(+), 31 deletions(-)

diff --git a/lib/Slic3r/Format/AMF/Parser.pm b/lib/Slic3r/Format/AMF/Parser.pm
index c99d71800..43dbf05a9 100644
--- a/lib/Slic3r/Format/AMF/Parser.pm
+++ b/lib/Slic3r/Format/AMF/Parser.pm
@@ -100,7 +100,7 @@ sub end_element {
         if ($self->{_material_metadata_type} =~ /^slic3r\.(.+)/) {
             my $opt_key = $1;
             if (exists $Slic3r::Config::Options->{$opt_key}) {
-                $self->{_material}->config->set_deserialize($opt_key, $self->{_material}->attributes->{$opt_key});
+                $self->{_material}->config->set_deserialize($opt_key, $self->{_material}->attributes->{"slic3r.$opt_key"});
             }
         }
         $self->{_material_metadata_type} = undef;
diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm
index 1fb606c3c..1926b17f0 100644
--- a/lib/Slic3r/GUI/Plater.pm
+++ b/lib/Slic3r/GUI/Plater.pm
@@ -387,59 +387,65 @@ sub load_file {
     my $model = eval { Slic3r::Model->read_from_file($input_file) };
     Slic3r::GUI::show_error($self, $@) if $@;
     
-    $self->load_model_object($_) for @{$model->objects};
+    $self->load_model_objects(@{$model->objects});
     
     $process_dialog->Destroy;
     $self->statusbar->SetStatusText("Loaded " . basename($input_file));
 }
 
-sub load_model_object {
-    my ($self, $model_object) = @_;
-    
-    my $o = $self->{model}->add_object($model_object);
-        
-    push @{ $self->{objects} }, Slic3r::GUI::Plater::Object->new(
-        name => basename($model_object->input_file),
-    );
+sub load_model_objects {
+    my ($self, @model_objects) = @_;
     
     my $need_arrange = 0;
-    if (!defined $model_object->instances) {
-        # if object has no defined position(s) we need to rearrange everything after loading
-        $need_arrange = 1;
+    my @obj_idx = ();
+    foreach my $model_object (@model_objects) {
+        my $o = $self->{model}->add_object($model_object);
         
-        # add a default instance and center object around origin
-        $o->center_around_origin;
-        $o->add_instance(offset => [ @{$self->{config}->print_center} ]);
-    }
+        push @{ $self->{objects} }, Slic3r::GUI::Plater::Object->new(
+            name => basename($model_object->input_file),
+        );
+        push @obj_idx, $#{ $self->{objects} };
     
-    $self->{print}->add_model_object($o);
+        if (!defined $model_object->instances) {
+            # if object has no defined position(s) we need to rearrange everything after loading
+            $need_arrange = 1;
+        
+            # add a default instance and center object around origin
+            $o->center_around_origin;
+            $o->add_instance(offset => [ @{$self->{config}->print_center} ]);
+        }
+    
+        $self->{print}->add_model_object($o);
+    }
     
     # if user turned autocentering off, automatic arranging would disappoint them
     if (!$Slic3r::GUI::Settings->{_}{autocenter}) {
         $need_arrange = 0;
     }
     
-    $self->object_loaded($#{ $self->{objects} }, no_arrange => !$need_arrange);
+    $self->objects_loaded(\@obj_idx, no_arrange => !$need_arrange);
 }
 
-sub object_loaded {
+sub objects_loaded {
     my $self = shift;
-    my ($obj_idx, %params) = @_;
+    my ($obj_idxs, %params) = @_;
     
-    my $object = $self->{objects}[$obj_idx];
-    my $model_object = $self->{model}->objects->[$obj_idx];
-    $self->{list}->InsertStringItem($obj_idx, $object->name);
-    $self->{list}->SetItemFont($obj_idx, Wx::Font->new(10, wxDEFAULT, wxNORMAL, wxNORMAL))
-        if $self->{list}->can('SetItemFont');  # legacy code for wxPerl < 0.9918 not supporting SetItemFont()
+    foreach my $obj_idx (@$obj_idxs) {
+        my $object = $self->{objects}[$obj_idx];
+        my $model_object = $self->{model}->objects->[$obj_idx];
+        $self->{list}->InsertStringItem($obj_idx, $object->name);
+        $self->{list}->SetItemFont($obj_idx, Wx::Font->new(10, wxDEFAULT, wxNORMAL, wxNORMAL))
+            if $self->{list}->can('SetItemFont');  # legacy code for wxPerl < 0.9918 not supporting SetItemFont()
     
-    $self->{list}->SetItem($obj_idx, 1, $model_object->instances_count);
-    $self->{list}->SetItem($obj_idx, 2, ($model_object->instances->[0]->scaling_factor * 100) . "%");
+        $self->{list}->SetItem($obj_idx, 1, $model_object->instances_count);
+        $self->{list}->SetItem($obj_idx, 2, ($model_object->instances->[0]->scaling_factor * 100) . "%");
     
-    $self->make_thumbnail($obj_idx);
+        $self->make_thumbnail($obj_idx);
+    }
     $self->arrange unless $params{no_arrange};
     $self->update;
     $self->{list}->Update;
-    $self->{list}->Select($obj_idx, 1);
+    $self->{list}->Select($obj_idxs->[-1], 1);
     $self->object_list_changed;
 }
 
@@ -657,7 +663,7 @@ sub split_object {
         }
         # we need to center this single object around origin
         $model_object->center_around_origin;
-        $self->load_model_object($model_object);
+        $self->load_model_objects($model_object);
     }
 }
 

From c523ddea35e3ac38386c0daaa83397073ef5f6ae Mon Sep 17 00:00:00 2001
From: Alessandro Ranellucci <aar@cpan.org>
Date: Sun, 5 Jan 2014 14:58:41 +0100
Subject: [PATCH 19/19] Fix option names not showing in plater object settings

---
 xs/xsp/Config.xsp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/xs/xsp/Config.xsp b/xs/xsp/Config.xsp
index 79d9bd269..461f1cd89 100644
--- a/xs/xsp/Config.xsp
+++ b/xs/xsp/Config.xsp
@@ -147,7 +147,8 @@ print_config_def()
             }
             (void)hv_stores( hv, "type",        newSVpv(opt_type, 0) );
             (void)hv_stores( hv, "label",       newSVpvn(optdef->label.c_str(), optdef->label.length()) );
-            (void)hv_stores( hv, "full_label",  newSVpvn(optdef->full_label.c_str(), optdef->full_label.length()) );
+            if (!optdef->full_label.empty())
+                (void)hv_stores( hv, "full_label",  newSVpvn(optdef->full_label.c_str(), optdef->full_label.length()) );
             (void)hv_stores( hv, "category",    newSVpvn(optdef->category.c_str(), optdef->category.length()) );
             (void)hv_stores( hv, "tooltip",     newSVpvn(optdef->tooltip.c_str(), optdef->tooltip.length()) );
             (void)hv_stores( hv, "sidetext",    newSVpvn(optdef->sidetext.c_str(), optdef->sidetext.length()) );