Refactoring: moved slicing code to new TriangleMesh class, leaving in STL just what's needed to read that particular input format. Slic3r will now warn if model is not manifold.

This commit is contained in:
Alessandro Ranellucci 2011-11-27 11:40:03 +01:00
parent 15d060019f
commit d51a37a0ae
16 changed files with 642 additions and 451 deletions

View file

@ -26,9 +26,6 @@ lib/Slic3r/GUI/OptionsGroup.pm
lib/Slic3r/GUI/SkeinPanel.pm lib/Slic3r/GUI/SkeinPanel.pm
lib/Slic3r/Layer.pm lib/Slic3r/Layer.pm
lib/Slic3r/Line.pm lib/Slic3r/Line.pm
lib/Slic3r/Line/FacetEdge.pm
lib/Slic3r/Line/FacetEdge/Bottom.pm
lib/Slic3r/Line/FacetEdge/Top.pm
lib/Slic3r/Perimeter.pm lib/Slic3r/Perimeter.pm
lib/Slic3r/Point.pm lib/Slic3r/Point.pm
lib/Slic3r/Polygon.pm lib/Slic3r/Polygon.pm
@ -39,6 +36,8 @@ lib/Slic3r/Skein.pm
lib/Slic3r/STL.pm lib/Slic3r/STL.pm
lib/Slic3r/Surface.pm lib/Slic3r/Surface.pm
lib/Slic3r/SVG.pm lib/Slic3r/SVG.pm
lib/Slic3r/TriangleMesh.pm
lib/Slic3r/TriangleMesh/IntersectionLine.pm
MANIFEST This list of files MANIFEST This list of files
README.markdown README.markdown
slic3r.pl slic3r.pl

View file

@ -3,7 +3,7 @@ package Slic3r;
use strict; use strict;
use warnings; use warnings;
our $VERSION = "0.5.3"; our $VERSION = "0.5.4beta";
our $debug = 0; our $debug = 0;
sub debugf { sub debugf {
@ -21,7 +21,6 @@ use Slic3r::Fill;
use Slic3r::Geometry; use Slic3r::Geometry;
use Slic3r::Layer; use Slic3r::Layer;
use Slic3r::Line; use Slic3r::Line;
use Slic3r::Line::FacetEdge;
use Slic3r::Perimeter; use Slic3r::Perimeter;
use Slic3r::Point; use Slic3r::Point;
use Slic3r::Polygon; use Slic3r::Polygon;
@ -31,6 +30,8 @@ use Slic3r::Print;
use Slic3r::Skein; use Slic3r::Skein;
use Slic3r::STL; use Slic3r::STL;
use Slic3r::Surface; use Slic3r::Surface;
use Slic3r::TriangleMesh;
use Slic3r::TriangleMesh::IntersectionLine;
# printer options # printer options
our $nozzle_diameter = 0.5; our $nozzle_diameter = 0.5;
@ -60,7 +61,7 @@ our $layer_height = 0.4;
our $first_layer_height_ratio = 1; our $first_layer_height_ratio = 1;
our $infill_every_layers = 1; our $infill_every_layers = 1;
our $extrusion_width_ratio = 0; our $extrusion_width_ratio = 0;
our $flow_speed_ratio = 1.1; our $flow_speed_ratio = 1;
our $flow_width; our $flow_width;
# print options # print options

View file

@ -4,7 +4,7 @@ use warnings;
use utf8; use utf8;
use File::Basename qw(basename); use File::Basename qw(basename);
use Wx qw(:sizer :progressdialog wxOK wxICON_INFORMATION wxICON_ERROR wxID_OK wxFD_OPEN use Wx qw(:sizer :progressdialog wxOK wxICON_INFORMATION wxICON_WARNING wxICON_ERROR wxID_OK wxFD_OPEN
wxFD_SAVE wxDEFAULT wxNORMAL); wxFD_SAVE wxDEFAULT wxNORMAL);
use Wx::Event qw(EVT_BUTTON); use Wx::Event qw(EVT_BUTTON);
use base 'Wx::Panel'; use base 'Wx::Panel';
@ -155,7 +155,14 @@ sub do_slice {
} }
}, },
); );
$skein->go; {
local $SIG{__WARN__} = sub {
my $message = shift;
Wx::MessageDialog->new($self, $message, 'Non-manifold object',
wxOK | wxICON_WARNING)->ShowModal;
};
$skein->go;
}
$process_dialog->Destroy; $process_dialog->Destroy;
undef $process_dialog; undef $process_dialog;

View file

