From b4515cf695e0126191476e3363285af3be2efc11 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 23 Jul 2015 15:53:02 +0200 Subject: [PATCH] Finished porting PerimeterGenerator to C++ --- lib/Slic3r.pm | 2 +- lib/Slic3r/Layer/PerimeterGenerator.pm | 454 ------------------ lib/Slic3r/Layer/Region.pm | 30 +- t/perimeters.t | 190 ++++---- xs/MANIFEST | 3 + xs/src/libslic3r/ClipperUtils.cpp | 75 +++ xs/src/libslic3r/ClipperUtils.hpp | 18 + xs/src/libslic3r/ExPolygon.cpp | 17 +- xs/src/libslic3r/ExtrusionEntity.hpp | 2 +- .../libslic3r/ExtrusionEntityCollection.cpp | 21 +- .../libslic3r/ExtrusionEntityCollection.hpp | 12 +- xs/src/libslic3r/PerimeterGenerator.cpp | 114 ++--- xs/src/libslic3r/PerimeterGenerator.hpp | 20 +- xs/xsp/ExtrusionEntityCollection.xsp | 9 +- xs/xsp/PerimeterGenerator.xsp | 34 ++ xs/xsp/my.map | 4 + xs/xsp/typemap.xspt | 7 +- 17 files changed, 368 insertions(+), 644 deletions(-) delete mode 100644 lib/Slic3r/Layer/PerimeterGenerator.pm create mode 100644 xs/xsp/PerimeterGenerator.xsp diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index 9bcebdb9b..bcd337dec 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -54,7 +54,6 @@ use Slic3r::GCode::VibrationLimit; use Slic3r::Geometry qw(PI); use Slic3r::Geometry::Clipper; use Slic3r::Layer; -use Slic3r::Layer::PerimeterGenerator; use Slic3r::Layer::Region; use Slic3r::Line; use Slic3r::Model; @@ -200,6 +199,7 @@ sub thread_cleanup { *Slic3r::Geometry::BoundingBox::DESTROY = sub {}; *Slic3r::Geometry::BoundingBoxf::DESTROY = sub {}; *Slic3r::Geometry::BoundingBoxf3::DESTROY = sub {}; + *Slic3r::Layer::PerimeterGenerator::DESTROY = sub {}; *Slic3r::Line::DESTROY = sub {}; *Slic3r::Linef3::DESTROY = sub {}; *Slic3r::Model::DESTROY = sub {}; diff --git a/lib/Slic3r/Layer/PerimeterGenerator.pm b/lib/Slic3r/Layer/PerimeterGenerator.pm deleted file mode 100644 index d8d5008b8..000000000 --- a/lib/Slic3r/Layer/PerimeterGenerator.pm +++ /dev/null @@ -1,454 +0,0 @@ -package Slic3r::Layer::PerimeterGenerator; -use Moo; - -use Slic3r::ExtrusionLoop ':roles'; -use Slic3r::ExtrusionPath ':roles'; -use Slic3r::Geometry qw(scale unscale chained_path); -use Slic3r::Geometry::Clipper qw(union_ex diff diff_ex intersection_ex offset offset2 - offset_ex offset2_ex intersection_ppl diff_ppl); -use Slic3r::Surface ':types'; - -use constant INSET_OVERLAP_TOLERANCE => 0.4; - -has 'slices' => (is => 'ro', required => 1); # SurfaceCollection -has 'lower_slices' => (is => 'ro', required => 0); -has 'layer_height' => (is => 'ro', required => 1); -has 'layer_id' => (is => 'ro', required => 0, default => sub { -1 }); -has 'perimeter_flow' => (is => 'ro', required => 1); -has 'ext_perimeter_flow' => (is => 'ro', required => 1); -has 'overhang_flow' => (is => 'ro', required => 1); -has 'solid_infill_flow' => (is => 'ro', required => 1); -has 'config' => (is => 'ro', default => sub { Slic3r::Config::PrintRegion->new }); -has 'object_config' => (is => 'ro', default => sub { Slic3r::Config::PrintObject->new }); -has 'print_config' => (is => 'ro', default => sub { Slic3r::Config::Print->new }); -has '_lower_slices_p' => (is => 'rw', default => sub { [] }); -has '_ext_mm3_per_mm' => (is => 'rw'); -has '_mm3_per_mm' => (is => 'rw'); -has '_mm3_per_mm_overhang' => (is => 'rw'); - -# generated loops will be put here -has 'loops' => (is => 'ro', default => sub { Slic3r::ExtrusionPath::Collection->new }); - -# generated gap fills will be put here -has 'gap_fill' => (is => 'ro', default => sub { Slic3r::ExtrusionPath::Collection->new }); - -# generated fill surfaces will be put here -has 'fill_surfaces' => (is => 'ro', default => sub { Slic3r::Surface::Collection->new }); - -sub BUILDARGS { - my ($class, %args) = @_; - - if (my $flow = delete $args{flow}) { - $args{perimeter_flow} //= $flow; - $args{ext_perimeter_flow} //= $flow; - $args{overhang_flow} //= $flow; - $args{solid_infill_flow} //= $flow; - } - - return { %args }; -} - -sub __process { - my ($self) = @_; - - # other perimeters - $self->_mm3_per_mm($self->perimeter_flow->mm3_per_mm); - my $pwidth = $self->perimeter_flow->scaled_width; - my $pspacing = $self->perimeter_flow->scaled_spacing; - - # external perimeters - $self->_ext_mm3_per_mm($self->ext_perimeter_flow->mm3_per_mm); - my $ext_pwidth = $self->ext_perimeter_flow->scaled_width; - my $ext_pspacing = scale($self->ext_perimeter_flow->spacing_to($self->perimeter_flow)); - - # overhang perimeters - $self->_mm3_per_mm_overhang($self->overhang_flow->mm3_per_mm); - - # solid infill - my $ispacing = $self->solid_infill_flow->scaled_spacing; - my $gap_area_threshold = $pwidth ** 2; - - # Calculate the minimum required spacing between two adjacent traces. - # This should be equal to the nominal flow spacing but we experiment - # with some tolerance in order to avoid triggering medial axis when - # some squishing might work. Loops are still spaced by the entire - # flow spacing; this only applies to collapsing parts. - my $min_spacing = $pspacing * (1 - &Slic3r::INSET_OVERLAP_TOLERANCE); - my $ext_min_spacing = $ext_pspacing * (1 - &Slic3r::INSET_OVERLAP_TOLERANCE); - - # prepare grown lower layer slices for overhang detection - if ($self->lower_slices && $self->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->print_config->get_at('nozzle_diameter', $self->config->perimeter_extruder-1); - - $self->_lower_slices_p( - offset([ map @$_, @{$self->lower_slices} ], scale +$nozzle_diameter/2) - ); - } - - # we need to process each island separately because we might have different - # extra perimeters for each one - foreach my $surface (@{$self->slices}) { - # detect how many perimeters must be generated for this island - my $loop_number = $self->config->perimeters + ($surface->extra_perimeters || 0); - $loop_number--; # 0-indexed loops - - my @gaps = (); # Polygons - - my @last = @{$surface->expolygon->simplify_p(&Slic3r::SCALED_RESOLUTION)}; - if ($loop_number >= 0) { # no loops = -1 - - my @contours = (); # depth => [ Polygon, Polygon ... ] - my @holes = (); # depth => [ Polygon, Polygon ... ] - my @thin_walls = (); # Polylines - - # we loop one time more than needed in order to find gaps after the last perimeter was applied - for my $i (0..($loop_number+1)) { # outer loop is 0 - my @offsets = (); - if ($i == 0) { - # the minimum thickness of a single loop is: - # ext_width/2 + ext_spacing/2 + spacing/2 + width/2 - if ($self->config->thin_walls) { - @offsets = @{offset2( - \@last, - -(0.5*$ext_pwidth + 0.5*$ext_min_spacing - 1), - +(0.5*$ext_min_spacing - 1), - )}; - } else { - @offsets = @{offset( - \@last, - -0.5*$ext_pwidth, - )}; - } - - # look for thin walls - if ($self->config->thin_walls) { - my $diff = diff( - \@last, - offset(\@offsets, +0.5*$ext_pwidth), - 1, # medial axis requires non-overlapping geometry - ); - - # the following offset2 ensures almost nothing in @thin_walls is narrower than $min_width - # (actually, something larger than that still may exist due to mitering or other causes) - my $min_width = $ext_pwidth / 2; - @thin_walls = @{offset2_ex($diff, -$min_width/2, +$min_width/2)}; - - # the maximum thickness of our thin wall area is equal to the minimum thickness of a single loop - @thin_walls = grep $_->length > $ext_pwidth*2, - map @{$_->medial_axis($ext_pwidth + $ext_pspacing, $min_width)}, @thin_walls; - Slic3r::debugf " %d thin walls detected\n", scalar(@thin_walls) if $Slic3r::debug; - - if (0) { - require "Slic3r/SVG.pm"; - Slic3r::SVG::output( - "medial_axis.svg", - no_arrows => 1, - #expolygons => \@expp, - polylines => \@thin_walls, - ); - } - } - } else { - my $distance = ($i == 1) ? $ext_pspacing : $pspacing; - - if ($self->config->thin_walls) { - @offsets = @{offset2( - \@last, - -($distance + 0.5*$min_spacing - 1), - +(0.5*$min_spacing - 1), - )}; - } else { - @offsets = @{offset( - \@last, - -$distance, - )}; - } - - # look for gaps - if ($self->config->gap_fill_speed > 0 && $self->config->fill_density > 0) { - # not using safety offset here would "detect" very narrow gaps - # (but still long enough to escape the area threshold) that gap fill - # won't be able to fill but we'd still remove from infill area - my $diff = diff_ex( - offset(\@last, -0.5*$distance), - offset(\@offsets, +0.5*$distance + 10), # safety offset - ); - push @gaps, map $_->clone, map @$_, grep abs($_->area) >= $gap_area_threshold, @$diff; - } - } - - last if !@offsets; - last if $i > $loop_number; # we were only looking for gaps this time - - @last = @offsets; - - $contours[$i] = []; - $holes[$i] = []; - foreach my $polygon (@offsets) { - my $loop = Slic3r::Layer::PerimeterGenerator::Loop->new( - polygon => $polygon, - is_contour => $polygon->is_counter_clockwise, - depth => $i, - ); - if ($loop->is_contour) { - push @{$contours[$i]}, $loop; - } else { - push @{$holes[$i]}, $loop; - } - } - } - - # nest loops: holes first - for my $d (0..$loop_number) { - # loop through all holes having depth $d - LOOP: for (my $i = 0; $i <= $#{$holes[$d]}; ++$i) { - my $loop = $holes[$d][$i]; - - # find the hole loop that contains this one, if any - for my $t (($d+1)..$loop_number) { - for (my $j = 0; $j <= $#{$holes[$t]}; ++$j) { - my $candidate_parent = $holes[$t][$j]; - if ($candidate_parent->polygon->contains_point($loop->polygon->first_point)) { - $candidate_parent->add_child($loop); - splice @{$holes[$d]}, $i, 1; - --$i; - next LOOP; - } - } - } - - # if no hole contains this hole, find the contour loop that contains it - for my $t (reverse 0..$loop_number) { - for (my $j = 0; $j <= $#{$contours[$t]}; ++$j) { - my $candidate_parent = $contours[$t][$j]; - if ($candidate_parent->polygon->contains_point($loop->polygon->first_point)) { - $candidate_parent->add_child($loop); - splice @{$holes[$d]}, $i, 1; - --$i; - next LOOP; - } - } - } - } - } - - # nest contour loops - for my $d (reverse 1..$loop_number) { - # loop through all contours having depth $d - LOOP: for (my $i = 0; $i <= $#{$contours[$d]}; ++$i) { - my $loop = $contours[$d][$i]; - - # find the contour loop that contains it - for my $t (reverse 0..($d-1)) { - for (my $j = 0; $j <= $#{$contours[$t]}; ++$j) { - my $candidate_parent = $contours[$t][$j]; - if ($candidate_parent->polygon->contains_point($loop->polygon->first_point)) { - $candidate_parent->add_child($loop); - splice @{$contours[$d]}, $i, 1; - --$i; - next LOOP; - } - } - } - } - } - - # at this point, all loops should be in $contours[0] - my @entities = $self->_traverse_loops($contours[0], \@thin_walls); - - # if brim will be printed, reverse the order of perimeters so that - # we continue inwards after having finished the brim - # TODO: add test for perimeter order - @entities = reverse @entities - if $self->config->external_perimeters_first - || ($self->layer_id == 0 && $self->print_config->brim_width > 0); - - # append perimeters for this slice as a collection - $self->loops->append(Slic3r::ExtrusionPath::Collection->new(@entities)) - if @entities; - } - - # fill gaps - if (@gaps) { - if (0) { - require "Slic3r/SVG.pm"; - Slic3r::SVG::output( - "gaps.svg", - expolygons => union_ex(\@gaps), - ); - } - - # where $pwidth < thickness < 2*$pspacing, infill with width = 2*$pwidth - # where 0.1*$pwidth < thickness < $pwidth, infill with width = 1*$pwidth - my @gap_sizes = ( - [ $pwidth, 2*$pspacing, unscale 2*$pwidth ], - [ 0.1*$pwidth, $pwidth, unscale 1*$pwidth ], - ); - foreach my $gap_size (@gap_sizes) { - my @gap_fill = $self->_fill_gaps(@$gap_size, \@gaps); - $self->gap_fill->append($_) for @gap_fill; - - # Make sure we don't infill narrow parts that are already gap-filled - # (we only consider this surface's gaps to reduce the diff() complexity). - # Growing actual extrusions ensures that gaps not filled by medial axis - # are not subtracted from fill surfaces (they might be too short gaps - # that medial axis skips but infill might join with other infill regions - # and use zigzag). - my $w = $gap_size->[2]; - my @filled = map { - @{($_->isa('Slic3r::ExtrusionLoop') ? $_->polygon->split_at_first_point : $_->polyline) - ->grow(scale $w/2)}; - } @gap_fill; - @last = @{diff(\@last, \@filled)}; - @gaps = @{diff(\@gaps, \@filled)}; # prevent more gap fill here - } - } - - # create one more offset to be used as boundary for fill - # we offset by half the perimeter spacing (to get to the actual infill boundary) - # and then we offset back and forth by half the infill spacing to only consider the - # non-collapsing regions - my $inset = 0; - if ($loop_number == 0) { - # one loop - $inset += $ext_pspacing/2; - } elsif ($loop_number > 0) { - # two or more loops - $inset += $pspacing/2; - } - - # only apply infill overlap if we actually have one perimeter - $inset -= $self->config->get_abs_value_over('infill_overlap', $inset + $ispacing/2) - if $inset > 0; - - my $min_perimeter_infill_spacing = $ispacing * (1 - &Slic3r::INSET_OVERLAP_TOLERANCE); - $self->fill_surfaces->append($_) - for map Slic3r::Surface->new(expolygon => $_, surface_type => S_TYPE_INTERNAL), # use a bogus surface type - @{offset2_ex( - [ map @{$_->simplify_p(&Slic3r::SCALED_RESOLUTION)}, @{union_ex(\@last)} ], - -$inset -$min_perimeter_infill_spacing/2, - +$min_perimeter_infill_spacing/2, - )}; - } -} - -sub ___traverse_loops { - my ($self, $loops, $thin_walls) = @_; - - # loops is an arrayref of ::Loop objects - # turn each one into an ExtrusionLoop object - my $coll = Slic3r::ExtrusionPath::Collection->new; - foreach my $loop (@$loops) { - my $is_external = $loop->is_external; - - my ($role, $loop_role); - if ($is_external) { - $role = EXTR_ROLE_EXTERNAL_PERIMETER; - } else { - $role = EXTR_ROLE_PERIMETER; - } - if ($loop->is_internal_contour) { - # Note that we set loop role to ContourInternalPerimeter - # also when loop is both internal and external (i.e. - # there's only one contour loop). - $loop_role = EXTRL_ROLE_CONTOUR_INTERNAL_PERIMETER; - } else { - $loop_role = EXTRL_ROLE_DEFAULT; - } - - # detect overhanging/bridging perimeters - my @paths = (); - if ($self->config->overhangs && $self->layer_id > 0 - && !($self->object_config->support_material && $self->object_config->support_material_contact_distance == 0)) { - # get non-overhang paths by intersecting this loop with the grown lower slices - foreach my $polyline (@{ intersection_ppl([ $loop->polygon ], $self->_lower_slices_p) }) { - push @paths, Slic3r::ExtrusionPath->new( - polyline => $polyline, - role => $role, - mm3_per_mm => ($is_external ? $self->_ext_mm3_per_mm : $self->_mm3_per_mm), - width => ($is_external ? $self->ext_perimeter_flow->width : $self->perimeter_flow->width), - height => $self->layer_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([ $loop->polygon ], $self->_lower_slices_p) }) { - push @paths, Slic3r::ExtrusionPath->new( - polyline => $polyline, - role => EXTR_ROLE_OVERHANG_PERIMETER, - mm3_per_mm => $self->_mm3_per_mm_overhang, - width => $self->overhang_flow->width, - height => $self->overhang_flow->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); # temporary collection - @paths = map $_->clone, @{$collection->chained_path(0)}; - } else { - push @paths, Slic3r::ExtrusionPath->new( - polyline => $loop->polygon->split_at_first_point, - role => $role, - mm3_per_mm => ($is_external ? $self->_ext_mm3_per_mm : $self->_mm3_per_mm), - width => ($is_external ? $self->ext_perimeter_flow->width : $self->perimeter_flow->width), - height => $self->layer_height, - ); - } - my $eloop = Slic3r::ExtrusionLoop->new_from_paths(@paths); - $eloop->role($loop_role); - $coll->append($eloop); - } - - # append thin walls to the nearest-neighbor search (only for first iteration) - if (@$thin_walls) { - foreach my $polyline (@$thin_walls) { - $coll->append(Slic3r::ExtrusionPath->new( - polyline => $polyline, - role => EXTR_ROLE_EXTERNAL_PERIMETER, - mm3_per_mm => $self->_mm3_per_mm, - width => $self->perimeter_flow->width, - height => $self->layer_height, - )); - } - - @$thin_walls = (); - } - - # sort entities - my $sorted_coll = $coll->chained_path_indices(0); - my @indices = @{$sorted_coll->orig_indices}; - - # traverse children - my @entities = (); - for my $i (0..$#indices) { - my $idx = $indices[$i]; - if ($idx > $#$loops) { - # this is a thin wall - # let's get it from the sorted collection as it might have been reversed - push @entities, $sorted_coll->[$i]->clone; - } else { - my $loop = $loops->[$idx]; - my $eloop = $coll->[$idx]->clone; - - my @children = $self->_traverse_loops($loop->children, $thin_walls); - if ($loop->is_contour) { - $eloop->make_counter_clockwise; - push @entities, @children, $eloop; - } else { - $eloop->make_clockwise; - push @entities, $eloop, @children; - } - } - } - return @entities; -} - -1; diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index f65e90129..51648c3ed 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -33,23 +33,25 @@ sub make_perimeters { my $generator = Slic3r::Layer::PerimeterGenerator->new( # input: - config => $self->config, - object_config => $self->layer->object->config, - print_config => $self->layer->print->config, - layer_height => $self->height, - layer_id => $self->layer->id, - slices => $slices, - lower_slices => defined($self->layer->lower_layer) ? $self->layer->lower_layer->slices : undef, - perimeter_flow => $self->flow(FLOW_ROLE_PERIMETER), - ext_perimeter_flow => $self->flow(FLOW_ROLE_EXTERNAL_PERIMETER), - overhang_flow => $self->region->flow(FLOW_ROLE_PERIMETER, -1, 1, 0, -1, $self->layer->object), - solid_infill_flow => $self->flow(FLOW_ROLE_SOLID_INFILL), + $slices, + $self->height, + $self->flow(FLOW_ROLE_PERIMETER), + $self->config, + $self->layer->object->config, + $self->layer->print->config, # output: - loops => $self->perimeters, - gap_fill => $self->thin_fills, - fill_surfaces => $fill_surfaces, + $self->perimeters, + $self->thin_fills, + $fill_surfaces, ); + $generator->set_lower_slices($self->layer->lower_layer->slices) + if defined($self->layer->lower_layer); + $generator->set_layer_id($self->id); + $generator->set_ext_perimeter_flow($self->flow(FLOW_ROLE_EXTERNAL_PERIMETER)); + $generator->set_overhang_flow($self->region->flow(FLOW_ROLE_PERIMETER, -1, 1, 0, -1, $self->layer->object)); + $generator->set_solid_infill_flow($self->flow(FLOW_ROLE_SOLID_INFILL)); + $generator->process; } diff --git a/t/perimeters.t b/t/perimeters.t index d103eb3f2..1e47b51f0 100644 --- a/t/perimeters.t +++ b/t/perimeters.t @@ -17,6 +17,101 @@ use Slic3r::Geometry::Clipper qw(union_ex diff union offset); use Slic3r::Surface ':types'; use Slic3r::Test; +{ + my $flow = Slic3r::Flow->new( + width => 1, + height => 1, + nozzle_diameter => 1, + ); + + my $config = Slic3r::Config->new; + my $test = sub { + my ($expolygons, %expected) = @_; + + my $slices = Slic3r::Surface::Collection->new; + $slices->append(Slic3r::Surface->new( + surface_type => S_TYPE_INTERNAL, + expolygon => $_, + )) for @$expolygons; + + my ($region_config, $object_config, $print_config, $loops, $gap_fill, $fill_surfaces); + my $g = Slic3r::Layer::PerimeterGenerator->new( + # input: + $slices, + 1, # layer height + $flow, + ($region_config = Slic3r::Config::PrintRegion->new), + ($object_config = Slic3r::Config::PrintObject->new), + ($print_config = Slic3r::Config::Print->new), + + # output: + ($loops = Slic3r::ExtrusionPath::Collection->new), + ($gap_fill = Slic3r::ExtrusionPath::Collection->new), + ($fill_surfaces = Slic3r::Surface::Collection->new), + ); + $g->config->apply_dynamic($config); + $g->process; + + is scalar(@$loops), + scalar(@$expolygons), 'expected number of collections'; + ok !defined(first { !$_->isa('Slic3r::ExtrusionPath::Collection') } @$loops), + 'everything is returned as collections'; + + my $flattened_loops = $loops->flatten; + my @loops = @$flattened_loops; + is scalar(@loops), + $expected{total}, 'expected number of loops'; + is scalar(grep $_->role == EXTR_ROLE_EXTERNAL_PERIMETER, map @$_, @loops), + $expected{external}, 'expected number of external loops'; + is scalar(grep $_->role == EXTRL_ROLE_CONTOUR_INTERNAL_PERIMETER, @loops), + $expected{cinternal}, 'expected number of internal contour loops'; + is scalar(grep $_->polygon->is_counter_clockwise, @loops), + $expected{ccw}, 'expected number of ccw loops'; + }; + + $config->set('perimeters', 3); + $test->( + [ + Slic3r::ExPolygon->new( + Slic3r::Polygon->new_scale([0,0], [100,0], [100,100], [0,100]), + ), + ], + total => 3, + external => 1, + cinternal => 1, + ccw => 3, + ); + $test->( + [ + Slic3r::ExPolygon->new( + Slic3r::Polygon->new_scale([0,0], [100,0], [100,100], [0,100]), + Slic3r::Polygon->new_scale([40,40], [40,60], [60,60], [60,40]), + ), + ], + total => 6, + external => 2, + cinternal => 1, + ccw => 3, + ); + $test->( + [ + Slic3r::ExPolygon->new( + Slic3r::Polygon->new_scale([0,0], [200,0], [200,200], [0,200]), + Slic3r::Polygon->new_scale([20,20], [20,180], [180,180], [180,20]), + ), + # nested: + Slic3r::ExPolygon->new( + Slic3r::Polygon->new_scale([50,50], [150,50], [150,150], [50,150]), + Slic3r::Polygon->new_scale([80,80], [80,120], [120,120], [120,80]), + ), + ], + total => 4*3, + external => 4, + cinternal => 2, + ccw => 2*3, + ); +} + { my $config = Slic3r::Config->new_from_defaults; $config->set('skirts', 0); @@ -215,7 +310,7 @@ use Slic3r::Test; [ map @$_, (@$covered_by_perimeters, @$covered_by_infill) ], ); - if (0) { + if (1) { printf "max non covered = %f\n", List::Util::max(map unscale unscale $_->area, @$non_covered); require "Slic3r/SVG.pm"; Slic3r::SVG::output( @@ -223,9 +318,12 @@ use Slic3r::Test; expolygons => [ map $_->expolygon, @{$layerm->slices} ], red_expolygons => union_ex([ map @$_, (@$covered_by_perimeters, @$covered_by_infill) ]), green_expolygons => union_ex($non_covered), + no_arrows => 1, + polylines => [ + map $_->polygon->split_at_first_point, map @$_, @{$layerm->perimeters}, + ], ); } - ok !(defined first { $_->area > ($iflow->scaled_width**2) } @$non_covered), 'no gap between perimeters and infill'; } @@ -299,92 +397,4 @@ use Slic3r::Test; $test->('small_dorito'); } -{ - my $flow = Slic3r::Flow->new( - width => 1, - height => 1, - nozzle_diameter => 1, - ); - - my $config = Slic3r::Config->new; - my $test = sub { - my ($expolygons, %expected) = @_; - - my $slices = Slic3r::Surface::Collection->new; - $slices->append(Slic3r::Surface->new( - surface_type => S_TYPE_INTERNAL, - expolygon => $_, - )) for @$expolygons; - - my $g = Slic3r::Layer::PerimeterGenerator->new( - # input: - layer_height => 1, - slices => $slices, - flow => $flow, - ); - $g->config->apply_dynamic($config); - $g->process; - - is scalar(@{$g->loops}), - scalar(@$expolygons), 'expected number of collections'; - ok !defined(first { !$_->isa('Slic3r::ExtrusionPath::Collection') } @{$g->loops}), - 'everything is returned as collections'; - - my $flattened_loops = $g->loops->flatten; - my @loops = @$flattened_loops; - is scalar(@loops), - $expected{total}, 'expected number of loops'; - is scalar(grep $_->role == EXTR_ROLE_EXTERNAL_PERIMETER, map @$_, @loops), - $expected{external}, 'expected number of external loops'; - is scalar(grep $_->role == EXTRL_ROLE_CONTOUR_INTERNAL_PERIMETER, @loops), - $expected{cinternal}, 'expected number of internal contour loops'; - is scalar(grep $_->polygon->is_counter_clockwise, @loops), - $expected{ccw}, 'expected number of ccw loops'; - - return $g; - }; - - $config->set('perimeters', 3); - $test->( - [ - Slic3r::ExPolygon->new( - Slic3r::Polygon->new_scale([0,0], [100,0], [100,100], [0,100]), - ), - ], - total => 3, - external => 1, - cinternal => 1, - ccw => 3, - ); - $test->( - [ - Slic3r::ExPolygon->new( - Slic3r::Polygon->new_scale([0,0], [100,0], [100,100], [0,100]), - Slic3r::Polygon->new_scale([40,40], [40,60], [60,60], [60,40]), - ), - ], - total => 6, - external => 2, - cinternal => 1, - ccw => 3, - ); - $test->( - [ - Slic3r::ExPolygon->new( - Slic3r::Polygon->new_scale([0,0], [200,0], [200,200], [0,200]), - Slic3r::Polygon->new_scale([20,20], [20,180], [180,180], [180,20]), - ), - # nested: - Slic3r::ExPolygon->new( - Slic3r::Polygon->new_scale([50,50], [150,50], [150,150], [50,150]), - Slic3r::Polygon->new_scale([80,80], [80,120], [120,120], [120,80]), - ), - ], - total => 4*3, - external => 4, - cinternal => 2, - ccw => 2*3, - ); -} - __END__ diff --git a/xs/MANIFEST b/xs/MANIFEST index 2011b9cf3..36ac111f6 100644 --- a/xs/MANIFEST +++ b/xs/MANIFEST @@ -1690,6 +1690,8 @@ src/libslic3r/MotionPlanner.cpp src/libslic3r/MotionPlanner.hpp src/libslic3r/MultiPoint.cpp src/libslic3r/MultiPoint.hpp +src/libslic3r/PerimeterGenerator.cpp +src/libslic3r/PerimeterGenerator.hpp src/libslic3r/PlaceholderParser.cpp src/libslic3r/PlaceholderParser.hpp src/libslic3r/Point.cpp @@ -1774,6 +1776,7 @@ xsp/Model.xsp xsp/MotionPlanner.xsp xsp/my.map xsp/mytype.map +xsp/PerimeterGenerator.xsp xsp/PlaceholderParser.xsp xsp/Point.xsp xsp/Polygon.xsp diff --git a/xs/src/libslic3r/ClipperUtils.cpp b/xs/src/libslic3r/ClipperUtils.cpp index ba243ee82..baddd8f45 100644 --- a/xs/src/libslic3r/ClipperUtils.cpp +++ b/xs/src/libslic3r/ClipperUtils.cpp @@ -136,6 +136,15 @@ offset(const Slic3r::Polygons &polygons, Slic3r::Polygons* retval, const float d ClipperPaths_to_Slic3rMultiPoints(output, retval); } +Slic3r::Polygons +offset(const Slic3r::Polygons &polygons, const float delta, + double scale, ClipperLib::JoinType joinType, double miterLimit) +{ + Slic3r::Polygons pp; + offset(polygons, &pp, delta, scale, joinType, miterLimit); + return pp; +} + void offset(const Slic3r::Polylines &polylines, ClipperLib::Paths* retval, const float delta, double scale, ClipperLib::JoinType joinType, double miterLimit) @@ -248,6 +257,15 @@ offset2(const Slic3r::Polygons &polygons, Slic3r::Polygons* retval, const float ClipperPaths_to_Slic3rMultiPoints(output, retval); } +Slic3r::Polygons +offset2(const Slic3r::Polygons &polygons, const float delta1, + const float delta2, const double scale, const ClipperLib::JoinType joinType, const double miterLimit) +{ + Slic3r::Polygons pp; + offset2(polygons, &pp, delta1, delta2, scale, joinType, miterLimit); + return pp; +} + void offset2(const Slic3r::Polygons &polygons, Slic3r::ExPolygons* retval, const float delta1, const float delta2, const double scale, const ClipperLib::JoinType joinType, const double miterLimit) @@ -260,6 +278,15 @@ offset2(const Slic3r::Polygons &polygons, Slic3r::ExPolygons* retval, const floa ClipperPaths_to_Slic3rExPolygons(output, retval); } +Slic3r::ExPolygons +offset2_ex(const Slic3r::Polygons &polygons, const float delta1, + const float delta2, const double scale, const ClipperLib::JoinType joinType, const double miterLimit) +{ + Slic3r::ExPolygons expp; + offset2(polygons, &expp, delta1, delta2, scale, joinType, miterLimit); + return expp; +} + template void _clipper_do(const ClipperLib::ClipType clipType, const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, T* retval, const ClipperLib::PolyFillType fillType, const bool safety_offset_) @@ -437,6 +464,22 @@ void diff(const SubjectType &subject, const Slic3r::ExPolygons &clip, ResultType } template void diff(const Slic3r::Polygons &subject, const Slic3r::ExPolygons &clip, Slic3r::ExPolygons* retval, bool safety_offset_); +Slic3r::Polygons +diff(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_) +{ + Slic3r::Polygons pp; + diff(subject, clip, &pp, safety_offset_); + return pp; +} + +Slic3r::ExPolygons +diff_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_) +{ + Slic3r::ExPolygons expp; + diff(subject, clip, &expp, safety_offset_); + return expp; +} + template void intersection(const SubjectType &subject, const Slic3r::Polygons &clip, ResultType* retval, bool safety_offset_) { @@ -448,6 +491,22 @@ template void intersection(const Slic3r::Po template void intersection(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, Slic3r::Polylines* retval, bool safety_offset_); template void intersection(const Slic3r::Lines &subject, const Slic3r::Polygons &clip, Slic3r::Lines* retval, bool safety_offset_); +Slic3r::Polygons +intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_) +{ + Slic3r::Polygons pp; + intersection(subject, clip, &pp, safety_offset_); + return pp; +} + +Slic3r::Polylines +intersection(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, bool safety_offset_) +{ + Slic3r::Polylines pp; + intersection(subject, clip, &pp, safety_offset_); + return pp; +} + template bool intersects(const SubjectType &subject, const Slic3r::Polygons &clip, bool safety_offset_) { @@ -474,6 +533,22 @@ void union_(const Slic3r::Polygons &subject, T* retval, bool safety_offset_) template void union_(const Slic3r::Polygons &subject, Slic3r::ExPolygons* retval, bool safety_offset_); template void union_(const Slic3r::Polygons &subject, Slic3r::Polygons* retval, bool safety_offset_); +Slic3r::Polygons +union_(const Slic3r::Polygons &subject, bool safety_offset) +{ + Polygons pp; + union_(subject, &pp, safety_offset); + return pp; +} + +Slic3r::ExPolygons +union_ex(const Slic3r::Polygons &subject, bool safety_offset) +{ + ExPolygons expp; + union_(subject, &expp, safety_offset); + return expp; +} + void union_(const Slic3r::Polygons &subject1, const Slic3r::Polygons &subject2, Slic3r::Polygons* retval, bool safety_offset) { Polygons pp = subject1; diff --git a/xs/src/libslic3r/ClipperUtils.hpp b/xs/src/libslic3r/ClipperUtils.hpp index ab144f202..4a3ba2e5c 100644 --- a/xs/src/libslic3r/ClipperUtils.hpp +++ b/xs/src/libslic3r/ClipperUtils.hpp @@ -40,6 +40,9 @@ void offset(const Slic3r::Polygons &polygons, ClipperLib::Paths* retval, const f void offset(const Slic3r::Polygons &polygons, Slic3r::Polygons* retval, const float delta, double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3); +Slic3r::Polygons offset(const Slic3r::Polygons &polygons, const float delta, + double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter, + double miterLimit = 3); // offset Polylines void offset(const Slic3r::Polylines &polylines, ClipperLib::Paths* retval, const float delta, @@ -62,9 +65,15 @@ void offset2(const Slic3r::Polygons &polygons, ClipperLib::Paths* retval, const void offset2(const Slic3r::Polygons &polygons, Slic3r::Polygons* retval, const float delta1, const float delta2, double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3); +Slic3r::Polygons offset2(const Slic3r::Polygons &polygons, const float delta1, + const float delta2, double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter, + double miterLimit = 3); void offset2(const Slic3r::Polygons &polygons, Slic3r::ExPolygons* retval, const float delta1, const float delta2, double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3); +Slic3r::ExPolygons offset2_ex(const Slic3r::Polygons &polygons, const float delta1, + const float delta2, double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter, + double miterLimit = 3); template void _clipper_do(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject, @@ -86,9 +95,15 @@ void diff(const SubjectType &subject, const Slic3r::Polygons &clip, ResultType* template void diff(const SubjectType &subject, const Slic3r::ExPolygons &clip, ResultType* retval, bool safety_offset_ = false); +Slic3r::Polygons diff(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false); +Slic3r::ExPolygons diff_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false); + template void intersection(const SubjectType &subject, const Slic3r::Polygons &clip, ResultType* retval, bool safety_offset_ = false); +Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false); +Slic3r::Polylines intersection(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false); + template bool intersects(const SubjectType &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false); @@ -98,6 +113,9 @@ void xor_(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r: template void union_(const Slic3r::Polygons &subject, T* retval, bool safety_offset_ = false); +Slic3r::Polygons union_(const Slic3r::Polygons &subject, bool safety_offset = false); +Slic3r::ExPolygons union_ex(const Slic3r::Polygons &subject, bool safety_offset = false); + void union_(const Slic3r::Polygons &subject1, const Slic3r::Polygons &subject2, Slic3r::Polygons* retval, bool safety_offset = false); void union_pt(const Slic3r::Polygons &subject, ClipperLib::PolyTree* retval, bool safety_offset_ = false); diff --git a/xs/src/libslic3r/ExPolygon.cpp b/xs/src/libslic3r/ExPolygon.cpp index 83e1929aa..ee0473656 100644 --- a/xs/src/libslic3r/ExPolygon.cpp +++ b/xs/src/libslic3r/ExPolygon.cpp @@ -187,16 +187,17 @@ ExPolygon::medial_axis(double max_width, double min_width, Polylines* polylines) } // compute the Voronoi diagram - ma.build(polylines); + Polylines pp; + ma.build(&pp); // clip segments to our expolygon area // (do this before extending endpoints as external segments coule be extended into // expolygon, this leaving wrong things inside) - intersection(*polylines, *this, polylines); + pp = intersection(pp, *this); // extend initial and final segments of each polyline (they will be clipped) // unless they represent closed loops - for (Polylines::iterator polyline = polylines->begin(); polyline != polylines->end(); ++polyline) { + for (Polylines::iterator polyline = pp.begin(); polyline != pp.end(); ++polyline) { if (polyline->points.front().distance_to(polyline->points.back()) < min_width) continue; // TODO: we should *not* extend endpoints where other polylines start/end // (such as T joints, which are returned as three polylines by MedialAxis) @@ -205,18 +206,20 @@ ExPolygon::medial_axis(double max_width, double min_width, Polylines* polylines) } // clip again after extending endpoints to prevent them from exceeding the expolygon boundaries - intersection(*polylines, *this, polylines); + pp = intersection(pp, *this); // remove too short polylines // (we can't do this check before endpoints extension and clipping because we don't // know how long will the endpoints be extended since it depends on polygon thickness // which is variable - extension will be <= max_width/2 on each side) - for (size_t i = 0; i < polylines->size(); ++i) { - if ((*polylines)[i].length() < max_width) { - polylines->erase(polylines->begin() + i); + for (size_t i = 0; i < pp.size(); ++i) { + if (pp[i].length() < max_width) { + pp.erase(pp.begin() + i); --i; } } + + polylines->insert(polylines->end(), pp.begin(), pp.end()); } void diff --git a/xs/src/libslic3r/ExtrusionEntity.hpp b/xs/src/libslic3r/ExtrusionEntity.hpp index 0cbabd1b8..c1eef6af3 100644 --- a/xs/src/libslic3r/ExtrusionEntity.hpp +++ b/xs/src/libslic3r/ExtrusionEntity.hpp @@ -127,7 +127,7 @@ class ExtrusionLoop : public ExtrusionEntity Polygons grow() const; double min_mm3_per_mm() const; Polyline as_polyline() const { - return this->polygon()->split_at_first_point(); + return this->polygon().split_at_first_point(); }; }; diff --git a/xs/src/libslic3r/ExtrusionEntityCollection.cpp b/xs/src/libslic3r/ExtrusionEntityCollection.cpp index 5904252ff..eb77aaede 100644 --- a/xs/src/libslic3r/ExtrusionEntityCollection.cpp +++ b/xs/src/libslic3r/ExtrusionEntityCollection.cpp @@ -8,7 +8,7 @@ namespace Slic3r { ExtrusionEntityCollection::ExtrusionEntityCollection(const ExtrusionEntityCollection& collection) : no_sort(collection.no_sort), orig_indices(collection.orig_indices) { - this->append(collection); + this->append(collection.entities); } ExtrusionEntityCollection::ExtrusionEntityCollection(const ExtrusionPaths &paths) @@ -32,6 +32,12 @@ ExtrusionEntityCollection::swap (ExtrusionEntityCollection &c) std::swap(this->no_sort, c.no_sort); } +ExtrusionEntityCollection::~ExtrusionEntityCollection() +{ + for (ExtrusionEntitiesPtr::iterator it = this->entities.begin(); it != this->entities.end(); ++it) + delete *it; +} + ExtrusionEntityCollection::operator ExtrusionPaths() const { ExtrusionPaths paths; @@ -45,7 +51,11 @@ ExtrusionEntityCollection::operator ExtrusionPaths() const ExtrusionEntityCollection* ExtrusionEntityCollection::clone() const { - return new ExtrusionEntityCollection(*this); + ExtrusionEntityCollection* coll = new ExtrusionEntityCollection(*this); + for (size_t i = 0; i < coll->entities.size(); ++i) { + coll->entities[i] = this->entities[i]->clone(); + } + return coll; } void @@ -78,9 +88,10 @@ ExtrusionEntityCollection::append(const ExtrusionEntity &entity) } void -ExtrusionEntityCollection::append(const ExtrusionEntityCollection &collection) +ExtrusionEntityCollection::append(const ExtrusionEntitiesPtr &entities) { - this->entities.insert(this->entities.end(), collection.entities.begin(), collection.entities.end()); + for (ExtrusionEntitiesPtr::const_iterator ptr = entities.begin(); ptr != entities.end(); ++ptr) + this->append(**ptr); } void @@ -186,7 +197,7 @@ ExtrusionEntityCollection::flatten(ExtrusionEntityCollection* retval) const for (ExtrusionEntitiesPtr::const_iterator it = this->entities.begin(); it != this->entities.end(); ++it) { if ((*it)->is_collection()) { ExtrusionEntityCollection* collection = dynamic_cast(*it); - retval->append(collection->flatten()); + retval->append(collection->flatten().entities); } else { retval->append(**it); } diff --git a/xs/src/libslic3r/ExtrusionEntityCollection.hpp b/xs/src/libslic3r/ExtrusionEntityCollection.hpp index 1cb288d0a..504c82ae1 100644 --- a/xs/src/libslic3r/ExtrusionEntityCollection.hpp +++ b/xs/src/libslic3r/ExtrusionEntityCollection.hpp @@ -10,13 +10,14 @@ class ExtrusionEntityCollection : public ExtrusionEntity { public: ExtrusionEntityCollection* clone() const; - ExtrusionEntitiesPtr entities; + ExtrusionEntitiesPtr entities; // we own these entities std::vector orig_indices; // handy for XS bool no_sort; ExtrusionEntityCollection(): no_sort(false) {}; ExtrusionEntityCollection(const ExtrusionEntityCollection &collection); ExtrusionEntityCollection(const ExtrusionPaths &paths); ExtrusionEntityCollection& operator= (const ExtrusionEntityCollection &other); + ~ExtrusionEntityCollection(); operator ExtrusionPaths() const; bool is_collection() const { @@ -25,9 +26,12 @@ class ExtrusionEntityCollection : public ExtrusionEntity bool can_reverse() const { return !this->no_sort; }; + bool empty() const { + return this->entities.empty(); + }; void swap (ExtrusionEntityCollection &c); void append(const ExtrusionEntity &entity); - void append(const ExtrusionEntityCollection &collection); + void append(const ExtrusionEntitiesPtr &entities); void append(const ExtrusionPaths &paths); ExtrusionEntityCollection chained_path(bool no_reverse = false, std::vector* orig_indices = NULL) const; void chained_path(ExtrusionEntityCollection* retval, bool no_reverse = false, std::vector* orig_indices = NULL) const; @@ -40,6 +44,10 @@ class ExtrusionEntityCollection : public ExtrusionEntity void flatten(ExtrusionEntityCollection* retval) const; ExtrusionEntityCollection flatten() const; double min_mm3_per_mm() const; + Polyline as_polyline() const { + CONFESS("Calling as_polyline() on a ExtrusionEntityCollection"); + return Polyline(); + }; }; } diff --git a/xs/src/libslic3r/PerimeterGenerator.cpp b/xs/src/libslic3r/PerimeterGenerator.cpp index a03598db7..f95930e2d 100644 --- a/xs/src/libslic3r/PerimeterGenerator.cpp +++ b/xs/src/libslic3r/PerimeterGenerator.cpp @@ -1,4 +1,6 @@ #include "PerimeterGenerator.hpp" +#include "ClipperUtils.hpp" +#include "ExtrusionEntityCollection.hpp" namespace Slic3r { @@ -12,14 +14,14 @@ PerimeterGenerator::process() // external perimeters this->_ext_mm3_per_mm = this->ext_perimeter_flow.mm3_per_mm(); - coord_t = ext_pwidth = this->ext_perimeter_flow.scaled_width(); - coord_t = ext_pspacing = scale_(this->ext_perimeter_flow.spacing_to(this->perimeter_flow)); + coord_t ext_pwidth = this->ext_perimeter_flow.scaled_width(); + coord_t ext_pspacing = scale_(this->ext_perimeter_flow.spacing(this->perimeter_flow)); // overhang perimeters this->_mm3_per_mm_overhang = this->overhang_flow.mm3_per_mm(); // solid infill - coord_t ispacing = this->solid_infill_flow->scaled_spacing; + coord_t ispacing = this->solid_infill_flow.scaled_spacing(); coord_t gap_area_threshold = pwidth * pwidth; // Calculate the minimum required spacing between two adjacent traces. @@ -37,7 +39,7 @@ PerimeterGenerator::process() // in the current layer double nozzle_diameter = this->print_config->nozzle_diameter.get_at(this->config->perimeter_extruder-1); - this->_lower_slices_p = offset(this->lower_slices, scale_(+nozzle_diameter/2)); + this->_lower_slices_p = offset(*this->lower_slices, scale_(+nozzle_diameter/2)); } // we need to process each island separately because we might have different @@ -53,8 +55,8 @@ PerimeterGenerator::process() Polygons last = surface->expolygon.simplify_p(SCALED_RESOLUTION); if (loop_number >= 0) { // no loops = -1 - std::vector contours(loop_number); // depth => loops - std::vector holes(loop_number); // depth => loops + std::vector contours(loop_number+1); // depth => loops + std::vector holes(loop_number+1); // depth => loops Polylines thin_walls; // we loop one time more than needed in order to find gaps after the last perimeter was applied @@ -65,7 +67,7 @@ PerimeterGenerator::process() // ext_width/2 + ext_spacing/2 + spacing/2 + width/2 if (this->config->thin_walls) { offsets = offset2( - \@last, + last, -(0.5*ext_pwidth + 0.5*ext_min_spacing - 1), +(0.5*ext_min_spacing - 1) ); @@ -75,7 +77,7 @@ PerimeterGenerator::process() // look for thin walls if (this->config->thin_walls) { - Polygons diff = diff( + Polygons diffpp = diff( last, offset(offsets, +0.5*ext_pwidth), true // medial axis requires non-overlapping geometry @@ -84,7 +86,7 @@ PerimeterGenerator::process() // the following offset2 ensures almost nothing in @thin_walls is narrower than $min_width // (actually, something larger than that still may exist due to mitering or other causes) coord_t min_width = ext_pwidth / 2; - ExPolygons expp = offset2(diff, -min_width/2, +min_width/2)}; + ExPolygons expp = offset2_ex(diffpp, -min_width/2, +min_width/2); // the maximum thickness of our thin wall area is equal to the minimum thickness of a single loop Polylines pp; @@ -121,27 +123,29 @@ PerimeterGenerator::process() offsets = offset2( last, -(distance + 0.5*min_spacing - 1), - +(0.5*min_spacing - 1), + +(0.5*min_spacing - 1) ); } else { offsets = offset( last, - -distance, + -distance ); } // look for gaps - if (this->config->gap_fill_speed > 0 && this->config->fill_density > 0) { + if (this->config->gap_fill_speed.value > 0 && this->config->fill_density.value > 0) { // not using safety offset here would "detect" very narrow gaps // (but still long enough to escape the area threshold) that gap fill // won't be able to fill but we'd still remove from infill area - ExPolygons diff = diff_ex( + ExPolygons diff_expp = diff_ex( offset(last, -0.5*distance), - offset(offsets, +0.5*distance + 10), // safety offset + offset(offsets, +0.5*distance + 10) // safety offset ); - for (ExPolygons::const_iterator ex = diff.begin(); ex != diff.end(); ++ex) { - if (fabs(ex->area()) >= gap_area_threshold) - gaps.push_back(*ex); + for (ExPolygons::const_iterator ex = diff_expp.begin(); ex != diff_expp.end(); ++ex) { + if (fabs(ex->area()) >= gap_area_threshold) { + Polygons pp = *ex; + gaps.insert(gaps.end(), pp.begin(), pp.end()); + } } } } @@ -150,7 +154,6 @@ PerimeterGenerator::process() if (i > loop_number) break; // we were only looking for gaps this time last = offsets; - for (Polygons::const_iterator polygon = offsets.begin(); polygon != offsets.end(); ++polygon) { PerimeterGeneratorLoop loop(*polygon, i); loop.is_contour = polygon->is_counter_clockwise(); @@ -163,7 +166,7 @@ PerimeterGenerator::process() } // nest loops: holes first - for (unsigned short d = 0; <= loop_number; ++d) { + for (unsigned short d = 0; d <= loop_number; ++d) { PerimeterGeneratorLoops &holes_d = holes[d]; // loop through all holes having depth == d @@ -172,13 +175,13 @@ PerimeterGenerator::process() // find the hole loop that contains this one, if any for (unsigned short t = d+1; t <= loop_number; ++t) { - for (unsigned short j = 0; j < holes_d.size(); ++j) { + for (unsigned short j = 0; j < holes[t].size(); ++j) { PerimeterGeneratorLoop &candidate_parent = holes[t][j]; if (candidate_parent.polygon.contains(loop.polygon.first_point())) { - candidate_parent.add_child(loop); + candidate_parent.children.push_back(loop); holes_d.erase(holes_d.begin() + i); --i; - goto NEXT_HOLE; + goto NEXT_LOOP; } } } @@ -188,15 +191,15 @@ PerimeterGenerator::process() for (unsigned short j = 0; j < contours[t].size(); ++j) { PerimeterGeneratorLoop &candidate_parent = contours[t][j]; if (candidate_parent.polygon.contains(loop.polygon.first_point())) { - candidate_parent.add_child(loop); + candidate_parent.children.push_back(loop); holes_d.erase(holes_d.begin() + i); --i; - goto NEXT_HOLE; + goto NEXT_LOOP; } } } + NEXT_LOOP: ; } - NEXT_HOLE: } // nest contour loops @@ -209,10 +212,10 @@ PerimeterGenerator::process() // find the contour loop that contains it for (unsigned short t = d-1; t >= 0; --t) { - for (unsigned short j = 0; j < contours_d[t].size(); ++j) { + for (unsigned short j = 0; j < contours[t].size(); ++j) { PerimeterGeneratorLoop &candidate_parent = contours[t][j]; if (candidate_parent.polygon.contains(loop.polygon.first_point())) { - candidate_parent.add_child(loop); + candidate_parent.children.push_back(loop); contours_d.erase(contours_d.begin() + i); --i; goto NEXT_CONTOUR; @@ -220,7 +223,7 @@ PerimeterGenerator::process() } } - NEXT_CONTOUR: + NEXT_CONTOUR: ; } } @@ -232,7 +235,7 @@ PerimeterGenerator::process() // we continue inwards after having finished the brim // TODO: add test for perimeter order if (this->config->external_perimeters_first - || (this->layer_id == 0 && this->print_config->brim_width > 0)) + || (this->layer_id == 0 && this->print_config->brim_width.value > 0)) entities.reverse(); // append perimeters for this slice as a collection @@ -260,8 +263,9 @@ PerimeterGenerator::process() for (std::vector::const_iterator gap_size = gap_sizes.begin(); gap_size != gap_sizes.end(); ++gap_size) { - ExtrusionEntityCollection gap_fill = this->_fill_gaps(gap_size.min, gap_size.max, gap_size.width); - this->gap_fill->append(gap_fill); + ExtrusionEntityCollection gap_fill = this->_fill_gaps(gap_size->min, + gap_size->max, gap_size->width, gaps); + this->gap_fill->append(gap_fill.entities); // Make sure we don't infill narrow parts that are already gap-filled // (we only consider this surface's gaps to reduce the diff() complexity). @@ -298,7 +302,7 @@ PerimeterGenerator::process() inset -= this->config->get_abs_value("infill_overlap", inset + ispacing/2); { - ExPolygons expp = union_(last); + ExPolygons expp = union_ex(last); // simplify infill contours according to resolution Polygons pp; @@ -307,10 +311,10 @@ PerimeterGenerator::process() // collapse too narrow infill areas coord_t min_perimeter_infill_spacing = ispacing * (1 - INSET_OVERLAP_TOLERANCE); - expp = offset2( + expp = offset2_ex( pp, -inset -min_perimeter_infill_spacing/2, - +min_perimeter_infill_spacing/2, + +min_perimeter_infill_spacing/2 ); // append infill areas to fill_surfaces @@ -322,7 +326,7 @@ PerimeterGenerator::process() ExtrusionEntityCollection PerimeterGenerator::_traverse_loops(const PerimeterGeneratorLoops &loops, - const Polylines &thin_walls) const + Polylines &thin_walls) const { // loops is an arrayref of ::Loop objects // turn each one into an ExtrusionLoop object @@ -346,17 +350,17 @@ PerimeterGenerator::_traverse_loops(const PerimeterGeneratorLoops &loops, // detect overhanging/bridging perimeters ExtrusionPaths paths; if (this->config->overhangs && this->layer_id > 0 - && !(this->object_config->support_material && this->object_config->support_material_contact_distance == 0)) { + && !(this->object_config->support_material && this->object_config->support_material_contact_distance.value == 0)) { // get non-overhang paths by intersecting this loop with the grown lower slices { Polylines polylines; - intersection(loop->polygon(), this->_lower_slices_p, &polylines); + intersection((Polygons)loop->polygon, this->_lower_slices_p, &polylines); for (Polylines::const_iterator polyline = polylines.begin(); polyline != polylines.end(); ++polyline) { ExtrusionPath path(role); path.polyline = *polyline; path.mm3_per_mm = is_external ? this->_ext_mm3_per_mm : this->_mm3_per_mm; - path.width = is_external ? this->ext_perimeter_flow->width : this->perimeter_flow.width; + path.width = is_external ? this->ext_perimeter_flow.width : this->perimeter_flow.width; path.height = this->layer_height; paths.push_back(path); } @@ -367,7 +371,7 @@ PerimeterGenerator::_traverse_loops(const PerimeterGeneratorLoops &loops, // the loop centerline and original lower slices is >= half nozzle diameter { Polylines polylines; - diff(loop->polygon(), this->_lower_slices_p, &polylines); + diff((Polygons)loop->polygon, this->_lower_slices_p, &polylines); for (Polylines::const_iterator polyline = polylines.begin(); polyline != polylines.end(); ++polyline) { ExtrusionPath path(erOverhangPerimeter); @@ -385,10 +389,11 @@ PerimeterGenerator::_traverse_loops(const PerimeterGeneratorLoops &loops, paths = ExtrusionEntityCollection(paths).chained_path(); } else { ExtrusionPath path(role); - path.polyline = loop.polygon().split_at_first_point(); + path.polyline = loop->polygon.split_at_first_point(); path.mm3_per_mm = is_external ? this->_ext_mm3_per_mm : this->_mm3_per_mm; - path.width = is_external ? this->ext_perimeter_flow->width : this->perimeter_flow.width; + path.width = is_external ? this->ext_perimeter_flow.width : this->perimeter_flow.width; path.height = this->layer_height; + paths.push_back(path); } coll.append(ExtrusionLoop(paths, loop_role)); @@ -409,11 +414,11 @@ PerimeterGenerator::_traverse_loops(const PerimeterGeneratorLoops &loops, } // sort entities - ExtrusionPathCollection sorted_coll; + ExtrusionEntityCollection sorted_coll; coll.chained_path(&sorted_coll, false, &sorted_coll.orig_indices); // traverse children - ExtrusionPathCollection entities; + ExtrusionEntityCollection entities; for (unsigned short i = 0; i < sorted_coll.orig_indices.size(); ++i) { size_t idx = sorted_coll.orig_indices[i]; if (idx >= loops.size()) { @@ -421,19 +426,18 @@ PerimeterGenerator::_traverse_loops(const PerimeterGeneratorLoops &loops, // let's get it from the sorted collection as it might have been reversed entities.append(*sorted_coll.entities[i]); } else { - PerimeterGeneratorLoop &loop = loops[i]; - ExtrusionLoop eloop = *coll.entities[idx]; + const PerimeterGeneratorLoop &loop = loops[i]; + ExtrusionLoop eloop = *dynamic_cast(coll.entities[idx]); - ExtrusionEntityCollection children = this->_traverse_loops(loop->children, thin_walls); - if (loop->is_contour()) { + ExtrusionEntityCollection children = this->_traverse_loops(loop.children, thin_walls); + if (loop.is_contour) { eloop.make_counter_clockwise(); - entities.append(children); - entities.append(elooop); + entities.append(children.entities); + entities.append(eloop); } else { eloop.make_clockwise(); - push @entities, $eloop, @children; - entities.append(elooop); - entities.append(children); + entities.append(eloop); + entities.append(children.entities); } } } @@ -448,10 +452,10 @@ PerimeterGenerator::_fill_gaps(double min, double max, double w, min *= (1 - INSET_OVERLAP_TOLERANCE); - ExPolygon curr = diff( + ExPolygons curr = diff_ex( offset2(gaps, -min/2, +min/2), offset2(gaps, -max/2, +max/2), - true, + true ); Polylines polylines; @@ -462,7 +466,7 @@ PerimeterGenerator::_fill_gaps(double min, double max, double w, #ifdef SLIC3R_DEBUG if (!curr.empty()) - printf(" %d gaps filled with extrusion width = %zu\n", curr.size(), w); + printf(" %zu gaps filled with extrusion width = %f\n", curr.size(), w); #endif //my $flow = $layerm->flow(FLOW_ROLE_SOLID_INFILL, 0, $w); diff --git a/xs/src/libslic3r/PerimeterGenerator.hpp b/xs/src/libslic3r/PerimeterGenerator.hpp index f5a3a6dd9..6465d63f9 100644 --- a/xs/src/libslic3r/PerimeterGenerator.hpp +++ b/xs/src/libslic3r/PerimeterGenerator.hpp @@ -2,6 +2,12 @@ #define slic3r_PerimeterGenerator_hpp_ #include +#include +#include "ExPolygonCollection.hpp" +#include "Flow.hpp" +#include "Polygon.hpp" +#include "PrintConfig.hpp" +#include "SurfaceCollection.hpp" namespace Slic3r { @@ -25,7 +31,7 @@ class PerimeterGeneratorLoop { class PerimeterGenerator { public: SurfaceCollection* slices; - SurfaceCollection* lower_slices; + ExPolygonCollection* lower_slices; double layer_height; int layer_id; Flow perimeter_flow; @@ -39,13 +45,15 @@ class PerimeterGenerator { ExtrusionEntityCollection* gap_fill; SurfaceCollection* fill_surfaces; - PerimeterGenerator(SurfaceCollection* slices, double layer_height, + PerimeterGenerator(SurfaceCollection* slices, double layer_height, Flow flow, PrintRegionConfig* config, PrintObjectConfig* object_config, PrintConfig* print_config, ExtrusionEntityCollection* loops, ExtrusionEntityCollection* gap_fill, SurfaceCollection* fill_surfaces) - : slices(slices), lower_slices(NULL), layer_height(layer_height), layer_id(-1), + : slices(slices), lower_slices(NULL), layer_height(layer_height), + perimeter_flow(flow), ext_perimeter_flow(flow), overhang_flow(flow), + solid_infill_flow(flow), layer_id(-1), config(config), object_config(object_config), print_config(print_config), - loops(loops), gap_fill(gap_fill), fill_surfaces(fill_surfaces) + loops(loops), gap_fill(gap_fill), fill_surfaces(fill_surfaces), _ext_mm3_per_mm(-1), _mm3_per_mm(-1), _mm3_per_mm_overhang(-1) {}; void process(); @@ -57,7 +65,7 @@ class PerimeterGenerator { Polygons _lower_slices_p; ExtrusionEntityCollection _traverse_loops(const PerimeterGeneratorLoops &loops, - const Polylines &thin_walls) const; + Polylines &thin_walls) const; ExtrusionEntityCollection _fill_gaps(double min, double max, double w, const Polygons &gaps) const; }; @@ -67,7 +75,7 @@ class PerimeterGeneratorGapSize { coord_t min; coord_t max; coord_t width; - PerimeterGeneratorGapSizes(coord_t min, coord_t max, coord_t width) + PerimeterGeneratorGapSize(coord_t min, coord_t max, coord_t width) : min(min), max(max), width(width) {}; }; diff --git a/xs/xsp/ExtrusionEntityCollection.xsp b/xs/xsp/ExtrusionEntityCollection.xsp index b7e439479..7570d078a 100644 --- a/xs/xsp/ExtrusionEntityCollection.xsp +++ b/xs/xsp/ExtrusionEntityCollection.xsp @@ -7,6 +7,7 @@ %name{Slic3r::ExtrusionPath::Collection} class ExtrusionEntityCollection { %name{_new} ExtrusionEntityCollection(); + ~ExtrusionEntityCollection(); Clone clone() %code{% RETVAL = THIS->clone(); %}; void reverse(); @@ -41,14 +42,6 @@ Polygons grow(); %{ -void -ExtrusionEntityCollection::DESTROY() - CODE: - for (ExtrusionEntitiesPtr::iterator it = THIS->entities.begin(); it != THIS->entities.end(); ++it) { - delete *it; - } - delete THIS; - SV* ExtrusionEntityCollection::arrayref() CODE: diff --git a/xs/xsp/PerimeterGenerator.xsp b/xs/xsp/PerimeterGenerator.xsp new file mode 100644 index 000000000..351c6f4c0 --- /dev/null +++ b/xs/xsp/PerimeterGenerator.xsp @@ -0,0 +1,34 @@ +%module{Slic3r::XS}; + +%{ +#include +#include "libslic3r/PerimeterGenerator.hpp" +%} + +%name{Slic3r::Layer::PerimeterGenerator} class PerimeterGenerator { + PerimeterGenerator(SurfaceCollection* slices, double layer_height, Flow* flow, + PrintRegionConfig* config, PrintObjectConfig* object_config, + PrintConfig* print_config, ExtrusionEntityCollection* loops, + ExtrusionEntityCollection* gap_fill, SurfaceCollection* fill_surfaces) + %code{% RETVAL = new PerimeterGenerator(slices, layer_height, *flow, + config, object_config, print_config, loops, gap_fill, fill_surfaces); %}; + ~PerimeterGenerator(); + + void set_lower_slices(ExPolygonCollection* lower_slices) + %code{% THIS->lower_slices = lower_slices; %}; + void set_layer_id(int layer_id) + %code{% THIS->layer_id = layer_id; %}; + void set_perimeter_flow(Flow* flow) + %code{% THIS->perimeter_flow = *flow; %}; + void set_ext_perimeter_flow(Flow* flow) + %code{% THIS->ext_perimeter_flow = *flow; %}; + void set_overhang_flow(Flow* flow) + %code{% THIS->overhang_flow = *flow; %}; + void set_solid_infill_flow(Flow* flow) + %code{% THIS->solid_infill_flow = *flow; %}; + + Ref config() + %code{% RETVAL = THIS->config; %}; + + void process(); +}; diff --git a/xs/xsp/my.map b/xs/xsp/my.map index af6f9f042..1f59b3fa0 100644 --- a/xs/xsp/my.map +++ b/xs/xsp/my.map @@ -198,6 +198,10 @@ BridgeDetector* O_OBJECT_SLIC3R Ref O_OBJECT_SLIC3R_T Clone O_OBJECT_SLIC3R_T +PerimeterGenerator* O_OBJECT_SLIC3R +Ref O_OBJECT_SLIC3R_T +Clone O_OBJECT_SLIC3R_T + GLVertexArray* O_OBJECT_SLIC3R Axis T_UV diff --git a/xs/xsp/typemap.xspt b/xs/xsp/typemap.xspt index 8b4eb0850..6c7beb17f 100644 --- a/xs/xsp/typemap.xspt +++ b/xs/xsp/typemap.xspt @@ -94,6 +94,12 @@ %typemap{BridgeDetector*}; %typemap{Ref}{simple}; %typemap{Clone}{simple}; +%typemap{SurfaceCollection*}; +%typemap{Ref}{simple}; +%typemap{Clone}{simple}; +%typemap{PerimeterGenerator*}; +%typemap{Ref}{simple}; +%typemap{Clone}{simple}; %typemap{Surface*}; %typemap{Ref}{simple}; @@ -153,7 +159,6 @@ %typemap{Polygons*}; %typemap{TriangleMesh*}; %typemap{TriangleMeshPtrs}; -%typemap{Ref}{simple}; %typemap{Extruder*}; %typemap{Ref}{simple}; %typemap{Clone}{simple};