Meged with release_candidate_1_3

This commit is contained in:
bubnikv 2016-09-12 11:29:39 +02:00
commit 9fcc8fe9ae
59 changed files with 4622 additions and 953 deletions

View file

@ -193,7 +193,9 @@ sub thread_cleanup {
*Slic3r::ExtrusionLoop::DESTROY = sub {};
*Slic3r::ExtrusionPath::DESTROY = sub {};
*Slic3r::ExtrusionPath::Collection::DESTROY = sub {};
*Slic3r::ExtrusionSimulator::DESTROY = sub {};
*Slic3r::Flow::DESTROY = sub {};
*Slic3r::Filler::Destroy = sub {};
*Slic3r::GCode::DESTROY = sub {};
*Slic3r::GCode::AvoidCrossingPerimeters::DESTROY = sub {};
*Slic3r::GCode::OozePrevention::DESTROY = sub {};

View file

@ -3,45 +3,25 @@ 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::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',
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;
return Slic3r::Filler->new_from_type($filler);
}
$self->fillers->{$filler} ||= $FillTypes{$filler}->new(
bounding_box => $self->bounding_box,
);
$self->fillers->{$filler} ||= Slic3r::Filler->new_from_type($filler);
$self->fillers->{$filler}->set_bounding_box($self->bounding_box);
return $self->fillers->{$filler};
}
@ -227,25 +207,25 @@ sub make_fill {
-1, # auto width
$layerm->layer->object,
);
$f->spacing($internal_flow->spacing);
$f->set_spacing($internal_flow->spacing);
$using_internal_flow = 1;
} else {
$f->spacing($flow->spacing);
$f->set_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);
$f->set_layer_id($layerm->layer->id);
$f->set_z($layerm->layer->print_z);
$f->set_angle(deg2rad($layerm->region->config->fill_angle));
$f->set_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(
$_,
my @polylines = $f->fill_surface(
$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)
@ -278,7 +258,7 @@ sub make_fill {
mm3_per_mm => $mm3_per_mm,
width => $flow->width,
height => $flow->height,
), @polylines,
), map @$_, @polylines,
);
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,168 +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 }
1;

View file

