CAD-Format-STL

 view release on metacpan or  search on metacpan

lib/CAD/Format/STL.pm  view on Meta::CPAN


  @{$self->{parts}} or croak("file has no parts");

  $index ||= 0;
  exists($self->{parts}[$index]) or croak("no part $index");
  return($self->{parts}[$index]);
} # end subroutine part definition
########################################################################

=head1 I/O Methods

=head2 load

Load an STL file (auto-detects binary/ascii)

  $stl = $stl->load("filename.stl");

Optionally, explicitly declare binary mode:

  $stl = $stl->load(binary => "filename.stl");

The $self object is returned to allow e.g. chaining to C<new()>.

The filename may also be a filehandle.

=cut

sub load {
  my $self = shift;
  my ($file, @and) = @_;

  my $mode;
  if(@and) {
    (@and > 1) and croak('too many arguments to load()');
    $mode = $file;
    ($file) = @and;
  }

  # allow filehandle
  unless((ref($file) || '') eq 'GLOB') {
    open(my $fh, '<', $file) or
      die "cannot open '$file' for reading $!";
    $file = $fh;
  }

  # detection
  unless($mode) {
    unless(seek($file, 0,0)) {
      croak('must have explicit mode for non-seekable filehandle');
    }
    # now, detection...
    $mode = sub {
      my $fh = shift;
      seek($fh, 80, 0);
      my $count = eval {
        my $buf; read($fh, $buf, 4) or die;
        unpack('L', $buf);
      };
      $@ and return 'ascii'; # if we hit eof, it can't be binary
      $count or die "detection failed - no facets?";
      my $size = (stat($fh))[7];
      # calculate the expected file size
      my $expect =
        + 80 # header
        +  4 # count
        + $count * (
          + 4 # normal, pt,pt,pt (vectors)
          * 4 # bytes per value
          * 3 # values per vector
          + 2 # the trailing 'short'
        );
      return ($size == $expect) ? 'binary' : 'ascii';
    }->($file);
    seek($file, 0, 0) or die "cannot reset filehandle";
  }

  my $method = '_read_' . lc($mode);
  $self->can($method) or croak("invalid read mode '$mode'");

  $self->$method($file);
  return($self);
} # end subroutine load definition
########################################################################

=head2 _read_ascii

  $self->_read_ascii($filehandle);

=cut

sub _read_ascii {
  my $self = shift;
  my ($fh) = @_;

  my $getline = sub {
    while(my $line = <$fh>) {
      $line =~ s/\s*$//; # allow any eol
      length($line) or next;
      return($line);
    }
    return;
  };
  my $p_re = qr/([^ ]+)\s+([^ ]+)\s+([^ ]+)$/;

  my $part;
  while(my $line = $getline->()) {

    if($line =~ m/^\s*solid (.*)/) {
      $part = $self->add_part($1);
    }
    elsif($line =~ m/^\s*endsolid (.*)/) {
      my $name = $1;
      $part or die "invalid 'endsolid' entry with no current part";
      ($name eq $part->name) or
        die "end of part '$name' should have been '",
          $part->name, "'";
      $part = undef;
    }
    elsif($part) {
      my @n = ($line =~ m/^\s*facet\s+normal\s+$p_re/) or
        die "how did that happen? ($line)";



( run in 2.185 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )