diff --git a/speckle_connector/src/actions/mapped_entities_updated.rb b/speckle_connector/src/actions/mapped_entities_updated.rb index 63de9c8..c9a0483 100644 --- a/speckle_connector/src/actions/mapped_entities_updated.rb +++ b/speckle_connector/src/actions/mapped_entities_updated.rb @@ -11,7 +11,7 @@ module SpeckleConnector # @return [States::State] the new updated state object def self.update_state(state, _data = nil) mapped_entities = SketchupModel::Reader::SpeckleEntitiesReader - .mapped_entity_details(state.speckle_state.mapped_entities.values.to_a) + .mapped_entity_details(state.speckle_state.speckle_mapper_state.mapped_entities.values.to_a) state.with_mapped_entities_queue(mapped_entities) end diff --git a/speckle_connector/src/actions/mapper_source_updated.rb b/speckle_connector/src/actions/mapper_source_updated.rb new file mode 100644 index 0000000..bbdbf3d --- /dev/null +++ b/speckle_connector/src/actions/mapper_source_updated.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +require_relative 'action' +require_relative '../mapper/mapper_source' +require_relative '../speckle_objects/built_elements/revit/revit_element_type' + +module SpeckleConnector + module Actions + # Action to update mapper source. + class MapperSourceUpdated < Action + def initialize(base, stream_id, commit_id) + super() + @base = base + @stream_id = stream_id + @commit_id = commit_id + end + + # @param state [States::State] the current state of the {App::SpeckleConnectorApp} + # @return [States::State] the new updated state object + def update_state(state) + levels = convert_levels(state, @base['@Levels']) + types = convert_types(@base['@Types']) + mapper_source = Mapper::MapperSource.new(@stream_id, @commit_id, levels, types) + new_speckle_state = state.speckle_state.with_mapper_source(mapper_source) + state = state.with_speckle_state(new_speckle_state) + + state.with_add_queue('mapperSourceUpdated', @stream_id, [ + { is_string: false, val: levels.to_json }, + { is_string: false, val: types.to_json } + ]) + end + + def convert_types(types) + types.collect do |type, type_elements| + next if type_elements.nil? || !type_elements.is_a?(Array) || type == '__closure' + + type = type[1..-1] if type[0] == '@' + elements = type_elements.map do |type_element| + SpeckleObjects::BuiltElements::Revit::RevitElementType.to_native(type_element) + end + [type, elements] + end.compact.to_h + end + + def convert_levels(state, levels) + levels.collect do |level| + SpeckleObjects::BuiltElements::Level.to_native(state, level) + end + end + end + end +end diff --git a/speckle_connector/src/commands/mapper_source_updated.rb b/speckle_connector/src/commands/mapper_source_updated.rb new file mode 100644 index 0000000..9f9599f --- /dev/null +++ b/speckle_connector/src/commands/mapper_source_updated.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +require_relative 'command' +require_relative '../actions/mapper_source_updated' + +module SpeckleConnector + module Commands + # Command to update mapper source. + class MapperSourceUpdated < Command + def _run(data) + base = data['base'] + stream_id = data['stream_id'] + commit_id = data['commit_id'] + action = Actions::MapperSourceUpdated.new(base, stream_id, commit_id) + app.update_state!(action) + end + end + end +end diff --git a/speckle_connector/src/constants/type_constants.rb b/speckle_connector/src/constants/type_constants.rb index e23511b..bfff901 100644 --- a/speckle_connector/src/constants/type_constants.rb +++ b/speckle_connector/src/constants/type_constants.rb @@ -9,6 +9,7 @@ module SpeckleConnector OBJECTS_BUILTELEMENTS_NETWORK = 'Objects.BuiltElements.Network' OBJECTS_BUILTELEMENTS_FLOOR = 'Objects.BuiltElements.Floor' OBJECTS_BUILTELEMENTS_REVIT_DIRECTSHAPE = 'Objects.BuiltElements.Revit.DirectShape' + OBJECTS_BUILTELEMENTS_REVIT_REVITELEMENTTYPE = 'Objects.BuiltElements.Revit.RevitElementType' OBJECTS_BUILTELEMENTS_REVIT_LEVEL = 'Objects.BuiltElements.Level:Objects.BuiltElements.Revit.RevitLevel' OBJECTS_GEOMETRY_LINE = 'Objects.Geometry.Line' diff --git a/speckle_connector/src/mapper/mapper_source.rb b/speckle_connector/src/mapper/mapper_source.rb new file mode 100644 index 0000000..4221c1c --- /dev/null +++ b/speckle_connector/src/mapper/mapper_source.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +require_relative '../immutable/immutable' +require_relative '../speckle_objects/built_elements/level' +require_relative '../speckle_objects/built_elements/revit/revit_element_type' + +module SpeckleConnector + # Mapper is a tool to convert SketchUp entities to other applications' native objects. + module Mapper + # Mapper source object that collects information about stream id and commit id to identify source in the branch, + # also contains levels and family types to be able to map objects with them. + class MapperSource + # @return [String] stream id of the mapper source. + attr_reader :stream_id + + # @return [String] commit id of the mapper source. + attr_reader :commit_id + + # @return [Array] levels in the source branch. + attr_reader :levels + + # @return [ImmutableHash{String=>Array}] revit element + # types. + attr_reader :types + + def initialize(stream_id, commit_id, levels, types) + @stream_id = stream_id + @commit_id = commit_id + @levels = levels + @types = types + end + end + end +end diff --git a/speckle_connector/src/speckle_objects/built_elements/level.rb b/speckle_connector/src/speckle_objects/built_elements/level.rb new file mode 100644 index 0000000..1a60b81 --- /dev/null +++ b/speckle_connector/src/speckle_objects/built_elements/level.rb @@ -0,0 +1,71 @@ +# frozen_string_literal: true + +require_relative '../base' +require_relative '../other/render_material' +require_relative '../geometry/line' +require_relative '../geometry/length' +require_relative '../geometry/polyline' +require_relative '../../constants/type_constants' + +module SpeckleConnector + module SpeckleObjects + module BuiltElements + # Level object. + class Level < Base + SPECKLE_TYPE = OBJECTS_BUILTELEMENTS_REVIT_LEVEL + + def initialize(name:, elevation:, units:, element_id:, application_id: nil, id: nil) + super( + speckle_type: SPECKLE_TYPE, + total_children_count: 0, + application_id: application_id, + id: id + ) + self[:name] = name + self[:elevation] = elevation + self[:units] = units + self[:elementId] = element_id + end + + # @param state [States::State] state of the application. + def self.to_native(state, speckle_level) + sketchup_model = state.sketchup_state.sketchup_model + levels_layer = sketchup_model.layers.layers.find { |layer| layer.display_name == 'Levels' } + levels_layer = sketchup_model.layers.add('Levels') if levels_layer.nil? + + name = speckle_level['name'] + elevation = speckle_level['elevation'] + units = speckle_level['units'] + element_id = speckle_level['elementId'] + application_id = speckle_level['applicationId'] + + skp_elevation = Geometry.length_to_native(elevation, units) + + definition_name = "#{name}-#{application_id}" + definition = sketchup_model.definitions.find { |definition| definition.name == definition_name } + definition.entities.clear! unless definition.nil? + definition = sketchup_model.definitions.add(definition_name) if definition.nil? + instance = sketchup_model.entities.add_instance(definition, Geom::Transformation.new) + instance.locked = true + + c1_e = Geom::Point3d.new(0, 10.m, skp_elevation) + c2_e = Geom::Point3d.new(0, 0, skp_elevation) + c3_e = Geom::Point3d.new(10.m, 0, skp_elevation) + cline_1 = definition.entities.add_cline(c1_e, c2_e) + cline_2 = definition.entities.add_cline(c2_e, c3_e) + text = definition.entities.add_text(" #{name}", c1_e) + [cline_1, cline_2, text, definition, instance].each { |o| o.layer = levels_layer } + + Level.new( + name: name, + elevation: elevation, + units: units, + element_id: element_id, + application_id: application_id, + id: speckle_level['id'] + ) + end + end + end + end +end diff --git a/speckle_connector/src/speckle_objects/built_elements/revit/revit_element_type.rb b/speckle_connector/src/speckle_objects/built_elements/revit/revit_element_type.rb new file mode 100644 index 0000000..32a7f66 --- /dev/null +++ b/speckle_connector/src/speckle_objects/built_elements/revit/revit_element_type.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +require_relative '../../base' + +module SpeckleConnector + module SpeckleObjects + module BuiltElements + module Revit + # Revit element type. + class RevitElementType < Base + SPECKLE_TYPE = OBJECTS_BUILTELEMENTS_REVIT_REVITELEMENTTYPE + + # rubocop:disable Metrics/ParameterLists + def initialize(category:, family:, type:, element_id:, application_id: nil, id: nil) + super( + speckle_type: SPECKLE_TYPE, + total_children_count: 0, + application_id: application_id, + id: id + ) + self[:category] = category + self[:family] = family + self[:type] = type + self[:elementId] = element_id + end + # rubocop:enable Metrics/ParameterLists + + def self.to_native(revit_element_type) + RevitElementType.new( + category: revit_element_type['category'], + family: revit_element_type['family'], + type: revit_element_type['type'], + element_id: revit_element_type['elementId'], + application_id: revit_element_type['applicationId'], + id: revit_element_type['id'] + ) + end + end + end + end + end +end diff --git a/speckle_connector/src/states/speckle_mapper_state.rb b/speckle_connector/src/states/speckle_mapper_state.rb new file mode 100644 index 0000000..28ef2d6 --- /dev/null +++ b/speckle_connector/src/states/speckle_mapper_state.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +require_relative '../immutable/immutable' +require_relative '../callbacks/callback_message' +require_relative '../speckle_entities/speckle_entity' +require_relative '../mapper/mapper_source' + +module SpeckleConnector + module States + # State of the speckle on ruby. + class SpeckleMapperState + include Immutable::ImmutableUtils + + # @return [ImmutableHash{Integer=>Sketchup::Entity}] persistent_id of the sketchup entity and itself + attr_reader :mapped_entities + + # @return [Mapper::MapperSource] source of the mapper. + attr_reader :mapper_source + + def initialize + @mapped_entities = Immutable::EmptyHash + @mapper_source = nil + end + + def with_mapped_entity(entity) + new_mapped_entities = mapped_entities.put(entity.persistent_id, entity) + with_mapped_entities(new_mapped_entities) + end + + def with_removed_mapped_entity(entity) + new_mapped_entities = mapped_entities.delete(entity.persistent_id) + with_mapped_entities(new_mapped_entities) + end + + def with_mapped_entities(new_mapped_entities) + with(:@mapped_entities => new_mapped_entities) + end + + def with_mapper_source(mapper_source) + # TODO: Check/Sync here parameters of the mapped entities. + with(:@mapper_source => mapper_source) + end + end + end +end diff --git a/speckle_connector/src/states/speckle_state.rb b/speckle_connector/src/states/speckle_state.rb index 1a80eae..0264080 100644 --- a/speckle_connector/src/states/speckle_state.rb +++ b/speckle_connector/src/states/speckle_state.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true +require_relative 'speckle_mapper_state' require_relative '../immutable/immutable' require_relative '../callbacks/callback_message' require_relative '../speckle_entities/speckle_entity' @@ -10,13 +11,13 @@ module SpeckleConnector class SpeckleState include Immutable::ImmutableUtils + # @return [States::SpeckleMapperState] state of the mapper. + attr_reader :speckle_mapper_state + # @return [ImmutableHash{Integer=>SpeckleEntities::SpeckleEntity}] persistent_id of the sketchup entity and # corresponding speckle entity attr_reader :speckle_entities - # @return [ImmutableHash{Integer=>Sketchup::Entity}] persistent_id of the sketchup entity and itself - attr_reader :mapped_entities - # @return [Array] accounts on appdata. attr_reader :accounts @@ -47,10 +48,10 @@ module SpeckleConnector @message_queue = queue @stream_queue = stream_queue @speckle_entities = Immutable::EmptyHash - @mapped_entities = Immutable::EmptyHash @render_materials = Immutable::EmptyHash @definitions = Immutable::EmptyHash @relation = Relations::ManyToOneRelation.new + @speckle_mapper_state = SpeckleMapperState.new end # @param callback_name [String] name of the callback command @@ -95,14 +96,24 @@ module SpeckleConnector with(:@accounts => new_accounts) end + def with_mapper_source(mapper_source) + new_speckle_mapper_state = speckle_mapper_state.with_mapper_source(mapper_source) + with(:@speckle_mapper_state => new_speckle_mapper_state) + end + def with_mapped_entity(entity) - new_mapped_entities = mapped_entities.put(entity.persistent_id, entity) - with_mapped_entities(new_mapped_entities) + new_speckle_mapper_state = speckle_mapper_state.with_mapped_entity(entity) + with(:@speckle_mapper_state => new_speckle_mapper_state) end def with_removed_mapped_entity(entity) - new_mapped_entities = mapped_entities.delete(entity.persistent_id) - with_mapped_entities(new_mapped_entities) + new_speckle_mapper_state = speckle_mapper_state.with_removed_mapped_entity(entity) + with(:@speckle_mapper_state => new_speckle_mapper_state) + end + + def with_mapped_entities(new_mapped_entities) + new_speckle_mapper_state = speckle_mapper_state.with_mapped_entities(new_mapped_entities) + with(:@speckle_mapper_state => new_speckle_mapper_state) end def with_speckle_entity(traversed_entity) @@ -114,10 +125,6 @@ module SpeckleConnector with(:@speckle_entities => new_speckle_entities) end - def with_mapped_entities(new_mapped_entities) - with(:@mapped_entities => new_mapped_entities) - end - def with_relation(new_relation) with(:@relation => new_relation) end diff --git a/speckle_connector/src/ui/vue_view.rb b/speckle_connector/src/ui/vue_view.rb index af48d13..fb7ce2b 100644 --- a/speckle_connector/src/ui/vue_view.rb +++ b/speckle_connector/src/ui/vue_view.rb @@ -16,6 +16,7 @@ require_relative '../commands/model_preferences_updated' require_relative '../commands/activate_diffing' require_relative '../commands/apply_mappings' require_relative '../commands/clear_mappings' +require_relative '../commands/mapper_source_updated' require_relative '../actions/reload_accounts' require_relative '../actions/load_saved_streams' @@ -89,7 +90,8 @@ module SpeckleConnector isolate_mappings_from_table: Commands::ActionCommand.new(@app, Actions::IsolateMappingsFromTable), hide_mappings_from_table: Commands::ActionCommand.new(@app, Actions::HideMappingsFromTable), select_mappings_from_table: Commands::ActionCommand.new(@app, Actions::SelectMappingsFromTable), - show_all_entities: Commands::ActionCommand.new(@app, Actions::ShowAllEntities) + show_all_entities: Commands::ActionCommand.new(@app, Actions::ShowAllEntities), + mapper_source_updated: Commands::MapperSourceUpdated.new(@app) }.freeze end # rubocop:enable Metrics/MethodLength diff --git a/ui/src/components/Mapper.vue b/ui/src/components/Mapper.vue index 354bb70..051566f 100644 --- a/ui/src/components/Mapper.vue +++ b/ui/src/components/Mapper.vue @@ -58,6 +58,34 @@ + + + + + mdi-source-branch + + {{ `Source` }} + + + mdi-update + + + + + + + + +
@@ -204,6 +232,12 @@ /*global sketchup*/ import {bus} from "@/main"; import {groupBy} from "@/utils/groupBy"; +import MappingSource from "@/components/MapperSource.vue"; + +global.mapperSourceUpdated = function (streamId, levels, types) { + console.log(JSON.stringify(levels), "levels") + console.log(JSON.stringify(types), "types") +} global.entitySelected = function (selectionParameters) { bus.$emit('entities-selected', JSON.stringify(selectionParameters)) @@ -220,11 +254,13 @@ global.mappedEntitiesUpdated = function (mappedEntities) { export default { name: "Mapper", components: { + MapperSource: () => import('@/components/MapperSource.vue'), GlobalToast: () => import('@/components/GlobalToast'), MappedElements: () => import('@/components/MappedElements.vue') }, data() { return { + sourceUpToDate: true, // Expanded indexes for selection table (Types) selectionExpandedIndexes: [], // Expanded indexes for mapped element table (Categories) @@ -247,7 +283,7 @@ export default { availableCategories: [], mappedEntityCount: 0, mappedEntities: [], - panel: [1], + panel: [2], selectionHeaders: [ { text: 'Type', sortable: false, value: 'entityType', width: '60%' }, { text: 'Count', sortable: false, align: 'center', value: 'count', width: '20%' }, @@ -354,6 +390,9 @@ export default { } }, methods:{ + refreshSourceBranch(){ + bus.$emit('refresh-source-branch') + }, clearInputs(){ this.enabledMethods = [] this.availableCategories = [] @@ -537,6 +576,10 @@ export default { bus.$on('mapped-entities-updated', async (mappedEntities) => { this.mappedEntityCount = mappedEntities.length }) + bus.$on('set-source-up-to-date', (isUpToDate) => { + this.sourceUpToDate = isUpToDate + console.log(this.sourceUpToDate) + }) } } diff --git a/ui/src/components/MapperSource.vue b/ui/src/components/MapperSource.vue new file mode 100644 index 0000000..08ba6dd --- /dev/null +++ b/ui/src/components/MapperSource.vue @@ -0,0 +1,167 @@ + + + + + \ No newline at end of file