New semantics for ExtrusionLoop objects. Early processing of perimeter overhangs for paralellizing such work and making G-code export lighter. Lots of refactoring. This should fix a number of minor bugs, including reversals of perimeter overhangs.
This commit is contained in:
parent
d2d885fc53
commit
c37ef2f18b
27 changed files with 618 additions and 423 deletions
|
@ -2,16 +2,4 @@ package Slic3r::ExtrusionLoop;
|
|||
use strict;
|
||||
use warnings;
|
||||
|
||||
sub split_at {
|
||||
my $self = shift;
|
||||
|
||||
return Slic3r::ExtrusionPath->new(
|
||||
polyline => $self->polygon->split_at(@_),
|
||||
role => $self->role,
|
||||
mm3_per_mm => $self->mm3_per_mm,
|
||||
width => $self->width,
|
||||
height => $self->height,
|
||||
);
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
|
@ -19,7 +19,6 @@ has 'layer' => (is => 'rw');
|
|||
has 'region' => (is => 'rw');
|
||||
has '_layer_islands' => (is => 'rw');
|
||||
has '_upper_layer_islands' => (is => 'rw');
|
||||
has '_lower_layer_slices' => (is => 'ro', default => sub { Slic3r::ExPolygon::Collection->new });
|
||||
has 'shift_x' => (is => 'rw', default => sub {0} );
|
||||
has 'shift_y' => (is => 'rw', default => sub {0} );
|
||||
has 'z' => (is => 'rw');
|
||||
|
@ -111,17 +110,6 @@ sub change_layer {
|
|||
# avoid computing islands and overhangs if they're not needed
|
||||
$self->_layer_islands($layer->islands);
|
||||
$self->_upper_layer_islands($layer->upper_layer ? $layer->upper_layer->islands : []);
|
||||
$self->_lower_layer_slices->clear;
|
||||
if ($layer->lower_layer && ($self->print_config->overhangs || $self->print_config->start_perimeters_at_non_overhang)) {
|
||||
# We consider overhang any part where the entire nozzle diameter is not supported by the
|
||||
# lower layer, so we take lower slices and offset them by half the nozzle diameter used
|
||||
# in the current layer
|
||||
my $max_nozzle_diameter = max(map $layer->print->config->get_at('nozzle_diameter', $_->region->config->perimeter_extruder-1), @{$layer->regions});
|
||||
$self->_lower_layer_slices->append(
|
||||
# clone ExPolygons because they come from Surface objects but will be used outside here
|
||||
@{offset_ex([ map @$_, @{$layer->lower_layer->slices} ], +scale($max_nozzle_diameter/2))},
|
||||
);
|
||||
}
|
||||
if ($self->print_config->avoid_crossing_perimeters) {
|
||||
$self->layer_mp(Slic3r::GCode::MotionPlanner->new(
|
||||
islands => union_ex([ map @$_, @{$layer->slices} ], 1),
|
||||
|
@ -199,17 +187,17 @@ sub extrude_loop {
|
|||
|
||||
# extrude all loops ccw
|
||||
my $was_clockwise = $loop->make_counter_clockwise;
|
||||
my $polygon = $loop->polygon;
|
||||
|
||||
# find candidate starting points
|
||||
# start looking for concave vertices not being overhangs
|
||||
my $polygon = $loop->polygon;
|
||||
my @concave = ();
|
||||
if ($self->print_config->start_perimeters_at_concave_points) {
|
||||
@concave = $polygon->concave_points;
|
||||
}
|
||||
my @candidates = ();
|
||||
if ($self->print_config->start_perimeters_at_non_overhang) {
|
||||
@candidates = grep $self->_lower_layer_slices->contains_point($_), @concave;
|
||||
@candidates = grep !$loop->has_overhang_point($_), @concave;
|
||||
}
|
||||
if (!@candidates) {
|
||||
# if none, look for any concave vertex
|
||||
|
@ -217,11 +205,11 @@ sub extrude_loop {
|
|||
if (!@candidates) {
|
||||
# if none, look for any non-overhang vertex
|
||||
if ($self->print_config->start_perimeters_at_non_overhang) {
|
||||
@candidates = grep $self->_lower_layer_slices->contains_point($_), @$polygon;
|
||||
@candidates = grep !$loop->has_overhang_point($_), @$polygon;
|
||||
}
|
||||
if (!@candidates) {
|
||||
# if none, all points are valid candidates
|
||||
@candidates = @{$polygon};
|
||||
@candidates = @$polygon;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -234,71 +222,49 @@ sub extrude_loop {
|
|||
$last_pos->rotate(rand(2*PI), $self->print_config->print_center);
|
||||
}
|
||||
|
||||
# split the loop at the starting point and make a path
|
||||
my $start_at = $last_pos->nearest_point(\@candidates);
|
||||
my $extrusion_path = $loop->split_at($start_at);
|
||||
# split the loop at the starting point
|
||||
$loop->split_at($last_pos->nearest_point(\@candidates));
|
||||
|
||||
# clip the path to avoid the extruder to get exactly on the first point of the loop;
|
||||
# if polyline was shorter than the clipping distance we'd get a null polyline, so
|
||||
# we discard it in that case
|
||||
$extrusion_path->clip_end(scale($self->extruder->nozzle_diameter) * &Slic3r::LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER)
|
||||
if $self->enable_loop_clipping;
|
||||
return '' if !@{$extrusion_path->polyline};
|
||||
my $clip_length = $self->enable_loop_clipping
|
||||
? scale($self->extruder->nozzle_diameter) * &Slic3r::LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER
|
||||
: 0;
|
||||
|
||||
my @paths = ();
|
||||
# detect overhanging/bridging perimeters
|
||||
if ($self->layer->print->config->overhangs && $extrusion_path->is_perimeter && $self->_lower_layer_slices->count > 0) {
|
||||
# get non-overhang paths by intersecting this loop with the grown lower slices
|
||||
push @paths,
|
||||
map $_->clone,
|
||||
@{$extrusion_path->intersect_expolygons($self->_lower_layer_slices)};
|
||||
|
||||
# get overhang paths by checking what parts of this loop fall
|
||||
# outside the grown lower slices (thus where the distance between
|
||||
# the loop centerline and original lower slices is >= half nozzle diameter
|
||||
foreach my $path (@{$extrusion_path->subtract_expolygons($self->_lower_layer_slices)}) {
|
||||
$path = $path->clone;
|
||||
$path->role(EXTR_ROLE_OVERHANG_PERIMETER);
|
||||
$path->mm3_per_mm($self->region->flow(FLOW_ROLE_PERIMETER, -1, 1, 0, undef, $self->layer->object)->mm3_per_mm(-1));
|
||||
push @paths, $path;
|
||||
}
|
||||
|
||||
# reapply the nearest point search for starting point
|
||||
# (clone because the collection gets DESTROY'ed)
|
||||
my $collection = Slic3r::ExtrusionPath::Collection->new(@paths);
|
||||
@paths = map $_->clone, @{$collection->chained_path_from($start_at, 1)};
|
||||
} else {
|
||||
push @paths, $extrusion_path;
|
||||
}
|
||||
# get paths
|
||||
my @paths = @{$loop->clip_end($clip_length)};
|
||||
return '' if !@paths;
|
||||
|
||||
# apply the small perimeter speed
|
||||
my %params = ();
|
||||
if ($extrusion_path->is_perimeter && abs($extrusion_path->length) <= &Slic3r::SMALL_PERIMETER_LENGTH) {
|
||||
if ($paths[0]->is_perimeter && abs($loop->length) <= &Slic3r::SMALL_PERIMETER_LENGTH) {
|
||||
$params{speed} = 'small_perimeter';
|
||||
}
|
||||
|
||||
# extrude along the path
|
||||
my $gcode = join '', map $self->extrude_path($_, $description, %params), @paths;
|
||||
$self->wipe_path($extrusion_path->polyline->clone) if $self->enable_wipe;
|
||||
$self->wipe_path($paths[-1]->polyline->clone) if $self->enable_wipe; # TODO: don't limit wipe to last path
|
||||
|
||||
# make a little move inwards before leaving loop
|
||||
if ($loop->role == EXTR_ROLE_EXTERNAL_PERIMETER && defined $self->layer && $self->region->config->perimeters > 1) {
|
||||
if ($paths[-1]->role == EXTR_ROLE_EXTERNAL_PERIMETER && defined $self->layer && $self->region->config->perimeters > 1) {
|
||||
my $last_path_polyline = $paths[-1]->polyline;
|
||||
# detect angle between last and first segment
|
||||
# the side depends on the original winding order of the polygon (left for contours, right for holes)
|
||||
my @points = $was_clockwise ? (-2, 1) : (1, -2);
|
||||
my $angle = Slic3r::Geometry::angle3points(@{$extrusion_path->polyline}[0, @points]) / 3;
|
||||
my $angle = Slic3r::Geometry::angle3points(@$last_path_polyline[0, @points]) / 3;
|
||||
$angle *= -1 if $was_clockwise;
|
||||
|
||||
# create the destination point along the first segment and rotate it
|
||||
# we make sure we don't exceed the segment length because we don't know
|
||||
# the rotation of the second segment so we might cross the object boundary
|
||||
my $first_segment = Slic3r::Line->new(@{$extrusion_path->polyline}[0,1]);
|
||||
my $first_segment = Slic3r::Line->new(@$last_path_polyline[0,1]);
|
||||
my $distance = min(scale($self->extruder->nozzle_diameter), $first_segment->length);
|
||||
my $point = $first_segment->point_at($distance);
|
||||
$point->rotate($angle, $extrusion_path->first_point);
|
||||
$point->rotate($angle, $last_path_polyline->first_point);
|
||||
|
||||
# generate the travel move
|
||||
$gcode .= $self->travel_to($point, $loop->role, "move inwards before travel");
|
||||
$gcode .= $self->travel_to($point, $paths[-1]->role, "move inwards before travel");
|
||||
}
|
||||
|
||||
return $gcode;
|
||||
|
|
|
@ -8,6 +8,6 @@ our @EXPORT_OK = qw(offset offset_ex
|
|||
diff_ex diff union_ex intersection_ex xor_ex JT_ROUND JT_MITER
|
||||
JT_SQUARE is_counter_clockwise union_pt offset2 offset2_ex
|
||||
intersection intersection_pl diff_pl union CLIPPER_OFFSET_SCALE
|
||||
union_pt_chained);
|
||||
union_pt_chained diff_ppl intersection_ppl);
|
||||
|
||||
1;
|
||||
|
|
|
@ -3,7 +3,8 @@ use Moo;
|
|||
|
||||
use List::Util qw(first sum max min);
|
||||
use Slic3r::Geometry qw(PI unscale scaled_epsilon rad2deg epsilon directions_parallel_within);
|
||||
use Slic3r::Geometry::Clipper qw(intersection_pl intersection_ex union offset diff_pl union_ex);
|
||||
use Slic3r::Geometry::Clipper qw(intersection_pl intersection_ex union offset diff_pl union_ex
|
||||
intersection_ppl);
|
||||
|
||||
has 'expolygon' => (is => 'ro', required => 1);
|
||||
has 'lower_slices' => (is => 'rw', required => 1); # ExPolygons or ExPolygonCollection
|
||||
|
@ -26,7 +27,7 @@ sub BUILD {
|
|||
foreach my $lower (@{$self->lower_slices}) {
|
||||
# turn bridge contour and holes into polylines and then clip them
|
||||
# with each lower slice's contour
|
||||
push @$edges, map @{$_->clip_as_polyline([$lower->contour])}, @$grown;
|
||||
push @$edges, @{intersection_ppl($grown, [ $lower->contour ])};
|
||||
}
|
||||
Slic3r::debugf " bridge has %d support(s)\n", scalar(@$edges);
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ use Slic3r::Flow ':roles';
|
|||
use Slic3r::Geometry qw(PI A B scale unscale chained_path points_coincide);
|
||||
use Slic3r::Geometry::Clipper qw(union_ex diff_ex intersection_ex
|
||||
offset offset_ex offset2 offset2_ex union_pt diff intersection
|
||||
union diff);
|
||||
union diff intersection_ppl diff_ppl);
|
||||
use Slic3r::Surface ':types';
|
||||
|
||||
has 'layer' => (
|
||||
|
@ -63,6 +63,8 @@ sub make_perimeters {
|
|||
|
||||
my $perimeter_flow = $self->flow(FLOW_ROLE_PERIMETER);
|
||||
my $mm3_per_mm = $perimeter_flow->mm3_per_mm($self->height);
|
||||
my $overhang_flow = $self->region->flow(FLOW_ROLE_PERIMETER, -1, 1, 0, undef, $self->layer->object);
|
||||
my $mm3_per_mm_overhang = $overhang_flow->mm3_per_mm(-1);
|
||||
my $pwidth = $perimeter_flow->scaled_width;
|
||||
my $pspacing = $perimeter_flow->scaled_spacing;
|
||||
my $solid_infill_flow = $self->flow(FLOW_ROLE_SOLID_INFILL);
|
||||
|
@ -229,6 +231,19 @@ sub make_perimeters {
|
|||
my $contours_pt = union_pt(\@contours);
|
||||
my $holes_pt = union_pt(\@holes);
|
||||
|
||||
# prepare grown lower layer slices for overhang detection
|
||||
my $lower_slices = Slic3r::ExPolygon::Collection->new;
|
||||
if ($self->layer->lower_layer && $self->layer->print->config->overhangs) {
|
||||
# We consider overhang any part where the entire nozzle diameter is not supported by the
|
||||
# lower layer, so we take lower slices and offset them by half the nozzle diameter used
|
||||
# in the current layer
|
||||
my $nozzle_diameter = $self->layer->print->config->get_at('nozzle_diameter', $self->region->config->perimeter_extruder-1);
|
||||
$lower_slices->append(
|
||||
@{offset_ex([ map @$_, @{$self->layer->lower_layer->slices} ], scale +$nozzle_diameter/2)},
|
||||
);
|
||||
}
|
||||
my $lower_slices_p = $lower_slices->polygons;
|
||||
|
||||
# prepare a coderef for traversing the PolyTree object
|
||||
# external contours are root items of $contours_pt
|
||||
# internal contours are the ones next to external
|
||||
|
@ -242,15 +257,6 @@ sub make_perimeters {
|
|||
foreach my $polynode (@$polynodes) {
|
||||
my $polygon = ($polynode->{outer} // $polynode->{hole})->clone;
|
||||
|
||||
# return ccw contours and cw holes
|
||||
# GCode.pm will convert all of them to ccw, but it needs to know
|
||||
# what the holes are in order to compute the correct inwards move
|
||||
if ($is_contour) {
|
||||
$polygon->make_counter_clockwise;
|
||||
} else {
|
||||
$polygon->make_clockwise;
|
||||
}
|
||||
|
||||
my $role = EXTR_ROLE_PERIMETER;
|
||||
if ($is_contour ? $depth == 0 : !@{ $polynode->{children} }) {
|
||||
# external perimeters are root level in case of contours
|
||||
|
@ -260,13 +266,62 @@ sub make_perimeters {
|
|||
$role = EXTR_ROLE_CONTOUR_INTERNAL_PERIMETER;
|
||||
}
|
||||
|
||||
$collection->append(Slic3r::ExtrusionLoop->new(
|
||||
polygon => $polygon,
|
||||
role => $role,
|
||||
mm3_per_mm => $mm3_per_mm,
|
||||
width => $perimeter_flow->width,
|
||||
height => $self->height,
|
||||
));
|
||||
# detect overhanging/bridging perimeters
|
||||
my @paths = ();
|
||||
if ($self->layer->print->config->overhangs && $lower_slices->count > 0) {
|
||||
# get non-overhang paths by intersecting this loop with the grown lower slices
|
||||
foreach my $polyline (@{ intersection_ppl([ $polygon ], $lower_slices_p) }) {
|
||||
push @paths, Slic3r::ExtrusionPath->new(
|
||||
polyline => $polyline,
|
||||
role => $role,
|
||||
mm3_per_mm => $mm3_per_mm,
|
||||
width => $perimeter_flow->width,
|
||||
height => $self->height,
|
||||
);
|
||||
}
|
||||
|
||||
# get overhang paths by checking what parts of this loop fall
|
||||
# outside the grown lower slices (thus where the distance between
|
||||
# the loop centerline and original lower slices is >= half nozzle diameter
|
||||
foreach my $polyline (@{ diff_ppl([ $polygon ], $lower_slices_p) }) {
|
||||
push @paths, Slic3r::ExtrusionPath->new(
|
||||
polyline => $polyline,
|
||||
role => EXTR_ROLE_OVERHANG_PERIMETER,
|
||||
mm3_per_mm => $mm3_per_mm_overhang,
|
||||
width => $overhang_flow->width,
|
||||
height => $self->height,
|
||||
);
|
||||
}
|
||||
|
||||
# reapply the nearest point search for starting point
|
||||
# (clone because the collection gets DESTROY'ed)
|
||||
# We allow polyline reversal because Clipper may have randomly
|
||||
# reversed polylines during clipping.
|
||||
my $collection = Slic3r::ExtrusionPath::Collection->new(@paths);
|
||||
@paths = map $_->clone, @{$collection->chained_path(0)};
|
||||
} else {
|
||||
push @paths, Slic3r::ExtrusionPath->new(
|
||||
polyline => $polygon->split_at_first_point,
|
||||
role => $role,
|
||||
mm3_per_mm => $mm3_per_mm,
|
||||
width => $perimeter_flow->width,
|
||||
height => $self->height,
|
||||
);
|
||||
}
|
||||
my $loop = Slic3r::ExtrusionLoop->new_from_paths(@paths);
|
||||
|
||||
# return ccw contours and cw holes
|
||||
# GCode.pm will convert all of them to ccw, but it needs to know
|
||||
# what the holes are in order to compute the correct inwards move
|
||||
# We do this on the final Loop object instead of the polygon because
|
||||
# overhang clipping might have reversed its order since Clipper does
|
||||
# not preserve polyline orientation.
|
||||
if ($is_contour) {
|
||||
$loop->make_counter_clockwise;
|
||||
} else {
|
||||
$loop->make_clockwise;
|
||||
}
|
||||
$collection->append($loop);
|
||||
|
||||
# save the children
|
||||
push @children, $polynode->{children};
|
||||
|
@ -367,11 +422,16 @@ sub _fill_gaps {
|
|||
Slic3r::debugf " %d gaps filled with extrusion width = %s\n", scalar @$this, $w
|
||||
if @$this;
|
||||
|
||||
return map {
|
||||
$_->isa('Slic3r::Polygon')
|
||||
? Slic3r::ExtrusionLoop->new(polygon => $_, %path_args)->split_at_first_point # should we keep these as loops?
|
||||
: Slic3r::ExtrusionPath->new(polyline => $_, %path_args),
|
||||
} @polylines;
|
||||
for my $i (0..$#polylines) {
|
||||
if ($polylines[$i]->isa('Slic3r::Polygon')) {
|
||||
my $loop = Slic3r::ExtrusionLoop->new;
|
||||
$loop->append(Slic3r::ExtrusionPath->new(polyline => $polylines[$i]->split_at_first_point, %path_args));
|
||||
$polylines[$i] = $loop;
|
||||
} else {
|
||||
$polylines[$i] = Slic3r::ExtrusionPath->new(polyline => $polylines[$i], %path_args);
|
||||
}
|
||||
}
|
||||
return @polylines;
|
||||
}
|
||||
|
||||
sub prepare_fill_surfaces {
|
||||
|
|
|
@ -51,41 +51,4 @@ sub concave_points {
|
|||
-1 .. ($#points-1);
|
||||
}
|
||||
|
||||
sub clip_as_polyline {
|
||||
my ($self, $polygons) = @_;
|
||||
|
||||
my $self_pl = $self->split_at_first_point;
|
||||
|
||||
# Clipper will remove a polyline segment if first point coincides with last one.
|
||||
# Until that bug is not fixed upstream, we move one of those points slightly.
|
||||
$self_pl->[0]->translate(1, 0);
|
||||
|
||||
my @polylines = @{intersection_pl([$self_pl], $polygons)};
|
||||
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_with($self_pl->[-1]) && $polylines[-1][0]->coincides_with($self_pl->[0])) {
|
||||
my $p = $polylines[0]->clone;
|
||||
$p->pop_back;
|
||||
$p->append(@{$polylines[-1]});
|
||||
return [$p];
|
||||
}
|
||||
if ($polylines[0][0]->coincides_with($self_pl->[0]) && $polylines[-1][-1]->coincides_with($self_pl->[-1])) {
|
||||
my $p = $polylines[-1]->clone;
|
||||
$p->pop_back;
|
||||
$p->append(@{$polylines[0]});
|
||||
return [$p];
|
||||
}
|
||||
}
|
||||
|
||||
return [ @polylines ];
|
||||
}
|
||||
|
||||
1;
|
|
@ -705,12 +705,14 @@ sub make_skirt {
|
|||
for (my $i = $self->config->skirts; $i > 0; $i--) {
|
||||
$distance += scale $spacing;
|
||||
my $loop = offset([$convex_hull], $distance, 1, JT_ROUND, scale(0.1))->[0];
|
||||
$self->skirt->append(Slic3r::ExtrusionLoop->new(
|
||||
polygon => Slic3r::Polygon->new(@$loop),
|
||||
role => EXTR_ROLE_SKIRT,
|
||||
mm3_per_mm => $mm3_per_mm,
|
||||
width => $flow->width,
|
||||
height => $first_layer_height,
|
||||
$self->skirt->append(Slic3r::ExtrusionLoop->new_from_paths(
|
||||
Slic3r::ExtrusionPath->new(
|
||||
polyline => Slic3r::Polygon->new(@$loop)->split_at_first_point,
|
||||
role => EXTR_ROLE_SKIRT,
|
||||
mm3_per_mm => $mm3_per_mm,
|
||||
width => $flow->width,
|
||||
height => $first_layer_height,
|
||||
),
|
||||
));
|
||||
|
||||
if ($self->config->min_skirt_length > 0) {
|
||||
|
@ -788,12 +790,14 @@ sub make_brim {
|
|||
push @loops, @{offset2(\@islands, ($i + 0.5) * $flow->scaled_spacing, -1.0 * $flow->scaled_spacing, 100000, JT_SQUARE)};
|
||||
}
|
||||
|
||||
$self->brim->append(map Slic3r::ExtrusionLoop->new(
|
||||
polygon => Slic3r::Polygon->new(@$_),
|
||||
role => EXTR_ROLE_SKIRT,
|
||||
mm3_per_mm => $mm3_per_mm,
|
||||
width => $flow->width,
|
||||
height => $first_layer_height,
|
||||
$self->brim->append(map Slic3r::ExtrusionLoop->new_from_paths(
|
||||
Slic3r::ExtrusionPath->new(
|
||||
polyline => Slic3r::Polygon->new(@$_)->split_at_first_point,
|
||||
role => EXTR_ROLE_SKIRT,
|
||||
mm3_per_mm => $mm3_per_mm,
|
||||
width => $flow->width,
|
||||
height => $first_layer_height,
|
||||
),
|
||||
), reverse @{union_pt_chained(\@loops)});
|
||||
}
|
||||
|
||||
|
@ -913,7 +917,7 @@ sub write_gcode {
|
|||
|
||||
# calculate wiping points if needed
|
||||
if ($self->config->ooze_prevention) {
|
||||
my @skirt_points = map @$_, @{$self->skirt};
|
||||
my @skirt_points = map @$_, map @$_, @{$self->skirt};
|
||||
if (@skirt_points) {
|
||||
my $outer_skirt = convex_hull(\@skirt_points);
|
||||
my @skirts = ();
|
||||
|
|
|
@ -70,14 +70,20 @@ sub generate {
|
|||
$self->clip_with_shape($base, $shape) if @$shape;
|
||||
|
||||
# Install support layers into object.
|
||||
push @{$object->support_layers}, map Slic3r::Layer::Support->new(
|
||||
object => $object,
|
||||
id => $_,
|
||||
height => ($_ == 0) ? $support_z->[$_] : ($support_z->[$_] - $support_z->[$_-1]),
|
||||
print_z => $support_z->[$_],
|
||||
slice_z => -1,
|
||||
slices => [],
|
||||
), 0 .. $#$support_z;
|
||||
for my $i (0 .. $#$support_z) {
|
||||
push @{$object->support_layers}, Slic3r::Layer::Support->new(
|
||||
object => $object,
|
||||
id => $i,
|
||||
height => ($i == 0) ? $support_z->[$i] : ($support_z->[$i] - $support_z->[$i-1]),
|
||||
print_z => $support_z->[$i],
|
||||
slice_z => -1,
|
||||
slices => [],
|
||||
);
|
||||
if ($i >= 1) {
|
||||
$object->support_layers->[-2]->upper_layer($object->support_layers->[-1]);
|
||||
$object->support_layers->[-1]->lower_layer($object->support_layers->[-2]);
|
||||
}
|
||||
}
|
||||
|
||||
# Generate the actual toolpaths and save them into each layer.
|
||||
$self->generate_toolpaths($object, $overhang, $contact, $interface, $base);
|
||||
|
|
|
@ -106,28 +106,12 @@ use overload
|
|||
'@{}' => sub { $_[0]->arrayref },
|
||||
'fallback' => 1;
|
||||
|
||||
sub new {
|
||||
my ($class, %args) = @_;
|
||||
sub new_from_paths {
|
||||
my ($class, @paths) = @_;
|
||||
|
||||
return $class->_new(
|
||||
$args{polygon}, # required
|
||||
$args{role}, # required
|
||||
$args{mm3_per_mm} // die("Missing required mm3_per_mm in ExtrusionLoop constructor"),
|
||||
$args{width} // -1,
|
||||
$args{height} // -1,
|
||||
);
|
||||
}
|
||||
|
||||
sub clone {
|
||||
my ($self, %args) = @_;
|
||||
|
||||
return __PACKAGE__->_new(
|
||||
$args{polygon} // $self->polygon,
|
||||
$args{role} // $self->role,
|
||||
$args{mm3_per_mm} // $self->mm3_per_mm,
|
||||
$args{width} // $self->width,
|
||||
$args{height} // $self->height,
|
||||
);
|
||||
my $loop = $class->new;
|
||||
$loop->append($_) for @paths;
|
||||
return $loop;
|
||||
}
|
||||
|
||||
package Slic3r::ExtrusionLoop::Ref;
|
||||
|
|
|
@ -300,23 +300,24 @@ void _clipper_do(const ClipperLib::ClipType clipType, const Slic3r::Polygons &su
|
|||
}
|
||||
|
||||
void _clipper_do(const ClipperLib::ClipType clipType, const Slic3r::Polylines &subject,
|
||||
const Slic3r::Polygons &clip, ClipperLib::PolyTree &retval, const ClipperLib::PolyFillType fillType)
|
||||
const Slic3r::Polygons &clip, ClipperLib::PolyTree &retval, const ClipperLib::PolyFillType fillType,
|
||||
const bool safety_offset_)
|
||||
{
|
||||
// read input
|
||||
ClipperLib::Paths* input_subject = new ClipperLib::Paths();
|
||||
ClipperLib::Paths* input_clip = new ClipperLib::Paths();
|
||||
Slic3rMultiPoints_to_ClipperPaths(subject, *input_subject);
|
||||
Slic3rMultiPoints_to_ClipperPaths(clip, *input_clip);
|
||||
ClipperLib::Paths input_subject, input_clip;
|
||||
Slic3rMultiPoints_to_ClipperPaths(subject, input_subject);
|
||||
Slic3rMultiPoints_to_ClipperPaths(clip, input_clip);
|
||||
|
||||
// perform safety offset
|
||||
if (safety_offset_) safety_offset(&input_clip);
|
||||
|
||||
// init Clipper
|
||||
ClipperLib::Clipper clipper;
|
||||
clipper.Clear();
|
||||
|
||||
// add polygons
|
||||
clipper.AddPaths(*input_subject, ClipperLib::ptSubject, false);
|
||||
delete input_subject;
|
||||
clipper.AddPaths(*input_clip, ClipperLib::ptClip, true);
|
||||
delete input_clip;
|
||||
clipper.AddPaths(input_subject, ClipperLib::ptSubject, false);
|
||||
clipper.AddPaths(input_clip, ClipperLib::ptClip, true);
|
||||
|
||||
// perform operation
|
||||
clipper.Execute(clipType, retval, fillType, fillType);
|
||||
|
@ -338,52 +339,114 @@ void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject,
|
|||
const Slic3r::Polygons &clip, Slic3r::ExPolygons &retval, bool safety_offset_)
|
||||
{
|
||||
// perform operation
|
||||
ClipperLib::PolyTree* polytree = new ClipperLib::PolyTree();
|
||||
_clipper_do<ClipperLib::PolyTree>(clipType, subject, clip, *polytree, ClipperLib::pftNonZero, safety_offset_);
|
||||
ClipperLib::PolyTree polytree;
|
||||
_clipper_do<ClipperLib::PolyTree>(clipType, subject, clip, polytree, ClipperLib::pftNonZero, safety_offset_);
|
||||
|
||||
// convert into ExPolygons
|
||||
PolyTreeToExPolygons(*polytree, retval);
|
||||
delete polytree;
|
||||
PolyTreeToExPolygons(polytree, retval);
|
||||
}
|
||||
|
||||
void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polylines &subject,
|
||||
const Slic3r::Polygons &clip, Slic3r::Polylines &retval)
|
||||
const Slic3r::Polygons &clip, Slic3r::Polylines &retval, bool safety_offset_)
|
||||
{
|
||||
// perform operation
|
||||
ClipperLib::PolyTree polytree;
|
||||
_clipper_do(clipType, subject, clip, polytree, ClipperLib::pftNonZero);
|
||||
_clipper_do(clipType, subject, clip, polytree, ClipperLib::pftNonZero, safety_offset_);
|
||||
|
||||
// convert into Polygons
|
||||
// convert into Polylines
|
||||
ClipperLib::Paths output;
|
||||
ClipperLib::PolyTreeToPaths(polytree, output);
|
||||
ClipperPaths_to_Slic3rMultiPoints(output, retval);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void diff(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, T &retval, bool safety_offset_)
|
||||
void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject,
|
||||
const Slic3r::Polygons &clip, Slic3r::Polylines &retval, bool safety_offset_)
|
||||
{
|
||||
// transform input polygons into polylines
|
||||
Slic3r::Polylines polylines;
|
||||
polylines.reserve(subject.size());
|
||||
for (Slic3r::Polygons::const_iterator polygon = subject.begin(); polygon != subject.end(); ++polygon)
|
||||
polylines.push_back(*polygon); // implicit call to split_at_first_point()
|
||||
|
||||
/* Clipper will remove a polyline segment if first point coincides with last one.
|
||||
Until that bug is not fixed upstream, we move one of those points slightly. */
|
||||
for (Slic3r::Polylines::iterator polyline = polylines.begin(); polyline != polylines.end(); ++polyline)
|
||||
polyline->points.front().translate(1, 0);
|
||||
|
||||
// perform clipping
|
||||
_clipper(clipType, polylines, clip, retval, safety_offset_);
|
||||
|
||||
// compensate for the above hack
|
||||
for (Slic3r::Polylines::iterator polyline = retval.begin(); polyline != retval.end(); ++polyline) {
|
||||
for (Slic3r::Polylines::iterator subj_polyline = polylines.begin(); subj_polyline != polylines.end(); ++subj_polyline) {
|
||||
// if first point of clipped line coincides with first point of subject line, compensate for hack
|
||||
if (polyline->points.front().coincides_with(subj_polyline->points.front())) {
|
||||
polyline->points.front().translate(-1, 0);
|
||||
break;
|
||||
}
|
||||
// since Clipper does not preserve orientation of polylines, check last point too
|
||||
if (polyline->points.back().coincides_with(subj_polyline->points.front())) {
|
||||
polyline->points.back().translate(-1, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 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 go through them in order
|
||||
to recombine continuous polylines. */
|
||||
for (size_t i = 0; i < retval.size(); ++i) {
|
||||
for (size_t j = i+1; j < retval.size(); ++j) {
|
||||
if (retval[i].points.back().coincides_with(retval[j].points.front())) {
|
||||
/* If last point of i coincides with first point of j,
|
||||
append points of j to i and delete j */
|
||||
retval[i].points.insert(retval[i].points.end(), retval[j].points.begin()+1, retval[j].points.end());
|
||||
retval.erase(retval.begin() + j);
|
||||
--j;
|
||||
} else if (retval[i].points.front().coincides_with(retval[j].points.back())) {
|
||||
/* If first point of i coincides with last point of j,
|
||||
prepend points of j to i and delete j */
|
||||
retval[i].points.insert(retval[i].points.begin(), retval[j].points.begin(), retval[j].points.end()-1);
|
||||
retval.erase(retval.begin() + j);
|
||||
--j;
|
||||
} else if (retval[i].points.front().coincides_with(retval[j].points.front())) {
|
||||
/* Since Clipper does not preserve orientation of polylines,
|
||||
also check the case when first point of i coincides with first point of j. */
|
||||
retval[j].reverse();
|
||||
retval[i].points.insert(retval[i].points.begin(), retval[j].points.begin(), retval[j].points.end()-1);
|
||||
retval.erase(retval.begin() + j);
|
||||
--j;
|
||||
} else if (retval[i].points.back().coincides_with(retval[j].points.back())) {
|
||||
/* Since Clipper does not preserve orientation of polylines,
|
||||
also check the case when last point of i coincides with last point of j. */
|
||||
retval[j].reverse();
|
||||
retval[i].points.insert(retval[i].points.end(), retval[j].points.begin()+1, retval[j].points.end());
|
||||
retval.erase(retval.begin() + j);
|
||||
--j;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class SubjectType, class ResultType>
|
||||
void diff(const SubjectType &subject, const Slic3r::Polygons &clip, ResultType &retval, bool safety_offset_)
|
||||
{
|
||||
_clipper(ClipperLib::ctDifference, subject, clip, retval, safety_offset_);
|
||||
}
|
||||
template void diff<Slic3r::ExPolygons>(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::ExPolygons &retval, bool safety_offset_);
|
||||
template void diff<Slic3r::Polygons>(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::Polygons &retval, bool safety_offset_);
|
||||
template void diff<Slic3r::Polygons, Slic3r::ExPolygons>(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::ExPolygons &retval, bool safety_offset_);
|
||||
template void diff<Slic3r::Polygons, Slic3r::Polygons>(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::Polygons &retval, bool safety_offset_);
|
||||
template void diff<Slic3r::Polygons, Slic3r::Polylines>(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::Polylines &retval, bool safety_offset_);
|
||||
template void diff<Slic3r::Polylines, Slic3r::Polylines>(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, Slic3r::Polylines &retval, bool safety_offset_);
|
||||
|
||||
void diff(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, Slic3r::Polylines &retval)
|
||||
{
|
||||
_clipper(ClipperLib::ctDifference, subject, clip, retval);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, T &retval, bool safety_offset_)
|
||||
template <class SubjectType, class ResultType>
|
||||
void intersection(const SubjectType &subject, const Slic3r::Polygons &clip, ResultType &retval, bool safety_offset_)
|
||||
{
|
||||
_clipper(ClipperLib::ctIntersection, subject, clip, retval, safety_offset_);
|
||||
}
|
||||
template void intersection<Slic3r::ExPolygons>(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::ExPolygons &retval, bool safety_offset_);
|
||||
template void intersection<Slic3r::Polygons>(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::Polygons &retval, bool safety_offset_);
|
||||
|
||||
void intersection(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, Slic3r::Polylines &retval)
|
||||
{
|
||||
_clipper(ClipperLib::ctIntersection, subject, clip, retval);
|
||||
}
|
||||
template void intersection<Slic3r::Polygons, Slic3r::ExPolygons>(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::ExPolygons &retval, bool safety_offset_);
|
||||
template void intersection<Slic3r::Polygons, Slic3r::Polygons>(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::Polygons &retval, bool safety_offset_);
|
||||
template void intersection<Slic3r::Polygons, Slic3r::Polylines>(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::Polylines &retval, bool safety_offset_);
|
||||
template void intersection<Slic3r::Polylines, Slic3r::Polylines>(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, Slic3r::Polylines &retval, bool safety_offset_);
|
||||
|
||||
void xor_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::ExPolygons &retval,
|
||||
bool safety_offset_)
|
||||
|
@ -492,24 +555,19 @@ void simplify_polygons(const Slic3r::Polygons &subject, Slic3r::ExPolygons &retv
|
|||
PolyTreeToExPolygons(polytree, retval);
|
||||
}
|
||||
|
||||
void safety_offset(ClipperLib::Paths* &subject)
|
||||
void safety_offset(ClipperLib::Paths* paths)
|
||||
{
|
||||
// scale input
|
||||
scaleClipperPolygons(*subject, CLIPPER_OFFSET_SCALE);
|
||||
scaleClipperPolygons(*paths, CLIPPER_OFFSET_SCALE);
|
||||
|
||||
// perform offset (delta = scale 1e-05)
|
||||
ClipperLib::Paths* retval = new ClipperLib::Paths();
|
||||
ClipperLib::ClipperOffset co;
|
||||
co.MiterLimit = 2;
|
||||
co.AddPaths(*subject, ClipperLib::jtMiter, ClipperLib::etClosedPolygon);
|
||||
co.Execute(*retval, 10.0 * CLIPPER_OFFSET_SCALE);
|
||||
co.AddPaths(*paths, ClipperLib::jtMiter, ClipperLib::etClosedPolygon);
|
||||
co.Execute(*paths, 10.0 * CLIPPER_OFFSET_SCALE);
|
||||
|
||||
// unscale output
|
||||
scaleClipperPolygons(*retval, 1.0/CLIPPER_OFFSET_SCALE);
|
||||
|
||||
// delete original data and switch pointer
|
||||
delete subject;
|
||||
subject = retval;
|
||||
scaleClipperPolygons(*paths, 1.0/CLIPPER_OFFSET_SCALE);
|
||||
}
|
||||
|
||||
///////////////////////
|
||||
|
|
|
@ -70,7 +70,7 @@ template <class T>
|
|||
void _clipper_do(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject,
|
||||
const Slic3r::Polygons &clip, T &retval, bool safety_offset_);
|
||||
void _clipper_do(ClipperLib::ClipType clipType, const Slic3r::Polylines &subject,
|
||||
const Slic3r::Polygons &clip, ClipperLib::Paths &retval);
|
||||
const Slic3r::Polygons &clip, ClipperLib::Paths &retval, bool safety_offset_);
|
||||
void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject,
|
||||
const Slic3r::Polygons &clip, Slic3r::Polygons &retval, bool safety_offset_);
|
||||
void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject,
|
||||
|
@ -78,15 +78,11 @@ void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject,
|
|||
void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polylines &subject,
|
||||
const Slic3r::Polygons &clip, Slic3r::Polylines &retval);
|
||||
|
||||
template <class T>
|
||||
void diff(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, T &retval, bool safety_offset_ = false);
|
||||
template <class SubjectType, class ResultType>
|
||||
void diff(const SubjectType &subject, const Slic3r::Polygons &clip, ResultType &retval, bool safety_offset_ = false);
|
||||
|
||||
void diff(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, Slic3r::Polylines &retval);
|
||||
|
||||
template <class T>
|
||||
void intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, T &retval, bool safety_offset_ = false);
|
||||
|
||||
void intersection(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, Slic3r::Polylines &retval);
|
||||
template <class SubjectType, class ResultType>
|
||||
void intersection(const SubjectType &subject, const Slic3r::Polygons &clip, ResultType &retval, bool safety_offset_ = false);
|
||||
|
||||
void xor_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::ExPolygons &retval,
|
||||
bool safety_offset_ = false);
|
||||
|
@ -101,7 +97,7 @@ static void traverse_pt(ClipperLib::PolyNodes &nodes, Slic3r::Polygons &retval);
|
|||
void simplify_polygons(const Slic3r::Polygons &subject, Slic3r::Polygons &retval, bool preserve_collinear = false);
|
||||
void simplify_polygons(const Slic3r::Polygons &subject, Slic3r::ExPolygons &retval, bool preserve_collinear = false);
|
||||
|
||||
void safety_offset(ClipperLib::Paths* &subject);
|
||||
void safety_offset(ClipperLib::Paths* paths);
|
||||
|
||||
/////////////////
|
||||
|
||||
|
|
|
@ -223,7 +223,7 @@ ExPolygon::get_trapezoids2(Polygons* polygons) const
|
|||
|
||||
// intersect with this expolygon
|
||||
Polygons trapezoids;
|
||||
intersection(poly, *this, trapezoids);
|
||||
intersection<Polygons,Polygons>(poly, *this, trapezoids);
|
||||
|
||||
// append results to return value
|
||||
polygons->insert(polygons->end(), trapezoids.begin(), trapezoids.end());
|
||||
|
|
|
@ -10,31 +10,6 @@
|
|||
|
||||
namespace Slic3r {
|
||||
|
||||
bool
|
||||
ExtrusionEntity::is_perimeter() const
|
||||
{
|
||||
return this->role == erPerimeter
|
||||
|| this->role == erExternalPerimeter
|
||||
|| this->role == erOverhangPerimeter
|
||||
|| this->role == erContourInternalPerimeter;
|
||||
}
|
||||
|
||||
bool
|
||||
ExtrusionEntity::is_fill() const
|
||||
{
|
||||
return this->role == erFill
|
||||
|| this->role == erSolidFill
|
||||
|| this->role == erTopSolidFill;
|
||||
}
|
||||
|
||||
bool
|
||||
ExtrusionEntity::is_bridge() const
|
||||
{
|
||||
return this->role == erBrige
|
||||
|| this->role == erInternalBridge
|
||||
|| this->role == erOverhangPerimeter;
|
||||
}
|
||||
|
||||
ExtrusionPath*
|
||||
ExtrusionPath::clone() const
|
||||
{
|
||||
|
@ -64,7 +39,7 @@ ExtrusionPath::intersect_expolygons(const ExPolygonCollection &collection, Extru
|
|||
{
|
||||
// perform clipping
|
||||
Polylines clipped;
|
||||
intersection(this->polyline, collection, clipped);
|
||||
intersection<Polylines,Polylines>(this->polyline, collection, clipped);
|
||||
return this->_inflate_collection(clipped, retval);
|
||||
}
|
||||
|
||||
|
@ -73,7 +48,7 @@ ExtrusionPath::subtract_expolygons(const ExPolygonCollection &collection, Extrus
|
|||
{
|
||||
// perform clipping
|
||||
Polylines clipped;
|
||||
diff(this->polyline, collection, clipped);
|
||||
diff<Polylines,Polylines>(this->polyline, collection, clipped);
|
||||
return this->_inflate_collection(clipped, retval);
|
||||
}
|
||||
|
||||
|
@ -95,6 +70,31 @@ ExtrusionPath::length() const
|
|||
return this->polyline.length();
|
||||
}
|
||||
|
||||
bool
|
||||
ExtrusionPath::is_perimeter() const
|
||||
{
|
||||
return this->role == erPerimeter
|
||||
|| this->role == erExternalPerimeter
|
||||
|| this->role == erOverhangPerimeter
|
||||
|| this->role == erContourInternalPerimeter;
|
||||
}
|
||||
|
||||
bool
|
||||
ExtrusionPath::is_fill() const
|
||||
{
|
||||
return this->role == erFill
|
||||
|| this->role == erSolidFill
|
||||
|| this->role == erTopSolidFill;
|
||||
}
|
||||
|
||||
bool
|
||||
ExtrusionPath::is_bridge() const
|
||||
{
|
||||
return this->role == erBridge
|
||||
|| this->role == erInternalBridge
|
||||
|| this->role == erOverhangPerimeter;
|
||||
}
|
||||
|
||||
void
|
||||
ExtrusionPath::_inflate_collection(const Polylines &polylines, ExtrusionEntityCollection* collection) const
|
||||
{
|
||||
|
@ -106,8 +106,22 @@ ExtrusionPath::_inflate_collection(const Polylines &polylines, ExtrusionEntityCo
|
|||
}
|
||||
|
||||
#ifdef SLIC3RXS
|
||||
SV*
|
||||
ExtrusionPath::to_SV_ref() {
|
||||
SV* sv = newSV(0);
|
||||
sv_setref_pv( sv, perl_class_name_ref(this), this );
|
||||
return sv;
|
||||
}
|
||||
|
||||
SV*
|
||||
ExtrusionPath::to_SV_clone_ref() const {
|
||||
SV* sv = newSV(0);
|
||||
sv_setref_pv( sv, perl_class_name(this), new ExtrusionPath(*this) );
|
||||
return sv;
|
||||
}
|
||||
|
||||
REGISTER_CLASS(ExtrusionPath, "ExtrusionPath");
|
||||
#endif
|
||||
|
||||
std::string
|
||||
ExtrusionPath::gcode(Extruder* extruder, double e, double F,
|
||||
|
@ -155,12 +169,12 @@ ExtrusionPath::gcode(Extruder* extruder, double e, double F,
|
|||
|
||||
return stream.str();
|
||||
}
|
||||
#endif
|
||||
|
||||
ExtrusionLoop::ExtrusionLoop(const Polygon &polygon, ExtrusionRole role)
|
||||
ExtrusionLoop::operator Polygon() const
|
||||
{
|
||||
this->role = role;
|
||||
this->set_polygon(polygon);
|
||||
Polygon polygon;
|
||||
this->polygon(&polygon);
|
||||
return polygon;
|
||||
}
|
||||
|
||||
ExtrusionLoop*
|
||||
|
@ -169,32 +183,19 @@ ExtrusionLoop::clone() const
|
|||
return new ExtrusionLoop (*this);
|
||||
}
|
||||
|
||||
void
|
||||
ExtrusionLoop::split_at_index(int index, ExtrusionPath* path) const
|
||||
bool
|
||||
ExtrusionLoop::make_clockwise()
|
||||
{
|
||||
Polygon polygon;
|
||||
this->polygon(&polygon);
|
||||
|
||||
polygon.split_at_index(index, &path->polyline);
|
||||
|
||||
path->role = this->role;
|
||||
path->mm3_per_mm = this->mm3_per_mm;
|
||||
path->width = this->width;
|
||||
path->height = this->height;
|
||||
}
|
||||
|
||||
void
|
||||
ExtrusionLoop::split_at_first_point(ExtrusionPath* path) const
|
||||
{
|
||||
return this->split_at_index(0, path);
|
||||
Polygon polygon = *this;
|
||||
bool was_ccw = polygon.is_counter_clockwise();
|
||||
if (was_ccw) this->reverse();
|
||||
return was_ccw;
|
||||
}
|
||||
|
||||
bool
|
||||
ExtrusionLoop::make_counter_clockwise()
|
||||
{
|
||||
Polygon polygon;
|
||||
this->polygon(&polygon);
|
||||
|
||||
Polygon polygon = *this;
|
||||
bool was_cw = polygon.is_clockwise();
|
||||
if (was_cw) this->reverse();
|
||||
return was_cw;
|
||||
|
@ -203,41 +204,116 @@ ExtrusionLoop::make_counter_clockwise()
|
|||
void
|
||||
ExtrusionLoop::reverse()
|
||||
{
|
||||
for (Polylines::iterator polyline = this->polylines.begin(); polyline != this->polylines.end(); ++polyline)
|
||||
polyline->reverse();
|
||||
std::reverse(this->polylines.begin(), this->polylines.end());
|
||||
for (ExtrusionPaths::iterator path = this->paths.begin(); path != this->paths.end(); ++path)
|
||||
path->reverse();
|
||||
std::reverse(this->paths.begin(), this->paths.end());
|
||||
}
|
||||
|
||||
Point
|
||||
ExtrusionLoop::first_point() const
|
||||
{
|
||||
return this->polylines.front().points.front();
|
||||
return this->paths.front().polyline.points.front();
|
||||
}
|
||||
|
||||
Point
|
||||
ExtrusionLoop::last_point() const
|
||||
{
|
||||
return this->polylines.back().points.back(); // which coincides with first_point(), by the way
|
||||
}
|
||||
|
||||
void
|
||||
ExtrusionLoop::set_polygon(const Polygon &polygon)
|
||||
{
|
||||
Polyline polyline;
|
||||
polygon.split_at_first_point(&polyline);
|
||||
this->polylines.clear();
|
||||
this->polylines.push_back(polyline);
|
||||
return this->paths.back().polyline.points.back(); // which coincides with first_point(), by the way
|
||||
}
|
||||
|
||||
void
|
||||
ExtrusionLoop::polygon(Polygon* polygon) const
|
||||
{
|
||||
for (Polylines::const_iterator polyline = this->polylines.begin(); polyline != this->polylines.end(); ++polyline) {
|
||||
for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path) {
|
||||
// for each polyline, append all points except the last one (because it coincides with the first one of the next polyline)
|
||||
polygon->points.insert(polygon->points.end(), polyline->points.begin(), polyline->points.end()-1);
|
||||
polygon->points.insert(polygon->points.end(), path->polyline.points.begin(), path->polyline.points.end()-1);
|
||||
}
|
||||
}
|
||||
|
||||
double
|
||||
ExtrusionLoop::length() const
|
||||
{
|
||||
double len = 0;
|
||||
for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path)
|
||||
len += path->polyline.length();
|
||||
return len;
|
||||
}
|
||||
|
||||
void
|
||||
ExtrusionLoop::split_at(const Point &point)
|
||||
{
|
||||
for (ExtrusionPaths::iterator path = this->paths.begin(); path != this->paths.end(); ++path) {
|
||||
int idx = path->polyline.find_point(point);
|
||||
if (idx != -1) {
|
||||
if (this->paths.size() == 1) {
|
||||
// just change the order of points
|
||||
path->polyline.points.insert(path->polyline.points.end(), path->polyline.points.begin() + 1, path->polyline.points.begin() + idx + 1);
|
||||
path->polyline.points.erase(path->polyline.points.begin(), path->polyline.points.begin() + idx);
|
||||
} else {
|
||||
// if we have multiple paths we assume they have different types, so no need to
|
||||
// check for continuity as we do for the single path case above
|
||||
|
||||
// new paths list starts with the second half of current path
|
||||
ExtrusionPaths new_paths;
|
||||
{
|
||||
ExtrusionPath p = *path;
|
||||
p.polyline.points.erase(p.polyline.points.begin(), p.polyline.points.begin() + idx);
|
||||
if (!p.polyline.points.empty()) new_paths.push_back(p);
|
||||
}
|
||||
|
||||
// then we add all paths until the end of current path list
|
||||
new_paths.insert(new_paths.end(), this->paths.begin(), path); // not including this path
|
||||
|
||||
// then we add all paths since the beginning of current list up to the previous one
|
||||
new_paths.insert(new_paths.end(), path+1, this->paths.end()); // not including this path
|
||||
|
||||
// finally we add the first half of current path
|
||||
{
|
||||
ExtrusionPath p = *path;
|
||||
p.polyline.points.erase(p.polyline.points.begin() + idx + 1, p.polyline.points.end());
|
||||
if (!p.polyline.points.empty()) new_paths.push_back(p);
|
||||
}
|
||||
// we can now override the old path list with the new one and stop looping
|
||||
this->paths = new_paths;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
CONFESS("Point not found");
|
||||
}
|
||||
|
||||
void
|
||||
ExtrusionLoop::clip_end(double distance, ExtrusionPaths* paths) const
|
||||
{
|
||||
*paths = this->paths;
|
||||
|
||||
while (distance > 0 && !paths->empty()) {
|
||||
ExtrusionPath &last = paths->back();
|
||||
double len = last.length();
|
||||
if (len <= distance) {
|
||||
paths->pop_back();
|
||||
distance -= len;
|
||||
} else {
|
||||
last.polyline.clip_end(distance);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
ExtrusionLoop::has_overhang_point(const Point &point) const
|
||||
{
|
||||
for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path) {
|
||||
int pos = path->polyline.find_point(point);
|
||||
if (pos != -1) {
|
||||
// point belongs to this path
|
||||
// we consider it overhang only if it's not an endpoint
|
||||
return (path->is_bridge() && pos > 0 && pos != path->polyline.points.size()-1);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef SLIC3RXS
|
||||
REGISTER_CLASS(ExtrusionLoop, "ExtrusionLoop");
|
||||
#endif
|
||||
|
|
|
@ -19,7 +19,7 @@ enum ExtrusionRole {
|
|||
erFill,
|
||||
erSolidFill,
|
||||
erTopSolidFill,
|
||||
erBrige,
|
||||
erBridge,
|
||||
erInternalBridge,
|
||||
erSkirt,
|
||||
erSupportMaterial,
|
||||
|
@ -29,19 +29,11 @@ enum ExtrusionRole {
|
|||
class ExtrusionEntity
|
||||
{
|
||||
public:
|
||||
ExtrusionEntity() : mm3_per_mm(-1), width(-1), height(-1) {};
|
||||
virtual ExtrusionEntity* clone() const = 0;
|
||||
virtual ~ExtrusionEntity() {};
|
||||
ExtrusionRole role;
|
||||
double mm3_per_mm; // mm^3 of plastic per mm of linear head motion
|
||||
float width;
|
||||
float height;
|
||||
virtual void reverse() = 0;
|
||||
virtual Point first_point() const = 0;
|
||||
virtual Point last_point() const = 0;
|
||||
bool is_perimeter() const;
|
||||
bool is_fill() const;
|
||||
bool is_bridge() const;
|
||||
};
|
||||
|
||||
typedef std::vector<ExtrusionEntity*> ExtrusionEntitiesPtr;
|
||||
|
@ -49,8 +41,14 @@ typedef std::vector<ExtrusionEntity*> ExtrusionEntitiesPtr;
|
|||
class ExtrusionPath : public ExtrusionEntity
|
||||
{
|
||||
public:
|
||||
ExtrusionPath* clone() const;
|
||||
Polyline polyline;
|
||||
ExtrusionRole role;
|
||||
double mm3_per_mm; // mm^3 of plastic per mm of linear head motion
|
||||
float width;
|
||||
float height;
|
||||
|
||||
ExtrusionPath() : mm3_per_mm(-1), width(-1), height(-1) {};
|
||||
ExtrusionPath* clone() const;
|
||||
void reverse();
|
||||
Point first_point() const;
|
||||
Point last_point() const;
|
||||
|
@ -59,32 +57,41 @@ class ExtrusionPath : public ExtrusionEntity
|
|||
void clip_end(double distance);
|
||||
void simplify(double tolerance);
|
||||
double length() const;
|
||||
|
||||
#ifdef SLIC3RXS
|
||||
bool is_perimeter() const;
|
||||
bool is_fill() const;
|
||||
bool is_bridge() const;
|
||||
std::string gcode(Extruder* extruder, double e, double F,
|
||||
double xofs, double yofs, std::string extrusion_axis,
|
||||
std::string gcode_line_suffix) const;
|
||||
|
||||
#ifdef SLIC3RXS
|
||||
SV* to_SV_ref();
|
||||
SV* to_SV_clone_ref() const;
|
||||
#endif
|
||||
|
||||
private:
|
||||
void _inflate_collection(const Polylines &polylines, ExtrusionEntityCollection* collection) const;
|
||||
};
|
||||
|
||||
typedef std::vector<ExtrusionPath> ExtrusionPaths;
|
||||
|
||||
class ExtrusionLoop : public ExtrusionEntity
|
||||
{
|
||||
public:
|
||||
Polylines polylines;
|
||||
ExtrusionPaths paths;
|
||||
|
||||
ExtrusionLoop(const Polygon &polygon, ExtrusionRole role);
|
||||
operator Polygon() const;
|
||||
ExtrusionLoop* clone() const;
|
||||
void split_at_index(int index, ExtrusionPath* path) const;
|
||||
void split_at_first_point(ExtrusionPath* path) const;
|
||||
bool make_clockwise();
|
||||
bool make_counter_clockwise();
|
||||
void reverse();
|
||||
Point first_point() const;
|
||||
Point last_point() const;
|
||||
void set_polygon(const Polygon &polygon);
|
||||
void polygon(Polygon* polygon) const;
|
||||
double length() const;
|
||||
void split_at(const Point &point);
|
||||
void clip_end(double distance, ExtrusionPaths* paths) const;
|
||||
bool has_overhang_point(const Point &point) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -55,6 +55,15 @@ MultiPoint::is_valid() const
|
|||
return this->points.size() >= 2;
|
||||
}
|
||||
|
||||
int
|
||||
MultiPoint::find_point(const Point &point) const
|
||||
{
|
||||
for (Points::const_iterator it = this->points.begin(); it != this->points.end(); ++it) {
|
||||
if (it->coincides_with(point)) return it - this->points.begin();
|
||||
}
|
||||
return -1; // not found
|
||||
}
|
||||
|
||||
Points
|
||||
MultiPoint::_douglas_peucker(const Points &points, const double tolerance)
|
||||
{
|
||||
|
|
|
@ -21,6 +21,7 @@ class MultiPoint
|
|||
virtual Lines lines() const = 0;
|
||||
double length() const;
|
||||
bool is_valid() const;
|
||||
int find_point(const Point &point) const;
|
||||
static Points _douglas_peucker(const Points &points, const double tolerance);
|
||||
|
||||
#ifdef SLIC3RXS
|
||||
|
|
|
@ -15,6 +15,13 @@ Polygon::operator Polygons() const
|
|||
return pp;
|
||||
}
|
||||
|
||||
Polygon::operator Polyline() const
|
||||
{
|
||||
Polyline polyline;
|
||||
this->split_at_first_point(&polyline);
|
||||
return polyline;
|
||||
}
|
||||
|
||||
Point&
|
||||
Polygon::operator[](Points::size_type idx)
|
||||
{
|
||||
|
|
|
@ -15,6 +15,7 @@ typedef std::vector<Polygon> Polygons;
|
|||
class Polygon : public MultiPoint {
|
||||
public:
|
||||
operator Polygons() const;
|
||||
operator Polyline() const;
|
||||
Point& operator[](Points::size_type idx);
|
||||
const Point& operator[](Points::size_type idx) const;
|
||||
Point last_point() const;
|
||||
|
|
|
@ -3,37 +3,86 @@
|
|||
use strict;
|
||||
use warnings;
|
||||
|
||||
use List::Util qw(sum);
|
||||
use Slic3r::XS;
|
||||
use Test::More tests => 8;
|
||||
|
||||
my $square = [
|
||||
[100, 100],
|
||||
[200, 100],
|
||||
[200, 200],
|
||||
[100, 200],
|
||||
];
|
||||
|
||||
my $loop = Slic3r::ExtrusionLoop->new(
|
||||
polygon => Slic3r::Polygon->new(@$square),
|
||||
role => Slic3r::ExtrusionPath::EXTR_ROLE_EXTERNAL_PERIMETER,
|
||||
mm3_per_mm => 1,
|
||||
);
|
||||
isa_ok $loop, 'Slic3r::ExtrusionLoop';
|
||||
isa_ok $loop->polygon, 'Slic3r::Polygon', 'loop polygon';
|
||||
is_deeply $loop->polygon->pp, $square, 'polygon points roundtrip';
|
||||
|
||||
$loop = $loop->clone;
|
||||
|
||||
is $loop->role, Slic3r::ExtrusionPath::EXTR_ROLE_EXTERNAL_PERIMETER, 'role';
|
||||
$loop->role(Slic3r::ExtrusionPath::EXTR_ROLE_FILL);
|
||||
is $loop->role, Slic3r::ExtrusionPath::EXTR_ROLE_FILL, 'modify role';
|
||||
use Test::More tests => 30;
|
||||
|
||||
{
|
||||
my $path = $loop->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';
|
||||
my $square = [
|
||||
[100, 100],
|
||||
[200, 100],
|
||||
[200, 200],
|
||||
[100, 200],
|
||||
];
|
||||
my $square_p = Slic3r::Polygon->new(@$square);
|
||||
|
||||
my $loop = Slic3r::ExtrusionLoop->new;
|
||||
$loop->append(Slic3r::ExtrusionPath->new(
|
||||
polyline => $square_p->split_at_first_point,
|
||||
role => Slic3r::ExtrusionPath::EXTR_ROLE_EXTERNAL_PERIMETER,
|
||||
mm3_per_mm => 1,
|
||||
));
|
||||
|
||||
isa_ok $loop, 'Slic3r::ExtrusionLoop';
|
||||
isa_ok $loop->polygon, 'Slic3r::Polygon', 'loop polygon';
|
||||
is $loop->polygon->area, $square_p->area, 'polygon area';
|
||||
is $loop->length, $square_p->length(), 'loop length';
|
||||
|
||||
$loop = $loop->clone;
|
||||
|
||||
is scalar(@$loop), 1, 'loop contains one path';
|
||||
{
|
||||
my $path = $loop->[0];
|
||||
isa_ok $path, 'Slic3r::ExtrusionPath::Ref';
|
||||
is $path->role, Slic3r::ExtrusionPath::EXTR_ROLE_EXTERNAL_PERIMETER, 'role';
|
||||
$path->role(Slic3r::ExtrusionPath::EXTR_ROLE_FILL);
|
||||
is $path->role, Slic3r::ExtrusionPath::EXTR_ROLE_FILL, 'modify role';
|
||||
}
|
||||
|
||||
$loop->split_at($square_p->[2]);
|
||||
is scalar(@$loop), 1, 'splitting a single-path loop results in a single path';
|
||||
is scalar(@{$loop->[0]->polyline}), 5, 'path has correct number of points';
|
||||
ok $loop->[0]->polyline->[0]->coincides_with($square_p->[2]), 'expected point order';
|
||||
ok $loop->[0]->polyline->[1]->coincides_with($square_p->[3]), 'expected point order';
|
||||
ok $loop->[0]->polyline->[2]->coincides_with($square_p->[0]), 'expected point order';
|
||||
ok $loop->[0]->polyline->[3]->coincides_with($square_p->[1]), 'expected point order';
|
||||
ok $loop->[0]->polyline->[4]->coincides_with($square_p->[2]), 'expected point order';
|
||||
}
|
||||
|
||||
{
|
||||
my $polyline1 = Slic3r::Polyline->new([100,100], [200,100], [200,200]);
|
||||
my $polyline2 = Slic3r::Polyline->new([200,200], [100,200], [100,100]);
|
||||
|
||||
is_deeply $loop->split_at_index(2)->polyline->pp, [ @$square[2,3,0,1,2] ], 'split_at_index';
|
||||
my $loop = Slic3r::ExtrusionLoop->new_from_paths(
|
||||
Slic3r::ExtrusionPath->new(
|
||||
polyline => $polyline1,
|
||||
role => Slic3r::ExtrusionPath::EXTR_ROLE_EXTERNAL_PERIMETER,
|
||||
mm3_per_mm => 1,
|
||||
),
|
||||
Slic3r::ExtrusionPath->new(
|
||||
polyline => $polyline2,
|
||||
role => Slic3r::ExtrusionPath::EXTR_ROLE_OVERHANG_PERIMETER,
|
||||
mm3_per_mm => 1,
|
||||
),
|
||||
);
|
||||
is $loop->length, sum($polyline1->length, $polyline2->length), 'length';
|
||||
is scalar(@$loop), 2, 'loop contains two paths';
|
||||
$loop->split_at($polyline1->[1]);
|
||||
is $loop->length, sum($polyline1->length, $polyline2->length), 'length after splitting';
|
||||
is scalar(@$loop), 3, 'loop contains three paths after splitting';
|
||||
ok $loop->[0]->polyline->[0]->coincides_with($polyline1->[1]), 'expected starting point';
|
||||
ok $loop->[-1]->polyline->[-1]->coincides_with($polyline1->[1]), 'expected ending point';
|
||||
ok $loop->[0]->polyline->[-1]->coincides_with($loop->[1]->polyline->[0]), 'paths have common point';
|
||||
ok $loop->[1]->polyline->[-1]->coincides_with($loop->[2]->polyline->[0]), 'paths have common point';
|
||||
is $loop->[0]->role, Slic3r::ExtrusionPath::EXTR_ROLE_EXTERNAL_PERIMETER, 'expected order after splitting';
|
||||
is $loop->[1]->role, Slic3r::ExtrusionPath::EXTR_ROLE_OVERHANG_PERIMETER, 'expected order after splitting';
|
||||
is $loop->[2]->role, Slic3r::ExtrusionPath::EXTR_ROLE_EXTERNAL_PERIMETER, 'expected order after splitting';
|
||||
is scalar(@{$loop->[0]->polyline}), 2, 'path has correct number of points';
|
||||
is scalar(@{$loop->[1]->polyline}), 3, 'path has correct number of points';
|
||||
is scalar(@{$loop->[2]->polyline}), 2, 'path has correct number of points';
|
||||
|
||||
my @paths = @{$loop->clip_end(3)};
|
||||
is sum(map $_->length, @paths), $loop->length - 3, 'returned paths have expected length';
|
||||
}
|
||||
|
||||
__END__
|
||||
|
|
|
@ -4,20 +4,20 @@ use strict;
|
|||
use warnings;
|
||||
|
||||
use Slic3r::XS;
|
||||
use Test::More tests => 11;
|
||||
use Test::More tests => 17;
|
||||
|
||||
my $square = [ # ccw
|
||||
my $square = Slic3r::Polygon->new( # ccw
|
||||
[200, 100],
|
||||
[200, 200],
|
||||
[100, 200],
|
||||
[100, 100],
|
||||
];
|
||||
my $hole_in_square = [ # cw
|
||||
);
|
||||
my $hole_in_square = Slic3r::Polygon->new( # cw
|
||||
[160, 140],
|
||||
[140, 140],
|
||||
[140, 160],
|
||||
[160, 160],
|
||||
];
|
||||
);
|
||||
my $expolygon = Slic3r::ExPolygon->new($square, $hole_in_square);
|
||||
|
||||
{
|
||||
|
@ -106,4 +106,53 @@ my $expolygon = Slic3r::ExPolygon->new($square, $hole_in_square);
|
|||
}
|
||||
}
|
||||
|
||||
if (0) { # Clipper does not preserve polyline orientation
|
||||
my $polyline = Slic3r::Polyline->new([50,150], [300,150]);
|
||||
my $result = Slic3r::Geometry::Clipper::intersection_pl([$polyline], [$square]);
|
||||
is scalar(@$result), 1, 'intersection_pl - correct number of result lines';
|
||||
is_deeply $result->[0]->pp, [[100,150], [200,150]], 'clipped line orientation is preserved';
|
||||
}
|
||||
|
||||
if (0) { # Clipper does not preserve polyline orientation
|
||||
my $polyline = Slic3r::Polyline->new([300,150], [50,150]);
|
||||
my $result = Slic3r::Geometry::Clipper::intersection_pl([$polyline], [$square]);
|
||||
is scalar(@$result), 1, 'intersection_pl - correct number of result lines';
|
||||
is_deeply $result->[0]->pp, [[200,150], [100,150]], 'clipped line orientation is preserved';
|
||||
}
|
||||
|
||||
if (0) { # Clipper does not preserve polyline orientation
|
||||
my $result = Slic3r::Geometry::Clipper::intersection_ppl([$hole_in_square], [$square]);
|
||||
is_deeply $result->[0]->pp, $hole_in_square->split_at_first_point->pp,
|
||||
'intersection_ppl - clipping cw polygon as polyline preserves winding order';
|
||||
}
|
||||
|
||||
{
|
||||
my $square2 = $square->clone;
|
||||
$square2->translate(50,50);
|
||||
{
|
||||
my $result = Slic3r::Geometry::Clipper::intersection_ppl([$square2], [$square]);
|
||||
is scalar(@$result), 1, 'intersection_ppl - result contains a single line';
|
||||
is scalar(@{$result->[0]}), 3, 'intersection_ppl - result contains expected number of points';
|
||||
# Clipper does not preserve polyline orientation so we only check the middle point
|
||||
###ok $result->[0][0]->coincides_with(Slic3r::Point->new(150,200)), 'intersection_ppl - expected point order';
|
||||
ok $result->[0][1]->coincides_with(Slic3r::Point->new(150,150)), 'intersection_ppl - expected point order';
|
||||
###ok $result->[0][2]->coincides_with(Slic3r::Point->new(200,150)), 'intersection_ppl - expected point order';
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
my $square2 = $square->clone;
|
||||
$square2->reverse;
|
||||
$square2->translate(50,50);
|
||||
{
|
||||
my $result = Slic3r::Geometry::Clipper::intersection_ppl([$square2], [$square]);
|
||||
is scalar(@$result), 1, 'intersection_ppl - result contains a single line';
|
||||
is scalar(@{$result->[0]}), 3, 'intersection_ppl - result contains expected number of points';
|
||||
# Clipper does not preserve polyline orientation so we only check the middle point
|
||||
###ok $result->[0][0]->coincides_with(Slic3r::Point->new(200,150)), 'intersection_ppl - expected point order';
|
||||
ok $result->[0][1]->coincides_with(Slic3r::Point->new(150,150)), 'intersection_ppl - expected point order';
|
||||
###ok $result->[0][2]->coincides_with(Slic3r::Point->new(150,200)), 'intersection_ppl - expected point order';
|
||||
}
|
||||
}
|
||||
|
||||
__END__
|
||||
|
|
|
@ -18,10 +18,12 @@ my $path = Slic3r::ExtrusionPath->new(
|
|||
mm3_per_mm => 1,
|
||||
);
|
||||
|
||||
my $loop = Slic3r::ExtrusionLoop->new(
|
||||
polygon => Slic3r::Polygon->new(@$points),
|
||||
role => Slic3r::ExtrusionPath::EXTR_ROLE_FILL,
|
||||
mm3_per_mm => 1,
|
||||
my $loop = Slic3r::ExtrusionLoop->new_from_paths(
|
||||
Slic3r::ExtrusionPath->new(
|
||||
polyline => Slic3r::Polygon->new(@$points)->split_at_first_point,
|
||||
role => Slic3r::ExtrusionPath::EXTR_ROLE_FILL,
|
||||
mm3_per_mm => 1,
|
||||
),
|
||||
);
|
||||
|
||||
my $collection = Slic3r::ExtrusionPath::Collection->new($path);
|
||||
|
|
|
@ -101,6 +101,15 @@ diff_pl(subject, clip)
|
|||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
Polylines
|
||||
diff_ppl(subject, clip)
|
||||
Polygons subject
|
||||
Polygons clip
|
||||
CODE:
|
||||
diff(subject, clip, RETVAL);
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
Polygons
|
||||
intersection(subject, clip, safety_offset = false)
|
||||
Polygons subject
|
||||
|
@ -130,6 +139,15 @@ intersection_pl(subject, clip)
|
|||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
Polylines
|
||||
intersection_ppl(subject, clip)
|
||||
Polygons subject
|
||||
Polygons clip
|
||||
CODE:
|
||||
intersection(subject, clip, RETVAL);
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
ExPolygons
|
||||
xor_ex(subject, clip, safety_offset = false)
|
||||
Polygons subject
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
bool contains_point(Point* point)
|
||||
%code{% RETVAL = THIS->contains_point(*point); %};
|
||||
void simplify(double tolerance);
|
||||
Polygons polygons()
|
||||
%code{% RETVAL = *THIS; %};
|
||||
%{
|
||||
|
||||
ExPolygonCollection*
|
||||
|
|
|
@ -7,92 +7,38 @@
|
|||
%}
|
||||
|
||||
%name{Slic3r::ExtrusionLoop} class ExtrusionLoop {
|
||||
ExtrusionLoop();
|
||||
~ExtrusionLoop();
|
||||
SV* arrayref()
|
||||
%code{% Polygon polygon; THIS->polygon(&polygon); RETVAL = polygon.to_AV(); %};
|
||||
SV* pp()
|
||||
%code{% Polygon polygon; THIS->polygon(&polygon); RETVAL = polygon.to_SV_pureperl(); %};
|
||||
Clone<ExtrusionLoop> clone()
|
||||
%code{% RETVAL = THIS; %};
|
||||
void reverse();
|
||||
ExtrusionPath* split_at_index(int index)
|
||||
%code{% RETVAL = new ExtrusionPath (); THIS->split_at_index(index, RETVAL); %};
|
||||
ExtrusionPath* split_at_first_point()
|
||||
%code{% RETVAL = new ExtrusionPath (); THIS->split_at_first_point(RETVAL); %};
|
||||
bool make_clockwise();
|
||||
bool make_counter_clockwise();
|
||||
Clone<Point> first_point();
|
||||
Clone<Point> last_point();
|
||||
bool is_perimeter();
|
||||
bool is_fill();
|
||||
bool is_bridge();
|
||||
Polygon* polygon()
|
||||
%code{% RETVAL = new Polygon (*THIS); %};
|
||||
void append(ExtrusionPath* path)
|
||||
%code{% THIS->paths.push_back(*path); %};
|
||||
double length();
|
||||
void split_at(Point* point)
|
||||
%code{% THIS->split_at(*point); %};
|
||||
ExtrusionPaths clip_end(double distance)
|
||||
%code{% THIS->clip_end(distance, &RETVAL); %};
|
||||
bool has_overhang_point(Point* point)
|
||||
%code{% RETVAL = THIS->has_overhang_point(*point); %};
|
||||
%{
|
||||
|
||||
ExtrusionLoop*
|
||||
_new(CLASS, polygon_sv, role, mm3_per_mm, width, height)
|
||||
char* CLASS;
|
||||
SV* polygon_sv;
|
||||
ExtrusionRole role;
|
||||
double mm3_per_mm;
|
||||
float width;
|
||||
float height;
|
||||
SV*
|
||||
ExtrusionLoop::arrayref()
|
||||
CODE:
|
||||
Polygon polygon;
|
||||
polygon.from_SV_check(polygon_sv);
|
||||
RETVAL = new ExtrusionLoop (polygon, role);
|
||||
RETVAL->mm3_per_mm = mm3_per_mm;
|
||||
RETVAL->width = width;
|
||||
RETVAL->height = height;
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
Polygon*
|
||||
ExtrusionLoop::polygon(...)
|
||||
CODE:
|
||||
if (items > 1) {
|
||||
Polygon polygon;
|
||||
polygon.from_SV_check( ST(1) );
|
||||
THIS->set_polygon(polygon);
|
||||
AV* av = newAV();
|
||||
av_fill(av, THIS->paths.size()-1);
|
||||
int i = 0;
|
||||
for (ExtrusionPaths::iterator it = THIS->paths.begin(); it != THIS->paths.end(); ++it) {
|
||||
av_store(av, i++, it->to_SV_ref());
|
||||
}
|
||||
RETVAL = new Polygon ();
|
||||
THIS->polygon(RETVAL);
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
ExtrusionRole
|
||||
ExtrusionLoop::role(...)
|
||||
CODE:
|
||||
if (items > 1) {
|
||||
THIS->role = (ExtrusionRole)SvUV(ST(1));
|
||||
}
|
||||
RETVAL = THIS->role;
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
double
|
||||
ExtrusionLoop::mm3_per_mm(...)
|
||||
CODE:
|
||||
if (items > 1) {
|
||||
THIS->mm3_per_mm = (double)SvNV(ST(1));
|
||||
}
|
||||
RETVAL = THIS->mm3_per_mm;
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
float
|
||||
ExtrusionLoop::width(...)
|
||||
CODE:
|
||||
if (items > 1) {
|
||||
THIS->width = (float)SvNV(ST(1));
|
||||
}
|
||||
RETVAL = THIS->width;
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
float
|
||||
ExtrusionLoop::height(...)
|
||||
CODE:
|
||||
if (items > 1) {
|
||||
THIS->height = (float)SvNV(ST(1));
|
||||
}
|
||||
RETVAL = THIS->height;
|
||||
RETVAL = newRV_noinc((SV*)av);
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
|
|
|
@ -140,7 +140,7 @@ _constant()
|
|||
EXTR_ROLE_FILL = erFill
|
||||
EXTR_ROLE_SOLIDFILL = erSolidFill
|
||||
EXTR_ROLE_TOPSOLIDFILL = erTopSolidFill
|
||||
EXTR_ROLE_BRIDGE = erBrige
|
||||
EXTR_ROLE_BRIDGE = erBridge
|
||||
EXTR_ROLE_INTERNALBRIDGE = erInternalBridge
|
||||
EXTR_ROLE_SKIRT = erSkirt
|
||||
EXTR_ROLE_SUPPORTMATERIAL = erSupportMaterial
|
||||
|
|
|
@ -97,6 +97,7 @@ Lines T_ARRAYREF
|
|||
Polygons T_ARRAYREF
|
||||
Polylines T_ARRAYREF
|
||||
ExPolygons T_ARRAYREF
|
||||
ExtrusionPaths T_ARRAYREF
|
||||
Surfaces T_ARRAYREF
|
||||
|
||||
# we return these types whenever we want the items to be returned
|
||||
|
|
|
@ -70,6 +70,7 @@
|
|||
%typemap{Polylines};
|
||||
%typemap{PrintState};
|
||||
%typemap{ExPolygons};
|
||||
%typemap{ExtrusionPaths};
|
||||
%typemap{Surfaces};
|
||||
%typemap{Polygons*};
|
||||
%typemap{TriangleMeshPtrs};
|
||||
|
|
Loading…
Reference in a new issue