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
This commit is contained in:
Vendored
+10
-1
@@ -12,6 +12,15 @@
|
||||
"command": "&'C:/Program Files/SketchUp/SketchUp 2021/SketchUp.exe' -rdebug 'ide port=7000'",
|
||||
},
|
||||
"problemMatcher": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "Debug SketchUp 2022",
|
||||
"type": "shell",
|
||||
"command": "open -a '/Applications/SketchUp 2022/SketchUp.app' --args -rdebug 'ide port=7000'",
|
||||
"windows": {
|
||||
"command": "&'C:/Program Files/SketchUp/SketchUp 2022/SketchUp.exe' -rdebug 'ide port=7000'",
|
||||
},
|
||||
"problemMatcher": []
|
||||
},
|
||||
]
|
||||
}
|
||||
@@ -9,11 +9,13 @@ module SpeckleSystems::SpeckleConnector
|
||||
include ToNative
|
||||
include ToSpeckle
|
||||
|
||||
attr_accessor :units, :component_defs
|
||||
attr_accessor :units, :component_defs, :registry, :entity_observer
|
||||
|
||||
def initialize(units = "m")
|
||||
@units = units
|
||||
@component_defs = {}
|
||||
# @registry = Sketchup.active_model.attribute_dictionary("speckle_id_registry", true)
|
||||
# @entity_observer = SpeckleEntityObserver.new
|
||||
end
|
||||
|
||||
def convert_to_speckle(obj)
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
module SpeckleSystems::SpeckleConnector
|
||||
class SpeckleEntityObserver < Sketchup::EntityObserver
|
||||
attr_accessor :registry
|
||||
|
||||
def initialize
|
||||
super()
|
||||
@registry = Sketchup.active_model.attribute_dictionary("speckle_id_registry", true)
|
||||
end
|
||||
|
||||
def onEraseEntity(entity)
|
||||
app_id = entity.get_attribute("speckle", "applicationId")
|
||||
return if app_id.nil?
|
||||
|
||||
p(app_id)
|
||||
|
||||
@registry.delete_key(app_id)
|
||||
|
||||
p(@registry)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -38,23 +38,56 @@ module SpeckleSystems::SpeckleConnector::ToNative
|
||||
["Objects.BuiltElements.Revit.Parameter"].include?(obj["speckle_type"])
|
||||
end
|
||||
|
||||
def convert_to_native(obj, entities = SketchUp.active_model.entities)
|
||||
def convert_to_native(obj, entities = Sketchup.active_model.entities)
|
||||
puts(">>> Converting #{obj["speckle_type"]}: #{obj["id"]}")
|
||||
|
||||
case obj["speckle_type"]
|
||||
when "Objects.Geometry.Line", "Objects.Geometry.Polyline" then edge_to_native(obj, entities)
|
||||
when "Objects.Geometry.Line", "Objects.Geometry.Polyline" then if entities == Sketchup.active_model.entities
|
||||
edge_to_native_component(obj, entities)
|
||||
else
|
||||
edge_to_native(obj, entities)
|
||||
end
|
||||
when "Objects.Other.BlockInstance" then component_instance_to_native(obj, entities)
|
||||
when "Objects.Other.BlockDefinition" then component_definition_to_native(obj)
|
||||
when "Objects.Geometry.Mesh" then mesh_to_native(obj, entities)
|
||||
when "Objects.Geometry.Brep" then mesh_to_native(obj["displayMesh"], entities)
|
||||
when "Objects.Geometry.Mesh" then if entities == Sketchup.active_model.entities
|
||||
mesh_to_native_component(obj, entities)
|
||||
else
|
||||
mesh_to_native(obj, entities)
|
||||
end
|
||||
when "Objects.Geometry.Brep" then if entities == Sketchup.active_model.entities
|
||||
mesh_to_native_component(obj["displayMesh"], entities)
|
||||
else
|
||||
mesh_to_native(obj["displayMesh"], entities)
|
||||
end
|
||||
when obj.key?["displayValue"]
|
||||
parent_id = obj["applicationId"] || obj["id"]
|
||||
obj["displayValue"].each do |o|
|
||||
o["applicationId"] = "#{parent_id}::#{o["id"]}" if o["applicationId"].nil?
|
||||
convert_to_native(o, entities)
|
||||
end
|
||||
else
|
||||
nil
|
||||
end
|
||||
# rescue StandardError => e
|
||||
# puts("Failed to convert #{obj["speckle_type"]} (id: #{obj["id"]})")
|
||||
# puts(e)
|
||||
# nil
|
||||
rescue StandardError => e
|
||||
puts("Failed to convert #{obj["speckle_type"]} (id: #{obj["id"]})")
|
||||
puts(e)
|
||||
nil
|
||||
end
|
||||
|
||||
# def register_receive(entity, id)
|
||||
# # entity.add_observer(@entity_observer)
|
||||
# # entity.set_attribute("speckle", "applicationId", id)
|
||||
# @registry[id] = entity.persistent_id
|
||||
# end
|
||||
|
||||
# def get_received_entity(app_id)
|
||||
# return if @registry[app_id].nil?
|
||||
# end
|
||||
|
||||
# def received?(id)
|
||||
# !@registry[id].nil?
|
||||
# end
|
||||
|
||||
def length_to_native(length, units = @units)
|
||||
length.__send__(SpeckleSystems::SpeckleConnector::SKETCHUP_UNIT_STRINGS[units])
|
||||
end
|
||||
@@ -72,6 +105,15 @@ module SpeckleSystems::SpeckleConnector::ToNative
|
||||
end
|
||||
end
|
||||
|
||||
def edge_to_native_component(line, entities)
|
||||
line_id = line["applicationId"] || line["id"]
|
||||
definition = component_definition_to_native([line], "def::#{line_id}")
|
||||
find_and_erase_existing_instance(definition, line_id)
|
||||
instance = entities.add_instance(definition, Geom::Transformation.new)
|
||||
instance.name = line_id
|
||||
instance
|
||||
end
|
||||
|
||||
def face_to_native
|
||||
nil
|
||||
end
|
||||
@@ -80,6 +122,7 @@ module SpeckleSystems::SpeckleConnector::ToNative
|
||||
Geom::Point3d.new(length_to_native(x, units), length_to_native(y, units), length_to_native(z, units))
|
||||
end
|
||||
|
||||
# converts a mesh to a native mesh and adds the faces to the given entities collection
|
||||
def mesh_to_native(mesh, entities)
|
||||
native_mesh = Geom::PolygonMesh.new(mesh["vertices"].count / 3)
|
||||
points = []
|
||||
@@ -99,25 +142,49 @@ module SpeckleSystems::SpeckleConnector::ToNative
|
||||
native_mesh
|
||||
end
|
||||
|
||||
def component_definition_to_native(block_def)
|
||||
definition = Sketchup.active_model.definitions[block_def["name"]]
|
||||
return definition if definition && (definition.name == block_def["name"] || definition.guid == block_def["applicationId"])
|
||||
# creates a component definition and instance from a mesh
|
||||
def mesh_to_native_component(mesh, entities)
|
||||
mesh_id = mesh["applicationId"] || mesh["id"]
|
||||
definition = component_definition_to_native([mesh], "def::#{mesh_id}")
|
||||
find_and_erase_existing_instance(definition, mesh_id)
|
||||
instance = entities.add_instance(definition, Geom::Transformation.new)
|
||||
instance.name = mesh_id
|
||||
instance.material = material_to_native(mesh["renderMaterial"])
|
||||
instance
|
||||
end
|
||||
|
||||
# finds or creates a component definition from the geometry and the given name
|
||||
def component_definition_to_native(geometry, name, application_id = "")
|
||||
definition = Sketchup.active_model.definitions[name]
|
||||
return definition if definition && (definition.name == name || definition.guid == application_id)
|
||||
|
||||
definition&.entities&.clear!
|
||||
definition ||= Sketchup.active_model.definitions.add(block_def["name"])
|
||||
block_def["geometry"].each { |obj| convert_to_native(obj, definition.entities) }
|
||||
puts("definition finished: #{block_def["name"]} (#{block_def["id"]})")
|
||||
definition ||= Sketchup.active_model.definitions.add(name)
|
||||
geometry.each { |obj| convert_to_native(obj, definition.entities) }
|
||||
puts("definition finished: #{name} (#{application_id})")
|
||||
puts(" entity count: #{definition.entities.count}")
|
||||
definition
|
||||
end
|
||||
|
||||
# takes a component definition and finds and erases the first instance with the matching name (and optionally the applicationId)
|
||||
def find_and_erase_existing_instance(definition, name, app_id = "")
|
||||
definition.instances.find { |ins| ins.name == name || ins.guid == app_id }&.erase!
|
||||
end
|
||||
|
||||
# creates a component instance from a block
|
||||
def component_instance_to_native(block, entities)
|
||||
# is_group = block.key?("is_sketchup_group") && block["is_sketchup_group"]
|
||||
# something about this conversion is freaking out if nested block geo is a group
|
||||
# so this is set to false always until I can figure this out
|
||||
is_group = false
|
||||
|
||||
definition = component_definition_to_native(block["blockDefinition"])
|
||||
definition = component_definition_to_native(
|
||||
block["blockDefinition"]["geometry"],
|
||||
block["blockDefinition"]["name"],
|
||||
block["blockDefinition"]["applicationId"]
|
||||
)
|
||||
name = block["name"].nil? || block["name"].empty? ? block["id"] : block["name"]
|
||||
find_and_erase_existing_instance(definition, name, block["applicationId"])
|
||||
transform = transform_to_native(
|
||||
block["transform"].is_a?(Hash) ? block["transform"]["value"] : block["transform"],
|
||||
block["units"]
|
||||
@@ -131,6 +198,7 @@ module SpeckleSystems::SpeckleConnector::ToNative
|
||||
puts("Failed to create instance for speckle block instance #{block["id"]}") if instance.nil?
|
||||
instance.transformation = transform if is_group
|
||||
instance.material = material_to_native(block["renderMaterial"])
|
||||
instance.name = name
|
||||
instance
|
||||
end
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ module SpeckleSystems::SpeckleConnector::ToSpeckle
|
||||
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",
|
||||
@@ -18,6 +19,7 @@ module SpeckleSystems::SpeckleConnector::ToSpeckle
|
||||
}
|
||||
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)
|
||||
@@ -38,6 +40,7 @@ module SpeckleSystems::SpeckleConnector::ToSpeckle
|
||||
@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
|
||||
{
|
||||
@@ -46,7 +49,7 @@ module SpeckleSystems::SpeckleConnector::ToSpeckle
|
||||
is_sketchup_group: is_group,
|
||||
units: @units,
|
||||
bbox: bounds_to_speckle(instance.bounds),
|
||||
name: instance.name,
|
||||
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)
|
||||
@@ -56,28 +59,35 @@ module SpeckleSystems::SpeckleConnector::ToSpeckle
|
||||
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 entity.typename == "Face"
|
||||
next unless %w[Edge Face].include?(entity.typename)
|
||||
|
||||
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 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
|
||||
|
||||
# add points and texture coordinates
|
||||
mesh = face.mesh(1)
|
||||
mat_groups[mat_id]["@(31250)vertices"].push(*points_to_array(mesh))
|
||||
mat_groups[mat_id]["@(31250)textureCoordinates"].push(*uvs_to_array(mesh))
|
||||
|
||||
# add faces
|
||||
mat_groups[mat_id]["@(62500)faces"].push(*faces_to_array(mesh, mat_groups[mat_id][:pt_count]))
|
||||
mat_groups[mat_id][:pt_count] += mesh.points.count
|
||||
end
|
||||
end
|
||||
|
||||
mat_groups.values.map { |group| group.delete(:pt_count) }
|
||||
mat_groups.values + nested_blocks
|
||||
mat_groups.values + lines + nested_blocks
|
||||
end
|
||||
|
||||
def transform_to_speckle(transform)
|
||||
@@ -114,28 +124,25 @@ module SpeckleSystems::SpeckleConnector::ToSpeckle
|
||||
"@(31250)vertices" => [],
|
||||
"@(62500)faces" => [],
|
||||
"@(31250)textureCoordinates" => [],
|
||||
pt_count: -1,
|
||||
pt_count: 0,
|
||||
renderMaterial: face.material.nil? ? nil : material_to_speckle(face.material)
|
||||
}
|
||||
end
|
||||
|
||||
def faces_to_array(mesh, offset)
|
||||
# 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(
|
||||
case poly.count
|
||||
when 3 then 0 # tris
|
||||
when 4 then 1 # polys
|
||||
else
|
||||
poly.count # ngons
|
||||
end,
|
||||
*poly.map { |coord| coord.abs + offset }
|
||||
poly.count, *poly.map { |index| index.abs + offset }
|
||||
)
|
||||
end
|
||||
faces
|
||||
end
|
||||
|
||||
def points_to_array(mesh)
|
||||
# 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(
|
||||
@@ -147,6 +154,25 @@ module SpeckleSystems::SpeckleConnector::ToSpeckle
|
||||
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|
|
||||
@@ -159,15 +185,14 @@ module SpeckleSystems::SpeckleConnector::ToSpeckle
|
||||
end
|
||||
|
||||
def face_to_speckle(face)
|
||||
mesh = face.mesh(1)
|
||||
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" => points_to_array(mesh),
|
||||
"@(62500)faces" => faces_to_array(mesh, -1),
|
||||
"@(31250)textureCoordinates" => uvs_to_array(mesh)
|
||||
"@(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
|
||||
|
||||
|
||||
Generated
+7183
-4602
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -11,7 +11,7 @@
|
||||
"prettier:fix": "prettier --write ."
|
||||
},
|
||||
"dependencies": {
|
||||
"@speckle/objectloader": "^2.3.0",
|
||||
"@speckle/objectloader": "^2.6.0",
|
||||
"aws-sdk": "^2.981.0",
|
||||
"core-js": "^3.6.5",
|
||||
"debounce": "^1.2.1",
|
||||
|
||||
Reference in New Issue
Block a user