Feat (SpeckleEntity): Read speckle entities when model loaded

This commit is contained in:
Oğuzhan Koral
2023-02-27 09:01:12 +03:00
committed by GitHub
12 changed files with 90 additions and 157 deletions
@@ -2,6 +2,7 @@
require_relative 'action'
require_relative 'initialize_materials'
require_relative '../sketchup_model/reader/speckle_entities_reader'
require_relative '../preferences/preferences'
require_relative '../states/state'
require_relative '../states/sketchup_state'
@@ -23,7 +24,10 @@ module SpeckleConnector
# Init materials again
new_state = InitializeMaterials.update_state(new_state)
# TODO: Read here SpeckleEntities if they exist in model.
# Read speckle entities
new_speckle_entities = SketchupModel::Reader::SpeckleEntitiesReader.read(sketchup_model.entities)
new_speckle_state = new_state.speckle_state.with_speckle_entities(Immutable::Hash.new(new_speckle_entities))
new_state = new_state.with_speckle_state(new_speckle_state)
# Read preferences from database and model.
preferences = Preferences.read_preferences(new_state.sketchup_state.sketchup_model)
@@ -6,6 +6,7 @@ module SpeckleConnector
SPECKLE_TYPE = 'speckle_type'
APPLICATION_ID = 'application_id'
TOTAL_CHILDREN_COUNT = 'total_children_count'
CHILDREN = 'children'
PARENT = 'parent'
VALID_STREAM_IDS = 'valid_stream_ids'
INVALID_STREAM_IDS = 'invalid_stream_ids'
@@ -5,6 +5,7 @@ require 'securerandom'
# rubocop:enable SketchupPerformance/OpenSSL
require 'digest'
require_relative 'converter'
require_relative '../speckle_entities/speckle_entity'
require_relative '../relations/many_to_one_relation'
module SpeckleConnector
@@ -117,11 +118,7 @@ module SpeckleConnector
if @preferences[:user][:diffing] && !entities.nil?
entities.uniq.each do |entity|
speckle_entity = if speckle_state.speckle_entities.keys.include?(entity.persistent_id)
speckle_state.speckle_entities[entity.persistent_id].with_valid_stream_id(stream_id)
else
SpeckleEntities.with_converted(entity, traversed_base, stream_id)
end
speckle_entity = create_or_update_speckle_entity(entity, id, traversed_base)
@speckle_state = speckle_state.with_speckle_entity(speckle_entity)
end
end
@@ -345,6 +342,22 @@ module SpeckleConnector
speckle_state.speckle_entities[entity.persistent_id].valid_stream_ids.include?(stream_id)
end
# Creates or updates speckle entity.
# If speckle entity exist in state, creates new one by updating old one.
# Else creates new one
# @return [SpeckleEntity] speckle entity that collects both speckle and sketchup information.
def create_or_update_speckle_entity(entity, id, traversed_base)
if speckle_state.speckle_entities.keys.include?(entity.persistent_id)
speckle_state.speckle_entities[entity.persistent_id].with_valid_stream_id(stream_id)
else
children = traversed_base[:__closure].nil? ? {} : traversed_base[:__closure]
speckle_entity = SpeckleEntities::SpeckleEntity.new(entity, id, traversed_base[:speckle_type],
children.keys, [stream_id])
speckle_entity.write_initial_base_data
speckle_entity
end
end
end
end
end
@@ -3,7 +3,6 @@
require_relative 'converter'
require_relative 'base_object_serializer'
require_relative '../relations/many_to_one_relation'
require_relative '../speckle_entities/speckle_entities'
require_relative '../speckle_objects/base'
require_relative '../speckle_objects/geometry/line'
require_relative '../speckle_objects/geometry/length'
@@ -12,13 +12,14 @@ module SpeckleConnector
# Writes initial data while speckle entity is creating first time.
# @param sketchup_entity [Sketchup::Entity] Sketchup entity to write data into it's attribute dictionary.
# rubocop:disable Metrics/ParameterLists
def self.write_initial_base_data(sketchup_entity, application_id, id, speckle_type, children_count, stream_id)
def self.write_initial_base_data(sketchup_entity, application_id, id, speckle_type, children, stream_id)
initial_dict_data = {
# Add here more if you want to write here initial data
SPECKLE_ID => id,
APPLICATION_ID => application_id,
SPECKLE_TYPE => speckle_type,
TOTAL_CHILDREN_COUNT => children_count,
TOTAL_CHILDREN_COUNT => children.length,
CHILDREN => children.keys,
VALID_STREAM_IDS => [stream_id],
INVALID_STREAM_IDS => []
}
@@ -0,0 +1,51 @@
# frozen_string_literal: true
require_relative '../../speckle_entities/speckle_entity'
require_relative '../../constants/dict_constants'
module SpeckleConnector
# Operations related to {SketchupModel}.
module SketchupModel
# Reader model for sketchup model.
module Reader
# Reader module for speckle entities.
module SpeckleEntitiesReader
# @param entities [Sketchup::Entities] entities to collect speckle entities.
def self.read(entities)
speckle_entities = {}
entities.each do |entity|
speckle_entities[entity.persistent_id] = read_speckle_entity(entity) if speckle_entity?(entity)
next unless entity.is_a?(Sketchup::Group) || entity.is_a?(Sketchup::ComponentInstance)
if speckle_entity?(entity.definition)
speckle_entities[entity.definition.persistent_id] = read_speckle_entity(entity.definition)
end
definition_speckle_entities = read(entity.definition.entities)
speckle_entities = speckle_entities.merge(definition_speckle_entities)
end
speckle_entities
end
# @param entity [Sketchup::Entity] sketchup entity to read from attribute dictionary.
def self.read_speckle_entity(entity)
dict = entity.attribute_dictionaries.to_a.find { |dict| dict.name == SPECKLE_BASE_OBJECT }
speckle_id = dict[:speckle_id]
speckle_type = dict[:speckle_type]
children = dict[:children]
valid_stream_ids = dict[:valid_stream_ids]
invalid_stream_ids = dict[:invalid_stream_ids]
SpeckleEntities::SpeckleEntity.new(entity, speckle_id, speckle_type, children,
valid_stream_ids, invalid_stream_ids)
end
# @param entity [Sketchup::Entity] sketchup entity to check if it was speckle entity once.
def self.speckle_entity?(entity)
return false if entity.attribute_dictionaries.nil?
return false if entity.attribute_dictionaries.to_a.empty?
entity.attribute_dictionaries.to_a.any? { |dict| dict.name == SPECKLE_BASE_OBJECT }
end
end
end
end
end
@@ -1,23 +0,0 @@
# frozen_string_literal: true
require_relative 'speckle_entity'
require_relative '../immutable/immutable'
module SpeckleConnector
module SpeckleEntities
# Speckle block definition entity is the state object for Sketchup::Entity and it's converted (or not yet) state.
class SpeckleBlockDefinitionEntity < SpeckleEntities::SpeckleEntity
include Immutable::ImmutableUtils
# @return [Hash{String=>SpeckleObjects::Base}] speckle objects belongs to block instance
attr_reader :children
def initialize(sketchup_group_or_component_instance, traversed_speckle_object, stream_id)
@children = traversed_speckle_object[:__closure].nil? ? {} : traversed_speckle_object[:__closure]
super(sketchup_group_or_component_instance, traversed_speckle_object, children, stream_id)
end
alias sketchup_edge sketchup_entity
end
end
end
@@ -1,27 +0,0 @@
# frozen_string_literal: true
require_relative 'speckle_entity'
require_relative '../immutable/immutable'
module SpeckleConnector
module SpeckleEntities
# Speckle block instance entity is the state object for Sketchup::Entity and it's converted (or not yet) state.
class SpeckleBlockInstanceEntity < SpeckleEntities::SpeckleEntity
include Immutable::ImmutableUtils
# @return [Hash{String=>SpeckleObjects::Base}] speckle objects belongs to block instance
attr_reader :children
# @return [Boolean] whether block instance represented as sketchup group or component instance
attr_reader :is_sketchup_group
def initialize(sketchup_group_or_component_instance, traversed_speckle_object, stream_id)
@children = traversed_speckle_object[:__closure].nil? ? {} : traversed_speckle_object[:__closure]
@is_sketchup_group = traversed_speckle_object[:is_sketchup_group]
super(sketchup_group_or_component_instance, traversed_speckle_object, children, stream_id)
end
alias sketchup_edge sketchup_entity
end
end
end
@@ -1,36 +0,0 @@
# frozen_string_literal: true
require_relative 'speckle_entity'
require_relative 'speckle_line_entity'
require_relative 'speckle_mesh_entity'
require_relative 'speckle_block_instance_entity'
require_relative 'speckle_block_definition_entity'
module SpeckleConnector
# Speckle entities are the state holder objects to achieve diffing, caching and updating.
# They are created whenever user send/receive objects between SketchUp and Speckle XYZ server.
# When Sketchup Entity is sent, by checking objects attributes, allow us to understand this object is sent previously?
# If yes, then use it for caching purpose only if object hasn't changed since it's previous sent state.
# If object has sent before but changed after, then update the SpeckleEntity with new traversed object.
# If no, then create SpeckleEntity and add it to the SpeckleState to check later.
module SpeckleEntities
# Speckle entity is the state object for Sketchup::Entity and it's converted (or not yet) state.
def self.with_converted(skp_entity, traversed, stream_id)
# return the same object if it is already SpeckleEntity
return skp_entity if skp_entity.is_a?(SpeckleEntity)
return SpeckleBlockInstanceEntity.new(skp_entity, traversed, stream_id) if skp_entity.is_a?(Sketchup::Group)
if skp_entity.is_a?(Sketchup::ComponentInstance)
return SpeckleBlockInstanceEntity.new(skp_entity, traversed,
stream_id)
end
if skp_entity.is_a?(Sketchup::ComponentDefinition)
return SpeckleBlockDefinitionEntity.new(skp_entity, traversed,
stream_id)
end
return SpeckleMeshEntity.new(skp_entity, traversed, stream_id) if skp_entity.is_a?(Sketchup::Face)
SpeckleLineEntity.new(skp_entity, traversed, stream_id) if skp_entity.is_a?(Sketchup::Edge)
end
end
end
@@ -13,9 +13,6 @@ module SpeckleConnector
# @return [Sketchup::Entity] Sketchup Entity represents {SpeckleEntity} on the model.
attr_reader :sketchup_entity
# @return [SpeckleObjects::Base] Speckle object that represented on server.
attr_reader :speckle_object
# @return [String] Speckle object type.
attr_reader :speckle_type
@@ -43,27 +40,26 @@ module SpeckleConnector
attr_reader :source_material, :active_diffing_stream_id
# @param sketchup_entity [Sketchup::Entity] sketchup entity represents {SpeckleEntity} on the model.
def initialize(sketchup_entity, traversed_speckle_object, children, stream_id)
# rubocop:disable Metrics/ParameterLists
def initialize(sketchup_entity, speckle_id, speckle_type, children, valid_stream_ids, invalid_stream_ids = [])
@status = SpeckleEntityStatus::UP_TO_DATE
@source_material = sketchup_entity.material
@active_diffing_stream_id = nil
@valid_stream_ids = [stream_id]
@invalid_stream_ids = []
@valid_stream_ids = valid_stream_ids
@invalid_stream_ids = invalid_stream_ids
@sketchup_entity = sketchup_entity
@application_id = @sketchup_entity.persistent_id
@id = traversed_speckle_object[:id]
@total_children_count = traversed_speckle_object[:totalChildrenCount]
@speckle_object = traversed_speckle_object
@speckle_type = speckle_object[:speckle_type]
@id = speckle_id
@total_children_count = children.length
@speckle_type = speckle_type
@speckle_children_objects = children
end
# rubocop:enable Metrics/ParameterLists
def write_initial_base_data
SketchupModel::Dictionary::SpeckleEntityDictionaryHandler
.write_initial_base_data(@sketchup_entity, application_id, id, speckle_type,
@speckle_children_objects.length, stream_id)
# FIXME: Understand why below condition does not match for same cases. I guess it is a typo bug.
# unless total_children_count == speckle_children_objects.length
# raise StandardError "total children count mismatch for #{application_id}"
# end
@speckle_children_objects, valid_stream_ids.first)
end
def with_up_to_date
@@ -1,23 +0,0 @@
# frozen_string_literal: true
require_relative 'speckle_entity'
require_relative '../immutable/immutable'
module SpeckleConnector
module SpeckleEntities
# Speckle line entity is the state object for Sketchup::Entity and it's converted (or not yet) state.
class SpeckleLineEntity < SpeckleEntities::SpeckleEntity
include Immutable::ImmutableUtils
# @return [Hash{String=>SpeckleObjects::Base}] speckle objects belongs to edge
attr_reader :children
def initialize(sketchup_edge, traversed_speckle_object, stream_id)
@children = traversed_speckle_object[:__closure].nil? ? {} : traversed_speckle_object[:__closure]
super(sketchup_edge, traversed_speckle_object, children, stream_id)
end
alias sketchup_edge sketchup_entity
end
end
end
@@ -1,23 +0,0 @@
# frozen_string_literal: true
require_relative 'speckle_entity'
require_relative '../immutable/immutable'
module SpeckleConnector
module SpeckleEntities
# Speckle mesh entity is the state object for Sketchup::Entity and it's converted (or not yet) state.
class SpeckleMeshEntity < SpeckleEntities::SpeckleEntity
include Immutable::ImmutableUtils
# @return [Hash{String=>SpeckleObjects::Base}] speckle objects belongs to edge
attr_reader :children
def initialize(sketchup_face, traversed_speckle_object, stream_id)
@children = traversed_speckle_object[:__closure].nil? ? {} : traversed_speckle_object[:__closure]
super(sketchup_face, traversed_speckle_object, children, stream_id)
end
alias sketchup_edge sketchup_entity
end
end
end