Ported "avoid crossing perimeters" and bridging unit tests from Perl

to C++.
Further reduced Perl bindings.
Got rid of the ExPolygonCollection wrapper, replaced with ExPolygons.
This commit is contained in:
Vojtech Bubnik 2022-05-04 18:21:08 +02:00
parent a627614b58
commit 576c167bd5
42 changed files with 204 additions and 1006 deletions

View file

@ -1,93 +0,0 @@
use Test::More;
use strict;
use warnings;
plan tests => 34;
BEGIN {
use FindBin;
use lib "$FindBin::Bin/../lib";
use local::lib "$FindBin::Bin/../local-lib";
}
use Slic3r;
use Slic3r::Geometry qw(rad2deg_dir angle3points PI);
#==========================================================
{
is line_atan([ [0, 0], [10, 0] ]), (0), 'E atan2';
is line_atan([ [10, 0], [0, 0] ]), (PI), 'W atan2';
is line_atan([ [0, 0], [0, 10] ]), (PI/2), 'N atan2';
is line_atan([ [0, 10], [0, 0] ]), -(PI/2), 'S atan2';
is line_atan([ [10, 10], [0, 0] ]), -(PI*3/4), 'SW atan2';
is line_atan([ [0, 0], [10, 10] ]), (PI*1/4), 'NE atan2';
is line_atan([ [0, 10], [10, 0] ]), -(PI*1/4), 'SE atan2';
is line_atan([ [10, 0], [0, 10] ]), (PI*3/4), 'NW atan2';
}
#==========================================================
{
is line_orientation([ [0, 0], [10, 0] ]), (0), 'E orientation';
is line_orientation([ [0, 0], [0, 10] ]), (PI/2), 'N orientation';
is line_orientation([ [10, 0], [0, 0] ]), (PI), 'W orientation';
is line_orientation([ [0, 10], [0, 0] ]), (PI*3/2), 'S orientation';
is line_orientation([ [0, 0], [10, 10] ]), (PI*1/4), 'NE orientation';
is line_orientation([ [10, 0], [0, 10] ]), (PI*3/4), 'NW orientation';
is line_orientation([ [10, 10], [0, 0] ]), (PI*5/4), 'SW orientation';
is line_orientation([ [0, 10], [10, 0] ]), (PI*7/4), 'SE orientation';
}
#==========================================================
{
is line_direction([ [0, 0], [10, 0] ]), (0), 'E direction';
is line_direction([ [10, 0], [0, 0] ]), (0), 'W direction';
is line_direction([ [0, 0], [0, 10] ]), (PI/2), 'N direction';
is line_direction([ [0, 10], [0, 0] ]), (PI/2), 'S direction';
is line_direction([ [10, 10], [0, 0] ]), (PI*1/4), 'SW direction';
is line_direction([ [0, 0], [10, 10] ]), (PI*1/4), 'NE direction';
is line_direction([ [0, 10], [10, 0] ]), (PI*3/4), 'SE direction';
is line_direction([ [10, 0], [0, 10] ]), (PI*3/4), 'NW direction';
}
#==========================================================
{
is rad2deg_dir(0), 90, 'E (degrees)';
is rad2deg_dir(PI), 270, 'W (degrees)';
is rad2deg_dir(PI/2), 0, 'N (degrees)';
is rad2deg_dir(-(PI/2)), 180, 'S (degrees)';
is rad2deg_dir(PI*1/4), 45, 'NE (degrees)';
is rad2deg_dir(PI*3/4), 135, 'NW (degrees)';
is rad2deg_dir(PI/6), 60, '30°';
is rad2deg_dir(PI/6*2), 30, '60°';
}
#==========================================================
{
is angle3points([0,0], [10,0], [0,10]), PI/2, 'CW angle3points';
is angle3points([0,0], [0,10], [10,0]), PI/2*3, 'CCW angle3points';
}
#==========================================================
sub line_atan {
my ($l) = @_;
return Slic3r::Line->new(@$l)->atan2_;
}
sub line_orientation {
my ($l) = @_;
return Slic3r::Line->new(@$l)->orientation;
}
sub line_direction {
my ($l) = @_;
return Slic3r::Line->new(@$l)->direction;
}

View file

@ -1,22 +0,0 @@
use Test::More tests => 1;
use strict;
use warnings;
BEGIN {
use FindBin;
use lib "$FindBin::Bin/../lib";
use local::lib "$FindBin::Bin/../local-lib";
}
use List::Util qw(first sum);
use Slic3r;
use Slic3r::Test;
{
my $config = Slic3r::Config::new_from_defaults;
$config->set('avoid_crossing_perimeters', 2);
my $print = Slic3r::Test::init_print('20mm_cube', config => $config, duplicate => 2);
ok my $gcode = Slic3r::Test::gcode($print), "no crash with avoid_crossing_perimeters and multiple objects";
}
__END__