@ -13,11 +13,11 @@ our @EXPORT_OK = qw(
polygon_has_vertex polyline_length can_connect_points deg2rad rad2deg polygon_has_vertex polyline_length can_connect_points deg2rad rad2deg
rotate_points move_points remove_coinciding_points clip_segment_polygon rotate_points move_points remove_coinciding_points clip_segment_polygon
sum_vectors multiply_vector subtract_vectors dot perp polygon_points_visibility sum_vectors multiply_vector subtract_vectors dot perp polygon_points_visibility
line_intersection bounding_box bounding_box_intersect line_intersection bounding_box bounding_box_intersect same_point
longest_segment angle3points three_points_aligned longest_segment angle3points three_points_aligned
polyline_remove_parallel_continuous_edges polyline_remove_acute_vertices polyline_remove_parallel_continuous_edges polyline_remove_acute_vertices
polygon_remove_acute_vertices polygon_remove_parallel_continuous_edges polygon_remove_acute_vertices polygon_remove_parallel_continuous_edges
shortest_path collinear shortest_path collinear scale unscale merge_collinear_lines
); );
use Slic3r::Geometry::DouglasPeucker qw(Douglas_Peucker); use Slic3r::Geometry::DouglasPeucker qw(Douglas_Peucker);
@ -35,9 +35,12 @@ use constant X2 => 2;
use constant Y2 => 3; use constant Y2 => 3;
our $parallel_degrees_limit = abs(deg2rad(3)); our $parallel_degrees_limit = abs(deg2rad(3));
our $epsilon = 1E-4; our $epsilon = 1E-6;
sub epsilon () { $epsilon } sub epsilon () { $epsilon }
sub scale ($) { $_[0] / $Slic3r::resolution }
sub unscale ($) { $_[0] * $Slic3r::resolution }
sub slope { sub slope {
my ($line) = @_; my ($line) = @_;
return undef if abs($line->[B][X] - $line->[A][X]) < epsilon; # line is vertical return undef if abs($line->[B][X] - $line->[A][X]) < epsilon; # line is vertical
@ -85,6 +88,11 @@ sub points_coincide {
return 0; return 0;
} }
sub same_point {
my ($p1, $p2) = @_;
return $p1->[X] == $p2->[X] && $p1->[Y] == $p2->[Y];
}
sub distance_between_points { sub distance_between_points {
my ($p1, $p2) = @_; my ($p1, $p2) = @_;
return sqrt((($p1->[X] - $p2->[X])**2) + ($p1->[Y] - $p2->[Y])**2); return sqrt((($p1->[X] - $p2->[X])**2) + ($p1->[Y] - $p2->[Y])**2);
@ -438,6 +446,40 @@ sub collinear {
return 1; return 1;
} }
sub merge_collinear_lines {
my ($lines) = @_;
my $line_count = @$lines;
for (my $i = 0; $i <= $#$lines-1; $i++) {
for (my $j = $i+1; $j <= $#$lines; $j++) {
# lines are collinear and overlapping?
next unless collinear($lines->[$i], $lines->[$j], 1);
# lines have same orientation?
next unless ($lines->[$i][A][X] <=> $lines->[$i][B][X]) == ($lines->[$j][A][X] <=> $lines->[$j][B][X])
&& ($lines->[$i][A][Y] <=> $lines->[$i][B][Y]) == ($lines->[$j][A][Y] <=> $lines->[$j][B][Y]);
# resulting line
my @x = sort { $a <=> $b } ($lines->[$i][A][X], $lines->[$i][B][X], $lines->[$j][A][X], $lines->[$j][B][X]);
my @y = sort { $a <=> $b } ($lines->[$i][A][Y], $lines->[$i][B][Y], $lines->[$j][A][Y], $lines->[$j][B][Y]);
my $new_line = Slic3r::Line->new([$x[0], $y[0]], [$x[-1], $y[-1]]);
for (X, Y) {
($new_line->[A][$_], $new_line->[B][$_]) = ($new_line->[B][$_], $new_line->[A][$_])
if $lines->[$i][A][$_] > $lines->[$i][B][$_];
}
# save new line and remove found one
$lines->[$i] = $new_line;
splice @$lines, $j, 1;
$j--;
}
}
Slic3r::debugf " merging %d lines resulted in %d lines\n", $line_count, scalar(@$lines);
return $lines;
}
sub _line_intersection { sub _line_intersection {
my ( $x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3 ); my ( $x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3 );

View file

@ -2,8 +2,7 @@ package Slic3r::Layer;
use Moo; use Moo;
use Math::Clipper ':all'; use Math::Clipper ':all';
use Slic3r::Geometry qw(polygon_lines points_coincide angle3points polyline_lines nearest_point use Slic3r::Geometry qw(collinear X Y A B PI);
line_length collinear X Y A B PI);
use Slic3r::Geometry::Clipper qw(union_ex diff_ex intersection_ex PFT_EVENODD); use Slic3r::Geometry::Clipper qw(union_ex diff_ex intersection_ex PFT_EVENODD);
use XXX; use XXX;
@ -18,7 +17,7 @@ has 'id' => (
# these need to be merged in continuos (closed) polylines # these need to be merged in continuos (closed) polylines
has 'lines' => ( has 'lines' => (
is => 'rw', is => 'rw',
#isa => 'ArrayRef[Slic3r::Line]', #isa => 'ArrayRef[Slic3r::TriangleMesh::IntersectionLine]',
default => sub { [] }, default => sub { [] },
); );
@ -98,202 +97,19 @@ sub add_line {
my $self = shift; my $self = shift;
my ($line) = @_; my ($line) = @_;
return if $line->a->coincides_with($line->b);
push @{ $self->lines }, $line; push @{ $self->lines }, $line;
return $line; return $line;
} }
# merge overlapping lines
sub cleanup_lines {
my $self = shift;
my $lines = $self->lines;
my $line_count = @$lines;
for (my $i = 0; $i <= $#$lines-1; $i++) {
for (my $j = $i+1; $j <= $#$lines; $j++) {
# lines are collinear and overlapping?
next unless collinear($lines->[$i], $lines->[$j], 1);
# lines have same orientation?
next unless ($lines->[$i][A][X] <=> $lines->[$i][B][X]) == ($lines->[$j][A][X] <=> $lines->[$j][B][X])
&& ($lines->[$i][A][Y] <=> $lines->[$i][B][Y]) == ($lines->[$j][A][Y] <=> $lines->[$j][B][Y]);
# resulting line
my @x = sort { $a <=> $b } ($lines->[$i][A][X], $lines->[$i][B][X], $lines->[$j][A][X], $lines->[$j][B][X]);
my @y = sort { $a <=> $b } ($lines->[$i][A][Y], $lines->[$i][B][Y], $lines->[$j][A][Y], $lines->[$j][B][Y]);
my $new_line = Slic3r::Line->new([$x[0], $y[0]], [$x[-1], $y[-1]]);
for (X, Y) {
($new_line->[A][$_], $new_line->[B][$_]) = ($new_line->[B][$_], $new_line->[A][$_])
if $lines->[$i][A][$_] > $lines->[$i][B][$_];
}
# save new line and remove found one
$lines->[$i] = $new_line;
splice @$lines, $j, 1;
$j--;
}
}
Slic3r::debugf " merging %d lines resulted in %d lines\n", $line_count, scalar(@$lines);
}
# build polylines from lines # build polylines from lines
sub make_surfaces { sub make_surfaces {
my $self = shift; my $self = shift;
my ($loops) = @_;
if (0) {
printf "Layer was sliced at z = %f\n", $self->slice_z * $Slic3r::resolution;
require "Slic3r/SVG.pm";
Slic3r::SVG::output(undef, "lines.svg",
lines => [ grep !$_->isa('Slic3r::Line::FacetEdge'), @{$self->lines} ],
red_lines => [ grep $_->isa('Slic3r::Line::FacetEdge'), @{$self->lines} ],
);
}
my (@polygons, %visited_lines, @discarded_lines, @discarded_polylines) = ();
my $detect = sub {
my @lines = @{$self->lines};
(@polygons, %visited_lines, @discarded_lines, @discarded_polylines) = ();
my $get_point_id = sub { sprintf "%.0f,%.0f", @{$_[0]} };
my (%pointmap, @pointmap_keys) = ();
foreach my $line (@lines) {
my $point_id = $get_point_id->($line->[A]);
if (!exists $pointmap{$point_id}) {
$pointmap{$point_id} = [];
push @pointmap_keys, $line->[A];
}
push @{ $pointmap{$point_id} }, $line;
}
my $n = 0;
while (my $first_line = shift @lines) {
next if $visited_lines{ $first_line->id };
my @points = @$first_line;
my @seen_lines = ($first_line);
my %seen_points = map { $get_point_id->($points[$_]) => $_ } 0..1;
CYCLE: while (1) {
my $next_lines = $pointmap{ $get_point_id->($points[-1]) };
# shouldn't we find the point, let's try with a slower algorithm
# as approximation may make the coordinates differ
if (!$next_lines) {
my $nearest_point = nearest_point($points[-1], \@pointmap_keys);
#printf " we have a nearest point: %f,%f (%s)\n", @$nearest_point, $get_point_id->($nearest_point);
if ($nearest_point) {
local $Slic3r::Geometry::epsilon = 1000000;
$next_lines = $pointmap{$get_point_id->($nearest_point)}
if points_coincide($points[-1], $nearest_point);
}
}
if (0 && !$next_lines) {
require "Slic3r/SVG.pm";
Slic3r::SVG::output(undef, "no_lines.svg",
lines => [ grep !$_->isa('Slic3r::Line::FacetEdge'), @{$self->lines} ],
red_lines => [ grep $_->isa('Slic3r::Line::FacetEdge'), @{$self->lines} ],
points => [ $points[-1] ],
no_arrows => 1,
);
}
$next_lines
or die sprintf("No lines start at point %s. This shouldn't happen. Please check the model for manifoldness.\n", $get_point_id->($points[-1]));
last CYCLE if !@$next_lines;
my @ordered_next_lines = sort
{ angle3points($points[-1], $points[-2], $next_lines->[$a][B]) <=> angle3points($points[-1], $points[-2], $next_lines->[$b][B]) }
0..$#$next_lines;
#if (@$next_lines > 1) {
# Slic3r::SVG::output(undef, "next_line.svg",
# lines => $next_lines,
# red_lines => [ polyline_lines([@points]) ],
# green_lines => [ $next_lines->[ $ordered_next_lines[0] ] ],
# );
#}
my ($next_line) = splice @$next_lines, $ordered_next_lines[0], 1;
push @seen_lines, $next_line;
push @points, $next_line->[B];
my $point_id = $get_point_id->($points[-1]);
if ($seen_points{$point_id}) {
splice @points, 0, $seen_points{$point_id};
last CYCLE;
}
$seen_points{$point_id} = $#points;
}
if (@points < 4 || !points_coincide($points[0], $points[-1])) {
# discarding polyline
push @discarded_lines, @seen_lines;
if (@points > 2) {
push @discarded_polylines, [@points];
}
next;
}
$visited_lines{ $_->id } = 1 for @seen_lines;
pop @points;
Slic3r::debugf "Discovered polygon of %d points\n", scalar(@points);
push @polygons, Slic3r::Polygon->new(@points);
$polygons[-1]->cleanup;
}
};
$detect->();
# Now, if we got a clean and manifold model then @polygons would contain everything
# we need to draw our layer. In real life, sadly, things are different and it is likely
# that the above algorithm wasn't able to detect every polygon. This may happen because
# of non-manifoldness or because of many close lines, often overlapping; both situations
# make a head-to-tail search difficult.
# On the other hand, we can safely assume that every polygon we detected is correct, as
# the above algorithm is quite strict. We can take a brute force approach to connect any
# other line.
# So, let's first check what lines were not detected as part of polygons.
if (@discarded_lines) {
Slic3r::debugf " %d lines out of %d were discarded and %d polylines were not closed\n",
scalar(@discarded_lines), scalar(@{$self->lines}), scalar(@discarded_polylines);
print " Warning: errors while parsing this layer (dirty or non-manifold model).\n";
print " Retrying with slower algorithm.\n";
if (0) {
require "Slic3r/SVG.pm";
Slic3r::SVG::output(undef, "layer" . $self->id . "_detected.svg",
white_polygons => \@polygons,
);
Slic3r::SVG::output(undef, "layer" . $self->id . "_discarded_lines.svg",
red_lines => \@discarded_lines,
);
Slic3r::SVG::output(undef, "layer" . $self->id . "_discarded_polylines.svg",
polylines => \@discarded_polylines,
);
}
$self->cleanup_lines;
eval { $detect->(); };
warn $@ if $@;
if (@discarded_lines) {
print " Warning: even slow detection algorithm threw errors. Review the output before printing.\n";
}
}
{ {
my $expolygons = union_ex([ @polygons ], PFT_EVENODD); my $expolygons = union_ex($loops, PFT_EVENODD);
Slic3r::debugf " %d surface(s) having %d holes detected from %d polylines\n", Slic3r::debugf " %d surface(s) having %d holes detected from %d polylines\n",
scalar(@$expolygons), scalar(map $_->holes, @$expolygons), scalar(@polygons); scalar(@$expolygons), scalar(map $_->holes, @$expolygons), scalar(@$loops);
push @{$self->surfaces}, push @{$self->surfaces},
map Slic3r::Surface->cast_from_expolygon($_, surface_type => 'internal'), map Slic3r::Surface->cast_from_expolygon($_, surface_type => 'internal'),

View file

@ -1,9 +0,0 @@
package Slic3r::Line::FacetEdge;
use Moo;
extends 'Slic3r::Line';
use Slic3r::Line::FacetEdge::Bottom;
use Slic3r::Line::FacetEdge::Top;
1;

View file

@ -1,6 +0,0 @@
package Slic3r::Line::FacetEdge::Bottom;
use Moo;
extends 'Slic3r::Line::FacetEdge';
1;

View file

@ -1,6 +0,0 @@
package Slic3r::Line::FacetEdge::Top;
use Moo;
extends 'Slic3r::Line::FacetEdge';
1;

View file

@ -2,11 +2,14 @@ package Slic3r::Print;
use Moo; use Moo;
use Math::ConvexHull 1.0.4 qw(convex_hull); use Math::ConvexHull 1.0.4 qw(convex_hull);
use Slic3r::Geometry qw(X Y PI); use Slic3r::Geometry qw(X Y Z PI scale);
use Slic3r::Geometry::Clipper qw(explode_expolygons safety_offset diff_ex intersection_ex use Slic3r::Geometry::Clipper qw(explode_expolygons safety_offset diff_ex intersection_ex
union_ex offset JT_ROUND JT_MITER); union_ex offset JT_ROUND JT_MITER);
use XXX; use XXX;
use constant MIN => 0;
use constant MAX => 1;
has 'x_length' => ( has 'x_length' => (
is => 'ro', is => 'ro',
required => 1, required => 1,
@ -26,11 +29,59 @@ has 'layers' => (
default => sub { [] }, default => sub { [] },
); );
sub new_from_stl { sub new_from_mesh {
my $self = shift; my $class = shift;
my ($stl_file) = @_; my ($mesh) = @_;
my $print = Slic3r::STL->new->parse_file($stl_file); $mesh->rotate($Slic3r::rotate);
$mesh->scale($Slic3r::scale / $Slic3r::resolution);
# calculate the displacements needed to
# have lowest value for each axis at coordinate 0
{
my @extents = $mesh->bounding_box;
my @shift = map -$extents[$_][MIN], X,Y,Z;
$mesh->move(@shift);
}
# duplicate object
{
my @size = $mesh->size;
my @duplicate_offset = (
($size[X] + scale $Slic3r::duplicate_distance),
($size[Y] + scale $Slic3r::duplicate_distance),
);
for (my $i = 2; $i <= $Slic3r::duplicate_x; $i++) {
$mesh->duplicate($duplicate_offset[X] * ($i-1), 0);
}
for (my $i = 2; $i <= $Slic3r::duplicate_y; $i++) {
$mesh->duplicate(0, $duplicate_offset[Y] * ($i-1));
}
}
# initialize print job
my @size = $mesh->size;
my $print = $class->new(
x_length => $size[X],
y_length => $size[Y],
);
$mesh->make_edge_table;
# process facets
for (my $i = 0; $i <= $#{$mesh->facets}; $i++) {
my $facet = $mesh->facets->[$i];
# transform vertex coordinates
my ($normal, @vertices) = @$facet;
$mesh->_facet($print, $i, $normal, @vertices);
}
die "Invalid input file\n" if !@{$print->layers};
# remove last layer if empty
# (we might have created it because of the $max_layer = ... + 1 code below)
pop @{$print->layers} if !@{$print->layers->[-1]->surfaces} && !@{$print->layers->[-1]->lines};
print "\n==> PROCESSING SLICES:\n"; print "\n==> PROCESSING SLICES:\n";
foreach my $layer (@{ $print->layers }) { foreach my $layer (@{ $print->layers }) {
@ -44,7 +95,7 @@ sub new_from_stl {
# inside a closed polyline) # inside a closed polyline)
# build surfaces from sparse lines # build surfaces from sparse lines
$layer->make_surfaces; $layer->make_surfaces($mesh->make_loops($layer));
} }
return $print; return $print;

View file

@ -5,205 +5,6 @@ use Math::Clipper qw(integerize_coordinate_sets is_counter_clockwise);
use Slic3r::Geometry qw(X Y Z three_points_aligned longest_segment); use Slic3r::Geometry qw(X Y Z three_points_aligned longest_segment);
use XXX; use XXX;
use constant MIN => 0;
use constant MAX => 1;
sub parse_file {
my $self = shift;
my ($file) = @_;
# open STL file
my $facets = $self->read_file($file);
if ($Slic3r::rotate > 0) {
my $deg = Slic3r::Geometry::deg2rad($Slic3r::rotate);
foreach my $facet (@$facets) {
my ($normal, @vertices) = @$facet;
foreach my $vertex (@vertices) {
@$vertex = (@{ +(Slic3r::Geometry::rotate_points($deg, undef, [ $vertex->[X], $vertex->[Y] ]))[0] }, $vertex->[Z]);
}
}
}
# we only want to work with positive coordinates, so let's
# find our object extents to calculate coordinate displacements
my @extents = (map [99999999999, -99999999999], X,Y,Z);
foreach my $facet (@$facets) {
my ($normal, @vertices) = @$facet;
foreach my $vertex (@vertices) {
for (X,Y,Z) {
$extents[$_][MIN] = $vertex->[$_] if $vertex->[$_] < $extents[$_][MIN];
$extents[$_][MAX] = $vertex->[$_] if $vertex->[$_] > $extents[$_][MAX];
}
}
}
# scale extents
for (X,Y,Z) {
$extents[$_][MIN] *= $Slic3r::scale;
$extents[$_][MAX] *= $Slic3r::scale;
}
# duplicate object
my @duplicate_offset = (
(($extents[X][MAX] - $extents[X][MIN]) + $Slic3r::duplicate_distance),
(($extents[Y][MAX] - $extents[Y][MIN]) + $Slic3r::duplicate_distance),
);
$extents[X][MAX] += $duplicate_offset[X] * ($Slic3r::duplicate_x-1);
$extents[Y][MAX] += $duplicate_offset[Y] * ($Slic3r::duplicate_y-1);
my @copies = ();
for (my $i = 0; $i < $Slic3r::duplicate_x; $i++) {
for (my $j = 0; $j < $Slic3r::duplicate_y; $j++) {
push @copies, [ $duplicate_offset[X] * $i, $duplicate_offset[Y] * $j ];
}
}
# initialize print job
my $print = Slic3r::Print->new(
x_length => ($extents[X][MAX] - $extents[X][MIN]) / $Slic3r::resolution,
y_length => ($extents[Y][MAX] - $extents[Y][MIN]) / $Slic3r::resolution,
);
# calculate the displacements needed to
# have lowest value for each axis at coordinate 0
my @shift = map sprintf('%.0f', -$extents[$_][MIN] / $Slic3r::resolution), X,Y,Z;
# process facets
foreach my $facet (@$facets) {
# transform vertex coordinates
my ($normal, @vertices) = @$facet;
foreach my $vertex (@vertices) {
$vertex->[$_] = ($Slic3r::scale * $vertex->[$_] / $Slic3r::resolution) + $shift[$_]
for X,Y,Z;
}
foreach my $copy (@copies) {
my @copy_vertices = map [ @$_ ], @vertices; # clone vertices
foreach my $vertex (@copy_vertices) {
$vertex->[$_] += $copy->[$_] / $Slic3r::resolution for X,Y;
}
$self->_facet($print, $normal, @copy_vertices);
}
}
die "Invalid input file\n" if !@{$print->layers};
# remove last layer if empty
# (we might have created it because of the $max_layer = ... + 1 code below)
pop @{$print->layers} if !@{$print->layers->[-1]->surfaces} && !@{$print->layers->[-1]->lines};
return $print;
}
sub _facet {
my $self = shift;
my ($print, $normal, @vertices) = @_;
Slic3r::debugf "\n==> FACET (%f,%f,%f - %f,%f,%f - %f,%f,%f):\n", map @$_, @vertices
if $Slic3r::debug;
# find the vertical extents of the facet
my ($min_z, $max_z) = (99999999999, -99999999999);
foreach my $vertex (@vertices) {
$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;
if ($min_z == $max_z) {
Slic3r::debugf "Facet is horizontal; ignoring\n";
return;
}
# calculate the layer extents
# (the -1 and +1 here are used as a quick and dirty replacement for some
# complex calculation of the first layer height ratio logic)
my $min_layer = int($min_z * $Slic3r::resolution / $Slic3r::layer_height) - 1;
$min_layer = 0 if $min_layer < 0;
my $max_layer = int($max_z * $Slic3r::resolution / $Slic3r::layer_height) + 1;
Slic3r::debugf "layers: min = %s, max = %s\n", $min_layer, $max_layer;
# reorder vertices so that the first one is the one with lowest Z
# this is needed to get all intersection lines in a consistent order
# (external on the right of the line)
{
my @z_order = sort { $vertices[$a][Z] <=> $vertices[$b][Z] } 0..2;
@vertices = (splice(@vertices, $z_order[0]), splice(@vertices, 0, $z_order[0]));
}
for (my $layer_id = $min_layer; $layer_id <= $max_layer; $layer_id++) {
my $layer = $print->layer($layer_id);
$layer->add_line($_) for $self->intersect_facet(\@vertices, $layer->slice_z);
}
}
sub intersect_facet {
my $self = shift;
my ($vertices, $z) = @_;
# build the three segments of the triangle facet
my @edges = (
[ $vertices->[0], $vertices->[1] ],
[ $vertices->[1], $vertices->[2] ],
[ $vertices->[2], $vertices->[0] ],
);
my (@lines, @intersection_points) = ();
foreach my $edge (@edges) {
my ($a, $b) = @$edge;
#printf "Az = %d, Bz = %d, z = %d\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 $_->[Z] > $z, @$vertices) ? 'Bottom' : 'Top';
($a, $b) = ($b, $a) if $edge_type eq 'Bottom';
push @lines, "Slic3r::Line::FacetEdge::$edge_type"->new(
[$a->[X], $a->[Y]], [$b->[X], $b->[Y]],
);
#print "Horizontal edge at $z!\n";
} elsif (($a->[Z] < $z && $b->[Z] > $z) || ($b->[Z] < $z && $a->[Z] > $z)) {
# edge intersects the current layer; calculate intersection
push @intersection_points, [
$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]),
];
#print "Intersects at $z!\n";
} elsif ($a->[Z] == $z) {
#print "A point on plane $z!\n";
push @intersection_points, [ $a->[X], $a->[Y] ];
} elsif ($b->[Z] == $z) {
#print "B point on plane $z!\n";
push @intersection_points, [ $b->[X], $b->[Y] ];
}
}
Slic3r::Geometry::remove_coinciding_points(\@intersection_points);
if (@intersection_points > 1 && !@lines) {
# remove coinciding points
# defensive programming:
die "Facets must intersect each plane 0 or 2 times" if @intersection_points != 2;
# check whether the two points coincide due to resolution rounding
#if ($intersection_points[0]->coincides_with($intersection_points[1])) {
# Slic3r::debugf "Points coincide; removing\n";
# return;
#}
# connect points:
push @lines, Slic3r::Line->new(@intersection_points);
#printf " intersection points = %f,%f - %f,%f\n", map @$_, @intersection_points;
}
return @lines;
}
sub read_file { sub read_file {
my $self = shift; my $self = shift;
my ($file) = @_; my ($file) = @_;
@ -238,7 +39,7 @@ sub read_file {
: _read_binary($fh, $facets); : _read_binary($fh, $facets);
close $fh; close $fh;
return $facets; return Slic3r::TriangleMesh->new(facets => $facets);
} }
sub _read_ascii { sub _read_ascii {

View file

@ -46,7 +46,7 @@ sub output {
my $g = $svg->group( my $g = $svg->group(
style => { style => {
'stroke-width' => 2, 'stroke-width' => 2,
'stroke' => 'black' || $colour || 'black', 'stroke' => $colour || 'black',
'fill' => ($type !~ /polygons/ ? 'none' : ($colour || 'grey')), 'fill' => ($type !~ /polygons/ ? 'none' : ($colour || 'grey')),
}, },
); );

View file

@ -20,7 +20,12 @@ sub go {
# skein the STL into layers # skein the STL into layers
# each layer has surfaces with holes # each layer has surfaces with holes
$self->status_cb->(10, "Processing triangulated mesh..."); $self->status_cb->(10, "Processing triangulated mesh...");
my $print = Slic3r::Print->new_from_stl($self->input_file); my $print;
{
my $mesh = Slic3r::STL->read_file($self->input_file);
$mesh->check_manifoldness;
$print = Slic3r::Print->new_from_mesh($mesh);
}
# make skirt # make skirt
$self->status_cb->(15, "Generating skirt..."); $self->status_cb->(15, "Generating skirt...");

457
lib/Slic3r/TriangleMesh.pm Normal file
View file

@ -0,0 +1,457 @@
package Slic3r::TriangleMesh;
use Moo;
use Slic3r::Geometry qw(X Y Z A B PI epsilon same_point points_coincide angle3points
merge_collinear_lines);
use XXX;
has 'facets' => (is => 'ro', default => sub { [] });
has 'edges' => (is => 'ro', default => sub { [] });
has 'edge_table' => (is => 'ro', default => sub { {} });
has 'edge_facets' => (is => 'ro', default => sub { {} });
use constant MIN => 0;
use constant MAX => 1;
sub make_edge_table {
my $self = shift;
@{$self->edges} = ();
%{$self->edge_table} = ();
%{$self->edge_facets} = ();
for (my $facet_index = 0; $facet_index <= $#{$self->facets}; $facet_index++) {
my $facet = $self->facets->[$facet_index];
foreach my $edge ($self->facet_edges($facet)) {
my $edge_id = $self->edge_id($edge);
if (!exists $self->edge_table->{$edge_id}) {
push @{$self->edges}, $edge;
$self->edge_table->{$edge_id} = $#{$self->edges};
$self->edge_facets->{$edge_id} = [];
}
my $edge_index = $self->edge_table->{$edge_id};
push @{$self->edge_facets->{$edge_id}}, $facet_index;
}
}
}
sub check_manifoldness {
my $self = shift;
$self->make_edge_table;
if (grep { @$_ != 2 } values %{$self->edge_facets}) {
warn "Warning: The input file is not manifold. You might want to check the "
. "resulting gcode before printing.\n";
}
}
sub make_loops {
my $self = shift;
my ($layer) = @_;
my @lines = @{$layer->lines};
# remove tangent edges
{
for (my $i = 0; $i <= $#lines; $i++) {
next unless defined $lines[$i] && $lines[$i]->facet_edge;
# if the line is a facet edge, find another facet edge
# having the same endpoints but in reverse order
for (my $j = $i+1; $j <= $#lines; $j++) {
next unless defined $lines[$j] && defined $lines[$j]->facet_edge;
next unless $lines[$j]->facet_edge eq $lines[$i]->facet_edge;
if (same_point($lines[$i]->a, $lines[$j]->b) && same_point($lines[$i]->b, $lines[$j]->a)) {
$lines[$j] = undef;
last;
}
}
}
}
my $sparse_lines = [ map $_->line, @lines ];
# detect closed loops
if (0) {
printf "Layer was sliced at z = %f\n", $self->slice_z * $Slic3r::resolution;
require "Slic3r/SVG.pm";
Slic3r::SVG::output(undef, "lines.svg",
lines => [ grep !$_->isa('Slic3r::Line::FacetEdge'), @lines ],
red_lines => [ grep $_->isa('Slic3r::Line::FacetEdge'), @lines ],
);
}
my (@polygons, %visited_lines, @discarded_lines, @discarded_polylines) = ();
my $detect = sub {
my @lines = @$sparse_lines;
(@polygons, %visited_lines, @discarded_lines, @discarded_polylines) = ();
my $get_point_id = sub { sprintf "%.0f,%.0f", @{$_[0]} };
my (%pointmap, @pointmap_keys) = ();
foreach my $line (@lines) {
my $point_id = $get_point_id->($line->[A]);
if (!exists $pointmap{$point_id}) {
$pointmap{$point_id} = [];
push @pointmap_keys, $line->[A];
}
push @{ $pointmap{$point_id} }, $line;
}
my $n = 0;
while (my $first_line = shift @lines) {
next if $visited_lines{ $first_line->id };
my @points = @$first_line;
my @seen_lines = ($first_line);
my %seen_points = map { $get_point_id->($points[$_]) => $_ } 0..1;
CYCLE: while (1) {
my $next_lines = $pointmap{ $get_point_id->($points[-1]) };
# shouldn't we find the point, let's try with a slower algorithm
# as approximation may make the coordinates differ
if (!$next_lines) {
my $nearest_point = nearest_point($points[-1], \@pointmap_keys);
#printf " we have a nearest point: %f,%f (%s)\n", @$nearest_point, $get_point_id->($nearest_point);
if ($nearest_point) {
local $Slic3r::Geometry::epsilon = 1000000;
$next_lines = $pointmap{$get_point_id->($nearest_point)}
if points_coincide($points[-1], $nearest_point);
}
}
if (0 && !$next_lines) {
require "Slic3r/SVG.pm";
Slic3r::SVG::output(undef, "no_lines.svg",
lines => [ grep !$_->isa('Slic3r::Line::FacetEdge'), @lines ],
red_lines => [ grep $_->isa('Slic3r::Line::FacetEdge'), @lines ],
points => [ $points[-1] ],
no_arrows => 1,
);
}
$next_lines
or die sprintf("No lines start at point %s. This shouldn't happen. Please check the model for manifoldness.\n", $get_point_id->($points[-1]));
last CYCLE if !@$next_lines;
my @ordered_next_lines = sort
{ angle3points($points[-1], $points[-2], $next_lines->[$a][B]) <=> angle3points($points[-1], $points[-2], $next_lines->[$b][B]) }
0..$#$next_lines;
#if (@$next_lines > 1) {
# Slic3r::SVG::output(undef, "next_line.svg",
# lines => $next_lines,
# red_lines => [ polyline_lines([@points]) ],
# green_lines => [ $next_lines->[ $ordered_next_lines[0] ] ],
# );
#}
my ($next_line) = splice @$next_lines, $ordered_next_lines[0], 1;
push @seen_lines, $next_line;
push @points, $next_line->[B];
my $point_id = $get_point_id->($points[-1]);
if ($seen_points{$point_id}) {
splice @points, 0, $seen_points{$point_id};
last CYCLE;
}
$seen_points{$point_id} = $#points;
}
if (@points < 4 || !points_coincide($points[0], $points[-1])) {
# discarding polyline
push @discarded_lines, @seen_lines;
if (@points > 2) {
push @discarded_polylines, [@points];
}
next;
}
$visited_lines{ $_->id } = 1 for @seen_lines;
pop @points;
Slic3r::debugf "Discovered polygon of %d points\n", scalar(@points);
push @polygons, Slic3r::Polygon->new(@points);
$polygons[-1]->cleanup;
}
};
$detect->();
# Now, if we got a clean and manifold model then @polygons would contain everything
# we need to draw our layer. In real life, sadly, things are different and it is likely
# that the above algorithm wasn't able to detect every polygon. This may happen because
# of non-manifoldness or because of many close lines, often overlapping; both situations
# make a head-to-tail search difficult.
# On the other hand, we can safely assume that every polygon we detected is correct, as
# the above algorithm is quite strict. We can take a brute force approach to connect any
# other line.
# So, let's first check what lines were not detected as part of polygons.
if (@discarded_lines) {
Slic3r::debugf " %d lines out of %d were discarded and %d polylines were not closed\n",
scalar(@discarded_lines), scalar(@lines), scalar(@discarded_polylines);
print " Warning: errors while parsing this layer (dirty or non-manifold model).\n";
print " Retrying with slower algorithm.\n";
if (0) {
require "Slic3r/SVG.pm";
Slic3r::SVG::output(undef, "layer" . $self->id . "_detected.svg",
white_polygons => \@polygons,
);
Slic3r::SVG::output(undef, "layer" . $self->id . "_discarded_lines.svg",
red_lines => \@discarded_lines,
);
Slic3r::SVG::output(undef, "layer" . $self->id . "_discarded_polylines.svg",
polylines => \@discarded_polylines,
);
}
$sparse_lines = merge_collinear_lines($sparse_lines);
eval { $detect->(); };
warn $@ if $@;
if (@discarded_lines) {
print " Warning: even slow detection algorithm threw errors. Review the output before printing.\n";
}
}
return [@polygons];
}
sub rotate {
my $self = shift;
my ($deg) = @_;
return if $deg == 0;
my $rad = Slic3r::Geometry::deg2rad($deg);
foreach my $facet (@{$self->facets}) {
my ($normal, @vertices) = @$facet;
foreach my $vertex (@vertices) {
@$vertex = (@{ +(Slic3r::Geometry::rotate_points($rad, undef, [ $vertex->[X], $vertex->[Y] ]))[0] }, $vertex->[Z]);
}
}
}
sub scale {
my $self = shift;
my ($factor) = @_;
return if $factor == 1;
foreach my $facet (@{$self->facets}) {
# transform vertex coordinates
my ($normal, @vertices) = @$facet;
foreach my $vertex (@vertices) {
$vertex->[$_] *= $factor for X,Y,Z;
}
}
}
sub move {
my $self = shift;
my (@shift) = @_;
foreach my $facet (@{$self->facets}) {
# transform vertex coordinates
my ($normal, @vertices) = @$facet;
foreach my $vertex (@vertices) {
$vertex->[$_] += $shift[$_] for X,Y,Z;
}
}
}
sub duplicate {
my $self = shift;
my (@shift) = @_;
my @new_facets = ();
foreach my $facet (@{$self->facets}) {
# transform vertex coordinates
my ($normal, @vertices) = @$facet;
push @new_facets, [ $normal ];
foreach my $vertex (@vertices) {
push @{$new_facets[-1]}, [ map $vertex->[$_] + ($shift[$_] || 0), (X,Y,Z) ];
}
}
push @{$self->facets}, @new_facets;
}
sub bounding_box {
my $self = shift;
my @extents = (map [99999999999, -99999999999], X,Y,Z);
foreach my $facet (@{$self->facets}) {
my ($normal, @vertices) = @$facet;
foreach my $vertex (@vertices) {
for (X,Y,Z) {
$extents[$_][MIN] = $vertex->[$_] if $vertex->[$_] < $extents[$_][MIN];
$extents[$_][MAX] = $vertex->[$_] if $vertex->[$_] > $extents[$_][MAX];
}
}
}
return @extents;
}
sub size {
my $self = shift;
my @extents = $self->bounding_box;
return map $extents[$_][MAX] - $extents[$_][MIN], (X,Y,Z);
}
sub _facet {
my $self = shift;
my ($print, $facet_index, $normal, @vertices) = @_;
Slic3r::debugf "\n==> FACET %d (%f,%f,%f - %f,%f,%f - %f,%f,%f):\n",
$facet_index, map @$_, @vertices
if $Slic3r::debug;
# find the vertical extents of the facet
my ($min_z, $max_z) = (99999999999, -99999999999);
foreach my $vertex (@vertices) {
$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;
if ($min_z == $max_z) {
Slic3r::debugf "Facet is horizontal; ignoring\n";
return;
}
# calculate the layer extents
# (the -1 and +1 here are used as a quick and dirty replacement for some
# complex calculation of the first layer height ratio logic)
my $min_layer = int($min_z * $Slic3r::resolution / $Slic3r::layer_height) - 1;
$min_layer = 0 if $min_layer < 0;
my $max_layer = int($max_z * $Slic3r::resolution / $Slic3r::layer_height) + 1;
Slic3r::debugf "layers: min = %s, max = %s\n", $min_layer, $max_layer;
# reorder vertices so that the first one is the one with lowest Z
# this is needed to get all intersection lines in a consistent order
# (external on the right of the line)
{
my @z_order = sort { $vertices[$a][Z] <=> $vertices[$b][Z] } 0..2;
@vertices = (splice(@vertices, $z_order[0]), splice(@vertices, 0, $z_order[0]));
}
for (my $layer_id = $min_layer; $layer_id <= $max_layer; $layer_id++) {
my $layer = $print->layer($layer_id);
my @intersections = $self->intersect_facet($facet_index, \@vertices, $layer->slice_z);
if ($facet_index =~ /^(488)$/ && $layer_id == 14) {
printf "z = %f\n", $layer->slice_z;
YYY \@intersections;
#exit if $facet_index == 488;
}
$layer->add_line($_) for @intersections;
}
}
sub intersect_facet {
my $self = shift;
my ($facet_index, $vertices, $z) = @_;
# build the three segments of the triangle facet
my @edges = $self->facet_edges($vertices);
my (@lines, @points, @intersection_points, @points_on_layer) = ();
foreach my $edge (@edges) {
my ($a, $b) = @$edge;
my $edge_id = $self->edge_id($edge);
#printf "Az = %f, Bz = %f, z = %f\n", $a->[Z], $b->[Z], $z;
if (abs($a->[Z] - $b->[Z]) < epsilon && abs($a->[Z] - $z) < epsilon) {
# edge is horizontal and belongs to the current layer
my $edge_type = (grep $_->[Z] > $z, @$vertices) ? 'bottom' : 'top';
($a, $b) = ($b, $a) if $edge_type eq 'bottom';
push @lines, Slic3r::TriangleMesh::IntersectionLine->new(
a => [$a->[X], $a->[Y]],
b => [$b->[X], $b->[Y]],
a_id => sprintf("%f,%f", @$a[X,Y]),
b_id => sprintf("%f,%f", @$b[X,Y]),
facet_edge => $edge_type,
facet_index => $facet_index,
);
#print "Horizontal edge at $z!\n";
} elsif (abs($a->[Z] - $z) < epsilon) {
#print "A point on plane $z!\n";
push @points, [ $a->[X], $a->[Y], sprintf("%f,%f", @$a[X,Y]) ];
push @points_on_layer, $#points;
} elsif (abs($b->[Z] - $z) < epsilon) {
#print "B point on plane $z!\n";
push @points, [ $b->[X], $b->[Y], sprintf("%f,%f", @$b[X,Y]) ];
push @points_on_layer, $#points;
} elsif (($a->[Z] < ($z - epsilon) && $b->[Z] > ($z + epsilon))
|| ($b->[Z] < ($z - epsilon) && $a->[Z] > ($z + epsilon))) {
# edge intersects the current layer; calculate intersection
push @points, [
$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]),
$edge_id,
$edge_id,
];
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;
}
if (@points_on_layer == 2 && @intersection_points == 0) {
if (same_point(map $points[$_], @points_on_layer)) {
return ();
}
}
if (@points) {
# defensive programming:
die "Facets must intersect each plane 0 or 2 times" if @points != 2;
# connect points:
return Slic3r::TriangleMesh::IntersectionLine->new(
a => [$points[A][X], $points[A][Y]],
b => [$points[B][X], $points[B][Y]],
a_id => $points[A][2],
b_id => $points[B][2],
facet_index => $facet_index,
prev_facet_index => ($points[A][3] ? +(grep $_ != $facet_index, @{$self->edge_facets->{$points[A][3]}})[0] || undef : undef),
next_facet_index => ($points[B][3] ? +(grep $_ != $facet_index, @{$self->edge_facets->{$points[B][3]}})[0] || undef : undef),
);
#printf " intersection points at z = %f: %f,%f - %f,%f\n", $z, map @$_, @intersection_points;
}
return ();
}
sub facet_edges {
my $self = shift;
my ($facet) = @_;
# ignore the normal if provided
my @vertices = @$facet[-3..-1];
return (
[ $vertices[0], $vertices[1] ],
[ $vertices[1], $vertices[2] ],
[ $vertices[2], $vertices[0] ],
)
}
sub edge_id {
my $self = shift;
my ($edge) = @_;
my @point_ids = map sprintf("%f,%f,%f", @$_), @$edge;
return join "-", sort @point_ids;
}
1;

View file

@ -0,0 +1,23 @@
package Slic3r::TriangleMesh::IntersectionLine;
use Moo;
has 'a' => (is => 'ro', required => 1);
has 'b' => (is => 'ro', required => 1);
has 'a_id' => (is => 'ro', required => 1);
has 'b_id' => (is => 'ro', required => 1);
has 'facet_index' => (is => 'ro', required => 1);
has 'prev_facet_index' => (is => 'ro', required => 0);
has 'next_facet_index' => (is => 'ro', required => 0);
has 'facet_edge' => (is => 'ro', default => sub {0});
sub points {
my $self = shift;
return [$self->a, $self->b];
}
sub line {
my $self = shift;
return Slic3r::Line->new($self->a, $self->b);
}
1;

View file

@ -28,21 +28,24 @@ isnt Slic3r::Geometry::line_intersection($line1, $line2, 1), undef, 'line_inters
#========================================================== #==========================================================
my $polyline = [ {
[459190000, 5152739000], [147261000, 4612464000], [147261000, 3487535000], [339887000, 3153898000], my $polyline = [
[437497000, 3438430000], [454223000, 3522515000], [523621000, 3626378000], [627484000, 3695776000], [459190000, 5152739000], [147261000, 4612464000], [147261000, 3487535000], [339887000, 3153898000],
[750000000, 3720147000], [872515000, 3695776000], [976378000, 3626378000], [1045776000, 3522515000], [437497000, 3438430000], [454223000, 3522515000], [523621000, 3626378000], [627484000, 3695776000],
[1070147000, 3400000000], [1045776000, 3277484000], [976378000, 3173621000], [872515000, 3104223000], [750000000, 3720147000], [872515000, 3695776000], [976378000, 3626378000], [1045776000, 3522515000],
[827892000, 3095347000], [698461000, 2947261000], [2540810000, 2947261000], [2852739000, 3487535000], [1070147000, 3400000000], [1045776000, 3277484000], [976378000, 3173621000], [872515000, 3104223000],
[2852739000, 4612464000], [2540810000, 5152739000], [827892000, 3095347000], [698461000, 2947261000], [2540810000, 2947261000], [2852739000, 3487535000],
]; [2852739000, 4612464000], [2540810000, 5152739000],
];
# this points belongs to $polyline
my $point = [2797980957.103410,3392691792.513960]; # this points belongs to $polyline
my $point = [2797980957.103410,3392691792.513960];
is_deeply Slic3r::Geometry::polygon_segment_having_point($polyline, $point),
[ [2540810000, 2947261000], [2852739000, 3487535000] ], local $Slic3r::Geometry::epsilon = 1E-5;
'polygon_segment_having_point'; is_deeply Slic3r::Geometry::polygon_segment_having_point($polyline, $point),
[ [2540810000, 2947261000], [2852739000, 3487535000] ],
'polygon_segment_having_point';
}
#========================================================== #==========================================================

21
t/stl.t
View file

@ -10,8 +10,10 @@ BEGIN {
} }
use Slic3r; use Slic3r;
use Slic3r::Geometry qw(X Y Z);
use XXX;
my $stl = Slic3r::STL->new; my $mesh = Slic3r::TriangleMesh->new;
my @lines; my @lines;
my $z = 20; my $z = 20;
@ -30,15 +32,20 @@ is_deeply lines(28, 20, 30), [ ], 'lower vertex on la
is_deeply lines(24, 10, 16), [ [ [4, 4], [2, 6] ] ], 'two edges intersect'; is_deeply lines(24, 10, 16), [ [ [4, 4], [2, 6] ] ], 'two edges intersect';
is_deeply lines(24, 10, 20), [ [ [4, 4], [1, 9] ] ], 'one vertex on plane and one edge intersects'; is_deeply lines(24, 10, 20), [ [ [4, 4], [1, 9] ] ], 'one vertex on plane and one edge intersects';
my @lower = $stl->intersect_facet(vertices(22, 20, 20), $z); my @lower = $mesh->intersect_facet(0, vertices(22, 20, 20), $z);
my @upper = $stl->intersect_facet(vertices(20, 20, 10), $z); my @upper = $mesh->intersect_facet(0, vertices(20, 20, 10), $z);
isa_ok $lower[0], 'Slic3r::Line::FacetEdge::Bottom', 'bottom edge on layer'; is $lower[0]->facet_edge, 'bottom', 'bottom edge on layer';
isa_ok $upper[0], 'Slic3r::Line::FacetEdge::Top', 'upper edge on layer'; is $upper[0]->facet_edge, 'top', 'upper edge on layer';
sub vertices { sub vertices {
[ map [ @{$points[$_]}, $_[$_] ], 0..2 ] [ map [ @{$points[$_]}, $_[$_] ], X,Y,Z ]
} }
sub lines { sub lines {
[ map [ map [ map sprintf('%.0f', $_), @$_ ], @$_ ], $stl->intersect_facet(vertices(@_), $z) ]; my @lines = $mesh->intersect_facet(0, vertices(@_), $z);
$_->a->[X] = sprintf('%.0f', $_->a->[X]) for @lines;
$_->a->[Y] = sprintf('%.0f', $_->a->[Y]) for @lines;
$_->b->[X] = sprintf('%.0f', $_->b->[X]) for @lines;
$_->b->[Y] = sprintf('%.0f', $_->b->[Y]) for @lines;
return [ map $_->points, @lines ];
} }