# SketchUp to DXF or STL Converter
# Last edited: 26 February 2009
# Authors: Nathan Bromham, Konrad Shroeder
# based on the KML exporter of Michael Ashbridge

require 'sketchup.rb'

def stldxf_erase_mesh
   model = Sketchup.active_model
   ent_arr=[]
   model.entities.each do |element|
      if(element.layer.name == "MESH_data"  )
         ent_arr.push element
      end
   end
   ent_arr.each {|e| e.erase! if not e.deleted?}
end

def stldxf_export_mesh_file ( file_type )
   if (file_type == "dxf") || (file_type == "stl")
      model = Sketchup.active_model
      ss = model.selection
      $stl_conv = 1.0
      dxf_option = "meshed triangles"
      entities = model.entities
      faces_array = []
      edges_array = []
      model.start_operation "export_dxf_mesh"
      if ss.empty?
         answer = UI.messagebox("No objects selected. Export entire model?", MB_YESNOCANCEL)
         if( answer == 6 )
            export_ents = model.entities
         else
            export_ents = ss
         end
      else
         export_ents = Sketchup.active_model.selection
      end
      if (export_ents.length > 0)
         (stldxf_erase_mesh)
         #get units for export
         stldxf_dxf_units_dialog
         #get DXF export options
         if (file_type == "dxf")
            dxf_option = stldxf_dxf_options_dialog()
         end
         #exported file name
         out_name = UI.savepanel( file_type+" file location", "." , "#{File.basename(model.path).split(".")[0]}." +file_type )
         mesh_file = File.new( out_name , "w" )
         # Recursively count faces and edges, exploding groups as we go.
         #Count "other" objects we can't parse.
         others = stldxf_find_faces(0, faces_array, edges_array, export_ents, Geom::Transformation.new())
         #layers
         model.layers.add("MESH_data")
         old_layer = model.active_layer
         model.active_layer = ("MESH_data")
         #convert the selection to triangles
         faces_array.each do |faceTform|
            face = faceTform[0]
            tform = faceTform[1]
            mesh=face.mesh(7)
            mesh.transform!(tform)
            normal = mesh.normal_at 1
            Sketchup.active_model.entities.add_faces_from_mesh mesh, 9
         end
         (stldxf_mesh_bucket)
         if (file_type == "stl")
            (stldxf_export_mesh_stl mesh_file)
         elsif (dxf_option == "meshed triangles")
            (stldxf_export_mesh_dxf mesh_file)
         elsif (dxf_option == "polylines")
            (stldxf_export_polylines mesh_file, faces_array)
         else
            (stldxf_export_edges mesh_file, edges_array)
         end
         (stldxf_erase_mesh)
         model.active_layer = old_layer
         model.layers.purge_unused
         if (file_type == "lines")
            UI.messagebox( edges_array.length.to_s + " edges converted to " + $linecount.to_s + " lines\n" + others.to_s + " objects ignored" )
         elsif (file_type == "polylines")
            UI.messagebox( edges_array.length.to_s + " faces converted to " + $linecount.to_s + " polylines\n" + others.to_s + " objects ignored" )
         else
            UI.messagebox( faces_array.length.to_s + " faces converted to " + $triangles_array.length.to_s + " #{dxf_option}\n" + others.to_s + " objects ignored" )
         end
         $triangles_array = []
      end
      model.commit_operation
   end
end

def stldxf_find_faces(others, faces_array, edges_array, entities, tform)
   entities.each do |entity|
      if( entity.typename == "Face")
         faces_array.push [entity, tform]
      elsif( entity.typename == "Edge")
         edges_array.push [entity, tform]
      elsif( entity.typename == "Group")
         others = stldxf_find_faces(others, faces_array, edges_array, entity.entities, tform)
      elsif( entity.typename == "ComponentInstance")
         others = stldxf_find_faces(others, faces_array, edges_array, entity.definition.entities, tform * entity.transformation)
      else
         others = others + 1
      end
   end
   others
