diff --git a/MANIFEST b/MANIFEST index b9b490a2b..36efa4b7f 100644 --- a/MANIFEST +++ b/MANIFEST @@ -26,9 +26,6 @@ lib/Slic3r/GUI/OptionsGroup.pm lib/Slic3r/GUI/SkeinPanel.pm lib/Slic3r/Layer.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/Point.pm lib/Slic3r/Polygon.pm @@ -39,6 +36,8 @@ lib/Slic3r/Skein.pm lib/Slic3r/STL.pm lib/Slic3r/Surface.pm lib/Slic3r/SVG.pm +lib/Slic3r/TriangleMesh.pm +lib/Slic3r/TriangleMesh/IntersectionLine.pm MANIFEST This list of files README.markdown slic3r.pl diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index 25dfb2ff7..2a04bc360 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -3,7 +3,7 @@ package Slic3r; use strict; use warnings; -our $VERSION = "0.5.3"; +our $VERSION = "0.5.4beta"; our $debug = 0; sub debugf { @@ -21,7 +21,6 @@ use Slic3r::Fill; use Slic3r::Geometry; use Slic3r::Layer; use Slic3r::Line; -use Slic3r::Line::FacetEdge; use Slic3r::Perimeter; use Slic3r::Point; use Slic3r::Polygon; @@ -31,6 +30,8 @@ use Slic3r::Print; use Slic3r::Skein; use Slic3r::STL; use Slic3r::Surface; +use Slic3r::TriangleMesh; +use Slic3r::TriangleMesh::IntersectionLine; # printer options our $nozzle_diameter = 0.5; @@ -60,7 +61,7 @@ our $layer_height = 0.4; our $first_layer_height_ratio = 1; our $infill_every_layers = 1; our $extrusion_width_ratio = 0; -our $flow_speed_ratio = 1.1; +our $flow_speed_ratio = 1; our $flow_width; # print options diff --git a/lib/Slic3r/GUI/SkeinPanel.pm b/lib/Slic3r/GUI/SkeinPanel.pm index fbf2d627f..60640bd40 100644 --- a/lib/Slic3r/GUI/SkeinPanel.pm +++ b/lib/Slic3r/GUI/SkeinPanel.pm @@ -4,7 +4,7 @@ use warnings; use utf8; 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); use Wx::Event qw(EVT_BUTTON); 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; undef $process_dialog; diff --git a/lib/Slic3r/Geometry.pm b/lib/Slic3r/Geometry.pm index 98ad804e2..2b88789cd 100644 --- a/lib/Slic3r/Geometry.pm +++ b/lib/Slic3r/Geometry.pm @@ -13,11 +13,11 @@ our @EXPORT_OK = qw( polygon_has_vertex polyline_length can_connect_points deg2rad rad2deg rotate_points move_points remove_coinciding_points clip_segment_polygon 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 polyline_remove_parallel_continuous_edges polyline_remove_acute_vertices 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); @@ -35,9 +35,12 @@ use constant X2 => 2; use constant Y2 => 3; our $parallel_degrees_limit = abs(deg2rad(3)); -our $epsilon = 1E-4; +our $epsilon = 1E-6; sub epsilon () { $epsilon } +sub scale ($) { $_[0] / $Slic3r::resolution } +sub unscale ($) { $_[0] * $Slic3r::resolution } + sub slope { my ($line) = @_; return undef if abs($line->[B][X] - $line->[A][X]) < epsilon; # line is vertical @@ -85,6 +88,11 @@ sub points_coincide { return 0; } +sub same_point { + my ($p1, $p2) = @_; + return $p1->[X] == $p2->[X] && $p1->[Y] == $p2->[Y]; +} + sub distance_between_points { my ($p1, $p2) = @_; return sqrt((($p1->[X] - $p2->[X])**2) + ($p1->[Y] - $p2->[Y])**2); @@ -438,6 +446,40 @@ sub collinear { 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 { my ( $x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3 ); diff --git a/lib/Slic3r/Layer.pm b/lib/Slic3r/Layer.pm index 754543f0f..bc4216889 100644 --- a/lib/Slic3r/Layer.pm +++ b/lib/Slic3r/Layer.pm @@ -2,8 +2,7 @@ package Slic3r::Layer; use Moo; use Math::Clipper ':all'; -use Slic3r::Geometry qw(polygon_lines points_coincide angle3points polyline_lines nearest_point - line_length collinear X Y A B PI); +use Slic3r::Geometry qw(collinear X Y A B PI); use Slic3r::Geometry::Clipper qw(union_ex diff_ex intersection_ex PFT_EVENODD); use XXX; @@ -18,7 +17,7 @@ has 'id' => ( # these need to be merged in continuos (closed) polylines has 'lines' => ( is => 'rw', - #isa => 'ArrayRef[Slic3r::Line]', + #isa => 'ArrayRef[Slic3r::TriangleMesh::IntersectionLine]', default => sub { [] }, ); @@ -98,202 +97,19 @@ sub add_line { my $self = shift; my ($line) = @_; - return if $line->a->coincides_with($line->b); - push @{ $self->lines }, $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 sub make_surfaces { my $self = shift; - - 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 ($loops) = @_; { - 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", - scalar(@$expolygons), scalar(map $_->holes, @$expolygons), scalar(@polygons); + scalar(@$expolygons), scalar(map $_->holes, @$expolygons), scalar(@$loops); push @{$self->surfaces}, map Slic3r::Surface->cast_from_expolygon($_, surface_type => 'internal'), diff --git a/lib/Slic3r/Line/FacetEdge.pm b/lib/Slic3r/Line/FacetEdge.pm deleted file mode 100644 index 411b1b14a..000000000 --- a/lib/Slic3r/Line/FacetEdge.pm +++ /dev/null @@ -1,9 +0,0 @@ -package Slic3r::Line::FacetEdge; -use Moo; - -extends 'Slic3r::Line'; - -use Slic3r::Line::FacetEdge::Bottom; -use Slic3r::Line::FacetEdge::Top; - -1; diff --git a/lib/Slic3r/Line/FacetEdge/Bottom.pm b/lib/Slic3r/Line/FacetEdge/Bottom.pm deleted file mode 100644 index 9efc84da7..000000000 --- a/lib/Slic3r/Line/FacetEdge/Bottom.pm +++ /dev/null @@ -1,6 +0,0 @@ -package Slic3r::Line::FacetEdge::Bottom; -use Moo; - -extends 'Slic3r::Line::FacetEdge'; - -1; diff --git a/lib/Slic3r/Line/FacetEdge/Top.pm b/lib/Slic3r/Line/FacetEdge/Top.pm deleted file mode 100644 index c08599eb7..000000000 --- a/lib/Slic3r/Line/FacetEdge/Top.pm +++ /dev/null @@ -1,6 +0,0 @@ -package Slic3r::Line::FacetEdge::Top; -use Moo; - -extends 'Slic3r::Line::FacetEdge'; - -1; diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index ae7b96858..cfba9561d 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -2,11 +2,14 @@ package Slic3r::Print; use Moo; 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 union_ex offset JT_ROUND JT_MITER); use XXX; +use constant MIN => 0; +use constant MAX => 1; + has 'x_length' => ( is => 'ro', required => 1, @@ -26,11 +29,59 @@ has 'layers' => ( default => sub { [] }, ); -sub new_from_stl { - my $self = shift; - my ($stl_file) = @_; +sub new_from_mesh { + my $class = shift; + 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"; foreach my $layer (@{ $print->layers }) { @@ -44,7 +95,7 @@ sub new_from_stl { # inside a closed polyline) # build surfaces from sparse lines - $layer->make_surfaces; + $layer->make_surfaces($mesh->make_loops($layer)); } return $print; diff --git a/lib/Slic3r/STL.pm b/lib/Slic3r/STL.pm index 4536c8892..ae964c4ef 100644 --- a/lib/Slic3r/STL.pm +++ b/lib/Slic3r/STL.pm @@ -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 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 { my $self = shift; my ($file) = @_; @@ -238,7 +39,7 @@ sub read_file { : _read_binary($fh, $facets); close $fh; - return $facets; + return Slic3r::TriangleMesh->new(facets => $facets); } sub _read_ascii { diff --git a/lib/Slic3r/SVG.pm b/lib/Slic3r/SVG.pm index f1bdb932b..65921b23e 100644 --- a/lib/Slic3r/SVG.pm +++ b/lib/Slic3r/SVG.pm @@ -46,7 +46,7 @@ sub output { my $g = $svg->group( style => { 'stroke-width' => 2, - 'stroke' => 'black' || $colour || 'black', + 'stroke' => $colour || 'black', 'fill' => ($type !~ /polygons/ ? 'none' : ($colour || 'grey')), }, ); diff --git a/lib/Slic3r/Skein.pm b/lib/Slic3r/Skein.pm index 617cd61f2..7e0a83434 100644 --- a/lib/Slic3r/Skein.pm +++ b/lib/Slic3r/Skein.pm @@ -20,7 +20,12 @@ sub go { # skein the STL into layers # each layer has surfaces with holes $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 $self->status_cb->(15, "Generating skirt..."); diff --git a/lib/Slic3r/TriangleMesh.pm b/lib/Slic3r/TriangleMesh.pm new file mode 100644 index 000000000..a2120a920 --- /dev/null +++ b/lib/Slic3r/TriangleMesh.pm @@ -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; diff --git a/lib/Slic3r/TriangleMesh/IntersectionLine.pm b/lib/Slic3r/TriangleMesh/IntersectionLine.pm new file mode 100644 index 000000000..9e2ca9be0 --- /dev/null +++ b/lib/Slic3r/TriangleMesh/IntersectionLine.pm @@ -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; diff --git a/t/geometry.t b/t/geometry.t index cd06d6f37..bc81cb610 100644 --- a/t/geometry.t +++ b/t/geometry.t @@ -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], - [437497000, 3438430000], [454223000, 3522515000], [523621000, 3626378000], [627484000, 3695776000], - [750000000, 3720147000], [872515000, 3695776000], [976378000, 3626378000], [1045776000, 3522515000], - [1070147000, 3400000000], [1045776000, 3277484000], [976378000, 3173621000], [872515000, 3104223000], - [827892000, 3095347000], [698461000, 2947261000], [2540810000, 2947261000], [2852739000, 3487535000], - [2852739000, 4612464000], [2540810000, 5152739000], -]; - -# 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] ], - 'polygon_segment_having_point'; +{ + my $polyline = [ + [459190000, 5152739000], [147261000, 4612464000], [147261000, 3487535000], [339887000, 3153898000], + [437497000, 3438430000], [454223000, 3522515000], [523621000, 3626378000], [627484000, 3695776000], + [750000000, 3720147000], [872515000, 3695776000], [976378000, 3626378000], [1045776000, 3522515000], + [1070147000, 3400000000], [1045776000, 3277484000], [976378000, 3173621000], [872515000, 3104223000], + [827892000, 3095347000], [698461000, 2947261000], [2540810000, 2947261000], [2852739000, 3487535000], + [2852739000, 4612464000], [2540810000, 5152739000], + ]; + + # this points belongs to $polyline + my $point = [2797980957.103410,3392691792.513960]; + + local $Slic3r::Geometry::epsilon = 1E-5; + is_deeply Slic3r::Geometry::polygon_segment_having_point($polyline, $point), + [ [2540810000, 2947261000], [2852739000, 3487535000] ], + 'polygon_segment_having_point'; +} #========================================================== diff --git a/t/stl.t b/t/stl.t index 51b38ca75..278e4041d 100644 --- a/t/stl.t +++ b/t/stl.t @@ -10,8 +10,10 @@ BEGIN { } use Slic3r; +use Slic3r::Geometry qw(X Y Z); +use XXX; -my $stl = Slic3r::STL->new; +my $mesh = Slic3r::TriangleMesh->new; my @lines; 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, 20), [ [ [4, 4], [1, 9] ] ], 'one vertex on plane and one edge intersects'; -my @lower = $stl->intersect_facet(vertices(22, 20, 20), $z); -my @upper = $stl->intersect_facet(vertices(20, 20, 10), $z); -isa_ok $lower[0], 'Slic3r::Line::FacetEdge::Bottom', 'bottom edge on layer'; -isa_ok $upper[0], 'Slic3r::Line::FacetEdge::Top', 'upper edge on layer'; +my @lower = $mesh->intersect_facet(0, vertices(22, 20, 20), $z); +my @upper = $mesh->intersect_facet(0, vertices(20, 20, 10), $z); +is $lower[0]->facet_edge, 'bottom', 'bottom edge on layer'; +is $upper[0]->facet_edge, 'top', 'upper edge on layer'; sub vertices { - [ map [ @{$points[$_]}, $_[$_] ], 0..2 ] + [ map [ @{$points[$_]}, $_[$_] ], X,Y,Z ] } 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 ]; }