Feat (Mapper): Fetch mapper source

This commit is contained in:
Oğuzhan Koral
2023-06-19 15:10:43 +03:00
committed by GitHub
12 changed files with 498 additions and 15 deletions
@@ -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
@@ -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
@@ -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
@@ -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'
@@ -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<SpeckleObjects::BuiltElements::Level>] levels in the source branch.
attr_reader :levels
# @return [ImmutableHash{String=>Array<SpeckleObjects::BuiltElements::Revit::RevitElementType>}] 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
@@ -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
@@ -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
@@ -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
+19 -12
View File
@@ -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
+3 -1
View File
@@ -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
+44 -1
View File
@@ -58,6 +58,34 @@
</v-expansion-panel-content>
</v-expansion-panel>
<v-expansion-panel key="source">
<v-expansion-panel-header class="flex">
<v-container class="ma-0 pa-0">
<v-icon>
mdi-source-branch
</v-icon>
{{ `Source` }}
<v-btn
v-if="!sourceUpToDate"
v-tooltip="'Source branch is not up-to-date!'"
class="ma-0"
height="20px"
icon
small
color="red"
@click="refreshSourceBranch"
>
<v-icon>
mdi-update
</v-icon>
</v-btn>
</v-container>
</v-expansion-panel-header>
<v-expansion-panel-content>
<mapper-source :source-up-to-date="this.sourceUpToDate"/>
</v-expansion-panel-content>
</v-expansion-panel>
<v-expansion-panel key="mapping">
<v-expansion-panel-header>
<div>
@@ -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)
})
}
}
</script>
+167
View File
@@ -0,0 +1,167 @@
<template>
<v-container class="pa-0">
<v-autocomplete
v-model="sourceStreamId"
label="Stream"
:items="allStreamsList"
item-text="name"
item-value="id"
density="compact"
></v-autocomplete>
<v-autocomplete
v-model="sourceBranchId"
class="pt-0 mb-n5"
label="Branch"
:items="allBranchesList"
:disabled="sourceStreamId === null"
item-text="name"
item-value="id"
density="compact"
@change="onSourceBranchChanged"
></v-autocomplete>
</v-container>
</template>
<script>
/*global sketchup*/
import gql from "graphql-tag";
import streamQuery from "@/graphql/stream.gql";
import {bus} from "@/main";
import ObjectLoader from "@speckle/objectloader";
const streamLimit = 20
export default {
name: "MappingSource",
props: {
streamSearchQuery: { type: String, default: null },
sourceUpToDate: { type: Boolean },
},
data() {
return {
sourceStreamId: null,
sourceBranchId: null,
sourceStreamName: null,
sourceBranchName: null
}
},
apollo: {
streams: {
prefetch: true,
debounce: 300,
fetchPolicy: 'cache-and-network',
query: gql`
query Streams($query: String, $limit: Int, $cursor: String) {
streams(query: $query, limit: $limit, cursor: $cursor) {
totalCount
cursor
items {
id
name
}
}
}
`,
variables() {
return {
query: this.streamSearchQuery,
limit: streamLimit,
cursor: null
}
},
update(data) {
bus.$emit('streams-loaded')
this.showMoreEnabled = data.streams?.items.length < data.streams.totalCount
return data.streams
},
},
$subscribe: {
commitCreated: {
query: gql`
subscription ($streamId: String!) {
commitCreated(streamId: $streamId)
}
`,
variables() {
return {
streamId: this.sourceStreamId
}
},
result() {
this.afterCommitCreated()
this.$eventHub.$emit('notification', {
text: `A new commit was created on source stream!`,
})
this.$apollo.queries.stream.refetch()
},
skip() {
// Return true to skip the initial query execution
return this.sourceStreamId === null;
},
}
},
stream: {
query: streamQuery,
prefetch: true,
variables() {
return {
id: this.sourceStreamId
}
},
skip() {
// Return true to skip the initial query execution
return this.sourceStreamId === null;
},
}
},
computed: {
selectedBranch() {
if (!this.stream) return
return this.stream.branches.items.find((branch) => branch.id === this.sourceBranchId)
},
allStreamsList() {
if (this.$apollo.loading) return
return this.streams?.items
},
allBranchesList() {
if (this.$apollo.loading) return
return this.stream?.branches.items
},
},
mounted() {
bus.$on('refresh-source-branch', () => {
this.onSourceBranchChanged()
bus.$emit('set-source-up-to-date', true)
})
},
methods: {
afterCommitCreated(){
bus.$emit('set-source-up-to-date', false)
},
async onSourceBranchChanged() {
const commitRefId = this.selectedBranch.commits.items[0]?.referencedObject
if (!commitRefId) { return }
const loader = new ObjectLoader({
serverUrl: localStorage.getItem('serverUrl'),
token: localStorage.getItem('SpeckleSketchup.AuthToken'),
streamId: this.sourceStreamId,
objectId: commitRefId
})
let rootObj = await loader.getAndConstructObject(this.updateLoadingStage)
sketchup.exec({name:"mapper_source_updated" , data: {
base: rootObj,
stream_id: this.sourceStreamId,
commit_id: commitRefId
}})
}
}
}
</script>
<style scoped>
</style>