end

def stldxf_transform_edge(edge, tform)
   points=[]
   points.push(transform_vertex(edge.start, tform))
   points.push(transform_vertex(edge.end, tform))
   points
end

def stldxf_transform_vertex(vertex, tform)
   point = Geom::Point3d.new(vertex.position.x, vertex.position.y, vertex.position.z)
   point.transform! tform
   point
end

def stldxf_mesh_bucket
   model = Sketchup.active_model
   $triangles_array = []
   entities = model.entities
   i = 0
   entities.each do |f|
      element = entities[i]
      if(element.typename == "Face" and element.layer.name == "MESH_data")
         $triangles_array.push element
      end
      i = i + 1
   end
end

def stldxf_export_edges( dxf_file, edges_array )
   #export the model as lines
   model = Sketchup.active_model
   model_filename = File.basename(model.path)
   if( model_filename == "" )
      model_filename = "no_name"
   end
   model_name = model_filename.split(".")[0]
   dxf_file.puts( " 0\nSECTION\n 2\nENTITIES")
   i = 0
   edges_array.each do |edgeTform|
      edge = edgeTform[0]
      tform = edgeTform[1]
      points = stldxf_transform_edge(edge, tform)
      dxf_file.puts( "  0\nLINE\n  8\nMY3DLAYER")
      for j in 0..1 do
         dxf_file.puts((10+j).to_s+"\n"+(points[j].x.to_f * $stl_conv).to_s)#x
         dxf_file.puts((20+j).to_s+"\n"+(points[j].y.to_f * $stl_conv).to_s)#y
         dxf_file.puts((30+j).to_s+"\n"+(points[j].z.to_f * $stl_conv).to_s)#z
      end
      i = i + 1
      done = ((i * 100.0) /edges_array.length).to_i
      Sketchup.set_status_text("Exporting edges: " + done.to_s + "%")
   end
   $linecount = i
   dxf_file.puts( "  0\nENDSEC\n  0\nEOF")
   dxf_file.close
end

def stldxf_export_polylines( dxf_file, faces_array )
   #export the faces of the model as polylines
   model = Sketchup.active_model
   model_filename = File.basename(model.path)
   if( model_filename == "" )
      model_filename = "no_name"
   end
   model_name = model_filename.split(".")[0]
   dxf_file.puts( " 0\nSECTION\n 2\nENTITIES")
   i = 0
   faces_array.each do |faceTform|
      face = faceTform[0]
      tform = faceTform[1]
      face.loops.each do |aloop|
         dxf_file.puts("  0\nPOLYLINE\n 8\nDEFAULT\n 66\n     1")
         dxf_file.puts("70\n    8\n 10\n0.0\n 20\n 0.0\n 30\n0.0")
         for j in 0..aloop.vertices.length do
            if (j==aloop.vertices.length)
               count = 0
            else
               count = j
            end
            point = stldxf_transform_vertex(aloop.vertices[count],tform)
            dxf_file.puts( "  0\nVERTEX\n  8\nMY3DLAYER")
            dxf_file.puts("10\n"+(point.x.to_f * $stl_conv).to_s)
            dxf_file.puts("20\n"+(point.y.to_f * $stl_conv).to_s)
            dxf_file.puts("30\n"+(point.z.to_f * $stl_conv).to_s)
            dxf_file.puts( " 70\n     32")
         end
         if (aloop.vertices.length > 0)
            dxf_file.puts( "  0\nSEQEND")
         end
      end
      i = i + 1
      done = ((i * 100.0) /faces_array.length).to_i
      Sketchup.set_status_text("Exporting polylines: " + done.to_s + "%")
   end
   $linecount = i
   dxf_file.puts( "  0\nENDSEC\n  0\nEOF")
   dxf_file.close
end

