mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-12 01:07:57 -06:00
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 strict;
|
||||||
use warnings;
|
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;
|
1;
|
||||||
|
|
|
@ -19,7 +19,6 @@ has 'layer' => (is => 'rw');
|
||||||
has 'region' => (is => 'rw');
|
has 'region' => (is => 'rw');
|
||||||
has '_layer_islands' => (is => 'rw');
|
has '_layer_islands' => (is => 'rw');
|
||||||
has '_upper_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_x' => (is => 'rw', default => sub {0} );
|
||||||
has 'shift_y' => (is => 'rw', default => sub {0} );
|
has 'shift_y' => (is => 'rw', default => sub {0} );
|
||||||
has 'z' => (is => 'rw');
|
has 'z' => (is => 'rw');
|
||||||
|
@ -111,17 +110,6 @@ sub change_layer {
|
||||||
# avoid computing islands and overhangs if they're not needed
|
# avoid computing islands and overhangs if they're not needed
|
||||||
$self->_layer_islands($layer->islands);
|
$self->_layer_islands($layer->islands);
|
||||||
$self->_upper_layer_islands($layer->upper_layer ? $layer->upper_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) {
|
if ($self->print_config->avoid_crossing_perimeters) {
|
||||||
$self->layer_mp(Slic3r::GCode::MotionPlanner->new(
|
$self->layer_mp(Slic3r::GCode::MotionPlanner->new(
|
||||||
islands => union_ex([ map @$_, @{$layer->slices} ], 1),
|
islands => union_ex([ map @$_, @{$layer->slices} ], 1),
|
||||||
|
@ -199,17 +187,17 @@ sub extrude_loop {
|
||||||
|
|
||||||
# extrude all loops ccw
|
# extrude all loops ccw
|
||||||
my $was_clockwise = $loop->make_counter_clockwise;
|
my $was_clockwise = $loop->make_counter_clockwise;
|
||||||
my $polygon = $loop->polygon;
|
|
||||||
|
|
||||||
# find candidate starting points
|
# find candidate starting points
|
||||||
# start looking for concave vertices not being overhangs
|
# start looking for concave vertices not being overhangs
|
||||||
|
my $polygon = $loop->polygon;
|
||||||
my @concave = ();
|
my @concave = ();
|
||||||
if ($self->print_config->start_perimeters_at_concave_points) {
|
if ($self->print_config->start_perimeters_at_concave_points) {
|
||||||
@concave = $polygon->concave_points;
|
@concave = $polygon->concave_points;
|
||||||
}
|
}
|
||||||
my @candidates = ();
|
my @candidates = ();
|
||||||
if ($self->print_config->start_perimeters_at_non_overhang) {
|
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 (!@candidates) {
|
||||||
# if none, look for any concave vertex
|
# if none, look for any concave vertex
|
||||||
|
@ -217,11 +205,11 @@ sub extrude_loop {
|
||||||
if (!@candidates) {
|
if (!@candidates) {
|
||||||
# if none, look for any non-overhang vertex
|
# if none, look for any non-overhang vertex
|
||||||
if ($self->print_config->start_perimeters_at_non_overhang) {
|
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 (!@candidates) {
|
||||||
# if none, all points are valid 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);
|
$last_pos->rotate(rand(2*PI), $self->print_config->print_center);
|
||||||
}
|
}
|
||||||
|
|
||||||
# split the loop at the starting point and make a path
|
# split the loop at the starting point
|
||||||
my $start_at = $last_pos->nearest_point(\@candidates);
|
$loop->split_at($last_pos->nearest_point(\@candidates));
|
||||||
my $extrusion_path = $loop->split_at($start_at);
|
|
||||||
|
|
||||||
# clip the path to avoid the extruder to get exactly on the first point of the loop;
|
# 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
|
# if polyline was shorter than the clipping distance we'd get a null polyline, so
|
||||||
# we discard it in that case
|
# we discard it in that case
|
||||||
$extrusion_path->clip_end(scale($self->extruder->nozzle_diameter) * &Slic3r::LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER)
|
my $clip_length = $self->enable_loop_clipping
|
||||||
if $self->enable_loop_clipping;
|
? scale($self->extruder->nozzle_diameter) * &Slic3r::LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER
|
||||||
return '' if !@{$extrusion_path->polyline};
|
: 0;
|
||||||
|
|
||||||
my @paths = ();
|
# get paths
|
||||||
# detect overhanging/bridging perimeters
|
my @paths = @{$loop->clip_end($clip_length)};
|
||||||
if ($self->layer->print->config->overhangs && $extrusion_path->is_perimeter && $self->_lower_layer_slices->count > 0) {
|
return '' if !@paths;
|
||||||
# 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
# apply the small perimeter speed
|
# apply the small perimeter speed
|
||||||
my %params = ();
|
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';
|
$params{speed} = 'small_perimeter';
|
||||||
}
|
}
|
||||||
|
|
||||||
# extrude along the path
|
# extrude along the path
|
||||||
my $gcode = join '', map $self->extrude_path($_, $description, %params), @paths;
|
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
|
# 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
|
# detect angle between last and first segment
|
||||||
# the side depends on the original winding order of the polygon (left for contours, right for holes)
|
# 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 @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;
|
$angle *= -1 if $was_clockwise;
|
||||||
|
|
||||||
# create the destination point along the first segment and rotate it
|
# 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
|
# 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
|
# 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 $distance = min(scale($self->extruder->nozzle_diameter), $first_segment->length);
|
||||||
my $point = $first_segment->point_at($distance);
|
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
|
# 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;
|
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
|
diff_ex diff union_ex intersection_ex xor_ex JT_ROUND JT_MITER
|
||||||
JT_SQUARE is_counter_clockwise union_pt offset2 offset2_ex
|
JT_SQUARE is_counter_clockwise union_pt offset2 offset2_ex
|
||||||
intersection intersection_pl diff_pl union CLIPPER_OFFSET_SCALE
|
intersection intersection_pl diff_pl union CLIPPER_OFFSET_SCALE
|
||||||
union_pt_chained);
|
union_pt_chained diff_ppl intersection_ppl);
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
|
|
@ -3,7 +3,8 @@ use Moo;
|
||||||
|
|
||||||
use List::Util qw(first sum max min);
|
use List::Util qw(first sum max min);
|
||||||
use Slic3r::Geometry qw(PI unscale scaled_epsilon rad2deg epsilon directions_parallel_within);
|
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 'expolygon' => (is => 'ro', required => 1);
|
||||||
has 'lower_slices' => (is => 'rw', required => 1); # ExPolygons or ExPolygonCollection
|
has 'lower_slices' => (is => 'rw', required => 1); # ExPolygons or ExPolygonCollection
|
||||||
|
@ -26,7 +27,7 @@ sub BUILD {
|
||||||
foreach my $lower (@{$self->lower_slices}) {
|
foreach my $lower (@{$self->lower_slices}) {
|
||||||
# turn bridge contour and holes into polylines and then clip them
|
# turn bridge contour and holes into polylines and then clip them
|
||||||
# with each lower slice's contour
|
# 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);
|
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 qw(PI A B scale unscale chained_path points_coincide);
|
||||||
use Slic3r::Geometry::Clipper qw(union_ex diff_ex intersection_ex
|
use Slic3r::Geometry::Clipper qw(union_ex diff_ex intersection_ex
|
||||||
offset offset_ex offset2 offset2_ex union_pt diff intersection
|
offset offset_ex offset2 offset2_ex union_pt diff intersection
|
||||||
union diff);
|
union diff intersection_ppl diff_ppl);
|
||||||
use Slic3r::Surface ':types';
|
use Slic3r::Surface ':types';
|
||||||
|
|
||||||
has 'layer' => (
|
has 'layer' => (
|
||||||
|
@ -63,6 +63,8 @@ sub make_perimeters {
|
||||||
|
|
||||||
my $perimeter_flow = $self->flow(FLOW_ROLE_PERIMETER);
|
my $perimeter_flow = $self->flow(FLOW_ROLE_PERIMETER);
|
||||||
my $mm3_per_mm = $perimeter_flow->mm3_per_mm($self->height);
|
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 $pwidth = $perimeter_flow->scaled_width;
|
||||||
my $pspacing = $perimeter_flow->scaled_spacing;
|
my $pspacing = $perimeter_flow->scaled_spacing;
|
||||||
my $solid_infill_flow = $self->flow(FLOW_ROLE_SOLID_INFILL);
|
my $solid_infill_flow = $self->flow(FLOW_ROLE_SOLID_INFILL);
|
||||||
|
@ -229,6 +231,19 @@ sub make_perimeters {
|
||||||
my $contours_pt = union_pt(\@contours);
|
my $contours_pt = union_pt(\@contours);
|
||||||
my $holes_pt = union_pt(\@holes);
|
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
|
# prepare a coderef for traversing the PolyTree object
|
||||||
# external contours are root items of $contours_pt
|
# external contours are root items of $contours_pt
|
||||||
# internal contours are the ones next to external
|
# internal contours are the ones next to external
|
||||||
|
@ -242,15 +257,6 @@ sub make_perimeters {
|
||||||
foreach my $polynode (@$polynodes) {
|
foreach my $polynode (@$polynodes) {
|
||||||
my $polygon = ($polynode->{outer} // $polynode->{hole})->clone;
|
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;
|
my $role = EXTR_ROLE_PERIMETER;
|
||||||
if ($is_contour ? $depth == 0 : !@{ $polynode->{children} }) {
|
if ($is_contour ? $depth == 0 : !@{ $polynode->{children} }) {
|
||||||
# external perimeters are root level in case of contours
|
# external perimeters are root level in case of contours
|
||||||
|
@ -260,13 +266,62 @@ sub make_perimeters {
|
||||||
$role = EXTR_ROLE_CONTOUR_INTERNAL_PERIMETER;
|
$role = EXTR_ROLE_CONTOUR_INTERNAL_PERIMETER;
|
||||||
}
|
}
|
||||||
|
|
||||||
$collection->append(Slic3r::ExtrusionLoop->new(
|
# detect overhanging/bridging perimeters
|
||||||
polygon => $polygon,
|
my @paths = ();
|
||||||
role => $role,
|
if ($self->layer->print->config->overhangs && $lower_slices->count > 0) {
|
||||||
mm3_per_mm => $mm3_per_mm,
|
# get non-overhang paths by intersecting this loop with the grown lower slices
|
||||||
width => $perimeter_flow->width,
|
foreach my $polyline (@{ intersection_ppl([ $polygon ], $lower_slices_p) }) {
|
||||||
height => $self->height,
|
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
|
# save the children
|
||||||
push @children, $polynode->{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
|
Slic3r::debugf " %d gaps filled with extrusion width = %s\n", scalar @$this, $w
|
||||||
if @$this;
|
if @$this;
|
||||||
|
|
||||||
return map {
|
for my $i (0..$#polylines) {
|
||||||
$_->isa('Slic3r::Polygon')
|
if ($polylines[$i]->isa('Slic3r::Polygon')) {
|
||||||
? Slic3r::ExtrusionLoop->new(polygon => $_, %path_args)->split_at_first_point # should we keep these as loops?
|
my $loop = Slic3r::ExtrusionLoop->new;
|
||||||
: Slic3r::ExtrusionPath->new(polyline => $_, %path_args),
|
$loop->append(Slic3r::ExtrusionPath->new(polyline => $polylines[$i]->split_at_first_point, %path_args));
|
||||||
} @polylines;
|
$polylines[$i] = $loop;
|
||||||
|
} else {
|
||||||
|
$polylines[$i] = Slic3r::ExtrusionPath->new(polyline => $polylines[$i], %path_args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return @polylines;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub prepare_fill_surfaces {
|
sub prepare_fill_surfaces {
|
||||||
|
|
|
@ -51,41 +51,4 @@ sub concave_points {
|
||||||
-1 .. ($#points-1);
|
-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;
|
1;
|
|
@ -705,12 +705,14 @@ sub make_skirt {
|
||||||
for (my $i = $self->config->skirts; $i > 0; $i--) {
|
for (my $i = $self->config->skirts; $i > 0; $i--) {
|
||||||
$distance += scale $spacing;
|
$distance += scale $spacing;
|
||||||
my $loop = offset([$convex_hull], $distance, 1, JT_ROUND, scale(0.1))->[0];
|
my $loop = offset([$convex_hull], $distance, 1, JT_ROUND, scale(0.1))->[0];
|
||||||
$self->skirt->append(Slic3r::ExtrusionLoop->new(
|
$self->skirt->append(Slic3r::ExtrusionLoop->new_from_paths(
|
||||||
polygon => Slic3r::Polygon->new(@$loop),
|
Slic3r::ExtrusionPath->new(
|
||||||
role => EXTR_ROLE_SKIRT,
|
polyline => Slic3r::Polygon->new(@$loop)->split_at_first_point,
|
||||||
mm3_per_mm => $mm3_per_mm,
|
role => EXTR_ROLE_SKIRT,
|
||||||
width => $flow->width,
|
mm3_per_mm => $mm3_per_mm,
|
||||||
height => $first_layer_height,
|
width => $flow->width,
|
||||||
|
height => $first_layer_height,
|
||||||
|
),
|
||||||
));
|
));
|
||||||
|
|
||||||
if ($self->config->min_skirt_length > 0) {
|
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)};
|
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(
|
$self->brim->append(map Slic3r::ExtrusionLoop->new_from_paths(
|
||||||
polygon => Slic3r::Polygon->new(@$_),
|
Slic3r::ExtrusionPath->new(
|
||||||
role => EXTR_ROLE_SKIRT,
|
polyline => Slic3r::Polygon->new(@$_)->split_at_first_point,
|
||||||
mm3_per_mm => $mm3_per_mm,
|
role => EXTR_ROLE_SKIRT,
|
||||||
width => $flow->width,
|
mm3_per_mm => $mm3_per_mm,
|
||||||
height => $first_layer_height,
|
width => $flow->width,
|
||||||
|
height => $first_layer_height,
|
||||||
|
),
|
||||||
), reverse @{union_pt_chained(\@loops)});
|
), reverse @{union_pt_chained(\@loops)});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -913,7 +917,7 @@ sub write_gcode {
|
||||||
|
|
||||||
# calculate wiping points if needed
|
# calculate wiping points if needed
|
||||||
if ($self->config->ooze_prevention) {
|
if ($self->config->ooze_prevention) {
|
||||||
my @skirt_points = map @$_, @{$self->skirt};
|
my @skirt_points = map @$_, map @$_, @{$self->skirt};
|
||||||
if (@skirt_points) {
|
if (@skirt_points) {
|
||||||
my $outer_skirt = convex_hull(\@skirt_points);
|
my $outer_skirt = convex_hull(\@skirt_points);
|
||||||
my @skirts = ();
|
my @skirts = ();
|
||||||
|
|
|
@ -70,14 +70,20 @@ sub generate {
|
||||||
$self->clip_with_shape($base, $shape) if @$shape;
|
$self->clip_with_shape($base, $shape) if @$shape;
|
||||||
|
|
||||||
# Install support layers into object.
|
# Install support layers into object.
|
||||||
push @{$object->support_layers}, map Slic3r::Layer::Support->new(
|
for my $i (0 .. $#$support_z) {
|
||||||
object => $object,
|
push @{$object->support_layers}, Slic3r::Layer::Support->new(
|
||||||
id => $_,
|
object => $object,
|
||||||
height => ($_ == 0) ? $support_z->[$_] : ($support_z->[$_] - $support_z->[$_-1]),
|
id => $i,
|
||||||
print_z => $support_z->[$_],
|
height => ($i == 0) ? $support_z->[$i] : ($support_z->[$i] - $support_z->[$i-1]),
|
||||||
slice_z => -1,
|
print_z => $support_z->[$i],
|
||||||
slices => [],
|
slice_z => -1,
|
||||||
), 0 .. $#$support_z;
|
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.
|
# Generate the actual toolpaths and save them into each layer.
|
||||||
$self->generate_toolpaths($object, $overhang, $contact, $interface, $base);
|
$self->generate_toolpaths($object, $overhang, $contact, $interface, $base);
|
||||||
|
|
|
@ -106,28 +106,12 @@ use overload
|
||||||
'@{}' => sub { $_[0]->arrayref },
|
'@{}' => sub { $_[0]->arrayref },
|
||||||
'fallback' => 1;
|
'fallback' => 1;
|
||||||
|
|
||||||
sub new {
|
sub new_from_paths {
|
||||||
my ($class, %args) = @_;
|
my ($class, @paths) = @_;
|
||||||
|
|
||||||
return $class->_new(
|
my $loop = $class->new;
|
||||||
$args{polygon}, # required
|
$loop->append($_) for @paths;
|
||||||
$args{role}, # required
|
return $loop;
|
||||||
$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,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
package Slic3r::ExtrusionLoop::Ref;
|
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,
|
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
|
// read input
|
||||||
ClipperLib::Paths* input_subject = new ClipperLib::Paths();
|
ClipperLib::Paths input_subject, input_clip;
|
||||||
ClipperLib::Paths* input_clip = new ClipperLib::Paths();
|
Slic3rMultiPoints_to_ClipperPaths(subject, input_subject);
|
||||||
Slic3rMultiPoints_to_ClipperPaths(subject, *input_subject);
|
Slic3rMultiPoints_to_ClipperPaths(clip, input_clip);
|
||||||
Slic3rMultiPoints_to_ClipperPaths(clip, *input_clip);
|
|
||||||
|
// perform safety offset
|
||||||
|
if (safety_offset_) safety_offset(&input_clip);
|
||||||
|
|
||||||
// init Clipper
|
// init Clipper
|
||||||
ClipperLib::Clipper clipper;
|
ClipperLib::Clipper clipper;
|
||||||
clipper.Clear();
|
clipper.Clear();
|
||||||
|
|
||||||
// add polygons
|
// add polygons
|
||||||
clipper.AddPaths(*input_subject, ClipperLib::ptSubject, false);
|
clipper.AddPaths(input_subject, ClipperLib::ptSubject, false);
|
||||||
delete input_subject;
|
clipper.AddPaths(input_clip, ClipperLib::ptClip, true);
|
||||||
clipper.AddPaths(*input_clip, ClipperLib::ptClip, true);
|
|
||||||
delete input_clip;
|
|
||||||
|
|
||||||
// perform operation
|
// perform operation
|
||||||
clipper.Execute(clipType, retval, fillType, fillType);
|
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_)
|
const Slic3r::Polygons &clip, Slic3r::ExPolygons &retval, bool safety_offset_)
|
||||||
{
|
{
|
||||||
// perform operation
|
// perform operation
|
||||||
ClipperLib::PolyTree* polytree = new ClipperLib::PolyTree();
|
ClipperLib::PolyTree polytree;
|
||||||
_clipper_do<ClipperLib::PolyTree>(clipType, subject, clip, *polytree, ClipperLib::pftNonZero, safety_offset_);
|
_clipper_do<ClipperLib::PolyTree>(clipType, subject, clip, polytree, ClipperLib::pftNonZero, safety_offset_);
|
||||||
|
|
||||||
// convert into ExPolygons
|
// convert into ExPolygons
|
||||||
PolyTreeToExPolygons(*polytree, retval);
|
PolyTreeToExPolygons(polytree, retval);
|
||||||
delete polytree;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polylines &subject,
|
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
|
// perform operation
|
||||||
ClipperLib::PolyTree polytree;
|
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::Paths output;
|
||||||
ClipperLib::PolyTreeToPaths(polytree, output);
|
ClipperLib::PolyTreeToPaths(polytree, output);
|
||||||
ClipperPaths_to_Slic3rMultiPoints(output, retval);
|
ClipperPaths_to_Slic3rMultiPoints(output, retval);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T>
|
void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject,
|
||||||
void diff(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, T &retval, bool safety_offset_)
|
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_);
|
_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, 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::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)
|
template <class SubjectType, class ResultType>
|
||||||
{
|
void intersection(const SubjectType &subject, const Slic3r::Polygons &clip, ResultType &retval, bool safety_offset_)
|
||||||
_clipper(ClipperLib::ctDifference, subject, clip, retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
void intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, T &retval, bool safety_offset_)
|
|
||||||
{
|
{
|
||||||
_clipper(ClipperLib::ctIntersection, subject, clip, retval, 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, 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_);
|
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_);
|
||||||
void intersection(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, Slic3r::Polylines &retval)
|
template void intersection<Slic3r::Polylines, Slic3r::Polylines>(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, Slic3r::Polylines &retval, bool safety_offset_);
|
||||||
{
|
|
||||||
_clipper(ClipperLib::ctIntersection, subject, clip, retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
void xor_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::ExPolygons &retval,
|
void xor_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::ExPolygons &retval,
|
||||||
bool safety_offset_)
|
bool safety_offset_)
|
||||||
|
@ -492,24 +555,19 @@ void simplify_polygons(const Slic3r::Polygons &subject, Slic3r::ExPolygons &retv
|
||||||
PolyTreeToExPolygons(polytree, retval);
|
PolyTreeToExPolygons(polytree, retval);
|
||||||
}
|
}
|
||||||
|
|
||||||
void safety_offset(ClipperLib::Paths* &subject)
|
void safety_offset(ClipperLib::Paths* paths)
|
||||||
{
|
{
|
||||||
// scale input
|
// scale input
|
||||||
scaleClipperPolygons(*subject, CLIPPER_OFFSET_SCALE);
|
scaleClipperPolygons(*paths, CLIPPER_OFFSET_SCALE);
|
||||||
|
|
||||||
// perform offset (delta = scale 1e-05)
|
// perform offset (delta = scale 1e-05)
|
||||||
ClipperLib::Paths* retval = new ClipperLib::Paths();
|
|
||||||
ClipperLib::ClipperOffset co;
|
ClipperLib::ClipperOffset co;
|
||||||
co.MiterLimit = 2;
|
co.MiterLimit = 2;
|
||||||
co.AddPaths(*subject, ClipperLib::jtMiter, ClipperLib::etClosedPolygon);
|
co.AddPaths(*paths, ClipperLib::jtMiter, ClipperLib::etClosedPolygon);
|
||||||
co.Execute(*retval, 10.0 * CLIPPER_OFFSET_SCALE);
|
co.Execute(*paths, 10.0 * CLIPPER_OFFSET_SCALE);
|
||||||
|
|
||||||
// unscale output
|
// unscale output
|
||||||
scaleClipperPolygons(*retval, 1.0/CLIPPER_OFFSET_SCALE);
|
scaleClipperPolygons(*paths, 1.0/CLIPPER_OFFSET_SCALE);
|
||||||
|
|
||||||
// delete original data and switch pointer
|
|
||||||
delete subject;
|
|
||||||
subject = retval;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////
|
///////////////////////
|
||||||
|
|
|
@ -70,7 +70,7 @@ template <class T>
|
||||||
void _clipper_do(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject,
|
void _clipper_do(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject,
|
||||||
const Slic3r::Polygons &clip, T &retval, bool safety_offset_);
|
const Slic3r::Polygons &clip, T &retval, bool safety_offset_);
|
||||||
void _clipper_do(ClipperLib::ClipType clipType, const Slic3r::Polylines &subject,
|
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,
|
void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject,
|
||||||
const Slic3r::Polygons &clip, Slic3r::Polygons &retval, bool safety_offset_);
|
const Slic3r::Polygons &clip, Slic3r::Polygons &retval, bool safety_offset_);
|
||||||
void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject,
|
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,
|
void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polylines &subject,
|
||||||
const Slic3r::Polygons &clip, Slic3r::Polylines &retval);
|
const Slic3r::Polygons &clip, Slic3r::Polylines &retval);
|
||||||
|
|
||||||
template <class T>
|
template <class SubjectType, class ResultType>
|
||||||
void diff(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, T &retval, bool safety_offset_ = false);
|
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 SubjectType, class ResultType>
|
||||||
|
void intersection(const SubjectType &subject, const Slic3r::Polygons &clip, ResultType &retval, bool safety_offset_ = false);
|
||||||
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);
|
|
||||||
|
|
||||||
void xor_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::ExPolygons &retval,
|
void xor_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::ExPolygons &retval,
|
||||||
bool safety_offset_ = false);
|
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::Polygons &retval, bool preserve_collinear = false);
|
||||||
void simplify_polygons(const Slic3r::Polygons &subject, Slic3r::ExPolygons &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
|
// intersect with this expolygon
|
||||||
Polygons trapezoids;
|
Polygons trapezoids;
|
||||||
intersection(poly, *this, trapezoids);
|
intersection<Polygons,Polygons>(poly, *this, trapezoids);
|
||||||
|
|
||||||
// append results to return value
|
// append results to return value
|
||||||
polygons->insert(polygons->end(), trapezoids.begin(), trapezoids.end());
|
polygons->insert(polygons->end(), trapezoids.begin(), trapezoids.end());
|
||||||
|
|
|
@ -10,31 +10,6 @@
|
||||||
|
|
||||||
namespace Slic3r {
|
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*
|
||||||
ExtrusionPath::clone() const
|
ExtrusionPath::clone() const
|
||||||
{
|
{
|
||||||
|
@ -64,7 +39,7 @@ ExtrusionPath::intersect_expolygons(const ExPolygonCollection &collection, Extru
|
||||||
{
|
{
|
||||||
// perform clipping
|
// perform clipping
|
||||||
Polylines clipped;
|
Polylines clipped;
|
||||||
intersection(this->polyline, collection, clipped);
|
intersection<Polylines,Polylines>(this->polyline, collection, clipped);
|
||||||
return this->_inflate_collection(clipped, retval);
|
return this->_inflate_collection(clipped, retval);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,7 +48,7 @@ ExtrusionPath::subtract_expolygons(const ExPolygonCollection &collection, Extrus
|
||||||
{
|
{
|
||||||
// perform clipping
|
// perform clipping
|
||||||
Polylines clipped;
|
Polylines clipped;
|
||||||
diff(this->polyline, collection, clipped);
|
diff<Polylines,Polylines>(this->polyline, collection, clipped);
|
||||||
return this->_inflate_collection(clipped, retval);
|
return this->_inflate_collection(clipped, retval);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,6 +70,31 @@ ExtrusionPath::length() const
|
||||||
return this->polyline.length();
|
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
|
void
|
||||||
ExtrusionPath::_inflate_collection(const Polylines &polylines, ExtrusionEntityCollection* collection) const
|
ExtrusionPath::_inflate_collection(const Polylines &polylines, ExtrusionEntityCollection* collection) const
|
||||||
{
|
{
|
||||||
|
@ -106,8 +106,22 @@ ExtrusionPath::_inflate_collection(const Polylines &polylines, ExtrusionEntityCo
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef SLIC3RXS
|
#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");
|
REGISTER_CLASS(ExtrusionPath, "ExtrusionPath");
|
||||||
|
#endif
|
||||||
|
|
||||||
std::string
|
std::string
|
||||||
ExtrusionPath::gcode(Extruder* extruder, double e, double F,
|
ExtrusionPath::gcode(Extruder* extruder, double e, double F,
|
||||||
|
@ -155,12 +169,12 @@ ExtrusionPath::gcode(Extruder* extruder, double e, double F,
|
||||||
|
|
||||||
return stream.str();
|
return stream.str();
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
ExtrusionLoop::ExtrusionLoop(const Polygon &polygon, ExtrusionRole role)
|
ExtrusionLoop::operator Polygon() const
|
||||||
{
|
{
|
||||||
this->role = role;
|
Polygon polygon;
|
||||||
this->set_polygon(polygon);
|
this->polygon(&polygon);
|
||||||
|
return polygon;
|
||||||
}
|
}
|
||||||
|
|
||||||
ExtrusionLoop*
|
ExtrusionLoop*
|
||||||
|
@ -169,32 +183,19 @@ ExtrusionLoop::clone() const
|
||||||
return new ExtrusionLoop (*this);
|
return new ExtrusionLoop (*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
bool
|
||||||
ExtrusionLoop::split_at_index(int index, ExtrusionPath* path) const
|
ExtrusionLoop::make_clockwise()
|
||||||
{
|
{
|
||||||
Polygon polygon;
|
Polygon polygon = *this;
|
||||||
this->polygon(&polygon);
|
bool was_ccw = polygon.is_counter_clockwise();
|
||||||
|
if (was_ccw) this->reverse();
|
||||||
polygon.split_at_index(index, &path->polyline);
|
return was_ccw;
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ExtrusionLoop::make_counter_clockwise()
|
ExtrusionLoop::make_counter_clockwise()
|
||||||
{
|
{
|
||||||
Polygon polygon;
|
Polygon polygon = *this;
|
||||||
this->polygon(&polygon);
|
|
||||||
|
|
||||||
bool was_cw = polygon.is_clockwise();
|
bool was_cw = polygon.is_clockwise();
|
||||||
if (was_cw) this->reverse();
|
if (was_cw) this->reverse();
|
||||||
return was_cw;
|
return was_cw;
|
||||||
|
@ -203,41 +204,116 @@ ExtrusionLoop::make_counter_clockwise()
|
||||||
void
|
void
|
||||||
ExtrusionLoop::reverse()
|
ExtrusionLoop::reverse()
|
||||||
{
|
{
|
||||||
for (Polylines::iterator polyline = this->polylines.begin(); polyline != this->polylines.end(); ++polyline)
|
for (ExtrusionPaths::iterator path = this->paths.begin(); path != this->paths.end(); ++path)
|
||||||
polyline->reverse();
|
path->reverse();
|
||||||
std::reverse(this->polylines.begin(), this->polylines.end());
|
std::reverse(this->paths.begin(), this->paths.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
Point
|
Point
|
||||||
ExtrusionLoop::first_point() const
|
ExtrusionLoop::first_point() const
|
||||||
{
|
{
|
||||||
return this->polylines.front().points.front();
|
return this->paths.front().polyline.points.front();
|
||||||
}
|
}
|
||||||
|
|
||||||
Point
|
Point
|
||||||
ExtrusionLoop::last_point() const
|
ExtrusionLoop::last_point() const
|
||||||
{
|
{
|
||||||
return this->polylines.back().points.back(); // which coincides with first_point(), by the way
|
return this->paths.back().polyline.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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ExtrusionLoop::polygon(Polygon* polygon) const
|
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)
|
// 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
|
#ifdef SLIC3RXS
|
||||||
REGISTER_CLASS(ExtrusionLoop, "ExtrusionLoop");
|
REGISTER_CLASS(ExtrusionLoop, "ExtrusionLoop");
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -19,7 +19,7 @@ enum ExtrusionRole {
|
||||||
erFill,
|
erFill,
|
||||||
erSolidFill,
|
erSolidFill,
|
||||||
erTopSolidFill,
|
erTopSolidFill,
|
||||||
erBrige,
|
erBridge,
|
||||||
erInternalBridge,
|
erInternalBridge,
|
||||||
erSkirt,
|
erSkirt,
|
||||||
erSupportMaterial,
|
erSupportMaterial,
|
||||||
|
@ -29,19 +29,11 @@ enum ExtrusionRole {
|
||||||
class ExtrusionEntity
|
class ExtrusionEntity
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ExtrusionEntity() : mm3_per_mm(-1), width(-1), height(-1) {};
|
|
||||||
virtual ExtrusionEntity* clone() const = 0;
|
virtual ExtrusionEntity* clone() const = 0;
|
||||||
virtual ~ExtrusionEntity() {};
|
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 void reverse() = 0;
|
||||||
virtual Point first_point() const = 0;
|
virtual Point first_point() const = 0;
|
||||||
virtual Point last_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;
|
typedef std::vector<ExtrusionEntity*> ExtrusionEntitiesPtr;
|
||||||
|
@ -49,8 +41,14 @@ typedef std::vector<ExtrusionEntity*> ExtrusionEntitiesPtr;
|
||||||
class ExtrusionPath : public ExtrusionEntity
|
class ExtrusionPath : public ExtrusionEntity
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ExtrusionPath* clone() const;
|
|
||||||
Polyline polyline;
|
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();
|
void reverse();
|
||||||
Point first_point() const;
|
Point first_point() const;
|
||||||
Point last_point() const;
|
Point last_point() const;
|
||||||
|
@ -59,32 +57,41 @@ class ExtrusionPath : public ExtrusionEntity
|
||||||
void clip_end(double distance);
|
void clip_end(double distance);
|
||||||
void simplify(double tolerance);
|
void simplify(double tolerance);
|
||||||
double length() const;
|
double length() const;
|
||||||
|
bool is_perimeter() const;
|
||||||
#ifdef SLIC3RXS
|
bool is_fill() const;
|
||||||
|
bool is_bridge() const;
|
||||||
std::string gcode(Extruder* extruder, double e, double F,
|
std::string gcode(Extruder* extruder, double e, double F,
|
||||||
double xofs, double yofs, std::string extrusion_axis,
|
double xofs, double yofs, std::string extrusion_axis,
|
||||||
std::string gcode_line_suffix) const;
|
std::string gcode_line_suffix) const;
|
||||||
|
|
||||||
|
#ifdef SLIC3RXS
|
||||||
|
SV* to_SV_ref();
|
||||||
|
SV* to_SV_clone_ref() const;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void _inflate_collection(const Polylines &polylines, ExtrusionEntityCollection* collection) const;
|
void _inflate_collection(const Polylines &polylines, ExtrusionEntityCollection* collection) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef std::vector<ExtrusionPath> ExtrusionPaths;
|
||||||
|
|
||||||
class ExtrusionLoop : public ExtrusionEntity
|
class ExtrusionLoop : public ExtrusionEntity
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Polylines polylines;
|
ExtrusionPaths paths;
|
||||||
|
|
||||||
ExtrusionLoop(const Polygon &polygon, ExtrusionRole role);
|
operator Polygon() const;
|
||||||
ExtrusionLoop* clone() const;
|
ExtrusionLoop* clone() const;
|
||||||
void split_at_index(int index, ExtrusionPath* path) const;
|
bool make_clockwise();
|
||||||
void split_at_first_point(ExtrusionPath* path) const;
|
|
||||||
bool make_counter_clockwise();
|
bool make_counter_clockwise();
|
||||||
void reverse();
|
void reverse();
|
||||||
Point first_point() const;
|
Point first_point() const;
|
||||||
Point last_point() const;
|
Point last_point() const;
|
||||||
void set_polygon(const Polygon &polygon);
|
|
||||||
void polygon(Polygon* polygon) const;
|
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;
|
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
|
Points
|
||||||
MultiPoint::_douglas_peucker(const Points &points, const double tolerance)
|
MultiPoint::_douglas_peucker(const Points &points, const double tolerance)
|
||||||
{
|
{
|
||||||
|
|
|
@ -21,6 +21,7 @@ class MultiPoint
|
||||||
virtual Lines lines() const = 0;
|
virtual Lines lines() const = 0;
|
||||||
double length() const;
|
double length() const;
|
||||||
bool is_valid() const;
|
bool is_valid() const;
|
||||||
|
int find_point(const Point &point) const;
|
||||||
static Points _douglas_peucker(const Points &points, const double tolerance);
|
static Points _douglas_peucker(const Points &points, const double tolerance);
|
||||||
|
|
||||||
#ifdef SLIC3RXS
|
#ifdef SLIC3RXS
|
||||||
|
|
|
@ -15,6 +15,13 @@ Polygon::operator Polygons() const
|
||||||
return pp;
|
return pp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Polygon::operator Polyline() const
|
||||||
|
{
|
||||||
|
Polyline polyline;
|
||||||
|
this->split_at_first_point(&polyline);
|
||||||
|
return polyline;
|
||||||
|
}
|
||||||
|
|
||||||
Point&
|
Point&
|
||||||
Polygon::operator[](Points::size_type idx)
|
Polygon::operator[](Points::size_type idx)
|
||||||
{
|
{
|
||||||
|
|
|
@ -15,6 +15,7 @@ typedef std::vector<Polygon> Polygons;
|
||||||
class Polygon : public MultiPoint {
|
class Polygon : public MultiPoint {
|
||||||
public:
|
public:
|
||||||
operator Polygons() const;
|
operator Polygons() const;
|
||||||
|
operator Polyline() const;
|
||||||
Point& operator[](Points::size_type idx);
|
Point& operator[](Points::size_type idx);
|
||||||
const Point& operator[](Points::size_type idx) const;
|
const Point& operator[](Points::size_type idx) const;
|
||||||
Point last_point() const;
|
Point last_point() const;
|
||||||
|
|
|
@ -3,37 +3,86 @@
|
||||||
use strict;
|
use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
|
|
||||||
|
use List::Util qw(sum);
|
||||||
use Slic3r::XS;
|
use Slic3r::XS;
|
||||||
use Test::More tests => 8;
|
use Test::More tests => 30;
|
||||||
|
|
||||||
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';
|
|
||||||
|
|
||||||
{
|
{
|
||||||
my $path = $loop->split_at_first_point;
|
my $square = [
|
||||||
is_deeply $path->polyline->pp, [ @$square[0,1,2,3,0] ], 'split_at_first_point';
|
[100, 100],
|
||||||
is $path->role, $loop->role, 'role preserved after split';
|
[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__
|
__END__
|
||||||
|
|
|
@ -4,20 +4,20 @@ use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
|
|
||||||
use Slic3r::XS;
|
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, 100],
|
||||||
[200, 200],
|
[200, 200],
|
||||||
[100, 200],
|
[100, 200],
|
||||||
[100, 100],
|
[100, 100],
|
||||||
];
|
);
|
||||||
my $hole_in_square = [ # cw
|
my $hole_in_square = Slic3r::Polygon->new( # cw
|
||||||
[160, 140],
|
[160, 140],
|
||||||
[140, 140],
|
[140, 140],
|
||||||
[140, 160],
|
[140, 160],
|
||||||
[160, 160],
|
[160, 160],
|
||||||
];
|
);
|
||||||
my $expolygon = Slic3r::ExPolygon->new($square, $hole_in_square);
|
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__
|
__END__
|
||||||
|
|
|
@ -18,10 +18,12 @@ my $path = Slic3r::ExtrusionPath->new(
|
||||||
mm3_per_mm => 1,
|
mm3_per_mm => 1,
|
||||||
);
|
);
|
||||||
|
|
||||||
my $loop = Slic3r::ExtrusionLoop->new(
|
my $loop = Slic3r::ExtrusionLoop->new_from_paths(
|
||||||
polygon => Slic3r::Polygon->new(@$points),
|
Slic3r::ExtrusionPath->new(
|
||||||
role => Slic3r::ExtrusionPath::EXTR_ROLE_FILL,
|
polyline => Slic3r::Polygon->new(@$points)->split_at_first_point,
|
||||||
mm3_per_mm => 1,
|
role => Slic3r::ExtrusionPath::EXTR_ROLE_FILL,
|
||||||
|
mm3_per_mm => 1,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
my $collection = Slic3r::ExtrusionPath::Collection->new($path);
|
my $collection = Slic3r::ExtrusionPath::Collection->new($path);
|
||||||
|
|
|
@ -101,6 +101,15 @@ diff_pl(subject, clip)
|
||||||
OUTPUT:
|
OUTPUT:
|
||||||
RETVAL
|
RETVAL
|
||||||
|
|
||||||
|
Polylines
|
||||||
|
diff_ppl(subject, clip)
|
||||||
|
Polygons subject
|
||||||
|
Polygons clip
|
||||||
|
CODE:
|
||||||
|
diff(subject, clip, RETVAL);
|
||||||
|
OUTPUT:
|
||||||
|
RETVAL
|
||||||
|
|
||||||
Polygons
|
Polygons
|
||||||
intersection(subject, clip, safety_offset = false)
|
intersection(subject, clip, safety_offset = false)
|
||||||
Polygons subject
|
Polygons subject
|
||||||
|
@ -130,6 +139,15 @@ intersection_pl(subject, clip)
|
||||||
OUTPUT:
|
OUTPUT:
|
||||||
RETVAL
|
RETVAL
|
||||||
|
|
||||||
|
Polylines
|
||||||
|
intersection_ppl(subject, clip)
|
||||||
|
Polygons subject
|
||||||
|
Polygons clip
|
||||||
|
CODE:
|
||||||
|
intersection(subject, clip, RETVAL);
|
||||||
|
OUTPUT:
|
||||||
|
RETVAL
|
||||||
|
|
||||||
ExPolygons
|
ExPolygons
|
||||||
xor_ex(subject, clip, safety_offset = false)
|
xor_ex(subject, clip, safety_offset = false)
|
||||||
Polygons subject
|
Polygons subject
|
||||||
|
|
|
@ -21,6 +21,8 @@
|
||||||
bool contains_point(Point* point)
|
bool contains_point(Point* point)
|
||||||
%code{% RETVAL = THIS->contains_point(*point); %};
|
%code{% RETVAL = THIS->contains_point(*point); %};
|
||||||
void simplify(double tolerance);
|
void simplify(double tolerance);
|
||||||
|
Polygons polygons()
|
||||||
|
%code{% RETVAL = *THIS; %};
|
||||||
%{
|
%{
|
||||||
|
|
||||||
ExPolygonCollection*
|
ExPolygonCollection*
|
||||||
|
|
|
@ -7,92 +7,38 @@
|
||||||
%}
|
%}
|
||||||
|
|
||||||
%name{Slic3r::ExtrusionLoop} class ExtrusionLoop {
|
%name{Slic3r::ExtrusionLoop} class ExtrusionLoop {
|
||||||
|
ExtrusionLoop();
|
||||||
~ExtrusionLoop();
|
~ExtrusionLoop();
|
||||||
SV* arrayref()
|
Clone<ExtrusionLoop> clone()
|
||||||
%code{% Polygon polygon; THIS->polygon(&polygon); RETVAL = polygon.to_AV(); %};
|
%code{% RETVAL = THIS; %};
|
||||||
SV* pp()
|
|
||||||
%code{% Polygon polygon; THIS->polygon(&polygon); RETVAL = polygon.to_SV_pureperl(); %};
|
|
||||||
void reverse();
|
void reverse();
|
||||||
ExtrusionPath* split_at_index(int index)
|
bool make_clockwise();
|
||||||
%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_counter_clockwise();
|
bool make_counter_clockwise();
|
||||||
Clone<Point> first_point();
|
Clone<Point> first_point();
|
||||||
Clone<Point> last_point();
|
Clone<Point> last_point();
|
||||||
bool is_perimeter();
|
Polygon* polygon()
|
||||||
bool is_fill();
|
%code{% RETVAL = new Polygon (*THIS); %};
|
||||||
bool is_bridge();
|
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*
|
SV*
|
||||||
_new(CLASS, polygon_sv, role, mm3_per_mm, width, height)
|
ExtrusionLoop::arrayref()
|
||||||
char* CLASS;
|
|
||||||
SV* polygon_sv;
|
|
||||||
ExtrusionRole role;
|
|
||||||
double mm3_per_mm;
|
|
||||||
float width;
|
|
||||||
float height;
|
|
||||||
CODE:
|
CODE:
|
||||||
Polygon polygon;
|
AV* av = newAV();
|
||||||
polygon.from_SV_check(polygon_sv);
|
av_fill(av, THIS->paths.size()-1);
|
||||||
RETVAL = new ExtrusionLoop (polygon, role);
|
int i = 0;
|
||||||
RETVAL->mm3_per_mm = mm3_per_mm;
|
for (ExtrusionPaths::iterator it = THIS->paths.begin(); it != THIS->paths.end(); ++it) {
|
||||||
RETVAL->width = width;
|
av_store(av, i++, it->to_SV_ref());
|
||||||
RETVAL->height = height;
|
|
||||||
OUTPUT:
|
|
||||||
RETVAL
|
|
||||||
|
|
||||||
Polygon*
|
|
||||||
ExtrusionLoop::polygon(...)
|
|
||||||
CODE:
|
|
||||||
if (items > 1) {
|
|
||||||
Polygon polygon;
|
|
||||||
polygon.from_SV_check( ST(1) );
|
|
||||||
THIS->set_polygon(polygon);
|
|
||||||
}
|
}
|
||||||
RETVAL = new Polygon ();
|
RETVAL = newRV_noinc((SV*)av);
|
||||||
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;
|
|
||||||
OUTPUT:
|
OUTPUT:
|
||||||
RETVAL
|
RETVAL
|
||||||
|
|
||||||
|
|
|
@ -140,7 +140,7 @@ _constant()
|
||||||
EXTR_ROLE_FILL = erFill
|
EXTR_ROLE_FILL = erFill
|
||||||
EXTR_ROLE_SOLIDFILL = erSolidFill
|
EXTR_ROLE_SOLIDFILL = erSolidFill
|
||||||
EXTR_ROLE_TOPSOLIDFILL = erTopSolidFill
|
EXTR_ROLE_TOPSOLIDFILL = erTopSolidFill
|
||||||
EXTR_ROLE_BRIDGE = erBrige
|
EXTR_ROLE_BRIDGE = erBridge
|
||||||
EXTR_ROLE_INTERNALBRIDGE = erInternalBridge
|
EXTR_ROLE_INTERNALBRIDGE = erInternalBridge
|
||||||
EXTR_ROLE_SKIRT = erSkirt
|
EXTR_ROLE_SKIRT = erSkirt
|
||||||
EXTR_ROLE_SUPPORTMATERIAL = erSupportMaterial
|
EXTR_ROLE_SUPPORTMATERIAL = erSupportMaterial
|
||||||
|
|
|
@ -97,6 +97,7 @@ Lines T_ARRAYREF
|
||||||
Polygons T_ARRAYREF
|
Polygons T_ARRAYREF
|
||||||
Polylines T_ARRAYREF
|
Polylines T_ARRAYREF
|
||||||
ExPolygons T_ARRAYREF
|
ExPolygons T_ARRAYREF
|
||||||
|
ExtrusionPaths T_ARRAYREF
|
||||||
Surfaces T_ARRAYREF
|
Surfaces T_ARRAYREF
|
||||||
|
|
||||||
# we return these types whenever we want the items to be returned
|
# we return these types whenever we want the items to be returned
|
||||||
|
|
|
@ -70,6 +70,7 @@
|
||||||
%typemap{Polylines};
|
%typemap{Polylines};
|
||||||
%typemap{PrintState};
|
%typemap{PrintState};
|
||||||
%typemap{ExPolygons};
|
%typemap{ExPolygons};
|
||||||
|
%typemap{ExtrusionPaths};
|
||||||
%typemap{Surfaces};
|
%typemap{Surfaces};
|
||||||
%typemap{Polygons*};
|
%typemap{Polygons*};
|
||||||
%typemap{TriangleMeshPtrs};
|
%typemap{TriangleMeshPtrs};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue