diff --git a/lib/Slic3r/Layer/BridgeDetector.pm b/lib/Slic3r/Layer/BridgeDetector.pm index 48fe03be3..b76202b88 100644 --- a/lib/Slic3r/Layer/BridgeDetector.pm +++ b/lib/Slic3r/Layer/BridgeDetector.pm @@ -5,9 +5,9 @@ use List::Util qw(first sum); use Slic3r::Geometry qw(PI scaled_epsilon rad2deg epsilon); use Slic3r::Geometry::Clipper qw(intersection_pl intersection_ex); -has 'lower_slices' => (is => 'ro', required => 1); # ExPolygons or ExPolygonCollection -has 'perimeter_flow' => (is => 'ro', required => 1); -has 'infill_flow' => (is => 'ro', required => 1); +has 'lower_slices' => (is => 'rw', required => 1); # ExPolygons or ExPolygonCollection +has 'perimeter_flow' => (is => 'rw', required => 1); +has 'infill_flow' => (is => 'rw', required => 1); sub detect_angle { my ($self, $expolygon) = @_; @@ -30,7 +30,7 @@ sub detect_angle { if (0) { require "Slic3r/SVG.pm"; - Slic3r::SVG::output("bridge_$expolygon.svg", + Slic3r::SVG::output("bridge.svg", expolygons => [ $expolygon ], red_expolygons => [ @lower ], polylines => [ @edges ], @@ -42,7 +42,7 @@ sub detect_angle { my @midpoints = map $_->midpoint, @chords; my $line_between_midpoints = Slic3r::Line->new(@midpoints); $bridge_angle = $line_between_midpoints->direction; - } elsif (@edges == 1 && $edges[0][0]->coincides_with($edges[0][-1])) { + } elsif (@edges == 1 && !$edges[0][0]->coincides_with($edges[0][-1])) { # Don't use this logic if $edges[0] is actually a closed loop # TODO: this case includes both U-shaped bridges and plain overhangs; # we need a trapezoidation algorithm to detect the actual bridged area diff --git a/lib/Slic3r/Polygon.pm b/lib/Slic3r/Polygon.pm index c43356ae0..57387d93e 100644 --- a/lib/Slic3r/Polygon.pm +++ b/lib/Slic3r/Polygon.pm @@ -61,18 +61,23 @@ sub clip_as_polyline { $self_pl->[0]->translate(1, 0); my @polylines = @{intersection_pl([$self_pl], $polygons)}; - if (@polylines == 2) { + if (@polylines == 1) { + if ($polylines[0][0]->coincides_with($self_pl->[0])) { + # compensate the above workaround for Clipper bug + $polylines[0][0]->translate(-1, 0); + } + } elsif (@polylines == 2) { # If the split_at_first_point() call above happens to split the polygon inside the clipping area # we would get two consecutive polylines instead of a single one, so we use this ugly hack to # recombine them back into a single one in order to trigger the @edges == 2 logic below. # This needs to be replaced with something way better. - if ($polylines[0][-1]->coincides_width($self_pl->[-1]) && $polylines[-1][0]->coincides_width($self_pl->[0])) { + if ($polylines[0][-1]->coincides_with($self_pl->[-1]) && $polylines[-1][0]->coincides_width($self_pl->[0])) { my $p = $polylines[0]->clone; $p->pop_back; $p->append(@{$polylines[-1]}); return [$p]; } - if ($polylines[0][0]->coincides_width($self_pl->[0]) && $polylines[-1][-1]->coincides_width($self_pl->[-1])) { + if ($polylines[0][0]->coincides_with($self_pl->[0]) && $polylines[-1][-1]->coincides_width($self_pl->[-1])) { my $p = $polylines[-1]->clone; $p->pop_back; $p->append(@{$polylines[0]}); diff --git a/t/bridges.t b/t/bridges.t index 20a8f081c..efe46a757 100644 --- a/t/bridges.t +++ b/t/bridges.t @@ -1,4 +1,4 @@ -use Test::More tests => 2; +use Test::More tests => 4; use strict; use warnings; @@ -9,10 +9,15 @@ BEGIN { use List::Util qw(first); use Slic3r; -use Slic3r::Geometry qw(scale epsilon rad2deg); +use Slic3r::Geometry qw(scale epsilon rad2deg PI); use Slic3r::Test; my $flow = Slic3r::Flow->new(width => 0.5, spacing => 0.45, nozzle_diameter => 0.5); +my $bd = Slic3r::Layer::BridgeDetector->new( + lower_slices => [], + perimeter_flow => $flow, + infill_flow => $flow, +); { my $test = sub { @@ -27,17 +32,54 @@ my $flow = Slic3r::Flow->new(width => 0.5, spacing => 0.45, nozzle_diameter => 0 my $bridge = $lower->[1]->clone; $bridge->reverse; $bridge = Slic3r::ExPolygon->new($bridge); - my $bd = Slic3r::Layer::BridgeDetector->new( - lower_slices => [$lower], - perimeter_flow => $flow, - infill_flow => $flow, - ); + $bd->lower_slices([$lower]); - ok abs(rad2deg($bd->detect_angle($bridge)) - $expected_angle) < epsilon, 'correct bridge angle detected'; + ok check_angle($bd->detect_angle($bridge), $expected_angle), 'correct bridge angle for O-shaped overhang'; }; $test->([20,10], 90); $test->([10,20], 0); } +{ + my $bridge = Slic3r::ExPolygon->new( + Slic3r::Polygon->new_scale([0,0], [20,0], [20,10], [0,10]), + ); + my $lower = [ + Slic3r::ExPolygon->new( + Slic3r::Polygon->new_scale([-2,0], [0,0], [0,10], [-2,10]), + ), + ]; + $_->translate(scale 20, scale 20) for $bridge, @$lower; # avoid negative coordinates for easier SVG preview + + $lower->[1] = $lower->[0]->clone; + $lower->[1]->translate(scale 22, 0); + + $bd->lower_slices($lower); + ok check_angle($bd->detect_angle($bridge), 0), 'correct bridge angle for two-sided bridge'; +} + +{ + my $bridge = Slic3r::ExPolygon->new( + Slic3r::Polygon->new_scale([0,0], [20,0], [10,10], [0,10]), + ); + my $lower = [ + Slic3r::ExPolygon->new( + Slic3r::Polygon->new_scale([0,0], [0,10], [10,10], [10,12], [-2,12], [-2,-2], [22,-2], [22,0]), + ), + ]; + $_->translate(scale 20, scale 20) for $bridge, @$lower; # avoid negative coordinates for easier SVG preview + + $bd->lower_slices($lower); + ok check_angle($bd->detect_angle($bridge), 135), 'correct bridge angle for C-shaped overhang'; +} + +sub check_angle { + my ($result, $expected) = @_; + + # our epsilon is equal to the steps used by the bridge detection algorithm + ###use XXX; YYY [ rad2deg($result), $expected ]; + return defined $result && abs(rad2deg($result) - $expected) < rad2deg(PI/36); +} + __END__