@ -40,6 +40,7 @@ use Wx 0.9901 qw(:bitmap :dialog :icon :id :misc :systemsettings :toplevelwindow
:filedialog :font);
use Wx::Event qw(EVT_IDLE EVT_COMMAND);
use base 'Wx::App';
#use base 'Wx::AppConsole';
use constant FILE_WILDCARDS => {
known => 'Known files (*.stl, *.obj, *.amf, *.xml)|*.stl;*.STL;*.obj;*.OBJ;*.amf;*.AMF;*.xml;*.XML',

View file

@ -294,8 +294,13 @@ sub _init_menubar {
$self->_append_menu_item($helpMenu, "&About Slic3r", 'Show about dialog', sub {
wxTheApp->about;
});
if (Slic3r::GUI::debugged()) {
$self->_append_menu_item($helpMenu, "&Debug", 'Break to debugger', sub {
Slic3r::GUI::break_to_debugger();
});
}
}
# menubar
# assign menubar to frame after appending items, otherwise special items
# will not be handled correctly

View file

@ -1,10 +1,13 @@
# 2D preview of the tool paths of a single layer, using a thin line.
# OpenGL is used to render the paths.
package Slic3r::GUI::Plater::2DToolpaths;
use strict;
use warnings;
use utf8;
use Slic3r::Print::State ':steps';
use Wx qw(:misc :sizer :slider :statictext wxWHITE);
use Wx qw(:misc :sizer :slider :statictext :keycode wxWHITE wxWANTS_CHARS);
use Wx::Event qw(EVT_SLIDER EVT_KEY_DOWN);
use base qw(Wx::Panel Class::Accessor);
@ -14,7 +17,7 @@ sub new {
my $class = shift;
my ($parent, $print) = @_;
my $self = $class->SUPER::new($parent, -1, wxDefaultPosition);
my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS);
$self->SetBackgroundColour(wxWHITE);
# init GUI elements
@ -48,17 +51,21 @@ sub new {
});
EVT_KEY_DOWN($canvas, sub {
my ($s, $event) = @_;
my $key = $event->GetKeyCode;
if ($key == 85 || $key == 315) {
if ($key == 85 || $key == WXK_LEFT) {
# Keys: 'D' or WXK_LEFT
$slider->SetValue($slider->GetValue + 1);
$self->set_z($self->{layers_z}[$slider->GetValue]);
} elsif ($key == 68 || $key == 317) {
} elsif ($key == 68 || $key == WXK_RIGHT) {
# Keys: 'U' or WXK_RIGHT
$slider->SetValue($slider->GetValue - 1);
$self->set_z($self->{layers_z}[$slider->GetValue]);
} elsif ($key >= 49 && $key <= 55) {
# Keys: '1' to '3'
$canvas->set_simulation_mode($key - 49);
}
});
$self->SetSizer($sizer);
$self->SetMinSize($self->GetSize);
$sizer->SetSizeHints($self);
@ -132,6 +139,10 @@ __PACKAGE__->mk_accessors(qw(
_zoom
_camera_target
_drag_start_xy
_texture_name
_texture_size
_extrusion_simulator
_simulation_mode
));
# make OpenGL::Array thread-safe
@ -155,7 +166,13 @@ sub new {
# 2D point in model space
$self->_camera_target(Slic3r::Pointf->new(0,0));
# Texture for the extrusion simulator. The texture will be allocated / reallocated on Resize.
$self->_texture_name(0);
$self->_texture_size(Slic3r::Point->new(0,0));
$self->_extrusion_simulator(Slic3r::ExtrusionSimulator->new());
$self->_simulation_mode(0);
EVT_PAINT($self, sub {
my $dc = Wx::PaintDC->new($self);
$self->Render($dc);
@ -210,6 +227,21 @@ sub new {
return $self;
}
sub Destroy {
my ($self) = @_;
# Deallocate the OpenGL resources.
my $context = $self->GetContext;
if ($context and $self->texture_id) {
$self->SetCurrent($context);
glDeleteTextures(1, ($self->texture_id));
$self->SetCurrent(0);
$self->texture_id(0);
$self->texture_size(new Slic3r::Point(0, 0));
}
return $self->SUPER::Destroy;
}
sub mouse_event {
my ($self, $e) = @_;
@ -278,6 +310,14 @@ sub set_z {
$self->Refresh;
}
sub set_simulation_mode
{
my ($self, $mode) = @_;
$self->_simulation_mode($mode);
$self->_dirty(1);
$self->Refresh;
}
sub Render {
my ($self, $dc) = @_;
@ -299,7 +339,43 @@ sub Render {
glDisable(GL_DEPTH_TEST);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
if ($self->_simulation_mode and $self->_texture_name and $self->_texture_size->x() > 0 and $self->_texture_size->y() > 0) {
$self->_simulate_extrusion();
my ($x, $y) = $self->GetSizeWH;
glEnable(GL_TEXTURE_2D);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,GL_REPLACE);
glBindTexture(GL_TEXTURE_2D, $self->_texture_name);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage2D_c(GL_TEXTURE_2D,
0, # level (0 normal, heighr is form mip-mapping)
GL_RGBA, # internal format
$self->_texture_size->x(), $self->_texture_size->y(),
0, # border
GL_RGBA, # format RGBA color data
GL_UNSIGNED_BYTE, # unsigned byte data
$self->_extrusion_simulator->image_ptr()); # ptr to texture data
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glOrtho(0, 1, 0, 1, 0, 1);
glBegin(GL_QUADS);
glTexCoord2f(0, 0);
glVertex2f(0, 0);
glTexCoord2f($x/$self->_texture_size->x(), 0);
glVertex2f(1, 0);
glTexCoord2f($x/$self->_texture_size->x(), $y/$self->_texture_size->y());
glVertex2f(1, 1);
glTexCoord2f(0, $y/$self->_texture_size->y());
glVertex2f(0, 1);
glEnd();
glPopMatrix();
glBindTexture(GL_TEXTURE_2D, 0);
}
# anti-alias
if (0) {
glEnable(GL_LINE_SMOOTH);
@ -308,8 +384,9 @@ sub Render {
glHint(GL_POLYGON_SMOOTH_HINT, GL_DONT_CARE);
}
# Tesselator triangulates polygons with holes on the fly for the rendering purposes only.
my $tess;
if (!(&Wx::wxMSW && $OpenGL::VERSION < 0.6704)) {
if ($self->_simulation_mode() == 0 and !(&Wx::wxMSW && $OpenGL::VERSION < 0.6704)) {
# We can't use the GLU tesselator on MSW with older OpenGL versions
# because of an upstream bug:
# http://sourceforge.net/p/pogl/bugs/16/
@ -448,8 +525,42 @@ sub _draw_path {
glVertex2f(@{$line->a});
glVertex2f(@{$line->b});
glEnd();
}
}
}
sub _simulate_extrusion {
my ($self) = @_;
$self->_extrusion_simulator->reset_accumulator();
foreach my $layer (@{$self->layers}) {
if (abs($layer->print_z - $self->z) < epsilon) {
my $object = $layer->object;
my @shifts = (defined $object) ? @{$object->_shifted_copies} : (Slic3r::Point->new(0, 0));
foreach my $layerm (@{$layer->regions}) {
my @extrusions = ();
if ($object->step_done(STEP_PERIMETERS)) {
push @extrusions, @$_ for @{$layerm->perimeters};
}
if ($object->step_done(STEP_INFILL)) {
push @extrusions, @$_ for @{$layerm->fills};
}
foreach my $extrusion_entity (@extrusions) {
my @paths = $extrusion_entity->isa('Slic3r::ExtrusionLoop')
? @$extrusion_entity
: ($extrusion_entity);
foreach my $path (@paths) {
print "width: ", $path->width,
" height: ", $path->height,
" mm3_per_mm: ", $path->mm3_per_mm,
" height2: ", $path->mm3_per_mm / $path->height,
"\n";
$self->_extrusion_simulator->extrude_to_accumulator($path, $_, $self->_simulation_mode()) for @shifts;
}
}
}
}
}
$self->_extrusion_simulator->evaluate_accumulator($self->_simulation_mode());
}
sub InitGL {
@ -457,6 +568,10 @@ sub InitGL {
return if $self->init;
return unless $self->GetContext;
my $texture_id = 0;
($texture_id) = glGenTextures_p(1);
$self->_texture_name($texture_id);
$self->init(1);
}
@ -489,6 +604,33 @@ sub Resize {
$self->SetCurrent($self->GetContext);
my ($x, $y) = $self->GetSizeWH;
if ($self->_texture_size->x() < $x or $self->_texture_size->y() < $y) {
# Allocate a large enough OpenGL texture with power of 2 dimensions.
$self->_texture_size->set_x(1) if ($self->_texture_size->x() == 0);
$self->_texture_size->set_y(1) if ($self->_texture_size->y() == 0);
$self->_texture_size->set_x($self->_texture_size->x() * 2) while ($self->_texture_size->x() < $x);
$self->_texture_size->set_y($self->_texture_size->y() * 2) while ($self->_texture_size->y() < $y);
#print "screen size ", $x, "x", $y;
#print "texture size ", $self->_texture_size->x(), "x", $self->_texture_size->y();
# Initialize an empty texture.
glBindTexture(GL_TEXTURE_2D, $self->_texture_name);
if (1) {
glTexImage2D_c(GL_TEXTURE_2D,
0, # level (0 normal, heighr is form mip-mapping)
GL_RGBA, # internal format
$self->_texture_size->x(), $self->_texture_size->y(),
0, # border
GL_RGBA, # format RGBA color data
GL_UNSIGNED_BYTE, # unsigned byte data
0); # ptr to texture data
}
glBindTexture(GL_TEXTURE_2D, 0);
$self->_extrusion_simulator->set_image_size($self->_texture_size);
}
$self->_extrusion_simulator->set_viewport(Slic3r::Geometry::BoundingBox->new_from_points(
[Slic3r::Point->new(0, 0), Slic3r::Point->new($x, $y)]));
glViewport(0, 0, $x, $y);
glMatrixMode(GL_PROJECTION);
@ -544,10 +686,18 @@ sub Resize {
$x2 = $x1 + $new_x;
}
glOrtho($x1, $x2, $y1, $y2, 0, 1);
# Set the adjusted bounding box at the extrusion simulator.
#print "Scene bbox ", $bb->x_min, ",", $bb->y_min, " ", $bb->x_max, ",", $bb->y_max, "\n";
#print "Setting simulator bbox ", $x1, ",", $y1, " ", $x2, ",", $y2, "\n";
$self->_extrusion_simulator->set_bounding_box(
Slic3r::Geometry::BoundingBox->new_from_points(
[Slic3r::Point->new($x1, $y1), Slic3r::Point->new($x2, $y2)]));
glMatrixMode(GL_MODELVIEW);
}
# Thick line drawing is not used anywhere. Probably not tested?
sub line {
my (
$x1, $y1, $x2, $y2, # coordinates of the line

View file

@ -675,8 +675,8 @@ sub generate_toolpaths {
# 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 +702,7 @@ sub generate_toolpaths {
my @paths = ();
foreach my $expolygon (@{union_ex($interface)}) {
my @p = $fillers{interface}->fill_surface(
my $polylines = $fillers{interface}->fill_surface(
Slic3r::Surface->new(expolygon => $expolygon, surface_type => S_TYPE_INTERNAL),
density => $interface_density,
layer_height => $layer->height,
@ -711,12 +711,12 @@ sub generate_toolpaths {
my $mm3_per_mm = $_interface_flow->mm3_per_mm;
push @paths, map Slic3r::ExtrusionPath->new(
polyline => Slic3r::Polyline->new(@$_),
polyline => $_,
role => EXTR_ROLE_SUPPORTMATERIAL_INTERFACE,
mm3_per_mm => $mm3_per_mm,
width => $_interface_flow->width,
height => $layer->height,
), @p;
), @$polylines,
}
$layer->support_interface_fills->append(@paths);
@ -725,11 +725,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 +742,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 +767,7 @@ sub generate_toolpaths {
my $mm3_per_mm = $base_flow->mm3_per_mm;
foreach my $expolygon (@$to_infill) {
my @p = $filler->fill_surface(
my $polylines = $filler->fill_surface(
Slic3r::Surface->new(expolygon => $expolygon, surface_type => S_TYPE_INTERNAL),
density => $density,
layer_height => $layer->height,
@ -775,12 +775,12 @@ sub generate_toolpaths {
);
push @paths, map Slic3r::ExtrusionPath->new(
polyline => Slic3r::Polyline->new(@$_),
polyline => $_,
role => EXTR_ROLE_SUPPORTMATERIAL,
mm3_per_mm => $mm3_per_mm,
width => $base_flow->width,
height => $layer->height,
), @p;
), @$polylines;
}
$layer->support_fills->append(@paths);

61
slic3r.sublime-project Normal file
View file

@ -0,0 +1,61 @@
{
"build_systems":
[
{
"name": "List",
//"file_regex": " at ([^-\\s]*) line ([0-9]*)",
// "file_regex": " at (D\\:\\/src\\/Slic3r\\/.*?) line ([0-9]*)",
"shell_cmd": "ls -l"
},
{
"name": "Run",
"working_dir": "$project_path",
"file_regex": " at (.*?) line ([0-9]*)",
"shell_cmd": "perl slic3r.pl --gui \"..\\Slic3r-tests\\gap fill torture 20 -rt.stl\""
},
{
"name": "full",
"file_regex": "^(..[^:]*):([0-9]+):?([0-9]+)?:? (.*)$",
"shell_cmd": "perl Build.pl"
},
{
"name": "xs",
"working_dir": "$project_path/xs",
"file_regex": "^(..[^:]*):([0-9]+):?([0-9]+)?:? (.*)$",
"shell_cmd": "perl Build install"
},
{
"name": "xs & run",
"working_dir": "$project_path/xs",
"file_regex": "^(..[^:]*):([0-9]+):?([0-9]+)?:? (.*)$",
"shell_cmd": "perl Build install & cd .. & perl slic3r.pl --gui \"..\\Slic3r-tests\\star3-big2.stl\""
}
],
"folders":
[
{
"path": "."
}
],
"settings":
{
"sublimegdb_workingdir": "${folder:${project_path:run}}",
// NOTE: You MUST provide --interpreter=mi for the plugin to work
// "sublimegdb_commandline": "D:\\Qt\\Tools\\mingw492_32\\bin\\gdb.exe --interpreter=mi -ex 'target localhost:2345'",
// "sublimegdb_commandline": "D:\\Qt\\Tools\\mingw492_32\\bin\\gdb.exe --interpreter=mi perl --args perl slic3r.pl",
// "sublimegdb_commandline": "D:\\Qt\\Tools\\mingw492_32\\bin\\gdb.exe --interpreter=mi perl --args slic3r.pl ",
// "sublimegdb_commandline": "D:\\Qt\\Tools\\mingw492_32\\bin\\gdb.exe --interpreter=mi -e C:\\Strawberry\\perl\\bin\\perl.exe -s C:\\Strawberry\\perl\\site\\lib\\auto\\Slic3r\\XS\\XS.xs.dll --args perl slic3r.pl -j 1 --gui D:\\src\\Slic3r-tests\\star3-big.stl",
"sublimegdb_commandline": "D:\\Qt\\Tools\\mingw492_32\\bin\\gdb.exe --interpreter=mi perl.exe --args perl slic3r.pl -j 1 --gui", // D:\\src\\Slic3r-tests\\star3-big.stl",
// "sublimegdb_commandline": "D:\\Qt\\Tools\\mingw492_32\\bin\\gdb.exe --interpreter=mi -x slic3r.gdb",
// "arguments": "slic3r -j 1 --gui ../Slic3r-tests/star3-big.stl",
// "arguments": "../slic3r.pl -j 1 --gui",
// "sublimegdb_exec_cmd": "-exec-continue",
// Add "pending breakpoints" for symbols that are dynamically loaded from
// external shared libraries
"debug_ext" : true,
"run_after_init": false,
"close_views": false
}
}

View file

@ -40,10 +40,15 @@ if (defined $ENV{BOOST_INCLUDEDIR}) {
push @boost_include, $ENV{BOOST_DIR};
}
} else {
# Boost library was not defined by the environment.
# Try to guess at some default paths.
if ($mswin) {
for my $path (glob('C:\dev\boost*\include'), glob ('C:\boost*\include')) {
push @boost_include, grep { -d $_ }
qw(/opt/local/include /usr/local/include /opt/include),
qw(/usr/include C:\Boost\include);
push @boost_libs, grep { -d $_ }
qw(/opt/local/lib /usr/local/lib /opt/lib /usr/lib),
qw(C:\Boost\lib /lib);
if ($^O eq 'MSWin32') {
for my $path (glob('C:\dev\boost*'), glob ('C:\boost*'), glob ('d:\src\boost*')) {
push @boost_include, $path;
}
if (! @boost_include) {

View file

@ -28,6 +28,22 @@ src/libslic3r/ExtrusionEntity.cpp
src/libslic3r/ExtrusionEntity.hpp
src/libslic3r/ExtrusionEntityCollection.cpp
src/libslic3r/ExtrusionEntityCollection.hpp
src/libslic3r/ExtrusionSimulator.cpp
src/libslic3r/ExtrusionSimulator.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
@ -129,6 +145,8 @@ xsp/Extruder.xsp
xsp/ExtrusionEntityCollection.xsp
xsp/ExtrusionLoop.xsp
xsp/ExtrusionPath.xsp
xsp/ExtrusionSimulator.xsp
xsp/Filler.xsp
xsp/Flow.xsp
xsp/GCode.xsp
xsp/GCodeSender.xsp

View file

@ -123,6 +123,26 @@ sub clone {
);
}
package Slic3r::ExtrusionSimulator;
sub new {
my ($class, %args) = @_;
return $class->_new();
}
package Slic3r::Filler;
sub fill_surface {
my ($self, $surface, %args) = @_;
$self->set_width($args{width}) if defined($args{width});
$self->set_density($args{density}) if defined($args{density});
$self->set_distance($args{distance}) if defined($args{distance});
$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 +235,8 @@ for my $class (qw(
Slic3r::ExtrusionLoop
Slic3r::ExtrusionPath
Slic3r::ExtrusionPath::Collection
Slic3r::ExtrusionSimulator
Slic3r::Filler
Slic3r::Flow
Slic3r::GCode
Slic3r::GCode::AvoidCrossingPerimeters

View file

@ -21,6 +21,7 @@ class BoundingBoxBase
bool defined;
BoundingBoxBase() : defined(false) {};
BoundingBoxBase(const PointClass &pmin, const PointClass &pmax) : min(pmin), max(pmax) {}
BoundingBoxBase(const std::vector<PointClass> &points);
void merge(const PointClass &point);
void merge(const std::vector<PointClass> &points);
@ -37,6 +38,7 @@ class BoundingBox3Base : public BoundingBoxBase<PointClass>
{
public:
BoundingBox3Base() : BoundingBoxBase<PointClass>() {};
BoundingBox3Base(const PointClass &pmin, const PointClass &pmax) : BoundingBoxBase<PointClass>(pmin, pmax) {}
BoundingBox3Base(const std::vector<PointClass> &points);
void merge(const PointClass &point);
void merge(const std::vector<PointClass> &points);
@ -54,6 +56,7 @@ class BoundingBox : public BoundingBoxBase<Point>
Polygon polygon() const;
BoundingBox() : BoundingBoxBase<Point>() {};
BoundingBox(const Point &pmin, const Point &pmax) : BoundingBoxBase<Point>(pmin, pmax) {};
BoundingBox(const Points &points) : BoundingBoxBase<Point>(points) {};
BoundingBox(const Lines &lines);
};
@ -65,15 +68,39 @@ class BoundingBox3 : public BoundingBox3Base<Point3> {};
class BoundingBoxf : public BoundingBoxBase<Pointf> {
public:
BoundingBoxf() : BoundingBoxBase<Pointf>() {};
BoundingBoxf(const Pointf &pmin, const Pointf &pmax) : BoundingBoxBase<Pointf>(pmin, pmax) {};
BoundingBoxf(const std::vector<Pointf> &points) : BoundingBoxBase<Pointf>(points) {};
};
class BoundingBoxf3 : public BoundingBox3Base<Pointf3> {
public:
BoundingBoxf3() : BoundingBox3Base<Pointf3>() {};
BoundingBoxf3(const Pointf3 &pmin, const Pointf3 &pmax) : BoundingBox3Base<Pointf3>(pmin, pmax) {};
BoundingBoxf3(const std::vector<Pointf3> &points) : BoundingBox3Base<Pointf3>(points) {};
};
template<typename VT>
inline bool operator==(const BoundingBoxBase<VT> &bb1, const BoundingBoxBase<VT> &bb2)
{
return bb1.min == bb2.min && bb1.max == bb2.max;
}
template<typename VT>
inline bool operator!=(const BoundingBoxBase<VT> &bb1, const BoundingBoxBase<VT> &bb2)
{
return !(bb1 == bb2);
}
template<typename VT>
inline bool empty(const BoundingBoxBase<VT> &bb)
{
return bb.min.x > bb.max.y || bb.min.y > bb.max.y;
}
template<typename VT>
inline bool empty(const BoundingBox3Base<VT> &bb)
{
return bb.min.x > bb.max.x || bb.min.y > bb.max.y || bb.min.z > bb.max.z;}
}
#endif

View file

@ -594,6 +594,10 @@ void union_pt_chained(const Slic3r::Polygons &subject, Slic3r::Polygons* retval,
{
ClipperLib::PolyTree pt;
union_pt(subject, &pt, safety_offset_);
if (&subject == retval)
// It is safe to use the same variable for input and output, because this function makes
// a temporary copy of the results.
retval->clear();
traverse_pt(pt.Childs, retval);
}

View file

@ -35,47 +35,47 @@ void scaleClipperPolygons(ClipperLib::Paths &polygons, const double scale);
// offset Polygons
void offset(const Slic3r::Polygons &polygons, ClipperLib::Paths* retval, const float delta,
double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
double miterLimit = 3);
void offset(const Slic3r::Polygons &polygons, Slic3r::Polygons* retval, const float delta,
double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
double miterLimit = 3);
Slic3r::Polygons offset(const Slic3r::Polygons &polygons, const float delta,
double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
double miterLimit = 3);
// offset Polylines
void offset(const Slic3r::Polylines &polylines, ClipperLib::Paths* retval, const float delta,
double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtSquare,
double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtSquare,
double miterLimit = 3);
void offset(const Slic3r::Polylines &polylines, Slic3r::Polygons* retval, const float delta,
double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtSquare,
double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtSquare,
double miterLimit = 3);
void offset(const Slic3r::Surface &surface, Slic3r::Surfaces* retval, const float delta,
double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtSquare,
double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtSquare,
double miterLimit = 3);
void offset(const Slic3r::Polygons &polygons, Slic3r::ExPolygons* retval, const float delta,
double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
double miterLimit = 3);
Slic3r::ExPolygons offset_ex(const Slic3r::Polygons &polygons, const float delta,
double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
double miterLimit = 3);
void offset2(const Slic3r::Polygons &polygons, ClipperLib::Paths* retval, const float delta1,
const float delta2, double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
const float delta2, double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
double miterLimit = 3);
void offset2(const Slic3r::Polygons &polygons, Slic3r::Polygons* retval, const float delta1,
const float delta2, double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
const float delta2, double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
double miterLimit = 3);
Slic3r::Polygons offset2(const Slic3r::Polygons &polygons, const float delta1,
const float delta2, double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
const float delta2, double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
double miterLimit = 3);
void offset2(const Slic3r::Polygons &polygons, Slic3r::ExPolygons* retval, const float delta1,
const float delta2, double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
const float delta2, double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
double miterLimit = 3);
Slic3r::ExPolygons offset2_ex(const Slic3r::Polygons &polygons, const float delta1,
const float delta2, double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
const float delta2, double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
double miterLimit = 3);
template <class T>

View file

@ -52,6 +52,15 @@ ExPolygon::translate(double x, double y)
}
}
void
ExPolygon::rotate(double angle)
{
contour.rotate(angle);
for (Polygons::iterator it = holes.begin(); it != holes.end(); ++it) {
(*it).rotate(angle);
}
}
void
ExPolygon::rotate(double angle, const Point &center)
{

View file

@ -20,6 +20,7 @@ class ExPolygon
operator Polygons() const;
void scale(double factor);
void translate(double x, double y);
void rotate(double angle);
void rotate(double angle, const Point &center);
double area() const;
bool is_valid() const;
@ -45,6 +46,32 @@ class ExPolygon
std::string dump_perl() const;
};
inline Polygons to_polygons(const ExPolygons &src)
{
Polygons polygons;
for (ExPolygons::const_iterator it = src.begin(); it != src.end(); ++it) {
polygons.push_back(it->contour);
for (Polygons::const_iterator ith = it->holes.begin(); ith != it->holes.end(); ++ith) {
polygons.push_back(*ith);
}
}
return polygons;
}
#if SLIC3R_CPPVER > 11
inline Polygons to_polygons(ExPolygons &&src)
{
Polygons polygons;
for (ExPolygons::const_iterator it = src.begin(); it != src.end(); ++it) {
polygons.push_back(std::move(it->contour));
for (Polygons::const_iterator ith = it->holes.begin(); ith != it->holes.end(); ++ith) {
polygons.push_back(std::move(*ith));
}
}
return polygons;
}
#endif
}
// start Boost

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,60 @@
#ifndef slic3r_ExtrusionSimulator_hpp_
#define slic3r_ExtrusionSimulator_hpp_
#include "libslic3r.h"
#include "Config.hpp"
#include "ExtrusionEntity.hpp"
#include "BoundingBox.hpp"
namespace Slic3r {
enum ExtrusionSimulationType
{
ExtrusionSimulationSimple,
ExtrusionSimulationDontSpread,
ExtrisopmSimulationSpreadNotOverfilled,
ExtrusionSimulationSpreadFull,
ExtrusionSimulationSpreadExcess,
};
// An opaque class, to keep the boost stuff away from the header.
class ExtrusionSimulatorImpl;
class ExtrusionSimulator
{
public:
ExtrusionSimulator();
~ExtrusionSimulator();
// Size of the image, that will be returned by image_ptr().
// The image may be bigger than the viewport as many graphics drivers
// expect the size of a texture to be rounded to a power of two.
void set_image_size(const Point &image_size);
// Which part of the image shall be rendered to?
void set_viewport(const BoundingBox &viewport);
// Shift and scale of the rendered extrusion paths into the viewport.
void set_bounding_box(const BoundingBox &bbox);
// Reset the extrusion accumulator to zero for all buckets.
void reset_accumulator();
// Paint a thick path into an extrusion buffer.
// A simple implementation is provided now, splatting a rectangular extrusion for each linear segment.
// In the future, spreading and suqashing of a material will be simulated.
void extrude_to_accumulator(const ExtrusionPath &path, const Point &shift, ExtrusionSimulationType simulationType);
// Evaluate the content of the accumulator and paint it into the viewport.
// After this call the image_ptr() call will return a valid image.
void evaluate_accumulator(ExtrusionSimulationType simulationType);
// An RGBA image of image_size, to be loaded into a GPU texture.
const void* image_ptr() const;
private:
Point image_size;
BoundingBox viewport;
BoundingBox bbox;
ExtrusionSimulatorImpl *pimpl;
};
}
#endif /* slic3r_ExtrusionSimulator_hpp_ */

View file

@ -0,0 +1,223 @@
#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)
{
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 = abs(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;
}
Polylines Fill3DHoneycomb::fill_surface(const Surface *surface, const FillParams &params)
{
ExPolygon expolygon = surface->expolygon;
BoundingBox bb = expolygon.contour.bounding_box();
Point size = bb.size();
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.merge(Point(
bb.min.x - (bb.min.x % (2*distance)),
bb.min.y - (bb.min.y % (2*distance))));
// generate pattern
Polylines polylines = makeGrid(
scale_(this->z),
distance,
ceil(size.x / distance) + 1,
ceil(size.y / distance) + 1,
((this->layer_id / surface->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
intersection(polylines, (Polygons)expolygon, &polylines);
// 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(
#if SLIC3R_CPPVER >= 11
std::move(polylines),
#else
polylines,
#endif
PolylineCollection::leftmost_point(polylines), false); // reverse allowed
#if SLIC3R_CPPVER >= 11
assert(polylines.empty());
#else
polylines.clear();
#endif
for (Polylines::iterator it_polyline = chained.begin(); it_polyline != chained.end(); ++ it_polyline) {
if (! polylines.empty()) {
// Try to connect the lines.
Points &pts_end = polylines.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.push_back(std::move(*it_polyline));
#else
polylines.push_back(Polyline());
std::swap(polylines.back(), *it_polyline);
#endif
}
}
// TODO: return ExtrusionLoop objects to get better chained paths
return polylines;
}
} // namespace Slic3r

View file

@ -0,0 +1,24 @@
#ifndef slic3r_Fill3DHoneycomb_hpp_
#define slic3r_Fill3DHoneycomb_hpp_
#include <map>
#include "../libslic3r.h"
#include "FillBase.hpp"
namespace Slic3r {
class Fill3DHoneycomb : public FillWithDirection
{
public:
virtual ~Fill3DHoneycomb() {}
virtual Polylines fill_surface(const Surface *surface, const FillParams &params);
// require bridge flow since most of this pattern hangs in air
virtual bool use_bridge_flow() const { return true; }
};
} // namespace Slic3r
#endif // slic3r_Fill3DHoneycomb_hpp_

View file

@ -0,0 +1,81 @@
#include "../Surface.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 std::string &type)
{
if (type == "concentric")
return new FillConcentric();
if (type == "honeycomb")
return new FillHoneycomb();
if (type == "3dhoneycomb")
return new Fill3DHoneycomb();
if (type == "rectilinear")
// return new FillRectilinear();
return new FillRectilinear2();
if (type == "line")
return new FillLine();
if (type == "grid")
return new FillGrid();
if (type == "archimedeanchords")
return new FillArchimedeanChords();
if (type == "hilbertcurve")
return new FillHilbertCurve();
if (type == "octagramspiral")
return new FillOctagramSpiral();
CONFESS("unknown type");
return NULL;
}
coord_t Fill::adjust_solid_spacing(const coord_t width, const coord_t distance)
{
coord_t number_of_lines = coord_t(coordf_t(width) / coordf_t(distance)) + 1;
coord_t extra_space = width % distance;
return (number_of_lines <= 1) ?
distance :
distance + extra_space / (number_of_lines - 1);
}
std::pair<float, Point> FillWithDirection::infill_direction(const Surface *surface) const
{
// set infill angle
float out_angle = this->angle;
if (out_angle == FLT_MAX) {
//FIXME Vojtech: Add a warning?
printf("Using undefined infill angle\n");
out_angle = 0.f;
}
Point out_shift = empty(this->bounding_box) ?
surface->expolygon.contour.bounding_box().center() :
this->bounding_box.center();
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 /* SLIC3R_DEBUG */
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);
} else {
printf("Layer_ID undefined!\n");
}
out_angle += float(M_PI/2.);
return std::pair<float, Point>(out_angle, out_shift);
}
} // namespace Slic3r

View file

@ -0,0 +1,103 @@
#ifndef slic3r_FillBase_hpp_
#define slic3r_FillBase_hpp_
#include <float.h>
#include "../libslic3r.h"
#include "../BoundingBox.hpp"
namespace Slic3r {
class Surface;
struct FillParams
{
FillParams() { memset(this, 0, sizeof(FillParams)); }
coordf_t width;
// Fraction in <0, 1>
float density;
coordf_t distance;
// 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;
// Height of the layer, in unscaled coordinates
coordf_t z;
// in unscaled coordinates
coordf_t spacing;
// in radians, ccw, 0 = East
float angle;
// in scaled coordinates
coord_t loop_clipping;
// in scaled coordinates
BoundingBox bounding_box;
public:
virtual ~Fill() {}
static Fill* new_from_type(const std::string &type);
void set_bounding_box(const Slic3r::BoundingBox &bbox) { bounding_box = bbox; }
// Use bridge flow for the fill?
virtual bool use_bridge_flow() const { return false; }
// Do not sort the fill lines to optimize the print head path?
virtual bool no_sort() const { return false; }
// Perform the fill.
virtual Polylines fill_surface(const Surface *surface, const FillParams &params) = 0;
protected:
Fill() :
layer_id(size_t(-1)),
z(0.f),
spacing(0.f),
// Initial angle is undefined.
angle(FLT_MAX),
loop_clipping(0),
// The initial bounding box is empty, therefore undefined.
bounding_box(Point(0, 0), Point(-1, -1))
{}
static coord_t adjust_solid_spacing(const coord_t width, const coord_t distance);
};
// An interface class to Perl, aggregating an instance of a Fill and a FillData.
class Filler
{
public:
Filler() : fill(NULL) {}
~Filler() { delete fill; fill = NULL; }
Fill *fill;
FillParams params;
};
class FillWithDirection : public Fill
{
public:
virtual float _layer_angle(size_t idx) const {
bool odd = idx & 1;
return (idx & 1) ? float(M_PI/2.) : 0;
}
virtual std::pair<float, Point> infill_direction(const Surface *surface) const ;
};
} // namespace Slic3r
#endif // slic3r_FillBase_hpp_

View file

@ -0,0 +1,60 @@
#include "../ClipperUtils.hpp"
#include "../ExPolygon.hpp"
#include "../Surface.hpp"
#include "FillConcentric.hpp"
namespace Slic3r {
Polylines FillConcentric::fill_surface(const Surface *surface, const FillParams &params)
{
// no rotation is supported for this infill pattern
ExPolygon expolygon = surface->expolygon;
BoundingBox bounding_box = expolygon.contour.bounding_box();
coord_t min_spacing = scale_(this->spacing);
coord_t distance = coord_t(min_spacing / params.density);
if (params.density > 0.9999f && !params.dont_adjust) {
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
union_pt_chained(loops, &loops, false);
// split paths using a nearest neighbor search
Polylines paths;
Point last_pos(0, 0);
for (Polygons::const_iterator it_loop = loops.begin(); it_loop != loops.end(); ++ it_loop) {
paths.push_back(it_loop->split_at_index(last_pos.nearest_point_index(*it_loop)));
last_pos = paths.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 = 0;
for (size_t i = 0; i < paths.size(); ++ i) {
paths[i].clip_end(this->loop_clipping);
if (paths[i].is_valid()) {
if (j < i)
std::swap(paths[j], paths[i]);
++ j;
}
}
if (j < paths.size())
paths.erase(paths.begin() + j, paths.end());
// TODO: return ExtrusionLoop objects to get better chained paths
return paths;
}
} // namespace Slic3r

View file

@ -0,0 +1,20 @@
#ifndef slic3r_FillConcentric_hpp_
#define slic3r_FillConcentric_hpp_
#include "FillBase.hpp"
namespace Slic3r {
class FillConcentric : public Fill
{
public:
virtual ~FillConcentric() {}
virtual Polylines fill_surface(const Surface *surface, const FillParams &params);
protected:
virtual bool no_sort() const { return true; }
};
} // namespace Slic3r
#endif // slic3r_FillConcentric_hpp_

View file

@ -0,0 +1,133 @@
#include "../ClipperUtils.hpp"
#include "../PolylineCollection.hpp"
#include "../Surface.hpp"
#include "FillHoneycomb.hpp"
namespace Slic3r {
Polylines FillHoneycomb::fill_surface(const Surface *surface, const FillParams &params)
{
std::pair<float, Point> rotate_vector = this->infill_direction(surface);
// cache hexagons math
CacheID cache_id(params.density, this->spacing);
Cache::iterator it_m = this->cache.find(cache_id);
if (it_m == this->cache.end()) {
#if SLIC3R_CPPVER > 11
it_m = this->cache.emplace_hint(it_m);
#else
it_m = this->cache.insert(it_m, std::pair<CacheID, CacheData>(cache_id, CacheData()));
#endif
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 = surface->expolygon.contour.bounding_box();
{
// rotate bounding box according to infill direction
Polygon bb_polygon = bounding_box.polygon();
bb_polygon.rotate(rotate_vector.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
bounding_box.merge(Point(
bounding_box.min.x - (bounding_box.min.x % m.hex_width),
bounding_box.min.y - (bounding_box.min.y % m.pattern_height)));
}
coord_t x = bounding_box.min.x;
while (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(-rotate_vector.first, m.hex_center);
polygons.push_back(p);
}
}
Polylines paths;
if (params.complete || true) {
// we were requested to complete each loop;
// in this case we don't try to make more continuous paths
Polygons polygons_trimmed = intersection((Polygons)*surface, polygons);
for (Polygons::iterator it = polygons_trimmed.begin(); it != polygons_trimmed.end(); ++ it)
paths.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 p;
for (Polygons::iterator it = polygons.begin(); it != polygons.end(); ++ it)
p.push_back((Polyline)(*it));
paths = intersection(p, (Polygons)*surface);
}
// connect paths
if (! paths.empty()) { // prevent calling leftmost_point() on empty collections
Polylines chained = PolylineCollection::chained_path_from(
#if SLIC3R_CPPVER >= 11
std::move(paths),
#else
paths,
#endif
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
Polylines paths_trimmed = intersection(paths, to_polygons(offset_ex(surface->expolygon, SCALED_EPSILON)));
#if SLIC3R_CPPVER >= 11
paths = std::move(paths_trimmed);
#else
std::swap(paths, paths_trimmed);
#endif
}
return paths;
}
} // namespace Slic3r

View file

@ -0,0 +1,50 @@
#ifndef slic3r_FillHoneycomb_hpp_
#define slic3r_FillHoneycomb_hpp_
#include <map>
#include "../libslic3r.h"
#include "FillBase.hpp"
namespace Slic3r {
class FillHoneycomb : public FillWithDirection
{
public:
virtual ~FillHoneycomb() {}
virtual Polylines fill_surface(const Surface *surface, const FillParams &params);
protected:
// Caching the
struct CacheID
{
CacheID(float adensity, coordf_t aspacing) :
density(adensity), spacing(aspacing) {}
float density;
coordf_t spacing;
bool operator<(const CacheID &other) const
{ return (density < other.density) || (density == other.density && spacing < other.spacing); }
bool operator==(const CacheID &other) const
{ return density == other.density && spacing == other.spacing; }
};
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::map<CacheID, CacheData> Cache;
Cache cache;
virtual float _layer_angle(size_t idx) const { return 0.5f * float(M_PI) * (idx % 3); }
};
} // namespace Slic3r
#endif // slic3r_FillHoneycomb_hpp_

View file

@ -0,0 +1,197 @@
#include "../ClipperUtils.hpp"
#include "../PolylineCollection.hpp"
#include "../Surface.hpp"
#include "FillPlanePath.hpp"
namespace Slic3r {
Polylines FillPlanePath::fill_surface(const Surface *surface, const FillParams &params)
{
ExPolygon expolygon = surface->expolygon;
std::pair<float, Point> rotate_vector = this->infill_direction(surface);
expolygon.rotate(- rotate_vector.first);
coord_t distance_between_lines = scale_(this->spacing) / params.density;
// align infill across layers using the object's bounding box
Polygon bb_polygon = this->bounding_box.polygon();
bb_polygon.rotate(- rotate_vector.first);
BoundingBox bounding_box = bb_polygon.bounding_box();
Point shift = this->_centered() ?
bounding_box.center() :
bounding_box.min;
expolygon.translate(-shift.x, -shift.y);
bounding_box.translate(-shift.x, -shift.y);
Pointfs pts = _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::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))));
// intersection(polylines_src, offset((Polygons)expolygon, scale_(0.02)), &polylines);
intersection(polylines, (Polygons)expolygon, &polylines);
/*
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(rotate_vector.first);
}
}
return polylines;
}
// 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.
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 a = 1.;
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;
}
}
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.
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;
coordf_t r_inc = sqrt(2.);
Pointfs out;
out.push_back(Pointf(0, 0));
while (r < rmax) {
r += r_inc;
coordf_t rx = r / sqrt(2.);
coordf_t r2 = r + rx;
out.push_back(Pointf( r, 0.));
out.push_back(Pointf( r2, rx));
out.push_back(Pointf( rx, rx));
out.push_back(Pointf( rx, r2));
out.push_back(Pointf(0., r));
out.push_back(Pointf(-rx, r2));
out.push_back(Pointf(-rx, rx));
out.push_back(Pointf(-r2, rx));
out.push_back(Pointf(-r, 0.));
out.push_back(Pointf(-r2, -rx));
out.push_back(Pointf(-rx, -rx));
out.push_back(Pointf(-rx, -r2));
out.push_back(Pointf(0., -r));
out.push_back(Pointf( rx, -r2));
out.push_back(Pointf( rx, -rx));
out.push_back(Pointf( r2+r_inc, -rx));
}
return out;
}
} // namespace Slic3r

View file

@ -0,0 +1,60 @@
#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 FillWithDirection
{
public:
virtual ~FillPlanePath() {}
virtual Polylines fill_surface(const Surface *surface, const FillParams &params);
protected:
virtual float _layer_angle(size_t idx) const { return 0.f; }
virtual bool _centered() const = 0;
virtual Pointfs _generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y) = 0;
};
class FillArchimedeanChords : public FillPlanePath
{
public:
virtual ~FillArchimedeanChords() {}
protected:
virtual bool _centered() const { return true; }
virtual Pointfs _generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y);
};
class FillHilbertCurve : public FillPlanePath
{
public:
virtual ~FillHilbertCurve() {}
protected:
virtual bool _centered() const { return false; }
virtual Pointfs _generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y);
};
class FillOctagramSpiral : public FillPlanePath
{
public:
virtual ~FillOctagramSpiral() {}
protected:
virtual bool _centered() const { return true; }
virtual Pointfs _generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y);
};
} // namespace Slic3r
#endif // slic3r_FillPlanePath_hpp_

View file

@ -0,0 +1,138 @@
#include "../ClipperUtils.hpp"
#include "../ExPolygon.hpp"
#include "../PolylineCollection.hpp"
#include "../Surface.hpp"
#include "FillRectilinear.hpp"
namespace Slic3r {
Polylines FillRectilinear::fill_surface(const Surface *surface, const FillParams &params)
{
// rotate polygons so that we can work with vertical lines here
ExPolygon expolygon = surface->expolygon;
std::pair<float, Point> rotate_vector = this->infill_direction(surface);
expolygon.rotate(- rotate_vector.first);
// No need to translate the polygon anyhow for the infill.
// The infill will be performed inside a bounding box of the expolygon and its absolute position does not matter.
// expolygon.translate(rotate_vector.second.x, rotate_vector.second.y);
this->_min_spacing = scale_(this->spacing);
assert(params.density > 0.0001f && params.density <= 1.f);
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
bounding_box.merge(Point(
bounding_box.min.x - (bounding_box.min.x % this->_line_spacing),
bounding_box.min.y - (bounding_box.min.y % this->_line_spacing)));
}
// generate the basic pattern
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()) {
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
//FIXME Vojtech: Update the intersecton function to work directly with lines.
Polylines polylines_src;
polylines_src.reserve(lines.size());
for (Lines::const_iterator it = lines.begin(); it != lines.end(); ++ it) {
polylines_src.push_back(Polyline());
Points &pts = polylines_src.back().points;
pts.reserve(2);
pts.push_back(it->a);
pts.push_back(it->b);
}
Polylines polylines = intersection(polylines_src, offset((Polygons)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;
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;
}
// 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(
#if SLIC3R_CPPVER >= 11
std::move(polylines),
#else
polylines,
#endif
PolylineCollection::leftmost_point(polylines), false); // reverse allowed
#if SLIC3R_CPPVER >= 11
assert(polylines.empty());
#else
polylines.clear();
#endif
for (Polylines::iterator it_polyline = chained.begin(); it_polyline != chained.end(); ++ it_polyline) {
if (! polylines.empty()) {
// Try to connect the lines.
Points &pts_end = polylines.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.
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.push_back(std::move(*it_polyline));
#else
polylines.push_back(Polyline());
std::swap(polylines.back(), *it_polyline);
#endif
}
}
// paths must be rotated back
for (Polylines::iterator it = polylines.begin(); it != polylines.end(); ++ it) {
// No need to translate, the absolute position is irrelevant.
// it->translate(- rotate_vector.second.x, - rotate_vector.second.y);
it->rotate(rotate_vector.first);
}
return polylines;
}
} // namespace Slic3r

View file

@ -0,0 +1,72 @@
#ifndef slic3r_FillRectilinear_hpp_
#define slic3r_FillRectilinear_hpp_
#include "../libslic3r.h"
#include "FillBase.hpp"
namespace Slic3r {
class Surface;
class FillRectilinear : public FillWithDirection
{
public:
virtual ~FillRectilinear() {}
virtual Polylines fill_surface(const Surface *surface, const FillParams &params);
protected:
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::Base.
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; }
};
}; // namespace Slic3r
#endif // slic3r_FillRectilinear_hpp_

View file

@ -0,0 +1,956 @@
#include <assert.h>
#include <stdint.h>
#include <algorithm>
#include <cmath>
#include <limits>
#include <boost/static_assert.hpp>
#include "../ClipperUtils.hpp"
#include "../ExPolygon.hpp"
#include "../Surface.hpp"
#include "FillRectilinear2.hpp"
#ifdef SLIC3R_DEBUG
#include "SVG.hpp"
#endif
#if defined(SLIC3R_DEBUG) and defined(_WIN32)
#include <Windows.h>
#pragma comment(lib, "user32.lib")
static inline void assert_fail(const char *assertion, const char *file, unsigned line, const char *function)
{
printf("Assert: %s in function %s\nfile %s:%d\n", assertion, function, file, line);
if (IsDebuggerPresent()) {
DebugBreak();
} else {
ExitProcess(-1);
}
}
#undef assert
#define assert(expr) \
((expr) \
? static_cast<void>(0) \
: assert_fail (#expr, __FILE__, __LINE__, __FUNCTION__))
#endif /* SLIC3R_DEBUG */
namespace Slic3r {
#ifndef clamp
template<typename T>
static inline T clamp(T low, T high, T x)
{
return std::max<T>(low, std::min<T>(high, x));
}
#endif /* clamp */
#ifndef sqr
template<typename T>
static inline T sqr(T x)
{
return x * x;
}
#endif /* sqr */
#ifndef mag2
static inline coordf_t mag2(const Point &p)
{
return sqr(coordf_t(p.x)) + sqr(coordf_t(p.y));
}
#endif /* mag2 */
#ifndef mag
static inline coordf_t mag(const Point &p)
{
return std::sqrt(mag2(p));
}
#endif /* mag */
enum Orientation
{
ORIENTATION_CCW = 1,
ORIENTATION_CW = -1,
ORIENTATION_COLINEAR = 0
};
// Return orientation of the three points (clockwise, counter-clockwise, colinear)
// The predicate is exact for the coord_t type, using 64bit signed integers for the temporaries.
//FIXME Make sure the temporaries do not overflow,
// which means, the coord_t types must not have some of the topmost bits utilized.
static inline Orientation orient(const Point &a, const Point &b, const Point &c)
{
BOOST_STATIC_ASSERT(sizeof(coord_t) * 2 == sizeof(int64_t));
int64_t u = int64_t(b.x) * int64_t(c.y) - int64_t(b.y) * int64_t(c.x);
int64_t v = int64_t(a.x) * int64_t(c.y) - int64_t(a.y) * int64_t(c.x);
int64_t w = int64_t(a.x) * int64_t(b.y) - int64_t(a.y) * int64_t(b.x);
int64_t d = u - v + w;
return (d > 0) ? ORIENTATION_CCW : ((d == 0) ? ORIENTATION_COLINEAR : ORIENTATION_CW);
}
// Return orientation of the polygon.
// The input polygon must not contain duplicate points.
static inline bool is_ccw(const Polygon &poly)
{
// The polygon shall be at least a triangle.
assert(poly.points.size() >= 3);
if (poly.points.size() < 3)
return true;
// 1) Find the lowest lexicographical point.
int imin = 0;
for (size_t i = 1; i < poly.points.size(); ++ i) {
const Point &pmin = poly.points[imin];
const Point &p = poly.points[i];
if (p.x < pmin.x || (p.x == pmin.x && p.y < pmin.y))
imin = i;
}
// 2) Detect the orientation of the corner imin.
size_t iPrev = ((imin == 0) ? poly.points.size() : imin) - 1;
size_t iNext = ((imin + 1 == poly.points.size()) ? 0 : imin + 1);
Orientation o = orient(poly.points[iPrev], poly.points[imin], poly.points[iNext]);
// The lowest bottom point must not be collinear if the polygon does not contain duplicate points
// or overlapping segments.
assert(o != ORIENTATION_COLINEAR);
return o == ORIENTATION_CCW;
}
// Having a segment of a closed polygon, calculate its Euclidian length.
// The segment indices seg1 and seg2 signify an end point of an edge in the forward direction of the loop,
// therefore the point p1 lies on poly.points[seg1-1], poly.points[seg1] etc.
static inline coordf_t segment_length(const Polygon &poly, size_t seg1, const Point &p1, size_t seg2, const Point &p2)
{
#ifdef SLIC3R_DEBUG
// Verify that p1 lies on seg1. This is difficult to verify precisely,
// but at least verify, that p1 lies in the bounding box of seg1.
for (size_t i = 0; i < 2; ++ i) {
size_t seg = (i == 0) ? seg1 : seg2;
Point px = (i == 0) ? p1 : p2;
Point pa = poly.points[((seg == 0) ? poly.points.size() : seg) - 1];
Point pb = poly.points[seg];
if (pa.x > pb.x)
std::swap(pa.x, pb.x);
if (pa.y > pb.y)
std::swap(pa.y, pb.y);
assert(px.x >= pa.x && px.x <= pb.x);
assert(px.y >= pa.y && px.y <= pb.y);
}
#endif /* SLIC3R_DEBUG */
const Point *pPrev = &p1;
const Point *pThis = NULL;
coordf_t len = 0;
if (seg1 <= seg2) {
for (size_t i = seg1; i < seg2; ++ i, pPrev = pThis)
len += pPrev->distance_to(*(pThis = &poly.points[i]));
} else {
for (size_t i = seg1; i < poly.points.size(); ++ i, pPrev = pThis)
len += pPrev->distance_to(*(pThis = &poly.points[i]));
for (size_t i = 0; i < seg2; ++ i, pPrev = pThis)
len += pPrev->distance_to(*(pThis = &poly.points[i]));
}
len += pPrev->distance_to(p2);
return len;
}
// Append a segment of a closed polygon to a polyline.
// The segment indices seg1 and seg2 signify an end point of an edge in the forward direction of the loop.
// Only insert intermediate points between seg1 and seg2.
static inline void polygon_segment_append(Points &out, const Polygon &polygon, size_t seg1, size_t seg2)
{
if (seg1 == seg2) {
// Nothing to append from this segment.
} else if (seg1 < seg2) {
// Do not append a point pointed to by seg2.
out.insert(out.end(), polygon.points.begin() + seg1, polygon.points.begin() + seg2);
} else {
out.reserve(out.size() + seg2 + polygon.points.size() - seg1);
out.insert(out.end(), polygon.points.begin() + seg1, polygon.points.end());
// Do not append a point pointed to by seg2.
out.insert(out.end(), polygon.points.begin(), polygon.points.begin() + seg2);
}
}
// Append a segment of a closed polygon to a polyline.
// The segment indices seg1 and seg2 signify an end point of an edge in the forward direction of the loop,
// but this time the segment is traversed backward.
// Only insert intermediate points between seg1 and seg2.
static inline void polygon_segment_append_reversed(Points &out, const Polygon &polygon, size_t seg1, size_t seg2)
{
if (seg1 >= seg2) {
out.reserve(seg1 - seg2);
for (size_t i = seg1; i > seg2; -- i)
out.push_back(polygon.points[i - 1]);
} else {
// it could be, that seg1 == seg2. In that case, append the complete loop.
out.reserve(out.size() + seg2 + polygon.points.size() - seg1);
for (size_t i = seg1; i > 0; -- i)
out.push_back(polygon.points[i - 1]);
for (size_t i = polygon.points.size(); i > seg2; -- i)
out.push_back(polygon.points[i - 1]);
}
}
// Intersection point of a vertical line with a polygon segment.
class SegmentIntersection
{
public:
SegmentIntersection() :
iContour(0),
iSegment(0),
pos(0),
type(UNKNOWN),
consumed_vertical_up(false),
consumed_perimeter_right(false)
{}
// Index of a contour in ExPolygonWithOffset, with which this vertical line intersects.
size_t iContour;
// Index of a segment in iContour, with which this vertical line intersects.
size_t iSegment;
// y position of the intersection.
coord_t pos;
// Kind of intersection. With the original contour, or with the inner offestted contour?
// A vertical segment will be at least intersected by OUTER_LOW, OUTER_HIGH,
// but it could be intersected with OUTER_LOW, INNER_LOW, INNER_HIGH, OUTER_HIGH,
// and there may be more than one pair of INNER_LOW, INNER_HIGH between OUTER_LOW, OUTER_HIGH.
enum SegmentIntersectionType {
OUTER_LOW = 0,
OUTER_HIGH = 1,
INNER_LOW = 2,
INNER_HIGH = 3,
UNKNOWN = -1
};
SegmentIntersectionType type;
// Was this segment along the y axis consumed?
// Up means up along the vertical segment.
bool consumed_vertical_up;
// Was a segment of the inner perimeter contour consumed?
// Right means right from the vertical segment.
bool consumed_perimeter_right;
// For the INNER_LOW type, this point may be connected to another INNER_LOW point following a perimeter contour.
// For the INNER_HIGH type, this point may be connected to another INNER_HIGH point following a perimeter contour.
// If INNER_LOW is connected to INNER_HIGH or vice versa,
// one has to make sure the vertical infill line does not overlap with the connecting perimeter line.
bool is_inner() const { return type == INNER_LOW || type == INNER_HIGH; }
bool is_outer() const { return type == OUTER_LOW || type == OUTER_HIGH; }
bool is_low () const { return type == INNER_LOW || type == OUTER_LOW; }
bool is_high () const { return type == INNER_HIGH || type == OUTER_HIGH; }
bool operator<(const SegmentIntersection &other) const
{ return pos < other.pos; }
};
// A vertical line with intersection points with polygons.
class SegmentedIntersectionLine
{
public:
// Index of this vertical intersection line.
size_t idx;
// x position of this vertical intersection line.
coord_t pos;
// List of intersection points with polygons, sorted increasingly by the y axis.
std::vector<SegmentIntersection> intersections;
};
// A container maintaining an expolygon with its inner offsetted polygon.
// The purpose of the inner offsetted polygon is to provide segments to connect the infill lines.
struct ExPolygonWithOffset
{
public:
ExPolygonWithOffset(const ExPolygon &aexpolygon, coord_t aoffset) : expolygon(aexpolygon)
{
polygons_inner = offset((Polygons)expolygon, aoffset,
CLIPPER_OFFSET_SCALE,
ClipperLib::jtMiter,
// for the infill pattern, don't cut the corners.
// default miterLimt = 3
10.);
n_contours_outer = 1 + expolygon.holes.size();
n_contours_inner = polygons_inner.size();
n_contours = n_contours_outer + n_contours_inner;
polygons_inner_ccw.assign(polygons_inner.size(), false);
for (size_t i = 0; i < polygons_inner.size(); ++ i)
polygons_inner_ccw[i] = is_ccw(polygons_inner[i]);
#ifdef SLIC3R_DEBUG
// Verify orientation of the expolygon.
assert(is_ccw(expolygon.contour));
for (size_t i = 0; i < expolygon.holes.size(); ++ i)
assert(is_ccw(expolygon.holes[i]));
#endif /* SLIC3R_DEBUG */
}
// Outer contour of the expolygon.
bool is_contour_external(size_t idx) const { return idx == 0; }
// Any contour of the expolygon.
bool is_contour_outer(size_t idx) const { return idx < n_contours_inner; }
// Contour of the shrunk expolygon.
bool is_contour_inner(size_t idx) const { return idx >= n_contours_inner; }
const Polygon& contour(size_t idx) const {
return is_contour_external(idx) ? expolygon.contour :
(is_contour_outer(idx) ? expolygon.holes[idx - 1] : polygons_inner[idx - n_contours_inner]);
}
bool is_contour_ccw(size_t idx) const {
return is_contour_external(idx) || (is_contour_inner(idx) && polygons_inner_ccw[idx - n_contours_inner]);
}
const ExPolygon &expolygon;
Polygons polygons_inner;
size_t n_contours_outer;
size_t n_contours_inner;
size_t n_contours;
protected:
// For each polygon of polygons_inner, remember its orientation.
std::vector<unsigned char> polygons_inner_ccw;
};
// For a vertical line, an inner contour and an intersection point,
// find an intersection point on the previous resp. next vertical line.
// The intersection point is connected with the prev resp. next intersection point with iInnerContour.
// Return -1 if there is no such point on the previous resp. next vertical line.
static inline int intersection_on_prev_next_vertical_line(
const ExPolygonWithOffset &poly_with_offset,
const std::vector<SegmentedIntersectionLine> &segs,
size_t iVerticalLine,
size_t iInnerContour,
size_t iIntersection,
bool dir_is_next)
{
size_t iVerticalLineOther = iVerticalLine;
if (dir_is_next) {
if (++ iVerticalLineOther == segs.size())
// No successive vertical line.
return -1;
} else if (iVerticalLineOther -- == 0) {
// No preceding vertical line.
return -1;
}
const SegmentedIntersectionLine &il = segs[iVerticalLine];
const SegmentIntersection &itsct = il.intersections[iIntersection];
const SegmentedIntersectionLine &il2 = segs[iVerticalLineOther];
const Polygon &poly = poly_with_offset.contour(iInnerContour);
const bool ccw = poly_with_offset.is_contour_ccw(iInnerContour);
// Resulting index of an intersection point on il2.
int out = -1;
// Find an intersection point on iVerticalLineOther, intersecting iInnerContour
// at the same orientation as iIntersection, and being closest to iIntersection
// in the number of contour segments, when following the direction of the contour.
int dmin = std::numeric_limits<int>::max();
for (size_t i = 0; i < il2.intersections.size(); ++ i) {
const SegmentIntersection &itsct2 = il2.intersections[i];
if (itsct.iContour == itsct2.iContour && itsct.type == itsct2.type) {
// The intersection points lie on the same contour and have the same orientation.
// Find the intersection point with a shortest path in the direction of the contour.
int d = int(itsct2.iSegment) - int(itsct.iSegment);
if (ccw != dir_is_next)
d = - d;
if (d < 0)
d += int(poly.points.size());
if (d < dmin) {
out = i;
dmin = d;
}
}
}
//FIXME this routine is not asymptotic optimal, it will be slow if there are many intersection points along the line.
return out;
}
static inline int intersection_on_prev_vertical_line(
const ExPolygonWithOffset &poly_with_offset,
const std::vector<SegmentedIntersectionLine> &segs,
size_t iVerticalLine,
size_t iInnerContour,
size_t iIntersection)
{
return intersection_on_prev_next_vertical_line(poly_with_offset, segs, iVerticalLine, iInnerContour, iIntersection, false);
}
static inline intersection_on_next_vertical_line(
const ExPolygonWithOffset &poly_with_offset,
const std::vector<SegmentedIntersectionLine> &segs,
size_t iVerticalLine,
size_t iInnerContour,
size_t iIntersection)
{
return intersection_on_prev_next_vertical_line(poly_with_offset, segs, iVerticalLine, iInnerContour, iIntersection, true);
}
// Find an intersection on a previous line, but return -1, if the connecting segment of a perimeter was already extruded.
static inline int intersection_unused_on_prev_next_vertical_line(
const ExPolygonWithOffset &poly_with_offset,
const std::vector<SegmentedIntersectionLine> &segs,
size_t iVerticalLine,
size_t iInnerContour,
size_t iIntersection,
bool dir_is_next)
{
int iIntersectionOther = intersection_on_prev_next_vertical_line(poly_with_offset, segs, iVerticalLine, iInnerContour, iIntersection, dir_is_next);
if (iIntersectionOther == -1)
return -1;
//FIXME this routine will propose a connecting line even if the connecting perimeter segment intersects iVertical line multiple times before reaching iIntersectionOther.
assert(dir_is_next ? (iVerticalLine + 1 < segs.size()) : (iVerticalLine > 0));
const SegmentedIntersectionLine &il_this = segs[iVerticalLine];
const SegmentIntersection &itsct_this = il_this.intersections[iIntersection];
const SegmentedIntersectionLine &il_other = segs[dir_is_next ? (iVerticalLine+1) : (iVerticalLine-1)];
const SegmentIntersection &itsct_other = il_other.intersections[iIntersectionOther];
assert(itsct_other.is_inner());
assert(itsct_other.is_low() || iIntersectionOther > 1);
if (dir_is_next ? itsct_this.consumed_perimeter_right : itsct_other.consumed_perimeter_right)
// This perimeter segment was already consumed.
return -1;
if (itsct_other.is_low() ? itsct_other.consumed_vertical_up : il_other.intersections[iIntersectionOther-1].consumed_vertical_up)
// This vertical segment was already consumed.
return -1;
return iIntersectionOther;
}
static inline intersection_unused_on_prev_vertical_line(
const ExPolygonWithOffset &poly_with_offset,
const std::vector<SegmentedIntersectionLine> &segs,
size_t iVerticalLine,
size_t iInnerContour,
size_t iIntersection)
{
return intersection_unused_on_prev_next_vertical_line(poly_with_offset, segs, iVerticalLine, iInnerContour, iIntersection, false);
}
static inline intersection_unused_on_next_vertical_line(
const ExPolygonWithOffset &poly_with_offset,
const std::vector<SegmentedIntersectionLine> &segs,
size_t iVerticalLine,
size_t iInnerContour,
size_t iIntersection)
{
return intersection_unused_on_prev_next_vertical_line(poly_with_offset, segs, iVerticalLine, iInnerContour, iIntersection, true);
}
// Measure an Euclidian length of a perimeter segment when going from iIntersection to iIntersection2.
static inline coordf_t measure_perimeter_prev_next_segment_length(
const ExPolygonWithOffset &poly_with_offset,
const std::vector<SegmentedIntersectionLine> &segs,
size_t iVerticalLine,
size_t iInnerContour,
size_t iIntersection,
size_t iIntersection2,
bool dir_is_next)
{
size_t iVerticalLineOther = iVerticalLine;
if (dir_is_next) {
if (++ iVerticalLineOther == segs.size())
// No successive vertical line.
return coordf_t(-1);
} else if (iVerticalLineOther -- == 0) {
// No preceding vertical line.
return coordf_t(-1);
}
const SegmentedIntersectionLine &il = segs[iVerticalLine];
const SegmentIntersection &itsct = il.intersections[iIntersection];
const SegmentedIntersectionLine &il2 = segs[iVerticalLineOther];
const SegmentIntersection &itsct2 = il2.intersections[iIntersection2];
const Polygon &poly = poly_with_offset.contour(iInnerContour);
const bool ccw = poly_with_offset.is_contour_ccw(iInnerContour);
assert(itsct.type == itsct2.type);
assert(itsct.iContour == itsct2.iContour);
assert(itsct.is_inner());
const bool forward = (itsct.is_low() == ccw) == dir_is_next;
Point p1(il.pos, itsct.pos);
Point p2(il2.pos, itsct2.pos);
return forward ?
segment_length(poly, itsct .iSegment, p1, itsct2.iSegment, p2) :
segment_length(poly, itsct2.iSegment, p2, itsct .iSegment, p1);
}
static inline coordf_t measure_perimeter_prev_segment_length(
const ExPolygonWithOffset &poly_with_offset,
const std::vector<SegmentedIntersectionLine> &segs,
size_t iVerticalLine,
size_t iInnerContour,
size_t iIntersection,
size_t iIntersection2)
{
return measure_perimeter_prev_next_segment_length(poly_with_offset, segs, iVerticalLine, iInnerContour, iIntersection, iIntersection2, false);
}
static inline coordf_t measure_perimeter_next_segment_length(
const ExPolygonWithOffset &poly_with_offset,
const std::vector<SegmentedIntersectionLine> &segs,
size_t iVerticalLine,
size_t iInnerContour,
size_t iIntersection,
size_t iIntersection2)
{
return measure_perimeter_prev_next_segment_length(poly_with_offset, segs, iVerticalLine, iInnerContour, iIntersection, iIntersection2, true);
}
// Append the points of a perimeter segment when going from iIntersection to iIntersection2.
// The first point (the point of iIntersection) will not be inserted,
// the last point will be inserted.
static inline void emit_perimeter_prev_next_segment(
const ExPolygonWithOffset &poly_with_offset,
const std::vector<SegmentedIntersectionLine> &segs,
size_t iVerticalLine,
size_t iInnerContour,
size_t iIntersection,
size_t iIntersection2,
Polyline &out,
bool dir_is_next)
{
size_t iVerticalLineOther = iVerticalLine;
if (dir_is_next) {
++ iVerticalLineOther;
assert(iVerticalLineOther < segs.size());
} else {
assert(iVerticalLineOther > 0);
-- iVerticalLineOther;
}
const SegmentedIntersectionLine &il = segs[iVerticalLine];
const SegmentIntersection &itsct = il.intersections[iIntersection];
const SegmentedIntersectionLine &il2 = segs[iVerticalLineOther];
const SegmentIntersection &itsct2 = il2.intersections[iIntersection2];
const Polygon &poly = poly_with_offset.contour(iInnerContour);
const bool ccw = poly_with_offset.is_contour_ccw(iInnerContour);
assert(itsct.type == itsct2.type);
assert(itsct.iContour == itsct2.iContour);
assert(itsct.is_inner());
const bool forward = (itsct.is_low() == ccw) == dir_is_next;
// Do not append the first point.
// out.points.push_back(Point(il.pos, itsct.pos));
if (forward)
polygon_segment_append(out.points, poly, itsct.iSegment, itsct2.iSegment);
else
polygon_segment_append_reversed(out.points, poly, itsct.iSegment, itsct2.iSegment);
// Append the last point.
out.points.push_back(Point(il2.pos, itsct2.pos));
}
Polylines FillRectilinear2::fill_surface(const Surface *surface, const FillParams &params)
{
// rotate polygons so that we can work with vertical lines here
ExPolygon expolygon = surface->expolygon;
std::pair<float, Point> rotate_vector = this->infill_direction(surface);
expolygon.rotate(- rotate_vector.first);
// No need to translate the polygon anyhow for the infill.
// The infill will be performed inside a bounding box of the expolygon and its absolute position does not matter.
// expolygon.translate(rotate_vector.second.x, rotate_vector.second.y);
this->_min_spacing = scale_(this->spacing);
assert(params.density > 0.0001f && params.density <= 1.f);
this->_line_spacing = coord_t(coordf_t(this->_min_spacing) / params.density);
this->_diagonal_distance = this->_line_spacing * 2;
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
bounding_box.merge(Point(
bounding_box.min.x - (bounding_box.min.x % this->_line_spacing),
bounding_box.min.y - (bounding_box.min.y % this->_line_spacing)));
}
// Intersect a set of euqally spaced vertical lines wiht expolygon.
size_t n_vlines = (bounding_box.max.x - bounding_box.min.x + SCALED_EPSILON) / this->_line_spacing;
coord_t x0 = bounding_box.min.x + this->_line_spacing;
// On these polygons the infill lines will be connected.
ExPolygonWithOffset poly_with_offset(expolygon, - _min_spacing / 2);
#ifdef SLIC3R_DEBUG
char path[2048];
static int iRun = 0;
sprintf(path, "out/FillRectilinear2-%d.svg", iRun);
BoundingBox bbox_svg = expolygon.contour.bounding_box();
bbox_svg.min.x -= coord_t(1. / SCALING_FACTOR);
bbox_svg.min.y -= coord_t(1. / SCALING_FACTOR);
bbox_svg.max.x += coord_t(1. / SCALING_FACTOR);
bbox_svg.max.y += coord_t(1. / SCALING_FACTOR);
::Slic3r::SVG svg(path, bbox_svg);
svg.draw(expolygon.lines());
svg.draw(poly_with_offset.polygons_inner);
{
char path2[2048];
sprintf(path2, "out/FillRectilinear2-initial-%d.svg", iRun);
::Slic3r::SVG svg(path2, bbox_svg);
svg.draw(expolygon.lines());
svg.draw(poly_with_offset.polygons_inner);
svg.Close();
}
iRun ++;
#endif /* SLIC3R_DEBUG */
// For each contour
// Allocate the storage for the segments.
std::vector<SegmentedIntersectionLine> segs(n_vlines, SegmentedIntersectionLine());
for (size_t i = 0; i < n_vlines; ++ i) {
segs[i].idx = i;
segs[i].pos = x0 + i * this->_line_spacing;
}
for (size_t iContour = 0; iContour < poly_with_offset.n_contours; ++ iContour) {
const Points &contour = poly_with_offset.contour(iContour);
if (contour.size() < 2)
continue;
// For each segment
for (size_t iSegment = 0; iSegment < contour.size(); ++ iSegment) {
size_t iPrev = ((iSegment == 0) ? contour.size() : iSegment) - 1;
const Point &p1 = contour[iPrev];
const Point &p2 = contour[iSegment];
// Which of the equally spaced vertical lines is intersected by this segment?
coord_t l = p1.x;
coord_t r = p2.x;
if (l > r)
std::swap(l, r);
// il, ir are the left / right indices of vertical lines intersecting a segment
int il = (l - x0) / this->_line_spacing;
while (il * this->_line_spacing + x0 < l)
++ il;
il = std::max(int(0), il);
int ir = (r - x0 + this->_line_spacing) / this->_line_spacing;
while (ir * this->_line_spacing + x0 > r)
-- ir;
ir = std::min(int(segs.size()) - 1, ir);
if (il > ir)
// No vertical line intersects this segment.
continue;
assert(il >= 0 && il < segs.size());
assert(ir >= 0 && ir < segs.size());
if (l == r) {
// The segment is vertical.
SegmentIntersection is;
is.iContour = iContour;
is.iSegment = iSegment;
is.pos = p1.y;
segs[il].intersections.push_back(is);
is.pos = p2.y;
segs[il].intersections.push_back(is);
continue;
}
for (int i = il; i <= ir; ++ i) {
SegmentIntersection is;
is.iContour = iContour;
is.iSegment = iSegment;
assert(l <= segs[i].pos);
assert(r >= segs[i].pos);
// Calculate the intersection position in y axis. x is known.
double t = double(segs[i].pos - p1.x) / double(p2.x - p1.x);
assert(t > -0.000001 && t < 1.000001);
t = clamp(0., 1., t);
coord_t lo = p1.y;
coord_t hi = p2.y;
if (lo > hi)
std::swap(lo, hi);
is.pos = p1.y + coord_t(t * double(p2.y - p1.y));
assert(is.pos > lo - 0.000001 && is.pos < hi + 0.000001);
is.pos = clamp(lo, hi, is.pos);
segs[i].intersections.push_back(is);
}
}
}
// Sort the intersections along their segments, specify the intersection types.
for (size_t i_seg = 0; i_seg < segs.size(); ++ i_seg) {
SegmentedIntersectionLine &sil = segs[i_seg];
// Sort the intersection points. This needs to be verified, because the intersection points were calculated
// using imprecise arithmetics.
std::sort(sil.intersections.begin(), sil.intersections.end());
// Verify the order, bubble sort the intersections until sorted.
bool modified = false;
do {
modified = false;
for (size_t i = 1; i < sil.intersections.size(); ++ i) {
size_t iContour1 = sil.intersections[i-1].iContour;
size_t iContour2 = sil.intersections[i].iContour;
const Points &contour1 = poly_with_offset.contour(iContour1);
const Points &contour2 = poly_with_offset.contour(iContour2);
size_t iSegment1 = sil.intersections[i-1].iSegment;
size_t iPrev1 = ((iSegment1 == 0) ? contour1.size() : iSegment1) - 1;
size_t iSegment2 = sil.intersections[i].iSegment;
size_t iPrev2 = ((iSegment2 == 0) ? contour2.size() : iSegment2) - 1;
bool swap = false;
if (iContour1 == iContour2 && iSegment1 == iSegment2) {
// The same segment, it has to be vertical.
assert(iPrev1 == iPrev2);
swap = contour1[iPrev1].y > contour1[iContour1].y;
#ifdef SLIC3R_DEBUG
if (swap)
printf("Swapping when single vertical segment\n");
#endif
} else {
// Segments are in a general position. Here an exact airthmetics may come into play.
coord_t y1max = std::max(contour1[iPrev1].y, contour1[iSegment1].y);
coord_t y2min = std::min(contour2[iPrev2].y, contour2[iSegment2].y);
if (y1max < y2min) {
// The segments are separated, nothing to do.
} else {
// Use an exact predicate to verify, that segment1 is below segment2.
const Point *a = &contour1[iPrev1];
const Point *b = &contour1[iSegment1];
const Point *c = &contour2[iPrev2];
const Point *d = &contour2[iSegment2];
#ifdef SLIC3R_DEBUG
const Point x1(sil.pos, sil.intersections[i-1].pos);
const Point x2(sil.pos, sil.intersections[i ].pos);
bool successive = false;
#endif /* SLIC3R_DEBUG */
if (a->x > b->x)
std::swap(a, b);
if (c->x > d->x)
std::swap(c, d);
bool upper_more_left = false;
if (a->x > c->x) {
upper_more_left = true;
std::swap(a, c);
std::swap(b, d);
}
if (a == c || b == c) {
assert(iContour1 == iContour2);
assert(iSegment1 == iPrev2 || iPrev1 == iSegment2);
std::swap(c, d);
assert(a != c && b != c);
#ifdef SLIC3R_DEBUG
successive = true;
#endif /* SLIC3R_DEBUG */
}
Orientation o = orient(*a, *b, *c);
assert(! ORIENTATION_COLINEAR);
swap = upper_more_left != (o == ORIENTATION_CW);
#ifdef SLIC3R_DEBUG
if (swap)
printf(successive ?
"Swapping when iContour1 == iContour2 and successive segments\n" :
"Swapping when exact predicate\n");
#endif
}
}
if (swap) {
// Swap the intersection points, but keep the original positions, so they are sorted.
std::swap(sil.intersections[i-1], sil.intersections[i]);
std::swap(sil.intersections[i-1].pos, sil.intersections[i].pos);
modified = true;
}
}
} while (modified);
// Assign the intersection types.
for (size_t i = 0; i < sil.intersections.size(); ++ i) {
// What is the orientation of the segment at the intersection point?
size_t iContour = sil.intersections[i].iContour;
const Points &contour = poly_with_offset.contour(iContour);
size_t iSegment = sil.intersections[i].iSegment;
size_t iPrev = ((iSegment == 0) ? contour.size() : iSegment) - 1;
coord_t dir = contour[iSegment].x - contour[iPrev].x;
bool ccw = poly_with_offset.is_contour_ccw(iContour);
bool low = (dir > 0) == ccw;
sil.intersections[i].type = poly_with_offset.is_contour_outer(iContour) ?
(low ? SegmentIntersection::OUTER_LOW : SegmentIntersection::OUTER_HIGH) :
(low ? SegmentIntersection::INNER_LOW : SegmentIntersection::INNER_HIGH);
}
}
#ifdef SLIC3R_DEBUG
// Verify the segments & paint them.
for (size_t i_seg = 0; i_seg < segs.size(); ++ i_seg) {
SegmentedIntersectionLine &sil = segs[i_seg];
// The intersection points have to be even.
assert((sil.intersections.size() & 1) == 0);
for (size_t i = 0; i < sil.intersections.size();) {
// An intersection segment crossing the bigger contour may cross the inner offsetted contour even number of times.
assert(sil.intersections[i].type == SegmentIntersection::OUTER_LOW);
size_t j = i + 1;
assert(j < sil.intersections.size());
assert(sil.intersections[j].type == SegmentIntersection::INNER_LOW || sil.intersections[j].type == SegmentIntersection::OUTER_HIGH);
for (; j < sil.intersections.size() && sil.intersections[j].is_inner(); ++ j) ;
assert(j < sil.intersections.size());
assert((j & 1) == 1);
assert(sil.intersections[j].type == SegmentIntersection::OUTER_HIGH);
assert(i + 1 == j || sil.intersections[j - 1].type == SegmentIntersection::INNER_HIGH);
if (i + 1 == j) {
svg.draw(Line(Point(sil.pos, sil.intersections[i].pos), Point(sil.pos, sil.intersections[j].pos)), "blue");
} else {
svg.draw(Line(Point(sil.pos, sil.intersections[i].pos), Point(sil.pos, sil.intersections[i+1].pos)), "green");
svg.draw(Line(Point(sil.pos, sil.intersections[i+1].pos), Point(sil.pos, sil.intersections[j-1].pos)), (j - i + 1 > 4) ? "yellow" : "magenta");
svg.draw(Line(Point(sil.pos, sil.intersections[j-1].pos), Point(sil.pos, sil.intersections[j].pos)), "green");
}
i = j + 1;
}
}
svg.Close();
#endif /* SLIC3R_DEBUG */
// Now construct a graph.
// Find the first point.
//FIXME ideally one would plan the initial point to be closest to the current print head position.
size_t i_vline = 0;
size_t i_intersection = size_t(-1);
// Follow the line, connect the lines into a graph.
// Until no new line could be added to the output path:
Point pointLast;
Polylines polylines_out;
Polyline *polyline_current = NULL;
for (;;) {
if (i_intersection == size_t(-1)) {
// The path has been interrupted. Find a next starting point, closest to the previous extruder position.
coordf_t dist2min = std::numeric_limits<coordf_t>().max();
for (size_t i_vline2 = 0; i_vline2 < segs.size(); ++ i_vline2) {
const SegmentedIntersectionLine &seg = segs[i_vline2];
if (! seg.intersections.empty()) {
assert(seg.intersections.size() > 1);
// Even number of intersections with the loops.
assert((seg.intersections.size() & 1) == 0);
assert(seg.intersections.front().type == SegmentIntersection::OUTER_LOW);
for (size_t i = 0; i < seg.intersections.size(); ++ i) {
const SegmentIntersection &intrsctn = seg.intersections[i];
if (intrsctn.is_outer()) {
assert(intrsctn.is_low() || i > 0);
bool consumed = intrsctn.is_low() ?
intrsctn.consumed_vertical_up :
seg.intersections[i-1].consumed_vertical_up;
if (! consumed) {
coordf_t dist2 = sqr(coordf_t(pointLast.x - seg.pos)) + sqr(coordf_t(pointLast.y - intrsctn.pos));
if (dist2 < dist2min) {
dist2min = dist2;
i_vline = i_vline2;
i_intersection = i;
if (polylines_out.empty()) {
// Initial state, take the first line, which is the first from the left.
goto found;
}
}
}
}
}
}
}
if (i_intersection == size_t(-1))
// We are finished.
break;
found:
// Start a new path.
polylines_out.push_back(Polyline());
polyline_current = &polylines_out.back();
// Emit the first point of a path.
pointLast = Point(segs[i_vline].pos, segs[i_vline].intersections[i_intersection].pos);
polyline_current->points.push_back(pointLast);
}
// From the initial point (i_vline, i_intersection), follow a path.
SegmentedIntersectionLine &seg = segs[i_vline];
SegmentIntersection *intrsctn = &seg.intersections[i_intersection];
bool going_up = intrsctn->is_low();
bool try_connect = false;
if (going_up) {
assert(! intrsctn->consumed_vertical_up);
assert(i_intersection + 1 < seg.intersections.size());
// Step back to the beginning of the vertical segment to mark it as consumed.
if (intrsctn->is_inner()) {
assert(i_intersection > 0);
-- intrsctn;
-- i_intersection;
}
// Consume the complete vertical segment up to the outer contour.
do {
intrsctn->consumed_vertical_up = true;
++ intrsctn;
++ i_intersection;
assert(i_intersection < seg.intersections.size());
} while (intrsctn->type != SegmentIntersection::OUTER_HIGH);
if ((intrsctn - 1)->is_inner()) {
// Step back.
-- intrsctn;
-- i_intersection;
assert(intrsctn->type == SegmentIntersection::INNER_HIGH);
try_connect = true;
}
} else {
// Going down.
assert(intrsctn->is_high());
assert(i_intersection > 0);
assert(! (intrsctn - 1)->consumed_vertical_up);
// Consume the complete vertical segment up to the outer contour.
if (intrsctn->is_inner())
intrsctn->consumed_vertical_up = true;
do {
assert(i_intersection > 0);
-- intrsctn;
-- i_intersection;
intrsctn->consumed_vertical_up = true;
} while (intrsctn->type != SegmentIntersection::OUTER_LOW);
if ((intrsctn + 1)->is_inner()) {
// Step back.
++ intrsctn;
++ i_intersection;
assert(intrsctn->type == SegmentIntersection::INNER_LOW);
try_connect = true;
}
}
if (try_connect) {
// Decide, whether to finish the segment, or whether to follow the perimeter.
int iPrev = intersection_unused_on_prev_vertical_line(poly_with_offset, segs, i_vline, intrsctn->iContour, i_intersection);
int iNext = intersection_unused_on_next_vertical_line(poly_with_offset, segs, i_vline, intrsctn->iContour, i_intersection);
if (iPrev != -1 || iNext != -1) {
// Zig zag
coord_t distPrev = (iPrev == -1) ? std::numeric_limits<coord_t>::max() :
measure_perimeter_prev_segment_length(poly_with_offset, segs, i_vline, intrsctn->iContour, i_intersection, iPrev);
coord_t distNext = (iNext == -1) ? std::numeric_limits<coord_t>::max() :
measure_perimeter_next_segment_length(poly_with_offset, segs, i_vline, intrsctn->iContour, i_intersection, iNext);
// Take the shorter path.
bool take_next = (iPrev != -1 && iNext != -1) ? (distNext < distPrev) : distNext != -1;
assert(intrsctn->is_inner());
polyline_current->points.push_back(Point(seg.pos, intrsctn->pos));
emit_perimeter_prev_next_segment(poly_with_offset, segs, i_vline, intrsctn->iContour, i_intersection, take_next ? iNext : iPrev, *polyline_current, take_next);
// Mark both the left and right connecting segment as consumed, because one cannot go to this intersection point as it has been consumed.
if (iPrev != -1)
segs[i_vline-1].intersections[iPrev].consumed_perimeter_right = true;
if (iNext != -1)
intrsctn->consumed_perimeter_right = true;
//FIXME consume the left / right connecting segments at the other end of this line? Currently it is not critical because a perimeter segment is not followed if the vertical segment at the other side has already been consumed.
// Advance to the neighbor line.
if (take_next) {
++ i_vline;
i_intersection = iNext;
} else {
-- i_vline;
i_intersection = iPrev;
}
continue;
}
// Take the complete line up to the outer contour.
if (going_up)
++ intrsctn;
else
-- intrsctn;
}
// Finish the current vertical line,
// reset the current vertical line to pick a new starting point in the next round.
assert(intrsctn->is_outer());
assert(intrsctn->is_high() == going_up);
pointLast = Point(seg.pos, intrsctn->pos);
polyline_current->points.push_back(pointLast);
intrsctn = NULL;
i_intersection = -1;
polyline_current = NULL;
}
// paths must be rotated back
for (Polylines::iterator it = polylines_out.begin(); it != polylines_out.end(); ++ it) {
// No need to translate, the absolute position is irrelevant.
// it->translate(- rotate_vector.second.x, - rotate_vector.second.y);
it->rotate(rotate_vector.first);
}
return polylines_out;
}
} // namespace Slic3r

View file

@ -0,0 +1,37 @@
#ifndef slic3r_FillRectilinear2_hpp_
#define slic3r_FillRectilinear2_hpp_
#include "../libslic3r.h"
#include "FillBase.hpp"
namespace Slic3r {
class Surface;
class FillRectilinear2 : public FillWithDirection
{
public:
virtual ~FillRectilinear2() {}
virtual Polylines fill_surface(const Surface *surface, const FillParams &params);
protected:
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;
};
class FillGrid2 : public FillRectilinear2
{
public:
virtual ~FillGrid2() {}
protected:
// The grid fill will keep the angle constant between the layers, see the implementation of Slic3r::Fill::Base.
virtual float _layer_angle(size_t idx) const { return 0.f; }
};
}; // namespace Slic3r
#endif // slic3r_FillRectilinear2_hpp_

View file

@ -11,12 +11,186 @@
#include <map>
#include <set>
#include <utility>
#include <stack>
#include <vector>
#ifdef SLIC3R_DEBUG
#include "SVG.hpp"
#endif
#ifdef SLIC3R_DEBUG
namespace boost { namespace polygon {
// The following code for the visualization of the boost Voronoi diagram is based on:
//
// Boost.Polygon library voronoi_graphic_utils.hpp header file
// Copyright Andrii Sydorchuk 2010-2012.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
template <typename CT>
class voronoi_visual_utils {
public:
// Discretize parabolic Voronoi edge.
// Parabolic Voronoi edges are always formed by one point and one segment
// from the initial input set.
//
// Args:
// point: input point.
// segment: input segment.
// max_dist: maximum discretization distance.
// discretization: point discretization of the given Voronoi edge.
//
// Template arguments:
// InCT: coordinate type of the input geometries (usually integer).
// Point: point type, should model point concept.
// Segment: segment type, should model segment concept.
//
// Important:
// discretization should contain both edge endpoints initially.
template <class InCT1, class InCT2,
template<class> class Point,
template<class> class Segment>
static
typename enable_if<
typename gtl_and<
typename gtl_if<
typename is_point_concept<
typename geometry_concept< Point<InCT1> >::type
>::type
>::type,
typename gtl_if<
typename is_segment_concept<
typename geometry_concept< Segment<InCT2> >::type
>::type
>::type
>::type,
void
>::type discretize(
const Point<InCT1>& point,
const Segment<InCT2>& segment,
const CT max_dist,
std::vector< Point<CT> >* discretization) {
// Apply the linear transformation to move start point of the segment to
// the point with coordinates (0, 0) and the direction of the segment to
// coincide the positive direction of the x-axis.
CT segm_vec_x = cast(x(high(segment))) - cast(x(low(segment)));
CT segm_vec_y = cast(y(high(segment))) - cast(y(low(segment)));
CT sqr_segment_length = segm_vec_x * segm_vec_x + segm_vec_y * segm_vec_y;
// Compute x-coordinates of the endpoints of the edge
// in the transformed space.
CT projection_start = sqr_segment_length *
get_point_projection((*discretization)[0], segment);
CT projection_end = sqr_segment_length *
get_point_projection((*discretization)[1], segment);
// Compute parabola parameters in the transformed space.
// Parabola has next representation:
// f(x) = ((x-rot_x)^2 + rot_y^2) / (2.0*rot_y).
CT point_vec_x = cast(x(point)) - cast(x(low(segment)));
CT point_vec_y = cast(y(point)) - cast(y(low(segment)));
CT rot_x = segm_vec_x * point_vec_x + segm_vec_y * point_vec_y;
CT rot_y = segm_vec_x * point_vec_y - segm_vec_y * point_vec_x;
// Save the last point.
Point<CT> last_point = (*discretization)[1];
discretization->pop_back();
// Use stack to avoid recursion.
std::stack<CT> point_stack;
point_stack.push(projection_end);
CT cur_x = projection_start;
CT cur_y = parabola_y(cur_x, rot_x, rot_y);
// Adjust max_dist parameter in the transformed space.
const CT max_dist_transformed = max_dist * max_dist * sqr_segment_length;
while (!point_stack.empty()) {
CT new_x = point_stack.top();
CT new_y = parabola_y(new_x, rot_x, rot_y);
// Compute coordinates of the point of the parabola that is
// furthest from the current line segment.
CT mid_x = (new_y - cur_y) / (new_x - cur_x) * rot_y + rot_x;
CT mid_y = parabola_y(mid_x, rot_x, rot_y);
// Compute maximum distance between the given parabolic arc
// and line segment that discretize it.
CT dist = (new_y - cur_y) * (mid_x - cur_x) -
(new_x - cur_x) * (mid_y - cur_y);
dist = dist * dist / ((new_y - cur_y) * (new_y - cur_y) +
(new_x - cur_x) * (new_x - cur_x));
if (dist <= max_dist_transformed) {
// Distance between parabola and line segment is less than max_dist.
point_stack.pop();
CT inter_x = (segm_vec_x * new_x - segm_vec_y * new_y) /
sqr_segment_length + cast(x(low(segment)));
CT inter_y = (segm_vec_x * new_y + segm_vec_y * new_x) /
sqr_segment_length + cast(y(low(segment)));
discretization->push_back(Point<CT>(inter_x, inter_y));
cur_x = new_x;
cur_y = new_y;
} else {
point_stack.push(mid_x);
}
}
// Update last point.
discretization->back() = last_point;
}
private:
// Compute y(x) = ((x - a) * (x - a) + b * b) / (2 * b).
static CT parabola_y(CT x, CT a, CT b) {
return ((x - a) * (x - a) + b * b) / (b + b);
}
// Get normalized length of the distance between:
// 1) point projection onto the segment
// 2) start point of the segment
// Return this length divided by the segment length. This is made to avoid
// sqrt computation during transformation from the initial space to the
// transformed one and vice versa. The assumption is made that projection of
// the point lies between the start-point and endpoint of the segment.
template <class InCT,
template<class> class Point,
template<class> class Segment>
static
typename enable_if<
typename gtl_and<
typename gtl_if<
typename is_point_concept<
typename geometry_concept< Point<int> >::type
>::type
>::type,
typename gtl_if<
typename is_segment_concept<
typename geometry_concept< Segment<long> >::type
>::type
>::type
>::type,
CT
>::type get_point_projection(
const Point<CT>& point, const Segment<InCT>& segment) {
CT segment_vec_x = cast(x(high(segment))) - cast(x(low(segment)));
CT segment_vec_y = cast(y(high(segment))) - cast(y(low(segment)));
CT point_vec_x = x(point) - cast(x(low(segment)));
CT point_vec_y = y(point) - cast(y(low(segment)));
CT sqr_segment_length =
segment_vec_x * segment_vec_x + segment_vec_y * segment_vec_y;
CT vec_dot = segment_vec_x * point_vec_x + segment_vec_y * point_vec_y;
return vec_dot / sqr_segment_length;
}
template <typename InCT>
static CT cast(const InCT& value) {
return static_cast<CT>(value);
}
};
} } // namespace boost::polygon
#endif
using namespace boost::polygon; // provides also high() and low()
namespace Slic3r { namespace Geometry {
@ -290,6 +464,294 @@ arrange(size_t total_parts, Pointf part, coordf_t dist, const BoundingBoxf* bb)
return positions;
}
#ifdef SLIC3R_DEBUG
// The following code for the visualization of the boost Voronoi diagram is based on:
//
// Boost.Polygon library voronoi_visualizer.cpp file
// Copyright Andrii Sydorchuk 2010-2012.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
namespace Voronoi { namespace Internal {
typedef double coordinate_type;
typedef boost::polygon::point_data<coordinate_type> point_type;
typedef boost::polygon::segment_data<coordinate_type> segment_type;
typedef boost::polygon::rectangle_data<coordinate_type> rect_type;
// typedef voronoi_builder<int> VB;
typedef boost::polygon::voronoi_diagram<coordinate_type> VD;
typedef VD::cell_type cell_type;
typedef VD::cell_type::source_index_type source_index_type;
typedef VD::cell_type::source_category_type source_category_type;
typedef VD::edge_type edge_type;
typedef VD::cell_container_type cell_container_type;
typedef VD::cell_container_type vertex_container_type;
typedef VD::edge_container_type edge_container_type;
typedef VD::const_cell_iterator const_cell_iterator;
typedef VD::const_vertex_iterator const_vertex_iterator;
typedef VD::const_edge_iterator const_edge_iterator;
static const std::size_t EXTERNAL_COLOR = 1;
inline void color_exterior(const VD::edge_type* edge)
{
if (edge->color() == EXTERNAL_COLOR)
return;
edge->color(EXTERNAL_COLOR);
edge->twin()->color(EXTERNAL_COLOR);
const VD::vertex_type* v = edge->vertex1();
if (v == NULL || !edge->is_primary())
return;
v->color(EXTERNAL_COLOR);
const VD::edge_type* e = v->incident_edge();
do {
color_exterior(e);
e = e->rot_next();
} while (e != v->incident_edge());
}
inline point_type retrieve_point(const std::vector<segment_type> &segments, const cell_type& cell)
{
assert(cell.source_category() == SOURCE_CATEGORY_SEGMENT_START_POINT || cell.source_category() == SOURCE_CATEGORY_SEGMENT_END_POINT);
return (cell.source_category() == SOURCE_CATEGORY_SEGMENT_START_POINT) ? low(segments[cell.source_index()]) : high(segments[cell.source_index()]);
}
inline void clip_infinite_edge(const std::vector<segment_type> &segments, const edge_type& edge, coordinate_type bbox_max_size, std::vector<point_type>* clipped_edge)
{
const cell_type& cell1 = *edge.cell();
const cell_type& cell2 = *edge.twin()->cell();
point_type origin, direction;
// Infinite edges could not be created by two segment sites.
if (cell1.contains_point() && cell2.contains_point()) {
point_type p1 = retrieve_point(segments, cell1);
point_type p2 = retrieve_point(segments, cell2);
origin.x((p1.x() + p2.x()) * 0.5);
origin.y((p1.y() + p2.y()) * 0.5);
direction.x(p1.y() - p2.y());
direction.y(p2.x() - p1.x());
} else {
origin = cell1.contains_segment() ? retrieve_point(segments, cell2) : retrieve_point(segments, cell1);
segment_type segment = cell1.contains_segment() ? segments[cell1.source_index()] : segments[cell2.source_index()];
coordinate_type dx = high(segment).x() - low(segment).x();
coordinate_type dy = high(segment).y() - low(segment).y();
if ((low(segment) == origin) ^ cell1.contains_point()) {
direction.x(dy);
direction.y(-dx);
} else {
direction.x(-dy);
direction.y(dx);
}
}
coordinate_type koef = bbox_max_size / (std::max)(fabs(direction.x()), fabs(direction.y()));
if (edge.vertex0() == NULL) {
clipped_edge->push_back(point_type(
origin.x() - direction.x() * koef,
origin.y() - direction.y() * koef));
} else {
clipped_edge->push_back(
point_type(edge.vertex0()->x(), edge.vertex0()->y()));
}
if (edge.vertex1() == NULL) {
clipped_edge->push_back(point_type(
origin.x() + direction.x() * koef,
origin.y() + direction.y() * koef));
} else {
clipped_edge->push_back(
point_type(edge.vertex1()->x(), edge.vertex1()->y()));
}
}
inline void sample_curved_edge(const std::vector<segment_type> &segments, const edge_type& edge, std::vector<point_type> &sampled_edge, coordinate_type max_dist)
{
point_type point = edge.cell()->contains_point() ?
retrieve_point(segments, *edge.cell()) :
retrieve_point(segments, *edge.twin()->cell());
segment_type segment = edge.cell()->contains_point() ?
segments[edge.twin()->cell()->source_index()] :
segments[edge.cell()->source_index()];
::boost::polygon::voronoi_visual_utils<coordinate_type>::discretize(point, segment, max_dist, &sampled_edge);
}
} /* namespace Internal */ } // namespace Voronoi
static inline void dump_voronoi_to_svg(const Lines &lines, /* const */ voronoi_diagram<double> &vd, const ThickPolylines *polylines, const char *path)
{
const double scale = 0.2;
const std::string inputSegmentPointColor = "lightseagreen";
const coord_t inputSegmentPointRadius = coord_t(0.09 * scale / SCALING_FACTOR);
const std::string inputSegmentColor = "lightseagreen";
const coord_t inputSegmentLineWidth = coord_t(0.03 * scale / SCALING_FACTOR);
const std::string voronoiPointColor = "black";
const coord_t voronoiPointRadius = coord_t(0.06 * scale / SCALING_FACTOR);
const std::string voronoiLineColorPrimary = "black";
const std::string voronoiLineColorSecondary = "green";
const std::string voronoiArcColor = "red";
const coord_t voronoiLineWidth = coord_t(0.02 * scale / SCALING_FACTOR);
const bool internalEdgesOnly = false;
const bool primaryEdgesOnly = false;
BoundingBox bbox = BoundingBox(lines);
bbox.min.x -= coord_t(1. / SCALING_FACTOR);
bbox.min.y -= coord_t(1. / SCALING_FACTOR);
bbox.max.x += coord_t(1. / SCALING_FACTOR);
bbox.max.y += coord_t(1. / SCALING_FACTOR);
::Slic3r::SVG svg(path, bbox);
if (polylines != NULL)
svg.draw(*polylines, "lime", "lime", voronoiLineWidth);
// bbox.scale(1.2);
// For clipping of half-lines to some reasonable value.
// The line will then be clipped by the SVG viewer anyway.
const double bbox_dim_max = double(bbox.max.x - bbox.min.x) + double(bbox.max.y - bbox.min.y);
// For the discretization of the Voronoi parabolic segments.
const double discretization_step = 0.0005 * bbox_dim_max;
// Make a copy of the input segments with the double type.
std::vector<Voronoi::Internal::segment_type> segments;
for (Lines::const_iterator it = lines.begin(); it != lines.end(); ++ it)
segments.push_back(Voronoi::Internal::segment_type(
Voronoi::Internal::point_type(double(it->a.x), double(it->a.y)),
Voronoi::Internal::point_type(double(it->b.x), double(it->b.y))));
// Color exterior edges.
for (voronoi_diagram<double>::const_edge_iterator it = vd.edges().begin(); it != vd.edges().end(); ++it)
if (!it->is_finite())
Voronoi::Internal::color_exterior(&(*it));
// Draw the end points of the input polygon.
for (Lines::const_iterator it = lines.begin(); it != lines.end(); ++it) {
svg.draw(it->a, inputSegmentPointColor, inputSegmentPointRadius);
svg.draw(it->b, inputSegmentPointColor, inputSegmentPointRadius);
}
// Draw the input polygon.
for (Lines::const_iterator it = lines.begin(); it != lines.end(); ++it)
svg.draw(Line(Point(coord_t(it->a.x), coord_t(it->a.y)), Point(coord_t(it->b.x), coord_t(it->b.y))), inputSegmentColor, inputSegmentLineWidth);
#if 1
// Draw voronoi vertices.
for (voronoi_diagram<double>::const_vertex_iterator it = vd.vertices().begin(); it != vd.vertices().end(); ++it)
if (! internalEdgesOnly || it->color() != Voronoi::Internal::EXTERNAL_COLOR)
svg.draw(Point(coord_t(it->x()), coord_t(it->y())), voronoiPointColor, voronoiPointRadius);
for (voronoi_diagram<double>::const_edge_iterator it = vd.edges().begin(); it != vd.edges().end(); ++it) {
if (primaryEdgesOnly && !it->is_primary())
continue;
if (internalEdgesOnly && (it->color() == Voronoi::Internal::EXTERNAL_COLOR))
continue;
std::vector<Voronoi::Internal::point_type> samples;
std::string color = voronoiLineColorPrimary;
if (!it->is_finite()) {
Voronoi::Internal::clip_infinite_edge(segments, *it, bbox_dim_max, &samples);
if (! it->is_primary())
color = voronoiLineColorSecondary;
} else {
// Store both points of the segment into samples. sample_curved_edge will split the initial line
// until the discretization_step is reached.
samples.push_back(Voronoi::Internal::point_type(it->vertex0()->x(), it->vertex0()->y()));
samples.push_back(Voronoi::Internal::point_type(it->vertex1()->x(), it->vertex1()->y()));
if (it->is_curved()) {
Voronoi::Internal::sample_curved_edge(segments, *it, samples, discretization_step);
color = voronoiArcColor;
} else if (! it->is_primary())
color = voronoiLineColorSecondary;
}
for (std::size_t i = 0; i + 1 < samples.size(); ++i)
svg.draw(Line(Point(coord_t(samples[i].x()), coord_t(samples[i].y())), Point(coord_t(samples[i+1].x()), coord_t(samples[i+1].y()))), color, voronoiLineWidth);
}
#endif
if (polylines != NULL)
svg.draw(*polylines, "blue", voronoiLineWidth);
svg.Close();
}
#endif /* SLIC3R_DEBUG */
// Euclidian distance of two boost::polygon points.
template<typename T>
T dist(const boost::polygon::point_data<T> &p1,const boost::polygon::point_data<T> &p2)
{
T dx = p2.x() - p1.x();
T dy = p2.y() - p1.y();
return sqrt(dx*dx+dy*dy);
}
// Find a foot point of "px" on a segment "seg".
template<typename segment_type, typename point_type>
inline point_type project_point_to_segment(segment_type &seg, point_type &px)
{
typedef typename point_type::coordinate_type T;
const point_type &p0 = low(seg);
const point_type &p1 = high(seg);
const point_type dir(p1.x()-p0.x(), p1.y()-p0.y());
const point_type dproj(px.x()-p0.x(), px.y()-p0.y());
const T t = (dir.x()*dproj.x() + dir.y()*dproj.y()) / (dir.x()*dir.x() + dir.y()*dir.y());
assert(t >= T(-1e-6) && t <= T(1. + 1e-6));
return point_type(p0.x() + t*dir.x(), p0.y() + t*dir.y());
}
template<typename VD, typename SEGMENTS>
inline const typename VD::point_type retrieve_cell_point(const typename VD::cell_type& cell, const SEGMENTS &segments)
{
assert(cell.source_category() == SOURCE_CATEGORY_SEGMENT_START_POINT || cell.source_category() == SOURCE_CATEGORY_SEGMENT_END_POINT);
return (cell.source_category() == SOURCE_CATEGORY_SEGMENT_START_POINT) ? low(segments[cell.source_index()]) : high(segments[cell.source_index()]);
}
template<typename VD, typename SEGMENTS>
inline std::pair<typename VD::coord_type, typename VD::coord_type>
measure_edge_thickness(const VD &vd, const typename VD::edge_type& edge, const SEGMENTS &segments)
{
typedef typename VD::coord_type T;
const typename VD::point_type pa(edge.vertex0()->x(), edge.vertex0()->y());
const typename VD::point_type pb(edge.vertex1()->x(), edge.vertex1()->y());
const typename VD::cell_type &cell1 = *edge.cell();
const typename VD::cell_type &cell2 = *edge.twin()->cell();
if (cell1.contains_segment()) {
if (cell2.contains_segment()) {
// Both cells contain a linear segment, the left / right cells are symmetric.
// Project pa, pb to the left segment.
const typename VD::segment_type segment1 = segments[cell1.source_index()];
const typename VD::point_type p1a = project_point_to_segment(segment1, pa);
const typename VD::point_type p1b = project_point_to_segment(segment1, pb);
return std::pair<T, T>(T(2.)*dist(pa, p1a), T(2.)*dist(pb, p1b));
} else {
// 1st cell contains a linear segment, 2nd cell contains a point.
// The medial axis between the cells is a parabolic arc.
// Project pa, pb to the left segment.
const typename VD::point_type p2 = retrieve_cell_point<VD>(cell2, segments);
return std::pair<T, T>(T(2.)*dist(pa, p2), T(2.)*dist(pb, p2));
}
} else if (cell2.contains_segment()) {
// 1st cell contains a point, 2nd cell contains a linear segment.
// The medial axis between the cells is a parabolic arc.
const typename VD::point_type p1 = retrieve_cell_point<VD>(cell1, segments);
return std::pair<T, T>(T(2.)*dist(pa, p1), T(2.)*dist(pb, p1));
} else {
// Both cells contain a point. The left / right regions are triangular and symmetric.
const typename VD::point_type p1 = retrieve_cell_point<VD>(cell1, segments);
return std::pair<T, T>(T(2.)*dist(pa, p1), T(2.)*dist(pb, p1));
}
}
// Converts the Line instances of Lines vector to VD::segment_type.
template<typename VD>
class Lines2VDSegments
{
public:
Lines2VDSegments(const Lines &alines) : lines(alines) {}
typename VD::segment_type operator[](size_t idx) const {
return typename VD::segment_type(
typename VD::point_type(typename VD::coord_type(lines[idx].a.x), typename VD::coord_type(lines[idx].a.y)),
typename VD::point_type(typename VD::coord_type(lines[idx].b.x), typename VD::coord_type(lines[idx].b.y)));
}
private:
const Lines &lines;
};
void
MedialAxis::build(ThickPolylines* polylines)
{
@ -373,6 +835,25 @@ MedialAxis::build(ThickPolylines* polylines)
// append polyline to result
polylines->push_back(polyline);
}
#ifdef SLIC3R_DEBUG
{
char path[2048];
static int iRun = 0;
sprintf(path, "out/MedialAxis-%d.svg", iRun ++);
dump_voronoi_to_svg(this->lines, this->vd, polylines, path);
printf("Thick lines: ");
for (ThickPolylines::const_iterator it = polylines->begin(); it != polylines->end(); ++ it) {
ThickLines lines = it->thicklines();
for (ThickLines::const_iterator it2 = lines.begin(); it2 != lines.end(); ++ it2) {
printf("%f,%f ", it2->a_width, it2->b_width);
}
}
printf("\n");
}
#endif /* SLIC3R_DEBUG */
}
void

View file

@ -52,7 +52,13 @@ class MedialAxis {
void build(Polylines* polylines);
private:
typedef voronoi_diagram<double> VD;
class VD : public voronoi_diagram<double> {
public:
typedef double coord_type;
typedef boost::polygon::point_data<coordinate_type> point_type;
typedef boost::polygon::segment_data<coordinate_type> segment_type;
typedef boost::polygon::rectangle_data<coordinate_type> rect_type;
};
VD vd;
std::set<const VD::edge_type*> edges, valid_edges;
std::map<const VD::edge_type*, std::pair<coordf_t,coordf_t> > thickness;

View file

@ -112,7 +112,7 @@ LayerRegion::process_external_surfaces(const Layer* lower_layer)
);
#ifdef SLIC3R_DEBUG
printf("Processing bridge at layer %zu:\n", this->layer()->id();
printf("Processing bridge at layer %zu:\n", this->layer()->id());
#endif
if (bd.detect_angle()) {

View file

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

View file

@ -22,6 +22,7 @@ class MultiPoint
void scale(double factor);
void translate(double x, double y);
void translate(const Point &vector);
void rotate(double angle);
void rotate(double angle, const Point &center);
void reverse();
Point first_point() const;
@ -32,7 +33,10 @@ class MultiPoint
int find_point(const Point &point) const;
bool has_boundary_point(const Point &point) const;
BoundingBox bounding_box() const;
void remove_duplicate_points();
// Return true if there are exact duplicates.
bool has_duplicate_points() const;
// Remove exact duplicates, return true if any duplicate has been removed.
bool remove_duplicate_points();
void append(const Point &point);
void append(const Points &points);
void append(const Points::const_iterator &begin, const Points::const_iterator &end);

View file

@ -54,13 +54,26 @@ Point::translate(const Vector &vector)
this->translate(vector.x, vector.y);
}
void
Point::rotate(double angle)
{
double cur_x = (double)this->x;
double cur_y = (double)this->y;
double s = sin(angle);
double c = cos(angle);
this->x = (coord_t)round(c * cur_x - s * cur_y);
this->y = (coord_t)round(c * cur_y + s * cur_x);
}
void
Point::rotate(double angle, const Point &center)
{
double cur_x = (double)this->x;
double cur_y = (double)this->y;
this->x = (coord_t)round( (double)center.x + cos(angle) * (cur_x - (double)center.x) - sin(angle) * (cur_y - (double)center.y) );
this->y = (coord_t)round( (double)center.y + cos(angle) * (cur_y - (double)center.y) + sin(angle) * (cur_x - (double)center.x) );
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) );
}
bool
@ -301,6 +314,12 @@ operator+(const Point& point1, const Point& point2)
return Point(point1.x + point2.x, point1.y + point2.y);
}
Point
operator-(const Point& point1, const Point& point2)
{
return Point(point1.x - point2.x, point1.y - point2.y);
}
Point
operator*(double scalar, const Point& point2)
{
@ -349,13 +368,26 @@ Pointf::translate(const Vectorf &vector)
this->translate(vector.x, vector.y);
}
void
Pointf::rotate(double angle)
{
double cur_x = this->x;
double cur_y = this->y;
double s = sin(angle);
double c = cos(angle);
this->x = c * cur_x - s * cur_y;
this->y = c * cur_y + s * cur_x;
}
void
Pointf::rotate(double angle, const Pointf &center)
{
double cur_x = this->x;
double cur_y = this->y;
this->x = center.x + cos(angle) * (cur_x - center.x) - sin(angle) * (cur_y - center.y);
this->y = center.y + cos(angle) * (cur_y - center.y) + sin(angle) * (cur_x - center.x);
double s = sin(angle);
double c = cos(angle);
this->x = center.x + c * (cur_x - center.x) - s * (cur_y - center.y);
this->y = center.y + c * (cur_y - center.y) + s * (cur_x - center.x);
}
Pointf

View file

@ -42,6 +42,7 @@ class Point
void scale(double factor);
void translate(double x, double y);
void translate(const Vector &vector);
void rotate(double angle);
void rotate(double angle, const Point &center);
bool coincides_with(const Point &point) const;
bool coincides_with_epsilon(const Point &point) const;
@ -64,6 +65,7 @@ class Point
};
Point operator+(const Point& point1, const Point& point2);
Point operator-(const Point& point1, const Point& point2);
Point operator*(double scalar, const Point& point2);
class Point3 : public Point
@ -92,6 +94,7 @@ class Pointf
void scale(double factor);
void translate(double x, double y);
void translate(const Vectorf &vector);
void rotate(double angle);
void rotate(double angle, const Pointf &center);
Pointf negative() const;
Vectorf vector_to(const Pointf &point) const;

View file

@ -2,50 +2,122 @@
namespace Slic3r {
void
PolylineCollection::chained_path(PolylineCollection* retval, bool no_reverse) const
struct Chaining
{
if (this->polylines.empty()) return;
this->chained_path_from(this->polylines.front().first_point(), retval, no_reverse);
}
Point first;
Point last;
size_t idx;
};
void
PolylineCollection::chained_path_from(Point start_near, PolylineCollection* retval, bool no_reverse) const
#ifndef sqr
template<typename T>
inline T sqr(T x) { return x * x; }
#endif /* sqr */
template<typename T>
inline int nearest_point_index(const std::vector<Chaining> &pairs, const Point &start_near, bool no_reverse)
{
Polylines my_paths = this->polylines;
Points endpoints;
for (Polylines::const_iterator it = my_paths.begin(); it != my_paths.end(); ++it) {
endpoints.push_back(it->first_point());
if (no_reverse) {
endpoints.push_back(it->first_point());
} else {
endpoints.push_back(it->last_point());
T dmin = std::numeric_limits<T>::max();
int idx = 0;
for (std::vector<Chaining>::const_iterator it = pairs.begin(); it != pairs.end(); ++it) {
T d = sqr(T(start_near.x - it->first.x));
if (d <= dmin) {
d += sqr(T(start_near.y - it->first.y));
if (d < dmin) {
idx = (it - pairs.begin()) * 2;
dmin = d;
if (dmin < EPSILON)
break;
}
}
if (! no_reverse) {
d = sqr(T(start_near.x - it->last.x));
if (d <= dmin) {
d += sqr(T(start_near.y - it->last.y));
if (d < dmin) {
idx = (it - pairs.begin()) * 2 + 1;
dmin = d;
if (dmin < EPSILON)
break;
}
}
}
}
while (!my_paths.empty()) {
return idx;
}
Polylines PolylineCollection::chained_path_from(
#if SLIC3R_CPPVER > 11
Polylines &&src,
#else
const Polylines &src,
#endif
Point start_near,
bool no_reverse)
{
std::vector<Chaining> endpoints;
endpoints.reserve(src.size());
for (size_t i = 0; i < src.size(); ++ i) {
Chaining c;
c.first = src[i].first_point();
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 start_index = start_near.nearest_point_index(endpoints);
int path_index = start_index/2;
if (start_index % 2 && !no_reverse) {
my_paths.at(path_index).reverse();
}
retval->polylines.push_back(my_paths.at(path_index));
my_paths.erase(my_paths.begin() + path_index);
endpoints.erase(endpoints.begin() + 2*path_index, endpoints.begin() + 2*path_index + 2);
start_near = retval->polylines.back().last_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]));
#else
retval.push_back(src[endpoints[endpoint_index/2].idx]);
#endif
if (endpoint_index & 1)
retval.back().reverse();
endpoints.erase(endpoints.begin() + endpoint_index/2);
start_near = retval.back().last_point();
}
return retval;
}
Point
PolylineCollection::leftmost_point() const
#if SLIC3R_CPPVER > 11
Polylines PolylineCollection::chained_path(Polylines &&src, bool no_reverse)
{
if (this->polylines.empty()) CONFESS("leftmost_point() called on empty PolylineCollection");
Point p = this->polylines.front().leftmost_point();
for (Polylines::const_iterator it = this->polylines.begin() + 1; it != this->polylines.end(); ++it) {
return (src.empty() || src.front().empty()) ?
Polylines() :
chained_path_from(std::move(src), src.front().first_point(), 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);
}
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
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);
}
#endif
Point PolylineCollection::leftmost_point(const Polylines &polylines)
{
if (polylines.empty()) CONFESS("leftmost_point() called on empty PolylineCollection");
Polylines::const_iterator it = polylines.begin();
Point p = it->leftmost_point();
for (++ it; it != polylines.end(); ++it) {
Point p2 = it->leftmost_point();
if (p2.x < p.x) p = p2;
if (p2.x < p.x)
p = p2;
}
return p;
}
@ -56,4 +128,4 @@ PolylineCollection::append(const Polylines &pp)
this->polylines.insert(this->polylines.end(), pp.begin(), pp.end());
}
}
} // namespace Slic3r

View file

@ -8,12 +8,26 @@ namespace Slic3r {
class PolylineCollection
{
public:
public:
Polylines polylines;
void chained_path(PolylineCollection* retval, bool no_reverse = false) const;
void chained_path_from(Point start_near, PolylineCollection* retval, bool no_reverse = false) const;
Point leftmost_point() const;
void chained_path(PolylineCollection* retval, bool no_reverse = false) const
{ retval->polylines = chained_path(this->polylines, no_reverse); }
void chained_path_from(Point start_near, PolylineCollection* retval, bool no_reverse = false) const
{ retval->polylines = chained_path_from(this->polylines, start_near, no_reverse); }
Point leftmost_point() const
{ return leftmost_point(polylines); }
void append(const Polylines &polylines);
static Point leftmost_point(const Polylines &polylines);
#if SLIC3R_CPPVER > 11
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
};
}

View file

@ -19,18 +19,54 @@ SVG::SVG(const char* filename)
);
}
SVG::SVG(const char* filename, const BoundingBox &bbox)
: arrows(false), fill("grey"), stroke("black"), filename(filename), origin(bbox.min)
{
this->f = fopen(filename, "w");
float w = COORD(bbox.max.x - bbox.min.x);
float h = COORD(bbox.max.y - bbox.min.y);
fprintf(this->f,
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n"
"<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.0//EN\" \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n"
"<svg height=\"%f\" width=\"%f\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:svg=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n"
" <marker id=\"endArrow\" markerHeight=\"8\" markerUnits=\"strokeWidth\" markerWidth=\"10\" orient=\"auto\" refX=\"1\" refY=\"5\" viewBox=\"0 0 10 10\">\n"
" <polyline fill=\"darkblue\" points=\"0,0 10,5 0,10 1,5\" />\n"
" </marker>\n",
h, w);
}
void
SVG::draw(const Line &line, std::string stroke)
SVG::draw(const Line &line, std::string stroke, coord_t stroke_width)
{
fprintf(this->f,
" <line x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\" style=\"stroke: %s; stroke-width: 1\"",
COORD(line.a.x), COORD(line.a.y), COORD(line.b.x), COORD(line.b.y), stroke.c_str()
);
" <line x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\" style=\"stroke: %s; stroke-width: %f\"",
COORD(line.a.x - origin.x), COORD(line.a.y - origin.y), COORD(line.b.x - origin.x), COORD(line.b.y - origin.y), stroke.c_str(), (stroke_width == 0) ? 1.f : COORD(stroke_width));
if (this->arrows)
fprintf(this->f, " marker-end=\"url(#endArrow)\"");
fprintf(this->f, "/>\n");
}
void SVG::draw(const ThickLine &line, const std::string &fill, const std::string &stroke, coord_t stroke_width)
{
Pointf dir(line.b.x-line.a.x, line.b.y-line.a.y);
Pointf perp(-dir.y, dir.x);
coordf_t len = sqrt(perp.x*perp.x + perp.y*perp.y);
coordf_t da = coordf_t(0.5)*line.a_width/len;
coordf_t db = coordf_t(0.5)*line.b_width/len;
fprintf(this->f,
" <polygon points=\"%f,%f %f,%f %f,%f %f,%f\" style=\"fill:%s; stroke: %s; stroke-width: %f\"/>\n",
COORD(line.a.x-da*perp.x-origin.x),
COORD(line.a.y-da*perp.y-origin.y),
COORD(line.b.x-db*perp.x-origin.x),
COORD(line.b.y-db*perp.y-origin.y),
COORD(line.b.x+db*perp.x-origin.x),
COORD(line.b.y+db*perp.y-origin.y),
COORD(line.a.x+da*perp.x-origin.x),
COORD(line.a.y+da*perp.y-origin.y),
fill.c_str(), stroke.c_str(),
(stroke_width == 0) ? 1.f : COORD(stroke_width));
}
void
SVG::draw(const Lines &lines, std::string stroke)
{
@ -80,31 +116,45 @@ SVG::draw(const Polygons &polygons, std::string fill)
}
void
SVG::draw(const Polyline &polyline, std::string stroke)
SVG::draw(const Polyline &polyline, std::string stroke, coord_t stroke_width)
{
this->stroke = stroke;
this->path(this->get_path_d(polyline, false), false);
this->path(this->get_path_d(polyline, false), false, stroke_width);
}
void
SVG::draw(const Polylines &polylines, std::string stroke)
SVG::draw(const Polylines &polylines, std::string stroke, coord_t stroke_width)
{
for (Polylines::const_iterator it = polylines.begin(); it != polylines.end(); ++it)
this->draw(*it, stroke);
this->draw(*it, fill, stroke_width);
}
void SVG::draw(const ThickLines &thicklines, const std::string &fill, const std::string &stroke, coord_t stroke_width)
{
for (ThickLines::const_iterator it = thicklines.begin(); it != thicklines.end(); ++it)
this->draw(*it, fill, stroke, stroke_width);
}
void
SVG::draw(const ThickPolylines &polylines, std::string stroke)
SVG::draw(const ThickPolylines &polylines, const std::string &stroke, coord_t stroke_width)
{
for (ThickPolylines::const_iterator it = polylines.begin(); it != polylines.end(); ++it)
this->draw((Polyline)*it, stroke);
this->draw((Polyline)*it, stroke, stroke_width);
}
void
SVG::draw(const ThickPolylines &thickpolylines, const std::string &fill, const std::string &stroke, coord_t stroke_width)
{
for (ThickPolylines::const_iterator it = thickpolylines.begin(); it != thickpolylines.end(); ++ it)
draw(it->thicklines(), fill, stroke, stroke_width);
}
void
SVG::draw(const Point &point, std::string fill, unsigned int radius)
SVG::draw(const Point &point, std::string fill, coord_t iradius)
{
float radius = (iradius == 0) ? 3.f : COORD(iradius);
std::ostringstream svg;
svg << " <circle cx=\"" << COORD(point.x) << "\" cy=\"" << COORD(point.y)
svg << " <circle cx=\"" << COORD(point.x - origin.x) << "\" cy=\"" << COORD(point.y - origin.y)
<< "\" r=\"" << radius << "\" "
<< "style=\"stroke: none; fill: " << fill << "\" />";
@ -112,22 +162,26 @@ SVG::draw(const Point &point, std::string fill, unsigned int radius)
}
void
SVG::draw(const Points &points, std::string fill, unsigned int radius)
SVG::draw(const Points &points, std::string fill, coord_t radius)
{
for (Points::const_iterator it = points.begin(); it != points.end(); ++it)
this->draw(*it, fill, radius);
}
void
SVG::path(const std::string &d, bool fill)
SVG::path(const std::string &d, bool fill, coord_t stroke_width)
{
float lineWidth = 0.f;
if (! fill)
lineWidth = (stroke_width == 0) ? 2.f : COORD(stroke_width);
fprintf(
this->f,
" <path d=\"%s\" style=\"fill: %s; stroke: %s; stroke-width: %s; fill-type: evenodd\" %s />\n",
" <path d=\"%s\" style=\"fill: %s; stroke: %s; stroke-width: %f; fill-type: evenodd\" %s />\n",
d.c_str(),
fill ? this->fill.c_str() : "none",
this->stroke.c_str(),
fill ? "0" : "2",
lineWidth,
(this->arrows && !fill) ? " marker-end=\"url(#endArrow)\"" : ""
);
}
@ -138,8 +192,8 @@ SVG::get_path_d(const MultiPoint &mp, bool closed) const
std::ostringstream d;
d << "M ";
for (Points::const_iterator p = mp.points.begin(); p != mp.points.end(); ++p) {
d << COORD(p->x) << " ";
d << COORD(p->y) << " ";
d << COORD(p->x - origin.x) << " ";
d << COORD(p->y - origin.y) << " ";
}
if (closed) d << "z";
return d.str();
@ -150,6 +204,7 @@ SVG::Close()
{
fprintf(this->f, "</svg>\n");
fclose(this->f);
this->f = NULL;
printf("SVG written to %s\n", this->filename.c_str());
}

View file

@ -13,27 +13,34 @@ class SVG
public:
bool arrows;
std::string fill, stroke;
Point origin;
SVG(const char* filename);
void draw(const Line &line, std::string stroke = "black");
SVG(const char* filename, const BoundingBox &bbox);
~SVG() { if (f != NULL) Close(); }
void draw(const Line &line, std::string stroke = "black", coord_t stroke_width = 0);
void draw(const ThickLine &line, const std::string &fill, const std::string &stroke, coord_t stroke_width = 0);
void draw(const Lines &lines, std::string stroke = "black");
void draw(const IntersectionLines &lines, std::string stroke = "black");
void draw(const ExPolygon &expolygon, std::string fill = "grey");
void draw(const ExPolygons &expolygons, std::string fill = "grey");
void draw(const Polygon &polygon, std::string fill = "grey");
void draw(const Polygons &polygons, std::string fill = "grey");
void draw(const Polyline &polyline, std::string stroke = "black");
void draw(const Polylines &polylines, std::string stroke = "black");
void draw(const ThickPolylines &polylines, std::string stroke = "black");
void draw(const Point &point, std::string fill = "black", unsigned int radius = 3);
void draw(const Points &points, std::string fill = "black", unsigned int radius = 3);
void draw(const Polyline &polyline, std::string stroke = "black", coord_t stroke_width = 0);
void draw(const Polylines &polylines, std::string stroke = "black", coord_t stroke_width = 0);
void draw(const ThickLines &thicklines, const std::string &fill = "lime", const std::string &stroke = "black", coord_t stroke_width = 0);
void draw(const ThickPolylines &polylines, const std::string &stroke = "black", coord_t stroke_width = 0);
void draw(const ThickPolylines &thickpolylines, const std::string &fill, const std::string &stroke, coord_t stroke_width);
void draw(const Point &point, std::string fill = "black", coord_t radius = 0);
void draw(const Points &points, std::string fill = "black", coord_t radius = 0);
void Close();
private:
std::string filename;
FILE* f;
void path(const std::string &d, bool fill);
void path(const std::string &d, bool fill, coord_t stroke_width = 0);
std::string get_path_d(const MultiPoint &mp, bool closed = false) const;
};

View file

@ -13,6 +13,7 @@
#include <assert.h>
#ifdef SLIC3R_DEBUG
// #define SLIC3R_TRIANGLEMESH_DEBUG
#include "SVG.hpp"
#endif
@ -445,7 +446,7 @@ TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<Polygons>* la
float min_z = fminf(facet->vertex[0].z, fminf(facet->vertex[1].z, facet->vertex[2].z));
float max_z = fmaxf(facet->vertex[0].z, fmaxf(facet->vertex[1].z, facet->vertex[2].z));
#ifdef SLIC3R_DEBUG
#ifdef SLIC3R_TRIANGLEMESH_DEBUG
printf("\n==> FACET %d (%f,%f,%f - %f,%f,%f - %f,%f,%f):\n", facet_idx,
facet->vertex[0].x, facet->vertex[0].y, facet->vertex[0].z,
facet->vertex[1].x, facet->vertex[1].y, facet->vertex[1].z,
@ -457,7 +458,7 @@ TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<Polygons>* la
std::vector<float>::const_iterator min_layer, max_layer;
min_layer = std::lower_bound(z.begin(), z.end(), min_z); // first layer whose slice_z is >= min_z
max_layer = std::upper_bound(z.begin() + (min_layer - z.begin()), z.end(), max_z) - 1; // last layer whose slice_z is <= max_z
#ifdef SLIC3R_DEBUG
#ifdef SLIC3R_TRIANGLEMESH_DEBUG
printf("layers: min = %d, max = %d\n", (int)(min_layer - z.begin()), (int)(max_layer - z.begin()));
#endif
@ -473,7 +474,7 @@ TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<Polygons>* la
layers->resize(z.size());
for (std::vector<IntersectionLines>::iterator it = lines.begin(); it != lines.end(); ++it) {
size_t layer_idx = it - lines.begin();
#ifdef SLIC3R_DEBUG
#ifdef SLIC3R_TRIANGLEMESH_DEBUG
printf("Layer %zu:\n", layer_idx);
#endif
this->make_loops(*it, &(*layers)[layer_idx]);
@ -488,7 +489,7 @@ TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<ExPolygons>*
layers->resize(z.size());
for (std::vector<Polygons>::const_iterator loops = layers_p.begin(); loops != layers_p.end(); ++loops) {
#ifdef SLIC3R_DEBUG
#ifdef SLIC3R_TRIANGLEMESH_DEBUG
size_t layer_id = loops - layers_p.begin();
printf("Layer %zu (slice_z = %.2f):\n", layer_id, z[layer_id]);
#endif
@ -712,7 +713,7 @@ TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygons* l
}
loops->push_back(p);
#ifdef SLIC3R_DEBUG
#ifdef SLIC3R_TRIANGLEMESH_DEBUG
printf(" Discovered %s polygon of %d points\n", (p.is_counter_clockwise() ? "ccw" : "cw"), (int)p.points.size());
#endif
@ -721,7 +722,7 @@ TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygons* l
// we can't close this loop!
//// push @failed_loops, [@loop];
//#ifdef SLIC3R_DEBUG
//#ifdef SLIC3R_TRIANGLEMESH_DEBUG
printf(" Unable to close this loop having %d points\n", (int)loop.size());
//#endif
goto CYCLE;
@ -833,7 +834,7 @@ TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slices)
ExPolygons ex_slices;
offset2(p_slices, &ex_slices, +safety_offset, -safety_offset);
#ifdef SLIC3R_DEBUG
#ifdef SLIC3R_TRIANGLEMESH_DEBUG
size_t holes_count = 0;
for (ExPolygons::const_iterator e = ex_slices.begin(); e != ex_slices.end(); ++e) {
holes_count += e->holes.size();
@ -1052,7 +1053,7 @@ TriangleMeshSlicer::TriangleMeshSlicer(TriangleMesh* _mesh) : mesh(_mesh), v_sca
}
this->facets_edges[facet_idx][i] = edge_idx;
#ifdef SLIC3R_DEBUG
#ifdef SLIC3R_TRIANGLEMESH_DEBUG
printf(" [facet %d, edge %d] a_id = %d, b_id = %d --> edge %d\n", facet_idx, i, a_id, b_id, edge_idx);
#endif
}

View file

@ -39,4 +39,14 @@ using namespace Slic3r;
void confess_at(const char *file, int line, const char *func, const char *pat, ...);
/* End implementation of CONFESS("foo"): */
// Which C++ version is supported?
// For example, could optimized functions with move semantics be used?
#if __cplusplus==201402L
#define SLIC3R_CPPVER 14
#elif __cplusplus==201103L
#define SLIC3R_CPPVER 11
#else
#define SLIC3R_CPPVER 0
#endif
#endif

View file

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

View file

@ -1,5 +1,7 @@
#include "3DScene.hpp"
#include <assert.h>
namespace Slic3r {
// caller is responsible for supplying NO lines with zero length
@ -15,6 +17,10 @@ _3DScene::_extrusionentity_to_verts_do(const Lines &lines, const std::vector<dou
// two triangles for each corner
tverts->reserve_more(3 * 3 * 2 * (lines.size() + 1));
*/
assert(! lines.empty());
if (lines.empty())
return;
Line prev_line;
Pointf prev_b1, prev_b2;

View file

@ -36,4 +36,23 @@ enable_screensaver()
#endif
}
bool
debugged()
{
#ifdef _WIN32
return IsDebuggerPresent();
#else
return false;
#endif /* _WIN32 */
}
void
break_to_debugger()
{
#ifdef _WIN32
if (IsDebuggerPresent())
DebugBreak();
#endif /* _WIN32 */
}
} }

View file

@ -5,6 +5,8 @@ namespace Slic3r { namespace GUI {
void disable_screensaver();
void enable_screensaver();
bool debugged();
void break_to_debugger();
} }

View file

@ -0,0 +1,50 @@
%module{Slic3r::XS};
%{
#include <xsinit.h>
#include "libslic3r/ExtrusionSimulator.hpp"
%}
%name{Slic3r::ExtrusionSimulator} class ExtrusionSimulator {
~ExtrusionSimulator();
%name{_new} ExtrusionSimulator();
Clone<ExtrusionSimulator> clone()
%code{% RETVAL = THIS; %};
void set_image_size(Point *image_size)
%code{% THIS->set_image_size(*image_size); %};
void set_viewport(BoundingBox *viewport)
%code{% THIS->set_viewport(*viewport); %};
void set_bounding_box(BoundingBox *bbox)
%code{% THIS->set_bounding_box(*bbox); %};
void reset_accumulator();
void extrude_to_accumulator(ExtrusionPath *path, Point *shift, ExtrusionSimulationType simulationType)
%code{% THIS->extrude_to_accumulator(*path, *shift, simulationType); %};
void evaluate_accumulator(ExtrusionSimulationType simulationType);
void* image_ptr()
%code{% RETVAL = const_cast<void*>(const_cast<Slic3r::ExtrusionSimulator*>(THIS)->image_ptr()); %};
%{
%}
};
%package{Slic3r::ExtrusionSimulator};
%{
IV
_constant()
ALIAS:
EXTRSIM_SIMPLE = ExtrusionSimulationSimple
EXTRSIM_DONT_SPREAD = ExtrusionSimulationDontSpread
EXTRSIM_SPREAD_NFULL = ExtrisopmSimulationSpreadNotOverfilled
EXTRSIM_SPREAD_FULL = ExtrusionSimulationSpreadFull
EXTRSIM_SPREAD_EXCESS = ExtrusionSimulationSpreadExcess
PROTOTYPE:
CODE:
RETVAL = ix;
OUTPUT: RETVAL
%}

68
xs/xsp/Filler.xsp Normal file
View file

@ -0,0 +1,68 @@
%module{Slic3r::XS};
%{
#include <xsinit.h>
#include "libslic3r/Fill/FillBase.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_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_width(float width)
%code{% THIS->params.width = width; %};
void set_density(float density)
%code{% THIS->params.density = density; %};
void set_distance(float distance)
%code{% THIS->params.distance = distance; %};
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
%}
};

View file

@ -13,3 +13,9 @@ void disable_screensaver()
void enable_screensaver()
%code{% Slic3r::GUI::enable_screensaver(); %};
bool debugged()
%code{% RETVAL=Slic3r::GUI::debugged(); %};
void break_to_debugger()
%code{% Slic3r::GUI::break_to_debugger(); %};

View file

@ -112,6 +112,14 @@ ExtrusionLoop* O_OBJECT_SLIC3R
Ref<ExtrusionLoop> O_OBJECT_SLIC3R_T
Clone<ExtrusionLoop> O_OBJECT_SLIC3R_T
ExtrusionSimulator* O_OBJECT_SLIC3R
Ref<ExtrusionSimulator> O_OBJECT_SLIC3R_T
Clone<ExtrusionSimulator> 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
@ -214,6 +222,7 @@ GLVertexArray* O_OBJECT_SLIC3R
Axis T_UV
ExtrusionLoopRole T_UV
ExtrusionRole T_UV
ExtrusionSimulationType T_UV
FlowRole T_UV
PrintStep T_UV
PrintObjectStep T_UV

View file

@ -57,6 +57,9 @@
%typemap{ExPolygonCollection*};
%typemap{Ref<ExPolygonCollection>}{simple};
%typemap{Clone<ExPolygonCollection>}{simple};
%typemap{Filler*};
%typemap{Ref<Filler>}{simple};
%typemap{Clone<Filler>}{simple};
%typemap{Flow*};
%typemap{Ref<Flow>}{simple};
%typemap{Clone<Flow>}{simple};
@ -81,6 +84,9 @@
%typemap{ExtrusionLoop*};
%typemap{Ref<ExtrusionLoop>}{simple};
%typemap{Clone<ExtrusionLoop>}{simple};
%typemap{ExtrusionSimulator*};
%typemap{Ref<ExtrusionSimulator>}{simple};
%typemap{Clone<ExtrusionSimulator>}{simple};
%typemap{TriangleMesh*};
%typemap{Ref<TriangleMesh>}{simple};
%typemap{Clone<TriangleMesh>}{simple};
@ -223,6 +229,12 @@
$CVar = (ExtrusionRole)SvUV($PerlVar);
%};
};
%typemap{ExtrusionSimulationType}{parsed}{
%cpp_type{ExtrusionSimulationType};
%precall_code{%
$CVar = (ExtrusionSimulationType)SvUV($PerlVar);
%};
};
%typemap{FlowRole}{parsed}{
%cpp_type{FlowRole};
%precall_code{%