View file

@ -1,137 +0,0 @@
use Test::More tests => 16;
use strict;
use warnings;
BEGIN {
use FindBin;
use lib "$FindBin::Bin/../lib";
use local::lib "$FindBin::Bin/../local-lib";
}
use List::Util qw(first sum);
use Slic3r;
use Slic3r::Geometry qw(scale epsilon deg2rad rad2deg);
use Slic3r::Test;
{
my $test = sub {
my ($bridge_size, $rotate, $expected_angle, $tolerance) = @_;
my ($x, $y) = @$bridge_size;
my $lower = Slic3r::ExPolygon->new(
Slic3r::Polygon->new_scale([-2,-2], [$x+2,-2], [$x+2,$y+2], [-2,$y+2]),
Slic3r::Polygon->new_scale([0,0], [0,$y], [$x,$y], [$x,0]),
);
$lower->translate(scale 20, scale 20); # avoid negative coordinates for easier SVG preview
$lower->rotate(deg2rad($rotate), [$x/2,$y/2]);
my $bridge = $lower->[1]->clone;
$bridge->reverse;
$bridge = Slic3r::ExPolygon->new($bridge);
ok check_angle([$lower], $bridge, $expected_angle, $tolerance), 'correct bridge angle for O-shaped overhang';
};
$test->([20,10], 0, 90);
$test->([10,20], 0, 0);
$test->([20,10], 45, 135, 20);
$test->([20,10], 135, 45, 20);
}
{
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);
ok check_angle($lower, $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
ok check_angle($lower, $bridge, 135), 'correct bridge angle for C-shaped overhang';
}
{
my $bridge = Slic3r::ExPolygon->new(
Slic3r::Polygon->new_scale([10,10],[20,10],[20,20], [10,20]),
);
my $lower = [
Slic3r::ExPolygon->new(
Slic3r::Polygon->new_scale([10,10],[10,20],[20,20],[30,30],[0,30],[0,0]),
),
];
$_->translate(scale 20, scale 20) for $bridge, @$lower; # avoid negative coordinates for easier SVG preview
ok check_angle($lower, $bridge, 45, undef, $bridge->area/2), 'correct bridge angle for square overhang with L-shaped anchors';
}
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::BridgeDetector->new($bridge, $lower, scale 0.5);
$tolerance //= rad2deg($bd->resolution) + epsilon;
$bd->detect_angle;
my $result = $bd->angle;
my $coverage = $bd->coverage;
is sum(map $_->area, @$coverage), $expected_coverage, 'correct coverage area';
# our epsilon is equal to the steps used by the bridge detection algorithm
###use XXX; YYY [ rad2deg($result), $expected ];
# returned value must be non-negative, check for that too
my $delta=rad2deg($result) - $expected;
$delta-=180 if $delta>=180 - epsilon;
return defined $result && $result>=0 && abs($delta) < $tolerance;
}
{
my $config = Slic3r::Config::new_from_defaults;
$config->set('top_solid_layers', 0); # to prevent bridging on sparse infill
$config->set('bridge_speed', 99);
my $print = Slic3r::Test::init_print('bridge', config => $config);
my %extrusions = (); # angle => length
Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub {
my ($self, $cmd, $args, $info) = @_;
if ($cmd eq 'G1' && ($args->{F} // $self->F)/60 == $config->bridge_speed) {
my $line = Slic3r::Line->new_scale(
[ $self->X, $self->Y ],
[ $info->{new_X}, $info->{new_Y} ],
);
my $angle = $line->direction;
$extrusions{$angle} //= 0;
$extrusions{$angle} += $line->length;
}
});
ok !!%extrusions, "bridge is generated";
my ($main_angle) = sort { $extrusions{$b} <=> $extrusions{$a} } keys %extrusions;
is $main_angle, 0, "bridge has the expected direction";
}
__END__

View file

