diff --git a/lib/Slic3r/ExPolygon.pm b/lib/Slic3r/ExPolygon.pm index 1fcdb00d9..0f1c3189b 100644 --- a/lib/Slic3r/ExPolygon.pm +++ b/lib/Slic3r/ExPolygon.pm @@ -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 { diff --git a/lib/Slic3r/ExtrusionLoop.pm b/lib/Slic3r/ExtrusionLoop.pm index 4298d0e0b..4fb5ce79c 100644 --- a/lib/Slic3r/ExtrusionLoop.pm +++ b/lib/Slic3r/ExtrusionLoop.pm @@ -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; diff --git a/lib/Slic3r/ExtrusionPath.pm b/lib/Slic3r/ExtrusionPath.pm index facddd614..5446736f6 100644 --- a/lib/Slic3r/ExtrusionPath.pm +++ b/lib/Slic3r/ExtrusionPath.pm @@ -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 { diff --git a/lib/Slic3r/ExtrusionPath/Arc.pm b/lib/Slic3r/ExtrusionPath/Arc.pm index 05596f185..330d84543 100644 --- a/lib/Slic3r/ExtrusionPath/Arc.pm +++ b/lib/Slic3r/ExtrusionPath/Arc.pm @@ -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}); diff --git a/lib/Slic3r/ExtrusionPath/Collection.pm b/lib/Slic3r/ExtrusionPath/Collection.pm index faee94af8..bb81381d6 100644 --- a/lib/Slic3r/ExtrusionPath/Collection.pm +++ b/lib/Slic3r/ExtrusionPath/Collection.pm @@ -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 { diff --git a/lib/Slic3r/Fill.pm b/lib/Slic3r/Fill.pm index 18ad6d6f0..b31032c9d 100644 --- a/lib/Slic3r/Fill.pm +++ b/lib/Slic3r/Fill.pm @@ -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 diff --git a/lib/Slic3r/Fill/Base.pm b/lib/Slic3r/Fill/Base.pm index d0c9d4f30..e63ce8843 100644 --- a/lib/Slic3r/Fill/Base.pm +++ b/lib/Slic3r/Fill/Base.pm @@ -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 { diff --git a/lib/Slic3r/Fill/Honeycomb.pm b/lib/Slic3r/Fill/Honeycomb.pm index 2ead45721..3db4a5c42 100644 --- a/lib/Slic3r/Fill/Honeycomb.pm +++ b/lib/Slic3r/Fill/Honeycomb.pm @@ -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 } diff --git a/lib/Slic3r/Fill/Rectilinear.pm b/lib/Slic3r/Fill/Rectilinear.pm index 54998cb40..d35f2d383 100644 --- a/lib/Slic3r/Fill/Rectilinear.pm +++ b/lib/Slic3r/Fill/Rectilinear.pm @@ -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; diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index aefb703b1..0b601fc2a 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -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 diff --git a/lib/Slic3r/GCode/Reader.pm b/lib/Slic3r/GCode/Reader.pm index 20a1313b0..ed3bdb73b 100644 --- a/lib/Slic3r/GCode/Reader.pm +++ b/lib/Slic3r/GCode/Reader.pm @@ -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; diff --git a/lib/Slic3r/Geometry.pm b/lib/Slic3r/Geometry.pm index 24856782a..6a54fa55d 100644 --- a/lib/Slic3r/Geometry.pm +++ b/lib/Slic3r/Geometry.pm @@ -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; diff --git a/lib/Slic3r/Geometry/Clipper.pm b/lib/Slic3r/Geometry/Clipper.pm index 02ec5c557..006a4130c 100644 --- a/lib/Slic3r/Geometry/Clipper.pm +++ b/lib/Slic3r/Geometry/Clipper.pm @@ -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; diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index 64b6758d0..9e635854b 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -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 = (); diff --git a/lib/Slic3r/Point.pm b/lib/Slic3r/Point.pm index 0f7354e29..0a3d7ec02 100644 --- a/lib/Slic3r/Point.pm +++ b/lib/Slic3r/Point.pm @@ -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) = @_; diff --git a/lib/Slic3r/Polygon.pm b/lib/Slic3r/Polygon.pm index 33e4da409..497daee0d 100644 --- a/lib/Slic3r/Polygon.pm +++ b/lib/Slic3r/Polygon.pm @@ -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; \ No newline at end of file diff --git a/lib/Slic3r/Polyline.pm b/lib/Slic3r/Polyline.pm index 637c39b42..e98fbc8d2 100644 --- a/lib/Slic3r/Polyline.pm +++ b/lib/Slic3r/Polyline.pm @@ -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; diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 338118a2b..af5e3eb4d 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -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 diff --git a/t/geometry.t b/t/geometry.t index ac808fa1d..cdec8eb95 100644 --- a/t/geometry.t +++ b/t/geometry.t @@ -29,37 +29,38 @@ isnt Slic3r::Geometry::line_intersection($line1, $line2, 1), undef, 'line_inters #========================================================== { - my $polyline = [ + my $polygon = Slic3r::Polygon->new( [459190000, 5152739000], [147261000, 4612464000], [147261000, 3487535000], [339887000, 3153898000], [437497000, 3438430000], [454223000, 3522515000], [523621000, 3626378000], [627484000, 3695776000], [750000000, 3720147000], [872515000, 3695776000], [976378000, 3626378000], [1045776000, 3522515000], [1070147000, 3400000000], [1045776000, 3277484000], [976378000, 3173621000], [872515000, 3104223000], [827892000, 3095347000], [698461000, 2947261000], [2540810000, 2947261000], [2852739000, 3487535000], [2852739000, 4612464000], [2540810000, 5152739000], - ]; + ); # this points belongs to $polyline - my $point = [2797980957.103410,3392691792.513960]; + # note: it's actually a vertex, while we should better check an intermediate point + my $point = Slic3r::Point->new(1045776000, 3277484000); local $Slic3r::Geometry::epsilon = 1E-5; - is_deeply Slic3r::Geometry::polygon_segment_having_point($polyline, $point), - [ [2540810000, 2947261000], [2852739000, 3487535000] ], + is_deeply Slic3r::Geometry::polygon_segment_having_point($polygon, $point)->pp, + [ [1070147000, 3400000000], [1045776000, 3277484000] ], 'polygon_segment_having_point'; } #========================================================== { - my $point = [ 736310778.185108, 5017423926.8924 ]; - my $line = [ [627484000, 3695776000], [750000000, 3720147000] ]; + my $point = Slic3r::Point->new(736310778.185108, 5017423926.8924); + my $line = Slic3r::Line->new([627484000, 3695776000], [750000000, 3720147000]); is Slic3r::Geometry::point_in_segment($point, $line), 0, 'point_in_segment'; } #========================================================== { - my $point = [ 736310778.185108, 5017423926.8924 ]; - my $line = [ [627484000, 3695776000], [750000000, 3720147000] ]; + my $point = Slic3r::Point->new(736310778.185108, 5017423926.8924); + my $line = Slic3r::Line->new([627484000, 3695776000], [750000000, 3720147000]); is Slic3r::Geometry::point_in_segment($point, $line), 0, 'point_in_segment'; } @@ -158,7 +159,7 @@ is Slic3r::Geometry::can_connect_points(@$points, $polygons), 0, 'can_connect_po { my $polyline = Slic3r::Polyline->new([0, 0], [10, 0], [20, 0]); - is_deeply [ map $_->pp, $polyline->lines ], [ + is_deeply [ map $_->pp, @{$polyline->lines} ], [ [ [0, 0], [10, 0] ], [ [10, 0], [20, 0] ], ], 'polyline_lines'; @@ -167,8 +168,8 @@ is Slic3r::Geometry::can_connect_points(@$points, $polygons), 0, 'can_connect_po #========================================================== { - my $polyline = Slic3r::Polygon->new([0, 0], [10, 0], [5, 5]); - my $result = $polyline->split_at_index(1); + my $polygon = Slic3r::Polygon->new([0, 0], [10, 0], [5, 5]); + my $result = $polygon->split_at_index(1); is ref($result), 'Slic3r::Polyline', 'split_at_index returns polyline'; is_deeply $result->pp, [ [10, 0], [5, 5], [0, 0], [10, 0] ], 'split_at_index'; } diff --git a/t/perimeters.t b/t/perimeters.t index 1dacd31c0..9ff53f01a 100644 --- a/t/perimeters.t +++ b/t/perimeters.t @@ -57,10 +57,10 @@ use Slic3r::Test; if ($cur_loop) { $has_cw_loops = 1 if !Slic3r::Geometry::Clipper::is_counter_clockwise($cur_loop); if ($self->F == $config->external_perimeter_speed*60) { - my $move_dest = [ @$info{qw(new_X new_Y)} ]; + my $move_dest = Slic3r::Point->new_scale(@$info{qw(new_X new_Y)}); $external_loops{$self->Z}++; $has_outwards_move = 1 - if !Slic3r::Polygon->new(@$cur_loop)->encloses_point($move_dest) + if !Slic3r::Polygon->new_scale(@$cur_loop)->encloses_point($move_dest) ? ($external_loops{$self->Z} == 2) # contour should include destination : ($external_loops{$self->Z} == 1); # hole should not } diff --git a/t/polyclip.t b/t/polyclip.t index 4882892bd..2cfe6951f 100644 --- a/t/polyclip.t +++ b/t/polyclip.t @@ -14,21 +14,21 @@ use Slic3r; #========================================================== -is Slic3r::Geometry::point_in_segment([10, 10], [ [5, 10], [20, 10] ]), 1, 'point in horizontal segment'; -is Slic3r::Geometry::point_in_segment([30, 10], [ [5, 10], [20, 10] ]), 0, 'point not in horizontal segment'; -is Slic3r::Geometry::point_in_segment([10, 10], [ [10, 5], [10, 20] ]), 1, 'point in vertical segment'; -is Slic3r::Geometry::point_in_segment([10, 30], [ [10, 5], [10, 20] ]), 0, 'point not in vertical segment'; -is Slic3r::Geometry::point_in_segment([15, 15], [ [10, 10], [20, 20] ]), 1, 'point in diagonal segment'; -is Slic3r::Geometry::point_in_segment([20, 15], [ [10, 10], [20, 20] ]), 0, 'point not in diagonal segment'; +is Slic3r::Geometry::point_in_segment(Slic3r::Point->new(10, 10), Slic3r::Line->new([5, 10], [20, 10])), 1, 'point in horizontal segment'; +is Slic3r::Geometry::point_in_segment(Slic3r::Point->new(30, 10), Slic3r::Line->new([5, 10], [20, 10])), 0, 'point not in horizontal segment'; +is Slic3r::Geometry::point_in_segment(Slic3r::Point->new(10, 10), Slic3r::Line->new([10, 5], [10, 20])), 1, 'point in vertical segment'; +is Slic3r::Geometry::point_in_segment(Slic3r::Point->new(10, 30), Slic3r::Line->new([10, 5], [10, 20])), 0, 'point not in vertical segment'; +is Slic3r::Geometry::point_in_segment(Slic3r::Point->new(15, 15), Slic3r::Line->new([10, 10], [20, 20])), 1, 'point in diagonal segment'; +is Slic3r::Geometry::point_in_segment(Slic3r::Point->new(20, 15), Slic3r::Line->new([10, 10], [20, 20])), 0, 'point not in diagonal segment'; #========================================================== -my $square = [ # ccw +my $square = Slic3r::Polygon->new( # ccw [100, 100], [200, 100], [200, 200], [100, 200], -]; +); my $line = Slic3r::Line->new([50, 150], [300, 150]); @@ -37,17 +37,17 @@ is_deeply $intersection, [ [100, 150], [200, 150] ], 'line is clipped to square' #========================================================== -$intersection = Slic3r::Geometry::clip_segment_polygon([ [0, 150], [80, 150] ], $square); +$intersection = Slic3r::Geometry::clip_segment_polygon(Slic3r::Line->new([0, 150], [80, 150]), $square); is $intersection, undef, 'external lines are ignored 1'; #========================================================== -$intersection = Slic3r::Geometry::clip_segment_polygon([ [300, 150], [400, 150] ], $square); +$intersection = Slic3r::Geometry::clip_segment_polygon(Slic3r::Line->new([300, 150], [400, 150]), $square); is $intersection, undef, 'external lines are ignored 2'; #========================================================== -$intersection = Slic3r::Geometry::clip_segment_polygon([ [120, 120], [180, 160] ], $square); +$intersection = Slic3r::Geometry::clip_segment_polygon(Slic3r::Line->new([120, 120], [180, 160]), $square); is_deeply $intersection, [ [120, 120], [180, 160] ], 'internal lines are preserved'; #========================================================== @@ -66,45 +66,45 @@ is_deeply $intersection, [ [120, 120], [180, 160] ], 'internal lines are preserv is $expolygon->encloses_point(Slic3r::Point->new(140, 140)), 1, 'point on hole corner is recognized'; { my $intersections = $expolygon->clip_line(Slic3r::Line->new([150,180], [150,150])); - is_deeply $intersections, [ + is_deeply [ map $_->pp, @$intersections ], [ [ [150, 180], [150, 160] ], ], 'line is clipped to square with hole'; } { my $intersections = $expolygon->clip_line(Slic3r::Line->new([150,150], [150,120])); - is_deeply $intersections, [ + is_deeply [ map $_->pp, @$intersections ], [ [ [150, 140], [150, 120] ], ], 'line is clipped to square with hole'; } { my $intersections = $expolygon->clip_line(Slic3r::Line->new([120,180], [180,180])); - is_deeply $intersections, [ + is_deeply [ map $_->pp, @$intersections ], [ [ [120,180], [180,180] ], ], 'line is clipped to square with hole'; } { my $intersections = $expolygon->clip_line($line); - is_deeply $intersections, [ + is_deeply [ map $_->pp, @$intersections ], [ [ [100, 150], [140, 150] ], [ [160, 150], [200, 150] ], ], 'line is clipped to square with hole'; } { my $intersections = $expolygon->clip_line(Slic3r::Line->new(reverse @$line)); - is_deeply $intersections, [ + is_deeply [ map $_->pp, @$intersections ], [ [ [200, 150], [160, 150] ], [ [140, 150], [100, 150] ], ], 'reverse line is clipped to square with hole'; } { my $intersections = $expolygon->clip_line(Slic3r::Line->new([100,180], [200,180])); - is_deeply $intersections, [ + is_deeply [ map $_->pp, @$intersections ], [ [ [100, 180], [200, 180] ], ], 'tangent line is clipped to square with hole'; } { my $polyline = Slic3r::Polyline->new([50, 180], [250, 180], [250, 150], [150, 150], [150, 120], [120, 120], [120, 50]); - is_deeply [ map $_, $polyline->clip_with_expolygon($expolygon) ], [ + is_deeply [ map $_->pp, $polyline->clip_with_expolygon($expolygon) ], [ [ [100, 180], [200, 180] ], [ [200, 150], [160, 150] ], [ [150, 140], [150, 120], [120, 120], [120, 100] ], @@ -143,9 +143,9 @@ is_deeply $intersection, [ [120, 120], [180, 160] ], 'internal lines are preserv $line = Slic3r::Line->new([152.742,288.086671142818], [152.742,34.166466971035]); my $intersections = $expolygon->clip_line($line); - is_deeply $intersections, [ - [ [152.742, 287.908315789474], [152.742, 214.522], ], - [ [152.742, 107.478], [152.742, 35] ], + is_deeply [ map $_->pp, @$intersections ], [ + [ [152, 287], [152, 214], ], + [ [152, 107], [152, 35] ], ], 'line is clipped to square with hole'; } diff --git a/xs/lib/Slic3r/XS.pm b/xs/lib/Slic3r/XS.pm index 363a95b82..fcf7054b0 100644 --- a/xs/lib/Slic3r/XS.pm +++ b/xs/lib/Slic3r/XS.pm @@ -7,26 +7,36 @@ our $VERSION = '0.01'; use XSLoader; XSLoader::load(__PACKAGE__, $VERSION); -package Slic3r::Point; -use overload - '@{}' => sub { $_[0]->arrayref }; - -package Slic3r::ExPolygon; -use overload - '@{}' => sub { $_[0]->arrayref }; - -package Slic3r::Polyline; +package Slic3r::Line; use overload '@{}' => sub { $_[0]->arrayref }, 'fallback' => 1; +package Slic3r::Point; +use overload + '@{}' => sub { $_[0]->arrayref }, + 'fallback' => 1; + +package Slic3r::ExPolygon; +use overload + '@{}' => sub { $_[0]->arrayref }, + 'fallback' => 1; + +package Slic3r::Polyline; +use overload + '@{}' => sub { $_[0]->arrayref }, + 'fallback' => 1, + 'fallback' => 1; + package Slic3r::Polygon; use overload - '@{}' => sub { $_[0]->arrayref }; + '@{}' => sub { $_[0]->arrayref }, + 'fallback' => 1; package Slic3r::ExPolygon::Collection; use overload - '@{}' => sub { $_[0]->arrayref }; + '@{}' => sub { $_[0]->arrayref }, + 'fallback' => 1; package Slic3r::ExtrusionLoop; use overload @@ -116,6 +126,7 @@ sub clone { package Slic3r::Surface::Collection; use overload - '@{}' => sub { $_[0]->arrayref }; + '@{}' => sub { $_[0]->arrayref }, + 'fallback' => 1; 1; diff --git a/xs/src/Point.hpp b/xs/src/Point.hpp index 09f9f36d3..b324ac6d1 100644 --- a/xs/src/Point.hpp +++ b/xs/src/Point.hpp @@ -72,8 +72,8 @@ void Point::from_SV(SV* point_sv) { AV* point_av = (AV*)SvRV(point_sv); - this->x = (unsigned long)SvIV(*av_fetch(point_av, 0, 0)); - this->y = (unsigned long)SvIV(*av_fetch(point_av, 1, 0)); + this->x = (long)SvIV(*av_fetch(point_av, 0, 0)); + this->y = (long)SvIV(*av_fetch(point_av, 1, 0)); } void diff --git a/xs/src/Polygon.hpp b/xs/src/Polygon.hpp index bb5a1440f..a644535db 100644 --- a/xs/src/Polygon.hpp +++ b/xs/src/Polygon.hpp @@ -40,7 +40,7 @@ Polygon::split_at_index(int index) for (int i = index; i < this->points.size(); i++) { poly->points.push_back( this->points[i] ); } - for (int i = 0; i < index; i++) { + for (int i = 0; i <= index; i++) { poly->points.push_back( this->points[i] ); } return poly; diff --git a/xs/t/03_point.t b/xs/t/03_point.t index a6e188279..687006b98 100644 --- a/xs/t/03_point.t +++ b/xs/t/03_point.t @@ -4,7 +4,7 @@ use strict; use warnings; use Slic3r::XS; -use Test::More tests => 5; +use Test::More tests => 7; my $point = Slic3r::Point->new(10, 15); is_deeply [ @$point ], [10, 15], 'point roundtrip'; @@ -19,4 +19,10 @@ is_deeply [ @$point2 ], [30, 15], 'translate'; ok $point->coincides_with($point->clone), 'coincides_with'; ok !$point->coincides_with($point2), 'coincides_with'; +{ + my $point3 = Slic3r::Point->new(4300000, -9880845); + is $point->[0], $point->x, 'x accessor'; + is $point->[1], $point->y, 'y accessor'; +} + __END__ diff --git a/xs/t/06_polygon.t b/xs/t/06_polygon.t index 473eaae0e..053bf5e65 100644 --- a/xs/t/06_polygon.t +++ b/xs/t/06_polygon.t @@ -27,7 +27,7 @@ is_deeply [ map $_->pp, @$lines ], [ [ [100, 200], [100, 100] ], ], 'polygon lines'; -is_deeply $polygon->split_at_first_point->pp, $square, 'split_at_first_point'; -is_deeply $polygon->split_at_index(2)->pp, [ @$square[2,3,0,1] ], 'split_at_index'; +is_deeply $polygon->split_at_first_point->pp, [ @$square[0,1,2,3,0] ], 'split_at_first_point'; +is_deeply $polygon->split_at_index(2)->pp, [ @$square[2,3,0,1,2] ], 'split_at_index'; __END__ diff --git a/xs/t/08_extrusionloop.t b/xs/t/08_extrusionloop.t index 9a7a087d2..a218825f1 100644 --- a/xs/t/08_extrusionloop.t +++ b/xs/t/08_extrusionloop.t @@ -28,10 +28,10 @@ is $loop->role, Slic3r::ExtrusionPath::EXTR_ROLE_FILL, 'modify role'; { my $path = $loop->split_at_first_point; - is_deeply $path->polyline->pp, $square, 'split_at_first_point'; + is_deeply $path->polyline->pp, [ @$square[0,1,2,3,0] ], 'split_at_first_point'; is $path->role, $loop->role, 'role preserved after split'; - is_deeply $loop->split_at_index(2)->polyline->pp, [ @$square[2,3,0,1] ], 'split_at_index'; + is_deeply $loop->split_at_index(2)->polyline->pp, [ @$square[2,3,0,1,2] ], 'split_at_index'; } __END__ diff --git a/xs/t/10_line.t b/xs/t/10_line.t new file mode 100644 index 000000000..b026bcf09 --- /dev/null +++ b/xs/t/10_line.t @@ -0,0 +1,40 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use Slic3r::XS; +use Test::More tests => 6; + +my $points = [ + [100, 100], + [200, 100], +]; + +my $line = Slic3r::Line->new(@$points); +is_deeply $line->pp, $points, 'line roundtrip'; + +is ref($line->arrayref), 'ARRAY', 'line arrayref is unblessed'; +isa_ok $line->[0], 'Slic3r::Point', 'line point is blessed'; + +{ + my $clone = $line->clone; + $clone->reverse; + is_deeply $clone->pp, [ reverse @$points ], 'reverse'; +} + +{ + my $line2 = Slic3r::Line->new($line->a->clone, $line->b->clone); + is_deeply $line2->pp, $points, 'line roundtrip with cloned points'; +} + +{ + my $clone = $line->clone; + $clone->translate(10, -5); + is_deeply $clone->pp, [ + [110, 95], + [210, 95], + ], 'translate'; +} + +__END__ diff --git a/xs/xsp/ExtrusionLoop.xsp b/xs/xsp/ExtrusionLoop.xsp index 22d6b37d1..9bcd196a3 100644 --- a/xs/xsp/ExtrusionLoop.xsp +++ b/xs/xsp/ExtrusionLoop.xsp @@ -11,6 +11,8 @@ %code{% RETVAL = THIS->polygon.to_SV(); %}; SV* pp() %code{% RETVAL = THIS->polygon.to_SV_pureperl(); %}; + void reverse() + %code{% THIS->polygon.reverse(); %}; ExtrusionPath* split_at_index(int index) %code{% const char* CLASS = "Slic3r::ExtrusionPath"; RETVAL = THIS->split_at_index(index); %}; ExtrusionPath* split_at_first_point() diff --git a/xs/xsp/ExtrusionPath.xsp b/xs/xsp/ExtrusionPath.xsp index 782530454..3d139d53b 100644 --- a/xs/xsp/ExtrusionPath.xsp +++ b/xs/xsp/ExtrusionPath.xsp @@ -14,6 +14,8 @@ void pop_back() %code{% THIS->polyline.points.pop_back(); %}; void reverse(); + Lines lines() + %code{% RETVAL = THIS->polyline.lines(); %}; %{ ExtrusionPath* diff --git a/xs/xsp/Line.xsp b/xs/xsp/Line.xsp index f80b27d38..4a8a1da56 100644 --- a/xs/xsp/Line.xsp +++ b/xs/xsp/Line.xsp @@ -18,6 +18,8 @@ Point* b() %code{% const char* CLASS = "Slic3r::Point"; RETVAL = new Point(THIS->b); %}; void reverse(); + void scale(double factor); + void translate(double x, double y); %{ Line* @@ -29,6 +31,16 @@ Line::new(...) RETVAL->b.from_SV_check( ST(2) ); OUTPUT: RETVAL + } + +void +Line::rotate(angle, center_sv) + double angle; + SV* center_sv; + CODE: + Point center; + center.from_SV_check(center_sv); + THIS->rotate(angle, ¢er); %} }; diff --git a/xs/xsp/Point.xsp b/xs/xsp/Point.xsp index e9a4c680c..6d308d62b 100644 --- a/xs/xsp/Point.xsp +++ b/xs/xsp/Point.xsp @@ -6,7 +6,7 @@ %} %name{Slic3r::Point} class Point { - Point(unsigned long _x = 0, unsigned long _y = 0); + Point(long _x = 0, long _y = 0); ~Point(); Point* clone() %code{% const char* CLASS = "Slic3r::Point"; RETVAL = new Point(*THIS); %}; @@ -14,9 +14,11 @@ void translate(double x, double y); SV* arrayref() %code{% RETVAL = THIS->to_SV_pureperl(); %}; - unsigned long x() + SV* pp() + %code{% RETVAL = THIS->to_SV_pureperl(); %}; + long x() %code{% RETVAL = THIS->x; %}; - unsigned long y() + long y() %code{% RETVAL = THIS->y; %}; %{ diff --git a/xs/xsp/Polygon.xsp b/xs/xsp/Polygon.xsp index 0c5e7a1a5..4217d1e80 100644 --- a/xs/xsp/Polygon.xsp +++ b/xs/xsp/Polygon.xsp @@ -15,6 +15,7 @@ %code{% RETVAL = THIS->to_SV_pureperl(); %}; void scale(double factor); void translate(double x, double y); + void reverse(); Lines lines(); Polyline* split_at_index(int index) %code{% const char* CLASS = "Slic3r::Polyline"; RETVAL = THIS->split_at_index(index); %}; diff --git a/xs/xsp/Polyline.xsp b/xs/xsp/Polyline.xsp index a2ce8a329..c2e0a548b 100644 --- a/xs/xsp/Polyline.xsp +++ b/xs/xsp/Polyline.xsp @@ -38,7 +38,7 @@ Polyline::append(...) CODE: for (unsigned int i = 1; i < items; i++) { Point p; - p.from_SV_check( ST(1) ); + p.from_SV_check( ST(i) ); THIS->points.push_back(p); }