Compare commits

...

74 Commits

Author SHA1 Message Date
Oğuzhan Koral e7f641046b Fix (Send): Do not send hidden geometries in components 2023-06-09 15:26:16 +03:00
oguzhankoral 9aaabe0fab Do not send hidden geometries in components 2023-06-09 15:24:40 +03:00
Oğuzhan Koral 966f7aaed5 Fix (Collection): fix blender receive 2023-05-29 18:12:02 +03:00
oguzhankoral 51b59fa995 Fix typo on including string 2023-05-29 18:09:48 +03:00
oguzhankoral 0b713736bd Include also detached @elements props to displayValue 2023-05-29 18:06:05 +03:00
oguzhankoral 9e33581c66 Accept also detached @displayValue props 2023-05-29 18:05:29 +03:00
oguzhankoral b97792b596 Check only collection is model or not 2023-05-29 18:04:56 +03:00
Matteo Cominetti c0746f8eff Merge pull request #270 from specklesystems/oguzhan/hash-only-hostname-of-server-url 2023-05-25 18:17:37 +01:00
oguzhankoral a826a9d692 Hash hostname of the serverUrl 2023-05-25 20:09:32 +03:00
Oğuzhan Koral 6d04203d37 Feat (Collections): Eliminate relations for layer info 2023-05-24 15:57:47 +03:00
oguzhankoral 33b2ed8a94 Fix performance penalty 2023-05-24 14:38:27 +03:00
oguzhankoral 4f16da7ad0 Do not extract relations if from revit 2023-05-24 01:16:51 +03:00
oguzhankoral 36f92c7655 Add network object support 2023-05-24 01:00:06 +03:00
oguzhankoral 1d4f5a759e Eliminate relations for model collections 2023-05-23 23:31:02 +03:00
oguzhankoral 28af9bc811 Enable rescue block for to_native 2023-05-23 12:07:48 +03:00
oguzhankoral cf04cd4094 Check view up and direction parallel or not 2023-05-23 11:50:08 +03:00
oguzhankoral 23e9efb28a Pass layer prop through to_native methods 2023-05-23 11:49:51 +03:00
Oğuzhan Koral 57322df29c Fix (Layer): Do not send line_style if none 2023-05-22 13:57:23 +03:00
oguzhankoral fff82d34c6 Do not send line_style if none 2023-05-22 13:56:10 +03:00
Oğuzhan Koral 7211860c21 Chore (Layer): Support line styles for layers 2023-05-22 11:46:52 +03:00
oguzhankoral 9fc69044f8 Support line styles for layers 2023-05-22 11:42:51 +03:00
Oğuzhan Koral 76467c3e81 Fix (Blocks): Eliminate nil block geometries 2023-05-19 19:36:28 +03:00
oguzhankoral 4cfc04e2f3 Eliminate nil block geometries 2023-05-19 19:13:15 +03:00
Oğuzhan Koral 6bfc7771d0 Fix (Face): Merge coplanar doesn't work some cases 2023-05-19 12:26:24 +03:00
oguzhankoral 7055135257 Return nil if layer path nil 2023-05-19 12:20:55 +03:00
Oğuzhan Koral 85312cf20d Fix (Mapper): inconsistency on mapped elements table state 2023-05-16 13:36:27 +03:00
oguzhankoral 4051cae5b0 Clear cached element and category selection when mapped elements updated 2023-05-16 13:27:02 +03:00
oguzhankoral c64bb6dedf Include instances for button reactions
- Clear, Isolate, Hide, Select
2023-05-16 13:25:41 +03:00
Oğuzhan Koral 7bb17ae0db Fix (Collections): Add layer property to objects as full path 2023-05-16 08:39:06 +03:00
oguzhankoral 3244eb9b15 Add layer property to objects as full path
This is for receiving on Rhino
2023-05-16 08:33:42 +03:00
Oğuzhan Koral 6e173847ba Fix (Performance): Smooth only non-planar quad meshes 2023-05-15 13:53:56 +03:00
oguzhankoral 8cbc1f763d Smooth only non-planar quad meshes 2023-05-15 13:40:31 +03:00
Oğuzhan Koral 2d45abeb28 Release 2.14.0
Merging 2.13 hotfixes into dev branch
2023-05-15 13:17:54 +03:00
oguzhankoral 2fd0ad77bc Fix or disable rubocop issues 2023-05-15 13:14:32 +03:00
oguzhankoral 53c1167831 Remove orthogonality checks 2023-05-15 13:04:22 +03:00
oguzhankoral 48d4a1d7bb Add plane_utils to calculate planarity of 4 points for quad meshes 2023-05-15 13:04:19 +03:00
oguzhankoral 4bbbba6e89 Split all quad meshes 2023-05-15 13:03:22 +03:00
oguzhankoral 0a13575325 Disable rubocop for to_native 2023-05-15 13:02:12 +03:00
Oğuzhan Koral d6075209df Fix (Update): Check if branch instance is deleted 2023-05-12 12:53:19 +03:00
oguzhankoral 9d1b39422b Extract entities_updated method 2023-05-12 12:39:37 +03:00
oguzhankoral 38e297d32b Correct condition for is_update_commit 2023-05-12 11:31:23 +03:00
oguzhankoral 459ef3c9f1 Check if branch instance is deleted 2023-05-12 11:21:34 +03:00
Oğuzhan Koral 3bb12487ba Feat (QGIS): Attribute support for qgis commits 2023-05-12 10:13:44 +03:00
oguzhankoral 8b2428c60d Workaround for QGIS geometry objects or arrays 2023-05-10 17:26:47 +03:00
oguzhankoral f9befd9051 Write qgis attributes to components 2023-05-10 12:43:29 +03:00
oguzhankoral 1020a9bd5d Wrap PolygonElement object into component 2023-05-10 12:07:40 +03:00
Oğuzhan Koral 2844c4ea86 Fix (Instance): Disable aligning axes of displayValue components 2023-05-08 16:54:52 +03:00
oguzhankoral 3344188fee Disable aligning axes of displayValue components 2023-05-08 16:44:10 +03:00
Oğuzhan Koral 67138c2f78 Fix (Update): Update behavior for Revit > Sketchup 2023-05-08 13:21:31 +03:00
oguzhankoral f59d19ef21 Check children changed or not for upcoming blocks 2023-05-08 13:13:53 +03:00
Oğuzhan Koral d0fa7e638f Feat (Collections): Model collections 2023-05-08 09:46:44 +03:00
oguzhankoral 63011e466e Change relation speckle types to collections 2023-05-08 09:34:47 +03:00
oguzhankoral 92d1976300 Split collections into helper classes 2023-05-04 20:08:17 +03:00
oguzhankoral a0cdc9fa07 Check sketchup entities for respomding true to layer set, if so set 2023-05-04 12:22:02 +03:00
oguzhankoral 60f0006597 Remove old layer helper methods 2023-05-04 12:06:56 +03:00
oguzhankoral 67a3c62a08 Align instance axes that created from displayValues without transform 2023-05-03 18:23:58 +03:00
oguzhankoral 21d5dc1e0b Remove layer argument passing for all to_native methods 2023-04-27 17:59:39 +01:00
oguzhankoral 0d810f59f9 Place received objects into correct layers 2023-04-27 17:39:15 +01:00
oguzhankoral dba60b700f Layer relations to native layer tree 2023-04-27 17:21:20 +01:00
oguzhankoral ad9a56bd20 Remove unused methods 2023-04-27 16:16:15 +01:00
oguzhankoral 82585e9104 Convert views to native with traversal method 2023-04-27 16:14:20 +01:00
oguzhankoral dfbb241b0b Model collections on send 2023-04-27 14:56:59 +01:00
oguzhankoral f69ea91f45 Add query for layers 2023-04-26 16:44:11 +01:00
oguzhankoral 768d916092 Convert scenes to view3d on class 2023-04-26 16:44:00 +01:00
oguzhankoral c0acecae10 Query methods for direct shapes 2023-04-26 16:43:31 +01:00
oguzhankoral f741d1c7e4 Clean converter from object specific functions 2023-04-26 16:43:03 +01:00
oguzhankoral d25c70f1d1 Do not load bootstrap again when reload triggered 2023-04-26 16:42:06 +01:00
oguzhankoral 6f954467ef Move collections into related namespace 2023-04-26 16:41:37 +01:00
oguzhankoral 867987a5f5 Group meshes also according to layers 2023-04-26 14:39:52 +01:00
oguzhankoral 61e0a6351c Init collection class 2023-04-14 10:09:44 +03:00
oguzhankoral 558264c23b Add documentation to app methods 2023-04-13 13:57:37 +03:00
oguzhankoral cf7aa371af Move view related methods to related class 2023-04-12 20:04:17 +03:00
oguzhankoral ffedaf6a73 Fix bug on views receive 2023-04-12 14:02:50 +03:00
oguzhankoral d9ae272b36 Proof of concept of layer relations
# Conflicts:
#	speckle_connector/src/convertors/to_speckle.rb
#	speckle_connector/src/speckle_objects/geometry/mesh.rb
#	speckle_connector/src/speckle_objects/other/block_definition.rb
#	speckle_connector/src/speckle_objects/other/block_instance.rb
2023-04-12 12:56:59 +03:00
36 changed files with 1200 additions and 378 deletions
+3 -1
View File
@@ -15,7 +15,9 @@ module SpeckleConnector
def self.reload
load(__FILE__)
pattern = File.join(__dir__, '**/*.rb')
Dir.glob(pattern).each { |file| load(file) }
# TODO: Here is a opportunity to improve reloading process.
# We can cache last edited time of the each file later to check which file need to be reloaded.
Dir.glob(pattern).each { |file| load(file) unless file.include?('bootstrap') }
.size
end
# rubocop:enable SketchupSuggestions/FileEncoding
@@ -20,6 +20,10 @@ module SpeckleConnector
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
# rubocop:disable Metrics/AbcSize
# rubocop:disable Metrics/CyclomaticComplexity
# rubocop:disable Metrics/PerceivedComplexity
# rubocop:disable Metrics/MethodLength
def update_state(state)
sketchup_model = state.sketchup_state.sketchup_model
entities = if sketchup_model.active_path.nil?
@@ -53,6 +57,10 @@ module SpeckleConnector
new_state = MappedEntitiesUpdated.update_state(state.with_speckle_state(speckle_state))
Events::SelectionEventAction.update_state(new_state, { apply: true })
end
# rubocop:enable Metrics/AbcSize
# rubocop:enable Metrics/CyclomaticComplexity
# rubocop:enable Metrics/PerceivedComplexity
# rubocop:enable Metrics/MethodLength
end
end
end
@@ -17,6 +17,8 @@ module SpeckleConnector
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
# rubocop:disable Metrics/CyclomaticComplexity
# rubocop:disable Metrics/PerceivedComplexity
def update_state(state)
sketchup_model = state.sketchup_state.sketchup_model
entities = if sketchup_model.active_path.nil?
@@ -43,6 +45,8 @@ module SpeckleConnector
new_state = MappedEntitiesUpdated.update_state(state.with_speckle_state(speckle_state))
Events::SelectionEventAction.update_state(new_state, { clear: true })
end
# rubocop:enable Metrics/CyclomaticComplexity
# rubocop:enable Metrics/PerceivedComplexity
end
end
end
@@ -21,6 +21,11 @@ module SpeckleConnector
flat_entities.each do |entity|
next unless entity_ids.include?(entity.persistent_id)
if entity.is_a?(Sketchup::ComponentDefinition)
entity.instances.each do |instance|
instance.hidden = true
end
end
entity.hidden = true
end
@@ -10,6 +10,10 @@ module SpeckleConnector
class IsolateMappingsFromTable < Action
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
# rubocop:disable Metrics/AbcSize
# rubocop:disable Metrics/MethodLength
# rubocop:disable Metrics/PerceivedComplexity
# rubocop:disable Metrics/CyclomaticComplexity
def self.update_state(state, data)
sketchup_model = state.sketchup_state.sketchup_model
@@ -21,14 +25,15 @@ module SpeckleConnector
# Flat entities to isolate mappings
flat_entities = SketchupModel::Query::Entity.flat_entities(sketchup_model.entities)
comp_flat_entities = flat_entities.grep(Sketchup::ComponentInstance) + flat_entities.grep(Sketchup::Group)
comp_flat_entities = flat_entities.grep(Sketchup::ComponentInstance) + flat_entities.grep(Sketchup::Group) +
flat_entities.grep(Sketchup::ComponentDefinition)
face_edge_flat_entities = flat_entities.grep(Sketchup::Face) + flat_entities.grep(Sketchup::Edge)
# Collect entity ids to clear mappings
selected_elements = data.collect { |_, entities| entities['selectedElements'] }.flatten
comps_or_groups, faces_or_edges = selected_elements.partition do |e|
e['entityType'] == 'Component' || e['entityType'] == 'Group'
e['entityType'] == 'Component' || e['entityType'] == 'Definition' || e['entityType'] == 'Group'
end
faces_or_edges_ids = faces_or_edges.collect { |e| e['entityId'] }
@@ -40,11 +45,20 @@ module SpeckleConnector
comps_or_groups_ids = comps_or_groups.collect { |e| e['entityId'] }
comp_flat_entities.select { |e| comps_or_groups_ids.include?(e.persistent_id) }.each do |entity|
if entity.is_a?(Sketchup::ComponentDefinition)
entity.instances.each do |instance|
instance.hidden = false
end
end
entity.hidden = false
end
Events::SelectionEventAction.update_state(state, { clear: true })
end
# rubocop:enable Metrics/AbcSize
# rubocop:enable Metrics/MethodLength
# rubocop:enable Metrics/PerceivedComplexity
# rubocop:enable Metrics/CyclomaticComplexity
end
end
end
@@ -24,6 +24,9 @@ module SpeckleConnector
flat_entities.each do |entity|
next unless entity_ids.include?(entity.persistent_id)
if entity.is_a?(Sketchup::ComponentDefinition)
state.sketchup_state.sketchup_model.selection.add(entity.instances)
end
state.sketchup_state.sketchup_model.selection.add(entity)
end
@@ -32,16 +32,20 @@ module SpeckleConnector
ui_controller.update_ui(state)
end
# Attach observers to application when speckle initialized via menu commands.
def add_observer_handler!(observer_handler)
@observer_handler = observer_handler
end
# Send messages to HtmlDialog if any.
def send_messages!
queue = @state.speckle_state.message_queue
queue.each_value { |value| ui_controller.user_interfaces[Ui::SPECKLE_UI_ID].dialog.execute_script(value) }
update_state!(Actions::ClearQueue)
end
def add_observer_handler!(observer_handler)
@observer_handler = observer_handler
end
# This is the only function application state will be switched by calling upcoming action with it's parameters
# if any.
def update_state!(action, *parameters)
old_state = @state
@state = action.update_state(old_state, *parameters)
@@ -3,8 +3,12 @@
module SpeckleConnector
BASE_OBJECT = 'Base'
OBJECTS_GIS_POLYGONELEMENT = 'Objects.GIS.PolygonElement'
OBJECTS_BUILTELEMENTS_VIEW3D = 'Objects.BuiltElements.View:Objects.BuiltElements.View3D'
OBJECTS_BUILTELEMENTS_NETWORK = 'Objects.BuiltElements.Network'
OBJECTS_BUILTELEMENTS_REVIT_DIRECTSHAPE = 'Objects.BuiltElements.Revit.DirectShape'
OBJECTS_BUILTELEMENTS_REVIT_LEVEL = 'Objects.BuiltElements.Level:Objects.BuiltElements.Revit.RevitLevel'
OBJECTS_GEOMETRY_LINE = 'Objects.Geometry.Line'
OBJECTS_GEOMETRY_POLYLINE = 'Objects.Geometry.Polyline'
@@ -17,4 +21,7 @@ module SpeckleConnector
OBJECTS_OTHER_REVIT_REVITINSTANCE = 'Objects.Other.Revit.RevitInstance'
OBJECTS_OTHER_BLOCKDEFINITION = 'Objects.Other.BlockDefinition'
OBJECTS_OTHER_RENDERMATERIAL = 'Objects.Other.RenderMaterial'
OBJECTS_OTHER_DISPLAYSTYLE = 'Objects.Other.DisplayStyle'
SPECKLE_CORE_MODELS_COLLECTION = 'Speckle.Core.Models.Collection'
end
+103 -126
View File
@@ -2,6 +2,7 @@
require_relative 'converter'
require_relative '../constants/type_constants'
require_relative '../speckle_objects/gis/polygon_element'
require_relative '../speckle_objects/other/transform'
require_relative '../speckle_objects/other/render_material'
require_relative '../speckle_objects/other/block_definition'
@@ -12,6 +13,9 @@ require_relative '../speckle_objects/geometry/point'
require_relative '../speckle_objects/geometry/line'
require_relative '../speckle_objects/geometry/mesh'
require_relative '../speckle_objects/built_elements/view3d'
require_relative '../speckle_objects/built_elements/network'
require_relative '../speckle_objects/speckle/core/models/collection'
require_relative '../sketchup_model/dictionary/speckle_entity_dictionary_handler'
module SpeckleConnector
module Converters
@@ -35,6 +39,8 @@ module SpeckleConnector
GEOMETRY = SpeckleObjects::Geometry
OTHER = SpeckleObjects::Other
REVIT = SpeckleObjects::Revit
BUILTELEMENTS = SpeckleObjects::BuiltElements
GIS = SpeckleObjects::GIS
# Class aliases
POINT = GEOMETRY::Point
@@ -45,6 +51,9 @@ module SpeckleConnector
REVIT_INSTANCE = REVIT::Other::RevitInstance
RENDER_MATERIAL = OTHER::RenderMaterial
DISPLAY_VALUE = OTHER::DisplayValue
VIEW3D = BUILTELEMENTS::View3d
POLYGON_ELEMENT = GIS::PolygonElement
COLLECTION = SpeckleObjects::Speckle::Core::Models::Collection
BASE_OBJECT_PROPS = %w[applicationId id speckle_type totalChildrenCount].freeze
CONVERTABLE_SPECKLE_TYPES = %w[
@@ -57,6 +66,10 @@ module SpeckleConnector
Objects.Other.BlockDefinition
Objects.Other.RenderMaterial
Objects.Other.Instance:Objects.Other.BlockInstance
Objects.BuiltElements.View:Objects.BuiltElements.View3D
Objects.BuiltElements.Network
Objects.GIS.PolygonElement
Speckle.Core.Models.Collection
].freeze
def from_revit
@@ -67,6 +80,10 @@ module SpeckleConnector
@from_sketchup ||= source_app.include?('sketchup')
end
def from_qgis
@from_qgis ||= source_app.include?('qgis')
end
# ReceiveObjects action call this method by giving everything that comes from server.
# Upcoming object is a referencedObject of selected commit to receive.
# UI is responsible currently to fetch objects from ObjectLoader module by calling getAndConstruct method.
@@ -74,27 +91,64 @@ module SpeckleConnector
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.
filtered_layer_containers = obj.keys.filter_map { |key| key if key.start_with?('@') && key != '@Named Views' }
create_layers(filtered_layer_containers, sketchup_model.layers) unless from_revit
# Convert views to sketchup scenes
SpeckleObjects::BuiltElements::View3d.to_native(obj, sketchup_model)
# Get default commit layer from sketchup model which will be used as fallback
# 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
end
# By default entities to fill is sketchup model's entities.
@entities_to_fill = sketchup_model.entities
# Navigate to branch entities if commit doesn't come from sketchup
unless from_sketchup
@branch_definition = branch_definition
@entities_to_fill = @branch_definition.entities
end
default_commit_layer = sketchup_model.layers.layers.find { |layer| layer.display_name == '@Untagged' }
@entities_to_fill = entities_to_fill(obj)
traverse_commit_object(obj, sketchup_model.layers, default_commit_layer, @entities_to_fill)
traverse_commit_object(obj, default_commit_layer, @entities_to_fill)
create_levels_from_section_planes
check_hiding_layers_needed
try_create_instance
@state
end
# Creating instance from @branch_definition only available for non-sketchup commits since we wrap commits
# under instance.
# There is also another use case that maybe definition is exist in file but user might be deleted it before.
# If this is the case we can add instance by checking number of instances.
# rubocop:disable Style/GuardClause
def try_create_instance
if !from_sketchup && (!@is_update_commit || @branch_definition.instances.empty?)
instance = sketchup_model.entities.add_instance(@branch_definition, Geom::Transformation.new)
BLOCK_INSTANCE.align_instance_axes(instance) if from_qgis
end
end
# rubocop:enable Style/GuardClause
def levels_layer
@levels_layer ||= sketchup_model.layers.add('Levels')
end
def clear_levels
instances = @entities_to_fill.grep(Sketchup::ComponentInstance)
instances.each do |instance|
speckle_type = instance.get_attribute(SPECKLE_BASE_OBJECT, 'speckle_type')
next if speckle_type.nil?
sketchup_model.definitions.remove(instance.definition) if speckle_type == OBJECTS_BUILTELEMENTS_REVIT_LEVEL
end
end
# Create levels from section planes that already created for this commit object.
# rubocop:disable Metrics/AbcSize
# rubocop:disable Metrics/MethodLength
def create_levels_from_section_planes
clear_levels if @is_update_commit
return unless from_revit
section_planes = @entities_to_fill.grep(Sketchup::SectionPlane)
@@ -106,7 +160,9 @@ module SpeckleConnector
section_planes.each do |section_plane|
level_name = "#{@definition_name}-#{section_plane.name}"
definition = sketchup_model.definitions.add(level_name)
@entities_to_fill.add_instance(definition, Geom::Transformation.new)
instance = @entities_to_fill.add_instance(definition, Geom::Transformation.new)
att = section_plane.attribute_dictionary(SPECKLE_BASE_OBJECT).to_h
SketchupModel::Dictionary::SpeckleEntityDictionaryHandler.set_hash(instance, att)
elevation = section_plane.bounds.center.z
c1_e = Geom::Point3d.new(c_1.x, c_1.y, elevation - LEVEL_SHIFT_VALUE)
c2_e = Geom::Point3d.new(c_2.x, c_2.y, elevation - LEVEL_SHIFT_VALUE)
@@ -123,8 +179,17 @@ module SpeckleConnector
# rubocop:enable Metrics/AbcSize
# rubocop:enable Metrics/MethodLength
# @return [Sketchup::ComponentDefinition] branch definition to fill objects in it.
def branch_definition
@definition_name = "#{@branch_name}-#{@stream_name}"
definition = sketchup_model.definitions.find { |d| d.name == @definition_name }
@is_update_commit = !definition.nil?
definition = sketchup_model.definitions.add(@definition_name) if definition.nil?
definition
end
def entities_to_fill(_obj)
return sketchup_model.entities if from_sketchup
return sketchup_model.entities unless from_revit
@definition_name = "#{@branch_name}-#{@stream_name}"
definition = sketchup_model.definitions.find { |d| d.name == @definition_name }
@@ -132,7 +197,7 @@ module SpeckleConnector
definition = sketchup_model.definitions.add(@definition_name)
sketchup_model.entities.add_instance(definition, Geom::Transformation.new)
end
definition.entities
definition
end
LAYERS_WILL_BE_HIDDEN = [
@@ -170,80 +235,12 @@ module SpeckleConnector
['Objects.BuiltElements.Revit.Parameter'].include?(obj['speckle_type'])
end
# Create actual Sketchup layers from layer_paths that taken from Speckle base object.
# @param layer_paths [Array<String>] layer paths to decompose it to folders and it's layers.
# @param folder [Sketchup::Layers, Sketchup::LayerFolder] folder to create folders and layers under it.
def create_layers(layer_paths, folder)
# Strip leading '@'
layers_with_folders = layer_paths.map { |layer| layer[1..-1] }
# Split layer_paths according to having parent folder or not.
layers_with_head_folder, headless_layers = layers_with_folders.partition { |layer| layer.include?('::') }
# Create array of array that split with '::'
folder_layer_arrays = layers_with_head_folder.collect { |folder_layer| folder_layer.split('::') }
# Add headless layers into `Sketchup.active_model.layers`
create_headless_layers(headless_layers, folder)
# Create layers that have parent folder(s)- this method is recursive until all tree is created.
create_folder_layers(folder_layer_arrays, folder)
end
# @param page [Sketchup::Page] scene to update -update properties-
def set_page_update_properties(page, update_properties)
update_properties.each do |prop, value|
page.instance_variable_set(:"@#{prop}", value)
end
end
# @param rendering_options [Sketchup::RenderingOptions] rendering options of scene (page)
def set_rendering_options(rendering_options, speckle_rendering_options)
speckle_rendering_options.each do |prop, value|
next if rendering_options[prop].nil?
rendering_options[prop] = if value.is_a?(Hash)
SpeckleObjects::Others::Color.to_native(value)
else
value
end
end
end
# @param headless_layers [Array<String>] headless layer names.
# @param folder [Sketchup::Layers, Sketchup::LayerFolder] layer folder to create commit layers under it.
def create_headless_layers(headless_layers, folder)
headless_layers.each do |layer_name|
# Add layer first to the layers object of sketchup model.
layer = sketchup_model.layers.add(layer_name)
folder.add_layer(layer) unless folder.layers.any? { |l| l.display_name == layer_name }
end
end
# Create layers with it's parent folders.
# @param folder [Sketchup::LayerFolder] layer folder to create commit layers under it.
def create_folder_layers(folder_layer_arrays, folder)
folder_layer_arrays.each do |folder_layer_array|
create_folder_layer(folder_layer_array, folder)
end
end
# Create layers that have parent folder(s)- this method is recursive (self-caller) until all tree is created.
def create_folder_layer(folder_array, folder)
if folder_array.length > 1
# add folder if it is not exist.
folder.add_folder(folder_array[0]) unless folder.folders.any? { |f| f.display_name == folder_array[0] }
new_folder = folder.folders.find { |f| f.display_name == folder_array[0] }
create_folder_layer(folder_array[1..-1], new_folder)
else
# Add layer first to the layers object of sketchup model.
layer = sketchup_model.layers.add(folder_array[0])
folder.add_layer(layer) unless folder.layers.any? { |l| l.display_name == layer }
end
end
# Traversal method to create Sketchup objects from upcoming base object.
# @param obj [Hash, Array] object might be source base object or it's sub objects, because this method is a
# self-caller method means that call itself according to conditions inside of it.
# rubocop:disable Metrics/CyclomaticComplexity
# rubocop:disable Metrics/PerceivedComplexity
def traverse_commit_object(obj, commit_folder, layer, entities)
def traverse_commit_object(obj, layer, entities)
if convertible_to_native?(obj)
@state = convert_to_native(@state, obj, layer, entities)
elsif obj.is_a?(Hash) && obj.key?('speckle_type')
@@ -253,57 +250,23 @@ module SpeckleConnector
# puts(">>> Found #{obj['speckle_type']}: #{obj['id']}. Continuing traversal.")
props = obj.keys.filter_map { |key| key unless key.start_with?('_') }
props.each do |prop|
layer_path = prop if prop.start_with?('@') && obj[prop].is_a?(Array)
layer = find_layer(layer_path, commit_folder, layer)
traverse_commit_object(obj[prop], commit_folder, layer, entities)
traverse_commit_object(obj[prop], layer, entities)
end
else
# puts(">>> Found #{obj['speckle_type']}: #{obj['id']} with displayValue.")
@state = convert_to_native(@state, obj, layer, entities)
end
elsif obj.is_a?(Hash)
obj.each_value { |value| traverse_commit_object(value, commit_folder, layer, entities) }
obj.each_value { |value| traverse_commit_object(value, layer, entities) }
elsif obj.is_a?(Array)
obj.each { |value| traverse_commit_object(value, commit_folder, layer, entities) }
obj.each { |value| traverse_commit_object(value, layer, entities) }
end
end
# rubocop:enable Metrics/CyclomaticComplexity
# rubocop:enable Metrics/PerceivedComplexity
# Find layer of the Speckle object by checking iteratively into folder.
# @param layer_path [String] complete layer_path to retrieve
# @param folder [Sketchup::LayerFolder, Sketchup::Layers] entry folder to search layer
# @param fallback_layer [Sketchup::Layer] fallback layer to assign object later if any error occur.
# @return [Sketchup::Layer] layer according to path
# @example
# "@folder_1::folder_2::layer_1"
# # it will return the layer object which has display name as `layer_1`.
def find_layer(layer_path, folder, fallback_layer)
begin
# Split folders and it's tail layer (last one is layer, others are folders.)
layer_path_array = layer_path[1..-1].split('::')
# Get sub folders as array, might be empty if `layer_path_array` has only 1 entry
sub_folders = layer_path_array.length > 1 ? layer_path_array[0..-2] : []
# Get exact layer name from last entry
layer_name = layer_path_array.last
# Iterate sub folders to find new sub folder to switch it.
# It help to search in the tree by switching the target search folder.
# Finally we can reach the layer name.
sub_folders.each do |sub_folder|
# Try to find sub folder into source folder passes by argument
s_f = folder.folders.find { |f| f.display_name == sub_folder }
# Switch source folder if any exist
folder = s_f unless s_f.nil?
end
# Find finally the layer into related folder
folder.layers.find { |l| l.display_name == layer_name }
rescue StandardError
return fallback_layer
end
end
def speckle_object_to_native(obj)
return DISPLAY_VALUE.method(:to_native) unless obj['displayValue'].nil?
return DISPLAY_VALUE.method(:to_native) unless obj['displayValue'].nil? && obj['@displayValue'].nil?
SPECKLE_OBJECT_TO_NATIVE[obj['speckle_type']]
end
@@ -317,7 +280,12 @@ module SpeckleConnector
OBJECTS_OTHER_BLOCKINSTANCE => BLOCK_INSTANCE.method(:to_native),
OBJECTS_OTHER_BLOCKINSTANCE_FULL => BLOCK_INSTANCE.method(:to_native),
OBJECTS_OTHER_REVIT_REVITINSTANCE => REVIT_INSTANCE.method(:to_native),
OBJECTS_OTHER_RENDERMATERIAL => RENDER_MATERIAL.method(:to_native)
OBJECTS_OTHER_RENDERMATERIAL => RENDER_MATERIAL.method(:to_native),
OBJECTS_BUILTELEMENTS_VIEW3D => VIEW3D.method(:to_native),
OBJECTS_BUILTELEMENTS_REVIT_DIRECTSHAPE => BUILTELEMENTS::Revit::DirectShape.method(:to_native),
OBJECTS_BUILTELEMENTS_NETWORK => BUILTELEMENTS::Network.method(:to_native),
OBJECTS_GIS_POLYGONELEMENT => POLYGON_ELEMENT.method(:to_native),
SPECKLE_CORE_MODELS_COLLECTION => COLLECTION.method(:to_native)
}.freeze
# @param state [States::State] state of the speckle application
@@ -350,13 +318,13 @@ module SpeckleConnector
layer = sketchup_model.layers.find { |l| l.display_name == speckle_object['category'] }
unless layer.nil?
entities.each { |entity| entity.layer = layer } if layer
entities.each { |entity| entity.layer = layer if entity.respond_to?(:layer) } if layer
return state
end
layer = sketchup_model.layers.add(speckle_object['category'])
unless layer.nil?
entities.each { |entity| entity.layer = layer } if layer
entities.each { |entity| entity.layer = layer if entity.respond_to?(:layer) } if layer
state
end
state
@@ -366,30 +334,37 @@ module SpeckleConnector
# @param state [States::State] state of the speckle application
def create_levels(state, speckle_object)
return state if speckle_object['level'].nil?
return state unless speckle_object['level']['speckle_type'].include?('Objects.BuiltElements.Level')
level = speckle_object['level']
return state if level.nil?
return state unless level['speckle_type'].include?('Objects.BuiltElements.Level')
level_name = speckle_object['level']['name'] || speckle_object['level']['id']
level_name = level['name'] || level['id']
is_exist = @entities_to_fill.grep(Sketchup::SectionPlane).any? { |sp| sp.name == level_name }
return state if is_exist
elevation = SpeckleObjects::Geometry.length_to_native(speckle_object['level']['elevation'],
speckle_object['level']['units'])
elevation = SpeckleObjects::Geometry.length_to_native(level['elevation'], level['units'])
section_plane = @entities_to_fill.add_section_plane([0, 0, elevation + LEVEL_SHIFT_VALUE], [0, 0, -1])
section_plane.name = level_name
SketchupModel::Dictionary::SpeckleEntityDictionaryHandler.write_initial_base_data(
section_plane, level['applicationId'], level['id'], level['speckle_type'], [], @stream_id
)
state
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)
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]
@@ -400,6 +375,8 @@ module SpeckleConnector
end
state.with_speckle_state(speckle_state)
end
# rubocop:enable Metrics/PerceivedComplexity
# rubocop:enable Metrics/CyclomaticComplexity
end
# rubocop:enable Metrics/ClassLength
end
+11 -162
View File
@@ -11,6 +11,8 @@ require_relative '../speckle_objects/other/block_definition'
require_relative '../speckle_objects/other/rendering_options'
require_relative '../speckle_objects/built_elements/view3d'
require_relative '../speckle_objects/built_elements/revit/direct_shape'
require_relative '../speckle_objects/relations/layers'
require_relative '../speckle_objects/speckle/core/models/model_collection'
require_relative '../constants/path_constants'
require_relative '../sketchup_model/reader/speckle_entities_reader'
require_relative '../sketchup_model/query/entity'
@@ -19,81 +21,19 @@ module SpeckleConnector
module Converters
# Converts sketchup entities to speckle objects.
class ToSpeckle < Converter
MODEL_COLLECTION = SpeckleObjects::Speckle::Core::Models::ModelCollection
DIRECT_SHAPE = SpeckleObjects::BuiltElements::Revit::DirectShape
SPECKLE_ENTITIES_READER = SketchupModel::Reader::SpeckleEntitiesReader
VIEW3D = SpeckleObjects::BuiltElements::View3d
# Convert selected objects by putting them into related array that grouped by layer.
# @return [Hash{Symbol=>Array}] layers -which only have objects- to hold it's objects under the base object.
def convert_selection_to_base(preferences)
layers = add_all_layers
state = speckle_state
sketchup_model.selection.each do |entity|
new_speckle_state, converted_object_with_entity = convert(entity, preferences, state)
state = new_speckle_state
layer_name = entity_layer_path(entity)
layers[layer_name].push(converted_object_with_entity) unless converted_object_with_entity.nil?
end
layers['@DirectShape'] = direct_shapes.collect do |entities|
from_mapped_to_speckle(entities[0], entities[1..-1], preferences)
end
# send only+ layers that have any object
base_object_properties = layers.reject { |_layer_name, objects| objects.empty? }
add_views(base_object_properties) if sketchup_model.pages.any?
return state, SpeckleObjects::Base.with_detached_layers(base_object_properties)
end
convert = method(:convert)
new_speckle_state, model_collection = MODEL_COLLECTION.from_sketchup_model(sketchup_model, speckle_state,
@units, preferences, &convert)
# Find flatten direct shapes by calculating their path to find global transformation later.
def direct_shapes
flat_selection_with_path = SketchupModel::Query::Entity
.flat_entities_with_path(
sketchup_model.selection,
[Sketchup::Face, Sketchup::ComponentInstance, Sketchup::Group], [sketchup_model]
)
mapped_selection = []
flat_selection_with_path.each do |entities|
entity = entities[0]
is_entity_mapped = SketchupModel::Reader::SpeckleEntitiesReader.mapped_with_schema?(entity)
if entity.respond_to?(:definition)
is_definition_mapped = SketchupModel::Reader::SpeckleEntitiesReader.mapped_with_schema?(entity.definition)
mapped_selection.append(entities) if is_entity_mapped || is_definition_mapped
next
end
mapped_selection.append(entities) if is_entity_mapped
end
mapped_selection
end
# Add views from pages.
# @param base_object_properties [Hash] dynamically attached base object properties.
def add_views(base_object_properties)
views = []
sketchup_model.pages.each do |page|
cam = page.camera
origin = get_camera_origin(cam)
target = get_camera_target(cam)
direction = get_camera_direction(cam)
update_properties = get_scene_update_properties(page)
rendering_options = SpeckleObjects::Others::RenderingOptions.to_speckle(page.rendering_options)
view = SpeckleObjects::BuiltElements::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
)
views.append(view)
end
base_object_properties['@Named Views'] = views
end
# Get scene properties
# @param page [Sketchup::Page] page on sketchup.
def get_scene_update_properties(page)
{
use_axes: page.use_axes?,
use_camera: page.use_camera?,
use_hidden_geometry: page.use_hidden_geometry?,
use_hidden_layers: page.use_hidden_layers?,
use_hidden_objects: page.use_hidden_objects?,
use_rendering_options: page.use_rendering_options?,
use_section_planes: page.use_section_planes?,
use_shadow_info: page.use_shadow_info?,
use_style: page.use_style?
}
return new_speckle_state, model_collection
end
# Serialized and traversed information to send batches.
@@ -180,97 +120,6 @@ module SpeckleConnector
return speckle_state, nil
end
# rubocop:enable Metrics/MethodLength
# Create layers -> {Hash{Symbol=>Array}} from sketchup model with empty array as hash entry values.
# This method add first headless layers (not belong to any folder),
# then goes through each folder, their sub-folders and their layers.
# @return [Hash{Symbol=>Array}] layers from sketchup model with empty array as hash entry values.
def add_all_layers
# add headless layers
layer_objects = add_layers(sketchup_model.layers.layers)
# add layers from folders
add_layers_from_folders(sketchup_model.layers.folders, layer_objects)
layer_objects
end
# @param layers [Array<Sketchup::Layer>] layers in sketchup model
# @return [Hash{Symbol=>Array}] layers with empty array value.
def add_layers(layers, layer_objects = {}, parent_name = '')
layers.each do |layer|
layer_name = parent_name.empty? ? "@#{layer.display_name}" : "#{parent_name}::#{layer.display_name}"
layer_objects[layer_name] = []
end
layer_objects
end
# @param folders [Array<Sketchup::LayerFolder>] layer folders in sketchup model.
# @param layer_objects [Hash{Symbol=>Array}] layer objects to fill in.
# @param parent_name [String] parent folder name to structure layer path before send to Speckle.
# ex: "@#{parent_name}::#{layer_name}"
def add_layers_from_folders(folders, layer_objects, parent_name = '')
folders.each do |folder|
folder_name = parent_name.empty? ? "@#{folder.display_name}" : "#{parent_name}::#{folder.display_name}"
add_layers(folder.layers, layer_objects, folder_name)
add_layers_from_folders(folder.folders, layer_objects, folder_name) unless folder.folders.empty?
end
end
# Find layer path of given Sketchup entity.
# @param entity [Sketchup::Entity] entity to find root layer.
# @return [String] layer path of Sketchup entity.
def entity_layer_path(entity)
layer_name = entity.layer.display_name
if entity.layer.folder.nil?
"@#{layer_name}"
else
folders = folder_name(entity.layer.folder)
path = ''
folders.reverse.each do |folder|
path += "#{folder}::"
end
"@#{path}#{layer_name}"
end
end
# Nested method to retrieve sub-folders until nothing found.
# @return [Array<String>] folder names as list from bottom to top. Might need to be reversed if you want to see
# from top to bottom.
def folder_name(folder, folders = [])
if folder.folder.nil?
folders.push(folder.display_name)
else
folder_name(folder.folder, folders.push(folder.display_name))
end
end
private
def get_camera_direction(cam)
SpeckleObjects::Geometry::Vector.new(
SpeckleObjects::Geometry.length_to_speckle(cam.direction[0], @units),
SpeckleObjects::Geometry.length_to_speckle(cam.direction[1], @units),
SpeckleObjects::Geometry.length_to_speckle(cam.direction[2], @units),
@units
)
end
def get_camera_target(cam)
SpeckleObjects::Geometry::Point.new(
SpeckleObjects::Geometry.length_to_speckle(cam.target[0], @units),
SpeckleObjects::Geometry.length_to_speckle(cam.target[1], @units),
SpeckleObjects::Geometry.length_to_speckle(cam.target[2], @units),
@units
)
end
def get_camera_origin(camera)
SpeckleObjects::Geometry::Point.new(
SpeckleObjects::Geometry.length_to_speckle(camera.eye[0], @units),
SpeckleObjects::Geometry.length_to_speckle(camera.eye[1], @units),
SpeckleObjects::Geometry.length_to_speckle(camera.eye[2], @units),
@units
)
end
end
end
end
@@ -0,0 +1,44 @@
# frozen_string_literal: true
module SpeckleConnector
module SketchupModel
# Query operations in sketchup model.
module Query
# Queries for layer and it's parents.
class Layer
class << self
# @param layer [Sketchup::Layer] layer to get folder path of the layer
# @return [Array<Sketchup::Folder>] path of the layer
def path(layer)
parent_folders = []
folder = layer.folder
until folder.nil?
parent_folders.append(folder)
folder = folder.folder
end
parent_folders.reverse
end
# @param entity [Sketchup::Entity] entity to find path.
def entity_path(entity, separation = '::')
path = path(entity.layer)
full_path = path.append(entity.layer)
full_path_string = ''
full_path.each_with_index do |layer, i|
full_path_string += layer.display_name
full_path_string += separation unless i == full_path.length - 1
end
full_path_string
end
# @param string_layer_path [String] string layer path to split.
def entity_layer_from_path(string_layer_path, separation = '::')
return string_layer_path if string_layer_path.nil?
string_layer_path.split(separation).last
end
end
end
end
end
end
@@ -77,7 +77,9 @@ module SpeckleConnector
entities.collect do |entity|
entity_selection_details = entity_selection_details(entity)
if entity.is_a?(Sketchup::ComponentInstance)
entity_selection_details = entity_selection_details.merge({ definition: entity_selection_details(entity.definition) })
entity_selection_details = entity_selection_details.merge(
{ definition: entity_selection_details(entity.definition) }
)
end
entity_selection_details
end
@@ -98,23 +100,30 @@ module SpeckleConnector
}
end
# rubocop:disable Metrics/CyclomaticComplexity
# rubocop:disable Metrics/PerceivedComplexity
def self.mapped_entity_details(entities)
reverse_category_dictionary = Mapping::Category::RevitCategory.reverse_dictionary
entities.collect do |entity|
speckle_schema = get_schema(entity)
speckle_schema_definition = entity.respond_to?(:definition) ? get_schema(entity.definition) : nil
entity_type = entity.class.name.split('::').last.gsub(/(?<=[a-z])(?=[A-Z])/, ' ').split.first
{
name: speckle_schema['name'],
category: speckle_schema['category'],
categoryName: reverse_category_dictionary[speckle_schema['category']],
method: speckle_schema['method'],
name: speckle_schema['name'] || speckle_schema_definition['name'],
category: speckle_schema['category'] || speckle_schema_definition['category'],
categoryName: reverse_category_dictionary[speckle_schema['category']] ||
reverse_category_dictionary[speckle_schema_definition['category']],
method: speckle_schema['method'] || speckle_schema_definition['method'],
entityName: entity.respond_to?(:name) ? entity.name : '',
entityId: entity.persistent_id,
entityType: entity.class.name.split('::').last.gsub(/(?<=[a-z])(?=[A-Z])/, ' ').split.first,
entityType: entity.is_a?(Sketchup::ComponentDefinition) ? 'Definition' : entity_type,
schema: speckle_schema,
definitionSchema: entity.respond_to?(:definition) ? get_schema(entity.definition) : nil
definitionSchema: speckle_schema_definition
}
end
end
# rubocop:enable Metrics/CyclomaticComplexity
# rubocop:enable Metrics/PerceivedComplexity
end
end
end
@@ -0,0 +1,84 @@
# frozen_string_literal: true
module SpeckleConnector
# Operations related to {SketchupModel}.
module SketchupModel
# Works directly with/on SketchUp Entities of different kinds (Groups, Faces, Edges, ...).
module Utils
# Static methods to do plane calculations with sketchup geom objects like Point3d and Vector3d.
class Plane
LENGTH_TOLERANCE = 1e-8
# Create plane from 3 points
# @param origin [Geom::Point3d] the point on the plane that wil become the origin of the local coordinate system
# @param point_1 [Geom::Point3d] the point that defines first direction
# @param point_2 [Geom::Point3d] the third point on the plane
# @return [Plane] the parametrization of the plane that goes through the given points
def self.from_points(origin, point_1, point_2)
direction_x = origin.vector_to(point_1).normalize
direction_x = direction_x.normalize
normal = direction_x.cross(origin.vector_to(point_2))
direction_y = direction_x.cross(normal.normalize)
new(origin: origin, direction_u: direction_x, direction_v: direction_y)
end
# @return [Geom::Vector3d] the direction of the u-axis on the plane
attr_reader :direction_u
# @return [Geom::Vector3d] the direction of the v-axis on the plane
attr_reader :direction_v
# @return [Geom::Point3d] the origin of the local coordinate system on the plane
attr_reader :origin
# @param origin [Geom::Point3d] the origin of the coordinate system on the plane
# @param direction_u [Geom::Vector3d] the direction of the x-axis
# @param direction_v [Geom::Vector3d] the direction of the y-axis
def initialize(origin:, direction_u:, direction_v:)
@origin = origin
@direction_u = direction_u
@direction_v = direction_v
end
# Get the point object in global coordinates for the point on the plane with local coordinates (u,v).
# @param coordinate_u [Float] the u-coordinate on the plane
# @param coordinate_v [Float] the v-coordinate on the plane
# @return [Geom::Point3d] the point in space that corresponds to the given (u, v) coordinates
def point_at(coordinate_u, coordinate_v)
scaled_direction_u = Geom::Vector3d.new(direction_u.x * coordinate_u,
direction_u.y * coordinate_u,
direction_u.z * coordinate_u)
scaled_direction_v = Geom::Vector3d.new(direction_v.x * coordinate_v,
direction_v.y * coordinate_v,
direction_v.z * coordinate_v)
origin + scaled_direction_u + scaled_direction_v
end
# Find local (u, v) coordinates of the projection of the given point to the plane
# @param point [Geom::Point3d] the point that will be projected to the plane
# @return [(Float, Float)] the local coordinates on the plane that correspond to the projected point
def plane_coordinates(point)
origin_to_point = origin.vector_to(point)
coordinate_u = origin_to_point.dot(direction_u)
coordinate_v = origin_to_point.dot(direction_v)
[coordinate_u, coordinate_v]
end
# Project a given point to the plane
# @param point [Geom::Point3d] the point that will be projected to the plane
# @return [Geom::Point3d] the projected point on the plane
def project_to_plane(point)
coordinate_u, coordinate_v = plane_coordinates(point)
point_at(coordinate_u, coordinate_v)
end
# Check if the given point lies on the plane
# @param point [Geom::Point3d] the point to check
# @return [Boolean] whether the point lies on the plane
def on_plane?(point)
point.distance(project_to_plane(point)).to_m < LENGTH_TOLERANCE
end
end
end
end
end
@@ -0,0 +1,23 @@
# frozen_string_literal: true
require_relative '../base'
require_relative '../../constants/type_constants'
module SpeckleConnector
module SpeckleObjects
module BuiltElements
# Network object represents scenes on Sketchup.
class Network < Base
SPECKLE_TYPE = OBJECTS_BUILTELEMENTS_NETWORK
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)
end
return state, []
end
end
end
end
end
@@ -2,8 +2,13 @@
require_relative '../../base'
require_relative '../../other/render_material'
require_relative '../../other/block_instance'
require_relative '../../other/block_definition'
require_relative '../../other/transform'
require_relative '../../../constants/type_constants'
require_relative '../../../sketchup_model/query/entity'
require_relative '../../../sketchup_model/reader/speckle_entities_reader'
require_relative '../../../sketchup_model/dictionary/speckle_schema_dictionary_handler'
module SpeckleConnector
module SpeckleObjects
@@ -12,6 +17,9 @@ module SpeckleConnector
# Direct shape definition for Revit mappings.
class DirectShape < Base
SPECKLE_TYPE = OBJECTS_BUILTELEMENTS_REVIT_DIRECTSHAPE
READER = SketchupModel::Reader
QUERY = SketchupModel::Query
DICTIONARY = SketchupModel::Dictionary
def initialize(name:, category:, units:, base_geometries:, application_id: nil)
super(
@@ -26,16 +34,71 @@ module SpeckleConnector
self[:baseGeometries] = base_geometries
end
def self.get_direct_shape_name(direct_shape)
if direct_shape['name'] == ''
direct_shape['applicationId'].to_s
else
"#{direct_shape['name']}::#{direct_shape['applicationId']}"
end
end
# @param state [States::State] state of the application.
def self.to_native(state, direct_shape, layer, entities, &convert_to_native)
direct_shape['geometry'] = direct_shape['baseGeometries']
direct_shape['name'] = get_direct_shape_name(direct_shape)
state, _definitions = Other::BlockDefinition.to_native(
state, direct_shape, layer, entities, &convert_to_native
)
definition = state.sketchup_state.sketchup_model
.definitions[Other::BlockDefinition.get_definition_name(direct_shape)]
instance = entities.add_instance(definition, Geom::Transformation.new)
instance.name = direct_shape['name'] unless direct_shape['name'].nil?
DICTIONARY::SpeckleSchemaDictionaryHandler.set_hash(
instance,
{
name: direct_shape['name'], category: direct_shape['category'], method: 'Direct Shape'
}
)
new_speckle_state = state.speckle_state.with_mapped_entity(instance)
state = state.with_speckle_state(new_speckle_state)
instance.layer = layer unless layer.nil?
return state, [instance, definition]
end
# Collects direct shapes on selection as flat list.
def self.direct_shapes_on_selection(sketchup_model)
flat_selection_with_path = QUERY::Entity.flat_entities_with_path(
sketchup_model.selection,
[Sketchup::Face, Sketchup::ComponentInstance, Sketchup::Group], [sketchup_model]
)
mapped_selection = []
flat_selection_with_path.each do |entities|
entity = entities[0]
is_entity_mapped = READER::SpeckleEntitiesReader.mapped_with_schema?(entity)
if entity.respond_to?(:definition)
is_definition_mapped = READER::SpeckleEntitiesReader.mapped_with_schema?(entity.definition)
mapped_selection.append(entities) if is_entity_mapped || is_definition_mapped
next
end
mapped_selection.append(entities) if is_entity_mapped
end
mapped_selection
end
def self.from_entity(entity, path, units, preferences)
schema = SketchupModel::Dictionary::SpeckleSchemaDictionaryHandler.attribute_dictionary(entity)
schema = DICTIONARY::SpeckleSchemaDictionaryHandler.attribute_dictionary(entity)
if schema.nil? && entity.respond_to?(:definition)
schema = SketchupModel::Dictionary::SpeckleSchemaDictionaryHandler.attribute_dictionary(entity.definition)
schema = DICTIONARY::SpeckleSchemaDictionaryHandler.attribute_dictionary(entity.definition)
end
entities_with_path = []
entities_with_path.append([entity] + path) if entity.is_a?(Sketchup::Face) || entity.is_a?(Sketchup::Edge)
# Collect here flat list
if entity.is_a?(Sketchup::ComponentInstance) || entity.is_a?(Sketchup::Group)
entities_with_path += SketchupModel::Query::Entity
entities_with_path += QUERY::Entity
.flat_entities_with_path(
entity.definition.entities, [Sketchup::Face], path.append(entity)
)
@@ -47,22 +110,23 @@ module SpeckleConnector
)
end
# rubocop:disable Metrics/MethodLength
def self.group_faces_under_mesh_by_material(faces_with_path, units, preferences)
mesh_groups = {}
faces_with_path.each do |face_with_path|
face = face_with_path[0]
entity_path = face_with_path[1..-1]
parent_material = SketchupModel::Query::Entity.parent_material(entity_path)
parent_material = QUERY::Entity.parent_material(entity_path)
mesh_group_id = Geometry::Mesh.get_mesh_group_id(face, preferences[:model], parent_material)
if mesh_groups.key?(mesh_group_id)
mesh_group = mesh_groups[mesh_group_id]
mesh_group[0].face_to_mesh(face, SketchupModel::Query::Entity.global_transformation(face, entity_path))
mesh_group[0].face_to_mesh(face, QUERY::Entity.global_transformation(face, entity_path))
mesh_group[1].append(face)
else
mesh = Geometry::Mesh.from_face(
face: face, units: units, model_preferences: preferences[:model],
global_transform: SketchupModel::Query::Entity.global_transformation(face, entity_path),
global_transform: QUERY::Entity.global_transformation(face, entity_path),
parent_material: parent_material
)
mesh_groups[mesh_group_id] = [mesh, [face]]
@@ -72,6 +136,7 @@ module SpeckleConnector
mesh_groups.each { |_, mesh| mesh.first.update_mesh }
mesh_groups.values
end
# rubocop:enable Metrics/MethodLength
end
end
end
@@ -2,6 +2,7 @@
require_relative '../base'
require_relative '../../constants/type_constants'
require_relative '../../speckle_objects/geometry/length'
require_relative '../../speckle_objects/geometry/point'
require_relative '../../speckle_objects/geometry/vector'
@@ -45,48 +46,121 @@ module SpeckleConnector
end
# rubocop:enable Metrics/ParameterLists
# Collects scenes as views from sketchup model.
# @param sketchup_model [Sketchup::Model] sketchup model to collect views from pages.
# @param units [String] units of the model.
def self.from_model(sketchup_model, units)
sketchup_model.pages.collect { |page| from_page(page, units) }
end
# @param page [Sketchup::Page] page to convert speckle view.
def self.from_page(page, units)
cam = page.camera
origin = get_camera_origin(cam, units)
target = get_camera_target(cam, units)
direction = get_camera_direction(cam, units)
update_properties = get_scene_update_properties(page)
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
)
end
# @param state [States::State] state of the speckle app.
# @param obj [Hash] commit object.
# @param sketchup_model [Sketchup::Model] active sketchup model.
# rubocop:disable Metrics/AbcSize
# rubocop:disable Metrics/PerceivedComplexity
# rubocop:disable Metrics/CyclomaticComplexity
def self.to_native(obj, sketchup_model)
views = collect_views(obj)
return if views.empty?
# rubocop:disable Metrics/PerceivedComplexity
def self.to_native(state, view, _layer, _entities, &_convert_to_native)
sketchup_model = state.sketchup_state.sketchup_model
views.each do |view|
next unless view['speckle_type'] == 'Objects.BuiltElements.View:Objects.BuiltElements.View3D'
return state, [] unless view['speckle_type'] == 'Objects.BuiltElements.View:Objects.BuiltElements.View3D'
name = view['name'] || view['id']
next if sketchup_model.pages.any? { |page| page.name == name }
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'])
# Set camera position before creating scene on it.
my_camera = Sketchup::Camera.new(origin, target, [0, 0, 1], !view['isOrthogonal'], lens)
sketchup_model.active_view.camera = my_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']
end
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
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/PerceivedComplexity
# rubocop:enable Metrics/CyclomaticComplexity
# rubocop:enable Metrics/PerceivedComplexity
def self.collect_views(obj)
views = []
views += obj.filter_map do |_key, value|
if value.is_a?(Array) &&
value.any? { |v| v['speckle_type'] == OBJECTS_BUILTELEMENTS_VIEW3D }
value
end
# @param page [Sketchup::Page] scene to update -update properties-
def self.set_page_update_properties(page, update_properties)
update_properties.each do |prop, value|
page.instance_variable_set(:"@#{prop}", value)
end
views.flatten.select { |view| view['speckle_type'] == OBJECTS_BUILTELEMENTS_VIEW3D }
end
# @param rendering_options [Sketchup::RenderingOptions] rendering options of scene (page)
def self.set_rendering_options(rendering_options, speckle_rendering_options)
speckle_rendering_options.each do |prop, value|
next if rendering_options[prop].nil?
rendering_options[prop] = if value.is_a?(Hash)
SpeckleObjects::Other::Color.to_native(value)
else
value
end
end
end
# Get scene properties
# @param page [Sketchup::Page] page on sketchup.
def self.get_scene_update_properties(page)
{
use_axes: page.use_axes?,
use_camera: page.use_camera?,
use_hidden_geometry: page.use_hidden_geometry?,
use_hidden_layers: page.use_hidden_layers?,
use_hidden_objects: page.use_hidden_objects?,
use_rendering_options: page.use_rendering_options?,
use_section_planes: page.use_section_planes?,
use_shadow_info: page.use_shadow_info?,
use_style: page.use_style?
}
end
def self.get_camera_direction(camera, units)
SpeckleObjects::Geometry::Vector.new(
SpeckleObjects::Geometry.length_to_speckle(camera.direction[0], units),
SpeckleObjects::Geometry.length_to_speckle(camera.direction[1], units),
SpeckleObjects::Geometry.length_to_speckle(camera.direction[2], units),
units
)
end
def self.get_camera_target(camera, units)
SpeckleObjects::Geometry::Point.new(
SpeckleObjects::Geometry.length_to_speckle(camera.target[0], units),
SpeckleObjects::Geometry.length_to_speckle(camera.target[1], units),
SpeckleObjects::Geometry.length_to_speckle(camera.target[2], units),
units
)
end
def self.get_camera_origin(camera, units)
SpeckleObjects::Geometry::Point.new(
SpeckleObjects::Geometry.length_to_speckle(camera.eye[0], units),
SpeckleObjects::Geometry.length_to_speckle(camera.eye[1], units),
SpeckleObjects::Geometry.length_to_speckle(camera.eye[2], units),
units
)
end
end
end
@@ -21,7 +21,7 @@ module SpeckleConnector
# @param units [String] units of the speckle line.
# @param application_id [String, nil] entity id of the {Sketchup::Edge} that represents to the speckle line.
# rubocop:disable Metrics/ParameterLists
def initialize(start_pt:, end_pt:, domain:, units:,
def initialize(start_pt:, end_pt:, domain:, units:, layer:,
sketchup_attributes: {}, speckle_schema: {}, application_id: nil)
super(
speckle_type: 'Objects.Geometry.Line',
@@ -33,6 +33,7 @@ module SpeckleConnector
self[:end] = end_pt
self[:domain] = domain
self[:units] = units
self[:layer] = layer unless layer.nil?
self[:SpeckleSchema] = speckle_schema if speckle_schema.any?
self[:sketchup_attributes] = sketchup_attributes if sketchup_attributes.any?
end
@@ -52,6 +53,7 @@ module SpeckleConnector
end_pt: end_pt,
domain: domain,
units: units,
layer: SketchupModel::Query::Layer.entity_path(edge),
sketchup_attributes: att,
speckle_schema: speckle_schema,
application_id: edge.persistent_id.to_s
@@ -63,6 +65,8 @@ module SpeckleConnector
# @param layer [Sketchup::Layer] layer to add {Sketchup::Edge} into it.
# @param entities [Sketchup::Entities] entities collection to add {Sketchup::Edge} into it.
# rubocop:disable Metrics/AbcSize
# rubocop:disable Metrics/PerceivedComplexity
# rubocop:disable Metrics/CyclomaticComplexity
def self.to_native(state, line, layer, entities, &_convert_to_native)
if line.key?('value')
values = line['value']
@@ -74,8 +78,10 @@ module SpeckleConnector
end_pt = Point.to_native(line['end']['x'], line['end']['y'], line['end']['z'], line['units'])
edges = entities.add_edges(start_pt, end_pt)
end
line_layer_name = SketchupModel::Query::Layer.entity_layer_from_path(line['layer'])
line_layer = state.sketchup_state.sketchup_model.layers.to_a.find { |l| l.display_name == line_layer_name }
edges.each do |edge|
edge.layer = layer
edge.layer = line_layer.nil? ? layer : line_layer
unless line['sketchup_attributes'].nil?
SketchupModel::Dictionary::BaseDictionaryHandler
.attribute_dictionaries_to_native(edge, line['sketchup_attributes']['dictionaries'])
@@ -84,6 +90,8 @@ module SpeckleConnector
return state, edges
end
# rubocop:enable Metrics/AbcSize
# rubocop:enable Metrics/PerceivedComplexity
# rubocop:enable Metrics/CyclomaticComplexity
def self.test_line(start_point, end_point, units)
domain = Primitive::Interval.from_numeric(0, 5, units)
@@ -91,6 +99,7 @@ module SpeckleConnector
start_pt: start_point,
end_pt: end_point,
domain: domain,
layer: 'test',
application_id: '',
units: units
)
@@ -7,6 +7,9 @@ require_relative '../../sketchup_model/query/entity'
require_relative '../../convertors/clean_up'
require_relative '../../sketchup_model/dictionary/base_dictionary_handler'
require_relative '../../sketchup_model/dictionary/speckle_schema_dictionary_handler'
require_relative '../../sketchup_model/dictionary/dictionary_handler'
require_relative '../../sketchup_model/utils/plane_utils'
require_relative '../../sketchup_model/query/layer'
module SpeckleConnector
module SpeckleObjects
@@ -32,7 +35,7 @@ module SpeckleConnector
# @param sketchup_attributes [Hash] additional information about speckle mesh.
# rubocop:disable Metrics/ParameterLists
def initialize(units:, render_material:, vertices:, faces:,
sketchup_attributes:, speckle_schema: {}, application_id: nil)
sketchup_attributes:, layer:, speckle_schema: {}, application_id: nil)
super(
speckle_type: SPECKLE_TYPE,
total_children_count: 0,
@@ -43,6 +46,7 @@ module SpeckleConnector
@polygons = []
@units = units
self[:units] = units
self[:layer] = layer
self[:renderMaterial] = render_material
self[:'@(31250)vertices'] = vertices
self[:'@(62500)faces'] = faces
@@ -51,6 +55,26 @@ module SpeckleConnector
end
# rubocop:enable Metrics/ParameterLists
# Checks 4 points are planar or not.
def self.check_4_points_planar(points)
plane = SketchupModel::Utils::Plane.from_points(points[0], points[1], points[2])
plane.on_plane?(points[3])
end
# Add quad mesh to sketchup native mesh by checking planarity.
# @param native_mesh [Geom::Mesh] sketchup mesh to convert them later faces.
# @param polygon_points [Array<Geom::Point3d>] sketchup points to add them with polygon to mesh.
def self.add_quad_mesh(native_mesh, polygon_points)
is_planar = check_4_points_planar(polygon_points)
if is_planar
native_mesh.add_polygon(polygon_points)
else
native_mesh.add_polygon([polygon_points[0], polygon_points[1], polygon_points[2]])
native_mesh.add_polygon([polygon_points[0], polygon_points[2], polygon_points[3]])
end
is_planar
end
# @param entities [Sketchup::Entities] entities to add
# rubocop:disable Metrics/MethodLength
# rubocop:disable Metrics/AbcSize
@@ -66,15 +90,23 @@ module SpeckleConnector
# Initialize native PolygonMesh object later to add polygon inside it.
native_mesh = Geom::PolygonMesh.new(mesh['vertices'].count / 3)
faces = mesh['faces']
has_any_non_planar_quad_mesh = false
while faces.count > 0
num_pts = faces.shift
# 0 -> 3, 1 -> 4 to preserve backwards compatibility
num_pts += 3 if num_pts < 3
indices = faces.shift(num_pts)
native_mesh.add_polygon(indices.map { |index| points[index] })
polygon_points = indices.map { |index| points[index] }
# Quad mesh
if num_pts == 4
is_planar = add_quad_mesh(native_mesh, polygon_points)
has_any_non_planar_quad_mesh = true unless is_planar
else
native_mesh.add_polygon(polygon_points)
end
end
state, _materials = Other::RenderMaterial.to_native(state, mesh['renderMaterial'],
layer, entities, &convert_to_native)
state, _materials = Other::RenderMaterial.to_native(state, mesh['renderMaterial'], layer,
entities, &convert_to_native)
# Find and assign material if exist
unless mesh['renderMaterial'].nil?
material_name = mesh['renderMaterial']['name'] || mesh['renderMaterial']['id']
@@ -85,8 +117,13 @@ module SpeckleConnector
# Add faces from mesh with material and smooth setting
entities.add_faces_from_mesh(native_mesh, smooth_flags, material, material)
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 }
added_faces.each do |face|
face.layer = layer
face.layer = mesh_layer unless mesh_layer.nil?
# Smooth edges if they already soft
# FIXME: Below line should be reconsidered. It might be a good to know here mesh comes from NURBS or not.
face.edges.each { |edge| edge.smooth = true if edge.soft? } if has_any_non_planar_quad_mesh
unless mesh['sketchup_attributes'].nil?
SketchupModel::Dictionary::BaseDictionaryHandler
.attribute_dictionaries_to_native(face, mesh['sketchup_attributes']['dictionaries'])
@@ -120,9 +157,8 @@ module SpeckleConnector
speckle_mesh = Mesh.new(
units: units,
render_material: material.nil? ? nil : Other::RenderMaterial.from_material(material),
vertices: [],
faces: [],
sketchup_attributes: att,
vertices: [], faces: [], sketchup_attributes: att,
layer: SketchupModel::Query::Layer.entity_path(face),
speckle_schema: speckle_schema,
application_id: face.persistent_id
)
@@ -250,9 +286,10 @@ module SpeckleConnector
end
material = face.material || face.back_material || parent_material
return 'none' if material.nil?
layer_name = face.layer.display_name
return layer_name if material.nil?
return material.entityID.to_s
return material.entityID.to_s + layer_name
end
def self.attribute_dictionary?(face)
@@ -0,0 +1,84 @@
# frozen_string_literal: true
require_relative '../base'
require_relative '../other/transform'
require_relative '../other/block_definition'
require_relative '../other/block_instance'
require_relative '../../constants/type_constants'
require_relative '../../sketchup_model/dictionary/dictionary_handler'
module SpeckleConnector
module SpeckleObjects
module GIS
# BoundingBox object definition for Speckle.
class PolygonElement < Base
SPECKLE_TYPE = OBJECTS_GIS_POLYGONELEMENT
def self.get_definition_name(obj, attributes)
return obj['name'] unless obj['name'].nil?
return attributes['name'] unless attributes['name'].nil?
return "def::#{obj['id']}"
end
def self.get_qgis_attributes(obj)
attributes = obj['attributes'].to_h
speckle_properties = %w[id speckle_type totalChildrenCount units applicationId]
speckle_properties.each { |key| attributes.delete(key) }
attributes
end
# Handles polygon element differently from display value.
def self.to_native(state, obj, layer, entities, &convert_to_native)
attributes = get_qgis_attributes(obj)
obj = collect_definition_geometries(obj)
obj['name'] = get_definition_name(obj, attributes)
state, _definitions = Other::BlockDefinition.to_native(
state, obj, layer, entities, &convert_to_native
)
definition = state.sketchup_state.sketchup_model
.definitions[Other::BlockDefinition.get_definition_name(obj)]
Other::BlockInstance.find_and_erase_existing_instance(definition, obj['id'], obj['applicationId'])
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?
SketchupModel::Dictionary::DictionaryHandler.set_hash(instance, attributes, 'qgis')
SketchupModel::Dictionary::DictionaryHandler.set_hash(definition, attributes, 'qgis')
# Align instance axes that created from display value. (without any transform)
Other::BlockInstance.align_instance_axes(instance)
return state, [instance, definition]
end
def self.collect_definition_geometries(obj)
geometries = []
# FIXME: This type check needed because of QGIS. It can send geometries both way, object or array..
# This is something need to be fixed by QGIS.
if obj['geometry'].is_a?(Array)
obj['geometry'].each do |geometry|
display_value = geometry['displayValue']
geometries += display_value
end
else
geometries += obj['geometry']['displayValue']
end
geometries.each do |geo|
if geo['speckle_type'] && geo['speckle_type'] == OBJECTS_GEOMETRY_MESH
geo['sketchup_attributes'] = { 'is_soften' => false }
end
end
obj['geometry'] = geometries
obj
end
end
end
end
end
@@ -75,7 +75,7 @@ module SpeckleConnector
block_definition = BlockDefinition.new(
units: units,
name: definition.name,
geometry: geometry,
geometry: geometry.compact,
always_face_camera: definition.behavior.always_face_camera?,
sketchup_attributes: att,
speckle_schema: speckle_schema,
@@ -106,7 +106,12 @@ module SpeckleConnector
definition_name = get_definition_name(definition_obj)
application_id = definition_obj['applicationId']
definition = sketchup_model.definitions[definition_name]
if definition && (definition.name == definition_name || definition.guid == application_id)
# Check any entities of definition changed
entities_updated = entities_updated?(definition, definition_obj)
if definition && !entities_updated &&
(definition.name == definition_name || definition.guid == application_id)
return state, [definition]
end
@@ -116,7 +121,6 @@ module SpeckleConnector
sketchup_attributes = definition_obj['sketchup_attributes']
definition&.entities&.clear!
definition ||= sketchup_model.definitions.add(definition_name)
definition.layer = layer
if geometry.is_a?(Array)
geometry.each do |obj|
@@ -145,6 +149,7 @@ module SpeckleConnector
# rubocop:disable Metrics/CyclomaticComplexity
# rubocop:disable Metrics/PerceivedComplexity
def self.group_entities_to_speckle(entities, preferences, speckle_state, parent, &convert)
entities = entities.reject(&:hidden?)
orphan_edges = entities.grep(Sketchup::Edge).filter { |edge| edge.faces.none? }
lines = orphan_edges.collect do |orphan_edge|
new_speckle_state, converted = convert.call(orphan_edge, preferences, speckle_state, parent)
@@ -207,7 +212,18 @@ module SpeckleConnector
end
# rubocop:enable Metrics/ParameterLists
# It is important check for hosted elements that wrapped into component in sketchup.
# Their definition name might be stay same but their speckle ids should be checked
# to compare they updated or not.
def self.entities_updated?(definition, speckle_definition)
children_changed = false
unless definition.nil?
# TODO: Here we need to check later if definition invalid or not.
previous_speckle_id = definition.get_attribute(SPECKLE_BASE_OBJECT, 'speckle_id')
children_changed = previous_speckle_id != speckle_definition['id']
end
children_changed
end
end
end
end
@@ -7,6 +7,7 @@ require_relative '../base'
require_relative '../geometry/bounding_box'
require_relative '../../sketchup_model/dictionary/base_dictionary_handler'
require_relative '../../sketchup_model/dictionary/speckle_schema_dictionary_handler'
require_relative '../../sketchup_model/query/layer'
module SpeckleConnector
module SpeckleObjects
@@ -24,7 +25,7 @@ module SpeckleConnector
# @param sketchup_attributes [Hash{Symbol=>Object}] sketchup attributes of the block instance.
# @param application_id [String] application id of the block instance.
# rubocop:disable Metrics/ParameterLists
def initialize(units:, is_sketchup_group:, name:, render_material:, transform:, block_definition:,
def initialize(units:, is_sketchup_group:, name:, render_material:, transform:, block_definition:, layer:,
sketchup_attributes: {}, speckle_schema: {}, application_id: nil)
super(
speckle_type: SPECKLE_TYPE,
@@ -34,6 +35,7 @@ module SpeckleConnector
)
self[:units] = units
self[:name] = name
self[:layer] = layer
self[:is_sketchup_group] = is_sketchup_group
self[:renderMaterial] = render_material
self[:transform] = transform
@@ -60,6 +62,7 @@ module SpeckleConnector
render_material: group.material.nil? ? nil : RenderMaterial.from_material(group.material),
transform: Other::Transform.from_transformation(group.transformation, units),
block_definition: block_definition,
layer: SketchupModel::Query::Layer.entity_path(group),
sketchup_attributes: att,
speckle_schema: speckle_schema,
application_id: group.guid
@@ -95,6 +98,7 @@ module SpeckleConnector
end,
transform: Other::Transform.from_transformation(component_instance.transformation, units),
block_definition: block_definition,
layer: SketchupModel::Query::Layer.entity_path(component_instance),
sketchup_attributes: att,
speckle_schema: speckle_schema,
application_id: component_instance.persistent_id.to_s
@@ -128,7 +132,10 @@ module SpeckleConnector
definition = state.sketchup_state.sketchup_model
.definitions[BlockDefinition.get_definition_name(block_definition)]
return add_instance_from_definition(state, block, layer, entities, definition, is_group, &convert_to_native)
block_layer_name = SketchupModel::Query::Layer.entity_layer_from_path(block['layer'])
block_layer = state.sketchup_state.sketchup_model.layers.to_a.find { |l| l.display_name == block_layer_name }
return add_instance_from_definition(state, block, block_layer, layer, entities, definition, is_group,
&convert_to_native)
end
def self.get_transform_matrix(block)
@@ -163,18 +170,19 @@ module SpeckleConnector
# rubocop:disable Metrics/CyclomaticComplexity
# rubocop:disable Metrics/PerceivedComplexity
# rubocop:disable Metrics/ParameterLists
def self.add_instance_from_definition(state, block, layer, entities, definition, is_group, &convert_to_native)
def self.add_instance_from_definition(state, block, block_layer, layer, entities, definition, is_group,
&convert_to_native)
t_arr = get_transform_matrix(block)
transform = Other::Transform.to_native(t_arr, block['units'])
instance = if is_group
# rubocop:disable SketchupSuggestions/AddGroup
group = entities.add_group(definition.entities.to_a)
group.layer = layer
group.layer = block_layer.nil? ? layer : block_layer
group
# rubocop:enable SketchupSuggestions/AddGroup
else
instance = entities.add_instance(definition, transform)
instance.layer = layer
instance.layer = block_layer.nil? ? layer : block_layer
instance
end
@@ -184,8 +192,8 @@ module SpeckleConnector
puts("Failed to create instance for speckle block instance #{block['id']}") if instance.nil?
# Transform already applied to instance unless is group
instance.transformation = transform if is_group
state, _materials = Other::RenderMaterial.to_native(state, block['renderMaterial'],
layer, entities, &convert_to_native)
state, _materials = Other::RenderMaterial.to_native(state, block['renderMaterial'], layer,
entities, &convert_to_native)
# Retrieve material from state
unless block['renderMaterial'].nil?
@@ -206,6 +214,20 @@ module SpeckleConnector
# rubocop:enable Metrics/CyclomaticComplexity
# rubocop:enable Metrics/PerceivedComplexity
# rubocop:enable Metrics/ParameterLists
# Instances that created from display value that has no any transform value.
# Because of this reason their definition created with origin axis. We basically create transformation
# vector between bounds min to origin, to move definition axis to bounds min. Otherwise they looks weird in
# sketchup and might be cumbersome when we want to add new entities into definition.
# @param instance [Sketchup::ComponentInstance] instance to align axis to it's bounds
def self.align_instance_axes(instance)
bounds = instance.bounds
transform = Geom::Transformation.translation(bounds.min.vector_to(Geom::Point3d.new(0, 0, 0)))
entities = instance.definition.entities
entities.transform_entities(transform, entities.to_a)
instance_transform = instance.transformation
instance.transform!(instance_transform * transform.inverse * instance_transform.inverse)
end
end
end
end
@@ -2,7 +2,7 @@
module SpeckleConnector
module SpeckleObjects
module Others
module Other
# Color object transformations between sketchup and speckle.
class Color
# @param color [Sketchup::Color] color to convert speckle object
@@ -16,9 +16,24 @@ module SpeckleConnector
end
def self.to_native(speckle_color)
return from_int(speckle_color) if speckle_color.is_a?(Numeric)
Sketchup::Color.new(speckle_color['red'], speckle_color['green'],
speckle_color['blue'], speckle_color['alpha'])
end
# @param color [Sketchup::Color] color to convert speckle object
# @return [Numeric] int value of the color
def self.to_int(color)
rgba = color.to_a
[rgba[3] << 24 | rgba[0] << 16 | rgba[1] << 8 | rgba[2]].pack('l').unpack1('l').to_i
end
# @param argb [Numeric] int value of the corresponding color
# @return [Sketchup::Color] sketchup color
def self.from_int(argb)
Sketchup::Color.new((argb >> 16) & 255, (argb >> 8) & 255, argb & 255, (argb >> 24) & 255)
end
end
end
end
@@ -0,0 +1,35 @@
# frozen_string_literal: true
require_relative 'color'
require_relative '../base'
require_relative '../../constants/type_constants'
module SpeckleConnector
module SpeckleObjects
module Other
# DisplayStyle object for layer.
class DisplayStyle < Base
def initialize(name:, color:, line_type:)
super(
speckle_type: OBJECTS_OTHER_DISPLAYSTYLE,
total_children_count: 0,
application_id: nil,
id: nil
)
self[:name] = name
self[:color] = color
self[:linetype] = line_type unless line_type.nil?
end
# @param layer [Sketchup::Layer] layer to get display style.
def self.from_layer(layer)
DisplayStyle.new(
name: '',
color: Color.to_int(layer.color),
line_type: layer.line_style.nil? ? nil : layer.line_style.name
)
end
end
end
end
end
@@ -22,7 +22,9 @@ module SpeckleConnector
return format_naming_convention([family, type, category, element_id]) unless element_id.nil?
return "def::#{def_obj['applicationId']}"
return "def::#{def_obj['applicationId']}" unless def_obj['applicationId'].nil?
return "def::#{def_obj['id']}"
end
def self.format_naming_convention(entries)
@@ -58,17 +60,22 @@ module SpeckleConnector
BlockInstance.find_and_erase_existing_instance(definition, obj['id'], obj['applicationId'])
t_arr = obj['transform']
transform = t_arr.nil? ? Geom::Transformation.new : OTHER::Transform.to_native(t_arr, units)
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.layer = layer unless layer.nil?
# Align instance axes that created from display value. (without any transform)
# BlockInstance.align_instance_axes(instance)
return state, [instance, definition]
end
def self.collect_definition_geometries(obj)
obj['geometry'] = obj['displayValue']
obj['geometry'] = obj['displayValue'] || obj['@displayValue']
if !obj['elements'].nil? && obj['elements'].is_a?(Array)
obj['elements'].each do |element|
elements = obj['elements'] || obj['@elements']
if !elements.nil? && elements.is_a?(Array)
elements.each do |element|
# Mullions is a special case here, they are extracted as base object with @displayValue from revit..
if element['@displayValue'].nil?
obj['geometry'].append(element)
@@ -1,5 +1,6 @@
# frozen_string_literal: true
require_relative 'color'
require_relative '../base'
module SpeckleConnector
@@ -38,10 +39,9 @@ module SpeckleConnector
# @param material [Sketchup::Material] material on the Sketchup.
def self.from_material(material)
rgba = material.color.to_a
RenderMaterial.new(
name: material.name,
diffuse: [rgba[3] << 24 | rgba[0] << 16 | rgba[1] << 8 | rgba[2]].pack('l').unpack1('l'),
diffuse: Other::Color.to_int(material.color),
opacity: material.alpha,
emissive: -16_777_216,
metalness: 0,
@@ -65,7 +65,7 @@ module SpeckleConnector
material = sketchup_model.materials.add(name)
material.alpha = render_material['opacity']
argb = render_material['diffuse']
material.color = Sketchup::Color.new((argb >> 16) & 255, (argb >> 8) & 255, argb & 255, (argb >> 24) & 255)
material.color = Color.from_int(argb)
new_sketchup_state = state.sketchup_state.with_materials(materials.add_material(name, material))
return state.with_sketchup_state(new_sketchup_state), [material]
end
@@ -4,7 +4,7 @@ require_relative 'color'
module SpeckleConnector
module SpeckleObjects
module Others
module Other
# Rendering options for scenes.
class RenderingOptions
# @param options [Sketchup::RenderingOptions] rendering options to convert speckle object
@@ -0,0 +1,89 @@
# frozen_string_literal: true
require_relative '../base'
require_relative '../other/color'
module SpeckleConnector
module SpeckleObjects
module Relations
# Sketchup layer (tag) tree relation.
class Layer < Base
SPECKLE_TYPE = 'Speckle.Core.Models.Collection'
# rubocop:disable Metrics/ParameterLists
def initialize(name:, visible:, is_folder:, line_style: nil, color: nil, layers_and_folders: [],
application_id: nil)
super(
speckle_type: SPECKLE_TYPE,
total_children_count: 0,
application_id: application_id,
id: nil
)
self[:name] = name
self[:color] = color
self[:visible] = visible
self[:is_folder] = is_folder
self[:line_style] = line_style unless line_style.nil?
self[:collectionType] = 'layer'
self[:elements] = layers_and_folders if layers_and_folders.any?
end
# rubocop:enable Metrics/ParameterLists
# @param speckle_layer [Object] speckle layer object.
# @param folder [Sketchup::Layers, Sketchup::LayerFolder] folder to create layers in it.
# @param sketchup_model [Sketchup::Model] sketchup active model.
def self.to_native_layer(speckle_layer, folder, sketchup_model)
layer = sketchup_model.layers.add_layer(speckle_layer[:name])
layer.visible = speckle_layer[:visible] unless speckle_layer[:visible].nil?
layer.color = SpeckleObjects::Other::Color.to_native(speckle_layer[:color]) if speckle_layer[:color]
if speckle_layer[:line_style]
line_style = sketchup_model.line_styles.find { |ls| ls.name == speckle_layer[:line_style] }
layer.line_style = line_style unless line_style.nil?
end
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? }
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.each do |speckle_folder|
sub_folder = folder.add_folder(speckle_folder[:name])
sub_folder.visible = speckle_folder[:visible] unless speckle_folder[:visible].nil?
to_native_layer_folder(speckle_folder, sub_folder, sketchup_model)
end
end
# @param folder [Sketchup::LayerFolder] sketchup layer folder that might contains other folders and layers.
def self.from_folder(folder)
layers = folder.layers.collect { |layer| from_layer(layer) }
sub_folders = folder.folders.collect { |sub_folder| from_folder(sub_folder) }
Layer.new(
name: folder.display_name,
visible: folder.visible?,
is_folder: true,
layers_and_folders: layers + sub_folders,
application_id: folder.persistent_id
)
end
# @param layer [Sketchup::Layer] sketchup layer (tag) that objects can be assigned..
def self.from_layer(layer)
Layer.new(
name: layer.display_name,
visible: layer.visible?,
is_folder: false,
line_style: layer.line_style.nil? ? nil : layer.line_style.name,
color: SpeckleObjects::Other::Color.to_speckle(layer.color),
application_id: layer.persistent_id
)
end
end
end
end
end
@@ -0,0 +1,79 @@
# frozen_string_literal: true
require_relative 'layer'
require_relative '../base'
module SpeckleConnector
module SpeckleObjects
module Relations
# Sketchup layers (tag) tree relation. The difference between Layer is, this is the top object that holds
# properties for all layers or folders, i.e. active layer.
class Layers < Base
SPECKLE_TYPE = 'Speckle.Core.Models.Collection'
def initialize(active:, layers:)
super(
speckle_type: SPECKLE_TYPE,
total_children_count: 0,
application_id: nil,
id: nil
)
self[:active_layer] = active
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)
return nil unless commit_obj['speckle_type'] == SPECKLE_CORE_MODELS_COLLECTION
elements = element_to_relation(commit_obj['elements'])
Layers.new(
active: commit_obj['active_layer'],
layers: elements
)
end
def self.to_native(layers_relation, sketchup_model)
folder = sketchup_model.layers
SpeckleObjects::Relations::Layer.to_native_layer_folder(layers_relation, folder, sketchup_model)
active_layer = folder.to_a.find { |layer| layer.display_name == layers_relation['active_layer'] }
sketchup_model.active_layer = active_layer unless active_layer.nil?
end
def self.from_model(sketchup_model)
# init with headless layers
headless_layers = sketchup_model.layers.layers.collect do |layer|
SpeckleObjects::Relations::Layer.from_layer(layer)
end
folders = sketchup_model.layers.folders.collect do |folder|
SpeckleObjects::Relations::Layer.from_folder(folder)
end
Layers.new(
active: sketchup_model.active_layer.display_name,
layers: headless_layers + folders
)
end
end
end
end
end
@@ -16,7 +16,7 @@ module SpeckleConnector
type = def_obj['type']
category = def_obj['category']
return "#{family}-#{type}-#{category}-#{def_obj['id']}"
return "#{family}-#{type}-#{category}-#{def_obj['elementId']}"
end
def self.to_native(state, definition, layer, entities, &convert_to_native)
@@ -30,8 +30,11 @@ module SpeckleConnector
definition = state.sketchup_state.sketchup_model
.definitions[RevitDefinition.get_definition_name(block_definition)]
block_layer = state.sketchup_state.sketchup_model.layers.to_a
.find { |l| l.display_name == block['category'] }
return SpeckleObjects::Other::BlockInstance.add_instance_from_definition(
state, block, layer, entities, definition, false, &convert_to_native
state, block, block_layer, layer, entities, definition, false, &convert_to_native
)
end
end
@@ -0,0 +1,43 @@
# frozen_string_literal: true
require_relative '../../../base'
require_relative '../../../../constants/type_constants'
module SpeckleConnector
module SpeckleObjects
module Speckle
module Core
module Models
# Collection object that collect other speckle objects under it's elements.
class Collection < Base
# @param name [String] name of the collection.
# @param collection_type [String] type of the collection like, layers, categories etc..
# @param elements [Array<Object>] elements of the collection.
# @param application_id [String, nil] id of the collection on the model.
def initialize(name:, collection_type:, elements: [], application_id: nil)
super(
speckle_type: SPECKLE_CORE_MODELS_COLLECTION,
total_children_count: 0,
application_id: application_id,
id: nil
)
self[:name] = name
self[:collectionType] = collection_type
self[:elements] = elements
end
def self.to_native(state, collection, layer, entities, &convert_to_native)
collection_type = collection['collectionType']
if collection_type.include?('model')
ModelCollection.to_native(state, collection, layer, entities, &convert_to_native)
else
LayerCollection.to_native(state, collection, layer, entities, &convert_to_native)
end
end
end
end
end
end
end
end
@@ -0,0 +1,118 @@
# frozen_string_literal: true
require_relative 'collection'
require_relative '../../../../sketchup_model/query/layer'
require_relative '../../../other/color'
require_relative '../../../other/display_style'
module SpeckleConnector
module SpeckleObjects
module Speckle
module Core
module Models
# LayerCollection object that collect other speckle objects under it's elements.
class LayerCollection < Collection
SPECKLE_TYPE = 'Speckle.Core.Models.Collection'
# rubocop:disable Metrics/ParameterLists
def initialize(name:, visible:, is_folder:, display_style: nil, color: nil, elements: [],
application_id: nil)
super(
name: name,
collection_type: 'layer',
elements: elements,
application_id: application_id
)
self[:visible] = visible
self[:is_folder] = is_folder
self[:color] = color unless color.nil?
self[:displayStyle] = display_style unless display_style.nil?
end
# rubocop:enable Metrics/ParameterLists
def self.create_layer_collections(sketchup_model)
headless_layers = sketchup_model.layers.layers.collect do |layer|
from_layer(layer)
end
folders = sketchup_model.layers.folders.collect do |folder|
from_folder(folder)
end
headless_layers + folders
end
# @param folder [Sketchup::LayerFolder] sketchup layer folder that might contains other folders and layers.
def self.from_folder(folder)
layers = folder.layers.collect { |layer| from_layer(layer) }
sub_folders = folder.folders.collect { |sub_folder| from_folder(sub_folder) }
LayerCollection.new(
name: folder.display_name,
visible: folder.visible?,
is_folder: true,
elements: layers + sub_folders,
application_id: folder.persistent_id
)
end
# @param layer [Sketchup::Layer] sketchup layer (tag) that objects can be assigned..
def self.from_layer(layer)
LayerCollection.new(
name: layer.display_name,
visible: layer.visible?,
is_folder: false,
display_style: Other::DisplayStyle.from_layer(layer),
application_id: layer.persistent_id
)
end
# @param entity_layer [Sketchup::Layer] entity layer.
# @param collection [Array] collection to search elements for entity's layer.
# rubocop:disable Metrics/CyclomaticComplexity
def self.get_or_create_layer_collection(entity_layer, collection)
folder_path = SpeckleConnector::SketchupModel::Query::Layer.path(entity_layer)
entity_layer_path = folder_path + [entity_layer]
entity_layer_path.each do |folder|
collection_candidate = collection[:elements].find do |el|
next if el.is_a?(Array)
el[:speckle_type] == SPECKLE_TYPE && el[:collectionType] == 'layer' &&
el[:name] == folder.display_name
end
if collection_candidate.nil?
color = folder.respond_to?(:color) ? SpeckleObjects::Other::Color.to_speckle(folder.color) : nil
collection_candidate = LayerCollection.new(
name: folder.display_name, visible: folder.visible?,
is_folder: folder.is_a?(Sketchup::LayerFolder), color: color
)
# Before switching collection with the new one, we should add it to current collection's elements
collection[:elements].append(collection_candidate)
end
collection = collection_candidate
end
collection
end
# rubocop:enable Metrics/CyclomaticComplexity
# @param state [States::State] state of the Speckle application.
def self.to_native(state, layer_collection, layer_or_folder, entities, &convert_to_native)
sketchup_model = state.sketchup_state.sketchup_model
elements = layer_collection['elements']
name = layer_collection['name']
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)
state = new_state
end
return state, []
end
end
end
end
end
end
end
@@ -0,0 +1,83 @@
# frozen_string_literal: true
require_relative 'collection'
require_relative 'layer_collection'
require_relative '../../../built_elements/view3d'
require_relative '../../../built_elements/revit/direct_shape'
module SpeckleConnector
module SpeckleObjects
module Speckle
module Core
module Models
# ModelCollection object that collect other speckle objects under it's elements.
class ModelCollection < Collection
DIRECT_SHAPE = SpeckleObjects::BuiltElements::Revit::DirectShape
VIEW3D = SpeckleObjects::BuiltElements::View3d
def initialize(name:, active_layer:, elements: [], application_id: nil)
super(
name: name,
collection_type: 'sketchup model',
elements: elements,
application_id: application_id
)
self[:active_layer] = active_layer
end
def self.from_sketchup_model(sketchup_model, speckle_state, units, preferences, &convert)
model_collection = ModelCollection.new(
name: 'Sketchup Model', active_layer: sketchup_model.active_layer.display_name,
application_id: sketchup_model.guid
)
# Direct shapes will pass directly to elements which are already flattened with all children
model_collection[:elements] += collect_direct_shapes(sketchup_model, units, preferences)
# Views will pass directly to elements since they don't have any relation with layers and geometries.
model_collection[:elements] += VIEW3D.from_model(sketchup_model, units) if sketchup_model.pages.any?
# Add layer collections.
model_collection[:elements] += LayerCollection.create_layer_collections(sketchup_model)
sketchup_model.selection.each do |entity|
layer_collection = LayerCollection.get_or_create_layer_collection(entity.layer, model_collection)
new_speckle_state, converted_object_with_entity = convert.call(entity, preferences, speckle_state)
speckle_state = new_speckle_state
unless converted_object_with_entity.nil?
layer_collection[:elements] = [] if layer_collection[:elements].nil?
layer_collection[:elements].append(converted_object_with_entity)
end
end
return speckle_state, model_collection
end
def self.collect_direct_shapes(sketchup_model, units, preferences)
DIRECT_SHAPE.direct_shapes_on_selection(sketchup_model).collect do |entities|
entity = entities[0]
path = entities[1..-1]
direct_shape = DIRECT_SHAPE.from_entity(entity, path, units, preferences)
[direct_shape, [entity]]
end
end
def self.to_native(state, model_collection, layer, entities, &convert_to_native)
elements = model_collection['elements']
elements.each do |element|
new_state = convert_to_native.call(state, element, layer, entities)
state = new_state
end
active_layer = model_collection['active_layer']
state.sketchup_state.sketchup_model.active_layer = active_layer unless active_layer.nil?
return state, []
end
end
end
end
end
end
end
+2
View File
@@ -64,6 +64,7 @@ module SpeckleConnector
private
# rubocop:disable Metrics/MethodLength
def commands
@commands ||= {
dialog_ready: Commands::DialogReady.new(@app),
@@ -91,6 +92,7 @@ module SpeckleConnector
show_all_entities: Commands::ActionCommand.new(@app, Actions::ShowAllEntities)
}.freeze
end
# rubocop:enable Metrics/MethodLength
end
end
end
+9 -3
View File
@@ -165,6 +165,7 @@ export default {
return {
isIsolated: false,
elementSelection: {},
categorySelection: [],
mappedEntities: [],
mappedEntityCount: 0,
// Expanded indexes for mapped element table (Categories)
@@ -181,9 +182,9 @@ export default {
}
},
computed: {
categorySelection() {
return Object.keys(this.elementSelection)
},
// categorySelection() {
// return Object.keys(this.elementSelection)
// },
},
mounted() {
sketchup.exec({name: "collect_mapped_entities", data: {}})
@@ -248,9 +249,14 @@ export default {
selectMappedElementsOnSketchup(){
sketchup.exec({ name: "select_mappings_from_table", data: this.elementSelection })
},
// Update mapped elements table whenever mapped elements has changed.
getMappedElementsTableData(){
let groupByCategoryName = groupBy('categoryName')
let groupedByCategoryName = groupByCategoryName(this.mappedEntities)
// Reset selected categories and elements whenever mapped elements states has changed
this.elementSelection = {}
this.categorySelection = []
this.mappedElementsExpandedIndexes = []
this.mappedEntitiesTableData = Object.entries(groupedByCategoryName).map(
(entry) => {
const [categoryName, entities] = entry
+3 -1
View File
@@ -26,9 +26,11 @@ const SpeckleMetrics = {
.digest('hex')
.toUpperCase()
let serverUrl = new URL(localStorage.getItem('serverUrl'))
let serverId = crypto
.createHash('md5')
.update(localStorage.getItem('serverUrl').toLowerCase())
.update(serverUrl.hostname.toLowerCase())
.digest('hex')
.toUpperCase()