Files
speckle-sketchup/speckle_connector/converter/to_speckle.rb
T
izzy lyseggen 7d73ebf7d0 feat(converter): edges and preserved faces on to_speckle (#44)
* chore: update packages

* chore: add debug 2022 vscode task

* feat(converter): ngons and edges on `to_speckle`

* feat(converter): big improvements 🥳

* feat(convert): add triangulation for faces with holes

you'll lose the true face with hole on receive, but this is the best
intermediary solution that won't break other connectors
2022-06-09 17:05:09 +01:00

271 lines
7.9 KiB
Ruby

require "sketchup"
# To Speckle conversions for the ConverterSketchup
module SpeckleSystems::SpeckleConnector::ToSpeckle
def length_to_speckle(length)
length.__send__("to_#{SpeckleSystems::SpeckleConnector::SKETCHUP_UNIT_STRINGS[@units]}")
end
# convert an edge to a speckle line
def edge_to_speckle(edge)
{
speckle_type: "Objects.Geometry.Line",
applicationId: edge.persistent_id.to_s,
units: @units,
start: vertex_to_speckle(edge.start),
end: vertex_to_speckle(edge.end),
domain: speckle_interval(0, Float(edge.length)),
bbox: bounds_to_speckle(edge.bounds)
}
end
# covnert a component definition to a speckle block definition
def component_definition_to_speckle(definition)
guid = definition.guid
return @component_defs[guid] if @component_defs.key?(guid)
speckle_def = {
speckle_type: "Objects.Other.BlockDefinition",
applicationId: guid,
units: @units,
name: definition.name,
# i think the base point is always the origin?
basePoint: speckle_point,
"@geometry" => if %w[Edge Face].include?(definition.entities[0].typename)
group_mesh_to_speckle(definition)
else
definition.entities.map { |entity| convert_to_speckle(entity) }
end
}
@component_defs[guid] = speckle_def
end
# convert a component instane to a speckle block instance
def component_instance_to_speckle(instance, is_group: false)
transform = instance.transformation
{
speckle_type: "Objects.Other.BlockInstance",
applicationId: instance.guid,
is_sketchup_group: is_group,
units: @units,
bbox: bounds_to_speckle(instance.bounds),
name: instance.name == "" ? nil : instance.name,
renderMaterial: instance.material.nil? ? nil : material_to_speckle(instance.material),
transform: transform_to_speckle(transform),
"@blockDefinition" => component_definition_to_speckle(instance.definition)
}
end
def group_mesh_to_speckle(component_def)
mat_groups = {}
nested_blocks = []
lines = []
component_def.entities.each do |entity|
nested_blocks.push(component_instance_to_speckle(entity)) if entity.typename == "ComponentInstance"
next unless %w[Edge Face].include?(entity.typename)
if entity.typename == "Edge"
lines.push(edge_to_speckle(entity))
else
face = entity
# convert material
mat_id = face.material.nil? ? "none" : face.material.entityID
mat_groups[mat_id] = initialise_group_mesh(face, component_def.bounds) unless mat_groups.key?(mat_id)
if face.loops.size > 1
mesh = face.mesh
mat_groups[mat_id]["@(31250)vertices"].push(*mesh_points_to_array(mesh))
mat_groups[mat_id]["@(62500)faces"].push(*mesh_faces_to_array(mesh, mat_groups[mat_id][:pt_count] - 1))
else
mat_groups[mat_id]["@(31250)vertices"].push(*face_vertices_to_array(face))
mat_groups[mat_id]["@(62500)faces"].push(*face_indices_to_array(face, mat_groups[mat_id][:pt_count]))
end
mat_groups[mat_id][:pt_count] += face.vertices.count
end
end
mat_groups.values.map { |group| group.delete(:pt_count) }
mat_groups.values + lines + nested_blocks
end
def transform_to_speckle(transform)
t_arr = transform.to_a
{
speckle_type: "Objects.Other.Transform",
units: @units,
value: [
t_arr[0],
t_arr[4],
t_arr[8],
length_to_speckle(t_arr[12]),
t_arr[1],
t_arr[5],
t_arr[9],
length_to_speckle(t_arr[13]),
t_arr[2],
t_arr[6],
t_arr[10],
length_to_speckle(t_arr[14]),
t_arr[3],
t_arr[7],
t_arr[11],
t_arr[15]
]
}
end
def initialise_group_mesh(face, bounds)
{
speckle_type: "Objects.Geometry.Mesh",
units: @units,
bbox: bounds_to_speckle(bounds),
"@(31250)vertices" => [],
"@(62500)faces" => [],
"@(31250)textureCoordinates" => [],
pt_count: 0,
renderMaterial: face.material.nil? ? nil : material_to_speckle(face.material)
}
end
# get an array of face indices from a sketchup polygon mesh
def mesh_faces_to_array(mesh, offset)
faces = []
puts(faces)
mesh.polygons.each do |poly|
faces.push(
poly.count, *poly.map { |index| index.abs + offset }
)
end
faces
end
# get a flat array of vertices from a sketchup polygon mesh
def mesh_points_to_array(mesh)
pts_array = []
mesh.points.each do |pt|
pts_array.push(
length_to_speckle(pt[0]),
length_to_speckle(pt[1]),
length_to_speckle(pt[2])
)
end
pts_array
end
# get a flat array of face indices from a sketchup face
def face_indices_to_array(face, offset)
face_array = [face.vertices.count]
face_array.push(*face.vertices.count.times.map { |index| index + offset })
face_array
end
# get a flat array of vertices from a list of sketchup vertices
def face_vertices_to_array(face)
pts_array = []
face.vertices.each do |v|
pt = v.position
pts_array.push(length_to_speckle(pt[0]), length_to_speckle(pt[1]), length_to_speckle(pt[2]))
end
pts_array
end
def uvs_to_array(mesh)
uvs_array = []
mesh.uvs(true).each do |pt|
uvs_array.push(
length_to_speckle(pt[0] / pt[2]),
length_to_speckle(pt[1] / pt[2])
)
end
uvs_array
end
def face_to_speckle(face)
mesh = face.loops.count > 1 ? face.mesh : nil
{
speckle_type: "Objects.Geometry.Mesh",
units: @units,
renderMaterial: face.material.nil? ? nil : material_to_speckle(face.material),
bbox: bounds_to_speckle(face.bounds),
"@(31250)vertices" => mesh.nil? ? face_vertices_to_array(face) : mesh_points_to_array(mesh),
"@(62500)faces" => mesh.nil? ? face_indices_to_array(face, 0) : mesh_faces_to_array(mesh, -1)
}
end
def vertex_to_speckle(vertex)
point = vertex.position
{
speckle_type: "Objects.Geometry.Point",
units: @units,
x: length_to_speckle(point[0]),
y: length_to_speckle(point[1]),
z: length_to_speckle(point[2])
}
end
def material_to_speckle(material)
rgba = material.color.to_a
{
speckle_type: "Objects.Other.RenderMaterial",
name: material.name,
diffuse: [rgba[3] << 24 | rgba[0] << 16 | rgba[1] << 8 | rgba[2]].pack("l").unpack1("l"),
opacity: material.alpha,
emissive: -16_777_216,
metalness: 0,
roughness: 1
}
end
def bounds_to_speckle(bounds)
min_pt = bounds.min
{
speckle_type: "Objects.Geometry.Box",
units: @units,
area: 0,
volume: 0,
xSize: speckle_interval(min_pt[0], bounds.width),
ySize: speckle_interval(min_pt[1], bounds.height),
zSize: speckle_interval(min_pt[2], bounds.depth),
basePlane: speckle_plane
}
end
def speckle_interval(start_val, end_val)
{
speckle_type: "Objects.Primitive.Interval",
units: @units,
start: start_val.is_a?(Length) ? length_to_speckle(start_val) : start_val,
end: end_val.is_a?(Length) ? length_to_speckle(end_val) : end_val
}
end
def speckle_point(x = 0.0, y = 0.0, z = 0.0, vector: false)
{
speckle_type: vector ? "Objects.Geometry.Vector" : "Objects.Geometry.Point",
units: @units,
x: x.is_a?(Length) ? length_to_speckle(x) : x,
y: y.is_a?(Length) ? length_to_speckle(y) : y,
z: z.is_a?(Length) ? length_to_speckle(z) : z
}
end
def speckle_vector(x = 0.0, y = 0.0, z = 0.0)
speckle_point(x, y, z, vector: true)
end
def speckle_plane(xdir: [1, 0, 0], ydir: [0, 1, 0], normal: [0, 0, 1], origin: [0, 0, 0])
{
speckle_type: "Objects.Geometry.Plane",
units: @units,
xdir: speckle_vector(*xdir),
ydir: speckle_vector(*ydir),
normal: speckle_vector(*normal),
origin: speckle_point(*origin)
}
end
end