123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210 |
- package Slic3r::STL;
- use Moo;
- use Slic3r::Geometry qw(X Y Z triangle_normal);
- use XXX;
- sub read_file {
- my $self = shift;
- my ($file) = @_;
-
- open my $fh, '<', $file or die "Failed to open $file\n";
-
- # let's detect whether file is ASCII or binary
- my $mode;
- {
- my $size = +(stat $fh)[7];
- $mode = 'ascii' if $size < 80 + 4;
-
- # skip binary header
- seek $fh, 80, 0;
- read $fh, my $buf, 4;
- my $triangle_count = unpack 'L', $buf;
- die "STL file seems invalid, could not read facet count\n" if !defined $triangle_count;
- my $expected_size =
- + 80 # header
- + 4 # count
- + $triangle_count * (
- + 4 # normal, pt,pt,pt (vectors)
- * 4 # bytes per value
- * 3 # values per vector
- + 2 # the trailing 'short'
- );
- $mode = ($size == $expected_size) ? 'binary' : 'ascii';
- }
-
- my $facets = [];
- $mode eq 'ascii'
- ? _read_ascii($fh, $facets)
- : _read_binary($fh, $facets);
- close $fh;
-
- my $vertices = [];
- {
- my %vertices_map = (); # given a vertex's coordinates, what's its index?
- my @vertices_facets = (); # given a vertex index, what are the indexes of its tangent facets?
- for (my $f = 0; $f <= $#$facets; $f++) {
- for (1..3) {
- my $point_id = join ',', @{$facets->[$f][$_]};
- if (exists $vertices_map{$point_id}) {
- $facets->[$f][$_] = $vertices_map{$point_id};
- push @{$vertices_facets[$facets->[$f][$_]]}, $f;
- } else {
- push @$vertices, $facets->[$f][$_];
- $facets->[$f][$_] = $vertices_map{$point_id} = $#$vertices;
- $vertices_facets[$#$vertices] = [$f];
- }
- }
- }
-
- # The following loop checks that @vertices_facets only groups facets that
- # are really connected together (i.e. neighbors or sharing neighbors);
- # in other words it takes care of multiple vertices occupying the same
- # point in space. It enforces topological correctness which is needed by
- # the slicing algorithm.
- # I'm keeping it disabled until I find a good test case.
- if (0) {
- my $vertices_count = $#$vertices; # store it to avoid processing newly created vertices
- for (my $v = 0; $v <= $vertices_count; $v++) {
- my $more_than_one_vertex_in_this_point = 0;
- while (@{$vertices_facets[$v]}) {
- my @facets_indexes = @{$vertices_facets[$v]};
- @{$vertices_facets[$v]} = ();
-
- my @this_f = shift @facets_indexes;
- CYCLE: while (@facets_indexes && @this_f) {
-
- # look for a facet that is connected to $this_f[-1] and whose common line contains $v
- my @other_vertices_indexes = grep $_ != $v, @{$facets->[$this_f[-1]]}[1..3];
-
- OTHER: for my $other_f (@facets_indexes) {
- # facet is connected if it shares one more point
- for (grep $_ != $v, @{$facets->[$other_f]}[1..3]) {
- if ($_ ~~ @other_vertices_indexes) {
- #printf "facet %d is connected to $other_f (sharing vertices $v and $_)\n", $this_f[-1];
-
- # TODO: we should ensure that the common edge has a different orientation
- # for each of the two adjacent facets
-
- push @this_f, $other_f;
- @facets_indexes = grep $_ != $other_f, @facets_indexes;
- next CYCLE;
- }
- }
- }
- # if we're here, then we couldn't find any facet connected to $this_f[-1]
- # so we should move this one to a different cluster (that is, a new vertex)
- # (or ignore it if it turns to be a non-manifold facet)
- if (@this_f > 1) {
- push @{$vertices_facets[$v]}, $this_f[-1];
- pop @this_f;
- $more_than_one_vertex_in_this_point++;
- } else {
- last CYCLE;
- }
- }
-
- if ($more_than_one_vertex_in_this_point) {
- Slic3r::debugf " more than one vertex in the same point\n";
- push @$vertices, $vertices->[$v];
- for my $f (@this_f) {
- $facets->[$f][$_] = $#$vertices for grep $facets->[$f][$_] == $v, 1..3;
- }
- }
- }
- }
- }
- }
-
- return Slic3r::TriangleMesh->new(vertices => $vertices, facets => $facets);
- }
- sub _read_ascii {
- my ($fh, $facets) = @_;
-
- my $point_re = qr/([^ ]+)\s+([^ ]+)\s+([^ ]+)\s*$/;
-
- my $facet;
- seek $fh, 0, 0;
- while (my $_ = <$fh>) {
- s/\R+$//;
- if (!$facet) {
- /^\s*facet\s+normal\s+$point_re/ or next;
- $facet = [ [$1, $2, $3] ];
- } else {
- if (/^\s*endfacet/) {
- push @$facets, $facet;
- undef $facet;
- } else {
- /^\s*vertex\s+$point_re/ or next;
- push @$facet, [map $_ * 1, $1, $2, $3];
- }
- }
- }
- if ($facet) {
- die "STL file seems invalid\n";
- }
- }
- sub _read_binary {
- my ($fh, $facets) = @_;
-
- die "bigfloat" unless length(pack "f", 1) == 4;
-
- binmode $fh;
- seek $fh, 80 + 4, 0;
- while (read $fh, my $_, 4*4*3+2) {
- my @v = unpack '(f<3)4';
- push @$facets, [ [@v[0..2]], [@v[3..5]], [@v[6..8]], [@v[9..11]] ];
- }
- }
- sub write_file {
- my $self = shift;
- my ($file, $mesh, $binary) = @_;
-
- open my $fh, '>', $file;
-
- $binary
- ? _write_binary($fh, $mesh)
- : _write_ascii($fh, $mesh);
-
- close $fh;
- }
- sub _write_binary {
- my ($fh, $mesh) = @_;
-
- die "bigfloat" unless length(pack "f", 1) == 4;
-
- binmode $fh;
- print $fh pack 'x80';
- print $fh pack 'L', scalar(@{$mesh->facets});
- foreach my $facet (@{$mesh->facets}) {
- print $fh pack '(f<3)4S',
- @{_facet_normal($mesh, $facet)},
- (map @{$mesh->vertices->[$_]}, @$facet[1,2,3]),
- 0;
- }
- }
- sub _write_ascii {
- my ($fh, $mesh) = @_;
-
- printf $fh "solid\n";
- foreach my $facet (@{$mesh->facets}) {
- printf $fh " facet normal %f %f %f\n", @{_facet_normal($mesh, $facet)};
- printf $fh " outer loop\n";
- printf $fh " vertex %f %f %f\n", @{$mesh->vertices->[$_]} for @$facet[1,2,3];
- printf $fh " endloop\n";
- printf $fh " endfacet\n";
- }
- printf $fh "endsolid\n";
- }
- sub _facet_normal {
- my ($mesh, $facet) = @_;
- return triangle_normal(map $mesh->vertices->[$_], @$facet[1,2,3]);
- }
- 1;
|