@ -1,87 +0,0 @@
use Test::More;
use strict;
use warnings;
plan tests => 11;
BEGIN {
use FindBin;
use lib "$FindBin::Bin/../lib";
use local::lib "$FindBin::Bin/../local-lib";
}
use Slic3r;
use Slic3r::Geometry qw(collinear);
#==========================================================
{
my @lines = (
Slic3r::Line->new([0,4], [4,2]),
Slic3r::Line->new([2,3], [8,0]),
Slic3r::Line->new([6,1], [8,0]),
);
is collinear($lines[0], $lines[1]), 1, 'collinear';
is collinear($lines[1], $lines[2]), 1, 'collinear';
is collinear($lines[0], $lines[2]), 1, 'collinear';
}
#==========================================================
{
# horizontal
my @lines = (
Slic3r::Line->new([0,1], [5,1]),
Slic3r::Line->new([2,1], [8,1]),
);
is collinear($lines[0], $lines[1]), 1, 'collinear';
}
#==========================================================
{
# vertical
my @lines = (
Slic3r::Line->new([1,0], [1,5]),
Slic3r::Line->new([1,2], [1,8]),
);
is collinear($lines[0], $lines[1]), 1, 'collinear';
}
#==========================================================
{
# non overlapping
my @lines = (
Slic3r::Line->new([0,1], [5,1]),
Slic3r::Line->new([7,1], [10,1]),
);
is collinear($lines[0], $lines[1], 1), 0, 'non overlapping';
is collinear($lines[0], $lines[1], 0), 1, 'overlapping';
}
#==========================================================
{
# with one common point
my @lines = (
Slic3r::Line->new([0,4], [4,2]),
Slic3r::Line->new([4,2], [8,0]),
);
is collinear($lines[0], $lines[1], 1), 1, 'one common point';
is collinear($lines[0], $lines[1], 0), 1, 'one common point';
}
#==========================================================
{
# not collinear
my @lines = (
Slic3r::Line->new([290000000,690525600], [285163380,684761540]),
Slic3r::Line->new([285163380,684761540], [193267599,575244400]),
);
is collinear($lines[0], $lines[1], 0), 0, 'not collinear';
is collinear($lines[0], $lines[1], 1), 0, 'not collinear';
}
#==========================================================

View file

@ -2,7 +2,7 @@ use Test::More;
use strict;
use warnings;
plan tests => 26;
plan tests => 11;
BEGIN {
use FindBin;
@ -11,7 +11,7 @@ BEGIN {
}
use Slic3r;
use Slic3r::Geometry qw(PI polygon_is_convex
use Slic3r::Geometry qw(PI
chained_path_from epsilon scale);
{
@ -27,18 +27,6 @@ use Slic3r::Geometry qw(PI polygon_is_convex
#==========================================================
my $line1 = [ [5, 15], [30, 15] ];
my $line2 = [ [10, 20], [10, 10] ];
is_deeply Slic3r::Geometry::line_intersection($line1, $line2, 1)->arrayref, [10, 15], 'line_intersection';
#==========================================================
$line1 = [ [73.6310778185108/0.0000001, 371.74239268924/0.0000001], [73.6310778185108/0.0000001, 501.74239268924/0.0000001] ];
$line2 = [ [75/0.0000001, 437.9853/0.0000001], [62.7484/0.0000001, 440.4223/0.0000001] ];
isnt Slic3r::Geometry::line_intersection($line1, $line2, 1), undef, 'line_intersection';
#==========================================================
my $polygons = [
Slic3r::Polygon->new( # contour, ccw
[45919000, 515273900], [14726100, 461246400], [14726100, 348753500], [33988700, 315389800],
@ -57,54 +45,6 @@ my $polygons = [
),
];
#==========================================================
{
my $p1 = [10, 10];
my $p2 = [10, 20];
my $p3 = [10, 30];
my $p4 = [20, 20];
my $p5 = [0, 20];
is Slic3r::Geometry::angle3points($p2, $p3, $p1), PI(), 'angle3points';
is Slic3r::Geometry::angle3points($p2, $p1, $p3), PI(), 'angle3points';
is Slic3r::Geometry::angle3points($p2, $p3, $p4), PI()/2*3, 'angle3points';
is Slic3r::Geometry::angle3points($p2, $p4, $p3), PI()/2, 'angle3points';
is Slic3r::Geometry::angle3points($p2, $p1, $p4), PI()/2, 'angle3points';
is Slic3r::Geometry::angle3points($p2, $p1, $p5), PI()/2*3, 'angle3points';
}
{
my $p1 = [30, 30];
my $p2 = [20, 20];
my $p3 = [10, 10];
my $p4 = [30, 10];
is Slic3r::Geometry::angle3points($p2, $p1, $p3), PI(), 'angle3points';
is Slic3r::Geometry::angle3points($p2, $p1, $p4), PI()/2*3, 'angle3points';
is Slic3r::Geometry::angle3points($p2, $p1, $p1), 2*PI(), 'angle3points';
}
#==========================================================
{
my $cw_square = [ [0,0], [0,10], [10,10], [10,0] ];
is polygon_is_convex($cw_square), 0, 'cw square is not convex';
is polygon_is_convex([ reverse @$cw_square ]), 1, 'ccw square is convex';
my $convex1 = [ [0,0], [10,0], [10,10], [0,10], [0,6], [4,6], [4,4], [0,4] ];
is polygon_is_convex($convex1), 0, 'concave polygon';
}
#==========================================================
{
my $polyline = Slic3r::Polyline->new([0, 0], [10, 0], [20, 0]);
is_deeply [ map $_->pp, @{$polyline->lines} ], [
[ [0, 0], [10, 0] ],
[ [10, 0], [20, 0] ],
], 'polyline_lines';
}
#==========================================================