Merge branch 'master' into xs

This commit is contained in:
Alessandro Ranellucci 2013-06-23 12:27:12 +02:00
commit ff795f2918
13 changed files with 175 additions and 60 deletions

View file

@ -27,16 +27,28 @@ my %recommends = qw(
Class::XSAccessor 0
Growl::GNTP 0.15
XML::SAX::ExpatXS 0
Wx 0.9901
);
# removed:
# Wx 0.9901
my @try = (
$ENV{CPANM} // (),
File::Spec->catfile($Config{sitebin}, 'cpanm'),
File::Spec->catfile($Config{installscript}, 'cpanm'),
);
my $cpanm;
if (defined $ENV{CPANM} && -x $ENV{CPANM}) {
$cpanm = $ENV{CPANM};
} elsif (-x (my $c = File::Spec->catfile($Config{installscript}, 'cpanm'))) {
$cpanm = $c;
} elsif ($^O = /^(?:darwin|linux)$/ && system(qw(which cpanm)) == 0) {
$cpanm = 'cpanm';
foreach my $path (@try) {
if (-e $path) { # don't use -x because it fails on Windows
$cpanm = $path;
last;
}
}
if (!$cpanm) {
if ($^O =~ /^(?:darwin|linux)$/ && system(qw(which cpanm)) == 0) {
$cpanm = 'cpanm';
}
}
die <<'EOF'
cpanm was not found. Please install it before running this script.

View file

@ -3,8 +3,8 @@ use Moo;
extends 'Slic3r::Fill::Base';
use Slic3r::Geometry qw(scale unscale X);
use Slic3r::Geometry::Clipper qw(offset2 union_pt traverse_pt PFT_EVENODD);
use Slic3r::Geometry qw(scale unscale X nearest_point_index);
use Slic3r::Geometry::Clipper qw(offset offset2 union_pt traverse_pt PFT_EVENODD);
sub fill_surface {
my $self = shift;
@ -27,16 +27,26 @@ sub fill_surface {
$flow_spacing = unscale $distance;
}
my @loops = my @last = @$expolygon;
# compensate the overlap which is good for rectilinear but harmful for concentric
# where the perimeter/infill spacing should be equal to any other loop spacing
my @loops = my @last = offset($expolygon, -&Slic3r::INFILL_OVERLAP_OVER_SPACING * $min_spacing / 2);
while (@last) {
push @loops, @last = offset2(\@last, -1.5*$distance, +0.5*$distance);
}
# generate paths from the outermost to the innermost, to avoid
# adhesion problems of the first central tiny loops
my @paths = map Slic3r::Polygon->new(@$_)->split_at_first_point,
@loops = map Slic3r::Polygon->new(@$_),
reverse traverse_pt( union_pt(\@loops, PFT_EVENODD) );
# order paths using a nearest neighbor search
my @paths = ();
my $last_pos = [0,0];
foreach my $loop (@loops) {
push @paths, $loop->split_at_index(nearest_point_index($last_pos, $loop));
$last_pos = $paths[-1][-1];
}
# clip the paths to avoid the extruder to get exactly on the first point of the loop
my $clip_length = scale $flow_spacing * &Slic3r::LOOP_CLIPPING_LENGTH_OVER_SPACING;
$_->clip_end($clip_length) for @paths;

View file

@ -190,8 +190,6 @@ sub extrude_loop {
$extrusion_path->intersect_expolygons($self->_layer_overhangs);
# reapply the nearest point search for starting point
# (TODO: choose the nearest point not on an overhang - make sure wipe and
# inwards move consider the new actual starting point)
@paths = Slic3r::ExtrusionPath::Collection
->new(paths => [@paths])
->chained_path($last_pos, 1);
@ -443,9 +441,16 @@ sub retract {
$self->speed('travel');
# subdivide the retraction
my $retracted = 0;
for (1 .. $#$wipe_path) {
my $segment_length = $wipe_path->[$_-1]->distance_to($wipe_path->[$_]);
$gcode .= $self->G1($wipe_path->[$_], undef, $retract->[2] * ($segment_length / $total_wipe_length), $retract->[3] . ";_WIPE");
$retracted += my $e = $retract->[2] * ($segment_length / $total_wipe_length);
$gcode .= $self->G1($wipe_path->[$_], undef, $e, $retract->[3] . ";_WIPE");
}
if ($retracted > $retract->[2]) {
# if we retracted less than we had to, retract the remainder
# TODO: add regression test
$gcode .= $self->G1(undef, undef, $retract->[2] - $retracted, $comment);
}
} else {
$self->speed('retract');

View file

@ -12,7 +12,7 @@ our @EXPORT_OK = qw(
point_is_on_left_of_segment polyline_lines polygon_lines nearest_point
point_along_segment polygon_segment_having_point polygon_has_subsegment
polygon_has_vertex polyline_length can_connect_points deg2rad rad2deg
rotate_points move_points clip_segment_polygon
rotate_points move_points clip_segment_polygon nearest_point_index
sum_vectors multiply_vector subtract_vectors dot perp polygon_points_visibility
line_intersection bounding_box bounding_box_intersect same_point same_line
longest_segment angle3points three_points_aligned line_direction

View file

@ -62,6 +62,18 @@ sub scale {
$self;
}
sub translate {
my $self = shift;
my @shift = @_;
for my $axis (X .. $#{$self->extents}) {
$self->extents->[$axis][MIN] += $shift[$axis];
$self->extents->[$axis][MAX] += $shift[$axis];
}
$self;
}
sub size {
my $self = shift;

View file

@ -256,7 +256,11 @@ sub make_perimeters {
foreach my $polynode (@nodes) {
push @loops, $traverse->($polynode->{children}, $depth+1, $is_contour);
# return ccw contours and cw holes
# GCode.pm will convert all of them to ccw, but it needs to know
# what the holes are in order to compute the correct inwards move
my $polygon = Slic3r::Polygon->new($polynode->{outer} // [ reverse @{$polynode->{hole}} ]);
$polygon->reverse if !$is_contour;
my $role = EXTR_ROLE_PERIMETER;
if ($is_contour ? $depth == 0 : !@{ $polynode->{children} }) {

View file

@ -6,6 +6,7 @@ use Slic3r::Geometry qw(X Y Z MIN move_points);
has 'materials' => (is => 'ro', default => sub { {} });
has 'objects' => (is => 'ro', default => sub { [] });
has '_bounding_box' => (is => 'rw');
sub read_from_file {
my $class = shift;
@ -57,6 +58,7 @@ sub add_object {
my $object = Slic3r::Model::Object->new(model => $self, @_);
push @{$self->objects}, $object;
$self->_bounding_box(undef);
return $object;
}
@ -70,11 +72,6 @@ sub set_material {
);
}
sub scale {
my $self = shift;
$_->scale(@_) for @{$self->objects};
}
sub arrange_objects {
my $self = shift;
my ($config) = @_;
@ -151,8 +148,9 @@ sub _arrange {
return ($config->duplicate_grid->[X] * $config->duplicate_grid->[Y]), @positions;
} else {
my $total_parts = $config->duplicate * @items;
my $partx = max(map $_->size->[X], @items);
my $party = max(map $_->size->[Y], @items);
my @sizes = map $_->size, @items;
my $partx = max(map $_->[X], @sizes);
my $party = max(map $_->[Y], @sizes);
return $config->duplicate,
Slic3r::Geometry::arrange
($total_parts, $partx, $party, (map $_, @{$config->bed_size}),
@ -172,12 +170,16 @@ sub used_vertices {
sub size {
my $self = shift;
return [ Slic3r::Geometry::size_3D($self->used_vertices) ];
return $self->bounding_box->size;
}
sub bounding_box {
my $self = shift;
return Slic3r::Geometry::BoundingBox->new_from_points_3D($self->used_vertices);
if (!defined $self->_bounding_box) {
$self->_bounding_box(Slic3r::Geometry::BoundingBox->new_from_points_3D($self->used_vertices));
}
return $self->_bounding_box;
}
sub align_to_origin {
@ -198,9 +200,18 @@ sub align_to_origin {
}
}
sub scale {
my $self = shift;
$_->scale(@_) for @{$self->objects};
$self->_bounding_box->scale(@_) if defined $self->_bounding_box;
}
sub move {
my $self = shift;
$_->move(@_) for @{$self->objects};
my @shift = @_;
$_->move(@shift) for @{$self->objects};
$self->_bounding_box->translate(@shift) if defined $self->_bounding_box;
}
# flattens everything to a single mesh
@ -286,6 +297,7 @@ has 'vertices' => (is => 'ro', default => sub { [] });
has 'volumes' => (is => 'ro', default => sub { [] });
has 'instances' => (is => 'rw');
has 'layer_height_ranges' => (is => 'rw', default => sub { [] }); # [ z_min, z_max, layer_height ]
has '_bounding_box' => (is => 'rw');
sub add_volume {
my $self = shift;
@ -304,6 +316,8 @@ sub add_volume {
my $volume = Slic3r::Model::Volume->new(object => $self, %args);
push @{$self->volumes}, $volume;
$self->_bounding_box(undef);
$self->model->_bounding_box(undef);
return $volume;
}
@ -312,6 +326,7 @@ sub add_instance {
$self->instances([]) if !defined $self->instances;
push @{$self->instances}, Slic3r::Model::Instance->new(object => $self, @_);
$self->model->_bounding_box(undef);
return $self->instances->[-1];
}
@ -333,7 +348,7 @@ sub used_vertices {
sub size {
my $self = shift;
return [ Slic3r::Geometry::size_3D($self->used_vertices) ];
return $self->bounding_box->size;
}
sub center {
@ -343,7 +358,11 @@ sub center {
sub bounding_box {
my $self = shift;
return Slic3r::Geometry::BoundingBox->new_from_points_3D($self->used_vertices);
if (!defined $self->_bounding_box) {
$self->_bounding_box(Slic3r::Geometry::BoundingBox->new_from_points_3D($self->used_vertices));
}
return $self->_bounding_box;
}
sub align_to_origin {
@ -359,7 +378,10 @@ sub align_to_origin {
sub move {
my $self = shift;
@{$self->vertices} = move_points_3D([ @_ ], @{$self->vertices});
my @shift = @_;
@{$self->vertices} = move_points_3D([ @shift ], @{$self->vertices});
$self->_bounding_box->translate(@shift) if defined $self->_bounding_box;
}
sub scale {
@ -371,6 +393,8 @@ sub scale {
foreach my $vertex (@{$self->vertices}) {
$vertex->[$_] *= $factor for X,Y,Z;
}
$self->_bounding_box->scale($factor) if defined $self->_bounding_box;
}
sub rotate {
@ -384,6 +408,8 @@ sub rotate {
foreach my $vertex (@{$self->vertices}) {
@$vertex = (@{ +(Slic3r::Geometry::rotate_points($rad, undef, [ $vertex->[X], $vertex->[Y] ]))[0] }, $vertex->[Z]);
}
$self->_bounding_box(undef);
}
sub materials_count {

View file

@ -7,7 +7,7 @@ use parent 'Slic3r::Polyline';
use Slic3r::Geometry qw(polygon_lines polygon_remove_parallel_continuous_edges
polygon_remove_acute_vertices polygon_segment_having_point point_in_polygon
PI X1 X2 Y1 Y2);
PI X1 X2 Y1 Y2 epsilon);
use Slic3r::Geometry::Clipper qw(JT_MITER);
sub lines {
@ -159,7 +159,7 @@ sub concave_points {
my $self = shift;
return map $self->[$_],
grep Slic3r::Geometry::angle3points(@$self[$_, $_-1, $_+1]) < PI,
grep Slic3r::Geometry::angle3points(@$self[$_, $_-1, $_+1]) < PI - epsilon,
-1 .. ($#$self-1);
}

View file

@ -145,6 +145,12 @@ sub slice {
my $self = shift;
my %params = @_;
# make sure all layers contain layer region objects for all regions
my $regions_count = $self->print->regions_count;
foreach my $layer (@{ $self->layers }) {
$layer->region($_) for 0 .. ($regions_count-1);
}
# process facets
for my $region_id (0 .. $#{$self->meshes}) {
my $mesh = $self->meshes->[$region_id]; # ignore undef meshes
@ -152,8 +158,7 @@ sub slice {
my $apply_lines = sub {
my $lines = shift;
foreach my $layer_id (keys %$lines) {
my $layerm = $self->layers->[$layer_id]->region($region_id);
push @{$layerm->lines}, @{$lines->{$layer_id}};
push @{$self->layers->[$layer_id]->regions->[$region_id]->lines}, @{$lines->{$layer_id}};
}
};
Slic3r::parallelize(
@ -192,9 +197,6 @@ sub slice {
pop @{$self->layers} while @{$self->layers} && (!map @{$_->lines}, @{$self->layers->[-1]->regions});
foreach my $layer (@{ $self->layers }) {
# make sure all layers contain layer region objects for all regions
$layer->region($_) for 0 .. ($self->print->regions_count-1);
Slic3r::debugf "Making surfaces for layer %d (slice z = %f):\n",
$layer->id, unscale $layer->slice_z if $Slic3r::debug;

View file

@ -27,6 +27,13 @@ sub model {
$facets = [
[0,1,2], [0,2,3], [4,5,6], [4,6,7], [0,4,7], [0,7,1], [1,7,6], [1,6,2], [2,6,5], [2,5,3], [4,0,3], [4,3,5],
],
} elsif ($model_name eq 'cube_with_hole') {
$vertices = [
[0,0,0],[0,0,10],[0,20,0],[0,20,10],[20,0,0],[20,0,10],[5,5,0],[15,5,0],[5,15,0],[20,20,0],[15,15,0],[20,20,10],[5,5,10],[5,15,10],[15,5,10],[15,15,10]
];
$facets = [
[0,1,2],[2,1,3],[1,0,4],[5,1,4],[6,7,4],[8,2,9],[0,2,8],[10,8,9],[0,8,6],[0,6,4],[4,7,9],[7,10,9],[2,3,9],[9,3,11],[12,1,5],[13,3,12],[14,12,5],[3,1,12],[11,3,13],[11,15,5],[11,13,15],[15,14,5],[5,4,9],[11,5,9],[8,13,12],[6,8,12],[10,15,13],[8,10,13],[15,10,14],[14,10,7],[14,7,12],[12,7,6]
],
} elsif ($model_name eq 'V') {
$vertices = [
[-14,0,20],[-14,15,20],[0,0,0],[0,15,0],[-4,0,20],[-4,15,20],[5,0,7.14286],[10,0,0],[24,0,20],[14,0,20],[10,15,0],[5,15,7.14286],[14,15,20],[24,15,20]

View file

@ -1,6 +1,7 @@
package Slic3r::TriangleMesh;
use Moo;
use List::Util qw(reduce min max);
use Slic3r::Geometry qw(X Y Z A B unscale same_point);
use Slic3r::Geometry::Clipper qw(union_ex);
use Storable;
@ -37,6 +38,7 @@ sub analyze {
$self->facets_edges([]);
$self->edges_facets([]);
my %table = (); # edge_coordinates => edge_id
my $vertices = $self->vertices; # save method calls
for (my $facet_id = 0; $facet_id <= $#{$self->facets}; $facet_id++) {
my $facet = $self->facets->[$facet_id];
@ -46,8 +48,10 @@ sub analyze {
# this is needed to get all intersection lines in a consistent order
# (external on the right of the line)
{
my @z_order = sort { $self->vertices->[$facet->[$a]][Z] <=> $self->vertices->[$facet->[$b]][Z] } -3..-1;
@$facet[-3..-1] = (@$facet[$z_order[0]..-1], @$facet[-3..($z_order[0]-1)]);
my $lowest_vertex_idx = reduce {
$vertices->[ $facet->[$a] ][Z] < $vertices->[ $facet->[$b] ][Z] ? $a : $b
} -3 .. -1;
@$facet[-3..-1] = (@$facet[$lowest_vertex_idx..-1], @$facet[-3..($lowest_vertex_idx-1)]);
}
# ignore the normal if provided
@ -420,13 +424,11 @@ sub slice_facet {
if $Slic3r::debug;
# find the vertical extents of the facet
my ($min_z, $max_z) = (99999999999, -99999999999);
foreach my $vertex (@vertices) {
my $vertex_z = $self->vertices->[$vertex][Z];
$min_z = $vertex_z if $vertex_z < $min_z;
$max_z = $vertex_z if $vertex_z > $max_z;
}
Slic3r::debugf "z: min = %.0f, max = %.0f\n", $min_z, $max_z;
my @z = map $_->[Z], @{$self->vertices}[@vertices];
my $min_z = min(@z);
my $max_z = max(@z);
Slic3r::debugf "z: min = %.0f, max = %.0f\n", $min_z, $max_z
if $Slic3r::debug;
if ($max_z == $min_z) {
Slic3r::debugf "Facet is horizontal; ignoring\n";
@ -435,10 +437,11 @@ sub slice_facet {
# calculate the layer extents
my ($min_layer, $max_layer) = $print_object->get_layer_range($min_z, $max_z);
Slic3r::debugf "layers: min = %s, max = %s\n", $min_layer, $max_layer;
Slic3r::debugf "layers: min = %s, max = %s\n", $min_layer, $max_layer
if $Slic3r::debug;
my $lines = {}; # layer_id => [ lines ]
for (my $layer_id = $min_layer; $layer_id <= $max_layer; $layer_id++) {
for my $layer_id ($min_layer .. $max_layer) {
my $layer = $print_object->layers->[$layer_id];
$lines->{$layer_id} ||= [];
push @{ $lines->{$layer_id} }, $self->intersect_facet($facet_id, $layer->slice_z);
@ -451,25 +454,27 @@ sub intersect_facet {
my ($facet_id, $z) = @_;
my @vertices_ids = @{$self->facets->[$facet_id]}[-3..-1];
my %vertices = map { $_ => $self->vertices->[$_] } @vertices_ids; # cache vertices
my @edge_ids = @{$self->facets_edges->[$facet_id]};
my @edge_vertices_ids = $self->_facet_edges($facet_id);
my (@lines, @points, @intersection_points, @points_on_layer) = ();
my (@points, @intersection_points, @points_on_layer) = ();
for my $e (0..2) {
my $edge_id = $edge_ids[$e];
my ($a_id, $b_id) = @{$edge_vertices_ids[$e]};
my ($a, $b) = map $self->vertices->[$_], ($a_id, $b_id);
my ($a, $b) = @vertices{$a_id, $b_id};
#printf "Az = %f, Bz = %f, z = %f\n", $a->[Z], $b->[Z], $z;
if ($a->[Z] == $b->[Z] && $a->[Z] == $z) {
# edge is horizontal and belongs to the current layer
my $edge_type = (grep $self->vertices->[$_][Z] < $z, @vertices_ids) ? FE_TOP : FE_BOTTOM;
my $edge_type = (grep $vertices{$_}[Z] < $z, @vertices_ids) ? FE_TOP : FE_BOTTOM;
if ($edge_type == FE_TOP) {
($a, $b) = ($b, $a);
($a_id, $b_id) = ($b_id, $a_id);
}
push @lines, pack I_FMT, (
# We assume that this method is never being called for horizontal
# facets, so no other edge is going to be on this layer.
return pack I_FMT, (
$b->[X], $b->[Y], # I_B
$a_id, # I_A_ID
$b_id, # I_B_ID
@ -499,14 +504,13 @@ sub intersect_facet {
$b->[X] + ($a->[X] - $b->[X]) * ($z - $b->[Z]) / ($a->[Z] - $b->[Z]),
$b->[Y] + ($a->[Y] - $b->[Y]) * ($z - $b->[Z]) / ($a->[Z] - $b->[Z]),
undef,
$edge_id,
$edge_ids[$e],
];
push @intersection_points, $#points;
#print "Intersects at $z!\n";
}
}
return @lines if @lines;
if (@points_on_layer == 2 && @intersection_points == 1) {
$points[ $points_on_layer[1] ] = undef;
@points = grep $_, @points;

View file

@ -1,4 +1,4 @@
use Test::More tests => 3;
use Test::More tests => 5;
use strict;
use warnings;
@ -41,6 +41,37 @@ use Slic3r::Test;
ok !$has_cw_loops, 'all perimeters extruded ccw';
}
{
$config->set('external_perimeter_speed', 68);
my $print = Slic3r::Test::init_print('cube_with_hole', config => $config);
my $has_cw_loops = my $has_outwards_move = 0;
my $cur_loop;
my %external_loops = (); # print_z => count of external loops
Slic3r::GCode::Reader->new(gcode => Slic3r::Test::gcode($print))->parse(sub {
my ($self, $cmd, $args, $info) = @_;
if ($info->{extruding} && $info->{dist_XY} > 0) {
$cur_loop ||= [ [$self->X, $self->Y] ];
push @$cur_loop, [ @$info{qw(new_X new_Y)} ];
} else {
if ($cur_loop) {
$has_cw_loops = 1 if !Slic3r::Geometry::Clipper::is_counter_clockwise($cur_loop);
if ($self->F == $config->external_perimeter_speed*60) {
my $move_dest = [ @$info{qw(new_X new_Y)} ];
$external_loops{$self->Z}++;
$has_outwards_move = 1
if !Slic3r::Polygon->new(@$cur_loop)->encloses_point($move_dest)
? ($external_loops{$self->Z} == 2) # contour should include destination
: ($external_loops{$self->Z} == 1); # hole should not
}
$cur_loop = undef;
}
}
});
ok !$has_cw_loops, 'all perimeters extruded ccw';
ok !$has_outwards_move, 'move inwards after completing external loop';
}
{
my $print = Slic3r::Test::init_print('L', config => $config);
my $loop_starts_from_convex_point = 0;

View file

@ -2,7 +2,7 @@ use Test::More;
use strict;
use warnings;
plan tests => 17;
plan tests => 16;
BEGIN {
use FindBin;
@ -20,11 +20,13 @@ my @points = ([3, 4], [8, 5], [1, 9]); # XY coordinates of the facet vertices
# the first point of the intersection lines is replaced by -1 because TriangleMesh.pm
# is saving memory and doesn't store point A anymore since it's not actually needed.
is_deeply lines(20, 20, 20), [
[ -1, $points[1] ], # $points[0]
[ -1, $points[2] ], # $points[1]
[ -1, $points[0] ], # $points[2]
], 'horizontal';
# We disable this test because intersect_facet() now assumes we never feed a horizontal
# facet to it.
# is_deeply lines(20, 20, 20), [
# [ -1, $points[1] ], # $points[0]
# [ -1, $points[2] ], # $points[1]
# [ -1, $points[0] ], # $points[2]
# ], 'horizontal';
is_deeply lines(22, 20, 20), [ [ -1, $points[2] ] ], 'lower edge on layer'; # $points[1]
is_deeply lines(20, 20, 22), [ [ -1, $points[1] ] ], 'lower edge on layer'; # $points[0]