def stldxf_export_mesh_dxf( dxf_file )
   model = Sketchup.active_model
   model_filename = File.basename(model.path)
   if( model_filename == "" )
      model_filename = "no_name"
   end
   model_name = model_filename.split(".")[0]
   dxf_file.puts( " 0\nSECTION\n 2\nENTITIES")
   i = 0
   $triangles_array.each do |tri|
      vertices = tri.vertices
      dxf_file.puts( "  0\n3DFACE\n 8\nMY3DLAYER")
      for j in 0..3 do
         if (j==3)
            count = 2
         else
            count = j
         end
         dxf_file.puts((10+j).to_s+"\n"+(vertices[count].position.x.to_f * $stl_conv).to_s)
         dxf_file.puts((20+j).to_s+"\n"+(vertices[count].position.y.to_f * $stl_conv).to_s)
         dxf_file.puts((30+j).to_s+"\n"+(vertices[count].position.z.to_f * $stl_conv).to_s)
      end
      i = i + 1
      done = ((i * 100.0) / $triangles_array.length).to_i
      Sketchup.set_status_text("Exporting mesh: " + done.to_s + "%")
   end
   dxf_file.puts( "  0\nENDSEC\n 0\nEOF")
   dxf_file.close
end

def stldxf_export_mesh_stl( stl_file )
   model = Sketchup.active_model
   model_filename = File.basename(model.path)
   if( model_filename == "" )
      model_filename = "no_name"
   end
   model_name = model_filename.split(".")[0]
   stl_file.puts( "solid " + model_filename)
   i = 0
   $triangles_array.each do |tri|
      vertices = tri.vertices
      stl_file.puts( "facet normal " + tri.normal.x.to_s + " " + tri.normal.y.to_s + " " + tri.normal.z.to_s)
      stl_file.puts( "outer loop")
      for j in 0..2 do
         stl_file.puts("vertex " + (vertices[j].position.x.to_f * $stl_conv).to_s + " " + (vertices[j].position.y.to_f * $stl_conv).to_s + " " + (vertices[j].position.z.to_f * $stl_conv).to_s)
      end
      stl_file.puts( "endloop")
      stl_file.puts( "endfacet")
      i = i + 1
      done = ((i * 100.0) / $triangles_array.length).to_i
      Sketchup.set_status_text("Exporting triangle mesh: " + done.to_s + "%")
   end
   stl_file.puts( "endsolid " + model_filename)
   stl_file.close
end

def stldxf_dxf_options_dialog()
   options_list=["meshed triangles","polylines","lines"].join("|")
   prompts=["Export to DXF options "]
   enums=[options_list]
   values=["meshed triangles"]
   results = inputbox prompts, values, enums, "Choose which entities to export"
   return if not results
   results[0]
end

def stldxf_dxf_units_dialog
   cu=Sketchup.active_model.options[0]["LengthUnit"]
   case cu
   when 4
      current_unit= "Meters"
   when 3
      current_unit= "Centimeters"
   when 2
      current_unit= "Millimeters"
   when 1
      current_unit= "Feet"
   when 0
      current_unit= "Inches"
   end
   units_list=["Meters","Centimeters","Millimeters","Inches","Feet"].join("|")
   prompts=["Export unit: "]
   enums=[units_list]
   values=[current_unit]
   results = inputbox prompts, values, enums, "Export units"
   return if not results
   case results[0]
   when "Meters"
      $stl_conv=0.0254
   when "Centimeters"
      $stl_conv=2.54
   when "Millimeters"
      $stl_conv=25.4
   when "Feet"
      $stl_conv=0.0833333333333333
   when "Inches"
      $stl_conv=1
   end
end

if( not file_loaded?("sketchup_to_dxf_stl.rb") )
   add_separator_to_menu("Tools")
   UI.menu("Tools").add_item("Export as DXF") { stldxf_export_mesh_file( "dxf" ) }
   UI.menu("Tools").add_item("Export as STL") { stldxf_export_mesh_file( "stl" ) }
end

file_loaded("sketchup_to_dxf_stl.rb")
