From 3d11d1aebfee4fc1a592832b60ed142874d1ada7 Mon Sep 17 00:00:00 2001
From: bubnikv <bubnikv@gmail.com>
Date: Wed, 15 Feb 2017 11:05:52 +0100
Subject: [PATCH] make_skirt ported to C++

---
 lib/Slic3r/Print.pm        | 107 +-----------------------------
 xs/src/libslic3r/Print.cpp | 131 ++++++++++++++++++++++++++++++++++++-
 xs/src/libslic3r/Print.hpp |   2 +
 xs/xsp/Print.xsp           |   2 +
 4 files changed, 136 insertions(+), 106 deletions(-)

diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm
index 87d0398ca..e18fa6abf 100644
--- a/lib/Slic3r/Print.pm
+++ b/lib/Slic3r/Print.pm
@@ -209,112 +209,9 @@ sub make_skirt {
         $self->set_step_done(STEP_SKIRT);
         return;
     }
+
     $self->status_cb->(88, "Generating skirt");
-    
-    # First off we need to decide how tall the skirt must be.
-    # The skirt_height option from config is expressed in layers, but our
-    # object might have different layer heights, so we need to find the print_z
-    # of the highest layer involved.
-    # Note that unless has_infinite_skirt() == true
-    # the actual skirt might not reach this $skirt_height_z value since the print
-    # order of objects on each layer is not guaranteed and will not generally
-    # include the thickest object first. It is just guaranteed that a skirt is
-    # prepended to the first 'n' layers (with 'n' = skirt_height).
-    # $skirt_height_z in this case is the highest possible skirt height for safety.
-    my $skirt_height_z = -1;
-    foreach my $object (@{$self->objects}) {
-        my $skirt_height = $self->has_infinite_skirt
-            ? $object->layer_count
-            : min($self->config->skirt_height, $object->layer_count);
-        my $highest_layer = $object->get_layer($skirt_height - 1);
-        $skirt_height_z = max($skirt_height_z, $highest_layer->print_z);
-    }
-    
-    # collect points from all layers contained in skirt height
-    my @points = ();
-    foreach my $object (@{$self->objects}) {
-        my @object_points = ();
-        
-        # get object layers up to $skirt_height_z
-        foreach my $layer (@{$object->layers}) {
-            last if $layer->print_z > $skirt_height_z;
-            push @object_points, map @$_, map @$_, @{$layer->slices};
-        }
-        
-        # get support layers up to $skirt_height_z
-        foreach my $layer (@{$object->support_layers}) {
-            last if $layer->print_z > $skirt_height_z;
-            push @object_points, map @{$_->polyline}, @{$layer->support_fills} if $layer->support_fills;
-            push @object_points, map @{$_->polyline}, @{$layer->support_interface_fills} if $layer->support_interface_fills;
-        }
-        
-        # repeat points for each object copy
-        foreach my $copy (@{$object->_shifted_copies}) {
-            my @copy_points = map $_->clone, @object_points;
-            $_->translate(@$copy) for @copy_points;
-            push @points, @copy_points;
-        }
-    }
-    return if @points < 3;  # at least three points required for a convex hull
-    
-    # find out convex hull
-    my $convex_hull = convex_hull(\@points);
-    
-    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 $first_layer_height = $self->skirt_first_layer_height;
-    my $flow = $self->skirt_flow;
-    my $spacing = $flow->spacing;
-    my $mm3_per_mm = $flow->mm3_per_mm;
-    
-    my @extruders_e_per_mm = ();
-    my $extruder_idx = 0;
-    
-    my $skirts = $self->config->skirts;
-    $skirts ||= 1 if $self->has_infinite_skirt;
-    
-    # draw outlines from outside to inside
-    # loop while we have less skirts than required or any extruder hasn't reached the min length if any
-    my $distance = scale max($self->config->skirt_distance, $self->config->brim_width);
-    for (my $i = $skirts; $i > 0; $i--) {
-        $distance += scale $spacing;
-        my $loop = offset([$convex_hull], $distance, JT_ROUND, scale(0.1))->[0];
-        my $eloop = Slic3r::ExtrusionLoop->new_from_paths(
-            Slic3r::ExtrusionPath->new(
-                polyline        => Slic3r::Polygon->new(@$loop)->split_at_first_point,
-                role            => EXTR_ROLE_SKIRT,
-                mm3_per_mm      => $mm3_per_mm,         # this will be overridden at G-code export time
-                width           => $flow->width,
-                height          => $first_layer_height, # this will be overridden at G-code export time
-            ),
-        );
-        $eloop->role(EXTRL_ROLE_SKIRT);
-        $self->skirt->append($eloop);
-        
-        if ($self->config->min_skirt_length > 0) {
-            $extruded_length[$extruder_idx] ||= 0;
-            if (!$extruders_e_per_mm[$extruder_idx]) {
-                my $config = Slic3r::Config::GCode->new;
-                $config->apply_static($self->config);
-                my $extruder = Slic3r::Extruder->new($extruder_idx, $config);
-                $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}) {
-                    $extruder_idx++;
-                    next;
-                }
-            }
-        }
-    }
-    
-    $self->skirt->reverse;
-    
+    $self->_make_skirt();
     $self->set_step_done(STEP_SKIRT);
 }
 
diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
index 1619f8c7b..44a5abd1f 100644
--- a/xs/src/libslic3r/Print.cpp
+++ b/xs/src/libslic3r/Print.cpp
@@ -1,6 +1,7 @@
 #include "Print.hpp"
 #include "BoundingBox.hpp"
 #include "ClipperUtils.hpp"
+#include "Extruder.hpp"
 #include "Flow.hpp"
 #include "Geometry.hpp"
 #include "SupportMaterial.hpp"
@@ -839,9 +840,137 @@ Print::auto_assign_extruders(ModelObject* model_object) const
             //FIXME Vojtech: This assigns an extruder ID even to a modifier volume, if it has a material assigned.
             size_t extruder_id = (v - model_object->volumes.begin()) + 1;
             if (!(*v)->config.has("extruder"))
-                (*v)->config.opt<ConfigOptionInt>("extruder", true)->value = extruder_id;
+                (*v)->config.opt<ConfigOptionInt>("extruder", true)->value = int(extruder_id);
         }
     }
 }
 
+
+void Print::_make_skirt()
+{
+    // First off we need to decide how tall the skirt must be.
+    // The skirt_height option from config is expressed in layers, but our
+    // object might have different layer heights, so we need to find the print_z
+    // of the highest layer involved.
+    // Note that unless has_infinite_skirt() == true
+    // the actual skirt might not reach this $skirt_height_z value since the print
+    // order of objects on each layer is not guaranteed and will not generally
+    // include the thickest object first. It is just guaranteed that a skirt is
+    // prepended to the first 'n' layers (with 'n' = skirt_height).
+    // $skirt_height_z in this case is the highest possible skirt height for safety.
+    coordf_t skirt_height_z = 0.;
+    for (const PrintObject *object : this->objects) {
+        size_t skirt_layers = this->has_infinite_skirt() ? 
+            object->layer_count() : 
+            std::min(size_t(this->config.skirt_height.value), object->layer_count());
+        skirt_height_z = std::max(skirt_height_z, object->layers[skirt_layers-1]->print_z);
+    }
+    
+    // Collect points from all layers contained in skirt height.
+    Points points;
+    for (const PrintObject *object : this->objects) {
+        Points object_points;
+        // Get object layers up to skirt_height_z.
+        for (const Layer *layer : object->layers) {
+            if (layer->print_z > skirt_height_z)
+                break;
+            for (const ExPolygon &expoly : layer->slices.expolygons)
+                // Collect the outer contour points only, ignore holes for the calculation of the convex hull.
+                append(object_points, expoly.contour.points);
+        }
+        // Get support layers up to skirt_height_z.
+        for (const SupportLayer *layer : object->support_layers) {
+            if (layer->print_z > skirt_height_z)
+                break;
+            for (const ExtrusionEntity *extrusion_entity : layer->support_fills.entities)
+                append(object_points, extrusion_entity->as_polyline().points);
+            for (const ExtrusionEntity *extrusion_entity : layer->support_interface_fills.entities)
+                append(object_points, extrusion_entity->as_polyline().points);
+        }
+        // Repeat points for each object copy.
+        for (const Point &shift : object->_shifted_copies) {
+            Points copy_points = object_points;
+            for (Point &pt : copy_points)
+                pt.translate(shift);
+            append(points, copy_points);
+        }
+    }
+
+    if (points.size() < 3)
+        // At least three points required for a convex hull.
+        return;
+    
+    Polygon convex_hull = Slic3r::Geometry::convex_hull(points);
+    
+    // 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
+    double first_layer_height = this->skirt_first_layer_height();
+    Flow   flow = this->skirt_flow();
+    float  spacing = flow.spacing();
+    double mm3_per_mm = flow.mm3_per_mm();
+    
+    std::vector<size_t> extruders;
+    std::vector<double> extruders_e_per_mm;
+    {
+        auto set_extruders = this->extruders();
+        extruders.reserve(set_extruders.size());
+        extruders_e_per_mm.reserve(set_extruders.size());
+        for (auto &extruder_id : set_extruders) {
+            extruders.push_back(extruder_id);
+            GCodeConfig config;
+            config.apply(this->config, true);
+            extruders_e_per_mm.push_back(Extruder((unsigned int)extruder_id, &config).e_per_mm(mm3_per_mm));
+        }
+    }
+
+    // Number of skirt loops per skirt layer.
+    int n_skirts = this->config.skirts.value;
+    if (this->has_infinite_skirt() && n_skirts == 0)
+        n_skirts = 1;
+
+    // Initial offset of the brim inner edge from the object (possible with a support & raft).
+    // The skirt will touch the brim if the brim is extruded.
+    coord_t distance = scale_(std::max(this->config.skirt_distance.value, this->config.brim_width.value));
+    // Draw outlines from outside to inside.
+    // Loop while we have less skirts than required or any extruder hasn't reached the min length if any.
+    std::vector<coordf_t> extruded_length(extruders.size(), 0.);
+    for (int i = n_skirts, extruder_idx = 0; i > 0; -- i) {
+        // Offset the skirt outside.
+        distance += coord_t(scale_(spacing));
+        // Generate the skirt centerline.
+        Polygon loop = offset(convex_hull, distance, ClipperLib::jtRound, scale_(0.1)).front();
+        // Extrude the skirt loop.
+        ExtrusionLoop eloop(elrSkirt);
+        eloop.paths.emplace_back(ExtrusionPath(
+            ExtrusionPath(
+                erSkirt,
+                mm3_per_mm,         // this will be overridden at G-code export time
+                flow.width,
+                first_layer_height  // this will be overridden at G-code export time
+            )));
+        eloop.paths.back().polyline = loop.split_at_first_point();
+        this->skirt.append(eloop);
+        if (this->config.min_skirt_length.value > 0) {
+            // The skirt length is limited. Sum the total amount of filament length extruded, in mm.
+            extruded_length[extruder_idx] += unscale(loop.length()) * extruders_e_per_mm[extruder_idx];
+            if (extruded_length[extruder_idx] < this->config.min_skirt_length.value) {
+                // Not extruded enough yet with the current extruder. Add another loop.
+                if (i == 1)
+                    ++ i;
+            } else {
+                assert(extruded_length[extruder_idx] >= this->config.min_skirt_length.value);
+                // Enough extruded with the current extruder. Extrude with the next one,
+                // until the prescribed number of skirt loops is extruded.
+                if (extruder_idx + 1 < extruders.size())
+                    ++ extruder_idx;
+            }
+        } else {
+            // The skirt lenght is not limited, extrude the skirt with the 1st extruder only.
+        }
+    }
+    // Brims were generated inside out, reverse to print the outmost contour first.
+    this->skirt.reverse();
+}
+
 }
diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp
index 30d49b236..f18432da6 100644
--- a/xs/src/libslic3r/Print.hpp
+++ b/xs/src/libslic3r/Print.hpp
@@ -251,6 +251,8 @@ class Print
     double max_allowed_layer_height() const;
     bool has_support_material() const;
     void auto_assign_extruders(ModelObject* model_object) const;
+
+    void _make_skirt();
     
     private:
     void clear_regions();
diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp
index b70ff9c70..8651469cd 100644
--- a/xs/xsp/Print.xsp
+++ b/xs/xsp/Print.xsp
@@ -270,6 +270,8 @@ _constant()
     double skirt_first_layer_height();
     Clone<Flow> brim_flow();
     Clone<Flow> skirt_flow();
+
+    void _make_skirt();
 %{
 
 double