Implement modifier volumes and port _merge_loops() to XS

This commit is contained in:
Alessandro Ranellucci 2014-01-07 15:40:38 +01:00
parent b17d06f9d1
commit c8a48b4527
10 changed files with 195 additions and 109 deletions

View file

@ -58,69 +58,6 @@ sub flow {
);
}
# build polylines from lines
sub make_surfaces {
my $self = shift;
my ($loops) = @_;
return if !@$loops;
$self->slices->clear;
$self->slices->append($self->_merge_loops($loops));
if (0) {
require "Slic3r/SVG.pm";
Slic3r::SVG::output("surfaces.svg",
#polylines => $loops,
red_polylines => [ grep $_->is_counter_clockwise, @$loops ],
green_polylines => [ grep !$_->is_counter_clockwise, @$loops ],
expolygons => [ map $_->expolygon, @{$self->slices} ],
);
}
}
sub _merge_loops {
my ($self, $loops, $safety_offset) = @_;
# Input loops are not suitable for evenodd nor nonzero fill types, as we might get
# two consecutive concentric loops having the same winding order - and we have to
# respect such order. In that case, evenodd would create wrong inversions, and nonzero
# would ignore holes inside two concentric contours.
# So we're ordering loops and collapse consecutive concentric loops having the same
# winding order.
# TODO: find a faster algorithm for this, maybe with some sort of binary search.
# If we computed a "nesting tree" we could also just remove the consecutive loops
# having the same winding order, and remove the extra one(s) so that we could just
# supply everything to offset_ex() instead of performing several union/diff calls.
# we sort by area assuming that the outermost loops have larger area;
# the previous sorting method, based on $b->contains_point($a->[0]), failed to nest
# loops correctly in some edge cases when original model had overlapping facets
my @abs_area = map abs($_), my @area = map $_->area, @$loops;
my @sorted = sort { $abs_area[$b] <=> $abs_area[$a] } 0..$#$loops; # outer first
# we don't perform a safety offset now because it might reverse cw loops
my $slices = [];
for my $i (@sorted) {
# we rely on the already computed area to determine the winding order
# of the loops, since the Orientation() function provided by Clipper
# would do the same, thus repeating the calculation
$slices = ($area[$i] >= 0)
? [ $loops->[$i], @$slices ]
: diff($slices, [$loops->[$i]]);
}
# perform a safety offset to merge very close facets (TODO: find test case for this)
$safety_offset //= scale 0.0499;
$slices = offset2_ex($slices, +$safety_offset, -$safety_offset);
Slic3r::debugf "Layer %d (slice_z = %.2f, print_z = %.2f): %d surface(s) having %d holes detected from %d polylines\n",
$self->id, $self->slice_z, $self->print_z,
scalar(@$slices), scalar(map @{$_->holes}, @$slices), scalar(@$loops)
if $Slic3r::debug;
return map Slic3r::Surface->new(expolygon => $_, surface_type => S_TYPE_INTERNAL), @$slices;
}
sub make_perimeters {
my $self = shift;
@ -318,8 +255,8 @@ sub _fill_gaps {
$filler->angle($self->config->fill_angle);
$filler->layer_id($self->layer->id);
# we should probably use this code to handle thin walls and remove that logic from
# make_surfaces(), but we need to enable dynamic extrusion width before as we can't
# we should probably use this code to handle thin walls
# but we need to enable dynamic extrusion width before as we can't
# use zigzag for thin walls.
# medial axis-based gap fill should benefit from detection of larger gaps too, so

View file

@ -50,6 +50,7 @@ sub add_object {
$new_object->add_volume(
material_id => $volume->material_id,
mesh => $volume->mesh->clone,
modifier => $volume->modifier,
);
if (defined $volume->material_id) {
@ -361,7 +362,7 @@ sub raw_mesh {
my $self = shift;
my $mesh = Slic3r::TriangleMesh->new;
$mesh->merge($_->mesh) for @{ $self->volumes };
$mesh->merge($_->mesh) for grep !$_->modifier, @{ $self->volumes };
return $mesh;
}
@ -458,12 +459,12 @@ sub unique_materials {
sub facets_count {
my $self = shift;
return sum(map $_->mesh->facets_count, @{$self->volumes});
return sum(map $_->mesh->facets_count, grep !$_->modifier, @{$self->volumes});
}
sub needed_repair {
my $self = shift;
return (first { !$_->mesh->needed_repair } @{$self->volumes}) ? 0 : 1;
return (first { !$_->mesh->needed_repair } grep !$_->modifier, @{$self->volumes}) ? 0 : 1;
}
sub mesh_stats {
@ -494,7 +495,7 @@ sub print_info {
printf " needed repair: no\n";
}
} else {
printf " number of facets: %d\n", scalar(map @{$_->facets}, @{$self->volumes});
printf " number of facets: %d\n", scalar(map @{$_->facets}, grep !$_->modifier, @{$self->volumes});
}
}
@ -504,6 +505,7 @@ use Moo;
has 'object' => (is => 'ro', weak_ref => 1, required => 1);
has 'material_id' => (is => 'rw');
has 'mesh' => (is => 'rw', required => 1);
has 'modifier' => (is => 'rw', defualt => sub { 0 });
package Slic3r::Model::Instance;
use Moo;

View file

@ -528,8 +528,7 @@ sub export_svg {
my $self = shift;
my %params = @_;
# this shouldn't be needed, but we're currently relying on ->make_surfaces() which
# calls ->perimeter_flow
# is this needed?
$self->init_extruders;
$_->slice for @{$self->objects};

View file

@ -178,35 +178,57 @@ sub slice {
$layer->region($_) for 0 .. ($regions_count-1);
}
# process facets
# get array of Z coordinates for slicing
my @z = map $_->slice_z, @{$self->layers};
# slice all non-modifier volumes
for my $region_id (0..$#{$self->region_volumes}) {
next if !defined $self->region_volumes->[$region_id];
# compose mesh
my $mesh;
foreach my $volume_id (@{$self->region_volumes->[$region_id]}) {
if (defined $mesh) {
$mesh->merge($self->model_object->volumes->[$volume_id]->mesh);
} else {
$mesh = $self->model_object->volumes->[$volume_id]->mesh->clone;
my $expolygons_by_layer = $self->_slice_region($region_id, \@z, 0);
for my $layer_id (0..$#$expolygons_by_layer) {
my $layerm = $self->layers->[$layer_id]->regions->[$region_id];
$layerm->slices->clear;
foreach my $expolygon (@{ $expolygons_by_layer->[$layer_id] }) {
$layerm->slices->append(Slic3r::Surface->new(
expolygon => $expolygon,
surface_type => S_TYPE_INTERNAL,
));
}
}
# transform mesh
# we ignore the per-instance transformations currently and only
# consider the first one
$self->model_object->instances->[0]->transform_mesh($mesh, 1);
# align mesh to Z = 0 and apply XY shift
$mesh->translate((map unscale(-$_), @{$self->_copies_shift}), -$self->model_object->bounding_box->z_min);
{
my $loops = $mesh->slice([ map $_->slice_z, @{$self->layers} ]);
for my $layer_id (0..$#$loops) {
my $layerm = $self->layers->[$layer_id]->regions->[$region_id];
$layerm->make_surfaces($loops->[$layer_id]);
}
# then slice all modifier volumes
if (@{$self->region_volumes} > 1) {
for my $region_id (0..$#{$self->region_volumes}) {
my $expolygons_by_layer = $self->_slice_region($region_id, \@z, 1);
# loop through the other regions and 'steal' the slices belonging to this one
for my $other_region_id (0..$#{$self->region_volumes}) {
next if $other_region_id == $region_id;
for my $layer_id (0..$#$expolygons_by_layer) {
my $layerm = $self->layers->[$layer_id]->regions->[$region_id];
my $other_layerm = $self->layers->[$layer_id]->regions->[$other_region_id];
my $other_slices = [ map $_->p, @{$other_layerm->slices} ]; # Polygons
my $my_parts = intersection_ex(
$other_slices,
[ map @$_, @{ $expolygons_by_layer->[$layer_id] } ],
);
next if !@$my_parts;
# append new parts to our region
foreach my $expolygon (@$my_parts) {
$layerm->slices->append(Slic3r::Surface->new(
expolygon => $expolygon,
surface_type => S_TYPE_INTERNAL,
));
}
# remove such parts from original region
$other_layerm->slices->clear;
$other_layerm->append($_) for @{ diff($other_slices, $my_parts) };
}
}
# TODO: read slicing_errors
}
}
@ -285,6 +307,38 @@ sub slice {
}
}
sub _slice_region {
my ($self, $region_id, $z, $modifier) = @_;
return [] if !defined $self->region_volumes->[$region_id];
# compose mesh
my $mesh;
foreach my $volume_id (@{$self->region_volumes->[$region_id]}) {
my $volume = $self->model_object->volumes->[$volume_id];
next if $volume->modifier && !$modifier;
next if !$volume->modifier && $modifier;
if (defined $mesh) {
$mesh->merge($volume->mesh);
} else {
$mesh = $volume->mesh->clone;
}
}
next if !defined $mesh;
# transform mesh
# we ignore the per-instance transformations currently and only
# consider the first one
$self->model_object->instances->[0]->transform_mesh($mesh, 1);
# align mesh to Z = 0 and apply XY shift
$mesh->translate((map unscale(-$_), @{$self->_copies_shift}), -$self->model_object->bounding_box->z_min);
# perform actual slicing
return $mesh->slice($z);
}
sub make_perimeters {
my $self = shift;