Compare commits

...

23 Commits

Author SHA1 Message Date
Oğuzhan Koral b6e4b711bf Feat (Edge): support rhino curve types 2023-06-09 11:19:57 +03:00
oguzhankoral 58fcfd210b Support arc and circle 2023-06-09 11:12:04 +03:00
oguzhankoral 5868b9c234 Support polycurves as displayValues 2023-06-09 09:55:52 +03:00
Oğuzhan Koral 0dc6d9cf9d Fix (DisplayValue): Use name of display value first 2023-06-08 23:42:05 +03:00
oguzhankoral 67f50cf2fd If displayValue has name use it 2023-06-08 23:39:54 +03:00
Oğuzhan Koral 8b26a4d49a Chore (DisplayValue): Better component names for displayValues 2023-06-08 23:15:07 +03:00
oguzhankoral a1d0bb0aa1 Instance name as speckle_type only 2023-06-08 23:08:25 +03:00
oguzhankoral bfe08560b1 Use speckle type instead def 2023-06-08 22:31:28 +03:00
Oğuzhan Koral ac3ac24272 Fix (Scene): Orthogonal view receiving as zoomed in 2023-06-08 14:39:07 +03:00
oguzhankoral 03e7191d0e Remove unnecessary lines on to_native 2023-06-08 14:36:39 +03:00
oguzhankoral b3a42f8723 Extract camera creation fuction to fix rubocop warnings 2023-06-08 14:34:18 +03:00
oguzhankoral 9c4b740300 Send 35mm focal length if camera is orthogonal 2023-06-08 14:27:05 +03:00
oguzhankoral 2a12bdadf2 Set camera height for isometric views 2023-06-08 14:14:34 +03:00
Oğuzhan Koral c90e8ad4d2 Feat (Face): extrudable brep faces 2023-06-08 12:23:21 +03:00
oguzhankoral 4a52c51c86 Rename returning single faces as ngon 2023-06-08 12:19:09 +03:00
oguzhankoral 0efc817ddc Remove remaining orphan edges after clean up 2023-06-07 13:06:16 +03:00
oguzhankoral fee54fc98c Do not smooth and soft single faces 2023-06-07 13:05:58 +03:00
oguzhankoral 210f751396 Return added entities too from conversion 2023-06-07 13:05:35 +03:00
Oğuzhan Koral 2e2bc3fe29 Feat (Layers): support flat layers 2023-06-06 13:35:05 +03:00
oguzhankoral a6f05f86d1 Document layer strategies 2023-06-06 13:33:37 +03:00
oguzhankoral fc144e4848 Convert layers as flat list from Rhino 2023-06-06 11:43:22 +03:00
oguzhankoral cef9531428 Log function to help write texts to local file 2023-06-06 11:42:54 +03:00
oguzhankoral abd4faefbf Move speckle entity creation from speckle object to SpeckleEntity class 2023-06-06 08:17:31 +03:00
21 changed files with 288 additions and 96 deletions
+1
View File
@@ -3,6 +3,7 @@
require 'sketchup'
require 'pathname'
require 'speckle_connector/debug'
require_relative 'src/log/log'
require_relative 'src/ui/sketchup_ui'
require_relative 'src/ui/ui_controller'
require_relative 'src/commands/menu_command_handler'
@@ -12,6 +12,9 @@ module SpeckleConnector
OBJECTS_GEOMETRY_LINE = 'Objects.Geometry.Line'
OBJECTS_GEOMETRY_POLYLINE = 'Objects.Geometry.Polyline'
OBJECTS_GEOMETRY_POLYCURVE = 'Objects.Geometry.Polycurve'
OBJECTS_GEOMETRY_ARC = 'Objects.Geometry.Arc'
OBJECTS_GEOMETRY_CIRCLE = 'Objects.Geometry.Circle'
OBJECTS_GEOMETRY_MESH = 'Objects.Geometry.Mesh'
OBJECTS_GEOMETRY_BREP = 'Objects.Geometry.Brep'
@@ -17,6 +17,8 @@ module SpeckleConnector
faces.each { |face| face.edges.each { |edge| edges << edge } }
edges.uniq!
edges.each { |edge| remove_edge_have_coplanar_faces(edge, faces, false) }
# Remove remaining orphan edges
edges.reject(&:deleted?).select { |edge| edge.faces.empty? }.each(&:erase!)
merged_faces(faces)
end
+24 -33
View File
@@ -2,6 +2,7 @@
require_relative 'converter'
require_relative '../constants/type_constants'
require_relative '../speckle_entities/speckle_entity'
require_relative '../speckle_objects/gis/polygon_element'
require_relative '../speckle_objects/other/transform'
require_relative '../speckle_objects/other/render_material'
@@ -11,6 +12,9 @@ require_relative '../speckle_objects/other/display_value'
require_relative '../speckle_objects/revit/revit_instance'
require_relative '../speckle_objects/geometry/point'
require_relative '../speckle_objects/geometry/line'
require_relative '../speckle_objects/geometry/polycurve'
require_relative '../speckle_objects/geometry/arc'
require_relative '../speckle_objects/geometry/circle'
require_relative '../speckle_objects/geometry/mesh'
require_relative '../speckle_objects/built_elements/view3d'
require_relative '../speckle_objects/built_elements/network'
@@ -45,6 +49,9 @@ module SpeckleConnector
# Class aliases
POINT = GEOMETRY::Point
LINE = GEOMETRY::Line
POLYCURVE = GEOMETRY::Polycurve
ARC = GEOMETRY::Arc
CIRCLE = GEOMETRY::Circle
MESH = GEOMETRY::Mesh
BLOCK_DEFINITION = OTHER::BlockDefinition
BLOCK_INSTANCE = OTHER::BlockInstance
@@ -59,6 +66,9 @@ module SpeckleConnector
CONVERTABLE_SPECKLE_TYPES = %w[
Objects.Geometry.Line
Objects.Geometry.Polyline
Objects.Geometry.Polycurve
Objects.Geometry.Arc
Objects.Geometry.Circle
Objects.Geometry.Mesh
Objects.Geometry.Brep
Objects.Other.BlockInstance
@@ -76,6 +86,10 @@ module SpeckleConnector
@from_revit ||= source_app.include?('revit')
end
def from_rhino
@from_rhino ||= source_app.include?('rhino')
end
def from_sketchup
@from_sketchup ||= source_app.include?('sketchup')
end
@@ -89,14 +103,9 @@ module SpeckleConnector
# UI is responsible currently to fetch objects from ObjectLoader module by calling getAndConstruct method.
# @param obj [Object] speckle commit object.
def receive_commit_object(obj)
# First create layers on the sketchup before starting traversing
# @Named Views are exception here. It does not mean a layer. But it is anti-pattern for now.
# layers_relation = obj['layers_relation']
unless from_revit
layers_relation = SpeckleObjects::Relations::Layers.extract_relations(obj)
# Create layers and it's folders from layers relation on the model collection.
SpeckleObjects::Relations::Layers.to_native(layers_relation, sketchup_model) if layers_relation
SpeckleObjects::Relations::Layers.to_native(obj, source_app, sketchup_model)
end
# By default entities to fill is sketchup model's entities.
@@ -242,7 +251,7 @@ module SpeckleConnector
# rubocop:disable Metrics/PerceivedComplexity
def traverse_commit_object(obj, layer, entities)
if convertible_to_native?(obj)
@state = convert_to_native(@state, obj, layer, entities)
@state, _converted_entities = convert_to_native(@state, obj, layer, entities)
elsif obj.is_a?(Hash) && obj.key?('speckle_type')
return if ignored_speckle_type?(obj)
@@ -254,7 +263,7 @@ module SpeckleConnector
end
else
# puts(">>> Found #{obj['speckle_type']}: #{obj['id']} with displayValue.")
@state = convert_to_native(@state, obj, layer, entities)
@state, _converted_entities = convert_to_native(@state, obj, layer, entities)
end
elsif obj.is_a?(Hash)
obj.each_value { |value| traverse_commit_object(value, layer, entities) }
@@ -274,6 +283,9 @@ module SpeckleConnector
SPECKLE_OBJECT_TO_NATIVE = {
OBJECTS_GEOMETRY_LINE => LINE.method(:to_native),
OBJECTS_GEOMETRY_POLYLINE => LINE.method(:to_native),
OBJECTS_GEOMETRY_POLYCURVE => POLYCURVE.method(:to_native),
OBJECTS_GEOMETRY_ARC => ARC.method(:to_native),
OBJECTS_GEOMETRY_CIRCLE => CIRCLE.method(:to_native),
OBJECTS_GEOMETRY_MESH => MESH.method(:to_native),
OBJECTS_GEOMETRY_BREP => MESH.method(:to_native),
OBJECTS_OTHER_BLOCKDEFINITION => BLOCK_DEFINITION.method(:to_native),
@@ -304,11 +316,11 @@ module SpeckleConnector
create_layers_from_categories(state, obj, converted_entities)
end
# Create speckle entities from sketchup entities to achieve continuous traversal.
convert_to_speckle_entities(state, obj, converted_entities)
SpeckleEntities::SpeckleEntity.from_speckle_object(state, obj, converted_entities, stream_id)
rescue StandardError => e
puts("Failed to convert #{obj['speckle_type']} (id: #{obj['id']})")
puts(e)
return state
return state, []
end
# rubocop:disable Metrics/CyclomaticComplexity
@@ -353,30 +365,9 @@ module SpeckleConnector
end
# @param state [States::State] state of the application
# rubocop:disable Metrics/PerceivedComplexity
# rubocop:disable Metrics/CyclomaticComplexity
def convert_to_speckle_entities(state, speckle_object, entities)
return state if entities.empty?
speckle_id = speckle_object['id']
application_id = speckle_object['applicationId']
speckle_type = speckle_object['speckle_type']
children = speckle_object['__closure'].nil? ? [] : speckle_object['__closure']
speckle_state = state.speckle_state
entities.each do |entity|
next if entity.is_a?(Sketchup::Material) || entity.is_a?(Sketchup::Page)
next if (entity.is_a?(Sketchup::Face) || entity.is_a?(Sketchup::Edge)) &&
!state.user_state.user_preferences[:register_speckle_entity]
ent = SpeckleEntities::SpeckleEntity.new(entity, speckle_id, application_id, speckle_type, children,
[stream_id])
ent.write_initial_base_data
speckle_state = speckle_state.with_speckle_entity(ent)
end
state.with_speckle_state(speckle_state)
def convert_to_speckle_entities(state, speckle_objects_with_entities)
return state if speckle_objects_with_entities.empty?
end
# rubocop:enable Metrics/PerceivedComplexity
# rubocop:enable Metrics/CyclomaticComplexity
end
# rubocop:enable Metrics/ClassLength
end
+12
View File
@@ -0,0 +1,12 @@
# frozen_string_literal: true
module SpeckleConnector
# Helper module for logging.
module Log
def self.write_to_file(text, file_name = 'log', path = "#{ENV['HOME']}/Desktop")
file_path = path + "/#{file_name}.json"
File.delete(file_path) if File.exist?(file_path)
File.write(file_path, text)
end
end
end
@@ -119,6 +119,32 @@ module SpeckleConnector
def valid?
sketchup_entity.valid?
end
# @param state [States::State] state of the application
# rubocop:disable Metrics/PerceivedComplexity
# rubocop:disable Metrics/CyclomaticComplexity
def self.from_speckle_object(state, speckle_object, entities, stream_id)
return state, [] if entities.empty?
speckle_id = speckle_object['id']
application_id = speckle_object['applicationId']
speckle_type = speckle_object['speckle_type']
children = speckle_object['__closure'].nil? ? [] : speckle_object['__closure']
speckle_state = state.speckle_state
entities.each do |entity|
next if entity.is_a?(Sketchup::Material) || entity.is_a?(Sketchup::Page)
next if (entity.is_a?(Sketchup::Face) || entity.is_a?(Sketchup::Edge)) &&
!state.user_state.user_preferences[:register_speckle_entity]
ent = SpeckleEntity.new(entity, speckle_id, application_id, speckle_type, children, [stream_id])
ent.write_initial_base_data
speckle_state = speckle_state.with_speckle_entity(ent)
end
new_state = state.with_speckle_state(speckle_state)
return new_state, entities
end
# rubocop:enable Metrics/PerceivedComplexity
# rubocop:enable Metrics/CyclomaticComplexity
end
end
end
@@ -12,7 +12,7 @@ module SpeckleConnector
def self.to_native(state, network, layer, entities, &convert_to_native)
network['elements'].each do |element|
state = convert_to_native.call(state, element['elements'], layer, entities)
state, _converted_entities = convert_to_native.call(state, element['elements'], layer, entities)
end
return state, []
@@ -19,7 +19,7 @@ module SpeckleConnector
# @param direction [SpeckleObjects::Geometry::Vector] direction of the view from eye to target.
# @param up_direction [SpeckleObjects::Geometry::Vector] up direction of the view.
# @param is_perspective [Boolean] whether view is perspective or not.
# @param lens [Boolean] fov value of the view camera.
# @param lens [Numeric] focal length value of the view camera.
# @param units [String] units of the camera.
# @param application_id [String] application_id of the view.
# @param update_properties [Hash{Symbol=>boolean}] properties of the view.
@@ -63,42 +63,44 @@ module SpeckleConnector
rendering_options = SpeckleObjects::Other::RenderingOptions.to_speckle(page.rendering_options)
View3d.new(
page.name, origin, target, direction, SpeckleObjects::Geometry::Vector.new(0, 0, 1, units),
cam.perspective?, cam.fov, units, page.name, update_properties, rendering_options
cam.perspective?, cam.perspective? ? cam.focal_length : 35, units, page.name,
update_properties, rendering_options
)
end
# @param state [States::State] state of the speckle app.
# @param obj [Hash] commit object.
# rubocop:disable Metrics/AbcSize
# rubocop:disable Metrics/CyclomaticComplexity
# rubocop:disable Metrics/PerceivedComplexity
def self.to_native(state, view, _layer, _entities, &_convert_to_native)
sketchup_model = state.sketchup_state.sketchup_model
return state, [] unless view['speckle_type'] == 'Objects.BuiltElements.View:Objects.BuiltElements.View3D'
name = view['name'] || view['id']
return state, [] if sketchup_model.pages.any? { |page| page.name == name }
origin = view['origin']
target = view['target']
lens = view['lens'] || 50
origin = SpeckleObjects::Geometry::Point.to_native(origin['x'], origin['y'], origin['z'], origin['units'])
target = SpeckleObjects::Geometry::Point.to_native(target['x'], target['y'], target['z'], target['units'])
view_direction = (origin - target).normalize
up = view_direction.parallel?([0, 0, 1]) ? [0, 1, 0] : [0, 0, 1]
# Set camera position before creating scene on it.
my_camera = Sketchup::Camera.new(origin, target, up, !view['isOrthogonal'], lens)
sketchup_model.active_view.camera = my_camera
camera = create_camera(view)
sketchup_model.active_view.camera = camera
sketchup_model.pages.add(name)
page = sketchup_model.pages[name]
set_page_update_properties(page, view['update_properties']) if view['update_properties']
set_rendering_options(page.rendering_options, view['rendering_options']) if view['rendering_options']
return state, [page]
end
# rubocop:enable Metrics/AbcSize
# rubocop:enable Metrics/CyclomaticComplexity
# rubocop:enable Metrics/PerceivedComplexity
def self.create_camera(view)
origin = view['origin']
target = view['target']
focal_length = view['lens'] || 35
origin = SpeckleObjects::Geometry::Point.to_native(origin['x'], origin['y'], origin['z'], origin['units'])
target = SpeckleObjects::Geometry::Point.to_native(target['x'], target['y'], target['z'], target['units'])
view_direction = (origin - target).normalize
up = view_direction.parallel?([0, 0, 1]) ? [0, 1, 0] : [0, 0, 1]
# Set camera position before creating scene on it.
is_perspective = !view['isOrthogonal']
camera = Sketchup::Camera.new(origin, target, up, is_perspective)
camera.focal_length = focal_length if is_perspective
camera.height = (origin - target).length * 2 unless is_perspective
camera
end
# @param page [Sketchup::Page] scene to update -update properties-
def self.set_page_update_properties(page, update_properties)
@@ -0,0 +1,31 @@
# frozen_string_literal: true
require_relative '../base'
require_relative '../../constants/type_constants'
module SpeckleConnector
module SpeckleObjects
module Geometry
# Arc object definition for Speckle.
class Arc < Base
SPECKLE_TYPE = OBJECTS_GEOMETRY_ARC
# @param [States::State] state of the current application.
# @param arc [Object] object represents Speckle Arc.
# @param layer [Sketchup::Layer] layer to add {Sketchup::Edge} into it.
# @param entities [Sketchup::Entities] entities collection to add {Sketchup::Edge} into it.
def self.to_native(state, arc, layer, entities, &_convert_to_native)
plane = arc['plane']
units = arc['units']
origin = Point.to_native(plane['origin']['x'], plane['origin']['y'], plane['origin']['z'], units)
normal = Vector.to_native(plane['normal']['x'], plane['normal']['y'], plane['normal']['z'], units)
x_axis = Vector.to_native(plane['xdir']['x'], plane['xdir']['y'], plane['xdir']['z'], units)
radius = Geometry.length_to_native(arc['radius'], units)
edges = entities.add_arc(origin, x_axis, normal, radius, arc['startAngle'], arc['endAngle'])
edges.each { |edge| edge.layer = layer }
return state, edges
end
end
end
end
end
@@ -0,0 +1,33 @@
# frozen_string_literal: true
require_relative 'point'
require_relative 'vector'
require_relative 'length'
require_relative '../base'
require_relative '../../constants/type_constants'
module SpeckleConnector
module SpeckleObjects
module Geometry
# Circle object definition for Speckle.
class Circle < Base
SPECKLE_TYPE = OBJECTS_GEOMETRY_CIRCLE
# @param [States::State] state of the current application.
# @param circle [Object] object represents Speckle Circle.
# @param layer [Sketchup::Layer] layer to add {Sketchup::Edge} into it.
# @param entities [Sketchup::Entities] entities collection to add {Sketchup::Edge} into it.
def self.to_native(state, circle, layer, entities, &_convert_to_native)
plane = circle['plane']
units = circle['units']
origin = Point.to_native(plane['origin']['x'], plane['origin']['y'], plane['origin']['z'], units)
normal = Vector.to_native(plane['normal']['x'], plane['normal']['y'], plane['normal']['z'], units)
radius = Geometry.length_to_native(circle['radius'], units)
edges = entities.add_circle(origin, normal, radius)
edges.each { |edge| edge.layer = layer }
return state, edges
end
end
end
end
end
@@ -60,7 +60,7 @@ module SpeckleConnector
)
end
# @param _state [States::State] state of the application.
# @param state [States::State] state of the application.
# @param line [Object] object represents Speckle line.
# @param layer [Sketchup::Layer] layer to add {Sketchup::Edge} into it.
# @param entities [Sketchup::Entities] entities collection to add {Sketchup::Edge} into it.
@@ -119,6 +119,10 @@ module SpeckleConnector
added_faces = entities.grep(Sketchup::Face).last(native_mesh.polygons.length)
mesh_layer_name = SketchupModel::Query::Layer.entity_layer_from_path(mesh['layer'])
mesh_layer = state.sketchup_state.sketchup_model.layers.to_a.find { |l| l.display_name == mesh_layer_name }
# Merge only added faces in this scope
if model_preferences[:merge_coplanar_faces]
added_faces = Converters::CleanUp.merge_coplanar_faces(added_faces)
end
added_faces.each do |face|
face.layer = mesh_layer unless mesh_layer.nil?
# Smooth edges if they already soft
@@ -129,10 +133,7 @@ module SpeckleConnector
.attribute_dictionaries_to_native(face, mesh['sketchup_attributes']['dictionaries'])
end
end
# Merge only added faces in this scope
if model_preferences[:merge_coplanar_faces]
added_faces = Converters::CleanUp.merge_coplanar_faces(added_faces)
end
return state, added_faces
end
# rubocop:enable Metrics/MethodLength
@@ -0,0 +1,21 @@
# frozen_string_literal: true
require_relative '../base'
require_relative '../../constants/type_constants'
module SpeckleConnector
module SpeckleObjects
module Geometry
# Polycurve object definition for Speckle.
# It basically groups the lines-curves under it's `segments` property.
class Polycurve < Base
SPECKLE_TYPE = OBJECTS_GEOMETRY_POLYCURVE
def self.to_native(state, polycurve, layer, entities, &convert_to_native)
polycurve['displayValue'] = polycurve['segments']
convert_to_native.call(state, polycurve, layer, entities)
end
end
end
end
end
@@ -1,5 +1,6 @@
# frozen_string_literal: true
require_relative 'length'
require_relative '../base'
module SpeckleConnector
@@ -25,6 +26,14 @@ module SpeckleConnector
self[:z] = z
self[:units] = units
end
def self.to_native(x, y, z, units)
Geom::Vector3d.new(
Geometry.length_to_native(x, units),
Geometry.length_to_native(y, units),
Geometry.length_to_native(z, units)
)
end
end
end
end
@@ -122,13 +122,23 @@ module SpeckleConnector
definition&.entities&.clear!
definition ||= sketchup_model.definitions.add(definition_name)
ngon_faces = []
if geometry.is_a?(Array)
geometry.each do |obj|
state = convert_to_native.call(state, obj, layer, definition.entities)
state, added_entities = convert_to_native.call(state, obj, layer, definition.entities)
if added_entities.length == 1 && added_entities.first.is_a?(Sketchup::Face)
ngon_faces.append(added_entities.first)
end
end
end
ngon_faces.each do |f|
f.edges.each do |e|
e.soft = false
e.smooth = false
end
end
if geometry.is_a?(Hash) && !definition_obj['speckle_type'].nil?
state = convert_to_native.call(state, geometry, layer, definition.entities)
state, _converted_entities = convert_to_native.call(state, geometry, layer, definition.entities)
end
# puts("definition finished: #{name} (#{application_id})")
# puts(" entity count: #{definition.entities.count}")
@@ -9,7 +9,6 @@ module SpeckleConnector
module Other
# DisplayStyle object for layer.
class DisplayStyle < Base
def initialize(name:, color:, line_type:)
super(
speckle_type: OBJECTS_OTHER_DISPLAYSTYLE,
@@ -22,9 +22,15 @@ module SpeckleConnector
return format_naming_convention([family, type, category, element_id]) unless element_id.nil?
return "def::#{def_obj['applicationId']}" unless def_obj['applicationId'].nil?
name = def_obj['name']
return "#{name}::#{def_obj['applicationId']}" if !name.nil? && !def_obj['applicationId'].nil?
return "def::#{def_obj['id']}"
return "#{name}::#{def_obj['id']}" unless name.nil?
speckle_type = def_obj['speckle_type'].split('.').last
return "#{speckle_type}::#{def_obj['applicationId']}" unless def_obj['applicationId'].nil?
return "#{speckle_type}::#{def_obj['id']}"
end
def self.format_naming_convention(entries)
@@ -41,6 +47,13 @@ module SpeckleConnector
name
end
# Get instance name as speckle_type if it is structured as `speckle_type::application_id`
def self.get_instance_name(definition_name)
return definition_name unless definition_name.include?('::')
definition_name.split('::').first
end
# Creates a component definition and instance from a speckle object with a display value
# @param state [States::State] state of the application.
def self.to_native(state, obj, layer, entities, &convert_to_native)
@@ -49,11 +62,7 @@ module SpeckleConnector
obj['name'] = get_definition_name(obj)
state, _definitions = BlockDefinition.to_native(
state,
obj,
layer,
entities,
&convert_to_native
state, obj, layer, entities, &convert_to_native
)
definition = state.sketchup_state.sketchup_model.definitions[BlockDefinition.get_definition_name(obj)]
@@ -62,7 +71,7 @@ module SpeckleConnector
t_arr = obj['transform']
transform = t_arr.nil? ? Geom::Transformation.new : Other::Transform.to_native(t_arr, obj['units'])
instance = entities.add_instance(definition, transform)
instance.name = obj['name'] unless obj['name'].nil?
instance.name = get_instance_name(obj['name']) unless obj['name'].nil?
instance.layer = layer unless layer.nil?
# Align instance axes that created from display value. (without any transform)
# BlockInstance.align_instance_axes(instance)
@@ -11,7 +11,7 @@ module SpeckleConnector
SPECKLE_TYPE = 'Speckle.Core.Models.Collection'
# rubocop:disable Metrics/ParameterLists
def initialize(name:, visible:, is_folder:, line_style: nil, color: nil, layers_and_folders: [],
def initialize(name:, visible:, is_folder:, full_path: nil, line_style: nil, color: nil, layers_and_folders: [],
application_id: nil)
super(
speckle_type: SPECKLE_TYPE,
@@ -23,6 +23,7 @@ module SpeckleConnector
self[:color] = color
self[:visible] = visible
self[:is_folder] = is_folder
self[:full_path] = full_path unless full_path.nil?
self[:line_style] = line_style unless line_style.nil?
self[:collectionType] = 'layer'
self[:elements] = layers_and_folders if layers_and_folders.any?
@@ -43,14 +44,31 @@ module SpeckleConnector
folder.add_layer(layer) if folder.is_a?(Sketchup::LayerFolder)
end
def self.to_native_layer_folder(speckle_layer_folder, folder, sketchup_model)
speckle_layers = speckle_layer_folder[:elements].select { |layer_or_fol| layer_or_fol[:elements].nil? }
# Flat layer conversion.
def self.to_native_flat_layers(layers_relation, sketchup_model)
speckle_layers = layers_relation[:elements]
elements_to_layers(speckle_layers, sketchup_model)
end
# Converts elements to layers with it's full path.
def self.elements_to_layers(elements, sketchup_model)
elements.each do |element|
element[:name] = element[:full_path]
to_native_layer(element, sketchup_model.layers, sketchup_model)
elements_to_layers(element[:elements], sketchup_model) unless element[:elements].nil?
end
end
# Nested layer conversion with folders.
def self.to_native_layer_folder(layers_relation, folder, sketchup_model)
speckle_layers = layers_relation[:elements].select { |layer_or_fol| layer_or_fol[:elements].nil? }
speckle_layers.each do |speckle_layer|
to_native_layer(speckle_layer, folder, sketchup_model)
end
speckle_folders = speckle_layer_folder[:elements].reject { |layer_or_fol| layer_or_fol[:elements].nil? }
speckle_folders = layers_relation[:elements].reject { |layer_or_fol| layer_or_fol[:elements].nil? }
speckle_folders.each do |speckle_folder|
sub_folder = folder.add_folder(speckle_folder[:name])
@@ -22,26 +22,18 @@ module SpeckleConnector
self[:elements] = layers
end
def self.element_to_relation(elements)
elements.collect do |element|
next unless element['speckle_type'] == SPECKLE_CORE_MODELS_COLLECTION
is_folder = element['elements'].any? { |e| e['speckle_type'] == SPECKLE_CORE_MODELS_COLLECTION }
color = element['color'] || element['displayStyle']['color'] unless element['displayStyle'].nil?
Layer.new(
name: element['name'],
visible: element['visible'],
is_folder: is_folder,
color: color,
layers_and_folders: element_to_relation(element['elements'])
)
end.compact
end
def self.extract_relations(commit_obj)
# Extract relations from commit obj to create layers in advance.
# By doing this, also checks layers will be created as flat list or nested structure according to source app.
# @param commit_obj [Hash] commit object to extract layer relations.
# @param source_app [String] source application to decide layer creation strategy.
# Currently for
# - Revit: we don't create layers in advance because we create layers according to categories.
# - SketchUp: we create layers in advance as nested.
# - Rhino: we create layers in advance as flat list with it's full path.
def self.extract_relations(commit_obj, source_app)
return nil unless commit_obj['speckle_type'] == SPECKLE_CORE_MODELS_COLLECTION
elements = element_to_relation(commit_obj['elements'])
elements = element_to_relation(commit_obj['elements'], source_app, [])
Layers.new(
active: commit_obj['active_layer'],
@@ -49,10 +41,41 @@ module SpeckleConnector
)
end
def self.to_native(layers_relation, sketchup_model)
folder = sketchup_model.layers
# rubocop:disable Metrics/CyclomaticComplexity
def self.element_to_relation(elements, source_app, parent_layers)
elements.collect do |element|
next unless element['speckle_type'] == SPECKLE_CORE_MODELS_COLLECTION
SpeckleObjects::Relations::Layer.to_native_layer_folder(layers_relation, folder, sketchup_model)
layers_tree = parent_layers.dup.append(element['name'])
full_path = ''
parent_layers.each { |parent| full_path += "#{parent}::" }
full_path += element['name']
# Add this info to commit object to check later layer_collection conversion
element['full_path'] = full_path if source_app.include?('rhino')
is_folder = element['elements'].any? { |e| e['speckle_type'] == SPECKLE_CORE_MODELS_COLLECTION }
color = element['color'] || element['displayStyle']['color'] unless element['displayStyle'].nil?
Layer.new(
name: element['name'], visible: element['visible'], is_folder: is_folder,
color: color, full_path: full_path,
layers_and_folders: element_to_relation(element['elements'], source_app, layers_tree)
)
end.compact
end
# rubocop:enable Metrics/CyclomaticComplexity
def self.to_native(obj, source_app, sketchup_model)
layers_relation = extract_relations(obj, source_app)
return if layers_relation.nil?
folder = sketchup_model.layers
is_flat = source_app.include?('rhino')
if is_flat
SpeckleObjects::Relations::Layer.to_native_flat_layers(layers_relation, sketchup_model)
else
SpeckleObjects::Relations::Layer.to_native_layer_folder(layers_relation, folder, sketchup_model)
end
active_layer = folder.to_a.find { |layer| layer.display_name == layers_relation['active_layer'] }
sketchup_model.active_layer = active_layer unless active_layer.nil?
@@ -99,12 +99,13 @@ module SpeckleConnector
sketchup_model = state.sketchup_state.sketchup_model
elements = layer_collection['elements']
name = layer_collection['name']
name = layer_collection['full_path'] if layer_collection['full_path']
layer = sketchup_model.layers.find { |l| l.display_name == name }
layer_or_folder = layer if layer
elements.each do |element|
new_state = convert_to_native.call(state, element, layer_or_folder, entities)
new_state, _converted_entities = convert_to_native.call(state, element, layer_or_folder, entities)
state = new_state
end
@@ -66,7 +66,7 @@ module SpeckleConnector
elements = model_collection['elements']
elements.each do |element|
new_state = convert_to_native.call(state, element, layer, entities)
new_state, _converted_entities = convert_to_native.call(state, element, layer, entities)
state = new_state
end