class STLParser

Public Instance Methods

area() click to toggle source
# File lib/stl_parser.rb, line 143
def area()
  @area.abs
end
custom_unpack(sig, num_lines) click to toggle source
# File lib/stl_parser.rb, line 46
def custom_unpack(sig, num_lines)
  str = @f.read(num_lines)
  @fb.push(str)
  return str.unpack(sig) if str
  return 0
end
process(infilename) click to toggle source
# File lib/stl_parser.rb, line 163
def process(infilename)
  resetVariables()

  @f = File.open(infilename, "rb")

  # Set the file type to ascii if needed
  binary_found = false
  @f.each_line do |line|
    if(@f.gets.ascii_only? == false)
      binary_found = true
      break
    end
  end

  unless(binary_found)
    @file_type = :ascii
  end

  # Go back to beginning of the file
  @f.seek(0)

  # Get past the header info that we don't care about
  if(@file_type === :binary)
    read_binary_header()
    @num_triangles = read_length()
  else
    @f.gets
  end

  # Keep repeating until the end of the file is reached to calculate values
  while @f.eof === false do
    read_triangle()
  end

  # Close the file
  @f.close
end
read_binary_header() click to toggle source
# File lib/stl_parser.rb, line 135
def read_binary_header()
  @f.seek(@f.tell()+80)
end
read_length() click to toggle source
# File lib/stl_parser.rb, line 130
def read_length()
  length = @f.read(4).unpack("s")
  return length[0]
end
read_triangle() click to toggle source
# File lib/stl_parser.rb, line 53
def read_triangle()
  if(@file_type === :binary)
    n  = custom_unpack("eee", 12)
    p1 = custom_unpack("eee", 12)
    p2 = custom_unpack("eee", 12)
    p3 = custom_unpack("eee", 12)

    # To get past the space filler
    @f.read(2)
  else
    temp = @f.gets
    unless temp.include? 'endsolid'
      # Get the normal
      temp.sub!(/facet normal/, '').strip!
      n  = temp.split(' ').map{ |num| num.to_f }

      # get past 'outer loop'
      @f.gets

      # get vertex one
      temp = @f.gets
      p1 = temp.sub(/vertex/, '').split(' ').map{ |num| num.to_f }

      # get vertex two
      temp = @f.gets
      p2 = temp.sub(/vertex/, '').split(' ').map{ |num| num.to_f }

      # get vertex three
      temp = @f.gets
      p3 = temp.sub(/vertex/, '').split(' ').map{ |num| num.to_f }

      # Get past endloop
      @f.gets
      # Get past endfacet
      temp = @f.gets
    end
  end
  unless @f.eof
    @min_x = p1[0] if(@min_x.nil? || @min_x > p1[0])
    @min_x = p2[0] if(@min_x.nil? || @min_x > p2[0])
    @min_x = p3[0] if(@min_x.nil? || @min_x > p3[0])

    @min_y = p1[1] if(@min_y.nil? || @min_y > p1[1])
    @min_y = p2[1] if(@min_y.nil? || @min_y > p2[1])
    @min_y = p3[1] if(@min_y.nil? || @min_y > p3[1])

    @min_z = p1[2] if(@min_z.nil? || @min_z > p1[2])
    @min_z = p2[2] if(@min_z.nil? || @min_z > p2[2])
    @min_z = p3[2] if(@min_z.nil? || @min_z > p3[2])

    @max_x = p1[0] if(@max_x.nil? || @max_x < p1[0])
    @max_x = p2[0] if(@max_x.nil? || @max_x < p2[0])
    @max_x = p3[0] if(@max_x.nil? || @max_x < p3[0])

    @max_y = p1[1] if(@max_y.nil? || @max_y < p1[1])
    @max_y = p2[1] if(@max_y.nil? || @max_y < p2[1])
    @max_y = p3[1] if(@max_y.nil? || @max_y < p3[1])

    @max_z = p1[2] if(@max_z.nil? || @max_z < p1[2])
    @max_z = p2[2] if(@max_z.nil? || @max_z < p2[2])
    @max_z = p3[2] if(@max_z.nil? || @max_z < p3[2])

    @normals.push(n)
    l = @points.length
    @points.push(p1)
    @points.push(p2)
    @points.push(p3)
    @triangles.push(l)

    # Update the volume
    @volume += signedVolumeOfTriangle(p1,p2,p3)

    # Update the area
    @area += signedAreaOfTriangle(p1,p2,p3)
  end
end
resetVariables() click to toggle source
# File lib/stl_parser.rb, line 3
def resetVariables()
  @normals = []
  @points = []
  @triangles = []
  @fb = [] # debug list
  @volume = 0
  @area = 0
  @max_x = nil
  @max_y = nil
  @max_z = nil
  @min_x = nil
  @min_y = nil
  @min_z = nil
  @num_triangles = 0
  @file_type = :binary
end
signedAreaOfTriangle(p1, p2, p3) click to toggle source

Calculate area of the triangle

# File lib/stl_parser.rb, line 32
def signedAreaOfTriangle(p1, p2, p3)
  # Update the area by adding
  a = p3.zip(p1).map { |x, y| x - y }
  b = p3.zip(p2).map { |x, y| x - y }

  cx = a[1]*b[2] - a[2]*b[1]
  cy = a[2]*b[0] - a[0]*b[2]
  cz = a[0]*b[1] - a[1]*b[0]

  c = [cx, cy, cz]

  return Math.sqrt(c[0]*c[0] + c[1]*c[1] + c[2]*c[2]).abs/2
end
signedVolumeOfTriangle(p1, p2, p3) click to toggle source

Calculate volume for the 3D mesh using Tetrahedron volume

# File lib/stl_parser.rb, line 21
def signedVolumeOfTriangle(p1, p2, p3)
  v321 = p3[0]*p2[1]*p1[2]
  v231 = p2[0]*p3[1]*p1[2]
  v312 = p3[0]*p1[1]*p2[2]
  v132 = p1[0]*p3[1]*p2[2]
  v213 = p2[0]*p1[1]*p3[2]
  v123 = p1[0]*p2[1]*p3[2]
  return (1.0/6.0)*(-v321 + v231 + v312 - v132 - v213 + v123)
end
triangles() click to toggle source
# File lib/stl_parser.rb, line 147
def triangles()
  @num_triangles
end
volume() click to toggle source
# File lib/stl_parser.rb, line 139
def volume()
  @volume.abs
end
x_dimensions() click to toggle source
# File lib/stl_parser.rb, line 151
def x_dimensions()
  @max_x - @min_x
end
y_dimensions() click to toggle source
# File lib/stl_parser.rb, line 155
def y_dimensions()
  @max_y - @min_y
end
z_dimensions() click to toggle source
# File lib/stl_parser.rb, line 159
def z_dimensions()
  @max_z - @min_z
end