Ported infill to XS/C++. Credits and many thanks go to @bubnikv for this work

This commit is contained in:
Alessandro Ranellucci 2016-11-24 15:32:32 +01:00
parent d98b7cf863
commit 103ec05774
64 changed files with 3916 additions and 1343 deletions

View File

@ -15,7 +15,6 @@ my %prereqs = qw(
File::Basename 0
File::Spec 0
Getopt::Long 0
Math::PlanePath 53
Module::Build::WithXSpp 0.14
Moo 1.003001
POSIX 0
@ -112,11 +111,6 @@ EOF
# make sure our cpanm is updated (old ones don't support the ~ syntax)
system $cpanm, @cpanm_args, 'App::cpanminus';
# install the Windows-compatible Math::Libm
if ($^O eq 'MSWin32' && !eval "use Math::Libm; 1") {
system $cpanm, @cpanm_args, 'https://github.com/alexrj/Math-Libm/tarball/master';
}
my %modules = (%prereqs, %recommends);
foreach my $module (sort keys %modules) {
my $version = $modules{$module};

View File

@ -39,7 +39,6 @@ use Slic3r::Config;
use Slic3r::ExPolygon;
use Slic3r::ExtrusionLoop;
use Slic3r::ExtrusionPath;
use Slic3r::Fill;
use Slic3r::Flow;
use Slic3r::Format::AMF;
use Slic3r::Format::OBJ;
@ -113,6 +112,12 @@ sub spawn_thread {
return $thread;
}
# If the threading is enabled, spawn a set of threads.
# Otherwise run the task on the current thread.
# Used for
# Slic3r::Print::Object->layers->make_perimeters
# Slic3r::Print::Object->layers->make_fill
# Slic3r::Print::SupportMaterial::generate_toolpaths
sub parallelize {
my %params = @_;
@ -193,6 +198,7 @@ sub thread_cleanup {
*Slic3r::ExtrusionLoop::DESTROY = sub {};
*Slic3r::ExtrusionPath::DESTROY = sub {};
*Slic3r::ExtrusionPath::Collection::DESTROY = sub {};
*Slic3r::Filler::DESTROY = sub {};
*Slic3r::Flow::DESTROY = sub {};
*Slic3r::GCode::DESTROY = sub {};
*Slic3r::GCode::AvoidCrossingPerimeters::DESTROY = sub {};

View File

@ -1,303 +0,0 @@
package Slic3r::Fill;
use Moo;
use List::Util qw(max);
use Slic3r::ExtrusionPath ':roles';
use Slic3r::Fill::3DHoneycomb;
use Slic3r::Fill::Base;
use Slic3r::Fill::Concentric;
use Slic3r::Fill::Honeycomb;
use Slic3r::Fill::PlanePath;
use Slic3r::Fill::Rectilinear;
use Slic3r::Fill::AlignedRectilinear;
use Slic3r::Flow ':roles';
use Slic3r::Geometry qw(X Y PI scale chained_path deg2rad);
use Slic3r::Geometry::Clipper qw(union union_ex diff diff_ex intersection_ex offset offset2);
use Slic3r::Surface ':types';
has 'bounding_box' => (is => 'ro', required => 0);
has 'fillers' => (is => 'rw', default => sub { {} });
our %FillTypes = (
archimedeanchords => 'Slic3r::Fill::ArchimedeanChords',
alignedrectilinear => 'Slic3r::Fill::AlignedRectilinear',
rectilinear => 'Slic3r::Fill::Rectilinear',
grid => 'Slic3r::Fill::Grid',
flowsnake => 'Slic3r::Fill::Flowsnake',
octagramspiral => 'Slic3r::Fill::OctagramSpiral',
hilbertcurve => 'Slic3r::Fill::HilbertCurve',
line => 'Slic3r::Fill::Line',
concentric => 'Slic3r::Fill::Concentric',
honeycomb => 'Slic3r::Fill::Honeycomb',
'3dhoneycomb' => 'Slic3r::Fill::3DHoneycomb',
);
sub filler {
my $self = shift;
my ($filler) = @_;
if (!ref $self) {
return $FillTypes{$filler}->new;
}
$self->fillers->{$filler} ||= $FillTypes{$filler}->new(
bounding_box => $self->bounding_box,
);
return $self->fillers->{$filler};
}
sub make_fill {
my $self = shift;
my ($layerm) = @_;
Slic3r::debugf "Filling layer %d:\n", $layerm->layer->id;
my $fill_density = $layerm->region->config->fill_density;
my $infill_flow = $layerm->flow(FLOW_ROLE_INFILL);
my $solid_infill_flow = $layerm->flow(FLOW_ROLE_SOLID_INFILL);
my $top_solid_infill_flow = $layerm->flow(FLOW_ROLE_TOP_SOLID_INFILL);
my @surfaces = ();
# merge adjacent surfaces
# in case of bridge surfaces, the ones with defined angle will be attached to the ones
# without any angle (shouldn't this logic be moved to process_external_surfaces()?)
{
my @surfaces_with_bridge_angle = grep { $_->bridge_angle >= 0 } @{$layerm->fill_surfaces};
# group surfaces by distinct properties
my @groups = @{$layerm->fill_surfaces->group};
# merge compatible groups (we can generate continuous infill for them)
{
# cache flow widths and patterns used for all solid groups
# (we'll use them for comparing compatible groups)
my @is_solid = my @fw = my @pattern = ();
for (my $i = 0; $i <= $#groups; $i++) {
# we can only merge solid non-bridge surfaces, so discard
# non-solid surfaces
if ($groups[$i][0]->is_solid && (!$groups[$i][0]->is_bridge || $layerm->layer->id == 0)) {
$is_solid[$i] = 1;
$fw[$i] = ($groups[$i][0]->surface_type == S_TYPE_TOP)
? $top_solid_infill_flow->width
: $solid_infill_flow->width;
$pattern[$i] = $groups[$i][0]->is_external
? $layerm->region->config->external_fill_pattern
: 'rectilinear';
} else {
$is_solid[$i] = 0;
$fw[$i] = 0;
$pattern[$i] = 'none';
}
}
# loop through solid groups
for (my $i = 0; $i <= $#groups; $i++) {
next if !$is_solid[$i];
# find compatible groups and append them to this one
for (my $j = $i+1; $j <= $#groups; $j++) {
next if !$is_solid[$j];
if ($fw[$i] == $fw[$j] && $pattern[$i] eq $pattern[$j]) {
# groups are compatible, merge them
push @{$groups[$i]}, @{$groups[$j]};
splice @groups, $j, 1;
splice @is_solid, $j, 1;
splice @fw, $j, 1;
splice @pattern, $j, 1;
}
}
}
}
# give priority to bridges
@groups = sort { ($a->[0]->bridge_angle >= 0) ? -1 : 0 } @groups;
foreach my $group (@groups) {
my $union_p = union([ map $_->p, @$group ], 1);
# subtract surfaces having a defined bridge_angle from any other
if (@surfaces_with_bridge_angle && $group->[0]->bridge_angle < 0) {
$union_p = diff(
$union_p,
[ map $_->p, @surfaces_with_bridge_angle ],
1,
);
}
# subtract any other surface already processed
my $union = diff_ex(
$union_p,
[ map $_->p, @surfaces ],
1,
);
push @surfaces, map $group->[0]->clone(expolygon => $_), @$union;
}
}
# we need to detect any narrow surfaces that might collapse
# when adding spacing below
# such narrow surfaces are often generated in sloping walls
# by bridge_over_infill() and combine_infill() as a result of the
# subtraction of the combinable area from the layer infill area,
# which leaves small areas near the perimeters
# we are going to grow such regions by overlapping them with the void (if any)
# TODO: detect and investigate whether there could be narrow regions without
# any void neighbors
{
my $distance_between_surfaces = max(
$infill_flow->scaled_spacing,
$solid_infill_flow->scaled_spacing,
$top_solid_infill_flow->scaled_spacing,
);
my $collapsed = diff(
[ map @{$_->expolygon}, @surfaces ],
offset2([ map @{$_->expolygon}, @surfaces ], -$distance_between_surfaces/2, +$distance_between_surfaces/2),
1,
);
push @surfaces, map Slic3r::Surface->new(
expolygon => $_,
surface_type => S_TYPE_INTERNALSOLID,
), @{intersection_ex(
offset($collapsed, $distance_between_surfaces),
[
(map @{$_->expolygon}, grep $_->surface_type == S_TYPE_INTERNALVOID, @surfaces),
(@$collapsed),
],
1,
)};
}
if (0) {
require "Slic3r/SVG.pm";
Slic3r::SVG::output("fill_" . $layerm->print_z . ".svg",
expolygons => [ map $_->expolygon, grep !$_->is_solid, @surfaces ],
red_expolygons => [ map $_->expolygon, grep $_->is_solid, @surfaces ],
);
}
my @fills = ();
SURFACE: foreach my $surface (@surfaces) {
next if $surface->surface_type == S_TYPE_INTERNALVOID;
my $filler = $layerm->region->config->fill_pattern;
my $density = $fill_density;
my $role = ($surface->surface_type == S_TYPE_TOP) ? FLOW_ROLE_TOP_SOLID_INFILL
: $surface->is_solid ? FLOW_ROLE_SOLID_INFILL
: FLOW_ROLE_INFILL;
my $is_bridge = $layerm->layer->id > 0 && $surface->is_bridge;
my $is_solid = $surface->is_solid;
if ($surface->is_solid) {
$density = 100;
$filler = 'rectilinear';
if ($surface->is_external && !$is_bridge) {
$filler = $layerm->region->config->external_fill_pattern;
}
} else {
next SURFACE unless $density > 0;
}
# get filler object
my $f = $self->filler($filler);
# calculate the actual flow we'll be using for this infill
my $h = $surface->thickness == -1 ? $layerm->layer->height : $surface->thickness;
my $flow = $layerm->region->flow(
$role,
$h,
$is_bridge || $f->use_bridge_flow,
$layerm->layer->id == 0,
-1,
$layerm->layer->object,
);
# calculate flow spacing for infill pattern generation
my $using_internal_flow = 0;
if (!$is_solid && !$is_bridge) {
# it's internal infill, so we can calculate a generic flow spacing
# for all layers, for avoiding the ugly effect of
# misaligned infill on first layer because of different extrusion width and
# layer height
my $internal_flow = $layerm->region->flow(
FLOW_ROLE_INFILL,
$layerm->layer->object->config->layer_height, # TODO: handle infill_every_layers?
0, # no bridge
0, # no first layer
-1, # auto width
$layerm->layer->object,
);
$f->spacing($internal_flow->spacing);
$using_internal_flow = 1;
# create the actual flow for internal flow that is used later.
$flow = Slic3r::Flow->new_from_spacing(
spacing => $internal_flow->spacing,
nozzle_diameter => $flow->nozzle_diameter,
layer_height => $h,
bridge => 0,
);
} else {
$f->spacing($flow->spacing);
}
$f->layer_id($layerm->layer->id);
$f->z($layerm->layer->print_z);
$f->angle(deg2rad($layerm->region->config->fill_angle));
$f->loop_clipping(scale($flow->nozzle_diameter) * &Slic3r::LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER);
# apply half spacing using this flow's own spacing and generate infill
my @polylines = map $f->fill_surface(
$_,
density => $density/100,
layer_height => $h,
), @{ $surface->offset(-scale($f->spacing)/2) };
next unless @polylines;
# calculate actual flow from spacing (which might have been adjusted by the infill
# pattern generator)
if ($using_internal_flow) {
# if we used the internal flow we're not doing a solid infill
# so we can safely ignore the slight variation that might have
# been applied to $f->flow_spacing
} else {
$flow = Slic3r::Flow->new_from_spacing(
spacing => $f->spacing,
nozzle_diameter => $flow->nozzle_diameter,
layer_height => $h,
bridge => $is_bridge || $f->use_bridge_flow,
);
}
my $mm3_per_mm = $flow->mm3_per_mm;
# save into layer
{
my $role = $is_bridge ? EXTR_ROLE_BRIDGE
: $is_solid ? (($surface->surface_type == S_TYPE_TOP) ? EXTR_ROLE_TOPSOLIDFILL : EXTR_ROLE_SOLIDFILL)
: EXTR_ROLE_FILL;
push @fills, my $collection = Slic3r::ExtrusionPath::Collection->new;
$collection->no_sort($f->no_sort);
$collection->append(
map Slic3r::ExtrusionPath->new(
polyline => $_,
role => $role,
mm3_per_mm => $mm3_per_mm,
width => $flow->width,
height => $flow->height,
), @polylines,
);
}
}
# add thin fill regions
foreach my $thin_fill (@{$layerm->thin_fills}) {
push @fills, Slic3r::ExtrusionPath::Collection->new($thin_fill);
}
return @fills;
}
1;

View File

@ -1,230 +0,0 @@
package Slic3r::Fill::3DHoneycomb;
use Moo;
extends 'Slic3r::Fill::Base';
use POSIX qw(ceil fmod);
use Slic3r::Geometry qw(scale scaled_epsilon);
use Slic3r::Geometry::Clipper qw(intersection_pl);
# require bridge flow since most of this pattern hangs in air
sub use_bridge_flow { 1 }
sub fill_surface {
my ($self, $surface, %params) = @_;
my $expolygon = $surface->expolygon;
my $bb = $expolygon->bounding_box;
my $size = $bb->size;
my $distance = scale($self->spacing) / $params{density};
# align bounding box to a multiple of our honeycomb grid module
# (a module is 2*$distance since one $distance half-module is
# growing while the other $distance half-module is shrinking)
{
my $min = $bb->min_point;
$min->translate(
-($bb->x_min % (2*$distance)),
-($bb->y_min % (2*$distance)),
);
$bb->merge_point($min);
}
# generate pattern
my @polylines = map Slic3r::Polyline->new(@$_),
makeGrid(
scale($self->z),
$distance,
ceil($size->x / $distance) + 1,
ceil($size->y / $distance) + 1, #//
(($self->layer_id / $surface->thickness_layers) % 2) + 1,
);
# move pattern in place
$_->translate($bb->x_min, $bb->y_min) for @polylines;
# clip pattern to boundaries
@polylines = @{intersection_pl(\@polylines, \@$expolygon)};
# connect lines
unless ($params{dont_connect} || !@polylines) { # prevent calling leftmost_point() on empty collections
my ($expolygon_off) = @{$expolygon->offset_ex(scaled_epsilon)};
my $collection = Slic3r::Polyline::Collection->new(@polylines);
@polylines = ();
foreach my $polyline (@{$collection->chained_path_from($collection->leftmost_point, 0)}) {
# try to append this polyline to previous one if any
if (@polylines) {
my $line = Slic3r::Line->new($polylines[-1]->last_point, $polyline->first_point);
if ($line->length <= 1.5*$distance && $expolygon_off->contains_line($line)) {
$polylines[-1]->append_polyline($polyline);
next;
}
}
# make a clone before $collection goes out of scope
push @polylines, $polyline->clone;
}
}
# TODO: return ExtrusionLoop objects to get better chained paths
return @polylines;
}
=head1 DESCRIPTION
Creates a contiguous sequence of points at a specified height that make
up a horizontal slice of the edges of a space filling truncated
octahedron tesselation. The octahedrons are oriented so that the
square faces are in the horizontal plane with edges parallel to the X
and Y axes.
Credits: David Eccles (gringer).
=head2 makeGrid(z, gridSize, gridWidth, gridHeight, curveType)
Generate a set of curves (array of array of 2d points) that describe a
horizontal slice of a truncated regular octahedron with a specified
grid square size.
=cut
sub makeGrid {
my ($z, $gridSize, $gridWidth, $gridHeight, $curveType) = @_;
my $scaleFactor = $gridSize;
my $normalisedZ = $z / $scaleFactor;
my @points = makeNormalisedGrid($normalisedZ, $gridWidth, $gridHeight, $curveType);
foreach my $lineRef (@points) {
foreach my $pointRef (@$lineRef) {
$pointRef->[0] *= $scaleFactor;
$pointRef->[1] *= $scaleFactor;
}
}
return @points;
}
=head1 FUNCTIONS
=cut
=head2 colinearPoints(offset, gridLength)
Generate an array of points that are in the same direction as the
basic printing line (i.e. Y points for columns, X points for rows)
Note: a negative offset only causes a change in the perpendicular
direction
=cut
sub colinearPoints {
my ($offset, $baseLocation, $gridLength) = @_;
my @points = ();
push @points, $baseLocation - abs($offset/2);
for (my $i = 0; $i < $gridLength; $i++) {
push @points, $baseLocation + $i + abs($offset/2);
push @points, $baseLocation + ($i+1) - abs($offset/2);
}
push @points, $baseLocation + $gridLength + abs($offset/2);
return @points;
}
=head2 colinearPoints(offset, baseLocation, gridLength)
Generate an array of points for the dimension that is perpendicular to
the basic printing line (i.e. X points for columns, Y points for rows)
=cut
sub perpendPoints {
my ($offset, $baseLocation, $gridLength) = @_;
my @points = ();
my $side = 2*(($baseLocation) % 2) - 1;
push @points, $baseLocation - $offset/2 * $side;
for (my $i = 0; $i < $gridLength; $i++) {
$side = 2*(($i+$baseLocation) % 2) - 1;
push @points, $baseLocation + $offset/2 * $side;
push @points, $baseLocation + $offset/2 * $side;
}
push @points, $baseLocation - $offset/2 * $side;
return @points;
}
=head2 trim(pointArrayRef, minX, minY, maxX, maxY)
Trims an array of points to specified rectangular limits. Point
components that are outside these limits are set to the limits.
=cut
sub trim {
my ($pointArrayRef, $minX, $minY, $maxX, $maxY) = @_;
foreach (@$pointArrayRef) {
$_->[0] = ($_->[0] < $minX) ? $minX : (($_->[0] > $maxX) ? $maxX : $_->[0]);
$_->[1] = ($_->[1] < $minY) ? $minY : (($_->[1] > $maxY) ? $maxY : $_->[1]);
}
}
=head2 makeNormalisedGrid(z, gridWidth, gridHeight, curveType)
Generate a set of curves (array of array of 2d points) that describe a
horizontal slice of a truncated regular octahedron with edge length 1.
curveType specifies which lines to print, 1 for vertical lines
(columns), 2 for horizontal lines (rows), and 3 for both.
=cut
sub makeNormalisedGrid {
my ($z, $gridWidth, $gridHeight, $curveType) = @_;
## offset required to create a regular octagram
my $octagramGap = 0.5;
# sawtooth wave function for range f($z) = [-$octagramGap .. $octagramGap]
my $a = sqrt(2); # period
my $wave = abs(fmod($z, $a) - $a/2)/$a*4 - 1;
my $offset = $wave * $octagramGap;
my @points = ();
if (($curveType & 1) != 0) {
for (my $x = 0; $x <= $gridWidth; $x++) {
my @xPoints = perpendPoints($offset, $x, $gridHeight);
my @yPoints = colinearPoints($offset, 0, $gridHeight);
# This is essentially @newPoints = zip(@xPoints, @yPoints)
my @newPoints = map [ $xPoints[$_], $yPoints[$_] ], 0..$#xPoints;
# trim points to grid edges
#trim(\@newPoints, 0, 0, $gridWidth, $gridHeight);
if ($x % 2 == 0){
push @points, [ @newPoints ];
} else {
push @points, [ reverse @newPoints ];
}
}
}
if (($curveType & 2) != 0) {
for (my $y = 0; $y <= $gridHeight; $y++) {
my @xPoints = colinearPoints($offset, 0, $gridWidth);
my @yPoints = perpendPoints($offset, $y, $gridWidth);
my @newPoints = map [ $xPoints[$_], $yPoints[$_] ], 0..$#xPoints;
# trim points to grid edges
#trim(\@newPoints, 0, 0, $gridWidth, $gridHeight);
if ($y % 2 == 0) {
push @points, [ @newPoints ];
} else {
push @points, [ reverse @newPoints ];
}
}
}
return @points;
}
1;

View File

@ -1,91 +0,0 @@
package Slic3r::Fill::Base;
use Moo;
has 'layer_id' => (is => 'rw');
has 'z' => (is => 'rw'); # in unscaled coordinates
has 'angle' => (is => 'rw'); # in radians, ccw, 0 = East
has 'spacing' => (is => 'rw'); # in unscaled coordinates
has 'loop_clipping' => (is => 'rw', default => sub { 0 }); # in scaled coordinates
has 'bounding_box' => (is => 'ro', required => 0); # Slic3r::Geometry::BoundingBox object
sub adjust_solid_spacing {
my $self = shift;
my %params = @_;
my $number_of_lines = int($params{width} / $params{distance}) + 1;
return $params{distance} if $number_of_lines <= 1;
my $extra_space = $params{width} % $params{distance};
return $params{distance} + $extra_space / ($number_of_lines - 1);
}
sub no_sort { 0 }
sub use_bridge_flow { 0 }
package Slic3r::Fill::WithDirection;
use Moo::Role;
use Slic3r::Geometry qw(PI rad2deg);
sub angles () { [0, PI/2] }
sub infill_direction {
my $self = shift;
my ($surface) = @_;
if (!defined $self->angle) {
warn "Using undefined infill angle";
$self->angle(0);
}
# set infill angle
my (@rotate);
$rotate[0] = $self->angle;
$rotate[1] = $self->bounding_box
? $self->bounding_box->center
: $surface->expolygon->bounding_box->center;
my $shift = $rotate[1]->clone;
if (defined $self->layer_id) {
# alternate fill direction
my $layer_num = $self->layer_id / $surface->thickness_layers;
my $angle = $self->angles->[$layer_num % @{$self->angles}];
$rotate[0] = $self->angle + $angle if $angle;
}
# use bridge angle
if ($surface->bridge_angle >= 0) {
Slic3r::debugf "Filling bridge with angle %d\n", rad2deg($surface->bridge_angle);
$rotate[0] = $surface->bridge_angle;
}
$rotate[0] += PI/2;
$shift->rotate(@rotate);
return [\@rotate, $shift];
}
# this method accepts any object that implements rotate() and translate()
sub rotate_points {
my $self = shift;
my ($expolygon, $rotate_vector) = @_;
# rotate points
my ($rotate, $shift) = @$rotate_vector;
$rotate = [ -$rotate->[0], $rotate->[1] ];
$expolygon->rotate(@$rotate);
$expolygon->translate(@$shift);
}
sub rotate_points_back {
my $self = shift;
my ($paths, $rotate_vector) = @_;
my ($rotate, $shift) = @$rotate_vector;
$shift = [ map -$_, @$shift ];
$_->translate(@$shift) for @$paths;
$_->rotate(@$rotate) for @$paths;
}
1;

View File

@ -1,57 +0,0 @@
package Slic3r::Fill::Concentric;
use Moo;
extends 'Slic3r::Fill::Base';
use Slic3r::Geometry qw(scale unscale X);
use Slic3r::Geometry::Clipper qw(offset offset2 union_pt_chained);
sub no_sort { 1 }
sub fill_surface {
my $self = shift;
my ($surface, %params) = @_;
# no rotation is supported for this infill pattern
my $expolygon = $surface->expolygon;
my $bounding_box = $expolygon->bounding_box;
my $min_spacing = scale($self->spacing);
my $distance = $min_spacing / $params{density};
if ($params{density} == 1 && !$params{dont_adjust}) {
$distance = $self->adjust_solid_spacing(
width => $bounding_box->size->[X],
distance => $distance,
);
$self->spacing(unscale $distance);
}
my @loops = my @last = map $_->clone, @$expolygon;
while (@last) {
push @loops, @last = @{offset2(\@last, -($distance + 0.5*$min_spacing), +0.5*$min_spacing)};
}
# generate paths from the outermost to the innermost, to avoid
# adhesion problems of the first central tiny loops
@loops = map Slic3r::Polygon->new(@$_),
reverse @{union_pt_chained(\@loops)};
# split paths using a nearest neighbor search
my @paths = ();
my $last_pos = Slic3r::Point->new(0,0);
foreach my $loop (@loops) {
push @paths, $loop->split_at_index($last_pos->nearest_point_index(\@$loop));
$last_pos = $paths[-1]->last_point;
}
# clip the paths to prevent the extruder from getting exactly on the first point of the loop
$_->clip_end($self->loop_clipping) for @paths;
@paths = grep $_->is_valid, @paths; # remove empty paths (too short, thus eaten by clipping)
# TODO: return ExtrusionLoop objects to get better chained paths
return @paths;
}
1;

View File

@ -1,129 +0,0 @@
package Slic3r::Fill::Honeycomb;
use Moo;
extends 'Slic3r::Fill::Base';
with qw(Slic3r::Fill::WithDirection);
has 'cache' => (is => 'rw', default => sub {{}});
use Slic3r::Geometry qw(PI X Y MIN MAX scale scaled_epsilon);
use Slic3r::Geometry::Clipper qw(intersection intersection_pl);
sub angles () { [0, PI/3, PI/3*2] }
sub fill_surface {
my $self = shift;
my ($surface, %params) = @_;
my $rotate_vector = $self->infill_direction($surface);
# cache hexagons math
my $cache_id = sprintf "d%s_s%s", $params{density}, $self->spacing;
my $m;
if (!($m = $self->cache->{$cache_id})) {
$m = $self->cache->{$cache_id} = {};
my $min_spacing = scale($self->spacing);
$m->{distance} = $min_spacing / $params{density};
$m->{hex_side} = $m->{distance} / (sqrt(3)/2);
$m->{hex_width} = $m->{distance} * 2; # $m->{hex_width} == $m->{hex_side} * sqrt(3);
my $hex_height = $m->{hex_side} * 2;
$m->{pattern_height} = $hex_height + $m->{hex_side};
$m->{y_short} = $m->{distance} * sqrt(3)/3;
$m->{x_offset} = $min_spacing / 2;
$m->{y_offset} = $m->{x_offset} * sqrt(3)/3;
$m->{hex_center} = Slic3r::Point->new($m->{hex_width}/2, $m->{hex_side});
}
my @polygons = ();
{
# adjust actual bounding box to the nearest multiple of our hex pattern
# and align it so that it matches across layers
my $bounding_box = $surface->expolygon->bounding_box;
{
# rotate bounding box according to infill direction
my $bb_polygon = $bounding_box->polygon;
$bb_polygon->rotate($rotate_vector->[0][0], $m->{hex_center});
$bounding_box = $bb_polygon->bounding_box;
# extend bounding box so that our pattern will be aligned with other layers
# $bounding_box->[X1] and [Y1] represent the displacement between new bounding box offset and old one
$bounding_box->merge_point(Slic3r::Point->new(
$bounding_box->x_min - ($bounding_box->x_min % $m->{hex_width}),
$bounding_box->y_min - ($bounding_box->y_min % $m->{pattern_height}),
));
}
my $x = $bounding_box->x_min;
while ($x <= $bounding_box->x_max) {
my $p = [];
my @x = ($x + $m->{x_offset}, $x + $m->{distance} - $m->{x_offset});
for (1..2) {
@$p = reverse @$p; # turn first half upside down
my @p = ();
for (my $y = $bounding_box->y_min; $y <= $bounding_box->y_max; $y += $m->{y_short} + $m->{hex_side} + $m->{y_short} + $m->{hex_side}) {
push @$p,
[ $x[1], $y + $m->{y_offset} ],
[ $x[0], $y + $m->{y_short} - $m->{y_offset} ],
[ $x[0], $y + $m->{y_short} + $m->{hex_side} + $m->{y_offset} ],
[ $x[1], $y + $m->{y_short} + $m->{hex_side} + $m->{y_short} - $m->{y_offset} ],
[ $x[1], $y + $m->{y_short} + $m->{hex_side} + $m->{y_short} + $m->{hex_side} + $m->{y_offset} ];
}
@x = map $_ + $m->{distance}, reverse @x; # draw symmetrical pattern
$x += $m->{distance};
}
push @polygons, Slic3r::Polygon->new(@$p);
}
$_->rotate(-$rotate_vector->[0][0], $m->{hex_center}) for @polygons;
}
my @paths;
if ($params{complete} || 1) {
# we were requested to complete each loop;
# in this case we don't try to make more continuous paths
@paths = map $_->split_at_first_point,
@{intersection([ $surface->p ], \@polygons)};
} else {
# consider polygons as polylines without re-appending the initial point:
# this cuts the last segment on purpose, so that the jump to the next
# path is more straight
@paths = @{intersection_pl(
[ map Slic3r::Polyline->new(@$_), @polygons ],
[ @{$surface->expolygon} ],
)};
# connect paths
if (@paths) { # prevent calling leftmost_point() on empty collections
my $collection = Slic3r::Polyline::Collection->new(@paths);
@paths = ();
foreach my $path (@{$collection->chained_path_from($collection->leftmost_point, 0)}) {
if (@paths) {
# distance between first point of this path and last point of last path
my $distance = $paths[-1]->last_point->distance_to($path->first_point);
if ($distance <= $m->{hex_width}) {
$paths[-1]->append_polyline($path);
next;
}
}
# make a clone before $collection goes out of scope
push @paths, $path->clone;
}
}
# clip paths again to prevent connection segments from crossing the expolygon boundaries
@paths = @{intersection_pl(
\@paths,
[ map @$_, @{$surface->expolygon->offset_ex(scaled_epsilon)} ],
)};
}
return @paths;
}
1;

View File

@ -1,118 +0,0 @@
package Slic3r::Fill::PlanePath;
use Moo;
extends 'Slic3r::Fill::Base';
with qw(Slic3r::Fill::WithDirection);
use Slic3r::Geometry qw(scale X1 Y1 X2 Y2);
use Slic3r::Geometry::Clipper qw(intersection_pl);
sub angles () { [0] }
sub multiplier () { 1 }
sub process_polyline {}
sub fill_surface {
my $self = shift;
my ($surface, %params) = @_;
# rotate polygons
my $expolygon = $surface->expolygon->clone;
my $rotate_vector = $self->infill_direction($surface);
$self->rotate_points($expolygon, $rotate_vector);
my $distance_between_lines = scale($self->spacing) / $params{density} * $self->multiplier;
# align infill across layers using the object's bounding box
my $bb_polygon = $self->bounding_box->polygon;
$self->rotate_points($bb_polygon, $rotate_vector);
my $bounding_box = $bb_polygon->bounding_box;
(ref $self) =~ /::([^:]+)$/;
my $path = "Math::PlanePath::$1"->new;
my $translate = Slic3r::Point->new(0,0); # vector
if ($path->x_negative || $path->y_negative) {
# if the curve extends on both positive and negative coordinate space,
# center our expolygon around origin
$translate = $bounding_box->center->negative;
} else {
# if the curve does not extend in negative coordinate space,
# move expolygon entirely in positive coordinate space
$translate = $bounding_box->min_point->negative;
}
$expolygon->translate(@$translate);
$bounding_box->translate(@$translate);
my ($n_lo, $n_hi) = $path->rect_to_n_range(
map { $_ / $distance_between_lines }
@{$bounding_box->min_point},
@{$bounding_box->max_point},
);
my $polyline = Slic3r::Polyline->new(
map [ map { $_ * $distance_between_lines } $path->n_to_xy($_) ], ($n_lo..$n_hi)
);
return {} if @$polyline <= 1;
$self->process_polyline($polyline, $bounding_box);
my @paths = @{intersection_pl([$polyline], \@$expolygon)};
if (0) {
require "Slic3r/SVG.pm";
Slic3r::SVG::output("fill.svg",
no_arrows => 1,
polygons => \@$expolygon,
green_polygons => [ $bounding_box->polygon ],
polylines => [ $polyline ],
red_polylines => \@paths,
);
}
# paths must be repositioned and rotated back
$_->translate(@{$translate->negative}) for @paths;
$self->rotate_points_back(\@paths, $rotate_vector);
return @paths;
}
package Slic3r::Fill::ArchimedeanChords;
use Moo;
extends 'Slic3r::Fill::PlanePath';
use Math::PlanePath::ArchimedeanChords;
package Slic3r::Fill::Flowsnake;
use Moo;
extends 'Slic3r::Fill::PlanePath';
use Math::PlanePath::Flowsnake;
use Slic3r::Geometry qw(X);
# Sorry, this fill is currently broken.
sub process_polyline {
my $self = shift;
my ($polyline, $bounding_box) = @_;
$_->[X] += $bounding_box->center->[X] for @$polyline;
}
package Slic3r::Fill::HilbertCurve;
use Moo;
extends 'Slic3r::Fill::PlanePath';
use Math::PlanePath::HilbertCurve;
package Slic3r::Fill::OctagramSpiral;
use Moo;
extends 'Slic3r::Fill::PlanePath';
use Math::PlanePath::OctagramSpiral;
sub multiplier () { sqrt(2) }
1;

View File

@ -1,175 +0,0 @@
package Slic3r::Fill::Rectilinear;
use Moo;
extends 'Slic3r::Fill::Base';
with qw(Slic3r::Fill::WithDirection);
has '_min_spacing' => (is => 'rw');
has '_line_spacing' => (is => 'rw');
has '_diagonal_distance' => (is => 'rw');
has '_line_oscillation' => (is => 'rw');
use Slic3r::Geometry qw(scale unscale scaled_epsilon);
use Slic3r::Geometry::Clipper qw(intersection_pl);
sub horizontal_lines { 0 }
sub fill_surface {
my $self = shift;
my ($surface, %params) = @_;
# rotate polygons so that we can work with vertical lines here
my $expolygon = $surface->expolygon->clone;
my $rotate_vector = $self->infill_direction($surface);
$self->rotate_points($expolygon, $rotate_vector);
$self->_min_spacing(scale $self->spacing);
$self->_line_spacing($self->_min_spacing / $params{density});
$self->_diagonal_distance($self->_line_spacing * 2);
$self->_line_oscillation($self->_line_spacing - $self->_min_spacing); # only for Line infill
my $bounding_box = $expolygon->bounding_box;
# define flow spacing according to requested density
if ($params{density} == 1 && !$params{dont_adjust}) {
$self->_line_spacing($self->adjust_solid_spacing(
width => $bounding_box->size->x,
distance => $self->_line_spacing,
));
$self->spacing(unscale $self->_line_spacing);
} else {
# extend bounding box so that our pattern will be aligned with other layers
$bounding_box->merge_point(Slic3r::Point->new(
$bounding_box->x_min - ($bounding_box->x_min % $self->_line_spacing),
$bounding_box->y_min - ($bounding_box->y_min % $self->_line_spacing),
));
}
# generate the basic pattern
my $x_max = $bounding_box->x_max + scaled_epsilon;
my @lines = ();
for (my $x = $bounding_box->x_min; $x <= $x_max; $x += $self->_line_spacing) {
push @lines, $self->_line($#lines, $x, $bounding_box->y_min, $bounding_box->y_max);
}
if ($self->horizontal_lines) {
my $y_max = $bounding_box->y_max + scaled_epsilon;
for (my $y = $bounding_box->y_min; $y <= $y_max; $y += $self->_line_spacing) {
push @lines, Slic3r::Polyline->new(
[$bounding_box->x_min, $y],
[$bounding_box->x_max, $y],
);
}
}
# clip paths against a slightly larger expolygon, so that the first and last paths
# are kept even if the expolygon has vertical sides
# the minimum offset for preventing edge lines from being clipped is scaled_epsilon;
# however we use a larger offset to support expolygons with slightly skewed sides and
# not perfectly straight
my @polylines = @{intersection_pl(\@lines, $expolygon->offset(+scale 0.02))};
my $extra = $self->_min_spacing * &Slic3r::INFILL_OVERLAP_OVER_SPACING;
foreach my $polyline (@polylines) {
my ($first_point, $last_point) = @$polyline[0,-1];
if ($first_point->y > $last_point->y) { #>
($first_point, $last_point) = ($last_point, $first_point);
}
$first_point->set_y($first_point->y - $extra); #--
$last_point->set_y($last_point->y + $extra); #++
}
# connect lines
unless ($params{dont_connect} || !@polylines) { # prevent calling leftmost_point() on empty collections
# offset the expolygon by max(min_spacing/2, extra)
my ($expolygon_off) = @{$expolygon->offset_ex($self->_min_spacing/2)};
my $collection = Slic3r::Polyline::Collection->new(@polylines);
@polylines = ();
foreach my $polyline (@{$collection->chained_path_from($collection->leftmost_point, 0)}) {
if (@polylines) {
my $first_point = $polyline->first_point;
my $last_point = $polylines[-1]->last_point;
my @distance = map abs($first_point->$_ - $last_point->$_), qw(x y);
# TODO: we should also check that both points are on a fill_boundary to avoid
# connecting paths on the boundaries of internal regions
if ($self->_can_connect(@distance) && $expolygon_off->contains_line(Slic3r::Line->new($last_point, $first_point))) {
$polylines[-1]->append_polyline($polyline);
next;
}
}
# make a clone before $collection goes out of scope
push @polylines, $polyline->clone;
}
}
# paths must be rotated back
$self->rotate_points_back(\@polylines, $rotate_vector);
return @polylines;
}
sub _line {
my ($self, $i, $x, $y_min, $y_max) = @_;
return Slic3r::Polyline->new(
[$x, $y_min],
[$x, $y_max],
);
}
sub _can_connect {
my ($self, $dist_X, $dist_Y) = @_;
return $dist_X <= $self->_diagonal_distance
&& $dist_Y <= $self->_diagonal_distance;
}
package Slic3r::Fill::Line;
use Moo;
extends 'Slic3r::Fill::Rectilinear';
use Slic3r::Geometry qw(scaled_epsilon);
sub _line {
my ($self, $i, $x, $y_min, $y_max) = @_;
if ($i % 2) {
return Slic3r::Polyline->new(
[$x - $self->_line_oscillation, $y_min],
[$x + $self->_line_oscillation, $y_max],
);
} else {
return Slic3r::Polyline->new(
[$x, $y_min],
[$x, $y_max],
);
}
}
sub _can_connect {
my ($self, $dist_X, $dist_Y) = @_;
my $TOLERANCE = 10 * scaled_epsilon;
return ($dist_X >= ($self->_line_spacing - $self->_line_oscillation) - $TOLERANCE)
&& ($dist_X <= ($self->_line_spacing + $self->_line_oscillation) + $TOLERANCE)
&& $dist_Y <= $self->_diagonal_distance;
}
package Slic3r::Fill::Grid;
use Moo;
extends 'Slic3r::Fill::Rectilinear';
sub angles () { [0] }
sub horizontal_lines { 1 }
package Slic3r::Fill::AlignedRectilinear;
use Moo;
extends 'Slic3r::Fill::Rectilinear';
sub angles () { [0, 0] }
1;

View File

@ -29,15 +29,6 @@ sub regions {
return [ map $self->get_region($_), 0..($self->region_count-1) ];
}
sub make_fill {
my ($self) = @_;
foreach my $layerm (@{$self->regions}) {
$layerm->fills->clear;
$layerm->fills->append($_) for $self->object->fill_maker->make_fill($layerm);
}
}
package Slic3r::Layer::Support;
our @ISA = qw(Slic3r::Layer);

View File

@ -522,12 +522,12 @@ sub infill {
thread_cb => sub {
my $q = shift;
while (defined (my $i = $q->dequeue)) {
$self->get_layer($i)->make_fill;
$self->get_layer($i)->make_fills;
}
},
no_threads_cb => sub {
foreach my $layer (@{$self->layers}) {
$layer->make_fill;
$layer->make_fills;
}
},
);

View File

@ -558,11 +558,6 @@ sub generate_toolpaths {
$pattern = 'honeycomb';
}
my %fillers = (
interface => $object->fill_maker->filler('rectilinear'),
support => $object->fill_maker->filler($pattern),
);
my $interface_angle = $self->object_config->support_material_angle + 90;
my $interface_spacing = $self->object_config->support_material_interface_spacing + $interface_flow->spacing;
my $interface_density = $interface_spacing == 0 ? 1 : $interface_flow->spacing / $interface_spacing;
@ -673,10 +668,20 @@ sub generate_toolpaths {
$layer->support_interface_fills->append(@loops);
}
# Allocate the fillers exclusively in the worker threads! Don't allocate them at the main thread,
# as Perl copies the C++ pointers by default, so then the C++ objects are shared between threads!
my %fillers = (
interface => Slic3r::Filler->new_from_type('rectilinear'),
support => Slic3r::Filler->new_from_type($pattern),
);
my $bounding_box = $object->bounding_box;
$fillers{interface}->set_bounding_box($object->bounding_box);
$fillers{support}->set_bounding_box($object->bounding_box);
# interface and contact infill
if (@$interface || @$contact_infill) {
$fillers{interface}->angle($interface_angle);
$fillers{interface}->spacing($_interface_flow->spacing);
$fillers{interface}->set_angle($interface_angle);
$fillers{interface}->set_spacing($_interface_flow->spacing);
# find centerline of the external loop
$interface = offset2($interface, +scaled_epsilon, -(scaled_epsilon + $_interface_flow->scaled_width/2));
@ -702,7 +707,7 @@ sub generate_toolpaths {
my @paths = ();
foreach my $expolygon (@{union_ex($interface)}) {
my @p = $fillers{interface}->fill_surface(
my $p = $fillers{interface}->fill_surface(
Slic3r::Surface->new(expolygon => $expolygon, surface_type => S_TYPE_INTERNAL),
density => $interface_density,
layer_height => $layer->height,
@ -716,7 +721,7 @@ sub generate_toolpaths {
mm3_per_mm => $mm3_per_mm,
width => $_interface_flow->width,
height => $layer->height,
), @p;
), @$p;
}
$layer->support_interface_fills->append(@paths);
@ -725,11 +730,11 @@ sub generate_toolpaths {
# support or flange
if (@$base) {
my $filler = $fillers{support};
$filler->angle($angles[ ($layer_id) % @angles ]);
$filler->set_angle($angles[ ($layer_id) % @angles ]);
# We don't use $base_flow->spacing because we need a constant spacing
# value that guarantees that all layers are correctly aligned.
$filler->spacing($flow->spacing);
$filler->set_spacing($flow->spacing);
my $density = $support_density;
my $base_flow = $_flow;
@ -742,13 +747,13 @@ sub generate_toolpaths {
# base flange
if ($layer_id == 0) {
$filler = $fillers{interface};
$filler->angle($self->object_config->support_material_angle + 90);
$filler->set_angle($self->object_config->support_material_angle + 90);
$density = 0.5;
$base_flow = $self->first_layer_flow;
# use the proper spacing for first layer as we don't need to align
# its pattern to the other layers
$filler->spacing($base_flow->spacing);
$filler->set_spacing($base_flow->spacing);
} else {
# draw a perimeter all around support infill
# TODO: use brim ordering algorithm
@ -767,7 +772,7 @@ sub generate_toolpaths {
my $mm3_per_mm = $base_flow->mm3_per_mm;
foreach my $expolygon (@$to_infill) {
my @p = $filler->fill_surface(
my $p = $filler->fill_surface(
Slic3r::Surface->new(expolygon => $expolygon, surface_type => S_TYPE_INTERNAL),
density => $density,
layer_height => $layer->height,
@ -780,7 +785,7 @@ sub generate_toolpaths {
mm3_per_mm => $mm3_per_mm,
width => $base_flow->width,
height => $layer->height,
), @p;
), @$p;
}
$layer->support_fills->append(@paths);

View File

@ -11,7 +11,7 @@ BEGIN {
use List::Util qw(first sum);
use Slic3r;
use Slic3r::Geometry qw(X Y scale unscale convex_hull);
use Slic3r::Geometry qw(PI X Y scale unscale convex_hull);
use Slic3r::Geometry::Clipper qw(union diff diff_ex offset offset2_ex);
use Slic3r::Surface qw(:types);
use Slic3r::Test;
@ -20,25 +20,17 @@ sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ }
{
my $print = Slic3r::Print->new;
my $filler = Slic3r::Fill::Rectilinear->new(
print => $print,
bounding_box => Slic3r::Geometry::BoundingBox->new_from_points([ Slic3r::Point->new(0, 0), Slic3r::Point->new(10, 10) ]),
);
my $surface_width = 250;
my $distance = $filler->adjust_solid_spacing(
width => $surface_width,
distance => 100,
);
is $distance, 125, 'adjusted solid distance';
my $distance = Slic3r::Filler::adjust_solid_spacing($surface_width, 47);
is $distance, 50, 'adjusted solid distance';
is $surface_width % $distance, 0, 'adjusted solid distance';
}
{
my $expolygon = Slic3r::ExPolygon->new([ scale_points [0,0], [50,0], [50,50], [0,50] ]);
my $filler = Slic3r::Fill::Rectilinear->new(
bounding_box => $expolygon->bounding_box,
angle => 0,
);
my $filler = Slic3r::Filler->new_from_type('rectilinear');
$filler->set_bounding_box($expolygon->bounding_box);
$filler->set_angle(0);
my $surface = Slic3r::Surface->new(
surface_type => S_TYPE_TOP,
expolygon => $expolygon,
@ -48,11 +40,11 @@ sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ }
height => 0.4,
nozzle_diameter => 0.50,
);
$filler->spacing($flow->spacing);
$filler->set_spacing($flow->spacing);
foreach my $angle (0, 45) {
$surface->expolygon->rotate(Slic3r::Geometry::deg2rad($angle), [0,0]);
my @paths = $filler->fill_surface($surface, layer_height => 0.4, density => 0.4);
is scalar @paths, 1, 'one continuous path';
my $paths = $filler->fill_surface($surface, layer_height => 0.4, density => 0.4);
is scalar @$paths, 1, 'one continuous path';
}
}
@ -60,10 +52,9 @@ sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ }
my $test = sub {
my ($expolygon, $flow_spacing, $angle, $density) = @_;
my $filler = Slic3r::Fill::Rectilinear->new(
bounding_box => $expolygon->bounding_box,
angle => $angle // 0,
);
my $filler = Slic3r::Filler->new_from_type('rectilinear');
$filler->set_bounding_box($expolygon->bounding_box);
$filler->set_angle($angle // 0);
my $surface = Slic3r::Surface->new(
surface_type => S_TYPE_BOTTOM,
expolygon => $expolygon,
@ -73,15 +64,15 @@ sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ }
height => 0.4,
nozzle_diameter => $flow_spacing,
);
$filler->spacing($flow->spacing);
my @paths = $filler->fill_surface(
$filler->set_spacing($flow->spacing);
my $paths = $filler->fill_surface(
$surface,
layer_height => $flow->height,
density => $density // 1,
);
# check whether any part was left uncovered
my @grown_paths = map @{Slic3r::Polyline->new(@$_)->grow(scale $filler->spacing/2)}, @paths;
my @grown_paths = map @{Slic3r::Polyline->new(@$_)->grow(scale $filler->spacing/2)}, @$paths;
my $uncovered = diff_ex([ @$expolygon ], [ @grown_paths ], 1);
# ignore very small dots
@ -93,8 +84,9 @@ sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ }
require "Slic3r/SVG.pm";
Slic3r::SVG::output(
"uncovered.svg",
expolygons => [$expolygon],
red_expolygons => $uncovered,
expolygons => [$expolygon],
red_expolygons => $uncovered,
polylines => $paths,
);
exit;
}
@ -116,7 +108,7 @@ sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ }
$expolygon = Slic3r::ExPolygon->new(
[[59515297,5422499],[59531249,5578697],[59695801,6123186],[59965713,6630228],[60328214,7070685],[60773285,7434379],[61274561,7702115],[61819378,7866770],[62390306,7924789],[62958700,7866744],[63503012,7702244],[64007365,7434357],[64449960,7070398],[64809327,6634999],[65082143,6123325],[65245005,5584454],[65266967,5422499],[66267307,5422499],[66269190,8310081],[66275379,17810072],[66277259,20697500],[65267237,20697500],[65245004,20533538],[65082082,19994444],[64811462,19488579],[64450624,19048208],[64012101,18686514],[63503122,18415781],[62959151,18251378],[62453416,18198442],[62390147,18197355],[62200087,18200576],[61813519,18252990],[61274433,18415918],[60768598,18686517],[60327567,19047892],[59963609,19493297],[59695865,19994587],[59531222,20539379],[59515153,20697500],[58502480,20697500],[58502480,5422499]]
);
$test->($expolygon, 0.524341649025257);
$test->($expolygon, 0.524341649025257, PI/2);
$expolygon = Slic3r::ExPolygon->new([ scale_points [0,0], [98,0], [98,10], [0,10] ]);
$test->($expolygon, 0.5, 45, 0.99); # non-solid infill

View File

@ -97,17 +97,6 @@ pp `
-M LWP::Protocol::http `
-M LWP::UserAgent `
-M List::Util `
-M Math::Libm `
-M Math::PlanePath `
-M Math::PlanePath::ArchimedeanChords `
-M Math::PlanePath::Base::Digits `
-M Math::PlanePath::Base::Generic `
-M Math::PlanePath::Base::NSEW `
-M Math::PlanePath::Flowsnake `
-M Math::PlanePath::FlowsnakeCentres `
-M Math::PlanePath::HilbertCurve `
-M Math::PlanePath::OctagramSpiral `
-M Math::PlanePath::SacksSpiral `
-M Math::Trig `
-M Method::Generate::Accessor `
-M Method::Generate::BuildAll `

View File

@ -154,6 +154,9 @@ if ($ENV{SLIC3R_DEBUG}) {
# only on newer GCCs: -ftemplate-backtrace-limit=0
push @cflags, '-DSLIC3R_DEBUG';
push @cflags, $cpp_guess->is_msvc ? '-Gd' : '-g';
} else {
# Disable asserts in the release builds.
push @cflags, '-DNDEBUG';
}
if ($cpp_guess->is_gcc) {
# check whether we're dealing with a buggy GCC version

View File

@ -28,6 +28,22 @@ src/libslic3r/ExtrusionEntity.cpp
src/libslic3r/ExtrusionEntity.hpp
src/libslic3r/ExtrusionEntityCollection.cpp
src/libslic3r/ExtrusionEntityCollection.hpp
src/libslic3r/Fill/Fill.cpp
src/libslic3r/Fill/Fill.hpp
src/libslic3r/Fill/FillBase.cpp
src/libslic3r/Fill/FillBase.hpp
src/libslic3r/Fill/FillConcentric.cpp
src/libslic3r/Fill/FillConcentric.hpp
src/libslic3r/Fill/FillHoneycomb.cpp
src/libslic3r/Fill/FillHoneycomb.hpp
src/libslic3r/Fill/Fill3DHoneycomb.cpp
src/libslic3r/Fill/Fill3DHoneycomb.hpp
src/libslic3r/Fill/FillPlanePath.cpp
src/libslic3r/Fill/FillPlanePath.hpp
src/libslic3r/Fill/FillRectilinear.cpp
src/libslic3r/Fill/FillRectilinear.hpp
src/libslic3r/Fill/FillRectilinear2.cpp
src/libslic3r/Fill/FillRectilinear2.hpp
src/libslic3r/Flow.cpp
src/libslic3r/Flow.hpp
src/libslic3r/GCode.cpp
@ -136,6 +152,7 @@ xsp/Extruder.xsp
xsp/ExtrusionEntityCollection.xsp
xsp/ExtrusionLoop.xsp
xsp/ExtrusionPath.xsp
xsp/Filler.xsp
xsp/Flow.xsp
xsp/GCode.xsp
xsp/GCodeSender.xsp

View File

@ -123,6 +123,17 @@ sub clone {
);
}
package Slic3r::Filler;
sub fill_surface {
my ($self, $surface, %args) = @_;
$self->set_density($args{density}) if defined($args{density});
$self->set_dont_connect($args{dont_connect}) if defined($args{dont_connect});
$self->set_dont_adjust($args{dont_adjust}) if defined($args{dont_adjust});
$self->set_complete($args{complete}) if defined($args{complete});
return $self->_fill_surface($surface);
}
package Slic3r::Flow;
sub new {
@ -215,6 +226,7 @@ for my $class (qw(
Slic3r::ExtrusionLoop
Slic3r::ExtrusionPath
Slic3r::ExtrusionPath::Collection
Slic3r::Filler
Slic3r::Flow
Slic3r::GCode
Slic3r::GCode::AvoidCrossingPerimeters

View File

@ -68,6 +68,26 @@ BoundingBox::polygon() const
return p;
}
BoundingBox BoundingBox::rotated(double angle) const
{
BoundingBox out;
out.merge(this->min.rotated(angle));
out.merge(this->max.rotated(angle));
out.merge(Point(this->min.x, this->max.y).rotated(angle));
out.merge(Point(this->max.x, this->min.y).rotated(angle));
return out;
}
BoundingBox BoundingBox::rotated(double angle, const Point &center) const
{
BoundingBox out;
out.merge(this->min.rotated(angle, center));
out.merge(this->max.rotated(angle, center));
out.merge(Point(this->min.x, this->max.y).rotated(angle, center));
out.merge(Point(this->max.x, this->min.y).rotated(angle, center));
return out;
}
template <class PointClass> void
BoundingBoxBase<PointClass>::scale(double factor)
{

View File

@ -55,6 +55,14 @@ class BoundingBox : public BoundingBoxBase<Point>
public:
void polygon(Polygon* polygon) const;
Polygon polygon() const;
BoundingBox rotated(double angle) const;
BoundingBox rotated(double angle, const Point &center) const;
void rotate(double angle) {
*this = this->rotated(angle);
}
void rotate(double angle, const Point &center) {
*this = this->rotated(angle, center);
}
BoundingBox() : BoundingBoxBase<Point>() {};
BoundingBox(const Point &pmin, const Point &pmax) : BoundingBoxBase<Point>(pmin, pmax) {};
@ -92,16 +100,6 @@ inline bool operator!=(const BoundingBoxBase<VT> &bb1, const BoundingBoxBase<VT>
return !(bb1 == bb2);
}
template<typename VT>
inline bool empty(const BoundingBoxBase<VT> &bb)
{
return bb.min.x > bb.max.y || bb.min.y > bb.max.y;
}
template<typename VT>
inline bool empty(const BoundingBox3Base<VT> &bb)
{
return bb.min.x > bb.max.x || bb.min.y > bb.max.y || bb.min.z > bb.max.z;}
}
#endif

View File

@ -52,6 +52,15 @@ ExPolygon::translate(double x, double y)
}
}
void
ExPolygon::rotate(double angle)
{
contour.rotate(angle);
for (Polygons::iterator it = holes.begin(); it != holes.end(); ++it) {
(*it).rotate(angle);
}
}
void
ExPolygon::rotate(double angle, const Point &center)
{
@ -497,15 +506,4 @@ ExPolygon::dump_perl() const
return ret.str();
}
Polygons
to_polygons(const ExPolygons &expolygons)
{
Slic3r::Polygons pp;
for (ExPolygons::const_iterator ex = expolygons.begin(); ex != expolygons.end(); ++ex) {
Slic3r::Polygons ppp = *ex;
pp.insert(pp.end(), ppp.begin(), ppp.end());
}
return pp;
}
}

View File

@ -20,6 +20,7 @@ class ExPolygon
operator Polygons() const;
void scale(double factor);
void translate(double x, double y);
void rotate(double angle);
void rotate(double angle, const Point &center);
double area() const;
bool is_valid() const;
@ -45,7 +46,14 @@ class ExPolygon
std::string dump_perl() const;
};
Polygons to_polygons(const ExPolygons &expolygons);
inline Polygons
to_polygons(const ExPolygons &expolygons)
{
Polygons pp;
for (ExPolygons::const_iterator ex = expolygons.begin(); ex != expolygons.end(); ++ex)
append_to(pp, (Polygons)*ex);
return pp;
}
}

View File

@ -101,6 +101,17 @@ ExtrusionEntityCollection::append(const ExtrusionPaths &paths)
this->append(*path);
}
void
ExtrusionEntityCollection::append(const Polylines &polylines, const ExtrusionPath &templ)
{
this->entities.reserve(this->entities.size() + polylines.size());
for (Polylines::const_iterator it_polyline = polylines.begin(); it_polyline != polylines.end(); ++ it_polyline) {
ExtrusionPath *path = templ.clone();
path->polyline = *it_polyline;
this->entities.push_back(path);
}
}
void
ExtrusionEntityCollection::replace(size_t i, const ExtrusionEntity &entity)
{

View File

@ -3,6 +3,7 @@
#include "libslic3r.h"
#include "ExtrusionEntity.hpp"
#include "Polyline.hpp"
namespace Slic3r {
@ -36,6 +37,7 @@ class ExtrusionEntityCollection : public ExtrusionEntity
void append(const ExtrusionEntity &entity);
void append(const ExtrusionEntitiesPtr &entities);
void append(const ExtrusionPaths &paths);
void append(const Polylines &polylines, const ExtrusionPath &templ);
void replace(size_t i, const ExtrusionEntity &entity);
void remove(size_t i);
ExtrusionEntityCollection chained_path(bool no_reverse = false, std::vector<size_t>* orig_indices = NULL) const;

View File

@ -0,0 +1,290 @@
#include <assert.h>
#include <stdio.h>
#include <memory>
#include "../ClipperUtils.hpp"
#include "../Geometry.hpp"
#include "../Layer.hpp"
#include "../Print.hpp"
#include "../PrintConfig.hpp"
#include "../Surface.hpp"
#include "../SurfaceCollection.hpp"
#include "FillBase.hpp"
namespace Slic3r {
struct SurfaceGroupAttrib
{
SurfaceGroupAttrib() : is_solid(false), fw(0.f), pattern(-1) {}
bool operator==(const SurfaceGroupAttrib &other) const
{ return is_solid == other.is_solid && fw == other.fw && pattern == other.pattern; }
bool is_solid;
float fw;
// pattern is of type InfillPattern, -1 for an unset pattern.
int pattern;
};
// Generate infills for a LayerRegion.
// The LayerRegion at this point of time may contain
// surfaces of various types (internal/bridge/top/bottom/solid).
// The infills are generated on the groups of surfaces with a compatible type.
// Returns an array of ExtrusionPathCollection objects containing the infills generated now
// and the thin fills generated by generate_perimeters().
void make_fill(const LayerRegion &layerm, ExtrusionEntityCollection* out)
{
// Slic3r::debugf "Filling layer %d:\n", $layerm->layer->id;
double fill_density = layerm.region()->config.fill_density;
const Flow infill_flow = layerm.flow(frInfill);
const Flow solid_infill_flow = layerm.flow(frSolidInfill);
const Flow top_solid_infill_flow = layerm.flow(frTopSolidInfill);
SurfaceCollection surfaces;
// merge adjacent surfaces
// in case of bridge surfaces, the ones with defined angle will be attached to the ones
// without any angle (shouldn't this logic be moved to process_external_surfaces()?)
{
Polygons polygons_bridged;
polygons_bridged.reserve(layerm.fill_surfaces.surfaces.size());
for (Surfaces::const_iterator it = layerm.fill_surfaces.surfaces.begin(); it != layerm.fill_surfaces.surfaces.end(); ++it)
if (it->bridge_angle >= 0)
append_to(polygons_bridged, (Polygons)*it);
// group surfaces by distinct properties (equal surface_type, thickness, thickness_layers, bridge_angle)
// group is of type SurfaceCollection
// FIXME: Use some smart heuristics to merge similar surfaces to eliminate tiny regions.
std::vector<SurfacesConstPtr> groups;
layerm.fill_surfaces.group(&groups);
// merge compatible groups (we can generate continuous infill for them)
{
// cache flow widths and patterns used for all solid groups
// (we'll use them for comparing compatible groups)
std::vector<SurfaceGroupAttrib> group_attrib(groups.size());
for (size_t i = 0; i < groups.size(); ++i) {
// we can only merge solid non-bridge surfaces, so discard
// non-solid surfaces
const Surface &surface = *groups[i].front();
if (surface.is_solid() && (!surface.is_bridge() || layerm.layer()->id() == 0)) {
group_attrib[i].is_solid = true;
group_attrib[i].fw = (surface.surface_type == stTop) ? top_solid_infill_flow.width : solid_infill_flow.width;
group_attrib[i].pattern = surface.is_external() ? layerm.region()->config.external_fill_pattern.value : ipRectilinear;
}
}
// Loop through solid groups, find compatible groups and append them to this one.
for (size_t i = 0; i < groups.size(); ++i) {
if (!group_attrib[i].is_solid)
continue;
for (size_t j = i + 1; j < groups.size();) {
if (group_attrib[i] == group_attrib[j]) {
// groups are compatible, merge them
append_to(groups[i], groups[j]);
groups.erase(groups.begin() + j);
group_attrib.erase(group_attrib.begin() + j);
} else {
++j;
}
}
}
}
// Give priority to bridges. Process the bridges in the first round, the rest of the surfaces in the 2nd round.
for (size_t round = 0; round < 2; ++ round) {
for (std::vector<SurfacesConstPtr>::const_iterator it_group = groups.begin(); it_group != groups.end(); ++ it_group) {
const SurfacesConstPtr &group = *it_group;
bool is_bridge = group.front()->bridge_angle >= 0;
if (is_bridge != (round == 0))
continue;
// Make a union of polygons defining the infiill regions of a group, use a safety offset.
Polygons union_p = union_(to_polygons(group), true);
// Subtract surfaces having a defined bridge_angle from any other, use a safety offset.
if (!polygons_bridged.empty() && !is_bridge)
union_p = diff(union_p, polygons_bridged, true);
// subtract any other surface already processed
//FIXME Vojtech: Because the bridge surfaces came first, they are subtracted twice!
surfaces.append(
diff_ex(union_p, to_polygons(surfaces), true),
*group.front() // template
);
}
}
}
// we need to detect any narrow surfaces that might collapse
// when adding spacing below
// such narrow surfaces are often generated in sloping walls
// by bridge_over_infill() and combine_infill() as a result of the
// subtraction of the combinable area from the layer infill area,
// which leaves small areas near the perimeters
// we are going to grow such regions by overlapping them with the void (if any)
// TODO: detect and investigate whether there could be narrow regions without
// any void neighbors
{
coord_t distance_between_surfaces = std::max(
std::max(infill_flow.scaled_spacing(), solid_infill_flow.scaled_spacing()),
top_solid_infill_flow.scaled_spacing()
);
Polygons surfaces_polygons = (Polygons)surfaces;
Polygons collapsed = diff(
surfaces_polygons,
offset2(surfaces_polygons, -distance_between_surfaces/2, +distance_between_surfaces/2),
true
);
Polygons to_subtract;
surfaces.filter_by_type(stInternalVoid, &to_subtract);
append_to(to_subtract, collapsed);
surfaces.append(
intersection_ex(
offset(collapsed, distance_between_surfaces),
to_subtract,
true
),
stInternalSolid
);
}
if (false) {
// require "Slic3r/SVG.pm";
// Slic3r::SVG::output("fill_" . $layerm->print_z . ".svg",
// expolygons => [ map $_->expolygon, grep !$_->is_solid, @surfaces ],
// red_expolygons => [ map $_->expolygon, grep $_->is_solid, @surfaces ],
// );
}
for (Surfaces::const_iterator surface_it = surfaces.surfaces.begin();
surface_it != surfaces.surfaces.end(); ++surface_it) {
const Surface &surface = *surface_it;
if (surface.surface_type == stInternalVoid)
continue;
InfillPattern fill_pattern = layerm.region()->config.fill_pattern.value;
double density = fill_density;
FlowRole role = (surface.surface_type == stTop) ? frTopSolidInfill
: surface.is_solid() ? frSolidInfill
: frInfill;
const bool is_bridge = layerm.layer()->id() > 0 && surface.is_bridge();
if (surface.is_solid()) {
density = 100.;
fill_pattern = (surface.is_external() && !is_bridge)
? layerm.region()->config.external_fill_pattern.value
: ipRectilinear;
} else if (density <= 0)
continue;
// get filler object
#if SLIC3R_CPPVER >= 11
std::unique_ptr<Fill> f = std::unique_ptr<Fill>(Fill::new_from_type(fill_pattern));
#else
std::auto_ptr<Fill> f = std::auto_ptr<Fill>(Fill::new_from_type(fill_pattern));
#endif
f->set_bounding_box(layerm.layer()->object()->bounding_box());
// calculate the actual flow we'll be using for this infill
coordf_t h = (surface.thickness == -1) ? layerm.layer()->height : surface.thickness;
Flow flow = layerm.region()->flow(
role,
h,
is_bridge || f->use_bridge_flow(), // bridge flow?
layerm.layer()->id() == 0, // first layer?
-1, // auto width
*layerm.layer()->object()
);
// calculate flow spacing for infill pattern generation
bool using_internal_flow = false;
if (!surface.is_solid() && !is_bridge) {
// it's internal infill, so we can calculate a generic flow spacing
// for all layers, for avoiding the ugly effect of
// misaligned infill on first layer because of different extrusion width and
// layer height
Flow internal_flow = layerm.region()->flow(
frInfill,
layerm.layer()->object()->config.layer_height.value, // TODO: handle infill_every_layers?
false, // no bridge
false, // no first layer
-1, // auto width
*layerm.layer()->object()
);
f->spacing = internal_flow.spacing();
using_internal_flow = true;
} else {
f->spacing = flow.spacing();
}
f->layer_id = layerm.layer()->id();
f->z = layerm.layer()->print_z;
f->angle = Geometry::deg2rad(layerm.region()->config.fill_angle.value);
// Maximum length of the perimeter segment linking two infill lines.
f->link_max_length = (!is_bridge && density > 80)
? scale_(3 * f->spacing)
: 0;
// Used by the concentric infill pattern to clip the loops to create extrusion paths.
f->loop_clipping = scale_(flow.nozzle_diameter) * LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER;
// apply half spacing using this flow's own spacing and generate infill
FillParams params;
params.density = density/100;
params.dont_adjust = false;
Polylines polylines = f->fill_surface(surface, params);
if (polylines.empty())
continue;
// calculate actual flow from spacing (which might have been adjusted by the infill
// pattern generator)
if (using_internal_flow) {
// if we used the internal flow we're not doing a solid infill
// so we can safely ignore the slight variation that might have
// been applied to f->spacing
} else {
flow = Flow::new_from_spacing(f->spacing, flow.nozzle_diameter, h, is_bridge || f->use_bridge_flow());
}
// Save into layer.
ExtrusionEntityCollection* coll = new ExtrusionEntityCollection();
coll->no_sort = f->no_sort();
out->entities.push_back(coll);
{
ExtrusionRole role;
if (is_bridge) {
role = erBridgeInfill;
} else if (surface.is_solid()) {
role = (surface.surface_type == stTop) ? erTopSolidInfill : erSolidInfill;
} else {
role = erInternalInfill;
}
ExtrusionPath templ(role);
templ.mm3_per_mm = flow.mm3_per_mm();
templ.width = flow.width;
templ.height = flow.height;
coll->append(STDMOVE(polylines), templ);
}
}
// add thin fill regions
// thin_fills are of C++ Slic3r::ExtrusionEntityCollection, perl type Slic3r::ExtrusionPath::Collection
// Unpacks the collection, creates multiple collections per path so that they will
// be individually included in the nearest neighbor search.
// The path type could be ExtrusionPath, ExtrusionLoop or ExtrusionEntityCollection.
for (ExtrusionEntitiesPtr::const_iterator thin_fill = layerm.thin_fills.entities.begin(); thin_fill != layerm.thin_fills.entities.end(); ++ thin_fill) {
ExtrusionEntityCollection* coll = new ExtrusionEntityCollection();
out->entities.push_back(coll);
coll->entities.push_back((*thin_fill)->clone());
}
}
} // namespace Slic3r

View File

@ -0,0 +1,23 @@
#ifndef slic3r_Fill_hpp_
#define slic3r_Fill_hpp_
#include <memory.h>
#include <float.h>
#include <stdint.h>
#include "../libslic3r.h"
#include "../BoundingBox.hpp"
#include "../PrintConfig.hpp"
#include "FillBase.hpp"
namespace Slic3r {
class ExtrusionEntityCollection;
class LayerRegion;
void make_fill(const LayerRegion &layerm, ExtrusionEntityCollection* out);
} // namespace Slic3r
#endif // slic3r_Fill_hpp_

View File

@ -0,0 +1,227 @@
#include "../ClipperUtils.hpp"
#include "../PolylineCollection.hpp"
#include "../Surface.hpp"
#include "Fill3DHoneycomb.hpp"
namespace Slic3r {
/*
Creates a contiguous sequence of points at a specified height that make
up a horizontal slice of the edges of a space filling truncated
octahedron tesselation. The octahedrons are oriented so that the
square faces are in the horizontal plane with edges parallel to the X
and Y axes.
Credits: David Eccles (gringer).
*/
// Generate an array of points that are in the same direction as the
// basic printing line (i.e. Y points for columns, X points for rows)
// Note: a negative offset only causes a change in the perpendicular
// direction
static std::vector<coordf_t>
colinearPoints(const coordf_t offset, const size_t baseLocation, size_t gridLength)
{
const coordf_t offset2 = std::abs(offset / coordf_t(2.));
std::vector<coordf_t> points;
points.push_back(baseLocation - offset2);
for (size_t i = 0; i < gridLength; ++i) {
points.push_back(baseLocation + i + offset2);
points.push_back(baseLocation + i + 1 - offset2);
}
points.push_back(baseLocation + gridLength + offset2);
return points;
}
// Generate an array of points for the dimension that is perpendicular to
// the basic printing line (i.e. X points for columns, Y points for rows)
static std::vector<coordf_t>
perpendPoints(const coordf_t offset, const size_t baseLocation, size_t gridLength)
{
const coordf_t offset2 = offset / coordf_t(2.);
coord_t side = 2 * (baseLocation & 1) - 1;
std::vector<coordf_t> points;
points.push_back(baseLocation - offset2 * side);
for (size_t i = 0; i < gridLength; ++i) {
side = 2*((i+baseLocation) & 1) - 1;
points.push_back(baseLocation + offset2 * side);
points.push_back(baseLocation + offset2 * side);
}
points.push_back(baseLocation - offset2 * side);
return points;
}
template<typename T>
static inline T
clamp(T low, T high, T x)
{
return std::max<T>(low, std::min<T>(high, x));
}
// Trims an array of points to specified rectangular limits. Point
// components that are outside these limits are set to the limits.
static inline void
trim(Pointfs &pts, coordf_t minX, coordf_t minY, coordf_t maxX, coordf_t maxY)
{
for (Pointfs::iterator it = pts.begin(); it != pts.end(); ++ it) {
it->x = clamp(minX, maxX, it->x);
it->y = clamp(minY, maxY, it->y);
}
}
static inline Pointfs
zip(const std::vector<coordf_t> &x, const std::vector<coordf_t> &y)
{
assert(x.size() == y.size());
Pointfs out;
out.reserve(x.size());
for (size_t i = 0; i < x.size(); ++ i)
out.push_back(Pointf(x[i], y[i]));
return out;
}
// Generate a set of curves (array of array of 2d points) that describe a
// horizontal slice of a truncated regular octahedron with edge length 1.
// curveType specifies which lines to print, 1 for vertical lines
// (columns), 2 for horizontal lines (rows), and 3 for both.
static std::vector<Pointfs>
makeNormalisedGrid(coordf_t z, size_t gridWidth, size_t gridHeight, size_t curveType)
{
// offset required to create a regular octagram
coordf_t octagramGap = coordf_t(0.5);
// sawtooth wave function for range f($z) = [-$octagramGap .. $octagramGap]
coordf_t a = std::sqrt(coordf_t(2.)); // period
coordf_t wave = fabs(fmod(z, a) - a/2.)/a*4. - 1.;
coordf_t offset = wave * octagramGap;
std::vector<Pointfs> points;
if ((curveType & 1) != 0) {
for (size_t x = 0; x <= gridWidth; ++x) {
points.push_back(Pointfs());
Pointfs &newPoints = points.back();
newPoints = zip(
perpendPoints(offset, x, gridHeight),
colinearPoints(offset, 0, gridHeight));
// trim points to grid edges
trim(newPoints, coordf_t(0.), coordf_t(0.), coordf_t(gridWidth), coordf_t(gridHeight));
if (x & 1)
std::reverse(newPoints.begin(), newPoints.end());
}
}
if ((curveType & 2) != 0) {
for (size_t y = 0; y <= gridHeight; ++y) {
points.push_back(Pointfs());
Pointfs &newPoints = points.back();
newPoints = zip(
colinearPoints(offset, 0, gridWidth),
perpendPoints(offset, y, gridWidth)
);
// trim points to grid edges
trim(newPoints, coordf_t(0.), coordf_t(0.), coordf_t(gridWidth), coordf_t(gridHeight));
if (y & 1)
std::reverse(newPoints.begin(), newPoints.end());
}
}
return points;
}
// Generate a set of curves (array of array of 2d points) that describe a
// horizontal slice of a truncated regular octahedron with a specified
// grid square size.
static Polylines
makeGrid(coord_t z, coord_t gridSize, size_t gridWidth, size_t gridHeight, size_t curveType)
{
coord_t scaleFactor = gridSize;
coordf_t normalisedZ = coordf_t(z) / coordf_t(scaleFactor);
std::vector<Pointfs> polylines = makeNormalisedGrid(normalisedZ, gridWidth, gridHeight, curveType);
Polylines result;
result.reserve(polylines.size());
for (std::vector<Pointfs>::const_iterator it_polylines = polylines.begin(); it_polylines != polylines.end(); ++ it_polylines) {
result.push_back(Polyline());
Polyline &polyline = result.back();
for (Pointfs::const_iterator it = it_polylines->begin(); it != it_polylines->end(); ++ it)
polyline.points.push_back(Point(coord_t(it->x * scaleFactor), coord_t(it->y * scaleFactor)));
}
return result;
}
void
Fill3DHoneycomb::_fill_surface_single(
const FillParams &params,
unsigned int thickness_layers,
const std::pair<float, Point> &direction,
ExPolygon &expolygon,
Polylines* polylines_out)
{
// no rotation is supported for this infill pattern
BoundingBox bb = expolygon.contour.bounding_box();
const coord_t distance = coord_t(scale_(this->spacing) / params.density);
// align bounding box to a multiple of our honeycomb grid module
// (a module is 2*$distance since one $distance half-module is
// growing while the other $distance half-module is shrinking)
bb.min.align_to_grid(Point(2*distance, 2*distance));
// generate pattern
Polylines polylines = makeGrid(
scale_(this->z),
distance,
ceil(bb.size().x / distance) + 1,
ceil(bb.size().y / distance) + 1,
((this->layer_id/thickness_layers) % 2) + 1
);
// move pattern in place
for (Polylines::iterator it = polylines.begin(); it != polylines.end(); ++ it)
it->translate(bb.min.x, bb.min.y);
// clip pattern to boundaries
polylines = intersection_pl(polylines, (Polygons)expolygon);
// connect lines
if (!params.dont_connect && !polylines.empty()) { // prevent calling leftmost_point() on empty collections
ExPolygon expolygon_off;
{
ExPolygons expolygons_off = offset_ex(expolygon, SCALED_EPSILON);
if (!expolygons_off.empty()) {
// When expanding a polygon, the number of islands could only shrink. Therefore the offset_ex shall generate exactly one expanded island for one input island.
assert(expolygons_off.size() == 1);
std::swap(expolygon_off, expolygons_off.front());
}
}
Polylines chained = PolylineCollection::chained_path_from(
STDMOVE(polylines),
PolylineCollection::leftmost_point(polylines),
false // reverse allowed
);
bool first = true;
for (Polylines::iterator it_polyline = chained.begin(); it_polyline != chained.end(); ++ it_polyline) {
if (!first) {
// Try to connect the lines.
Points &pts_end = polylines_out->back().points;
const Point &first_point = it_polyline->points.front();
const Point &last_point = pts_end.back();
// TODO: we should also check that both points are on a fill_boundary to avoid
// connecting paths on the boundaries of internal regions
if (first_point.distance_to(last_point) <= 1.5 * distance &&
expolygon_off.contains(Line(last_point, first_point))) {
// Append the polyline.
pts_end.insert(pts_end.end(), it_polyline->points.begin(), it_polyline->points.end());
continue;
}
}
// The lines cannot be connected.
#if SLIC3R_CPPVER >= 11
polylines_out->push_back(std::move(*it_polyline));
#else
polylines_out->push_back(Polyline());
std::swap(polylines_out->back(), *it_polyline);
#endif
first = false;
}
}
}
} // namespace Slic3r

View File

@ -0,0 +1,31 @@
#ifndef slic3r_Fill3DHoneycomb_hpp_
#define slic3r_Fill3DHoneycomb_hpp_
#include <map>
#include "../libslic3r.h"
#include "FillBase.hpp"
namespace Slic3r {
class Fill3DHoneycomb : public Fill
{
public:
virtual ~Fill3DHoneycomb() {}
// require bridge flow since most of this pattern hangs in air
virtual bool use_bridge_flow() const { return true; }
protected:
virtual void _fill_surface_single(
const FillParams &params,
unsigned int thickness_layers,
const std::pair<float, Point> &direction,
ExPolygon &expolygon,
Polylines* polylines_out);
};
} // namespace Slic3r
#endif // slic3r_Fill3DHoneycomb_hpp_

View File

@ -0,0 +1,138 @@
#include <math.h>
#include <stdio.h>
#include "../ClipperUtils.hpp"
#include "../Surface.hpp"
#include "../PrintConfig.hpp"
#include "FillBase.hpp"
#include "FillConcentric.hpp"
#include "FillHoneycomb.hpp"
#include "Fill3DHoneycomb.hpp"
#include "FillPlanePath.hpp"
#include "FillRectilinear.hpp"
#include "FillRectilinear2.hpp"
namespace Slic3r {
Fill*
Fill::new_from_type(const InfillPattern type)
{
switch (type) {
case ipConcentric: return new FillConcentric();
case ipHoneycomb: return new FillHoneycomb();
case ip3DHoneycomb: return new Fill3DHoneycomb();
case ipRectilinear: return new FillRectilinear();
case ipLine: return new FillLine();
case ipGrid: return new FillGrid();
case ipAlignedRectilinear: return new FillAlignedRectilinear();
case ipRectilinear2: return new FillRectilinear2();
case ipGrid2: return new FillGrid2();
case ipTriangles: return new FillTriangles();
case ipStars: return new FillStars();
case ipCubic: return new FillCubic();
case ipArchimedeanChords: return new FillArchimedeanChords();
case ipHilbertCurve: return new FillHilbertCurve();
case ipOctagramSpiral: return new FillOctagramSpiral();
default: CONFESS("unknown type"); return NULL;
}
}
Fill*
Fill::new_from_type(const std::string &type)
{
static t_config_enum_values enum_keys_map = ConfigOptionEnum<InfillPattern>::get_enum_values();
t_config_enum_values::const_iterator it = enum_keys_map.find(type);
return (it == enum_keys_map.end()) ? NULL : new_from_type(InfillPattern(it->second));
}
Polylines
Fill::fill_surface(const Surface &surface, const FillParams &params)
{
// Perform offset.
ExPolygons expp = offset_ex(surface.expolygon, -scale_(this->spacing)/2);
// Create the infills for each of the regions.
Polylines polylines_out;
for (size_t i = 0; i < expp.size(); ++i)
this->_fill_surface_single(
params,
surface.thickness_layers,
this->_infill_direction(surface),
expp[i],
&polylines_out
);
return polylines_out;
}
// Calculate a new spacing to fill width with possibly integer number of lines,
// the first and last line being centered at the interval ends.
// This function possibly increases the spacing, never decreases,
// and for a narrow width the increase in spacing may become severe,
// therefore the adjustment is limited to 20% increase.
coord_t
Fill::adjust_solid_spacing(const coord_t width, const coord_t distance)
{
assert(width >= 0);
assert(distance > 0);
// floor(width / distance)
coord_t number_of_intervals = floor(width / distance);
coord_t distance_new = (number_of_intervals == 0)
? distance
: (width / number_of_intervals);
const coordf_t factor = coordf_t(distance_new) / coordf_t(distance);
assert(factor > 1. - 1e-5);
// How much could the extrusion width be increased? By 20%.
const coordf_t factor_max = 1.2;
if (factor > factor_max)
distance_new = floor((double)distance * factor_max + 0.5);
return distance_new;
}
// Returns orientation of the infill and the reference point of the infill pattern.
// For a normal print, the reference point is the center of a bounding box of the STL.
std::pair<float, Point>
Fill::_infill_direction(const Surface &surface) const
{
// set infill angle
float out_angle = this->angle;
// Bounding box is the bounding box of a Slic3r::PrintObject
// The bounding box is only undefined in unit tests.
Point out_shift = this->bounding_box.defined
? this->bounding_box.center()
: surface.expolygon.contour.bounding_box().center();
#if 0
if (!this->bounding_box.defined) {
printf("Fill::_infill_direction: empty bounding box!");
} else {
printf("Fill::_infill_direction: reference point %d, %d\n", out_shift.x, out_shift.y);
}
#endif
if (surface.bridge_angle >= 0) {
// use bridge angle
//FIXME Vojtech: Add a debugf?
// Slic3r::debugf "Filling bridge with angle %d\n", rad2deg($surface->bridge_angle);
#ifdef SLIC3R_DEBUG
printf("Filling bridge with angle %f\n", surface.bridge_angle);
#endif
out_angle = surface.bridge_angle;
} else if (this->layer_id != size_t(-1)) {
// alternate fill direction
out_angle += this->_layer_angle(this->layer_id / surface.thickness_layers);
}
out_angle += float(M_PI/2.);
return std::pair<float, Point>(out_angle, out_shift);
}
} // namespace Slic3r

View File

@ -0,0 +1,111 @@
#ifndef slic3r_FillBase_hpp_
#define slic3r_FillBase_hpp_
#include <assert.h>
#include <memory.h>
#include <float.h>
#include <stdint.h>
#include "../libslic3r.h"
#include "../BoundingBox.hpp"
#include "../ExPolygon.hpp"
#include "../Polyline.hpp"
#include "../PrintConfig.hpp"
namespace Slic3r {
class Surface;
struct FillParams
{
public:
FillParams() : density(0), dont_connect(false), dont_adjust(false), complete(false) {};
// Fill density, fraction in <0, 1>
float density;
// Don't connect the fill lines around the inner perimeter.
bool dont_connect;
// Don't adjust spacing to fill the space evenly.
bool dont_adjust;
// For Honeycomb.
// we were requested to complete each loop;
// in this case we don't try to make more continuous paths
bool complete;
};
class Fill
{
public:
// Index of the layer.
size_t layer_id;
// Z coordinate of the top print surface, in unscaled coordinates
coordf_t z;
// in unscaled coordinates
coordf_t spacing;
// in radians, ccw, 0 = East
float angle;
// In scaled coordinates. Maximum lenght of a perimeter segment connecting two infill lines.
// Used by the FillRectilinear2, FillGrid2, FillTriangles, FillStars and FillCubic.
// If left to zero, the links will not be limited.
coord_t link_max_length;
// In scaled coordinates. Used by the concentric infill pattern to clip the loops to create extrusion paths.
coord_t loop_clipping;
// In scaled coordinates. Bounding box of the 2D projection of the object.
BoundingBox bounding_box;
public:
virtual ~Fill() {}
static Fill* new_from_type(const InfillPattern type);
static Fill* new_from_type(const std::string &type);
void set_bounding_box(const BoundingBox &bb) { this->bounding_box = bb; }
// Use bridge flow for the fill?
virtual bool use_bridge_flow() const { return false; }
// Do not sort the fill lines to optimize the print head path?
virtual bool no_sort() const { return false; }
// Perform the fill.
virtual Polylines fill_surface(const Surface &surface, const FillParams &params);
static coord_t adjust_solid_spacing(const coord_t width, const coord_t distance);
protected:
Fill() :
layer_id(size_t(-1)),
z(0.f),
spacing(0.f),
angle(0),
link_max_length(0),
loop_clipping(0)
{};
// The expolygon may be modified by the method to avoid a copy.
virtual void _fill_surface_single(
const FillParams &params,
unsigned int thickness_layers,
const std::pair<float, Point> &direction,
ExPolygon &expolygon,
Polylines* polylines_out) {};
virtual float _layer_angle(size_t idx) const {
return (idx % 2) == 0 ? (M_PI/2.) : 0;
}
std::pair<float, Point> _infill_direction(const Surface &surface) const;
};
} // namespace Slic3r
#endif // slic3r_FillBase_hpp_

View File

@ -0,0 +1,63 @@
#include "../ClipperUtils.hpp"
#include "../ExPolygon.hpp"
#include "../Surface.hpp"
#include "FillConcentric.hpp"
namespace Slic3r {
void
FillConcentric::_fill_surface_single(
const FillParams &params,
unsigned int thickness_layers,
const std::pair<float, Point> &direction,
ExPolygon &expolygon,
Polylines* polylines_out)
{
// no rotation is supported for this infill pattern
const coord_t min_spacing = scale_(this->spacing);
coord_t distance = coord_t(min_spacing / params.density);
if (params.density > 0.9999f && !params.dont_adjust) {
BoundingBox bounding_box = expolygon.contour.bounding_box();
distance = this->adjust_solid_spacing(bounding_box.size().x, distance);
this->spacing = unscale(distance);
}
Polygons loops = (Polygons)expolygon;
Polygons last = loops;
while (!last.empty()) {
last = offset2(last, -(distance + min_spacing/2), +min_spacing/2);
loops.insert(loops.end(), last.begin(), last.end());
}
// generate paths from the outermost to the innermost, to avoid
// adhesion problems of the first central tiny loops
loops = union_pt_chained(loops, false);
// split paths using a nearest neighbor search
size_t iPathFirst = polylines_out->size();
Point last_pos(0, 0);
for (Polygons::const_iterator it_loop = loops.begin(); it_loop != loops.end(); ++ it_loop) {
polylines_out->push_back(it_loop->split_at_index(last_pos.nearest_point_index(*it_loop)));
last_pos = polylines_out->back().last_point();
}
// clip the paths to prevent the extruder from getting exactly on the first point of the loop
// Keep valid paths only.
size_t j = iPathFirst;
for (size_t i = iPathFirst; i < polylines_out->size(); ++ i) {
(*polylines_out)[i].clip_end(this->loop_clipping);
if ((*polylines_out)[i].is_valid()) {
if (j < i)
std::swap((*polylines_out)[j], (*polylines_out)[i]);
++j;
}
}
if (j < polylines_out->size())
polylines_out->erase(polylines_out->begin() + j, polylines_out->end());
// TODO: return ExtrusionLoop objects to get better chained paths
}
} // namespace Slic3r

View File

@ -0,0 +1,26 @@
#ifndef slic3r_FillConcentric_hpp_
#define slic3r_FillConcentric_hpp_
#include "FillBase.hpp"
namespace Slic3r {
class FillConcentric : public Fill
{
public:
virtual ~FillConcentric() {}
protected:
virtual void _fill_surface_single(
const FillParams &params,
unsigned int thickness_layers,
const std::pair<float, Point> &direction,
ExPolygon &expolygon,
Polylines* polylines_out);
virtual bool no_sort() const { return true; }
};
} // namespace Slic3r
#endif // slic3r_FillConcentric_hpp_

View File

@ -0,0 +1,126 @@
#include "FillHoneycomb.hpp"
#include "../ClipperUtils.hpp"
#include "../PolylineCollection.hpp"
#include "../Surface.hpp"
namespace Slic3r {
void
FillHoneycomb::_fill_surface_single(
const FillParams &params,
unsigned int thickness_layers,
const std::pair<float, Point> &direction,
ExPolygon &expolygon,
Polylines* polylines_out)
{
// cache hexagons math
CacheID cache_id = std::make_pair(params.density, this->spacing);
Cache::iterator it_m = this->cache.find(cache_id);
if (it_m == this->cache.end()) {
it_m = this->cache.insert(it_m, std::pair<CacheID,CacheData>(cache_id, CacheData()));
CacheData &m = it_m->second;
coord_t min_spacing = scale_(this->spacing);
m.distance = min_spacing / params.density;
m.hex_side = m.distance / (sqrt(3)/2);
m.hex_width = m.distance * 2; // $m->{hex_width} == $m->{hex_side} * sqrt(3);
coord_t hex_height = m.hex_side * 2;
m.pattern_height = hex_height + m.hex_side;
m.y_short = m.distance * sqrt(3)/3;
m.x_offset = min_spacing / 2;
m.y_offset = m.x_offset * sqrt(3)/3;
m.hex_center = Point(m.hex_width/2, m.hex_side);
}
CacheData &m = it_m->second;
Polygons polygons;
{
// adjust actual bounding box to the nearest multiple of our hex pattern
// and align it so that it matches across layers
BoundingBox bounding_box = expolygon.contour.bounding_box();
{
// rotate bounding box according to infill direction
Polygon bb_polygon = bounding_box.polygon();
bb_polygon.rotate(direction.first, m.hex_center);
bounding_box = bb_polygon.bounding_box();
// extend bounding box so that our pattern will be aligned with other layers
// $bounding_box->[X1] and [Y1] represent the displacement between new bounding box offset and old one
// The infill is not aligned to the object bounding box, but to a world coordinate system. Supposedly good enough.
bounding_box.min.align_to_grid(Point(m.hex_width, m.pattern_height));
}
for (coord_t x = bounding_box.min.x; x <= bounding_box.max.x; ) {
Polygon p;
coord_t ax[2] = { x + m.x_offset, x + m.distance - m.x_offset };
for (size_t i = 0; i < 2; ++ i) {
std::reverse(p.points.begin(), p.points.end()); // turn first half upside down
for (coord_t y = bounding_box.min.y; y <= bounding_box.max.y; y += m.y_short + m.hex_side + m.y_short + m.hex_side) {
p.points.push_back(Point(ax[1], y + m.y_offset));
p.points.push_back(Point(ax[0], y + m.y_short - m.y_offset));
p.points.push_back(Point(ax[0], y + m.y_short + m.hex_side + m.y_offset));
p.points.push_back(Point(ax[1], y + m.y_short + m.hex_side + m.y_short - m.y_offset));
p.points.push_back(Point(ax[1], y + m.y_short + m.hex_side + m.y_short + m.hex_side + m.y_offset));
}
ax[0] = ax[0] + m.distance;
ax[1] = ax[1] + m.distance;
std::swap(ax[0], ax[1]); // draw symmetrical pattern
x += m.distance;
}
p.rotate(-direction.first, m.hex_center);
polygons.push_back(p);
}
}
if (true || params.complete) {
// we were requested to complete each loop;
// in this case we don't try to make more continuous paths
Polygons polygons_trimmed = intersection((Polygons)expolygon, polygons);
for (Polygons::iterator it = polygons_trimmed.begin(); it != polygons_trimmed.end(); ++ it)
polylines_out->push_back(it->split_at_first_point());
} else {
// consider polygons as polylines without re-appending the initial point:
// this cuts the last segment on purpose, so that the jump to the next
// path is more straight
Polylines paths = intersection_pl(
to_polylines(polygons),
(Polygons)expolygon
);
// connect paths
if (!paths.empty()) { // prevent calling leftmost_point() on empty collections
Polylines chained = PolylineCollection::chained_path_from(
STDMOVE(paths),
PolylineCollection::leftmost_point(paths),
false
);
assert(paths.empty());
paths.clear();
for (Polylines::iterator it_path = chained.begin(); it_path != chained.end(); ++ it_path) {
if (!paths.empty()) {
// distance between first point of this path and last point of last path
double distance = paths.back().last_point().distance_to(it_path->first_point());
if (distance <= m.hex_width) {
paths.back().points.insert(paths.back().points.end(), it_path->points.begin(), it_path->points.end());
continue;
}
}
// Don't connect the paths.
paths.push_back(*it_path);
}
}
// clip paths again to prevent connection segments from crossing the expolygon boundaries
paths = intersection_pl(paths, to_polygons(offset_ex(expolygon, SCALED_EPSILON)));
// Move the polylines to the output, avoid a deep copy.
size_t j = polylines_out->size();
polylines_out->resize(j + paths.size(), Polyline());
for (size_t i = 0; i < paths.size(); ++ i)
std::swap((*polylines_out)[j++], paths[i]);
}
}
} // namespace Slic3r

View File

@ -0,0 +1,47 @@
#ifndef slic3r_FillHoneycomb_hpp_
#define slic3r_FillHoneycomb_hpp_
#include <map>
#include "../libslic3r.h"
#include "FillBase.hpp"
namespace Slic3r {
class FillHoneycomb : public Fill
{
public:
virtual ~FillHoneycomb() {}
protected:
virtual void _fill_surface_single(
const FillParams &params,
unsigned int thickness_layers,
const std::pair<float, Point> &direction,
ExPolygon &expolygon,
Polylines* polylines_out
);
// Cache the hexagon math.
struct CacheData
{
coord_t distance;
coord_t hex_side;
coord_t hex_width;
coord_t pattern_height;
coord_t y_short;
coord_t x_offset;
coord_t y_offset;
Point hex_center;
};
typedef std::pair<float,coordf_t> CacheID; // density, spacing
typedef std::map<CacheID, CacheData> Cache;
Cache cache;
virtual float _layer_angle(size_t idx) const { return float(M_PI/3.) * (idx % 3); }
};
} // namespace Slic3r
#endif // slic3r_FillHoneycomb_hpp_

View File

@ -0,0 +1,206 @@
#include "../ClipperUtils.hpp"
#include "../PolylineCollection.hpp"
#include "../Surface.hpp"
#include "FillPlanePath.hpp"
namespace Slic3r {
void FillPlanePath::_fill_surface_single(
const FillParams &params,
unsigned int thickness_layers,
const std::pair<float, Point> &direction,
ExPolygon &expolygon,
Polylines* polylines_out)
{
expolygon.rotate(-direction.first);
const coord_t distance_between_lines = scale_(this->spacing) / params.density;
// align infill across layers using the object's bounding box
// Rotated bounding box of the whole object.
BoundingBox bounding_box = this->bounding_box.rotated(-direction.first);
const Point shift = this->_centered()
? bounding_box.center()
: bounding_box.min;
expolygon.translate(-shift.x, -shift.y);
bounding_box.translate(-shift.x, -shift.y);
const Pointfs pts = this->_generate(
coord_t(ceil(coordf_t(bounding_box.min.x) / distance_between_lines)),
coord_t(ceil(coordf_t(bounding_box.min.y) / distance_between_lines)),
coord_t(ceil(coordf_t(bounding_box.max.x) / distance_between_lines)),
coord_t(ceil(coordf_t(bounding_box.max.y) / distance_between_lines))
);
Polylines polylines;
if (pts.size() >= 2) {
// Convert points to a polyline, upscale.
polylines.push_back(Polyline());
Polyline &polyline = polylines.back();
polyline.points.reserve(pts.size());
for (Pointfs::const_iterator it = pts.begin(); it != pts.end(); ++ it) {
polyline.points.push_back(Point(
coord_t(floor(it->x * distance_between_lines + 0.5)),
coord_t(floor(it->y * distance_between_lines + 0.5))
));
}
// polylines = intersection_pl(polylines_src, offset((Polygons)expolygon, scale_(0.02)));
polylines = intersection_pl(polylines, (Polygons)expolygon);
/*
if (1) {
require "Slic3r/SVG.pm";
print "Writing fill.svg\n";
Slic3r::SVG::output("fill.svg",
no_arrows => 1,
polygons => \@$expolygon,
green_polygons => [ $bounding_box->polygon ],
polylines => [ $polyline ],
red_polylines => \@paths,
);
}
*/
// paths must be repositioned and rotated back
for (Polylines::iterator it = polylines.begin(); it != polylines.end(); ++ it) {
it->translate(shift.x, shift.y);
it->rotate(direction.first);
}
}
// Move the polylines to the output, avoid a deep copy.
size_t j = polylines_out->size();
polylines_out->resize(j + polylines.size(), Polyline());
for (size_t i = 0; i < polylines.size(); ++ i)
std::swap((*polylines_out)[j++], polylines[i]);
}
// Follow an Archimedean spiral, in polar coordinates: r=a+b\theta
Pointfs FillArchimedeanChords::_generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y)
{
// Radius to achieve.
const coordf_t rmax = std::sqrt(coordf_t(max_x)*coordf_t(max_x)+coordf_t(max_y)*coordf_t(max_y)) * std::sqrt(2.) + 1.5;
// Now unwind the spiral.
const coordf_t a = 1.;
const coordf_t b = 1./(2.*M_PI);
coordf_t theta = 0.;
coordf_t r = 1;
Pointfs out;
//FIXME Vojtech: If used as a solid infill, there is a gap left at the center.
out.push_back(Pointf(0, 0));
out.push_back(Pointf(1, 0));
while (r < rmax) {
theta += 1. / r;
r = a + b * theta;
out.push_back(Pointf(r * cos(theta), r * sin(theta)));
}
return out;
}
// Adapted from
// http://cpansearch.perl.org/src/KRYDE/Math-PlanePath-122/lib/Math/PlanePath/HilbertCurve.pm
//
// state=0 3--2 plain
// |
// 0--1
//
// state=4 1--2 transpose
// | |
// 0 3
//
// state=8
//
// state=12 3 0 rot180 + transpose
// | |
// 2--1
//
static inline Point hilbert_n_to_xy(const size_t n)
{
static const int next_state[16] = { 4,0,0,12, 0,4,4,8, 12,8,8,4, 8,12,12,0 };
static const int digit_to_x[16] = { 0,1,1,0, 0,0,1,1, 1,0,0,1, 1,1,0,0 };
static const int digit_to_y[16] = { 0,0,1,1, 0,1,1,0, 1,1,0,0, 1,0,0,1 };
// Number of 2 bit digits.
size_t ndigits = 0;
{
size_t nc = n;
while(nc > 0) {
nc >>= 2;
++ ndigits;
}
}
int state = (ndigits & 1) ? 4 : 0;
int dirstate = (ndigits & 1) ? 0 : 4;
coord_t x = 0;
coord_t y = 0;
for (int i = (int)ndigits - 1; i >= 0; -- i) {
int digit = (n >> (i * 2)) & 3;
state += digit;
if (digit != 3)
dirstate = state; // lowest non-3 digit
x |= digit_to_x[state] << i;
y |= digit_to_y[state] << i;
state = next_state[state];
}
return Point(x, y);
}
Pointfs FillHilbertCurve::_generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y)
{
// Minimum power of two square to fit the domain.
size_t sz = 2;
size_t pw = 1;
{
size_t sz0 = std::max(max_x + 1 - min_x, max_y + 1 - min_y);
while (sz < sz0) {
sz = sz << 1;
++pw;
}
}
const size_t sz2 = sz * sz;
Pointfs line;
line.reserve(sz2);
for (size_t i = 0; i < sz2; ++ i) {
Point p = hilbert_n_to_xy(i);
line.push_back(Pointf(p.x + min_x, p.y + min_y));
}
return line;
}
Pointfs FillOctagramSpiral::_generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y)
{
// Radius to achieve.
const coordf_t rmax = std::sqrt(coordf_t(max_x)*coordf_t(max_x)+coordf_t(max_y)*coordf_t(max_y)) * std::sqrt(2.) + 1.5;
// Now unwind the spiral.
coordf_t r = 0;
const coordf_t r_inc = sqrt(2.);
Pointfs out;
out.push_back(Pointf(0, 0));
while (r < rmax) {
r += r_inc;
coordf_t rx = r / sqrt(2.);
coordf_t r2 = r + rx;
out.push_back(Pointf( r, 0.));
out.push_back(Pointf( r2, rx));
out.push_back(Pointf( rx, rx));
out.push_back(Pointf( rx, r2));
out.push_back(Pointf(0., r));
out.push_back(Pointf(-rx, r2));
out.push_back(Pointf(-rx, rx));
out.push_back(Pointf(-r2, rx));
out.push_back(Pointf(-r, 0.));
out.push_back(Pointf(-r2, -rx));
out.push_back(Pointf(-rx, -rx));
out.push_back(Pointf(-rx, -r2));
out.push_back(Pointf(0., -r));
out.push_back(Pointf( rx, -r2));
out.push_back(Pointf( rx, -rx));
out.push_back(Pointf( r2+r_inc, -rx));
}
return out;
}
} // namespace Slic3r

View File

@ -0,0 +1,66 @@
#ifndef slic3r_FillPlanePath_hpp_
#define slic3r_FillPlanePath_hpp_
#include <map>
#include "../libslic3r.h"
#include "FillBase.hpp"
namespace Slic3r {
// The original Perl code used path generators from Math::PlanePath library:
// http://user42.tuxfamily.org/math-planepath/
// http://user42.tuxfamily.org/math-planepath/gallery.html
class FillPlanePath : public Fill
{
public:
virtual ~FillPlanePath() {}
protected:
virtual void _fill_surface_single(
const FillParams &params,
unsigned int thickness_layers,
const std::pair<float, Point> &direction,
ExPolygon &expolygon,
Polylines* polylines_out);
virtual float _layer_angle(size_t idx) const { return 0.f; }
virtual bool _centered() const = 0;
virtual Pointfs _generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y) = 0;
};
class FillArchimedeanChords : public FillPlanePath
{
public:
virtual ~FillArchimedeanChords() {}
protected:
virtual bool _centered() const { return true; }
virtual Pointfs _generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y);
};
class FillHilbertCurve : public FillPlanePath
{
public:
virtual ~FillHilbertCurve() {}
protected:
virtual bool _centered() const { return false; }
virtual Pointfs _generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y);
};
class FillOctagramSpiral : public FillPlanePath
{
public:
virtual ~FillOctagramSpiral() {}
protected:
virtual bool _centered() const { return true; }
virtual Pointfs _generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y);
};
} // namespace Slic3r
#endif // slic3r_FillPlanePath_hpp_

View File

@ -0,0 +1,138 @@
#include "../ClipperUtils.hpp"
#include "../ExPolygon.hpp"
#include "../PolylineCollection.hpp"
#include "../Surface.hpp"
#include "FillRectilinear.hpp"
namespace Slic3r {
void FillRectilinear::_fill_surface_single(
const FillParams &params,
unsigned int thickness_layers,
const std::pair<float, Point> &direction,
ExPolygon &expolygon,
Polylines* polylines_out)
{
assert(params.density > 0.0001f && params.density <= 1.f);
// rotate polygons so that we can work with vertical lines here
expolygon.rotate(-direction.first);
this->_min_spacing = scale_(this->spacing);
this->_line_spacing = coord_t(coordf_t(this->_min_spacing) / params.density);
this->_diagonal_distance = this->_line_spacing * 2;
this->_line_oscillation = this->_line_spacing - this->_min_spacing; // only for Line infill
BoundingBox bounding_box = expolygon.contour.bounding_box();
// define flow spacing according to requested density
if (params.density > 0.9999f && !params.dont_adjust) {
this->_line_spacing = this->adjust_solid_spacing(bounding_box.size().x, this->_line_spacing);
this->spacing = unscale(this->_line_spacing);
} else {
// extend bounding box so that our pattern will be aligned with other layers
// Transform the reference point to the rotated coordinate system.
bounding_box.min.align_to_grid(
Point(this->_line_spacing, this->_line_spacing),
direction.second.rotated(-direction.first)
);
}
// generate the basic pattern
const coord_t x_max = bounding_box.max.x + SCALED_EPSILON;
Lines lines;
for (coord_t x = bounding_box.min.x; x <= x_max; x += this->_line_spacing)
lines.push_back(this->_line(lines.size(), x, bounding_box.min.y, bounding_box.max.y));
if (this->_horizontal_lines()) {
const coord_t y_max = bounding_box.max.y + SCALED_EPSILON;
for (coord_t y = bounding_box.min.y; y <= y_max; y += this->_line_spacing)
lines.push_back(Line(Point(bounding_box.min.x, y), Point(bounding_box.max.x, y)));
}
// clip paths against a slightly larger expolygon, so that the first and last paths
// are kept even if the expolygon has vertical sides
// the minimum offset for preventing edge lines from being clipped is SCALED_EPSILON;
// however we use a larger offset to support expolygons with slightly skewed sides and
// not perfectly straight
Polylines polylines = intersection_pl(
to_polylines(lines),
offset(expolygon, scale_(0.02)),
false
);
// FIXME Vojtech: This is only performed for horizontal lines, not for the vertical lines!
const float INFILL_OVERLAP_OVER_SPACING = 0.3f;
// How much to extend an infill path from expolygon outside?
const coord_t extra = coord_t(floor(this->_min_spacing * INFILL_OVERLAP_OVER_SPACING + 0.5f));
for (Polylines::iterator it_polyline = polylines.begin();
it_polyline != polylines.end(); ++ it_polyline) {
Point *first_point = &it_polyline->points.front();
Point *last_point = &it_polyline->points.back();
if (first_point->y > last_point->y)
std::swap(first_point, last_point);
first_point->y -= extra;
last_point->y += extra;
}
size_t n_polylines_out_old = polylines_out->size();
// connect lines
if (!params.dont_connect && !polylines.empty()) { // prevent calling leftmost_point() on empty collections
// offset the expolygon by max(min_spacing/2, extra)
ExPolygon expolygon_off;
{
ExPolygons expolygons_off = offset_ex(expolygon, this->_min_spacing/2);
if (!expolygons_off.empty()) {
// When expanding a polygon, the number of islands could only shrink.
// Therefore the offset_ex shall generate exactly one expanded island
// for one input island.
assert(expolygons_off.size() == 1);
std::swap(expolygon_off, expolygons_off.front());
}
}
Polylines chained = PolylineCollection::chained_path_from(
STDMOVE(polylines),
PolylineCollection::leftmost_point(polylines),
false // reverse allowed
);
bool first = true;
for (Polylines::iterator it_polyline = chained.begin(); it_polyline != chained.end(); ++ it_polyline) {
if (!first) {
// Try to connect the lines.
Points &pts_end = polylines_out->back().points;
const Point &first_point = it_polyline->points.front();
const Point &last_point = pts_end.back();
// Distance in X, Y.
const Vector distance = first_point.vector_to(last_point);
// TODO: we should also check that both points are on a fill_boundary to avoid
// connecting paths on the boundaries of internal regions
if (this->_can_connect(std::abs(distance.x), std::abs(distance.y))
&& expolygon_off.contains(Line(last_point, first_point))) {
// Append the polyline.
append_to(pts_end, it_polyline->points);
continue;
}
}
// The lines cannot be connected.
#if SLIC3R_CPPVER >= 11
polylines_out->push_back(std::move(*it_polyline));
#else
polylines_out->push_back(Polyline());
std::swap(polylines_out->back(), *it_polyline);
#endif
first = false;
}
}
// paths must be rotated back
for (Polylines::iterator it = polylines_out->begin() + n_polylines_out_old;
it != polylines_out->end(); ++ it) {
// No need to translate, the absolute position is irrelevant.
// it->translate(- direction.second.x, - direction.second.y);
it->rotate(direction.first);
}
}
} // namespace Slic3r

View File

@ -0,0 +1,86 @@
#ifndef slic3r_FillRectilinear_hpp_
#define slic3r_FillRectilinear_hpp_
#include "../libslic3r.h"
#include "FillBase.hpp"
namespace Slic3r {
class FillRectilinear : public Fill
{
public:
virtual ~FillRectilinear() {}
protected:
virtual void _fill_surface_single(
const FillParams &params,
unsigned int thickness_layers,
const std::pair<float, Point> &direction,
ExPolygon &expolygon,
Polylines* polylines_out);
coord_t _min_spacing;
coord_t _line_spacing;
// distance threshold for allowing the horizontal infill lines to be connected into a continuous path
coord_t _diagonal_distance;
// only for line infill
coord_t _line_oscillation;
// Enabled for the grid infill, disabled for the rectilinear and line infill.
virtual bool _horizontal_lines() const { return false; };
virtual Line _line(int i, coord_t x, coord_t y_min, coord_t y_max) const
{ return Line(Point(x, y_min), Point(x, y_max)); };
virtual bool _can_connect(coord_t dist_X, coord_t dist_Y) {
return dist_X <= this->_diagonal_distance
&& dist_Y <= this->_diagonal_distance;
};
};
class FillLine : public FillRectilinear
{
public:
virtual ~FillLine() {}
protected:
virtual Line _line(int i, coord_t x, coord_t y_min, coord_t y_max) const {
coord_t osc = (i & 1) ? this->_line_oscillation : 0;
return Line(Point(x - osc, y_min), Point(x + osc, y_max));
};
virtual bool _can_connect(coord_t dist_X, coord_t dist_Y)
{
coord_t TOLERANCE = 10 * SCALED_EPSILON;
return (dist_X >= (this->_line_spacing - this->_line_oscillation) - TOLERANCE)
&& (dist_X <= (this->_line_spacing + this->_line_oscillation) + TOLERANCE)
&& (dist_Y <= this->_diagonal_distance);
};
};
class FillGrid : public FillRectilinear
{
public:
virtual ~FillGrid() {}
protected:
// The grid fill will keep the angle constant between the layers,; see the implementation of Slic3r::Fill.
virtual float _layer_angle(size_t idx) const { return 0.f; }
// Flag for Slic3r::Fill::Rectilinear to fill both directions.
virtual bool _horizontal_lines() const { return true; };
};
class FillAlignedRectilinear : public FillRectilinear
{
public:
virtual ~FillAlignedRectilinear() {};
protected:
// Keep the angle constant in all layers.
virtual float _layer_angle(size_t idx) const { printf("ALIGNED\n"); return 0.f; };
};
}; // namespace Slic3r
#endif // slic3r_FillRectilinear_hpp_

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,76 @@
#ifndef slic3r_FillRectilinear2_hpp_
#define slic3r_FillRectilinear2_hpp_
#include "../libslic3r.h"
#include "FillBase.hpp"
namespace Slic3r {
class Surface;
class FillRectilinear2 : public Fill
{
public:
virtual ~FillRectilinear2() {}
virtual Polylines fill_surface(const Surface &surface, const FillParams &params);
protected:
bool fill_surface_by_lines(const Surface *surface, const FillParams &params, float angleBase, float pattern_shift, Polylines &polylines_out);
};
class FillGrid2 : public FillRectilinear2
{
public:
virtual ~FillGrid2() {}
virtual Polylines fill_surface(const Surface &surface, const FillParams &params);
protected:
// The grid fill will keep the angle constant between the layers, see the implementation of Slic3r::Fill.
virtual float _layer_angle(size_t idx) const { return 0.f; }
};
class FillTriangles : public FillRectilinear2
{
public:
virtual ~FillTriangles() {}
virtual Polylines fill_surface(const Surface &surface, const FillParams &params);
protected:
// The grid fill will keep the angle constant between the layers, see the implementation of Slic3r::Fill.
virtual float _layer_angle(size_t idx) const { return 0.f; }
};
class FillStars : public FillRectilinear2
{
public:
virtual ~FillStars() {}
virtual Polylines fill_surface(const Surface &surface, const FillParams &params);
protected:
// The grid fill will keep the angle constant between the layers, see the implementation of Slic3r::Fill.
virtual float _layer_angle(size_t idx) const { return 0.f; }
};
class FillCubic : public FillRectilinear2
{
public:
virtual ~FillCubic() {}
virtual Polylines fill_surface(const Surface &surface, const FillParams &params);
protected:
// The grid fill will keep the angle constant between the layers, see the implementation of Slic3r::Fill.
virtual float _layer_angle(size_t idx) const { return 0.f; }
};
// Remove sticks (tentacles with zero area) from the polygon.
extern bool remove_sticks(Polygon &poly);
extern bool remove_sticks(Polygons &polys);
extern bool remove_sticks(ExPolygon &poly);
extern bool remove_small(Polygons &polys, double min_area);
}; // namespace Slic3r
#endif // slic3r_FillRectilinear2_hpp_

View File

@ -2,7 +2,7 @@
#include "ClipperUtils.hpp"
#include "Geometry.hpp"
#include "Print.hpp"
#include "Fill/Fill.hpp"
namespace Slic3r {
@ -47,18 +47,6 @@ Layer::set_id(size_t id)
this->_id = id;
}
PrintObject*
Layer::object()
{
return this->_object;
}
const PrintObject*
Layer::object() const
{
return this->_object;
}
size_t
Layer::region_count() const
@ -249,16 +237,23 @@ Layer::make_perimeters()
}
}
SupportLayer::SupportLayer(size_t id, PrintObject *object, coordf_t height,
coordf_t print_z, coordf_t slice_z)
: Layer(id, object, height, print_z, slice_z)
void
Layer::make_fills()
{
#ifdef SLIC3R_DEBUG
printf("Making fills for layer %zu\n", this->id());
#endif
FOREACH_LAYERREGION(this, it_layerm) {
LayerRegion &layerm = **it_layerm;
layerm.fills.clear();
make_fill(layerm, &layerm.fills);
#ifndef NDEBUG
for (size_t i = 0; i < layerm.fills.entities.size(); ++i)
assert(dynamic_cast<ExtrusionEntityCollection*>(layerm.fills.entities[i]) != NULL);
#endif
}
}
SupportLayer::~SupportLayer()
{
}
}

