Fix integration of XS containers

This commit is contained in:
Alessandro Ranellucci 2013-07-16 17:13:01 +02:00
parent 9b582a11ff
commit 9458c7db97
34 changed files with 279 additions and 152 deletions

View file

@ -87,9 +87,9 @@ sub encloses_line {
my $clip = $self->clip_line($line);
if (!defined $tolerance) {
# optimization
return @$clip == 1 && same_line($clip->[0], $line);
return @$clip == 1 && same_line($clip->[0]->pp, $line->pp);
} else {
return @$clip == 1 && abs(Boost::Geometry::Utils::linestring_length($clip->[0]) - $line->length) < $tolerance;
return @$clip == 1 && abs(Boost::Geometry::Utils::linestring_length($clip->[0]->pp) - $line->length) < $tolerance;
}
}
@ -102,7 +102,10 @@ sub clip_line {
my $self = shift;
my ($line) = @_; # line must be a Slic3r::Line object
return Boost::Geometry::Utils::polygon_multi_linestring_intersection($self->pp, [$line->pp]);
return [
map Slic3r::Line->new(@$_),
@{Boost::Geometry::Utils::polygon_multi_linestring_intersection($self->pp, [$line->pp])}
];
}
sub simplify {

View file

@ -18,4 +18,13 @@ sub first_point {
return $self->polygon->[0];
}
sub make_counter_clockwise {
my $self = shift;
if (!$self->polygon->is_counter_clockwise) {
$self->reverse;
return 1;
}
return 0;
}
1;

View file

@ -32,7 +32,7 @@ sub intersect_expolygons {
my ($expolygons) = @_;
return map $self->clone(polyline => Slic3r::Polyline->new(@$_)),
@{Boost::Geometry::Utils::multi_polygon_multi_linestring_intersection($expolygons, [$self->arrayref])};
@{Boost::Geometry::Utils::multi_polygon_multi_linestring_intersection([ map $_->pp, @$expolygons ], [$self->pp])};
}
sub subtract_expolygons {
@ -40,12 +40,24 @@ sub subtract_expolygons {
my ($expolygons) = @_;
return map $self->clone(polyline => Slic3r::Polyline->new(@$_)),
@{Boost::Geometry::Utils::multi_linestring_multi_polygon_difference([$self->arrayref], $expolygons)};
@{Boost::Geometry::Utils::multi_linestring_multi_polygon_difference([$self->pp], [ map $_->pp, @$expolygons ])};
}
sub simplify {
my $self = shift;
$self->set_polyline($self->polyline->simplify(@_));
$self->polyline($self->polyline->simplify(@_));
}
sub clip_end {
my $self = shift;
my $polyline = $self->polyline;
$polyline->clip_end(@_);
$self->polyline($polyline);
}
sub length {
my $self = shift;
return $self->polyline->length;
}
sub points {

View file

@ -1,14 +1,21 @@
package Slic3r::ExtrusionPath::Arc;
use Moo;
extends 'Slic3r::ExtrusionPath';
has 'polyline' => (is => 'rw', required => 1);
has 'role' => (is => 'rw', required => 1);
has 'height' => (is => 'rw');
has 'flow_spacing' => (is => 'rw');
has 'center' => (is => 'ro', required => 1);
has 'radius' => (is => 'ro', required => 1);
has 'orientation' => (is => 'ro', required => 1); # cw/ccw
use Slic3r::Geometry qw(PI angle3points);
sub points {
my $self = shift;
return $self->polyline;
}
sub angle {
my $self = shift;
return angle3points($self->center, @{$self->points});

View file

@ -12,21 +12,35 @@ sub first_point {
return $self->paths->[0]->polyline->[0];
}
# Note that our paths will be reversed in place when necessary.
# (Same algorithm as Polyline::Collection)
sub chained_path {
my $self = shift;
my ($start_near, $no_reverse) = @_;
return @{$self->paths} if $self->no_sort;
my @my_paths = @{$self->paths};
# make sure we pass the same path objects to the Collection constructor
# and the ->chained_path() method because the latter will reverse the
# paths in-place when needed and we need to return them that way
my @paths = @{$self->paths};
my $collection = Slic3r::Polyline::Collection->new(
polylines => [ map $_->polyline, @paths ],
);
return $collection->chained_path($start_near, \@paths, $no_reverse);
my @paths = ();
my $start_at;
my $endpoints = $no_reverse
? [ map { @$_[0,0] } @my_paths ]
: [ map { @$_[0,-1] } @my_paths ];
while (@my_paths) {
# find nearest point
my $start_index = defined $start_near
? Slic3r::Geometry::nearest_point_index($start_near, $endpoints)
: 0;
my $path_index = int($start_index/2);
if ($start_index % 2 && !$no_reverse) { # index is end so reverse to make it the start
$my_paths[$path_index]->reverse;
}
push @paths, splice @my_paths, $path_index, 1;
splice @$endpoints, $path_index*2, 2;
$start_near = $paths[-1][-1];
}
return @paths;
}
sub cleanup {

View file

@ -157,24 +157,20 @@ sub make_fill {
next SURFACE unless $density > 0;
}
my @paths;
{
my $f = $self->filler($filler);
$f->layer_id($layerm->id);
@paths = $f->fill_surface(
$surface,
density => $density,
flow_spacing => $flow_spacing,
dont_adjust => $is_bridge,
);
}
my $params = shift @paths;
my $f = $self->filler($filler);
$f->layer_id($layerm->id);
my ($params, @polylines) = $f->fill_surface(
$surface,
density => $density,
flow_spacing => $flow_spacing,
dont_adjust => $is_bridge,
);
next unless @polylines;
# ugly hack(tm) to get the right amount of flow (GCode.pm should be fixed)
$params->{flow_spacing} = $layerm->extruders->{infill}->bridge_flow->width if $is_bridge;
# save into layer
next unless @paths;
push @fills, Slic3r::ExtrusionPath::Collection->new(
no_sort => $params->{no_sort},
paths => [
@ -189,10 +185,10 @@ sub make_fill {
: EXTR_ROLE_FILL),
height => $surface->thickness,
flow_spacing => $params->{flow_spacing} || (warn "Warning: no flow_spacing was returned by the infill engine, please report this to the developer\n"),
), @paths,
), @polylines,
],
);
push @fills_ordering_points, $paths[0][0];
push @fills_ordering_points, $polylines[0][0];
}
# add thin fill regions

View file

@ -52,8 +52,8 @@ sub rotate_points_back {
my @rotate = (-$rotate_vector->[0][0], $rotate_vector->[0][1]);
my $shift = [ map -$_, @{$rotate_vector->[1]} ];
@$paths = map [ Slic3r::Geometry::rotate_points(@rotate, @$_) ],
map [ Slic3r::Geometry::move_points($shift, @$_) ], @$paths;
$_->translate(@$shift) for @$paths;
$_->rotate(@rotate) for @$paths;
}
sub adjust_solid_spacing {

View file

@ -91,7 +91,7 @@ sub fill_surface {
@paths = map Slic3r::Polyline->new(@$_),
@{ Boost::Geometry::Utils::polygon_multi_linestring_intersection(
$surface->expolygon->pp,
\@polygons,
[ map $_->pp, @polygons ],
) };
# connect paths
@ -104,7 +104,7 @@ sub fill_surface {
my $distance = $paths[-1][-1]->distance_to($path->[0]);
if ($distance <= $m->{hex_width}) {
push @{$paths[-1]}, @$path;
$paths[-1]->append(@$path);
next;
}
}
@ -115,8 +115,8 @@ sub fill_surface {
# clip paths again to prevent connection segments from crossing the expolygon boundaries
@paths = map Slic3r::Polyline->new(@$_),
@{ Boost::Geometry::Utils::multi_polygon_multi_linestring_intersection(
[ map $_->arrayref, $surface->expolygon->offset_ex(scaled_epsilon) ],
[ @paths ],
[ map $_->pp, $surface->expolygon->offset_ex(scaled_epsilon) ],
[ map $_->pp, @paths ],
) } if @paths; # this temporary check is a workaround for the multilinestring bug in B::G::U
}

View file

@ -65,17 +65,18 @@ sub fill_surface {
# clip paths against a slightly offsetted expolygon, so that the first and last paths
# are kept even if the expolygon has vertical sides
my @paths = @{ Boost::Geometry::Utils::polygon_multi_linestring_intersection(
+($expolygon->offset_ex(scaled_epsilon))[0]->pp, # TODO: we should use all the resulting expolygons and clip the linestrings to a multipolygon object
[ map $_->pp, @{ $self->cache->{$cache_id} } ],
) };
my @polylines = map Slic3r::Polyline->new(@$_),
@{ Boost::Geometry::Utils::multi_polygon_multi_linestring_intersection(
[ map $_->pp, $expolygon->offset_ex(scaled_epsilon) ],
[ map $_->pp, @{ $self->cache->{$cache_id} } ],
) };
# connect lines
unless ($params{dont_connect}) {
my $collection = Slic3r::Polyline::Collection->new(
polylines => [ map Slic3r::Polyline->new(@$_), @paths ],
polylines => [ @polylines ],
);
@paths = ();
@polylines = ();
my $tolerance = 10 * scaled_epsilon;
my $diagonal_distance = $distance_between_lines * 2;
@ -86,26 +87,26 @@ sub fill_surface {
}
: sub { $_[X] <= $diagonal_distance && $_[Y] <= $diagonal_distance };
foreach my $path ($collection->chained_path) {
if (@paths) {
my @distance = map abs($path->[0][$_] - $paths[-1][-1][$_]), (X,Y);
foreach my $polyline ($collection->chained_path) {
if (@polylines) {
my $last_point = $polylines[-1][-1]->pp;
my @distance = map abs($polyline->[0][$_] - $last_point->[$_]), (X,Y);
# TODO: we should also check that both points are on a fill_boundary to avoid
# connecting paths on the boundaries of internal regions
if ($can_connect->(@distance)
&& $expolygon_off->encloses_line(Slic3r::Line->new($paths[-1][-1], $path->[0]), $tolerance)) {
push @{$paths[-1]}, @$path;
if ($can_connect->(@distance) && $expolygon_off->encloses_line(Slic3r::Line->new($last_point, $polyline->[0]), $tolerance)) {
$polylines[-1]->append(@$polyline);
next;
}
}
push @paths, $path;
push @polylines, $polyline;
}
}
# paths must be rotated back
$self->rotate_points_back(\@paths, $rotate_vector);
$self->rotate_points_back(\@polylines, $rotate_vector);
return { flow_spacing => $flow_spacing }, @paths;
return { flow_spacing => $flow_spacing }, @polylines;
}
1;

View file

@ -100,7 +100,7 @@ sub change_layer {
# avoid computing overhangs if they're not needed
$self->_layer_overhangs(
$layer->id > 0 && ($Slic3r::Config->overhangs || $Slic3r::Config->start_perimeters_at_non_overhang)
? [ map $_->expolygon->arrayref, grep $_->surface_type == S_TYPE_BOTTOM, map @{$_->slices}, @{$layer->regions} ]
? [ map $_->expolygon, grep $_->surface_type == S_TYPE_BOTTOM, map @{$_->slices}, @{$layer->regions} ]
: []
);
if ($self->config->avoid_crossing_perimeters) {
@ -152,8 +152,8 @@ sub extrude_loop {
my ($loop, $description) = @_;
# extrude all loops ccw
my $was_clockwise = $loop->make_counter_clockwise;
my $polygon = $loop->polygon;
my $was_clockwise = $polygon->make_counter_clockwise;
# find candidate starting points
# start looking for concave vertices not being overhangs
@ -310,8 +310,8 @@ sub extrude_path {
$path->center, $e * unscale $path_length, $description);
$self->wipe_path(undef);
} else {
foreach my $line ($path->lines) {
my $line_length = unscale $line->length;
foreach my $line (@{$path->lines}) {
my $line_length = unscale($line->length);
$path_length += $line_length;
$gcode .= $self->G1($line->[B], undef, $e * $line_length, $description);
}
@ -341,8 +341,7 @@ sub travel_to {
my ($point, $role, $comment) = @_;
my $gcode = "";
my $travel = Slic3r::Line->new($self->last_pos->clone, $point->clone);
my $travel = Slic3r::Line->new($self->last_pos, $point);
# move travel back to original layer coordinates for the island check.
# note that we're only considering the current object's islands, while we should

View file

@ -37,7 +37,7 @@ sub parse {
$info{"new_$axis"} = $self->$axis;
}
}
$info{dist_XY} = Slic3r::Line->new([0,0], [@info{qw(dist_X dist_Y)}])->length;
$info{dist_XY} = Slic3r::Geometry::unscale(Slic3r::Line->new_scale([0,0], [@info{qw(dist_X dist_Y)}])->length);
if (exists $args{E}) {
if ($info{dist_E} > 0) {
$info{extruding} = 1;

View file

@ -195,21 +195,22 @@ sub point_in_segment {
my ($point, $line) = @_;
my ($x, $y) = @$point;
my @line_x = sort { $a <=> $b } $line->[A][X], $line->[B][X];
my @line_y = sort { $a <=> $b } $line->[A][Y], $line->[B][Y];
my $line_p = $line->pp;
my @line_x = sort { $a <=> $b } $line_p->[A][X], $line_p->[B][X];
my @line_y = sort { $a <=> $b } $line_p->[A][Y], $line_p->[B][Y];
# check whether the point is in the segment bounding box
return 0 unless $x >= ($line_x[0] - epsilon) && $x <= ($line_x[1] + epsilon)
&& $y >= ($line_y[0] - epsilon) && $y <= ($line_y[1] + epsilon);
# if line is vertical, check whether point's X is the same as the line
if ($line->[A][X] == $line->[B][X]) {
return abs($x - $line->[A][X]) < epsilon ? 1 : 0;
if ($line_p->[A][X] == $line_p->[B][X]) {
return abs($x - $line_p->[A][X]) < epsilon ? 1 : 0;
}
# calculate the Y in line at X of the point
my $y3 = $line->[A][Y] + ($line->[B][Y] - $line->[A][Y])
* ($x - $line->[A][X]) / ($line->[B][X] - $line->[A][X]);
my $y3 = $line_p->[A][Y] + ($line_p->[B][Y] - $line_p->[A][Y])
* ($x - $line_p->[A][X]) / ($line_p->[B][X] - $line_p->[A][X]);
return abs($y3 - $y) < epsilon ? 1 : 0;
}
@ -249,19 +250,19 @@ sub nearest_point_index {
my ($point, $points) = @_;
my ($nearest_point_index, $distance) = ();
my $point_x = $point->[X];
my $point_y = $point->[Y];
my ($point_x, $point_y) = @$point;
my @points_pp = map $_->pp, @$points;
for my $i (0..$#$points) {
my $d = ($point_x - $points->[$i]->[X])**2;
my $d = ($point_x - $points_pp[$i][X])**2;
# If the X distance of the candidate is > than the total distance of the
# best previous candidate, we know we don't want it
next if (defined $distance && $d > $distance);
# If the total distance of the candidate is > than the total distance of the
# best previous candidate, we know we don't want it
$d += ($point_y - $points->[$i]->[Y])**2;
$d += ($point_y - $points_pp[$i][Y])**2;
next if (defined $distance && $d > $distance);
$nearest_point_index = $i;
@ -286,14 +287,14 @@ sub point_along_segment {
}
}
return $point;
return Slic3r::Point->new(@$point);
}
# given a $polygon, return the (first) segment having $point
sub polygon_segment_having_point {
my ($polygon, $point) = @_;
foreach my $line (polygon_lines($polygon)) {
foreach my $line (@{ $polygon->lines }) {
return $line if point_in_segment($point, $line);
}
return undef;

View file

@ -181,7 +181,7 @@ sub traverse_pt {
sub _convert {
my $p = shift;
$p = $p->pp if ref($p) ne 'ARRAY' && $p->can('pp');
return [ map { ref($_) ne 'ARRAY' && $_->can('pp') ? $_->pp : $_ } @$p ];
return [ map { (ref($_) ne 'ARRAY' && $_->can('pp')) ? $_->pp : $_ } @$p ];
}
1;

View file

@ -31,8 +31,7 @@ has 'slices' => (is => 'rw', default => sub { Slic3r::Surface::Collection->new }
# in the original geometry
has 'thin_walls' => (is => 'rw', default => sub { [] });
# collection of polygons or polylines representing thin infill regions that
# need to be filled with a medial axis
# collection of extrusion paths/loops filling gaps
has 'thin_fills' => (is => 'rw', default => sub { [] });
# collection of surfaces for infill generation
@ -234,7 +233,7 @@ sub make_perimeters {
# use a nearest neighbor search to order these children
# TODO: supply second argument to chained_path_items() too?
my @nodes = @{Slic3r::Geometry::chained_path_items(
[ map [ ($_->{outer} ? $_->{outer}[0] : $_->{hole}[0]), $_ ], @$polynodes ],
[ map [ Slic3r::Point->new(@{$_->{outer} ? $_->{outer}[0] : $_->{hole}[0]}), $_ ], @$polynodes ],
)};
my @loops = ();

View file

@ -2,6 +2,11 @@ package Slic3r::Point;
use strict;
use warnings;
sub new_scale {
my $class = shift;
return $class->new(map Slic3r::Geometry::scale($_), @_);
}
sub distance_to {
my $self = shift;
my ($point) = @_;

View file

@ -54,7 +54,7 @@ sub remove_acute_vertices {
sub encloses_point {
my $self = shift;
my ($point) = @_;
return Boost::Geometry::Utils::point_covered_by_polygon($point->arrayref, [$self->pp]);
return Boost::Geometry::Utils::point_covered_by_polygon($point->pp, [$self->pp]);
}
sub area {
@ -138,9 +138,10 @@ sub split_at {
sub concave_points {
my $self = shift;
my @points = @{$self->pp};
return map $self->[$_],
grep Slic3r::Geometry::angle3points(@$self[$_, $_-1, $_+1]) < PI - epsilon,
-1 .. ($#$self-1);
grep Slic3r::Geometry::angle3points(@points[$_, $_-1, $_+1]) < PI - epsilon,
-1 .. ($#points-1);
}
1;

View file

@ -6,6 +6,12 @@ use Slic3r::Geometry qw(A B X Y X1 X2 Y1 Y2 polyline_remove_parallel_continuous_
use Slic3r::Geometry::Clipper qw(JT_SQUARE);
use Storable qw();
sub new_scale {
my $class = shift;
my @points = map { ref($_) eq 'Slic3r::Point' ? $_->pp : $_ } @_;
return $class->new(map [ Slic3r::Geometry::scale($_->[X]), Slic3r::Geometry::scale($_->[Y]) ], @points);
}
sub wkt {
my $self = shift;
return sprintf "LINESTRING((%s))", join ',', map "$_->[0] $_->[1]", @$self;
@ -39,9 +45,10 @@ sub grow {
my ($distance, $scale, $joinType, $miterLimit) = @_;
$joinType //= JT_SQUARE;
my @points = @$self;
return map Slic3r::Polygon->new(@$_),
Slic3r::Geometry::Clipper::offset(
[ [ @$self, CORE::reverse @$self[1..($#$self-1)] ] ],
[ Slic3r::Polygon->new(@points, CORE::reverse @points[1..($#points-1)]) ],
$distance, $scale, $joinType, $miterLimit,
);
}
@ -72,7 +79,7 @@ sub clip_with_expolygon {
my ($expolygon) = @_;
my $result = Boost::Geometry::Utils::polygon_multi_linestring_intersection($expolygon->pp, [$self->pp]);
return @$result;
return map { (ref $self)->new(@$_) } @$result;
}
sub bounding_box {
@ -108,7 +115,7 @@ sub clip_end {
}
my $new_point = Slic3r::Geometry::point_along_segment($last_point, $self->[-1], $distance);
$self->append(Slic3r::Point->new($new_point));
$self->append($new_point);
$distance = 0;
}
}
@ -142,22 +149,18 @@ use Moo;
has 'polylines' => (is => 'ro', default => sub { [] });
# If the second argument is provided, this method will return its items sorted
# instead of returning the actual sorted polylines.
# Note that our polylines will be reversed in place when necessary.
sub chained_path {
my $self = shift;
my ($start_near, $items, $no_reverse) = @_;
my ($start_near, $no_reverse) = @_;
$items ||= $self->polylines;
my %items_map = map { $self->polylines->[$_] => $items->[$_] } 0 .. $#{$self->polylines};
my @my_paths = @{$self->polylines};
my @paths = ();
my $start_at;
my $endpoints = $no_reverse
? [ map { $_->[0], $_->[0] } @my_paths ]
: [ map { $_->[0], $_->[-1] } @my_paths ];
? [ map { @$_[0,0] } @my_paths ]
: [ map { @$_[0,-1] } @my_paths ];
while (@my_paths) {
# find nearest point
my $start_index = defined $start_near
@ -172,7 +175,7 @@ sub chained_path {
splice @$endpoints, $path_index*2, 2;
$start_near = $paths[-1][-1];
}
return map $items_map{"$_"}, @paths;
return @paths;
}
1;

View file

@ -833,7 +833,7 @@ sub write_gcode {
}
} else {
# order objects using a nearest neighbor search
my @obj_idx = chained_path([ map $_->copies->[0], @{$self->objects} ]);
my @obj_idx = chained_path([ map Slic3r::Point->new(@{$_->copies->[0]}), @{$self->objects} ]);
# sort layers by Z
my %layers = (); # print_z => [ layer, layer, layer ] by obj_idx