New fill types (hilbertcurve, archimedeanchords, octagramspiral) and ability to use different patterns for solid layers. #20

This commit is contained in:
Alessandro Ranellucci 2011-11-13 18:14:02 +01:00
parent 041e9877a3
commit 038caddcda
22 changed files with 391 additions and 93 deletions

View File

@ -11,6 +11,7 @@ my $build = Module::Build->new(
'Getopt::Long' => '0',
'Math::Clipper' => '1.02',
'Math::ConvexHull' => '1.0.4',
'Math::PlanePath' => '53',
'Moo' => '0',
'Time::HiRes' => '0',
'XXX' => '0',

View File

@ -94,9 +94,11 @@ The author is Alessandro Ranellucci (me).
Use relative distances for extrusion in GCODE output
--z-offset Additional height in mm to add to vertical coordinates
(+/-, default: 0)
--gcode-arcs Use G2/G3 commands for native arcs (experimental, not supported
by all firmwares)
Filament options:
--filament-diameter Diameter of your raw filament (default: 3)
--filament-diameter Diameter in mm of your raw filament (default: 3)
--filament-packing-density
Ratio of the extruded volume over volume pushed
into the extruder (default: 1)
@ -123,6 +125,8 @@ The author is Alessandro Ranellucci (me).
(range: 1+, default: 3)
--fill-density Infill density (range: 0-1, default: 0.4)
--fill-angle Infill angle in degrees (range: 0-90, default: 0)
--fill-pattern Pattern to use to fill non-solid layers (default: rectilinear)
--solid-fill-pattern Pattern to use to fill solid layers (default: rectilinear)
--start-gcode Load initial gcode from the supplied file. This will overwrite
the default command (home all axes [G28]).
--end-gcode Load final gcode from the supplied file. This will overwrite
@ -138,6 +142,7 @@ The author is Alessandro Ranellucci (me).
compensating retraction (default: 0)
--retract-before-travel
Only retract before travel moves of this length (default: 2)
--retract-lift Lift Z by the given distance in mm when retracting (default: 0)
Skirt options:
--skirts Number of skirts to draw (default: 1)
@ -152,3 +157,5 @@ The author is Alessandro Ranellucci (me).
--duplicate-distance Distance in mm between copies (default: 6)

View File

@ -60,7 +60,8 @@ our $flow_width;
our $perimeter_offsets = 3;
our $solid_layers = 3;
our $bridge_overlap = 3; # mm
our $fill_type = 'rectilinear';
our $fill_pattern = 'rectilinear';
our $solid_fill_pattern = 'rectilinear';
our $fill_density = 0.4; # 1 = 100%
our $fill_angle = 0;
our $start_gcode = "G28 ; home all axes";

View File

@ -78,6 +78,16 @@ our $Options = {
label => 'Solid layers',
type => 'i',
},
'fill_pattern' => {
label => 'Fill pattern',
type => 'select',
values => [qw(rectilinear hilbertcurve archimedeanchords octagramspiral)],
},
'solid_fill_pattern' => {
label => 'Solid fill pattern',
type => 'select',
values => [qw(rectilinear hilbertcurve archimedeanchords octagramspiral)],
},
'fill_density' => {
label => 'Fill density',
type => 'f',
@ -266,9 +276,13 @@ sub validate {
$Slic3r::print_center = [ split /,/, $Slic3r::print_center ]
if !ref $Slic3r::print_center;
# --fill-type
die "Invalid value for --fill-type\n"
if !exists $Slic3r::Fill::FillTypes{$Slic3r::fill_type};
# --fill-pattern
die "Invalid value for --fill-pattern\n"
if !exists $Slic3r::Fill::FillTypes{$Slic3r::fill_pattern};
# --solid-fill-pattern
die "Invalid value for --solid-fill-pattern\n"
if !exists $Slic3r::Fill::FillTypes{$Slic3r::solid_fill_pattern};
# --fill-density
die "Invalid value for --fill-density\n"

View File

@ -5,6 +5,7 @@ use warnings;
# an ExPolygon is a polygon with holes
use Math::Clipper qw(CT_UNION PFT_NONZERO JT_MITER);
use Slic3r::Geometry qw(point_in_polygon X Y A B);
use Slic3r::Geometry::Clipper qw(union_ex);
# the constructor accepts an array of polygons
@ -24,14 +25,6 @@ sub new {
$self;
}
# this class method accepts an array of polygons and returns
# an array of expolygons with the right holes applied to the
# right contours
sub make {
my $class = shift;
return @{ union_ex(\@_) };
}
sub contour {
my $self = shift;
return $self->[0];
@ -60,7 +53,73 @@ sub offset {
my $offsets = Math::Clipper::offset($self, $distance, $scale, $joinType, $miterLimit);
# apply holes to the right contours
return (ref $self)->make(@$offsets);
return @{ union_ex($offsets) };
}
sub encloses_point {
my $self = shift;
my ($point) = @_;
return $self->contour->encloses_point($point)
&& (!grep($_->encloses_point($point), $self->holes)
|| grep($_->point_on_segment($point), $self->holes));
}
sub point_on_segment {
my $self = shift;
my ($point) = @_;
for (@$self) {
my $line = $_->point_on_segment($point);
return $line if $line;
}
return undef;
}
sub bounding_box {
my $self = shift;
return Slic3r::Geometry::bounding_box($self->contour);
}
sub clip_line {
my $self = shift;
my ($line) = @_;
$line = Slic3r::Line->cast($line);
my @intersections = grep $_, map $_->intersection($line, 1), map $_->lines, @$self;
my @dir = (
$line->[B][X] <=> $line->[A][X],
$line->[B][Y] <=> $line->[A][Y],
);
@intersections = sort {
(($a->[X] <=> $b->[X]) == $dir[X]) && (($a->[Y] <=> $b->[Y]) == $dir[Y]) ? 1 : -1
} @intersections, @$line;
shift @intersections if $intersections[0]->coincides_with($intersections[1]);
pop @intersections if $intersections[-1]->coincides_with($intersections[-2]);
shift @intersections
if !$self->encloses_point($intersections[0])
&& !$self->point_on_segment($intersections[0]);
my @lines = ();
while (@intersections) {
# skip tangent points
my @points = splice @intersections, 0, 2;
next if !$points[1];
next if $points[0]->coincides_with($points[1]);
push @lines, [ @points ];
}
return [@lines];
}
sub translate {
my $self = shift;
$_->translate(@_) for @$self;
}
sub rotate {
my $self = shift;
$_->rotate(@_) for @$self;
}
1;

View File

@ -1,7 +1,11 @@
package Slic3r::Fill;
use Moo;
use Slic3r::Fill::ArchimedeanChords;
use Slic3r::Fill::Base;
use Slic3r::Fill::Flowsnake;
use Slic3r::Fill::HilbertCurve;
use Slic3r::Fill::OctagramSpiral;
use Slic3r::Fill::Rectilinear;
use Slic3r::Fill::Rectilinear2;
@ -11,14 +15,18 @@ has 'print' => (is => 'ro', required => 1);
has 'fillers' => (is => 'rw', default => sub { {} });
our %FillTypes = (
rectilinear => 'Slic3r::Fill::Rectilinear',
rectilinear2 => 'Slic3r::Fill::Rectilinear2',
archimedeanchords => 'Slic3r::Fill::ArchimedeanChords',
rectilinear => 'Slic3r::Fill::Rectilinear',
rectilinear2 => 'Slic3r::Fill::Rectilinear2',
flowsnake => 'Slic3r::Fill::Flowsnake',
octagramspiral => 'Slic3r::Fill::OctagramSpiral',
hilbertcurve => 'Slic3r::Fill::HilbertCurve',
);
sub BUILD {
my $self = shift;
$self->fillers->{$_} ||= $FillTypes{$_}->new(print => $self->print)
for ('rectilinear', $Slic3r::fill_type);
for ('rectilinear', $Slic3r::fill_pattern, $Slic3r::solid_fill_pattern);
}
sub make_fill {
@ -38,13 +46,15 @@ sub make_fill {
SURFACE: foreach my $surface (@$surfaces) {
Slic3r::debugf " Processing surface %s:\n", $surface->id;
my $filler = $Slic3r::fill_type;
my $filler = $Slic3r::fill_pattern;
my $density = $Slic3r::fill_density;
# force 100% density and rectilinear fill for external surfaces
if ($surface->surface_type ne 'internal') {
$density = 1;
$filler = 'rectilinear';
$filler = $surface->isa('Slic3r::Surface::Bridge')
? 'rectilinear'
: $Slic3r::solid_fill_pattern;
} else {
next SURFACE unless $density > 0;
}

View File

@ -0,0 +1,7 @@
package Slic3r::Fill::ArchimedeanChords;
use Moo;
extends 'Slic3r::Fill::PlanePath';
use Math::PlanePath::ArchimedeanChords;
1;

View File

@ -35,14 +35,15 @@ sub infill_direction {
sub rotate_points {
my $self = shift;
my ($polygons, $rotate_vector) = @_;
my ($expolygon, $rotate_vector) = @_;
my @rotate = @{$rotate_vector->[0]};
my @shift = @{$rotate_vector->[1]};
# rotate surface as needed
@$polygons = map [ Slic3r::Geometry::move_points(\@shift, @$_) ],
map [ Slic3r::Geometry::rotate_points(@rotate, @$_) ], @$polygons if $rotate[0];
# rotate points as needed
if ($rotate[0]) {
$expolygon->rotate(@rotate);
$expolygon->translate(@shift);
}
}
sub rotate_points_back {

View File

@ -0,0 +1,18 @@
package Slic3r::Fill::Flowsnake;
use Moo;
extends 'Slic3r::Fill::PlanePath';
use Math::PlanePath::Flowsnake;
use Slic3r::Geometry qw(X X1 X2);
# Sorry, this fill is currently broken.
sub process_polyline {
my $self = shift;
my ($polyline, $bounding_box) = @_;
$_->[X] += ($bounding_box->[X1] + $bounding_box->[X2]/2) for @{$polyline->points};
}
1;

View File

@ -0,0 +1,7 @@
package Slic3r::Fill::HilbertCurve;
use Moo;
extends 'Slic3r::Fill::PlanePath';
use Math::PlanePath::HilbertCurve;
1;

View File

@ -0,0 +1,9 @@
package Slic3r::Fill::OctagramSpiral;
use Moo;
extends 'Slic3r::Fill::PlanePath';
use Math::PlanePath::OctagramSpiral;
sub multiplier () { sqrt(2) }
1;

View File

@ -0,0 +1,62 @@
package Slic3r::Fill::PlanePath;
use Moo;
extends 'Slic3r::Fill::Base';
use Slic3r::Geometry qw(bounding_box);
use XXX;
sub multiplier () { 1 }
sub get_n {
my $self = shift;
my ($path, $bounding_box) = @_;
my ($n_lo, $n_hi) = $path->rect_to_n_range(@$bounding_box);
return ($n_lo .. $n_hi);
}
sub process_polyline {}
sub fill_surface {
my $self = shift;
my ($surface, %params) = @_;
# rotate polygons
my $expolygon = $surface->expolygon;
my $rotate_vector = $self->infill_direction($surface);
$self->rotate_points($expolygon, $rotate_vector);
my $distance_between_lines = $Slic3r::flow_width / $Slic3r::resolution / $params{density} * $self->multiplier;
my $bounding_box = [ bounding_box(map @$_, $expolygon) ];
(ref $self) =~ /::([^:]+)$/;
my $path = "Math::PlanePath::$1"->new;
my @n = $self->get_n($path, [map +($_ / $distance_between_lines), @$bounding_box]);
my $polyline = Slic3r::Polyline->cast([
map [ map {$_*$distance_between_lines} $path->n_to_xy($_) ], @n,
]);
return [] if !@{$polyline->points};
$self->process_polyline($polyline, $bounding_box);
my @paths = ($polyline->clip_with_expolygon($expolygon));
if (0) {
require "Slic3r/SVG.pm";
Slic3r::SVG::output(undef, "fill.svg",
polygons => $expolygon,
polylines => [map $_->p, @paths],
);
}
@paths = map $_->p, @paths;
# paths must be rotated back
$self->rotate_points_back(\@paths, $rotate_vector);
return @paths;
}
1;

View File

@ -3,11 +3,7 @@ use Moo;
extends 'Slic3r::Fill::Base';
use constant X1 => 0;
use constant Y1 => 1;
use constant X2 => 2;
use constant Y2 => 3;
use Slic3r::Geometry qw(X1 Y1 X2 Y2);
use XXX;
sub fill_surface {
@ -15,21 +11,18 @@ sub fill_surface {
my ($surface, %params) = @_;
# rotate polygons so that we can work with vertical lines here
my $polygons = [ $surface->p ];
my $expolygon = $surface->expolygon;
my $rotate_vector = $self->infill_direction($surface);
$self->rotate_points($polygons, $rotate_vector);
my $bounding_box = [ Slic3r::Geometry::bounding_box(map @$_, $polygons) ];
my $surface_width = $bounding_box->[X2] - $bounding_box->[X1];
my $surface_height = $bounding_box->[Y2] - $bounding_box->[Y1];
$self->rotate_points($expolygon, $rotate_vector);
my $bounding_box = [ $expolygon->bounding_box ];
my $distance_between_lines = $Slic3r::flow_width / $Slic3r::resolution / $params{density};
my @paths = ();
my $x = $bounding_box->[X1];
while ($x < $bounding_box->[X2]) {
my $vertical_line = [ [$x, $bounding_box->[Y2]], [$x, $bounding_box->[Y1]] ];
push @paths, @{ Slic3r::Geometry::clip_segment_complex_polygon($vertical_line, $polygons) };
push @paths, @{ $expolygon->clip_line($vertical_line) };
$x += int($distance_between_lines);
}

View File

@ -3,15 +3,7 @@ use Moo;
extends 'Slic3r::Fill::Base';
use constant X1 => 0;
use constant Y1 => 1;
use constant X2 => 2;
use constant Y2 => 3;
use constant A => 0;
use constant B => 1;
use constant X => 0;
use constant Y => 1;
use Slic3r::Geometry qw(X1 Y1 X2 Y2 A B X Y);
use XXX;
sub fill_surface {

View File

@ -3,7 +3,7 @@ use strict;
use warnings;
use Wx qw(:sizer);
use Wx::Event qw(EVT_TEXT EVT_CHECKBOX);
use Wx::Event qw(EVT_TEXT EVT_CHECKBOX EVT_CHOICE);
use base 'Wx::StaticBoxSizer';
# not very elegant, but this solution is temporary waiting for a better GUI
@ -55,6 +55,14 @@ sub new {
$x_field->SetValue($value->[0]);
$y_field->SetValue($value->[1]);
};
} elsif ($opt->{type} eq 'select') {
$field = Wx::Choice->new($parent, -1, Wx::wxDefaultPosition, Wx::wxDefaultSize, $opt->{values});
EVT_CHOICE($parent, $field, sub { Slic3r::Config->set($opt_key, $opt->{values}[$field->GetSelection]) });
push @reload_callbacks, sub {
my $value = Slic3r::Config->get($opt_key);
$field->SetSelection(grep $opt->{values}[$_] eq $value, 0..$#{$opt->{values}});
};
$reload_callbacks[-1]->();
} else {
die "Unsupported option type: " . $opt->{type};
}

View File

@ -33,7 +33,7 @@ sub new {
),
print => Slic3r::GUI::OptionsGroup->new($self,
title => 'Print settings',
options => [qw(perimeter_offsets solid_layers fill_density fill_angle)],
options => [qw(perimeter_offsets solid_layers fill_density fill_angle fill_pattern solid_fill_pattern)],
),
retract => Slic3r::GUI::OptionsGroup->new($self,
title => 'Retraction',

View File

@ -5,7 +5,7 @@ use warnings;
require Exporter;
our @ISA = qw(Exporter);
our @EXPORT_OK = qw(
PI X Y Z A B epsilon slope line_atan lines_parallel three_points_aligned
PI X Y Z A B X1 Y1 X2 Y2 epsilon slope line_atan lines_parallel
line_point_belongs_to_segment points_coincide distance_between_points
line_length midpoint point_in_polygon point_in_segment segment_in_segment
point_is_on_left_of_segment polyline_lines polygon_lines nearest_point
@ -14,7 +14,7 @@ our @EXPORT_OK = qw(
rotate_points move_points remove_coinciding_points clip_segment_polygon
sum_vectors multiply_vector subtract_vectors dot perp polygon_points_visibility
line_intersection bounding_box bounding_box_intersect
clip_segment_complex_polygon longest_segment angle3points
longest_segment angle3points three_points_aligned
polyline_remove_parallel_continuous_edges polyline_remove_acute_vertices
polygon_remove_acute_vertices polygon_remove_parallel_continuous_edges
shortest_path collinear
@ -29,6 +29,10 @@ use constant B => 1;
use constant X => 0;
use constant Y => 1;
use constant Z => 2;
use constant X1 => 0;
use constant Y1 => 1;
use constant X2 => 2;
use constant Y2 => 3;
our $parallel_degrees_limit = abs(deg2rad(3));
our $epsilon = 1E-4;
@ -110,6 +114,7 @@ sub midpoint {
return [ ($line->[B][X] + $line->[A][X]) / 2, ($line->[B][Y] + $line->[A][Y]) / 2 ];
}
# this will check whether a point is in a polygon regardless of polygon orientation
sub point_in_polygon {
my ($point, $polygon) = @_;
@ -311,7 +316,7 @@ sub rotate_points {
sub move_points {
my ($shift, @points) = @_;
return map [ $shift->[X] + $_->[X], $shift->[Y] + $_->[Y] ], @points;
return map Slic3r::Point->new($shift->[X] + $_->[X], $shift->[Y] + $_->[Y]), @points;
}
# preserves order
@ -558,32 +563,6 @@ sub bounding_box_intersect {
return 1;
}
sub clip_segment_complex_polygon {
my ($line, $polygons) = @_;
my @intersections = grep $_, map line_intersection($line, $_, 1),
map polygon_lines($_), @$polygons or return ();
# this is not very elegant, however it works
@intersections = sort { sprintf("%020f,%020f", @$a) cmp sprintf("%020f,%020f", @$b) } @intersections;
shift(@intersections) if !grep(point_in_polygon($intersections[0], $_), @$polygons)
&& !grep(polygon_segment_having_point($_, $intersections[0]), @$polygons);
# defensive programming
###die "Invalid intersections" if @intersections % 2 != 0;
my @lines = ();
while (@intersections) {
# skip tangent points
my @points = map shift @intersections, 1..2;
next if !$points[1];
next if points_coincide(@points);
push @lines, [ @points ];
}
return [@lines];
}
sub angle3points {
my ($p1, $p2, $p3) = @_;
# p1 is the center

View File

@ -8,18 +8,26 @@ sub new {
my $class = shift;
my $self;
if (@_ == 2) {
$self = [ map Slic3r::Point->new($_), @_ ];
$self = [ @_ ];
} elsif (ref $_[0] eq 'ARRAY') {
$self = [ map Slic3r::Point->new($_), $_[0][0], $_[0][1] ];
$self = [ $_[0][0], $_[0][1] ];
} elsif ($_[0]->isa(__PACKAGE__)) {
return $_[0];
} else {
die "Invalid argument for $class->new";
}
bless $self, $class;
bless $_, 'Slic3r::Point' for @$self;
return $self;
}
sub cast {
my $class = shift;
my ($line) = @_;
return $line if ref $line eq __PACKAGE__;
return $class->new($line);
}
sub a { $_[0][0] }
sub b { $_[0][1] }

View File

@ -7,7 +7,8 @@ use warnings;
# as a Slic3r::Polyline::Closed you're right. I plan to
# ditch the latter and port everything to this class.
use Slic3r::Geometry qw(polygon_lines polygon_remove_parallel_continuous_edges);
use Slic3r::Geometry qw(polygon_lines polygon_remove_parallel_continuous_edges
polygon_segment_having_point point_in_polygon move_points rotate_points);
# the constructor accepts an array(ref) of points
sub new {
@ -19,8 +20,8 @@ sub new {
$self = [ @_ ];
}
@$self = map Slic3r::Point->cast($_), @$self;
bless $self, $class;
bless $_, 'Slic3r::Point' for @$self;
$self;
}
@ -40,4 +41,28 @@ sub cleanup {
polygon_remove_parallel_continuous_edges($self);
}
sub point_on_segment {
my $self = shift;
my ($point) = @_;
return polygon_segment_having_point($self, $point);
}
sub encloses_point {
my $self = shift;
my ($point) = @_;
return point_in_polygon($point, $self);
}
sub translate {
my $self = shift;
my ($x, $y) = @_;
@$self = move_points([$x, $y], @$self);
}
sub rotate {
my $self = shift;
my ($angle, $center) = @_;
@$self = rotate_points($angle, $center, @$self);
}
1;

View File

@ -2,8 +2,8 @@ package Slic3r::Polyline;
use Moo;
use Math::Clipper qw();
use Slic3r::Geometry qw(polyline_remove_parallel_continuous_edges polyline_remove_acute_vertices
polygon_remove_acute_vertices polygon_remove_parallel_continuous_edges);
use Slic3r::Geometry qw(A B polyline_remove_parallel_continuous_edges polyline_remove_acute_vertices
polygon_remove_acute_vertices polygon_remove_parallel_continuous_edges move_points);
use Sub::Quote;
use XXX;
@ -118,4 +118,49 @@ sub has_segment {
return 0;
}
sub clip_with_expolygon {
my $self = shift;
my ($expolygon) = @_;
my @polylines = ();
my $current_polyline = [];
foreach my $line ($self->lines) {
my ($first_line, @other_lines) = @{ $expolygon->clip_line($line) };
next unless $first_line;
if (!@$current_polyline) {
push @$current_polyline, @$first_line;
} elsif ($first_line->[A]->coincides_with($current_polyline->[-1])) {
push @$current_polyline, $first_line->[B];
} else {
push @polylines, $current_polyline;
$current_polyline = [ @$first_line ];
}
foreach my $other_line (@other_lines) {
if (@$current_polyline) {
push @polylines, $current_polyline;
$current_polyline = [];
}
push @polylines, [ @$other_line ];
}
}
if (@$current_polyline) {
push @polylines, $current_polyline;
}
return map Slic3r::Polyline->cast($_), @polylines;
}
sub bounding_box {
my $self = shift;
return Slic3r::Geometry::bounding_box($self->points);
}
sub translate {
my $self = shift;
my ($x, $y) = @_;
@{$self->points} = move_points([$x, $y], @{$self->points});
}
1;

View File

@ -47,7 +47,8 @@ GetOptions(
# print options
'perimeters=i' => \$Slic3r::perimeter_offsets,
'solid-layers=i' => \$Slic3r::solid_layers,
'fill-type=s' => \$Slic3r::fill_type,
'fill-pattern=s' => \$Slic3r::fill_pattern,
'solid-fill-pattern=s' => \$Slic3r::solid_fill_pattern,
'fill-density=f' => \$Slic3r::fill_density,
'fill-angle=i' => \$Slic3r::fill_angle,
'start-gcode=s' => \$opt{start_gcode},
@ -161,6 +162,8 @@ Usage: slic3r.pl [ OPTIONS ] file.stl
(range: 1+, default: $Slic3r::solid_layers)
--fill-density Infill density (range: 0-1, default: $Slic3r::fill_density)
--fill-angle Infill angle in degrees (range: 0-90, default: $Slic3r::fill_angle)
--fill-pattern Pattern to use to fill non-solid layers (default: $Slic3r::fill_pattern)
--solid-fill-pattern Pattern to use to fill solid layers (default: $Slic3r::solid_fill_pattern)
--start-gcode Load initial gcode from the supplied file. This will overwrite
the default command (home all axes [G28]).
--end-gcode Load final gcode from the supplied file. This will overwrite

View File

@ -2,7 +2,7 @@ use Test::More;
use strict;
use warnings;
plan tests => 14;
plan tests => 24;
BEGIN {
use FindBin;
@ -59,11 +59,57 @@ is_deeply $intersection, [ [12, 12], [18, 16] ], 'internal lines are preserved';
[16, 16],
[16, 14],
];
my $intersections = Slic3r::Geometry::clip_segment_complex_polygon($line, [ $square, $hole_in_square ]);
is_deeply $intersections, [
[ [10, 15], [14, 15] ],
[ [16, 15], [20, 15] ],
], 'line is clipped to square with hole';
my $expolygon = Slic3r::ExPolygon->new($square, $hole_in_square);
is $expolygon->encloses_point([10, 10]), 1, 'corner point is recognized';
is $expolygon->encloses_point([10, 18]), 1, 'point on contour is recognized';
is $expolygon->encloses_point([14, 15]), 1, 'point on hole contour is recognized';
is $expolygon->encloses_point([14, 14]), 1, 'point on hole corner is recognized';
{
my $intersections = $expolygon->clip_line([ [15,18], [15,15] ]);
is_deeply $intersections, [
[ [15, 18], [15, 16] ],
], 'line is clipped to square with hole';
}
{
my $intersections = $expolygon->clip_line([ [15,15], [15,12] ]);
is_deeply $intersections, [
[ [15, 14], [15, 12] ],
], 'line is clipped to square with hole';
}
{
my $intersections = $expolygon->clip_line([ [12,18], [18,18] ]);
is_deeply $intersections, [
[ [12,18], [18,18] ],
], 'line is clipped to square with hole';
}
{
my $intersections = $expolygon->clip_line($line);
is_deeply $intersections, [
[ [10, 15], [14, 15] ],
[ [16, 15], [20, 15] ],
], 'line is clipped to square with hole';
}
{
my $intersections = $expolygon->clip_line([ reverse @$line ]);
is_deeply $intersections, [
[ [20, 15], [16, 15] ],
[ [14, 15], [10, 15] ],
], 'reverse line is clipped to square with hole';
}
{
my $intersections = $expolygon->clip_line([ [10,18], [20,18] ]);
is_deeply $intersections, [
[ [10, 18], [20, 18] ],
], 'tangent line is clipped to square with hole';
}
{
my $polyline = Slic3r::Polyline->cast([ [5, 18], [25, 18], [25, 15], [15, 15], [15, 12], [12, 12], [12, 5] ]);
is_deeply [ map $_->p, $polyline->clip_with_expolygon($expolygon) ], [
[ [10, 18], [20, 18] ],
[ [20, 15], [16, 15] ],
[ [15, 14], [15, 12], [12, 12], [12, 10] ],
], 'polyline is clipped to square with hole';
}
}
#==========================================================
@ -93,11 +139,14 @@ is_deeply $intersection, [ [12, 12], [18, 16] ], 'internal lines are preserved';
];
is is_counter_clockwise($small_circle), 0, "hole is clockwise";
my $expolygon = Slic3r::ExPolygon->new($large_circle, $small_circle);
$line = [ [152.741724,288.086671142818], [152.741724,34.166466971035] ];
my $intersections = Slic3r::Geometry::clip_segment_complex_polygon($line, [ $large_circle, $small_circle ]);
my $intersections = $expolygon->clip_line($line);
is_deeply $intersections, [
[ [152.741724, 35.166466971035], [152.741724, 108.087543109156] ],
[ [152.741724, 215.178806915206], [152.741724, 288.086671142818] ],
[ [152.741724, 288.086671142818], [152.741724, 215.178806915206], ],
[ [152.741724, 108.087543109156], [152.741724, 35.166466971035] ],
], 'line is clipped to square with hole';
}
#==========================================================