Просмотр исходного кода

Ported Slic3r::BridgeDetector to XS

Alessandro Ranellucci 10 лет назад
Родитель
Сommit
379cde30e2

+ 1 - 1
lib/Slic3r.pm

@@ -58,7 +58,6 @@ use Slic3r::GCode::VibrationLimit;
 use Slic3r::Geometry qw(PI);
 use Slic3r::Geometry::Clipper;
 use Slic3r::Layer;
-use Slic3r::Layer::BridgeDetector;
 use Slic3r::Layer::Region;
 use Slic3r::Line;
 use Slic3r::Model;
@@ -162,6 +161,7 @@ sub thread_cleanup {
     
     # prevent destruction of shared objects
     no warnings 'redefine';
+    *Slic3r::BridgeDetector::DESTROY        = sub {};
     *Slic3r::Config::DESTROY                = sub {};
     *Slic3r::Config::Full::DESTROY          = sub {};
     *Slic3r::Config::GCode::DESTROY         = sub {};

+ 0 - 17
lib/Slic3r/Geometry.pm

@@ -207,23 +207,6 @@ sub polygon_is_convex {
     return 1;
 }
 
-sub deg2rad {
-    my ($degrees) = @_;
-    return PI() * $degrees / 180;
-}
-
-sub rad2deg {
-    my ($rad) = @_;
-    return $rad / PI() * 180;
-}
-
-sub rad2deg_dir {
-    my ($rad) = @_;
-    $rad = ($rad < PI) ? (-$rad + PI/2) : ($rad + PI/2);
-    $rad += PI if $rad < 0;
-    return rad2deg($rad);
-}
-
 sub rotate_points {
     my ($radians, $center, @points) = @_;
     $center //= [0,0];

+ 0 - 277
lib/Slic3r/Layer/BridgeDetector.pm

@@ -1,277 +0,0 @@
-package Slic3r::Layer::BridgeDetector;
-use Moo;
-
-use List::Util qw(first sum max min);
-use Slic3r::Geometry qw(PI unscale scaled_epsilon rad2deg epsilon directions_parallel_within);
-use Slic3r::Geometry::Clipper qw(intersection_pl intersection_ex union offset diff_pl union_ex
-    intersection_ppl);
-
-has 'expolygon'         => (is => 'ro', required => 1);
-has 'lower_slices'      => (is => 'rw', required => 1);  # ExPolygons or ExPolygonCollection
-has 'extrusion_width'   => (is => 'rw', required => 1);  # scaled
-has 'resolution'        => (is => 'rw', default => sub { PI/36 });
-
-has '_edges'            => (is => 'rw'); # Polylines representing the supporting edges
-has '_anchors'          => (is => 'rw'); # ExPolygons
-has 'angle'             => (is => 'rw');
-
-sub BUILD {
-    my ($self) = @_;
-    
-    # outset our bridge by an arbitrary amout; we'll use this outer margin
-    # for detecting anchors
-    my $grown = $self->expolygon->offset(+$self->extrusion_width);
-    
-    # detect what edges lie on lower slices
-    $self->_edges(my $edges = []);
-    foreach my $lower (@{$self->lower_slices}) {
-        # turn bridge contour and holes into polylines and then clip them
-        # with each lower slice's contour
-        push @$edges, @{intersection_ppl($grown, [ $lower->contour ])};
-    }
-    Slic3r::debugf "  bridge has %d support(s)\n", scalar(@$edges);
-    
-    # detect anchors as intersection between our bridge expolygon and the lower slices
-    $self->_anchors(intersection_ex(
-        $grown,
-        [ map @$_, @{$self->lower_slices} ],
-        1,  # safety offset required to avoid Clipper from detecting empty intersection while Boost actually found some @edges
-    ));
-    
-    if (0) {
-        require "Slic3r/SVG.pm";
-        Slic3r::SVG::output("bridge.svg",
-            expolygons      => [ $self->expolygon ],
-            red_expolygons  => $self->lower_slices,
-            polylines       => $self->_edges,
-        );
-    }
-}
-
-sub detect_angle {
-    my ($self) = @_;
-    
-    return undef if !@{$self->_edges};
-    
-    my @edges = @{$self->_edges};
-    my $anchors = $self->_anchors;
-    
-    if (!@$anchors) {
-        $self->angle(undef);
-        return undef;
-    }
-    
-    # Outset the bridge expolygon by half the amount we used for detecting anchors;
-    # we'll use this one to clip our test lines and be sure that their endpoints
-    # are inside the anchors and not on their contours leading to false negatives.
-    my $clip_area = $self->expolygon->offset_ex(+$self->extrusion_width/2);
-    
-    # we'll now try several directions using a rudimentary visibility check:
-    # bridge in several directions and then sum the length of lines having both
-    # endpoints within anchors
-    
-    # we test angles according to configured resolution
-    my @angles = map { $_*$self->resolution } 0..(PI/$self->resolution);
-    
-    # we also test angles of each bridge contour
-    push @angles, map $_->direction, map @{$_->lines}, @{$self->expolygon};
-    
-    # we also test angles of each open supporting edge
-    # (this finds the optimal angle for C-shaped supports)
-    push @angles,
-        map Slic3r::Line->new($_->first_point, $_->last_point)->direction,
-        grep { !$_->first_point->coincides_with($_->last_point) }
-        @edges;
-    
-    # remove duplicates
-    my $min_resolution = PI/180; # 1 degree
-    # proceed in reverse order so that when we compare first value with last one (-1)
-    # we remove the greatest one (PI) in case they are parallel (PI, 0)
-    @angles = reverse sort @angles;
-    for (my $i = 0; $i <= $#angles; ++$i) {
-        if (directions_parallel_within($angles[$i], $angles[$i-1], $min_resolution)) {
-            splice @angles, $i, 1;
-            --$i;
-        }
-    }
-    
-    my %directions_coverage     = ();  # angle => score
-    my %directions_avg_length   = ();  # angle => score
-    my $line_increment = $self->extrusion_width;
-    my %unique_angles = map { $_ => 1 } @angles;
-    for my $angle (@angles) {
-        my $my_clip_area    = [ map $_->clone, @$clip_area ];
-        my $my_anchors      = [ map $_->clone, @$anchors ];
-        
-        # rotate everything - the center point doesn't matter
-        $_->rotate(-$angle, [0,0]) for @$my_clip_area, @$my_anchors;
-    
-        # generate lines in this direction
-        my $bounding_box = Slic3r::Geometry::BoundingBox->new_from_points([ map @$_, map @$_, @$my_anchors ]);
-    
-        my @lines = ();
-        for (my $y = $bounding_box->y_min; $y <= $bounding_box->y_max; $y+= $line_increment) {
-            push @lines, Slic3r::Polyline->new(
-                [$bounding_box->x_min, $y],
-                [$bounding_box->x_max, $y],
-            );
-        }
-        
-        my @clipped_lines = map Slic3r::Line->new(@$_), @{ intersection_pl(\@lines, [ map @$_, @$my_clip_area ]) };
-        
-        # remove any line not having both endpoints within anchors
-        @clipped_lines = grep {
-            my $line = $_;
-            (first { $_->contains_point($line->a) } @$my_anchors)
-                && (first { $_->contains_point($line->b) } @$my_anchors);
-        } @clipped_lines;
-        
-        my @lengths = map $_->length, @clipped_lines;
-        
-        # sum length of bridged lines
-        $directions_coverage{$angle} = sum(@lengths) // 0;
-        
-        ### The following produces more correct results in some cases and more broken in others.
-        ### TODO: investigate, as it looks more reliable than line clipping.
-        ###$directions_coverage{$angle} = sum(map $_->area, @{$self->coverage($angle)}) // 0;
-        
-        # max length of bridged lines
-        $directions_avg_length{$angle} = @lengths ? (max(@lengths)) : -1;
-    }
-    
-    # if no direction produced coverage, then there's no bridge direction
-    return undef if !defined first { $_ > 0 } values %directions_coverage;
-    
-    # the best direction is the one causing most lines to be bridged (thus most coverage)
-    # and shortest max line length
-    my @sorted_directions = sort {
-        my $cmp;
-        my $coverage_diff = $directions_coverage{$a} - $directions_coverage{$b};
-        if (abs($coverage_diff) < $self->extrusion_width) {
-            $cmp = $directions_avg_length{$b} <=> $directions_avg_length{$a};
-        } else {
-            $cmp = ($coverage_diff > 0) ? 1 : -1;
-        }
-        $cmp;
-    } keys %directions_coverage;
-    
-    $self->angle($sorted_directions[-1]);
-    
-    if ($self->angle >= PI) {
-        $self->angle($self->angle - PI);
-    }
-    
-    Slic3r::debugf "  Optimal infill angle is %d degrees\n", rad2deg($self->angle);
-    
-    return $self->angle;
-}
-
-sub coverage {
-    my ($self, $angle) = @_;
-    
-    if (!defined $angle) {
-        return [] if !defined($angle = $self->angle);
-    }
-    
-    # Clone our expolygon and rotate it so that we work with vertical lines.
-    my $expolygon = $self->expolygon->clone;
-    $expolygon->rotate(PI/2 - $angle, [0,0]);
-    
-    # Outset the bridge expolygon by half the amount we used for detecting anchors;
-    # we'll use this one to generate our trapezoids and be sure that their vertices
-    # are inside the anchors and not on their contours leading to false negatives.
-    my $grown = $expolygon->offset_ex(+$self->extrusion_width/2);
-    
-    # Compute trapezoids according to a vertical orientation
-    my $trapezoids = [ map @{$_->get_trapezoids2(PI/2)}, @$grown ];
-    
-    # get anchors and rotate them too
-    my $anchors = [ map $_->clone, @{$self->_anchors} ];
-    $_->rotate(PI/2 - $angle, [0,0]) for @$anchors;
-    
-    my @covered = ();  # polygons
-    foreach my $trapezoid (@$trapezoids) {
-        my @polylines = map $_->as_polyline, @{$trapezoid->lines};
-        my @supported = @{intersection_pl(\@polylines, [map @$_, @$anchors])};
-        
-        # not nice, we need a more robust non-numeric check
-        @supported = grep $_->length >= $self->extrusion_width, @supported;
-        
-        if (@supported >= 2) {
-            push @covered, $trapezoid;
-        }
-    }
-    
-    # merge trapezoids and rotate them back
-    my $coverage = union(\@covered);
-    $_->rotate(-(PI/2 - $angle), [0,0]) for @$coverage;
-    
-    # intersect trapezoids with actual bridge area to remove extra margins
-    $coverage = intersection_ex($coverage, [ @{$self->expolygon} ]);
-    
-    if (0) {
-        my @lines = map @{$_->lines}, @$trapezoids;
-        $_->rotate(-(PI/2 - $angle), [0,0]) for @lines;
-        
-        require "Slic3r/SVG.pm";
-        Slic3r::SVG::output(
-            "coverage_" . rad2deg($angle) . ".svg",
-            expolygons          => [$self->expolygon],
-            green_expolygons    => $self->_anchors,
-            red_expolygons      => $coverage,
-            lines               => \@lines,
-        );
-    }
-    
-    return $coverage;
-}
-
-# this method returns the bridge edges (as polylines) that are not supported
-# but would allow the entire bridge area to be bridged with detected angle
-# if supported too
-sub unsupported_edges {
-    my ($self, $angle) = @_;
-    
-    if (!defined $angle) {
-        return [] if !defined($angle = $self->angle);
-    }
-    
-    # get bridge edges (both contour and holes)
-    my @bridge_edges = map $_->split_at_first_point, @{$self->expolygon};
-    $_->[0]->translate(1,0) for @bridge_edges;  # workaround for Clipper bug, see comments in Slic3r::Polygon::clip_as_polyline()
-    
-    # get unsupported edges
-    my $grown_lower = offset([ map @$_, @{$self->lower_slices} ], +$self->extrusion_width);
-    my $unsupported = diff_pl(
-        \@bridge_edges,
-        $grown_lower,
-    );
-    
-    # split into individual segments and filter out edges parallel to the bridging angle
-    # TODO: angle tolerance should probably be based on segment length and flow width,
-    # so that we build supports whenever there's a chance that at least one or two bridge
-    # extrusions would be anchored within such length (i.e. a slightly non-parallel bridging
-    # direction might still benefit from anchors if long enough)
-    my $angle_tolerance = PI/180*5;
-    @$unsupported = map $_->as_polyline,
-        grep !directions_parallel_within($_->direction, $angle, $angle_tolerance),
-        map @{$_->lines},
-        @$unsupported;
-    
-    if (0) {
-        require "Slic3r/SVG.pm";
-        Slic3r::SVG::output(
-            "unsupported_" . rad2deg($angle) . ".svg",
-            expolygons          => [$self->expolygon],
-            green_expolygons    => $self->_anchors,
-            red_expolygons      => union_ex($grown_lower),
-            no_arrows           => 1,
-            polylines           => \@bridge_edges,
-            red_polylines       => $unsupported,
-        );
-    }
-    
-    return $unsupported;
-}
-
-1;

+ 7 - 6
lib/Slic3r/Layer/Region.pm

@@ -486,16 +486,17 @@ sub process_external_surfaces {
         # of very thin (but still working) anchors, the grown expolygon would go beyond them
         my $angle;
         if ($lower_layer) {
-            my $bridge_detector = Slic3r::Layer::BridgeDetector->new(
-                expolygon       => $surface->expolygon,
-                lower_slices    => $lower_layer->slices,
-                extrusion_width => $self->flow(FLOW_ROLE_INFILL, $self->height, 1)->scaled_width,
+            my $bridge_detector = Slic3r::BridgeDetector->new(
+                $surface->expolygon,
+                $lower_layer->slices,
+                $self->flow(FLOW_ROLE_INFILL, $self->height, 1)->scaled_width,
             );
             Slic3r::debugf "Processing bridge at layer %d:\n", $self->id;
-            $angle = $bridge_detector->detect_angle;
+            $bridge_detector->detect_angle;
+            $angle = $bridge_detector->angle;
             
             if (defined $angle && $self->object->config->support_material) {
-                $self->bridged->append($_) for @{ $bridge_detector->coverage($angle) };
+                $self->bridged->append($_) for @{ $bridge_detector->coverage_with_angle($angle) };
                 $self->unsupported_bridge_edges->append($_) for @{ $bridge_detector->unsupported_edges }; 
             }
         }

+ 7 - 6
t/bridges.t

@@ -84,17 +84,18 @@ use Slic3r::Test;
 sub check_angle {
     my ($lower, $bridge, $expected, $tolerance, $expected_coverage) = @_;
     
+    if (ref($lower) eq 'ARRAY') {
+        $lower = Slic3r::ExPolygon::Collection->new(@$lower);
+    }
+    
     $expected_coverage //= -1;
     $expected_coverage = $bridge->area if $expected_coverage == -1;
     
-    my $bd = Slic3r::Layer::BridgeDetector->new(
-        expolygon       => $bridge,
-        lower_slices    => $lower,
-        extrusion_width => scale 0.5,
-    );
+    my $bd = Slic3r::BridgeDetector->new($bridge, $lower, scale 0.5);
     
     $tolerance //= rad2deg($bd->resolution) + epsilon;
-    my $result = $bd->detect_angle;
+    $bd->detect_angle;
+    my $result = $bd->angle;
     my $coverage = $bd->coverage;
     is sum(map $_->area, @$coverage), $expected_coverage, 'correct coverage area';
     

+ 6 - 0
xs/MANIFEST

@@ -1652,6 +1652,8 @@ src/clipper.cpp
 src/clipper.hpp
 src/libslic3r/BoundingBox.cpp
 src/libslic3r/BoundingBox.hpp
+src/libslic3r/BridgeDetector.cpp
+src/libslic3r/BridgeDetector.hpp
 src/libslic3r/ClipperUtils.cpp
 src/libslic3r/ClipperUtils.hpp
 src/libslic3r/Config.cpp
@@ -1669,6 +1671,8 @@ src/libslic3r/ExtrusionEntityCollection.hpp
 src/libslic3r/Flow.cpp
 src/libslic3r/Flow.hpp
 src/libslic3r/GCode.hpp
+src/libslic3r/GCodeWriter.cpp
+src/libslic3r/GCodeWriter.hpp
 src/libslic3r/Geometry.cpp
 src/libslic3r/Geometry.hpp
 src/libslic3r/Layer.cpp
@@ -1745,6 +1749,7 @@ t/18_motionplanner.t
 t/19_model.t
 t/20_print.t
 xsp/BoundingBox.xsp
+xsp/BridgeDetector.xsp
 xsp/Clipper.xsp
 xsp/Config.xsp
 xsp/ExPolygon.xsp
@@ -1754,6 +1759,7 @@ xsp/ExtrusionEntityCollection.xsp
 xsp/ExtrusionLoop.xsp
 xsp/ExtrusionPath.xsp
 xsp/Flow.xsp
+xsp/GCodeWriter.xsp
 xsp/Geometry.xsp
 xsp/Layer.xsp
 xsp/Line.xsp

+ 1 - 0
xs/lib/Slic3r/XS.pm

@@ -188,6 +188,7 @@ sub new {
 
 package main;
 for my $class (qw(
+        Slic3r::BridgeDetector
         Slic3r::Config
         Slic3r::Config::Full
         Slic3r::Config::GCode

+ 15 - 0
xs/src/libslic3r/BoundingBox.cpp

@@ -94,6 +94,14 @@ BoundingBoxBase<PointClass>::merge(const PointClass &point)
 template void BoundingBoxBase<Point>::merge(const Point &point);
 template void BoundingBoxBase<Pointf>::merge(const Pointf &point);
 
+template <class PointClass> void
+BoundingBoxBase<PointClass>::merge(const std::vector<PointClass> &points)
+{
+    this->merge(BoundingBoxBase(points));
+}
+template void BoundingBoxBase<Point>::merge(const Points &points);
+template void BoundingBoxBase<Pointf>::merge(const Pointfs &points);
+
 template <class PointClass> void
 BoundingBoxBase<PointClass>::merge(const BoundingBoxBase<PointClass> &bb)
 {
@@ -122,6 +130,13 @@ BoundingBox3Base<PointClass>::merge(const PointClass &point)
 }
 template void BoundingBox3Base<Pointf3>::merge(const Pointf3 &point);
 
+template <class PointClass> void
+BoundingBox3Base<PointClass>::merge(const std::vector<PointClass> &points)
+{
+    this->merge(BoundingBox3Base(points));
+}
+template void BoundingBox3Base<Pointf3>::merge(const Pointf3s &points);
+
 template <class PointClass> void
 BoundingBox3Base<PointClass>::merge(const BoundingBox3Base<PointClass> &bb)
 {

+ 2 - 0
xs/src/libslic3r/BoundingBox.hpp

@@ -23,6 +23,7 @@ class BoundingBoxBase
     BoundingBoxBase() : defined(false) {};
     BoundingBoxBase(const std::vector<PointClass> &points);
     void merge(const PointClass &point);
+    void merge(const std::vector<PointClass> &points);
     void merge(const BoundingBoxBase<PointClass> &bb);
     void scale(double factor);
     PointClass size() const;
@@ -38,6 +39,7 @@ class BoundingBox3Base : public BoundingBoxBase<PointClass>
     BoundingBox3Base() : BoundingBoxBase<PointClass>() {};
     BoundingBox3Base(const std::vector<PointClass> &points);
     void merge(const PointClass &point);
+    void merge(const std::vector<PointClass> &points);
     void merge(const BoundingBox3Base<PointClass> &bb);
     PointClass size() const;
     void translate(coordf_t x, coordf_t y, coordf_t z);

+ 331 - 0
xs/src/libslic3r/BridgeDetector.cpp

@@ -0,0 +1,331 @@
+#include "BridgeDetector.hpp"
+#include "ClipperUtils.hpp"
+#include "Geometry.hpp"
+#include <algorithm>
+
+namespace Slic3r {
+
+class BridgeDirectionComparator {
+    public:
+    std::map<double,double> dir_coverage, dir_avg_length;  // angle => score
+    
+    BridgeDirectionComparator(double _extrusion_width)
+        : extrusion_width(_extrusion_width) {};
+    
+    // the best direction is the one causing most lines to be bridged (thus most coverage)
+    // and shortest max line length
+    bool operator() (double a, double b) {
+        double coverage_diff = this->dir_coverage[a] - this->dir_coverage[b];
+        if (fabs(coverage_diff) < this->extrusion_width) {
+            return (this->dir_avg_length[b] > this->dir_avg_length[a]);
+        } else {
+            return (coverage_diff > 0);
+        }
+    };
+    
+    private:
+    double extrusion_width;
+};
+
+BridgeDetector::BridgeDetector(const ExPolygon &_expolygon, const ExPolygonCollection &_lower_slices,
+    coord_t _extrusion_width)
+    : expolygon(_expolygon), lower_slices(_lower_slices), extrusion_width(_extrusion_width),
+        angle(-1), resolution(PI/36.0)
+{
+    /*  outset our bridge by an arbitrary amout; we'll use this outer margin
+        for detecting anchors */
+    Polygons grown;
+    offset((Polygons)this->expolygon, grown, this->extrusion_width);
+    
+    // detect what edges lie on lower slices
+    for (ExPolygons::const_iterator lower = this->lower_slices.expolygons.begin();
+        lower != this->lower_slices.expolygons.end();
+        ++lower) {
+        /*  turn bridge contour and holes into polylines and then clip them
+            with each lower slice's contour */
+        intersection(grown, lower->contour, this->_edges);
+    }
+    #ifdef SLIC3R_DEBUG
+    printf("  bridge has %zu support(s)\n", this->_edges.size());
+    #endif
+    
+    // detect anchors as intersection between our bridge expolygon and the lower slices
+    // safety offset required to avoid Clipper from detecting empty intersection while Boost actually found some edges
+    intersection(grown, this->lower_slices, this->_anchors, true);
+    
+    /*
+    if (0) {
+        require "Slic3r/SVG.pm";
+        Slic3r::SVG::output("bridge.svg",
+            expolygons      => [ $self->expolygon ],
+            red_expolygons  => $self->lower_slices,
+            polylines       => $self->_edges,
+        );
+    }
+    */
+}
+
+bool
+BridgeDetector::detect_angle()
+{
+    if (this->_edges.empty() || this->_anchors.empty()) return false;
+    
+    /*  Outset the bridge expolygon by half the amount we used for detecting anchors;
+        we'll use this one to clip our test lines and be sure that their endpoints
+        are inside the anchors and not on their contours leading to false negatives. */
+    Polygons clip_area;
+    offset(this->expolygon, clip_area, +this->extrusion_width/2);
+    
+    /*  we'll now try several directions using a rudimentary visibility check:
+        bridge in several directions and then sum the length of lines having both
+        endpoints within anchors */
+    
+    // we test angles according to configured resolution
+    std::vector<double> angles;
+    for (int i = 0; i <= PI/this->resolution; ++i)
+        angles.push_back(i * this->resolution);
+    
+    // we also test angles of each bridge contour
+    {
+        Polygons pp = this->expolygon;
+        for (Polygons::const_iterator p = pp.begin(); p != pp.end(); ++p) {
+            Lines lines;
+            p->lines(&lines);
+            for (Lines::const_iterator line = lines.begin(); line != lines.end(); ++line)
+                angles.push_back(line->direction());
+        }
+    }
+    
+    /*  we also test angles of each open supporting edge
+        (this finds the optimal angle for C-shaped supports) */
+    for (Polylines::const_iterator edge = this->_edges.begin(); edge != this->_edges.end(); ++edge) {
+        if (edge->first_point().coincides_with(edge->last_point())) continue;
+        angles.push_back(Line(edge->first_point(), edge->last_point()).direction());
+    }
+    
+    // remove duplicates
+    double min_resolution = PI/180.0;  // 1 degree
+    std::sort(angles.begin(), angles.end());
+    for (size_t i = 1; i < angles.size(); ++i) {
+        if (Slic3r::Geometry::directions_parallel(angles[i], angles[i-1], min_resolution)) {
+            angles.erase(angles.begin() + i);
+            --i;
+        }
+    }
+    /*  compare first value with last one and remove the greatest one (PI) 
+        in case they are parallel (PI, 0) */
+    if (Slic3r::Geometry::directions_parallel(angles.front(), angles.back(), min_resolution))
+        angles.pop_back();
+    
+    BridgeDirectionComparator bdcomp(this->extrusion_width);
+    double line_increment = this->extrusion_width;
+    bool have_coverage = false;
+    for (std::vector<double>::const_iterator angle = angles.begin(); angle != angles.end(); ++angle) {
+        Polygons my_clip_area = clip_area;
+        ExPolygons my_anchors = this->_anchors;
+        
+        // rotate everything - the center point doesn't matter
+        for (Polygons::iterator it = my_clip_area.begin(); it != my_clip_area.end(); ++it)
+            it->rotate(-*angle, Point(0,0));
+        for (ExPolygons::iterator it = my_anchors.begin(); it != my_anchors.end(); ++it)
+            it->rotate(-*angle, Point(0,0));
+    
+        // generate lines in this direction
+        BoundingBox bb;
+        for (ExPolygons::const_iterator it = my_anchors.begin(); it != my_anchors.end(); ++it)
+            bb.merge((Points)*it);
+        
+        Lines lines;
+        for (coord_t y = bb.min.y; y <= bb.max.y; y += line_increment)
+            lines.push_back(Line(Point(bb.min.x, y), Point(bb.max.x, y)));
+        
+        Lines clipped_lines;
+        intersection(lines, my_clip_area, clipped_lines);
+        
+        // remove any line not having both endpoints within anchors
+        for (size_t i = 0; i < clipped_lines.size(); ++i) {
+            Line &line = clipped_lines[i];
+            if (!Slic3r::Geometry::contains_point(my_anchors, line.a)
+                || !Slic3r::Geometry::contains_point(my_anchors, line.b)) {
+                clipped_lines.erase(clipped_lines.begin() + i);
+                --i;
+            }
+        }
+        
+        std::vector<double> lengths;
+        double total_length = 0;
+        for (Lines::const_iterator line = clipped_lines.begin(); line != clipped_lines.end(); ++line) {
+            double len = line->length();
+            lengths.push_back(len);
+            total_length += len;
+        }
+        if (total_length) have_coverage = true;
+        
+        // sum length of bridged lines
+        bdcomp.dir_coverage[*angle] = total_length;
+        
+        /*  The following produces more correct results in some cases and more broken in others.
+            TODO: investigate, as it looks more reliable than line clipping. */
+        // $directions_coverage{$angle} = sum(map $_->area, @{$self->coverage($angle)}) // 0;
+        
+        // max length of bridged lines
+        bdcomp.dir_avg_length[*angle] = !lengths.empty()
+            ? *std::max_element(lengths.begin(), lengths.end())
+            : 0;
+    }
+    
+    // if no direction produced coverage, then there's no bridge direction
+    if (!have_coverage) return false;
+    
+    // sort directions by score
+    std::sort(angles.begin(), angles.end(), bdcomp);
+    
+    this->angle = angles.front();
+    if (this->angle >= PI) this->angle -= PI;
+    
+    #ifdef SLIC3R_DEBUG
+    printf("  Optimal infill angle is %d degrees\n", (int)Slic3r::Geometry::rad2deg(this->angle));
+    #endif
+    
+    return true;
+}
+
+void
+BridgeDetector::coverage(Polygons* coverage) const
+{
+    if (this->angle == -1) return;
+    return this->coverage(angle, coverage);
+}
+
+void
+BridgeDetector::coverage(double angle, Polygons* coverage) const
+{
+    // Clone our expolygon and rotate it so that we work with vertical lines.
+    ExPolygon expolygon = this->expolygon;
+    expolygon.rotate(PI/2.0 - angle, Point(0,0));
+    
+    /*  Outset the bridge expolygon by half the amount we used for detecting anchors;
+        we'll use this one to generate our trapezoids and be sure that their vertices
+        are inside the anchors and not on their contours leading to false negatives. */
+    ExPolygons grown;
+    offset_ex(expolygon, grown, this->extrusion_width/2.0);
+    
+    // Compute trapezoids according to a vertical orientation
+    Polygons trapezoids;
+    for (ExPolygons::const_iterator it = grown.begin(); it != grown.end(); ++it)
+        it->get_trapezoids2(&trapezoids, PI/2.0);
+    
+    // get anchors, convert them to Polygons and rotate them too
+    Polygons anchors;
+    for (ExPolygons::const_iterator anchor = this->_anchors.begin(); anchor != this->_anchors.end(); ++anchor) {
+        Polygons pp = *anchor;
+        for (Polygons::iterator p = pp.begin(); p != pp.end(); ++p)
+            p->rotate(PI/2.0 - angle, Point(0,0));
+        anchors.insert(anchors.end(), pp.begin(), pp.end());
+    }
+    
+    Polygons covered;
+    for (Polygons::const_iterator trapezoid = trapezoids.begin(); trapezoid != trapezoids.end(); ++trapezoid) {
+        Lines lines = trapezoid->lines();
+        Lines supported;
+        intersection(lines, anchors, supported);
+        
+        // not nice, we need a more robust non-numeric check
+        for (size_t i = 0; i < supported.size(); ++i) {
+            if (supported[i].length() < this->extrusion_width) {
+                supported.erase(supported.begin() + i);
+                i--;
+            }
+        }
+
+        if (supported.size() >= 2) covered.push_back(*trapezoid);        
+    }
+    
+    // merge trapezoids and rotate them back
+    Polygons _coverage;
+    union_(covered, _coverage);
+    for (Polygons::iterator p = _coverage.begin(); p != _coverage.end(); ++p)
+        p->rotate(-(PI/2.0 - angle), Point(0,0));
+    
+    // intersect trapezoids with actual bridge area to remove extra margins
+    // and append it to result
+    intersection(_coverage, this->expolygon, *coverage);
+    
+    /*
+    if (0) {
+        my @lines = map @{$_->lines}, @$trapezoids;
+        $_->rotate(-(PI/2 - $angle), [0,0]) for @lines;
+        
+        require "Slic3r/SVG.pm";
+        Slic3r::SVG::output(
+            "coverage_" . rad2deg($angle) . ".svg",
+            expolygons          => [$self->expolygon],
+            green_expolygons    => $self->_anchors,
+            red_expolygons      => $coverage,
+            lines               => \@lines,
+        );
+    }
+    */
+}
+
+/*  This method returns the bridge edges (as polylines) that are not supported
+    but would allow the entire bridge area to be bridged with detected angle
+    if supported too */
+void
+BridgeDetector::unsupported_edges(Polylines* unsupported) const
+{
+    if (this->angle == -1) return;
+    return this->unsupported_edges(this->angle, unsupported);
+}
+
+void
+BridgeDetector::unsupported_edges(double angle, Polylines* unsupported) const
+{
+    // get bridge edges (both contour and holes)
+    Polylines bridge_edges;
+    {
+        Polygons pp = this->expolygon;
+        bridge_edges.insert(bridge_edges.end(), pp.begin(), pp.end());  // this uses split_at_first_point()
+    }
+    
+    // get unsupported edges
+    Polygons grown_lower;
+    offset(this->lower_slices, grown_lower, +this->extrusion_width);
+    Polylines _unsupported;
+    diff(bridge_edges, grown_lower, _unsupported);
+    
+    /*  Split into individual segments and filter out edges parallel to the bridging angle
+        TODO: angle tolerance should probably be based on segment length and flow width,
+        so that we build supports whenever there's a chance that at least one or two bridge
+        extrusions would be anchored within such length (i.e. a slightly non-parallel bridging
+        direction might still benefit from anchors if long enough) */
+    double angle_tolerance = PI / 180.0 * 5.0;
+    for (Polylines::const_iterator polyline = _unsupported.begin(); polyline != _unsupported.end(); ++polyline) {
+        Lines lines = polyline->lines();
+        for (Lines::const_iterator line = lines.begin(); line != lines.end(); ++line) {
+            if (!Slic3r::Geometry::directions_parallel(line->direction(), angle))
+                unsupported->push_back(*line);
+        }
+    }
+    
+    /*
+    if (0) {
+        require "Slic3r/SVG.pm";
+        Slic3r::SVG::output(
+            "unsupported_" . rad2deg($angle) . ".svg",
+            expolygons          => [$self->expolygon],
+            green_expolygons    => $self->_anchors,
+            red_expolygons      => union_ex($grown_lower),
+            no_arrows           => 1,
+            polylines           => \@bridge_edges,
+            red_polylines       => $unsupported,
+        );
+    }
+    */
+}
+
+#ifdef SLIC3RXS
+REGISTER_CLASS(BridgeDetector, "BridgeDetector");
+#endif
+
+}

Некоторые файлы не были показаны из-за большого количества измененных файлов