mirror of
https://git.mirrors.martin98.com/https://github.com/slic3r/Slic3r.git
synced 2025-08-01 05:42:00 +08:00
Ported infill to XS/C++. Credits and many thanks go to @bubnikv for this work
This commit is contained in:
parent
d98b7cf863
commit
103ec05774
6
Build.PL
6
Build.PL
@ -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};
|
||||
|
@ -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 {};
|
||||
|
@ -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;
|
@ -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;
|
@ -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;
|
@ -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;
|
@ -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;
|
@ -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;
|
@ -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;
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
},
|
||||
);
|
||||
|
@ -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);
|
||||
|
46
t/fill.t
46
t/fill.t
@ -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
|
||||
|
@ -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 `
|
||||
|
@ -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
|
||||
|
17
xs/MANIFEST
17
xs/MANIFEST
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 ¢er) 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)
|
||||
{
|
||||
|
@ -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 ¢er) const;
|
||||
void rotate(double angle) {
|
||||
*this = this->rotated(angle);
|
||||
}
|
||||
void rotate(double angle, const Point ¢er) {
|
||||
*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
|
||||
|
@ -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 ¢er)
|
||||
{
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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 ¢er);
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
290
xs/src/libslic3r/Fill/Fill.cpp
Normal file
290
xs/src/libslic3r/Fill/Fill.cpp
Normal 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
|
23
xs/src/libslic3r/Fill/Fill.hpp
Normal file
23
xs/src/libslic3r/Fill/Fill.hpp
Normal 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_
|
227
xs/src/libslic3r/Fill/Fill3DHoneycomb.cpp
Normal file
227
xs/src/libslic3r/Fill/Fill3DHoneycomb.cpp
Normal 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 ¶ms,
|
||||
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
|
31
xs/src/libslic3r/Fill/Fill3DHoneycomb.hpp
Normal file
31
xs/src/libslic3r/Fill/Fill3DHoneycomb.hpp
Normal 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 ¶ms,
|
||||
unsigned int thickness_layers,
|
||||
const std::pair<float, Point> &direction,
|
||||
ExPolygon &expolygon,
|
||||
Polylines* polylines_out);
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // slic3r_Fill3DHoneycomb_hpp_
|
138
xs/src/libslic3r/Fill/FillBase.cpp
Normal file
138
xs/src/libslic3r/Fill/FillBase.cpp
Normal 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 ¶ms)
|
||||
{
|
||||
// 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
|
111
xs/src/libslic3r/Fill/FillBase.hpp
Normal file
111
xs/src/libslic3r/Fill/FillBase.hpp
Normal 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 ¶ms);
|
||||
|
||||
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 ¶ms,
|
||||
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_
|
63
xs/src/libslic3r/Fill/FillConcentric.cpp
Normal file
63
xs/src/libslic3r/Fill/FillConcentric.cpp
Normal 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 ¶ms,
|
||||
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
|
26
xs/src/libslic3r/Fill/FillConcentric.hpp
Normal file
26
xs/src/libslic3r/Fill/FillConcentric.hpp
Normal 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 ¶ms,
|
||||
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_
|
126
xs/src/libslic3r/Fill/FillHoneycomb.cpp
Normal file
126
xs/src/libslic3r/Fill/FillHoneycomb.cpp
Normal 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 ¶ms,
|
||||
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
|
47
xs/src/libslic3r/Fill/FillHoneycomb.hpp
Normal file
47
xs/src/libslic3r/Fill/FillHoneycomb.hpp
Normal 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 ¶ms,
|
||||
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_
|
206
xs/src/libslic3r/Fill/FillPlanePath.cpp
Normal file
206
xs/src/libslic3r/Fill/FillPlanePath.cpp
Normal 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 ¶ms,
|
||||
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
|
66
xs/src/libslic3r/Fill/FillPlanePath.hpp
Normal file
66
xs/src/libslic3r/Fill/FillPlanePath.hpp
Normal 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 ¶ms,
|
||||
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_
|
138
xs/src/libslic3r/Fill/FillRectilinear.cpp
Normal file
138
xs/src/libslic3r/Fill/FillRectilinear.cpp
Normal 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 ¶ms,
|
||||
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
|
86
xs/src/libslic3r/Fill/FillRectilinear.hpp
Normal file
86
xs/src/libslic3r/Fill/FillRectilinear.hpp
Normal 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 ¶ms,
|
||||
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_
|
1712
xs/src/libslic3r/Fill/FillRectilinear2.cpp
Normal file
1712
xs/src/libslic3r/Fill/FillRectilinear2.cpp
Normal file
File diff suppressed because it is too large
Load Diff
76
xs/src/libslic3r/Fill/FillRectilinear2.hpp
Normal file
76
xs/src/libslic3r/Fill/FillRectilinear2.hpp
Normal 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 ¶ms);
|
||||
|
||||
protected:
|
||||
bool fill_surface_by_lines(const Surface *surface, const FillParams ¶ms, float angleBase, float pattern_shift, Polylines &polylines_out);
|
||||
};
|
||||
|
||||
class FillGrid2 : public FillRectilinear2
|
||||
{
|
||||
public:
|
||||
virtual ~FillGrid2() {}
|
||||
virtual Polylines fill_surface(const Surface &surface, const FillParams ¶ms);
|
||||
|
||||
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 ¶ms);
|
||||
|
||||
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 ¶ms);
|
||||
|
||||
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 ¶ms);
|
||||
|
||||
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_
|
@ -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()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -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() {};
|
||||
};
|
||||
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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 ¢er)
|
||||
{
|
||||
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
|
||||
|
@ -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);
|
||||
|
@ -72,8 +72,10 @@ Point::rotate(double angle, const Point ¢er)
|
||||
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)
|
||||
{
|
||||
|
@ -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 ¢er);
|
||||
Point rotated(double angle) const {
|
||||
Point p(*this);
|
||||
p.rotate(angle);
|
||||
return p;
|
||||
}
|
||||
Point rotated(double angle, const Point ¢er) 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
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
#endif
|
||||
Point start_near,
|
||||
bool no_reverse)
|
||||
bool no_reverse
|
||||
#if SLIC3R_CPPVER >= 11
|
||||
, bool move_from_src
|
||||
#endif
|
||||
)
|
||||
{
|
||||
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)
|
||||
{
|
||||
|
@ -8,6 +8,15 @@ 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
|
||||
@ -19,15 +28,12 @@ public:
|
||||
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);
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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");
|
||||
|
@ -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
78
xs/xsp/Filler.xsp
Normal 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); %};
|
@ -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 {
|
||||
|
@ -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));
|
||||
|
@ -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
|
||||
|
@ -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};
|
||||
|
Loading…
x
Reference in New Issue
Block a user