View File

@ -25,8 +25,10 @@ class LayerRegion
friend class Layer;
public:
Layer* layer();
PrintRegion* region();
Layer* layer() { return this->_layer; };
const Layer* layer() const { return this->_layer; };
PrintRegion* region() { return this->_region; };
const PrintRegion* region() const { return this->_region; };
// collection of surfaces generated by slicing the original geometry
// divided by type top/bottom/internal
@ -64,8 +66,9 @@ class LayerRegion
Layer *_layer;
PrintRegion *_region;
LayerRegion(Layer *layer, PrintRegion *region);
~LayerRegion();
LayerRegion(Layer *layer, PrintRegion *region)
: _layer(layer), _region(region) {};
~LayerRegion() {};
};
@ -77,8 +80,8 @@ class Layer {
public:
size_t id() const;
void set_id(size_t id);
PrintObject* object();
const PrintObject* object() const;
PrintObject* object() { return this->_object; };
const PrintObject* object() const { return this->_object; };
Layer *upper_layer;
Layer *lower_layer;
@ -102,10 +105,11 @@ class Layer {
template <class T> bool any_internal_region_slice_contains(const T &item) const;
template <class T> bool any_bottom_region_slice_contains(const T &item) const;
void make_perimeters();
void make_fills();
protected:
size_t _id; // sequential number of layer, 0-based
PrintObject *_object;
PrintObject* _object;
Layer(size_t id, PrintObject *object, coordf_t height, coordf_t print_z,
@ -126,9 +130,10 @@ class SupportLayer : public Layer {
ExtrusionEntityCollection support_interface_fills;
protected:
SupportLayer(size_t id, PrintObject *object, coordf_t height, coordf_t print_z,
coordf_t slice_z);
virtual ~SupportLayer();
SupportLayer(size_t id, PrintObject *object, coordf_t height,
coordf_t print_z, coordf_t slice_z)
: Layer(id, object, height, print_z, slice_z) {};
virtual ~SupportLayer() {};
};

View File

@ -7,28 +7,6 @@
namespace Slic3r {
LayerRegion::LayerRegion(Layer *layer, PrintRegion *region)
: _layer(layer),
_region(region)
{
}
LayerRegion::~LayerRegion()
{
}
Layer*
LayerRegion::layer()
{
return this->_layer;
}
PrintRegion*
LayerRegion::region()
{
return this->_region;
}
Flow
LayerRegion::flow(FlowRole role, bool bridge, double width) const
{
@ -167,18 +145,16 @@ LayerRegion::process_external_surfaces(const Layer* lower_layer)
{
// merge top and bottom in a single collection
SurfaceCollection tb = top;
tb.surfaces.insert(tb.surfaces.end(), bottom.surfaces.begin(), bottom.surfaces.end());
tb.append(bottom);
// group surfaces
std::vector<SurfacesPtr> groups;
std::vector<SurfacesConstPtr> groups;
tb.group(&groups);
for (std::vector<SurfacesPtr>::const_iterator g = groups.begin(); g != groups.end(); ++g) {
for (std::vector<SurfacesConstPtr>::const_iterator g = groups.begin(); g != groups.end(); ++g) {
Polygons subject;
for (SurfacesPtr::const_iterator s = g->begin(); s != g->end(); ++s) {
Polygons pp = **s;
subject.insert(subject.end(), pp.begin(), pp.end());
}
for (SurfacesConstPtr::const_iterator s = g->begin(); s != g->end(); ++s)
append_to(subject, (Polygons)**s);
ExPolygons expp = intersection_ex(
subject,
@ -203,15 +179,13 @@ LayerRegion::process_external_surfaces(const Layer* lower_layer)
}
// group surfaces
std::vector<SurfacesPtr> groups;
std::vector<SurfacesConstPtr> groups;
other.group(&groups);
for (std::vector<SurfacesPtr>::const_iterator g = groups.begin(); g != groups.end(); ++g) {
for (std::vector<SurfacesConstPtr>::const_iterator g = groups.begin(); g != groups.end(); ++g) {
Polygons subject;
for (SurfacesPtr::const_iterator s = g->begin(); s != g->end(); ++s) {
Polygons pp = **s;
subject.insert(subject.end(), pp.begin(), pp.end());
}
for (SurfacesConstPtr::const_iterator s = g->begin(); s != g->end(); ++s)
append_to(subject, (Polygons)**s);
ExPolygons expp = diff_ex(
subject,

View File

@ -33,22 +33,26 @@ MultiPoint::translate(const Point &vector)
void
MultiPoint::rotate(double angle)
{
double s = sin(angle);
double c = cos(angle);
double s = sin(angle);
double c = cos(angle);
for (Points::iterator it = points.begin(); it != points.end(); ++it) {
(*it).rotate(angle);
double cur_x = (double)it->x;
double cur_y = (double)it->y;
it->x = (coord_t)round(c * cur_x - s * cur_y);
it->y = (coord_t)round(c * cur_y + s * cur_x);
double cur_x = (double)it->x;
double cur_y = (double)it->y;
it->x = (coord_t)round(c * cur_x - s * cur_y);
it->y = (coord_t)round(c * cur_y + s * cur_x);
}
}
void
MultiPoint::rotate(double angle, const Point &center)
{
double s = sin(angle);
double c = cos(angle);
for (Points::iterator it = points.begin(); it != points.end(); ++it) {
(*it).rotate(angle, center);
double dx = double(it->x - center.x);
double dy = double(it->y - center.y);
it->x = (coord_t)round(double(center.x) + c * dx - s * dy);
it->y = (coord_t)round(double(center.y) + c * dy + s * dx);
}
}
@ -97,15 +101,33 @@ MultiPoint::bounding_box() const
return BoundingBox(this->points);
}
void
bool
MultiPoint::has_duplicate_points() const
{
for (size_t i = 1; i < points.size(); ++i)
if (points[i-1].coincides_with(points[i]))
return true;
return false;
}
bool
MultiPoint::remove_duplicate_points()
{
for (size_t i = 1; i < this->points.size(); ++i) {
if (this->points.at(i).coincides_with(this->points.at(i-1))) {
this->points.erase(this->points.begin() + i);
--i;
size_t j = 0;
for (size_t i = 1; i < points.size(); ++i) {
if (points[j].coincides_with(points[i])) {
// Just increase index i.
} else {
++ j;
if (j < i)
points[j] = points[i];
}
}
if (++ j < points.size()) {
points.erase(points.begin() + j, points.end());
return true;
}
return false;
}
void

View File

@ -34,7 +34,13 @@ class MultiPoint
int find_point(const Point &point) const;
bool has_boundary_point(const Point &point) const;
BoundingBox bounding_box() const;
void remove_duplicate_points();
// Return true if there are exact duplicates.
bool has_duplicate_points() const;
// Remove exact duplicates, return true if any duplicate has been removed.
bool remove_duplicate_points();
void append(const Point &point);
void append(const Points &points);
void append(const Points::const_iterator &begin, const Points::const_iterator &end);

View File

@ -72,8 +72,10 @@ Point::rotate(double angle, const Point &center)
double cur_y = (double)this->y;
double s = sin(angle);
double c = cos(angle);
this->x = (coord_t)round( (double)center.x + c * (cur_x - (double)center.x) - s * (cur_y - (double)center.y) );
this->y = (coord_t)round( (double)center.y + c * (cur_y - (double)center.y) + s * (cur_x - (double)center.x) );
double dx = cur_x - (double)center.x;
double dy = cur_y - (double)center.y;
this->x = (coord_t)round( (double)center.x + c * dx - s * dy );
this->y = (coord_t)round( (double)center.y + c * dy + s * dx );
}
bool
@ -302,6 +304,27 @@ Point::vector_to(const Point &point) const
return Vector(point.x - this->x, point.y - this->y);
}
// Align a coordinate to a grid. The coordinate may be negative,
// the aligned value will never be bigger than the original one.
static coord_t
_align_to_grid(const coord_t coord, const coord_t spacing) {
// Current C++ standard defines the result of integer division to be rounded to zero,
// for both positive and negative numbers. Here we want to round down for negative
// numbers as well.
coord_t aligned = (coord < 0) ?
((coord - spacing + 1) / spacing) * spacing :
(coord / spacing) * spacing;
assert(aligned <= coord);
return aligned;
}
void
Point::align_to_grid(const Point &spacing, const Point &base)
{
this->x = base.x + _align_to_grid(this->x - base.x, spacing.x);
this->y = base.y + _align_to_grid(this->y - base.y, spacing.y);
}
Point
operator+(const Point& point1, const Point& point2)
{

View File

@ -2,10 +2,10 @@
#define slic3r_Point_hpp_
#include "libslic3r.h"
#include <vector>
#include <math.h>
#include <string>
#include <sstream>
#include <string>
#include <vector>
namespace Slic3r {
@ -44,6 +44,16 @@ class Point
void translate(const Vector &vector);
void rotate(double angle);
void rotate(double angle, const Point &center);
Point rotated(double angle) const {
Point p(*this);
p.rotate(angle);
return p;
}
Point rotated(double angle, const Point &center) const {
Point p(*this);
p.rotate(angle, center);
return p;
}
bool coincides_with(const Point &point) const { return this->x == point.x && this->y == point.y; }
bool coincides_with_epsilon(const Point &point) const;
int nearest_point_index(const Points &points) const;
@ -62,6 +72,7 @@ class Point
Point projection_onto(const Line &line) const;
Point negative() const;
Vector vector_to(const Point &point) const;
void align_to_grid(const Point &spacing, const Point &base = Point(0,0));
};
Point operator+(const Point& point1, const Point& point2);
@ -116,6 +127,16 @@ class Pointf3 : public Pointf
Vectorf3 vector_to(const Pointf3 &point) const;
};
template <class T>
inline Points
to_points(const std::vector<T> &items)
{
Points pp;
for (typename std::vector<T>::const_iterator it = items.begin(); it != items.end(); ++it)
append_to(pp, (Points)*it);
return pp;
}
}
// start Boost

View File

@ -6,11 +6,11 @@
#include <string>
#include "Line.hpp"
#include "MultiPoint.hpp"
#include "Polyline.hpp"
namespace Slic3r {
class Polygon;
class Polyline;
typedef std::vector<Polygon> Polygons;
class Polygon : public MultiPoint {

View File

@ -4,6 +4,7 @@
#include "libslic3r.h"
#include "Line.hpp"
#include "MultiPoint.hpp"
#include "Polygon.hpp"
#include <string>
#include <vector>
@ -42,6 +43,24 @@ class ThickPolyline : public Polyline {
void reverse();
};
inline Polylines
to_polylines(const Polygons &polygons)
{
Polylines pp;
for (Polygons::const_iterator it = polygons.begin(); it != polygons.end(); ++it)
pp.push_back((Polyline)*it);
return pp;
}
inline Polylines
to_polylines(const Lines &lines)
{
Polylines pp;
for (Lines::const_iterator it = lines.begin(); it != lines.end(); ++it)
pp.push_back((Polyline)*it);
return pp;
}
}
#endif

View File

@ -10,9 +10,8 @@ struct Chaining
};
#ifndef sqr
template<typename T>
inline sqr(T x) { return x * x; }
#endif /* sqr */
#define sqr(x) (x * x);
#endif
template<typename T>
inline int nearest_point_index(const std::vector<Chaining> &pairs, const Point &start_near, bool no_reverse)
@ -43,18 +42,17 @@ inline int nearest_point_index(const std::vector<Chaining> &pairs, const Point &
}
}
}
return idx;
}
Polylines PolylineCollection::chained_path_from(
#if SLIC3R_CPPVER > 11
Polylines &&src,
#else
Polylines PolylineCollection::_chained_path_from(
const Polylines &src,
Point start_near,
bool no_reverse
#if SLIC3R_CPPVER >= 11
, bool move_from_src
#endif
Point start_near,
bool no_reverse)
)
{
std::vector<Chaining> endpoints;
endpoints.reserve(src.size());
@ -64,14 +62,19 @@ Polylines PolylineCollection::chained_path_from(
if (! no_reverse)
c.last = src[i].last_point();
c.idx = i;
endpoints.push_back(c);
}
Polylines retval;
while (! endpoints.empty()) {
// find nearest point
int endpoint_index = nearest_point_index<double>(endpoints, start_near, no_reverse);
assert(endpoint_index >= 0 && endpoint_index < endpoints.size() * 2);
#if SLIC3R_CPPVER > 11
retval.push_back(std::move(src[endpoints[endpoint_index/2].idx]));
if (move_from_src) {
retval.push_back(std::move(src[endpoints[endpoint_index/2].idx]));
} else {
retval.push_back(src[endpoints[endpoint_index/2].idx]);
}
#else
retval.push_back(src[endpoints[endpoint_index/2].idx]);
#endif
@ -80,33 +83,42 @@ Polylines PolylineCollection::chained_path_from(
endpoints.erase(endpoints.begin() + endpoint_index/2);
start_near = retval.back().last_point();
}
return retval;
}
#if SLIC3R_CPPVER > 11
#if SLIC3R_CPPVER >= 11
Polylines PolylineCollection::chained_path(Polylines &&src, bool no_reverse)
{
return (src.empty() || src.front().empty()) ?
return (src.empty() || src.front().points.empty()) ?
Polylines() :
chained_path_from(std::move(src), src.front().first_point(), no_reverse);
_chained_path_from(src, src.front().first_point(), no_reverse, true);
}
Polylines PolylineCollection::chained_path_from(Polylines src, Point start_near, bool no_reverse)
Polylines PolylineCollection::chained_path_from(Polylines &&src, Point start_near, bool no_reverse)
{
return chained_path_from(std::move(src), start_near, no_reverse);
return _chained_path_from(src, start_near, no_reverse, true);
}
Polylines PolylineCollection::chained_path(Polylines src, bool no_reverse)
{
return (src.empty() || src.front().empty()) ?
Polylines() :
chained_path_from(std::move(src), src.front().first_point(), no_reverse);
}
#else
#endif
Polylines PolylineCollection::chained_path(const Polylines &src, bool no_reverse)
{
return (src.empty() || src.front().points.empty()) ?
Polylines() :
chained_path_from(src, src.front().first_point(), no_reverse);
}
_chained_path_from(src, src.front().first_point(), no_reverse
#if SLIC3R_CPPVER >= 11
, false
#endif
);
}
Polylines PolylineCollection::chained_path_from(const Polylines &src, Point start_near, bool no_reverse)
{
return _chained_path_from(src, start_near, no_reverse
#if SLIC3R_CPPVER >= 11
, false
#endif
);
}
Point PolylineCollection::leftmost_point(const Polylines &polylines)
{

View File

@ -8,26 +8,32 @@ namespace Slic3r {
class PolylineCollection
{
static Polylines _chained_path_from(
const Polylines &src,
Point start_near,
bool no_reverse
#if SLIC3R_CPPVER >= 11
, bool move_from_src
#endif
);
public:
Polylines polylines;
void chained_path(PolylineCollection* retval, bool no_reverse = false) const
{ retval->polylines = chained_path(this->polylines, no_reverse); }
void chained_path_from(Point start_near, PolylineCollection* retval, bool no_reverse = false) const
void chained_path_from(Point start_near, PolylineCollection* retval, bool no_reverse = false) const
{ retval->polylines = chained_path_from(this->polylines, start_near, no_reverse); }
Point leftmost_point() const
{ return leftmost_point(polylines); }
void append(const Polylines &polylines);
static Point leftmost_point(const Polylines &polylines);
#if SLIC3R_CPPVER > 11
#if SLIC3R_CPPVER >= 11
static Polylines chained_path(Polylines &&src, bool no_reverse = false);
static Polylines chained_path_from(Polylines &&src, Point start_near, bool no_reverse = false);
static Polylines chained_path(Polylines src, bool no_reverse = false);
static Polylines chained_path_from(Polylines src, Point start_near, bool no_reverse = false);
#else
static Polylines chained_path(const Polylines &src, bool no_reverse = false);
static Polylines chained_path_from(const Polylines &src, Point start_near, bool no_reverse = false);
#endif
static Polylines chained_path(const Polylines &src, bool no_reverse = false);
static Polylines chained_path_from(const Polylines &src, Point start_near, bool no_reverse = false);
};
}

View File

@ -368,6 +368,11 @@ PrintConfigDef::PrintConfigDef()
def->enum_values.push_back("alignedrectilinear");
def->enum_values.push_back("grid");
def->enum_values.push_back("line");
def->enum_values.push_back("rectilinear2");
def->enum_values.push_back("grid2");
def->enum_values.push_back("triangles");
def->enum_values.push_back("stars");
def->enum_values.push_back("cubic");
def->enum_values.push_back("concentric");
def->enum_values.push_back("honeycomb");
def->enum_values.push_back("3dhoneycomb");
@ -375,9 +380,14 @@ PrintConfigDef::PrintConfigDef()
def->enum_values.push_back("archimedeanchords");
def->enum_values.push_back("octagramspiral");
def->enum_labels.push_back("Rectilinear");
def->enum_labels.push_back("AlignedRectilinear");
def->enum_labels.push_back("Aligned Rectilinear");
def->enum_labels.push_back("Grid");
def->enum_labels.push_back("Line");
def->enum_labels.push_back("Rectilinear 2");
def->enum_labels.push_back("Grid 2");
def->enum_labels.push_back("Triangles");
def->enum_labels.push_back("Stars");
def->enum_labels.push_back("Cubic");
def->enum_labels.push_back("Concentric");
def->enum_labels.push_back("Honeycomb");
def->enum_labels.push_back("3D Honeycomb");

View File

@ -30,7 +30,9 @@ enum GCodeFlavor {
};
enum InfillPattern {
ipRectilinear, ipAlignedRectilinear, ipGrid, ipLine, ipConcentric, ipHoneycomb, ip3DHoneycomb,
ipRectilinear, ipGrid, ipLine, ipAlignedRectilinear,
ipRectilinear2, ipGrid2, ipTriangles, ipStars, ipCubic,
ipConcentric, ipHoneycomb, ip3DHoneycomb,
ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral,
};
@ -62,6 +64,11 @@ template<> inline t_config_enum_values ConfigOptionEnum<InfillPattern>::get_enum
keys_map["alignedrectilinear"] = ipAlignedRectilinear;
keys_map["grid"] = ipGrid;
keys_map["line"] = ipLine;
keys_map["rectilinear2"] = ipRectilinear2;
keys_map["grid2"] = ipGrid2;
keys_map["triangles"] = ipTriangles;
keys_map["stars"] = ipStars;
keys_map["cubic"] = ipCubic;
keys_map["concentric"] = ipConcentric;
keys_map["honeycomb"] = ipHoneycomb;
keys_map["3dhoneycomb"] = ip3DHoneycomb;

View File

@ -54,15 +54,4 @@ Surface::is_bridge() const
|| this->surface_type == stInternalBridge;
}
Polygons
to_polygons(const Surfaces &surfaces)
{
Slic3r::Polygons pp;
for (Surfaces::const_iterator s = surfaces.begin(); s != surfaces.end(); ++s) {
Slic3r::Polygons ppp = *s;
pp.insert(pp.end(), ppp.begin(), ppp.end());
}
return pp;
}
}

View File

@ -33,8 +33,34 @@ class Surface
typedef std::vector<Surface> Surfaces;
typedef std::vector<Surface*> SurfacesPtr;
typedef std::vector<const Surface*> SurfacesConstPtr;
Polygons to_polygons(const Surfaces &surfaces);
inline Polygons
to_polygons(const Surfaces &surfaces)
{
Slic3r::Polygons pp;
for (Surfaces::const_iterator s = surfaces.begin(); s != surfaces.end(); ++s)
append_to(pp, (Polygons)*s);
return pp;
}
inline Polygons
to_polygons(const SurfacesPtr &surfaces)
{
Slic3r::Polygons pp;
for (SurfacesPtr::const_iterator s = surfaces.begin(); s != surfaces.end(); ++s)
append_to(pp, (Polygons)**s);
return pp;
}
inline Polygons
to_polygons(const SurfacesConstPtr &surfaces)
{
Slic3r::Polygons pp;
for (SurfacesConstPtr::const_iterator s = surfaces.begin(); s != surfaces.end(); ++s)
append_to(pp, (Polygons)**s);
return pp;
}
}

View File

@ -41,13 +41,13 @@ SurfaceCollection::simplify(double tolerance)
/* group surfaces by common properties */
void
SurfaceCollection::group(std::vector<SurfacesPtr> *retval)
SurfaceCollection::group(std::vector<SurfacesConstPtr> *retval) const
{
for (Surfaces::iterator it = this->surfaces.begin(); it != this->surfaces.end(); ++it) {
for (Surfaces::const_iterator it = this->surfaces.begin(); it != this->surfaces.end(); ++it) {
// find a group with the same properties
SurfacesPtr* group = NULL;
for (std::vector<SurfacesPtr>::iterator git = retval->begin(); git != retval->end(); ++git) {
Surface* gkey = git->front();
SurfacesConstPtr* group = NULL;
for (std::vector<SurfacesConstPtr>::iterator git = retval->begin(); git != retval->end(); ++git) {
const Surface* gkey = git->front();
if ( gkey->surface_type == it->surface_type
&& gkey->thickness == it->thickness
&& gkey->thickness_layers == it->thickness_layers
@ -104,17 +104,48 @@ void
SurfaceCollection::filter_by_type(SurfaceType type, Polygons* polygons)
{
for (Surfaces::iterator surface = this->surfaces.begin(); surface != this->surfaces.end(); ++surface) {
if (surface->surface_type == type) {
Polygons pp = surface->expolygon;
polygons->insert(polygons->end(), pp.begin(), pp.end());
}
if (surface->surface_type == type)
append_to(*polygons, (Polygons)surface->expolygon);
}
}
void
SurfaceCollection::append(const SurfaceCollection &coll)
{
this->surfaces.insert(this->surfaces.end(), coll.surfaces.begin(), coll.surfaces.end());
this->append(coll.surfaces);
}
void
SurfaceCollection::append(const Surfaces &surfaces)
{
append_to(this->surfaces, surfaces);
}
void
SurfaceCollection::append(const ExPolygons &src, const Surface &templ)
{
this->surfaces.reserve(this->surfaces.size() + src.size());
for (ExPolygons::const_iterator it = src.begin(); it != src.end(); ++ it) {
this->surfaces.push_back(templ);
this->surfaces.back().expolygon = *it;
}
}
void
SurfaceCollection::append(const ExPolygons &src, SurfaceType surfaceType)
{
this->surfaces.reserve(this->surfaces.size() + src.size());
for (ExPolygons::const_iterator it = src.begin(); it != src.end(); ++ it)
this->surfaces.push_back(Surface(surfaceType, *it));
}
size_t
SurfaceCollection::polygons_count() const
{
size_t count = 0;
for (Surfaces::const_iterator it = this->surfaces.begin(); it != this->surfaces.end(); ++ it)
count += 1 + it->expolygon.holes.size();
return count;
}
}

View File

@ -18,12 +18,16 @@ class SurfaceCollection
operator Polygons() const;
operator ExPolygons() const;
void simplify(double tolerance);
void group(std::vector<SurfacesPtr> *retval);
void group(std::vector<SurfacesConstPtr> *retval) const;
template <class T> bool any_internal_contains(const T &item) const;
template <class T> bool any_bottom_contains(const T &item) const;
SurfacesPtr filter_by_type(SurfaceType type);
void filter_by_type(SurfaceType type, Polygons* polygons);
void append(const SurfaceCollection &coll);
void append(const Surfaces &surfaces);
void append(const ExPolygons &src, const Surface &templ);
void append(const ExPolygons &src, SurfaceType surfaceType);
size_t polygons_count() const;
};
}

View File

@ -5,6 +5,7 @@
#include <ostream>
#include <iostream>
#include <sstream>
#include <vector>
#define SLIC3R_VERSION "1.3.0-dev"
@ -37,6 +38,12 @@ namespace Slic3r {
enum Axis { X=0, Y, Z };
template <class T>
inline void append_to(std::vector<T> &dst, const std::vector<T> &src)
{
dst.insert(dst.end(), src.begin(), src.end());
}
}
using namespace Slic3r;
@ -53,10 +60,13 @@ void confess_at(const char *file, int line, const char *func, const char *pat, .
// For example, could optimized functions with move semantics be used?
#if __cplusplus==201402L
#define SLIC3R_CPPVER 14
#define STDMOVE(WHAT) std::move(WHAT)
#elif __cplusplus==201103L
#define SLIC3R_CPPVER 11
#define STDMOVE(WHAT) std::move(WHAT)
#else
#define SLIC3R_CPPVER 0
#define STDMOVE(WHAT) (WHAT)
#endif
#endif

View File

@ -11,6 +11,7 @@ REGISTER_CLASS(ExtrusionLoop, "ExtrusionLoop");
// there is no ExtrusionLoop::Collection or ExtrusionEntity::Collection
REGISTER_CLASS(ExtrusionEntityCollection, "ExtrusionPath::Collection");
REGISTER_CLASS(Flow, "Flow");
REGISTER_CLASS(Filler, "Filler");
REGISTER_CLASS(AvoidCrossingPerimeters, "GCode::AvoidCrossingPerimeters");
REGISTER_CLASS(OozePrevention, "GCode::OozePrevention");
REGISTER_CLASS(Wipe, "GCode::Wipe");

View File

@ -53,6 +53,7 @@ extern "C" {
#include <ClipperUtils.hpp>
#include <Config.hpp>
#include <ExPolygon.hpp>
#include <Fill/FillBase.hpp>
#include <MultiPoint.hpp>
#include <Point.hpp>
#include <Polygon.hpp>
@ -151,6 +152,20 @@ SV* to_SV(TriangleMesh* THIS);
SV* polynode_children_2_perl(const ClipperLib::PolyNode& node);
SV* polynode2perl(const ClipperLib::PolyNode& node);
class Filler
{
public:
Filler() : fill(NULL) {};
~Filler() {
if (fill != NULL) {
delete fill;
fill = NULL;
}
};
Fill *fill;
FillParams params;
};
}
#endif

78
xs/xsp/Filler.xsp Normal file
View File

@ -0,0 +1,78 @@
%module{Slic3r::XS};
%{
#include <xsinit.h>
#include "libslic3r/Fill/Fill.hpp"
#include "libslic3r/PolylineCollection.hpp"
#include "libslic3r/ExtrusionEntity.hpp"
#include "libslic3r/ExtrusionEntityCollection.hpp"
%}
%name{Slic3r::Filler} class Filler {
~Filler();
void set_bounding_box(BoundingBox *bbox)
%code{% THIS->fill->set_bounding_box(*bbox); %};
void set_spacing(coordf_t spacing)
%code{% THIS->fill->spacing = spacing; %};
coordf_t spacing()
%code{% RETVAL = THIS->fill->spacing; %};
void set_layer_id(size_t layer_id)
%code{% THIS->fill->layer_id = layer_id; %};
void set_z(coordf_t z)
%code{% THIS->fill->z = z; %};
void set_angle(float angle)
%code{% THIS->fill->angle = angle; %};
void set_link_max_length(coordf_t len)
%code{% THIS->fill->link_max_length = len; %};
void set_loop_clipping(coordf_t clipping)
%code{% THIS->fill->loop_clipping = clipping; %};
bool use_bridge_flow()
%code{% RETVAL = THIS->fill->use_bridge_flow(); %};
bool no_sort()
%code{% RETVAL = THIS->fill->no_sort(); %};
void set_density(float density)
%code{% THIS->params.density = density; %};
void set_dont_connect(bool dont_connect)
%code{% THIS->params.dont_connect = dont_connect; %};
void set_dont_adjust(bool dont_adjust)
%code{% THIS->params.dont_adjust = dont_adjust; %};
void set_complete(bool complete)
%code{% THIS->params.complete = complete; %};
PolylineCollection* _fill_surface(Surface *surface)
%code{%
PolylineCollection *pc = NULL;
if (THIS->fill != NULL) {
pc = new PolylineCollection();
pc->polylines = THIS->fill->fill_surface(*surface, THIS->params);
}
RETVAL = pc;
%};
%{
Filler*
new_from_type(CLASS, type)
char* CLASS;
std::string type;
CODE:
Filler *filler = new Filler();
filler->fill = Fill::new_from_type(type);
RETVAL = filler;
OUTPUT:
RETVAL
%}
};
%package{Slic3r::Filler};
void make_fill(LayerRegion* layerm, ExtrusionEntityCollection* out)
%code{% make_fill(*layerm, out); %};
coord_t adjust_solid_spacing(coord_t width, coord_t distance)
%code{% RETVAL = Fill::adjust_solid_spacing(width, distance); %};

View File

@ -86,6 +86,7 @@
bool any_bottom_region_slice_contains_polyline(Polyline* polyline)
%code%{ RETVAL = THIS->any_bottom_region_slice_contains(*polyline); %};
void make_perimeters();
void make_fills();
};
%name{Slic3r::Layer::Support} class SupportLayer {

View File

@ -60,18 +60,18 @@ SV*
SurfaceCollection::group()
CODE:
// perform grouping
std::vector<SurfacesPtr> groups;
std::vector<SurfacesConstPtr> groups;
THIS->group(&groups);
// build return arrayref
AV* av = newAV();
av_fill(av, groups.size()-1);
size_t i = 0;
for (std::vector<SurfacesPtr>::iterator it = groups.begin(); it != groups.end(); ++it) {
for (std::vector<SurfacesConstPtr>::const_iterator it = groups.begin(); it != groups.end(); ++it) {
AV* innerav = newAV();
av_fill(innerav, it->size()-1);
size_t j = 0;
for (SurfacesPtr::iterator it_s = it->begin(); it_s != it->end(); ++it_s) {
for (SurfacesConstPtr::const_iterator it_s = it->begin(); it_s != it->end(); ++it_s) {
av_store(innerav, j++, perl_to_SV_clone_ref(**it_s));
}
av_store(av, i++, newRV_noinc((SV*)innerav));

View File

@ -1,3 +1,4 @@
coord_t T_IV
coordf_t T_NV
std::string T_STD_STRING
@ -116,6 +117,10 @@ ExtrusionLoop* O_OBJECT_SLIC3R
Ref<ExtrusionLoop> O_OBJECT_SLIC3R_T
Clone<ExtrusionLoop> O_OBJECT_SLIC3R_T
Filler* O_OBJECT_SLIC3R
Ref<Filler> O_OBJECT_SLIC3R_T
Clone<Filler> O_OBJECT_SLIC3R_T
Flow* O_OBJECT_SLIC3R
Ref<Flow> O_OBJECT_SLIC3R_T
Clone<Flow> O_OBJECT_SLIC3R_T

View File

@ -1,5 +1,6 @@
%typemap{bool}{simple};
%typemap{size_t}{simple};
%typemap{coord_t}{simple};
%typemap{coordf_t}{simple};
%typemap{std::string};
%typemap{t_config_option_key};
@ -60,6 +61,9 @@
%typemap{Flow*};
%typemap{Ref<Flow>}{simple};
%typemap{Clone<Flow>}{simple};
%typemap{Filler*};
%typemap{Ref<Filler>}{simple};
%typemap{Clone<Filler>}{simple};
%typemap{Line*};
%typemap{Ref<Line>}{simple};
%typemap{Clone<Line>}{simple};