

class StlImporter < Sketchup::Importer
    @@units = 'Millimeters'

    def description
	return "Stereolithography File Importer (*.stl)"
    end

    def file_extension
	return "stl"
    end

    def id
	return "com.belfry.importers.stl"
    end

    def supports_options?
	return true
    end

    def do_options
	# In a real use you would probably store this information in an
	# instance variable.
	result = UI.inputbox(['File Units:'], ['Millimeters'], ['Meters|Centimeters|Millimeters|Feet|Inches'], "Import Options")
	return if not result
	@@units = result[0]
    end

    def load_file(file_path, status)
	begin
	    file = File.new(file_path, "r")

	    model = Sketchup.active_model
	    if (Sketchup.version_number==7)
	      model.start_operation("Import STL File", true)
	    else
	      model.start_operation("Import STL File")
	    end

	    groupents = model.entities.add_group.entities

	    mesh = Geom::PolygonMesh.new
	    precis = 0.0001
	    if (@@units == 'Meter')
		unitmult = 1000.0
	    elsif (@@units == 'Centimeter')
		unitmult = 10.0
	    elsif (@@units == 'Feet')
		unitmult = 304.8
	    elsif (@@units == 'Inches')
		unitmult = 25.4
	    else
		unitmult = 1.0
	    end

	    line = file.read(5)
	    if (line == "solid")
		# ASCII STL file
		line = file.gets
		while (!file.eof)
		    line = file.gets
		    if (/endsolid .*/.match(line))
			break
		    end
		    while (!/facet *normal/.match(line))
		      line = file.gets
		    end

		    line = file.gets
		    while (!/outer *loop/.match(line))
		      line = file.gets
		    end

		    line = file.gets
		    while (!/vertex/.match(line))
		      line = file.gets
		    end
		    x1, y1, z1 = /vertex  *([-0-9.][0-9.e+-]*)  *([-0-9.][0-9.e+-]*)  *([-0-9.][0-9.e+-]*)/.match(line).captures

		    line = file.gets
		    while (!/vertex/.match(line))
		      line = file.gets
		    end
		    x2, y2, z2 = /vertex  *([-0-9.][0-9.e+-]*)  *([-0-9.][0-9.e+-]*)  *([-0-9.][0-9.e+-]*)/.match(line).captures

		    line = file.gets
		    while (!/vertex/.match(line))
		      line = file.gets
		    end
		    x3, y3, z3 = /vertex  *([-0-9.][0-9.e+-]*)  *([-0-9.][0-9.e+-]*)  *([-0-9.][0-9.e+-]*)/.match(line).captures

		    line = file.gets
		    while (!/endloop/.match(line))
		      line = file.gets
		    end

		    line = file.gets
		    while (!/endfacet/.match(line))
		      line = file.gets
		    end

		    x1 = (x1.to_f * unitmult / precis).round * precis
		    y1 = (y1.to_f * unitmult / precis).round * precis
		    z1 = (z1.to_f * unitmult / precis).round * precis
		    x2 = (x2.to_f * unitmult / precis).round * precis
		    y2 = (y2.to_f * unitmult / precis).round * precis
		    z2 = (z2.to_f * unitmult / precis).round * precis
		    x3 = (x3.to_f * unitmult / precis).round * precis
		    y3 = (y3.to_f * unitmult / precis).round * precis
		    z3 = (z3.to_f * unitmult / precis).round * precis

		    pt1 = Geom::Point3d.new(x1.mm, y1.mm, z1.mm)
		    pt2 = Geom::Point3d.new(x2.mm, y2.mm, z2.mm)
		    pt3 = Geom::Point3d.new(x3.mm, y3.mm, z3.mm)
		    mesh.add_polygon(pt1, pt2, pt3);
		end
	    else
		# Binary STL file
		file.binmode
		line = file.read(75)
		line = file.read(4)
		numtris = line.unpack('V')[0]
		while (!file.eof)
		    line = file.read(12*4+2)
		    if (line == nil or line.size < 12*4+2)
			break
		    end
		    x1, y1, z1,  x2, y2, z2,  x3, y3, z3 = line.unpack('x4x4x4 e3 e3 e3 x2')

		    x1 = (x1 * unitmult / precis).round * precis
		    y1 = (y1 * unitmult / precis).round * precis
		    z1 = (z1 * unitmult / precis).round * precis
		    x2 = (x2 * unitmult / precis).round * precis
		    y2 = (y2 * unitmult / precis).round * precis
		    z2 = (z2 * unitmult / precis).round * precis
		    x3 = (x3 * unitmult / precis).round * precis
		    y3 = (y3 * unitmult / precis).round * precis
		    z3 = (z3 * unitmult / precis).round * precis

		    pt1 = Geom::Point3d.new(x1.mm, y1.mm, z1.mm)
		    pt2 = Geom::Point3d.new(x2.mm, y2.mm, z2.mm)
		    pt3 = Geom::Point3d.new(x3.mm, y3.mm, z3.mm)
		    mesh.add_polygon(pt1, pt2, pt3);

		    numtris = numtris - 1
		    if (numtris < 1)
			break
		    end
		end
	    end
	    groupents.fill_from_mesh mesh, true, 4
	    file.close
	    model.commit_operation
	rescue => err
	    result = UI.messagebox("Exception: #{err}");
	    #result = UI.messagebox("Exception: #{err}\n#{err.backtrace.inspect}");
	    return 1
	end
	return 0
    end
end


def stl_export_file
  unitmult = 1.0
  bintype = "ASCII"

  model = Sketchup.active_model
  if (Sketchup.version_number==7)
    model.start_operation("Export STL File",true)
  else
    model.start_operation("Export STL File")
  end

  sel = model.selection
  entities = model.entities
  if sel.empty?
    entities = model.entities
  else
    entities = Sketchup.active_model.selection
  end

  if (entities.length > 0)
    begin
      currunits=Sketchup.active_model.options[0]["LengthUnit"]
      currmult = 25.4
      case currunits
      when 4
	currunits= "Meters"
      when 3
	currunits= "Centimeters"
      when 2
	currunits= "Millimeters"
      when 1
	currunits= "Feet"
      when 0
	currunits= "Inches"
      end
      unitlist="Meters|Centimeters|Millimeters|Inches|Feet"
      bintypelist="ASCII|Binary"
      prompts=["Export unit: ", "STL Type"]
      enums=[unitlist, bintypelist]
      values=[currunits, "Binary"]
      results = inputbox prompts, values, enums, "Export STL Options"
      return if not results

      case results[0]
      when "Meters"
	unitmult=1000.0
      when "Centimeters"
	unitmult=10.0
      when "Millimeters"
	unitmult=1.0
      when "Feet"
	unitmult=304.8
      when "Inches"
	unitmult=25.4
      end
      unitmult = currmult / unitmult
      bintype = results[1]

      filename = model.path
      filename = File.basename(filename)
      if (filename == "")
        filename = "model.stl"
      else
	parts = filename.split(".")
	if (not parts[0])
	  filename = filename+".stl"
	else
	  filename = parts[0]+".stl"
	end
      end
      filename = UI.savepanel("STL Export", "." , filename)
      return if not filename

      file = File.new( filename , "w" )  
      filename = File.basename(filename)
      if (bintype == "ASCII")
	if( filename == "" )
	  file.puts( "solid model" + filename)
	else
	  file.puts( "solid " + filename)
	end
      else
	file.binmode
	file.write("Binary STL File                                                                 ");
	file.write([0xdeadbeef].pack("V"));
      end
      tricount = stl_write_entities(file, entities, Geom::Transformation.new(), unitmult, bintype)
      if (bintype == "ASCII")
	if( filename == "" )
	  file.puts( "endsolid model" + filename)
	else
	  file.puts( "endsolid " + filename)
	end
      else
	file.flush
	file.seek(80)
	file.write([tricount].pack("V"));
      end
      file.close
    rescue => err
      result = UI.messagebox("Exception: #{err}");
      #result = UI.messagebox("Exception: #{err}\n#{err.backtrace.inspect}");
      return 1
    end
  end
end


def stl_write_entities(file, entities, trans, unitmult, bintype)
  tricount = 0
  entities.each do |entity|
    if (entity.typename == "Face")
      tricount = tricount + stl_write_face(file, entity, trans, unitmult, bintype)
    elsif (entity.typename == "ComponentInstance")
      tricount = tricount + stl_write_entities(file, entity.definition.entities, trans * entity.transformation, unitmult, bintype)
    elsif (entity.typename == "Group")
      tricount = tricount + stl_write_entities(file, entity.entities, trans * entity.transformation, unitmult, bintype)
    end
  end
  tricount
end


def stl_write_face(file, face, trans, unitmult, bintype)
  tricount = 0
  precis = 0.0001
  mesh = face.mesh 7
  mesh.transform! trans
  polys = mesh.polygons
  polys.each do |poly|
    if (poly.length == 3)
      norm = mesh.normal_at(poly[0].abs);
      x = (norm.x / precis).round * precis
      y = (norm.y / precis).round * precis
      z = (norm.z / precis).round * precis
      if (bintype == "ASCII")
	file.puts( " facet normal " + x.to_s + " " + y.to_s + " " + z.to_s)
	file.puts( "  outer loop")
      else
	file.write([norm.x].pack("e"));
	file.write([norm.y].pack("e"));
	file.write([norm.z].pack("e"));
      end
      for j in 0..2 do
	pt = mesh.point_at(poly[j].abs);
	x = pt.x.to_f * unitmult
	y = pt.y.to_f * unitmult
	z = pt.z.to_f * unitmult
	x = (x / precis).round * precis
	y = (y / precis).round * precis
	z = (z / precis).round * precis
	if (bintype == "ASCII")
	  file.puts("   vertex " + x.to_s + " " + y.to_s + " " + z.to_s)
	else
	  file.write([x].pack("e"));
	  file.write([y].pack("e"));
	  file.write([z].pack("e"));
	end
      end
      if (bintype == "ASCII")
	file.puts( "  endloop\n endfacet")
      else
	file.write([0].pack("v"));
      end
    end
    tricount = tricount + 1
  end
  tricount
end


if( not file_loaded?("stl_importexport.rb") )
   Sketchup.register_importer(StlImporter.new)
   add_separator_to_menu("Tools")
   UI.menu("Tools").add_item("Export to STL...") { stl_export_file }
end
file_loaded("stl_importexport.rb")

