Compare commits

...

95 Commits

Author SHA1 Message Date
Oğuzhan Koral 3c5d1f5c9e Fix (References): missing namespaces 2023-04-12 11:09:03 +03:00
oguzhankoral b69b8b7585 Fix missing references after couple of implementation 2023-04-12 10:44:15 +03:00
Oğuzhan Koral 8c7e498fb2 Fix (Security): Bump sqlite3 from 5.0.8 to 5.1.5 in /ui 2023-04-10 14:42:56 +03:00
dependabot[bot] 359413a296 Bump sqlite3 from 5.0.8 to 5.1.5 in /ui
Bumps [sqlite3](https://github.com/TryGhost/node-sqlite3) from 5.0.8 to 5.1.5.
- [Release notes](https://github.com/TryGhost/node-sqlite3/releases)
- [Commits](https://github.com/TryGhost/node-sqlite3/compare/v5.0.8...v5.1.5)

---
updated-dependencies:
- dependency-name: sqlite3
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-10 11:19:39 +00:00
Oğuzhan Koral cef7d894c4 Fix (Security): Bump git from 1.12.0 to 1.18.0 2023-04-10 14:15:16 +03:00
dependabot[bot] 7d9cc1aacc Bump git from 1.12.0 to 1.18.0
Bumps [git](https://github.com/ruby-git/ruby-git) from 1.12.0 to 1.18.0.
- [Release notes](https://github.com/ruby-git/ruby-git/releases)
- [Changelog](https://github.com/ruby-git/ruby-git/blob/master/CHANGELOG.md)
- [Commits](https://github.com/ruby-git/ruby-git/compare/v1.12.0...v1.18.0)

---
updated-dependencies:
- dependency-name: git
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-10 11:12:22 +00:00
Oğuzhan Koral a6780a756b Feat (Mapper): Mapper tool for direct shapes 2023-04-10 14:05:44 +03:00
Oğuzhan Koral 4e37d17b42 Feat (Mapper): Mapped elements table and it's buttons 2023-04-10 13:48:19 +03:00
oguzhankoral 793923348f Switch button for isolate/show-all accoding to state 2023-04-10 13:37:04 +03:00
oguzhankoral 700db1788d Improve performance dramatically of isolate 2023-04-08 00:02:52 +03:00
oguzhankoral 68a5a44702 Make functional buttons for mapped elements 2023-04-07 20:16:00 +03:00
oguzhankoral 464891f5a1 Manage state of selected elements and existing elements 2023-04-07 11:33:05 +03:00
oguzhankoral d2e5db2680 Custom data table header for child tables 2023-04-06 22:59:46 +03:00
oguzhankoral a9c41d0545 Connect data relationship between parent and childeren data tables 2023-04-05 17:19:21 +03:00
oguzhankoral 0bc458d307 Connect nested data tables with their v-model 2023-04-05 13:54:05 +03:00
oguzhankoral 36c7cc285e Update vuetifyjs to 2.6.10 2023-04-05 13:52:56 +03:00
oguzhankoral f9fbd31a0f Wrap data table with container 2023-04-03 16:43:19 +03:00
oguzhankoral 8789c1f855 Extract mapped elements from mapper component 2023-04-03 15:41:04 +03:00
oguzhankoral f65073480a Apply mixed mapping name 2023-04-03 15:04:20 +03:00
oguzhankoral 157ed831a7 Shrink v-cards for map selection between component-definition 2023-03-31 23:19:19 +03:00
oguzhankoral 0799a59a15 Get info from definition about number of instances 2023-03-31 23:18:23 +03:00
Oğuzhan Koral 60ea3e83e1 Fix (DirectShape): Nested objects material 2023-03-31 19:57:46 +03:00
oguzhankoral 4b874deb50 Don't add mapped entities to layer groups 2023-03-31 19:55:12 +03:00
oguzhankoral 6726b9ad50 Fix nested material issıe 2023-03-31 19:54:29 +03:00
Oğuzhan Koral 75e6fe609e Fix (Mapper): Direct shape objects on viewer 2023-03-31 15:28:05 +03:00
oguzhankoral b40d1ccf7d Send directShapes collection as detached 2023-03-31 15:26:13 +03:00
Oğuzhan Koral 4f7ba23904 Feat (Mapper): Enable multiple mapping apply and clear 2023-03-31 14:33:41 +03:00
oguzhankoral 49084706f2 Enable multiple mapping apply and clear 2023-03-31 14:31:49 +03:00
Oğuzhan Koral 1852e5f6bf Chore (Mapper): Show default values for selected entity 2023-03-30 20:33:59 +03:00
oguzhankoral be0ed5428a Apply default values from selected entity 2023-03-30 20:31:16 +03:00
oguzhankoral f428efde6a Mark mapped cards and make active one 2023-03-30 19:56:49 +03:00
oguzhankoral e79f04f33e Remove unnecessary statement 2023-03-30 15:59:33 +03:00
oguzhankoral 04db8c281f Remove selected rows which was unneccessary 2023-03-30 15:59:03 +03:00
oguzhankoral 2681698186 Disable definition option for groups
- This is uncessary since each group has it's own unique definition on sketchup
2023-03-30 15:49:04 +03:00
Oğuzhan Koral 1682c66da6 Feat (Mapper): Read mapped elements on document load 2023-03-29 23:20:29 +03:00
oguzhankoral 38bc1032d8 Read mapped entities on document load model 2023-03-29 23:18:05 +03:00
Oğuzhan Koral ba37eef9ca Feat (Mapper): Group meshes also for base geometries of direct shapes 2023-03-29 22:49:30 +03:00
oguzhankoral 3db179882a Group direct shape meshes by material 2023-03-29 22:45:45 +03:00
oguzhankoral ba8f25a807 Move mesh related methods from definition 2023-03-29 22:44:06 +03:00
oguzhankoral eac3d3089b Find entities on active path 2023-03-29 22:43:13 +03:00
Oğuzhan Koral 0454a9e147 Fix (Mapper): Group global transformations for direct shapes 2023-03-29 21:03:13 +03:00
oguzhankoral f390cd1c68 Fix/disable rubocop issues 2023-03-29 21:02:49 +03:00
oguzhankoral aaa1ba5aa9 Apply parent's material to direct shape faces 2023-03-29 20:58:57 +03:00
oguzhankoral fb9556abd9 Fix sub elements transform 2023-03-29 20:58:35 +03:00
Oğuzhan Koral 3cfb3b1f84 Feat (Mapper): Show mapped elements on table 2023-03-29 14:54:21 +03:00
oguzhankoral b071bab137 Collect mapped entities when mapper mounted 2023-03-29 14:49:48 +03:00
oguzhankoral 5dbc68ea76 State management for mapped elements 2023-03-29 14:45:09 +03:00
oguzhankoral e76fbc3b77 Notify user after apply or clear mapping 2023-03-29 13:12:59 +03:00
oguzhankoral 09ca0514d1 Remove mapping word from buttons 2023-03-29 12:57:12 +03:00
oguzhankoral faec32a5bb Remove attribute dictionary completely if mappings cleared 2023-03-29 12:35:56 +03:00
oguzhankoral 1f5793ab79 Nil check for reading objects from scratch 2023-03-29 11:50:03 +03:00
oguzhankoral df22bd1cec Fallback to definition schema of instance 2023-03-29 11:47:21 +03:00
oguzhankoral 61dba4e78d Return false if entities are missing for base_and_entities 2023-03-29 11:44:58 +03:00
Oğuzhan Koral bb678117f8 Feat (Mapper): UI communication with Sketchup 2023-03-28 21:36:12 +03:00
oguzhankoral 2dc08e71b7 Collect also instances that definitions' mapped 2023-03-28 21:02:21 +03:00
oguzhankoral df2f844e74 Styles for entity selection to map 2023-03-28 20:11:52 +03:00
oguzhankoral 621c602fc0 Make narrower scrool bar 2023-03-28 20:11:15 +03:00
oguzhankoral f809169757 Fetch definition schema from selection 2023-03-28 20:10:59 +03:00
oguzhankoral 848b135612 Clear mappings 2023-03-28 20:10:26 +03:00
oguzhankoral db6af66705 Fill inputs according to selection 2023-03-28 02:34:55 +03:00
oguzhankoral 728e1f5a86 Send categories as key-value pair objects 2023-03-28 02:34:38 +03:00
oguzhankoral d02d95bc9e Apply mapping command and action 2023-03-28 02:34:13 +03:00
oguzhankoral c1180c5373 Trigger sketchup action to apply mappings 2023-03-28 01:49:17 +03:00
oguzhankoral 4e01fb64e6 Track objects from selection table and prepare mapping 2023-03-28 01:46:21 +03:00
oguzhankoral d0fcb1da34 Add theme colors for mapper active entity cards 2023-03-28 01:45:44 +03:00
oguzhankoral 48352d06b3 Extend selected entity data from sketchup 2023-03-28 01:45:18 +03:00
oguzhankoral d995652569 Get info from last selected entity 2023-03-27 18:57:42 +03:00
oguzhankoral 5a958fa51c Split class names with space 2023-03-27 18:57:13 +03:00
oguzhankoral cfb9e2cacd Print mapped count to selection table 2023-03-27 18:12:08 +03:00
oguzhankoral 7932fc1cab Send entity persistent_id to UI 2023-03-27 18:11:40 +03:00
oguzhankoral e0fc1715b5 Create data table for selection 2023-03-27 14:54:19 +03:00
oguzhankoral 1cfd6caa64 Read object schemas from selection with their type 2023-03-27 14:54:01 +03:00
oguzhankoral 54bf18888d Helper method definition to group objects by property value 2023-03-27 14:53:23 +03:00
Oğuzhan Koral 6b60fc4259 Feat (Observers): Selection observers to collect data 2023-03-24 19:37:20 +03:00
oguzhankoral 216af8697c Create accordion for mapper 2023-03-24 17:33:42 +03:00
oguzhankoral d97e7314d0 Add categories to fake data 2023-03-24 17:33:11 +03:00
oguzhankoral 67d9dda143 Sample interactions 2023-03-24 13:06:55 +03:00
oguzhankoral 5c60e303cb Move mapped_with_schema method to reader 2023-03-24 12:36:45 +03:00
oguzhankoral e36b8f3a56 Create selection observer to trigger UI 2023-03-24 12:36:21 +03:00
Oğuzhan Koral 1e7a19b463 Chore (UI): UI components for mapping tool 2023-03-24 12:30:55 +03:00
oguzhankoral 8004249a29 Init mapper with tab 2023-03-23 16:08:36 +03:00
Oğuzhan Koral 6cd800e4ef Feat (Mapping): Send direct shape PoC 2023-03-23 12:54:42 +03:00
oguzhankoral e182bc7ed2 Clean unused methods 2023-03-22 21:38:23 +03:00
oguzhankoral c338d51c9d Skip faces into definitions if it is mapped 2023-03-22 21:19:11 +03:00
oguzhankoral c1912250df Do definition check in advance and that's why definitions argument removed 2023-03-22 21:18:50 +03:00
oguzhankoral 994c41980e Init definitions and render_materials for later caching 2023-03-22 21:18:03 +03:00
oguzhankoral 385b7a514d Transform mesh if global_transformation defined 2023-03-22 21:17:25 +03:00
oguzhankoral 3aaade9228 Entity queries for parental relationships 2023-03-22 21:16:58 +03:00
oguzhankoral b4dd19b711 Split direct shape conversion from generic conversion 2023-03-22 21:06:53 +03:00
oguzhankoral 9e9b83b4ba Init direct shape definition 2023-03-22 21:06:14 +03:00
oguzhankoral 5ff5114669 Add revit categories dictionary 2023-03-21 19:55:44 +03:00
oguzhankoral f63039f3fb Change module name with revit specific 2023-03-21 19:53:47 +03:00
oguzhankoral b39399463d Add speckle schema to objects for to speckle 2023-03-21 14:17:36 +03:00
oguzhankoral 8217ef7fbb Create speckle schema dictionary handler 2023-03-21 14:17:18 +03:00
oguzhankoral b4851c34e1 Separate base dictionary_handler from root 2023-03-21 14:16:52 +03:00
54 changed files with 2238 additions and 251 deletions
+3 -4
View File
@@ -1,7 +1,7 @@
GEM
remote: https://rubygems.org/
specs:
addressable (2.8.1)
addressable (2.8.4)
public_suffix (>= 2.0.2, < 6.0)
ast (2.4.2)
axiom-types (0.1.1)
@@ -26,7 +26,7 @@ GEM
path_expander (~> 1.0)
ruby_parser (~> 3.1, > 3.1.0)
sexp_processor (~> 4.8)
git (1.12.0)
git (1.18.0)
addressable (~> 2.8)
rchardet (~> 1.8)
ice_nine (0.11.2)
@@ -48,8 +48,7 @@ GEM
pry (0.14.1)
coderay (~> 1.1)
method_source (~> 1.0)
psych (3.3.4)
public_suffix (5.0.0)
public_suffix (5.0.1)
rainbow (3.1.1)
rake (13.0.6)
rchardet (1.8.0)
@@ -0,0 +1,58 @@
# frozen_string_literal: true
require_relative 'action'
require_relative 'mapped_entities_updated'
require_relative 'events/selection_event_action'
require_relative '../sketchup_model/dictionary/speckle_schema_dictionary_handler'
module SpeckleConnector
module Actions
# Apply mappings for selected entities.
class ApplyMappings < Action
def initialize(entities_to_map, method, category, name, is_definition)
super()
@entities_to_map = entities_to_map
@method = method
@category = category
@name = name
@is_definition = is_definition
end
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def update_state(state)
sketchup_model = state.sketchup_state.sketchup_model
entities = if sketchup_model.active_path.nil?
sketchup_model.entities
else
sketchup_model.active_path.last.definition.entities
end
# Collect entities from entity ids that comes from UI as list
entities_to_map = entities.select { |e| @entities_to_map.include?(e.persistent_id) }
# Switch to definitions if all entities are component instance and UI flag shows that
if entities_to_map.all? { |e| e.is_a?(Sketchup::ComponentInstance) } && @is_definition
entities_to_map = entities_to_map.collect(&:definition).uniq
end
# Store speckle state to update with mapped entities.
speckle_state = state.speckle_state
entities_to_map.each do |entity|
name = if @name == '<Mixed>'
entity.respond_to?(:name) ? entity.name : ''
else
@name
end
SketchupModel::Dictionary::SpeckleSchemaDictionaryHandler.set_attribute(entity, :category, @category)
SketchupModel::Dictionary::SpeckleSchemaDictionaryHandler.set_attribute(entity, :name, name)
SketchupModel::Dictionary::SpeckleSchemaDictionaryHandler.set_attribute(entity, :method, @method)
speckle_state = speckle_state.with_mapped_entity(entity)
end
new_state = MappedEntitiesUpdated.update_state(state.with_speckle_state(speckle_state))
Events::SelectionEventAction.update_state(new_state, { apply: true })
end
end
end
end
@@ -0,0 +1,48 @@
# frozen_string_literal: true
require_relative 'action'
require_relative 'mapped_entities_updated'
require_relative 'events/selection_event_action'
require_relative '../sketchup_model/dictionary/speckle_schema_dictionary_handler'
module SpeckleConnector
module Actions
# Clear mappings for selected entities.
class ClearMappings < Action
def initialize(entities_to_map, is_definition)
super()
@entities_to_map = entities_to_map
@is_definition = is_definition
end
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def update_state(state)
sketchup_model = state.sketchup_state.sketchup_model
entities = if sketchup_model.active_path.nil?
sketchup_model.entities
else
sketchup_model.active_path.last.definition.entities
end
# Collect entities from entity ids that comes from UI as list
entities_to_map = entities.select { |e| @entities_to_map.include?(e.persistent_id) }
# Switch to definitions if all entities are component instance and UI flag shows that
if entities_to_map.all? { |e| e.is_a?(Sketchup::ComponentInstance) } && @is_definition
entities_to_map = entities_to_map.collect(&:definition).uniq
end
# Store speckle state to update with mapped entities.
speckle_state = state.speckle_state
entities_to_map.each do |entity|
SketchupModel::Dictionary::SpeckleSchemaDictionaryHandler.remove_dictionary(entity)
speckle_state = speckle_state.with_removed_mapped_entity(entity)
end
new_state = MappedEntitiesUpdated.update_state(state.with_speckle_state(speckle_state))
Events::SelectionEventAction.update_state(new_state, { clear: true })
end
end
end
end
@@ -0,0 +1,34 @@
# frozen_string_literal: true
require_relative 'action'
require_relative 'mapped_entities_updated'
require_relative 'events/selection_event_action'
require_relative '../sketchup_model/dictionary/speckle_schema_dictionary_handler'
module SpeckleConnector
module Actions
# Clear mappings for selected entities from mapped elements table.
class ClearMappingsFromTable < Action
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def self.update_state(state, data)
# Flat entities to clear mappings
flat_entities = SketchupModel::Query::Entity.flat_entities(state.sketchup_state.sketchup_model.entities)
# Collect entity ids to clear mappings
entity_ids = data.collect { |_, entities| entities['selectedElements'].collect { |e| e['entityId'] } }.flatten
# Store speckle state to update with mapped entities.
speckle_state = state.speckle_state
flat_entities.each do |entity|
next unless entity_ids.include?(entity.persistent_id)
SketchupModel::Dictionary::SpeckleSchemaDictionaryHandler.remove_dictionary(entity)
speckle_state = speckle_state.with_removed_mapped_entity(entity)
end
new_state = MappedEntitiesUpdated.update_state(state.with_speckle_state(speckle_state))
Events::SelectionEventAction.update_state(new_state, { clear: true })
end
end
end
end
@@ -0,0 +1,33 @@
# frozen_string_literal: true
require_relative 'event_action'
require_relative '../../mapping/category/revit_category'
require_relative '../../sketchup_model/reader/speckle_entities_reader'
require_relative '../../sketchup_model/query/entity'
module SpeckleConnector
module Actions
module Events
# Update selected speckle objects when the selection changes for mapping tool.
class SelectionEventAction < EventAction
# @param state [States::State] the current state of Speckle application.
# @return [States::State] the new updated state object
def self.update_state(state, event_data)
return state unless event_data&.any?
sketchup_selection = state.sketchup_state.sketchup_model.selection
selection = {
selection: SketchupModel::Reader::SpeckleEntitiesReader.entities_schema_details(sketchup_selection),
mappingMethods: [
'Direct Shape'
],
categories: Mapping::Category::RevitCategory.to_a
}
selection = { selection: [], mappingMethods: [], categories: [] } if sketchup_selection.none?
state.with_selection_queue(selection)
end
end
end
end
end
@@ -0,0 +1,31 @@
# frozen_string_literal: true
require_relative 'action'
require_relative 'events/selection_event_action'
require_relative '../sketchup_model/dictionary/speckle_schema_dictionary_handler'
module SpeckleConnector
module Actions
# Hide entities that selected from mapped elements table.
class HideMappingsFromTable < Action
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def self.update_state(state, data)
# Flat entities to clear mappings
flat_entities = SketchupModel::Query::Entity.flat_entities(state.sketchup_state.sketchup_model.entities)
# Collect entity ids to clear mappings
entity_ids = data.collect { |_, entities| entities['selectedElements'].collect { |e| e['entityId'] } }.flatten
# Store speckle state to update with mapped entities.
flat_entities.each do |entity|
next unless entity_ids.include?(entity.persistent_id)
entity.hidden = true
end
Events::SelectionEventAction.update_state(state, { clear: true })
end
end
end
end
@@ -23,6 +23,7 @@ module SpeckleConnector
preferences = Preferences.read_preferences(sketchup_state.sketchup_model)
user_state_with_preferences = state.user_state.with_preferences(preferences)
state = States::State.new(user_state_with_preferences, speckle_state, sketchup_state, false)
# This is where we attach observers to related model objects like selection, entities..
Actions::LoadSketchupModel.update_state(state, sketchup_state.sketchup_model)
end
@@ -0,0 +1,50 @@
# frozen_string_literal: true
require_relative 'action'
require_relative 'events/selection_event_action'
require_relative '../sketchup_model/dictionary/speckle_schema_dictionary_handler'
module SpeckleConnector
module Actions
# Isolate entities that selected from mapped elements table.
class IsolateMappingsFromTable < Action
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def self.update_state(state, data)
sketchup_model = state.sketchup_state.sketchup_model
# Hide all entities first
sketchup_model.entities.each do |ent|
ent.hidden = true
end
# 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)
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'
end
faces_or_edges_ids = faces_or_edges.collect { |e| e['entityId'] }
face_edge_flat_entities.select { |e| faces_or_edges_ids.include?(e.persistent_id) }.each do |entity|
entity.hidden = false
end
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|
entity.hidden = false
end
Events::SelectionEventAction.update_state(state, { clear: true })
end
end
end
end
@@ -28,6 +28,9 @@ module SpeckleConnector
# 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))
# Read mapped entities
new_mapped_entities = SketchupModel::Reader::SpeckleEntitiesReader.read_mapped_entities(sketchup_model.entities)
new_speckle_state = new_speckle_state.with_mapped_entities(Immutable::Hash.new(new_mapped_entities))
new_state = new_state.with_speckle_state(new_speckle_state)
# Read preferences from database and model.
@@ -42,8 +45,8 @@ module SpeckleConnector
# @param sketchup_model [Sketchup::Model] the model to attach observers to
# @param observers [Hash{Class=>}] the observer objects indexed by their class that will be attached
def self.attach_observers(sketchup_model, observers)
# selection = sketchup_model.selection
# selection.add_observer(observers[SELECTION_OBSERVER_NAME])
selection = sketchup_model.selection
selection.add_observer(observers[SELECTION_OBSERVER])
# layers = sketchup_model.layers
# layers.add_observer(observers[LAYERS_OBSERVER_NAME])
entities = sketchup_model.entities
@@ -0,0 +1,20 @@
# frozen_string_literal: true
require_relative 'action'
require_relative '../sketchup_model/reader/speckle_entities_reader'
module SpeckleConnector
module Actions
# Triggers when mapped entities updated.
class MappedEntitiesUpdated < Action
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @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)
state.with_mapped_entities_queue(mapped_entities)
end
end
end
end
@@ -4,6 +4,7 @@ require_relative 'action'
require_relative 'events/app_event_action'
require_relative 'events/entities_event_action'
require_relative 'events/model_event_action'
require_relative 'events/selection_event_action'
require_relative '../constants/observer_constants'
module SpeckleConnector
@@ -13,11 +14,11 @@ module SpeckleConnector
RUN_ORDER = {
APP_OBSERVER => Events::AppEventAction,
ENTITIES_OBSERVER => Events::EntitiesEventAction,
MODEL_OBSERVER => Events::ModelEventAction
MODEL_OBSERVER => Events::ModelEventAction,
# MATERIALS_OBSERVER => Events::MaterialsEventAction,
# LAYERS_OBSERVER => Events::LayerEventAction,
# PAGES_OBSERVER => Events::PagesEventAction,
# SELECTION_OBSERVER => Events::SelectionEventAction
SELECTION_OBSERVER => Events::SelectionEventAction
}.freeze
def self.update_state(state, events)
@@ -0,0 +1,34 @@
# frozen_string_literal: true
require_relative 'action'
require_relative 'events/selection_event_action'
require_relative '../sketchup_model/dictionary/speckle_schema_dictionary_handler'
module SpeckleConnector
module Actions
# Select entities that selected from mapped elements table.
class SelectMappingsFromTable < Action
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def self.update_state(state, data)
# Clear first selection
state.sketchup_state.sketchup_model.selection.clear
# Flat entities to clear mappings
flat_entities = SketchupModel::Query::Entity.flat_entities(state.sketchup_state.sketchup_model.entities)
# Collect entity ids to clear mappings
entity_ids = data.collect { |_, entities| entities['selectedElements'].collect { |e| e['entityId'] } }.flatten
# Store speckle state to update with mapped entities.
flat_entities.each do |entity|
next unless entity_ids.include?(entity.persistent_id)
state.sketchup_state.sketchup_model.selection.add(entity)
end
Events::SelectionEventAction.update_state(state, { clear: true })
end
end
end
end
@@ -0,0 +1,20 @@
# frozen_string_literal: true
require_relative 'action'
module SpeckleConnector
module Actions
# Show all entities on the model.
class ShowAllEntities < Action
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def self.update_state(state, _data)
# Show all entities first
state.sketchup_state.sketchup_model.entities.each do |ent|
ent.hidden = false
end
state
end
end
end
end
@@ -0,0 +1,21 @@
# frozen_string_literal: true
require_relative 'command'
require_relative '../actions/apply_mappings'
module SpeckleConnector
module Commands
# Command to apply mapping for selected entities.
class ApplyMappings < Command
def _run(data)
entities_to_map = data['entitiesToMap']
method = data['method']
category = data['category']
name = data['name']
is_definition = data['isDefinition']
action = Actions::ApplyMappings.new(entities_to_map, method, category, name, is_definition)
app.update_state!(action)
end
end
end
end
@@ -0,0 +1,18 @@
# frozen_string_literal: true
require_relative 'command'
require_relative '../actions/clear_mappings'
module SpeckleConnector
module Commands
# Command to clear mapping for selected entities.
class ClearMappings < Command
def _run(data)
entities_to_map = data['entitiesToClearMap']
is_definition = data['isDefinition']
action = Actions::ClearMappings.new(entities_to_map, is_definition)
app.update_state!(action)
end
end
end
end
@@ -2,6 +2,9 @@
module SpeckleConnector
SPECKLE_BASE_OBJECT = 'Speckle_Base_Object'
SPECKLE_MAPPING_TOOL_SCHEMA = 'Speckle_Mapping_Tool_Schema'
SPECKLE_SCHEMA = 'Speckle_Schema'
SPECKLE_ID = 'speckle_id'
SPECKLE_TYPE = 'speckle_type'
APPLICATION_ID = 'application_id'
@@ -4,4 +4,5 @@ module SpeckleConnector
APP_OBSERVER = 'SpeckleConnector::Observers::AppObserver'
ENTITIES_OBSERVER = 'SpeckleConnector::Observers::EntitiesObserver'
MODEL_OBSERVER = 'SpeckleConnector::Observers::ModelObserver'
SELECTION_OBSERVER = 'SpeckleConnector::Observers::SelectionObserver'
end
@@ -11,6 +11,13 @@ module SpeckleConnector
INCLUDE_COMPONENT_ENTITY_ATTRIBUTES = :include_component_entity_attributes
MERGE_COPLANAR_FACES = :merge_coplanar_faces
ENTITY_KEYS_FOR_INCLUDING_ATTRIBUTES = {
Sketchup::ComponentInstance => INCLUDE_COMPONENT_ENTITY_ATTRIBUTES,
Sketchup::Group => INCLUDE_GROUP_ENTITY_ATTRIBUTES,
Sketchup::Face => INCLUDE_FACE_ENTITY_ATTRIBUTES,
Sketchup::Face => INCLUDE_EDGE_ENTITY_ATTRIBUTES
}.freeze
LEVEL_SHIFT_VALUE = SpeckleObjects::Geometry.length_to_native(1.5, 'm')
DEFAULT_MODEL_PREFERENCES = {
@@ -4,6 +4,7 @@ module SpeckleConnector
BASE_OBJECT = 'Base'
OBJECTS_BUILTELEMENTS_VIEW3D = 'Objects.BuiltElements.View:Objects.BuiltElements.View3D'
OBJECTS_BUILTELEMENTS_REVIT_DIRECTSHAPE = 'Objects.BuiltElements.Revit.DirectShape'
OBJECTS_GEOMETRY_LINE = 'Objects.Geometry.Line'
OBJECTS_GEOMETRY_POLYLINE = 'Objects.Geometry.Polyline'
@@ -228,6 +228,8 @@ module SpeckleConnector
return false unless is_array && value.length == 2
return false if value[1].nil?
value[1].all? { |v| v.is_a?(Sketchup::Entity) }
end
@@ -7,7 +7,7 @@ require_relative '../speckle_objects/other/render_material'
require_relative '../speckle_objects/other/block_definition'
require_relative '../speckle_objects/other/block_instance'
require_relative '../speckle_objects/other/display_value'
require_relative '../speckle_objects/other/revit/revit_instance'
require_relative '../speckle_objects/revit/revit_instance'
require_relative '../speckle_objects/geometry/point'
require_relative '../speckle_objects/geometry/line'
require_relative '../speckle_objects/geometry/mesh'
@@ -34,6 +34,7 @@ module SpeckleConnector
# Module aliases
GEOMETRY = SpeckleObjects::Geometry
OTHER = SpeckleObjects::Other
REVIT = SpeckleObjects::Revit
# Class aliases
POINT = GEOMETRY::Point
@@ -41,7 +42,7 @@ module SpeckleConnector
MESH = GEOMETRY::Mesh
BLOCK_DEFINITION = OTHER::BlockDefinition
BLOCK_INSTANCE = OTHER::BlockInstance
REVIT_INSTANCE = OTHER::Revit::RevitInstance
REVIT_INSTANCE = REVIT::Other::RevitInstance
RENDER_MATERIAL = OTHER::RenderMaterial
DISPLAY_VALUE = OTHER::DisplayValue
+50 -4
View File
@@ -10,7 +10,10 @@ require_relative '../speckle_objects/other/block_instance'
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 '../constants/path_constants'
require_relative '../sketchup_model/reader/speckle_entities_reader'
require_relative '../sketchup_model/query/entity'
module SpeckleConnector
module Converters
@@ -25,7 +28,10 @@ module SpeckleConnector
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)
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? }
@@ -33,6 +39,27 @@ module SpeckleConnector
return state, SpeckleObjects::Base.with_detached_layers(base_object_properties)
end
# 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)
@@ -93,17 +120,32 @@ module SpeckleConnector
# @param entity [Sketchup::Entity] sketchup entity to convert Speckle.
# @param speckle_state [States::SpeckleState] the current speckle state of the {States::State}
# @param parent [Symbol, String] parent of the Sketchup Entity to be converted.
# rubocop:disable Metrics/MethodLength
def convert(entity, preferences, speckle_state, parent = :base)
convert = method(:convert)
unless SketchupModel::Reader::SpeckleEntitiesReader.mapped_with_schema?(entity)
return from_native_to_speckle(entity, preferences, speckle_state, parent, &convert)
end
return speckle_state, nil
end
def from_mapped_to_speckle(entity, path, preferences)
direct_shape = SpeckleObjects::BuiltElements::Revit::DirectShape
.from_entity(entity, path, @units, preferences)
return [direct_shape, [entity]]
end
# rubocop:disable Metrics/MethodLength
def from_native_to_speckle(entity, preferences, speckle_state, parent, &convert)
if entity.is_a?(Sketchup::Edge)
line = SpeckleObjects::Geometry::Line.from_edge(entity, @units, preferences[:model]).to_h
return speckle_state, [line, [entity]]
end
if entity.is_a?(Sketchup::Face)
mesh = SpeckleObjects::Geometry::Mesh.from_face(entity, @units, preferences[:model])
mesh = SpeckleObjects::Geometry::Mesh.from_face(face: entity, units: @units,
model_preferences: preferences[:model])
return speckle_state, [mesh, [entity]]
end
@@ -124,9 +166,13 @@ module SpeckleConnector
end
if entity.is_a?(Sketchup::ComponentDefinition)
# Local caching
return speckle_state, [definitions[entity.guid], [entity]] if definitions.key?(entity.guid)
new_speckle_state, block_definition = SpeckleObjects::Other::BlockDefinition.from_definition(
entity, @units, @definitions, preferences, speckle_state, parent, &convert
entity, @units, preferences, speckle_state, parent, &convert
)
definitions[entity.guid] = block_definition
speckle_state = new_speckle_state
return speckle_state, [block_definition, [entity]]
end
@@ -0,0 +1,141 @@
# frozen_string_literal: true
module SpeckleConnector
module Mapping
module Category
# Revit categories.
class RevitCategory < Hash
class << self
# rubocop:disable Metrics/MethodLength
def dictionary
{
AbutmentFoundations: 0,
AbutmentPiles: 1,
AbutmentWalls: 2,
BridgeAbutments: 3,
DuctTerminal: 4,
Alignments: 5,
StructConnectionAnchors: 6,
ApproachSlabs: 7,
BridgeArches: 8,
AudioVisualDevices: 9,
StairsRailingBaluster: 10,
BridgeBearings: 11,
StructConnectionBolts: 12,
BridgeCables: 13,
BridgeDecks: 14,
BridgeFraming: 15,
CableTrayFitting: 16,
CableTrayRun: 17,
CableTray: 18,
Casework: 19,
Ceilings: 20,
Columns: 21,
CommunicationDevices: 22,
ConduitFitting: 23,
Conduit: 24,
Coordination_Model: 25,
BridgeFramingCrossBracing: 26,
CurtainWallPanels: 27,
CurtaSystem: 28,
CurtainWallMullions: 29,
DataDevices: 30,
BridgeFramingDiaphragms: 31,
Doors: 32,
DuctAccessory: 33,
DuctFitting: 34,
PlaceHolderDucts: 35,
DuctSystem: 36,
DuctCurves: 37,
ElectricalEquipment: 38,
ElectricalFixtures: 39,
Entourage: 40,
ExpansionJoints: 41,
FireAlarmDevices: 42,
FireProtection: 43,
Floors: 44,
FoodServiceEquipment: 45,
Furniture: 46,
FurnitureSystems: 47,
GenericAnnotation: 48,
GenericModel: 49,
BridgeGirders: 50,
Hardscape: 51,
LightingDevices: 52,
LightingFixtures: 53,
Lines: 54,
Mass: 55,
MechanicalEquipment: 56,
MedicalEquipment: 57,
NurseCallDevices: 58,
Parking: 59,
Parts: 60,
PierCaps: 61,
PierColumns: 62,
BridgeFoundations: 63,
PierPiles: 64,
BridgeTowers: 65,
PierWalls: 66,
BridgePiers: 67,
PipeAccessory: 68,
PipeFitting: 69,
PlaceHolderPipes: 70,
PipeSegments: 71,
PipeCurves: 72,
PipingSystem: 73,
Planting: 74,
StructConnectionPlates: 75,
PlumbingFixtures: 76,
StructConnectionProfiles: 77,
StairsRailing: 78,
Ramps: 79,
Roads: 80,
Roofs: 81,
SecurityDevices: 82,
StructConnectionShearStuds: 83,
Signage: 84,
Site: 85,
SpecialityEquipment: 86,
Sprinklers: 87,
Stairs: 88,
StructuralFramingSystem: 89,
StructuralColumns: 90,
StructConnections: 91,
FabricAreas: 92,
StructuralFoundation: 93,
StructuralFraming: 94,
Rebar: 95,
Coupler: 96,
StructuralStiffener: 97,
StructuralTendons: 98,
StructuralTruss: 99,
TemporaryStructure: 100,
Topography: 101,
BridgeFramingTrusses: 102,
VerticalCirculation: 103,
VibrationDampers: 104,
VibrationIsolators: 105,
VibrationManagement: 106,
Walls: 107,
StructConnectionWelds: 108,
Windows: 109
}.freeze
end
# rubocop:enable Metrics/MethodLength
def reverse_dictionary
dictionary.collect { |k, v| [v, k] }.to_h
end
def to_a
dictionary.collect { |k, v| { key: k, value: v } }.to_a
end
def reverse_to_a
dictionary.collect { |k, v| { key: v, value: k } }.to_a
end
end
end
end
end
end
+3 -1
View File
@@ -5,6 +5,7 @@ require_relative 'entities_observer'
require_relative 'observer_handler'
require_relative 'model_observer'
require_relative 'event_handler'
require_relative 'selection_observer'
require_relative '../constants/observer_constants'
module SpeckleConnector
@@ -22,7 +23,8 @@ module SpeckleConnector
{
APP_OBSERVER => AppObserver.new(handler),
ENTITIES_OBSERVER => EntitiesObserver.new(handler),
MODEL_OBSERVER => ModelObserver.new(handler)
MODEL_OBSERVER => ModelObserver.new(handler),
SELECTION_OBSERVER => SelectionObserver.new(handler)
}.freeze
end
end
@@ -0,0 +1,49 @@
# frozen_string_literal: true
require_relative 'event_observer'
module SpeckleConnector
module Observers
# @see https://ruby.sketchup.com/Sketchup/SelectionObserver.html
class SelectionObserver < Sketchup::SelectionObserver
include EventObserver
# rubocop:disable Naming/MethodName
# @param _selection (Sketchup::Selection)
# @param _entity (Sketchup::Entity)
def onSelectionAdded(_selection, _entity)
push_selection_event(:onSelectionAdded)
end
# @param _selection (Sketchup::Selection)
def onSelectionBulkChange(_selection)
push_selection_event(:onSelectionBulkChange)
end
# @param _selection (Sketchup::Selection)
def onSelectionCleared(_selection)
push_selection_event(:onSelectionCleared)
end
# @param _selection (Sketchup::Selection)
def onSelectionRemoved(_selection, _entity)
push_selection_event(:onSelectionRemoved)
end
# Due to a SketchUp bug, this method is called by the wrong name.
alias onSelectedRemoved onSelectionRemoved
# rubocop:enable Naming/MethodName
private
# Selection changes need to be registered only once
def push_selection_event(event_name)
# Don't push anything if the selection event was already registered
selection_events = observer_handler.events[self.class]
return if selection_events&.any?
push_event(event_name)
end
end
end
end
@@ -0,0 +1,89 @@
# frozen_string_literal: true
require 'delegate'
require_relative 'dictionary_handler'
require_relative '../../constants/dict_constants'
module SpeckleConnector
module SketchupModel
module Dictionary
# Read and write attributes for Speckle objects on SketchUp model.
class BaseDictionaryHandler < DictionaryHandler
IGNORED_DICTIONARY_NAMES = [
SPECKLE_BASE_OBJECT,
'IFC 4',
'IFC 2x3'
].freeze
# @param entity [Sketchup::Entity] entity to get attribute dictionaries
def self.attribute_dictionaries_to_speckle(entity, model_preferences)
dictionaries = {}
return dictionaries unless model_preferences[INCLUDE_ENTITY_ATTRIBUTES]
klass = get_entity_setting_type(entity)
return dictionaries unless model_preferences[ENTITY_KEYS_FOR_INCLUDING_ATTRIBUTES[klass]]
return dictionaries if entity.attribute_dictionaries.nil?
entity.attribute_dictionaries.each do |att_dict|
dict_name = att_dict == '' ? 'empty_dictionary_name' : att_dict.name
dictionaries[dict_name] = att_dict.to_h unless IGNORED_DICTIONARY_NAMES.include?(att_dict.name)
end
dictionaries
end
# @param entity [Sketchup::Entity] entity to set attribute dictionaries
# rubocop:disable Metrics/CyclomaticComplexity
def self.attribute_dictionaries_to_native(entity, dictionaries)
return if dictionaries.nil?
classification_to_native(entity, dictionaries) if entity.is_a?(Sketchup::ComponentDefinition)
dictionaries.each do |dict_name, entries|
next unless entries.is_a?(Hash)
dict_name = dict_name == 'empty_dictionary_name' ? '' : dict_name
entries.each do |key, value|
set_attribute(entity, key, value, dict_name)
rescue StandardError => e
puts("Failed to write key: #{key} value: #{value} to dictionary #{dict_name}")
puts(e)
end
end
end
# rubocop:enable Metrics/CyclomaticComplexity
# Classification is ComponentDefinition specific, so they can be added only definition by add_classification
# method.
# @param definition_entity [Sketchup::ComponentDefinition] definition to add callback
def self.classification_to_native(definition_entity, dictionaries)
applied_schema_types = dictionaries['AppliedSchemaTypes']
return if applied_schema_types.nil?
applied_schema_types.each do |key, value|
definition_entity.add_classification(key, value)
end
end
# @return [String] the name of the dictionary to read from
def self.dictionary_name
SPECKLE_BASE_OBJECT
end
# Gets entity type for including entity attributes setting.
# @param entity [Sketchup::Entity] entity to find setting entity.
# @return [Sketchup::Face, Sketchup::Edge, Sketchup::Group, Sketchup::ComponentInstance]
def self.get_entity_setting_type(entity)
klass = entity.class
if entity.is_a?(Sketchup::ComponentDefinition)
klass = if entity.group?
Sketchup::Group
else
Sketchup::ComponentInstance
end
end
klass
end
end
end
end
end
@@ -1,65 +1,13 @@
# frozen_string_literal: true
require 'delegate'
require_relative '../../constants/dict_constants'
module SpeckleConnector
module SketchupModel
module Dictionary
# Read and write attributes from the groups and other entities that represents Speckle objects on SketchUp model.
class DictionaryHandler
DICTIONARY_NAME = 'Speckle_Base_Object'
IGNORED_DICTIONARY_NAMES = [
DICTIONARY_NAME,
'IFC 4',
'IFC 2x3'
].freeze
# @param entity [Sketchup::Entity] entity to get attribute dictionaries
def self.attribute_dictionaries_to_speckle(entity)
dictionaries = {}
return dictionaries if entity.attribute_dictionaries.nil?
entity.attribute_dictionaries.each do |att_dict|
dict_name = att_dict == '' ? 'empty_dictionary_name' : att_dict.name
dictionaries[dict_name] = att_dict.to_h unless IGNORED_DICTIONARY_NAMES.include?(att_dict.name)
end
dictionaries
end
# @param entity [Sketchup::Entity] entity to set attribute dictionaries
# rubocop:disable Metrics/CyclomaticComplexity
def self.attribute_dictionaries_to_native(entity, dictionaries)
return if dictionaries.nil?
classification_to_native(entity, dictionaries) if entity.is_a?(Sketchup::ComponentDefinition)
dictionaries.each do |dict_name, entries|
next unless entries.is_a?(Hash)
dict_name = dict_name == 'empty_dictionary_name' ? '' : dict_name
entries.each do |key, value|
set_attribute(entity, key, value, dict_name)
rescue StandardError => e
puts("Failed to write key: #{key} value: #{value} to dictionary #{dict_name}")
puts(e)
end
end
end
# rubocop:enable Metrics/CyclomaticComplexity
# Classification is ComponentDefinition specific, so they can be added only definition by add_classification
# method.
# @param definition_entity [Sketchup::ComponentDefinition] definition to add callback
def self.classification_to_native(definition_entity, dictionaries)
applied_schema_types = dictionaries['AppliedSchemaTypes']
return if applied_schema_types.nil?
applied_schema_types.each do |key, value|
definition_entity.add_classification(key, value)
end
end
# @param entity [Sketchup::Entity] the sketchup entity of Speckle object
# @param key [Symbol] the name of the attribute
# @param dictionary_name [String, Symbol] the name of the attribute dictionary
@@ -105,9 +53,15 @@ module SpeckleConnector
dictionary.delete_key(key)
end
# @param entity [Sketchup::Entity] the sketchup entity of Speckle object
# @param dictionary_name [String, Symbol] the name of the attribute dictionary to remove
def self.remove_dictionary(entity, dictionary_name = self.dictionary_name)
entity.attribute_dictionaries.delete(dictionary_name)
end
# @return [String] the name of the dictionary to read from
def self.dictionary_name
DICTIONARY_NAME
raise NotImplementedError 'Implement this in subclass'
end
end
end
@@ -1,6 +1,6 @@
# frozen_string_literal: true
require_relative 'dictionary_handler'
require_relative 'base_dictionary_handler'
require_relative '../../constants/dict_constants'
require_relative '../../constants/type_constants'
@@ -9,6 +9,8 @@ module SpeckleConnector
module Dictionary
# Dictionary handler of the speckle entity.
class SpeckleEntityDictionaryHandler < DictionaryHandler
DICTIONARY_NAME = SPECKLE_BASE_OBJECT
# 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
@@ -26,6 +28,11 @@ module SpeckleConnector
set_hash(sketchup_entity, initial_dict_data)
end
# rubocop:enable Metrics/ParameterLists
# @return [String] the name of the dictionary to read from
def self.dictionary_name
DICTIONARY_NAME
end
end
end
end
@@ -1,6 +1,6 @@
# frozen_string_literal: true
require_relative 'dictionary_handler'
require_relative 'base_dictionary_handler'
require_relative '../../constants/dict_constants'
require_relative '../../constants/type_constants'
@@ -8,7 +8,7 @@ module SpeckleConnector
module SketchupModel
module Dictionary
# Dictionary handler of the speckle model.
class SpeckleModelDictionaryHandler < DictionaryHandler
class SpeckleModelDictionaryHandler < BaseDictionaryHandler
DICTIONARY_NAME = 'Speckle'
# Writes initial data while speckle entity is creating first time.
# @param sketchup_model [Sketchup::Model] Sketchup model to write data into it's attribute dictionary.
@@ -0,0 +1,29 @@
# frozen_string_literal: true
require 'delegate'
require_relative 'dictionary_handler'
require_relative '../../constants/dict_constants'
module SpeckleConnector
module SketchupModel
module Dictionary
# Read and write attributes for Speckle objects' schema on SketchUp model.
class SpeckleSchemaDictionaryHandler < DictionaryHandler
def self.speckle_schema_to_speckle(entity)
schema = {}
return schema if entity.attribute_dictionaries.nil?
schema_dict = entity.attribute_dictionaries.find { |dict| dict.name == dictionary_name }
return schema if schema_dict.nil?
schema_dict.to_h
end
# @return [String] the name of the dictionary to read from
def self.dictionary_name
SPECKLE_MAPPING_TOOL_SCHEMA
end
end
end
end
end
@@ -0,0 +1,113 @@
# frozen_string_literal: true
module SpeckleConnector
module SketchupModel
# Query operations in sketchup model.
module Query
# Queries for entity.
class Entity
class << self
# Creates flat list for entities that defined in classes property. It searches from top to bottom to collect
# entities.
# @param entities_to_flat [Sketchup::Entities] entities to flat their children, grandchildren and so on..
# @param classes [Array<Class>] objects types to collect as flat list.
def flat_entities(entities_to_flat,
classes = [Sketchup::Edge, Sketchup::Face, Sketchup::ComponentInstance,
Sketchup::Group, Sketchup::ComponentDefinition])
entities = []
entities_to_flat.each do |entity|
entities.append(entity) if classes.include?(entity.class)
if entity.is_a?(Sketchup::Group) || entity.is_a?(Sketchup::ComponentInstance)
entities.append(entity.definition) if classes.include?(Sketchup::ComponentDefinition)
entities += flat_entities(entity.definition.entities, classes)
end
end
entities
end
# Create array for each entity with their path.
# @param entities_to_flat [Sketchup::Entities, Array<Sketchup::Entity>] entities to flat with their path.
# @param classes [Array<Class>] classes to flat. Put class into this array if you want to find their paths.
# @param path [Array<Object>] path for entity that we are in.
# @return [Array<Object>] entity with it's path as flat array. See example.
# @example
# path[0] is entity itself
# path[1..-1] rest as path from top to bottom
def flat_entities_with_path(entities_to_flat,
classes = [Sketchup::Edge, Sketchup::Face, Sketchup::ComponentInstance,
Sketchup::Group, Sketchup::ComponentDefinition],
path = [])
entities = []
entities_to_flat.each do |entity|
# Collect object itself
entities.append([entity] + path) if classes.include?(entity.class)
# entities[entity] = path if classes.include?(entity.class) && entities[entity].nil?
# Skip unless entity is a container entity like group or component.
next unless entity.is_a?(Sketchup::Group) || entity.is_a?(Sketchup::ComponentInstance)
# Add entity definition also with it's path.
entities[entity.definition] = path if classes.include?(Sketchup::ComponentDefinition)
# Collect sub-objects if object is a container at the same time.
sub_entities = flat_entities_with_path(entity.definition.entities.to_a,
classes, path + [entity])
entities += sub_entities
end
entities
end
# Calculates global transformation of entity by multiplying path entries from bottom to top by reversing path.
# @param entity [Sketchup::Entity] entity to find global transformation.
# @param path [Array<Object>] path that parents of entity that has transformation value to calculate global
# transformation of the entity.
# @return [Geom::Transformation] global transformation of the entity.
def global_transformation(entity, path)
# If entity is face, use Identity
global = entity.respond_to?(:transformation) ? entity.transformation : Geom::Transformation.new
path.reverse.each do |local|
global = local.transformation * global if local.respond_to?(:transformation)
end
global
end
# Global transformation search for entity that lies on only one instance.
# @param entity [Sketchup::Entity] entity to find global transformation.
def global_transformation_from_bottom(entity)
# If entity is face, use Identity
transformation = entity.respond_to?(:transformation) ? entity.transformation : Geom::Transformation.new
parent = parent_or_model(entity)
until parent.is_a?(Sketchup::Model) || parent.nil?
transformation = parent.transformation * transformation
parent = parent_or_model(parent)
end
transformation
end
# Parent search for entity from bottom to top. It is not ideal if entity lives in different instances.
def parent_or_model(entity)
parent = entity.parent
return parent if parent.is_a?(Sketchup::Model)
instances = parent.instances
if instances.length > 1
puts 'Parent has more than one instance'
instances.each(&:make_unique)
instances = instances.select { |instance| instance.definition.entities.include?(entity) }
end
instances.first
end
# Finds first material of parents from bottom to top.
def parent_material(path)
material = nil
path.reverse.each do |local|
material = local.material if local.respond_to?(:material)
return material unless material.nil?
end
material
end
end
end
end
end
end
@@ -1,6 +1,8 @@
# frozen_string_literal: true
require_relative '../dictionary/speckle_schema_dictionary_handler'
require_relative '../../speckle_entities/speckle_entity'
require_relative '../../mapping/category/revit_category'
require_relative '../../constants/dict_constants'
module SpeckleConnector
@@ -11,6 +13,7 @@ module SpeckleConnector
# Reader module for speckle entities.
module SpeckleEntitiesReader
# @param entities [Sketchup::Entities] entities to collect speckle entities.
# @return [Hash{String=>Sketchup::Entity}] speckle entities with persistent id.
def self.read(entities)
speckle_entities = {}
entities.each do |entity|
@@ -26,6 +29,16 @@ module SpeckleConnector
speckle_entities
end
# @param entities [Sketchup::Entities] entities to collect mapped entities.
# @return [Hash{String=>Sketchup::Entity}] mapped entities with persistent id.
def self.read_mapped_entities(entities)
mapped_entities = {}
Query::Entity.flat_entities(entities).each do |entity|
mapped_entities[entity.persistent_id] = entity if mapped_with_schema?(entity)
end
mapped_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 { |d| d.name == SPECKLE_BASE_OBJECT }
@@ -46,6 +59,62 @@ module SpeckleConnector
entity.attribute_dictionaries.to_a.any? { |dict| dict.name == SPECKLE_BASE_OBJECT }
end
# @param entity [Sketchup::Entity] sketchup entity to check whether mapped with speckle schema or not.
def self.mapped_with_schema?(entity)
is_entity_mapped = !Dictionary::SpeckleSchemaDictionaryHandler.attribute_dictionary(entity).nil?
return is_entity_mapped if is_entity_mapped
return is_entity_mapped unless entity.is_a?(Sketchup::ComponentInstance)
!Dictionary::SpeckleSchemaDictionaryHandler.attribute_dictionary(entity.definition).nil?
end
def self.get_schema(entity)
Dictionary::SpeckleSchemaDictionaryHandler.speckle_schema_to_speckle(entity)
end
def self.entities_schema_details(entities)
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) })
end
entity_selection_details
end
end
def self.entity_selection_details(entity)
sanitized_type = entity.class.name.split('::').last.gsub(/(?<=[a-z])(?=[A-Z])/, ' ').split
is_definition = entity.is_a?(Sketchup::ComponentDefinition)
entity_type = is_definition ? sanitized_type.last : sanitized_type.first
speckle_schema = get_schema(entity)
{
name: speckle_schema['name'],
entityName: entity.respond_to?(:name) ? entity.name : '',
entityId: entity.persistent_id,
entityType: entity_type,
schema: speckle_schema,
numberOfInstances: is_definition ? entity.instances.length : 1
}
end
def self.mapped_entity_details(entities)
reverse_category_dictionary = Mapping::Category::RevitCategory.reverse_dictionary
entities.collect do |entity|
speckle_schema = get_schema(entity)
{
name: speckle_schema['name'],
category: speckle_schema['category'],
categoryName: reverse_category_dictionary[speckle_schema['category']],
method: speckle_schema['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,
schema: speckle_schema,
definitionSchema: entity.respond_to?(:definition) ? get_schema(entity.definition) : nil
}
end
end
end
end
end
@@ -51,9 +51,9 @@ module SpeckleConnector
@sketchup_entity = sketchup_entity
@application_id = application_id
@id = speckle_id
@total_children_count = children.length
@total_children_count = children.nil? ? 0 : children.length
@speckle_type = speckle_type
@speckle_children_objects = children
@speckle_children_objects = children.nil? ? [] : children
end
# rubocop:enable Metrics/ParameterLists
@@ -0,0 +1,79 @@
# frozen_string_literal: true
require_relative '../../base'
require_relative '../../other/render_material'
require_relative '../../../constants/type_constants'
require_relative '../../../sketchup_model/query/entity'
module SpeckleConnector
module SpeckleObjects
module BuiltElements
module Revit
# Direct shape definition for Revit mappings.
class DirectShape < Base
SPECKLE_TYPE = OBJECTS_BUILTELEMENTS_REVIT_DIRECTSHAPE
def initialize(name:, category:, units:, base_geometries:, application_id: nil)
super(
speckle_type: SPECKLE_TYPE,
total_children_count: 0,
application_id: application_id,
id: nil
)
self[:name] = name
self[:category] = category
self[:units] = units
self[:baseGeometries] = base_geometries
end
def self.from_entity(entity, path, units, preferences)
schema = SketchupModel::Dictionary::SpeckleSchemaDictionaryHandler.attribute_dictionary(entity)
if schema.nil? && entity.respond_to?(:definition)
schema = SketchupModel::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
.flat_entities_with_path(
entity.definition.entities, [Sketchup::Face], path.append(entity)
)
end
base_geometries = group_faces_under_mesh_by_material(entities_with_path, units, preferences)
DirectShape.new(
name: schema[:name], category: schema[:category], units: units,
base_geometries: base_geometries, application_id: entity.persistent_id
)
end
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)
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[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),
parent_material: parent_material
)
mesh_groups[mesh_group_id] = [mesh, [face]]
end
end
# Update mesh overwrites points and polygons into base object.
mesh_groups.each { |_, mesh| mesh.first.update_mesh }
mesh_groups.values
end
end
end
end
end
end
@@ -5,7 +5,8 @@ require_relative 'point'
require_relative 'bounding_box'
require_relative '../base'
require_relative '../primitive/interval'
require_relative '../../sketchup_model/dictionary/dictionary_handler'
require_relative '../../sketchup_model/dictionary/base_dictionary_handler'
require_relative '../../sketchup_model/dictionary/speckle_schema_dictionary_handler'
module SpeckleConnector
module SpeckleObjects
@@ -17,11 +18,11 @@ module SpeckleConnector
# @param start_pt [Geometry::Point] start point speckle object of the speckle line.
# @param end_pt [Geometry::Point] end point speckle object of the speckle line.
# @param domain [Primitive::Interval] interval speckle object of the speckle line -represents domain.
# @param bbox [Geometry::BoundingBox] bounding box speckle object of the speckle line.
# @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:, bbox:, units:, sketchup_attributes: {}, application_id: nil)
def initialize(start_pt:, end_pt:, domain:, units:,
sketchup_attributes: {}, speckle_schema: {}, application_id: nil)
super(
speckle_type: 'Objects.Geometry.Line',
total_children_count: 0,
@@ -31,30 +32,28 @@ module SpeckleConnector
self[:start] = start_pt
self[:end] = end_pt
self[:domain] = domain
self[:bbox] = bbox
self[:units] = units
self[:SpeckleSchema] = speckle_schema if speckle_schema.any?
self[:sketchup_attributes] = sketchup_attributes if sketchup_attributes.any?
end
# rubocop:enable Metrics/ParameterLists
# @param edge [Sketchup::Edge] edge to convert line.
def self.from_edge(edge, units, model_preferences)
dictionaries = {}
if model_preferences[:include_entity_attributes] && model_preferences[:include_edge_entity_attributes]
dictionaries = SketchupModel::Dictionary::DictionaryHandler.attribute_dictionaries_to_speckle(edge)
end
dictionaries = SketchupModel::Dictionary::BaseDictionaryHandler
.attribute_dictionaries_to_speckle(edge, model_preferences)
att = dictionaries.any? ? { dictionaries: dictionaries } : {}
speckle_schema = SketchupModel::Dictionary::SpeckleSchemaDictionaryHandler.speckle_schema_to_speckle(edge)
start_pt = Geometry::Point.from_vertex(edge.start.position, units)
end_pt = Geometry::Point.from_vertex(edge.end.position, units)
domain = Primitive::Interval.from_numeric(0, Float(edge.length), units)
bbox = Geometry::BoundingBox.from_bounds(edge.bounds, units)
Line.new(
start_pt: start_pt,
end_pt: end_pt,
domain: domain,
bbox: bbox,
units: units,
sketchup_attributes: att,
speckle_schema: speckle_schema,
application_id: edge.persistent_id.to_s
)
end
@@ -78,7 +77,7 @@ module SpeckleConnector
edges.each do |edge|
edge.layer = layer
unless line['sketchup_attributes'].nil?
SketchupModel::Dictionary::DictionaryHandler
SketchupModel::Dictionary::BaseDictionaryHandler
.attribute_dictionaries_to_native(edge, line['sketchup_attributes']['dictionaries'])
end
end
@@ -88,12 +87,10 @@ module SpeckleConnector
def self.test_line(start_point, end_point, units)
domain = Primitive::Interval.from_numeric(0, 5, units)
bbox = Geometry::BoundingBox.test_bounds(units)
Line.new(
start_pt: start_point,
end_pt: end_point,
domain: domain,
bbox: bbox,
application_id: '',
units: units
)
@@ -3,8 +3,10 @@
require_relative '../base'
require_relative '../geometry/bounding_box'
require_relative '../other/render_material'
require_relative '../../sketchup_model/query/entity'
require_relative '../../convertors/clean_up'
require_relative '../../sketchup_model/dictionary/dictionary_handler'
require_relative '../../sketchup_model/dictionary/base_dictionary_handler'
require_relative '../../sketchup_model/dictionary/speckle_schema_dictionary_handler'
module SpeckleConnector
module SpeckleObjects
@@ -29,7 +31,8 @@ module SpeckleConnector
# @param faces [Array] faces of the speckle mesh.
# @param sketchup_attributes [Hash] additional information about speckle mesh.
# rubocop:disable Metrics/ParameterLists
def initialize(units:, render_material:, vertices:, faces:, sketchup_attributes:, application_id: nil)
def initialize(units:, render_material:, vertices:, faces:,
sketchup_attributes:, speckle_schema: {}, application_id: nil)
super(
speckle_type: SPECKLE_TYPE,
total_children_count: 0,
@@ -41,10 +44,10 @@ module SpeckleConnector
@units = units
self[:units] = units
self[:renderMaterial] = render_material
# self[:bbox] = bbox
self[:'@(31250)vertices'] = vertices
self[:'@(62500)faces'] = faces
self[:sketchup_attributes] = sketchup_attributes if sketchup_attributes.any?
self[:SpeckleSchema] = speckle_schema if speckle_schema.any?
end
# rubocop:enable Metrics/ParameterLists
@@ -85,7 +88,7 @@ module SpeckleConnector
added_faces.each do |face|
face.layer = layer
unless mesh['sketchup_attributes'].nil?
SketchupModel::Dictionary::DictionaryHandler
SketchupModel::Dictionary::BaseDictionaryHandler
.attribute_dictionaries_to_native(face, mesh['sketchup_attributes']['dictionaries'])
end
end
@@ -101,37 +104,46 @@ module SpeckleConnector
# rubocop:enable Metrics/PerceivedComplexity:
# @param face [Sketchup::Face] face to convert mesh
# @param units [String] model units to send Speckle.
# @param model_preferences [Hash{Symbol=>Boolean}] model preferences to check include attributes or not.
# @param global_transform [Geom::Transformation, nil] global transformation value of face if it is not included
# into any component.
# rubocop:disable Style/MultilineTernaryOperator
# rubocop:disable Metrics/CyclomaticComplexity
def self.from_face(face, units, model_preferences)
dictionaries = {}
if model_preferences[:include_entity_attributes] && model_preferences[:include_face_entity_attributes]
dictionaries = SketchupModel::Dictionary::DictionaryHandler.attribute_dictionaries_to_speckle(face)
end
def self.from_face(face:, units:, model_preferences:, global_transform: nil, parent_material: nil)
dictionaries = SketchupModel::Dictionary::BaseDictionaryHandler
.attribute_dictionaries_to_speckle(face, model_preferences)
has_any_soften_edge = face.edges.any?(&:soft?)
att = dictionaries.any? ? { is_soften: has_any_soften_edge, dictionaries: dictionaries }
: { is_soften: has_any_soften_edge }
speckle_schema = SketchupModel::Dictionary::SpeckleSchemaDictionaryHandler.speckle_schema_to_speckle(face)
material = face.material || face.back_material || parent_material
speckle_mesh = Mesh.new(
units: units,
render_material: face.material.nil? && face.back_material.nil? ? nil : Other::RenderMaterial
.from_material(face.material || face.back_material),
# bbox: Geometry::BoundingBox.from_bounds(face.bounds, units),
render_material: material.nil? ? nil : Other::RenderMaterial.from_material(material),
vertices: [],
faces: [],
sketchup_attributes: att,
speckle_schema: speckle_schema,
application_id: face.persistent_id
)
speckle_mesh.face_to_mesh(face)
speckle_mesh.face_to_mesh(face, global_transform)
speckle_mesh.update_mesh
speckle_mesh
end
# rubocop:enable Style/MultilineTernaryOperator
# rubocop:enable Metrics/CyclomaticComplexity
def face_to_mesh(face)
# @param global_transform [Geom::Transformation, nil] global transformation value of face if it is not included
# into any component. So it's mesh will be transformed into global coordinates to represent it correctly in
# Speckle viewer or other connectors.
def face_to_mesh(face, global_transform = nil)
mesh = face.loops.count > 1 ? face.mesh : nil
mesh.nil? ? face_vertices_to_array(face) : mesh_points_to_array(mesh)
mesh.nil? ? face_indices_to_array(face) : mesh_faces_to_array(mesh)
if global_transform.nil?
mesh.nil? ? face_vertices_to_array(face) : mesh_points_to_array(mesh)
mesh.nil? ? face_indices_to_array(face) : mesh_faces_to_array(mesh)
else
mesh_points_to_array(face.mesh, global_transform)
mesh_faces_to_array(face.mesh, global_transform)
end
end
# Collects indexed Sketchup vertices into flat array for Speckle use.
@@ -176,7 +188,8 @@ module SpeckleConnector
# Get a flat array of vertices from a sketchup polygon mesh
# @param mesh [Geom::PolygonMesh] mesh to get points.
def mesh_points_to_array(mesh)
def mesh_points_to_array(mesh, global_transform = nil)
mesh.transform!(global_transform) unless global_transform.nil?
mesh.points.each do |pt|
# FIXME: Enable previous line when viewer supports shared vertices
# vertices.push(pt) unless vertices.any? { |point| point == pt }
@@ -186,7 +199,8 @@ module SpeckleConnector
# Get an array of face indices from a sketchup polygon mesh
# @param mesh [Geom::PolygonMesh] mesh to convert into polygons.
def mesh_faces_to_array(mesh)
def mesh_faces_to_array(mesh, global_transform = nil)
mesh.transform!(global_transform) unless global_transform.nil?
mesh.polygons.each do |poly|
global_polygon_array = [poly.count]
poly.each do |index|
@@ -225,6 +239,38 @@ module SpeckleConnector
end
points
end
# Mesh group id helps to determine how to group faces into meshes.
# @param face [Sketchup::Face] face to get mesh group id.
def self.get_mesh_group_id(face, model_preferences, parent_material = nil)
if model_preferences[:include_entity_attributes] &&
model_preferences[:include_face_entity_attributes] &&
attribute_dictionary?(face)
return face.persistent_id.to_s
end
material = face.material || face.back_material || parent_material
return 'none' if material.nil?
return material.entityID.to_s
end
def self.attribute_dictionary?(face)
any_attribute_dictionary = !(face.attribute_dictionaries.nil? || face.attribute_dictionaries.first.nil?)
return any_attribute_dictionary unless any_attribute_dictionary
# If there are any attribute dictionary, then make sure that they are not ignored ones.
all_attribute_dictionary_ignored = face.attribute_dictionaries.all? do |dict|
ignored_dictionaries.include?(dict.name)
end
!all_attribute_dictionary_ignored
end
def self.ignored_dictionaries
[
'Speckle_Base_Object'
]
end
end
end
end
@@ -7,7 +7,8 @@ require_relative '../base'
require_relative '../geometry/point'
require_relative '../geometry/mesh'
require_relative '../geometry/bounding_box'
require_relative '../../sketchup_model/dictionary/dictionary_handler'
require_relative '../../sketchup_model/dictionary/base_dictionary_handler'
require_relative '../../sketchup_model/dictionary/speckle_schema_dictionary_handler'
module SpeckleConnector
module SpeckleObjects
@@ -22,7 +23,7 @@ module SpeckleConnector
# @param application_id [String, NilClass] application id of the block definition.
# rubocop:disable Metrics/ParameterLists
def initialize(geometry:, name:, units:, always_face_camera:, sketchup_attributes: {},
application_id: nil)
speckle_schema: {}, application_id: nil)
super(
speckle_type: SPECKLE_TYPE,
total_children_count: 0,
@@ -33,7 +34,8 @@ module SpeckleConnector
self[:name] = name
self[:always_face_camera] = always_face_camera
self[:sketchup_attributes] = sketchup_attributes if sketchup_attributes.any?
# FIXME: Since geometry sends with @ as detached, block basePlane renders on viewer.
self[:SpeckleSchema] = speckle_schema if speckle_schema.any?
# '@@' means that it is a detached property.
self['@@geometry'] = geometry
end
# rubocop:enable Metrics/ParameterLists
@@ -42,32 +44,19 @@ module SpeckleConnector
# instance
# @param units [String] units of the Sketchup model
# @param definitions [Hash{String=>BlockDefinition}] all converted {BlockDefinition}s on the converter.
# rubocop:disable Metrics/CyclomaticComplexity
# rubocop:disable Metrics/PerceivedComplexity
# rubocop:disable Metrics/MethodLength
# rubocop:disable Metrics/AbcSize
# rubocop:disable Metrics/ParameterLists
def self.from_definition(definition, units, definitions, preferences, speckle_state, parent, &convert)
guid = definition.guid
return definitions[guid] if definitions.key?(guid)
dictionaries = {}
if preferences[:model][:include_entity_attributes]
if definition.group?
if preferences[:model][:include_group_entity_attributes]
dictionaries = SketchupModel::Dictionary::DictionaryHandler
.attribute_dictionaries_to_speckle(definition)
end
elsif preferences[:model][:include_component_entity_attributes]
dictionaries = SketchupModel::Dictionary::DictionaryHandler.attribute_dictionaries_to_speckle(definition)
end
end
def self.from_definition(definition, units, preferences, speckle_state, parent, &convert)
dictionaries = SketchupModel::Dictionary::BaseDictionaryHandler
.attribute_dictionaries_to_speckle(definition, preferences[:model])
att = dictionaries.any? ? { dictionaries: dictionaries } : {}
speckle_schema = SketchupModel::Dictionary::SpeckleSchemaDictionaryHandler
.speckle_schema_to_speckle(definition)
# TODO: Solve logic
geometry = if definition.entities[0].is_a?(Sketchup::Edge) || definition.entities[0].is_a?(Sketchup::Face)
new_speckle_state, geo = group_entities_to_speckle(
definition, preferences, speckle_state, parent, &convert
definition.entities, preferences, speckle_state, parent, &convert
)
speckle_state = new_speckle_state
geo
@@ -83,21 +72,18 @@ module SpeckleConnector
end
end
# FIXME: Decide how to approach base point of the definition instead origin.
block_definition = BlockDefinition.new(
units: units,
name: definition.name,
geometry: geometry,
always_face_camera: definition.behavior.always_face_camera?,
sketchup_attributes: att,
speckle_schema: speckle_schema,
application_id: definition.persistent_id.to_s
)
return speckle_state, block_definition
end
# rubocop:enable Metrics/CyclomaticComplexity
# rubocop:enable Metrics/PerceivedComplexity
# rubocop:enable Metrics/MethodLength
# rubocop:enable Metrics/AbcSize
# rubocop:enable Metrics/ParameterLists
def self.get_definition_name(def_obj)
@@ -144,7 +130,7 @@ module SpeckleConnector
# puts(" entity count: #{definition.entities.count}")
definition.behavior.always_face_camera = always_face_camera
unless sketchup_attributes.nil?
SketchupModel::Dictionary::DictionaryHandler
SketchupModel::Dictionary::BaseDictionaryHandler
.attribute_dictionaries_to_native(definition, sketchup_attributes['dictionaries'])
end
return state, [definition]
@@ -158,21 +144,21 @@ module SpeckleConnector
# rubocop:disable Metrics/MethodLength
# rubocop:disable Metrics/CyclomaticComplexity
# rubocop:disable Metrics/PerceivedComplexity
def self.group_entities_to_speckle(definition, preferences, speckle_state, parent, &convert)
orphan_edges = definition.entities.grep(Sketchup::Edge).filter { |edge| edge.faces.none? }
def self.group_entities_to_speckle(entities, preferences, speckle_state, parent, &convert)
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)
speckle_state = new_speckle_state
converted
end
nested_blocks = definition.entities.grep(Sketchup::ComponentInstance).collect do |component_instance|
nested_blocks = entities.grep(Sketchup::ComponentInstance).collect do |component_instance|
new_speckle_state, converted = convert.call(component_instance, preferences, speckle_state, parent)
speckle_state = new_speckle_state
converted
end
nested_groups = definition.entities.grep(Sketchup::Group).collect do |group|
nested_groups = entities.grep(Sketchup::Group).collect do |group|
new_speckle_state, converted = convert.call(group, preferences, speckle_state, parent)
speckle_state = new_speckle_state
converted
@@ -180,7 +166,9 @@ module SpeckleConnector
if preferences[:model][:combine_faces_by_material]
mesh_groups = {}
definition.entities.grep(Sketchup::Face).collect do |face|
entities.grep(Sketchup::Face).collect do |face|
next unless SketchupModel::Dictionary::SpeckleSchemaDictionaryHandler.attribute_dictionary(face).nil?
new_speckle_state = group_meshes_by_material(
face, mesh_groups, speckle_state, preferences, parent, &convert
)
@@ -192,7 +180,7 @@ module SpeckleConnector
return speckle_state, lines + nested_blocks + nested_groups + mesh_groups.values
else
meshes = []
definition.entities.grep(Sketchup::Face).collect do |face|
entities.grep(Sketchup::Face).collect do |face|
new_speckle_state, converted = convert.call(face, preferences, speckle_state, parent)
meshes.append(converted)
speckle_state = new_speckle_state
@@ -209,7 +197,7 @@ module SpeckleConnector
# rubocop:disable Metrics/ParameterLists
def self.group_meshes_by_material(face, mesh_groups, speckle_state, preferences, parent, &convert)
# convert material
mesh_group_id = get_mesh_group_id(face, preferences[:model])
mesh_group_id = Geometry::Mesh.get_mesh_group_id(face, preferences[:model])
new_speckle_state, converted = convert.call(face, preferences, speckle_state, parent)
mesh_groups[mesh_group_id] = converted unless mesh_groups.key?(mesh_group_id)
mesh_group = mesh_groups[mesh_group_id]
@@ -219,37 +207,7 @@ module SpeckleConnector
end
# rubocop:enable Metrics/ParameterLists
# Mesh group id helps to determine how to group faces into meshes.
# @param face [Sketchup::Face] face to get mesh group id.
def self.get_mesh_group_id(face, model_preferences)
if model_preferences[:include_entity_attributes] &&
model_preferences[:include_face_entity_attributes] &&
attribute_dictionary?(face)
return face.persistent_id.to_s
end
material = face.material || face.back_material
return 'none' if material.nil?
return material.entityID.to_s
end
def self.attribute_dictionary?(face)
any_attribute_dictionary = !(face.attribute_dictionaries.nil? || face.attribute_dictionaries.first.nil?)
return any_attribute_dictionary unless any_attribute_dictionary
# If there are any attribute dictionary, then make sure that they are not ignored ones.
all_attribute_dictionary_ignored = face.attribute_dictionaries.all? do |dict|
ignored_dictionaries.include?(dict.name)
end
!all_attribute_dictionary_ignored
end
def self.ignored_dictionaries
[
'Speckle_Base_Object'
]
end
end
end
end
@@ -5,7 +5,8 @@ require_relative 'transform'
require_relative 'block_definition'
require_relative '../base'
require_relative '../geometry/bounding_box'
require_relative '../../sketchup_model/dictionary/dictionary_handler'
require_relative '../../sketchup_model/dictionary/base_dictionary_handler'
require_relative '../../sketchup_model/dictionary/speckle_schema_dictionary_handler'
module SpeckleConnector
module SpeckleObjects
@@ -24,7 +25,7 @@ module SpeckleConnector
# @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:,
sketchup_attributes: {}, application_id: nil)
sketchup_attributes: {}, speckle_schema: {}, application_id: nil)
super(
speckle_type: SPECKLE_TYPE,
total_children_count: 0,
@@ -37,6 +38,7 @@ module SpeckleConnector
self[:renderMaterial] = render_material
self[:transform] = transform
self[:sketchup_attributes] = sketchup_attributes if sketchup_attributes.any?
self[:SpeckleSchema] = speckle_schema if speckle_schema.any?
# FIXME: Since blockDefinition sends with @ as detached, block basePlane renders on viewer.
self['@@definition'] = block_definition
end
@@ -47,12 +49,10 @@ module SpeckleConnector
new_speckle_state, block_definition = convert.call(group.definition, preferences, speckle_state,
group.persistent_id)
speckle_state = new_speckle_state
dictionaries = {}
if preferences[:model][:include_entity_attributes] && preferences[:model][:include_group_entity_attributes]
dictionaries = SketchupModel::Dictionary::DictionaryHandler.attribute_dictionaries_to_speckle(group)
end
dictionaries = SketchupModel::Dictionary::BaseDictionaryHandler
.attribute_dictionaries_to_speckle(group, preferences[:model])
att = dictionaries.any? ? { dictionaries: dictionaries } : {}
speckle_schema = SketchupModel::Dictionary::SpeckleSchemaDictionaryHandler.speckle_schema_to_speckle(group)
block_instance = BlockInstance.new(
units: units,
is_sketchup_group: true,
@@ -61,6 +61,7 @@ module SpeckleConnector
transform: Other::Transform.from_transformation(group.transformation, units),
block_definition: block_definition,
sketchup_attributes: att,
speckle_schema: speckle_schema,
application_id: group.guid
)
return speckle_state, block_instance
@@ -77,13 +78,11 @@ module SpeckleConnector
)
speckle_state = new_speckle_state
dictionaries = {}
if preferences[:model][:include_entity_attributes] &&
preferences[:model][:include_component_entity_attributes]
dictionaries = SketchupModel::Dictionary::DictionaryHandler
.attribute_dictionaries_to_speckle(component_instance)
end
dictionaries = SketchupModel::Dictionary::BaseDictionaryHandler
.attribute_dictionaries_to_speckle(component_instance, preferences[:model])
att = dictionaries.any? ? { dictionaries: dictionaries } : {}
speckle_schema = SketchupModel::Dictionary::SpeckleSchemaDictionaryHandler
.speckle_schema_to_speckle(component_instance)
block_instance = BlockInstance.new(
units: units,
@@ -97,6 +96,7 @@ module SpeckleConnector
transform: Other::Transform.from_transformation(component_instance.transformation, units),
block_definition: block_definition,
sketchup_attributes: att,
speckle_schema: speckle_schema,
application_id: component_instance.persistent_id.to_s
)
return speckle_state, block_instance
@@ -196,7 +196,7 @@ module SpeckleConnector
instance.name = block['name'] unless block['name'].nil?
unless block['sketchup_attributes'].nil?
SketchupModel::Dictionary::DictionaryHandler
SketchupModel::Dictionary::BaseDictionaryHandler
.attribute_dictionaries_to_native(instance, block['sketchup_attributes']['dictionaries'])
end
return state, [instance, definition]
@@ -7,7 +7,7 @@ require_relative '../../immutable/immutable'
require_relative '../../ext/immutable_ruby/hash'
require_relative '../base'
require_relative '../geometry/bounding_box'
require_relative '../../sketchup_model/dictionary/dictionary_handler'
require_relative '../../sketchup_model/dictionary/base_dictionary_handler'
module SpeckleConnector
module SpeckleObjects
@@ -1,12 +1,12 @@
# frozen_string_literal: true
require_relative '../block_definition'
require_relative '../../base'
require_relative '../other/block_definition'
require_relative '../base'
module SpeckleConnector
module SpeckleObjects
module Other
module Revit
module Revit
module Other
# RevitDefinition for Speckle.
class RevitDefinition < Base
SPECKLE_TYPE = OBJECTS_OTHER_REVIT_REVITINSTANCE
@@ -23,7 +23,7 @@ module SpeckleConnector
definition_name = get_definition_name(definition)
definition['name'] = definition_name
definition['displayValue'] += definition['elements'] unless definition['elements'].nil?
BlockDefinition.to_native(state, definition, layer, entities, &convert_to_native)
SpeckleObjects::Other::BlockDefinition.to_native(state, definition, layer, entities, &convert_to_native)
end
end
end
@@ -1,17 +1,12 @@
# frozen_string_literal: true
require_relative 'revit_definition'
require_relative '../render_material'
require_relative '../transform'
require_relative '../block_definition'
require_relative '../../base'
require_relative '../../geometry/bounding_box'
require_relative '../../../sketchup_model/dictionary/dictionary_handler'
require_relative '../base'
module SpeckleConnector
module SpeckleObjects
module Other
module Revit
module Revit
module Other
# RevitInstance object definition for Speckle.
class RevitInstance < Base
SPECKLE_TYPE = OBJECTS_OTHER_REVIT_REVITINSTANCE
@@ -35,8 +30,9 @@ module SpeckleConnector
definition = state.sketchup_state.sketchup_model
.definitions[RevitDefinition.get_definition_name(block_definition)]
return BlockInstance.add_instance_from_definition(state, block, layer, entities,
definition, false, &convert_to_native)
return SpeckleObjects::Other::BlockInstance.add_instance_from_definition(
state, block, layer, entities, definition, false, &convert_to_native
)
end
end
end
@@ -14,6 +14,9 @@ module SpeckleConnector
# 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
@@ -30,12 +33,23 @@ module SpeckleConnector
# @return [Relations::ManyToOneRelation] relations between objects.
attr_accessor :relation
# TODO: Do cashing later
# @return [ImmutableHash{String=>SpeckleObjects::Other::RenderMaterial}] converted render materials
attr_accessor :render_materials
# TODO: Do cashing later
# @return [ImmutableHash{String=>SpeckleObjects::Other::BlockDefinition}] converted component definitions
attr_accessor :definitions
def initialize(accounts, observers, queue, stream_queue)
@accounts = accounts
@observers = observers
@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
end
@@ -48,6 +62,23 @@ module SpeckleConnector
with(:@message_queue => new_queue)
end
def with_mapped_entities_queue(mapped_entities)
new_queue = message_queue.merge({ "mappedEntitiesUpdated":
"mappedEntitiesUpdated(#{JSON.generate(mapped_entities)})" })
with(:@message_queue => new_queue)
end
def with_selection_queue(selection_parameters)
new_queue = message_queue.merge({ "entitySelected":
"entitySelected(#{JSON.generate(selection_parameters)})" })
with(:@message_queue => new_queue)
end
def with_deselection_queue
new_queue = message_queue.merge({ "entitiesDeselected": 'entitiesDeselected()' })
with(:@message_queue => new_queue)
end
def with_invalid_streams_queue
new_queue = message_queue.merge({ "updateInvalidStreams":
"updateInvalidStreams(#{JSON.generate(invalid_streams)})" })
@@ -64,6 +95,16 @@ module SpeckleConnector
with(:@accounts => new_accounts)
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_speckle_entity(traversed_entity)
new_speckle_entities = speckle_entities.put(traversed_entity.application_id, traversed_entity)
with_speckle_entities(new_speckle_entities)
@@ -73,6 +114,10 @@ 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
+14
View File
@@ -36,6 +36,20 @@ module SpeckleConnector
with(:@speckle_state => new_speckle_state)
end
def with_mapped_entities_queue(mapped_entities)
new_speckle_state = speckle_state.with_mapped_entities_queue(mapped_entities)
with(:@speckle_state => new_speckle_state)
end
def with_selection_queue(selection_parameters)
new_speckle_state = if selection_parameters[:selection].any?
speckle_state.with_selection_queue(selection_parameters)
else
speckle_state.with_deselection_queue
end
with(:@speckle_state => new_speckle_state)
end
def with_empty_stream_queue
new_speckle_state = speckle_state.with(:@stream_queue => {})
with(:@speckle_state => new_speckle_state)
+17 -1
View File
@@ -14,6 +14,8 @@ require_relative '../commands/notify_connected'
require_relative '../commands/user_preferences_updated'
require_relative '../commands/model_preferences_updated'
require_relative '../commands/activate_diffing'
require_relative '../commands/apply_mappings'
require_relative '../commands/clear_mappings'
require_relative '../actions/reload_accounts'
require_relative '../actions/load_saved_streams'
@@ -21,6 +23,12 @@ require_relative '../actions/init_local_accounts'
require_relative '../actions/collect_preferences'
require_relative '../actions/deactivate_diffing'
require_relative '../actions/collect_versions'
require_relative '../actions/mapped_entities_updated'
require_relative '../actions/clear_mappings_from_table'
require_relative '../actions/isolate_mappings_from_table'
require_relative '../actions/hide_mappings_from_table'
require_relative '../actions/select_mappings_from_table'
require_relative '../actions/show_all_entities'
module SpeckleConnector
module Ui
@@ -72,7 +80,15 @@ module SpeckleConnector
user_preferences_updated: Commands::UserPreferencesUpdated.new(@app),
model_preferences_updated: Commands::ModelPreferencesUpdated.new(@app),
activate_diffing: Commands::ActivateDiffing.new(@app),
deactivate_diffing: Commands::ActionCommand.new(@app, Actions::DeactivateDiffing)
deactivate_diffing: Commands::ActionCommand.new(@app, Actions::DeactivateDiffing),
collect_mapped_entities: Commands::ActionCommand.new(@app, Actions::MappedEntitiesUpdated),
apply_mappings: Commands::ApplyMappings.new(@app),
clear_mappings: Commands::ClearMappings.new(@app),
clear_mappings_from_table: Commands::ActionCommand.new(@app, Actions::ClearMappingsFromTable),
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)
}.freeze
end
end
+14 -14
View File
@@ -14,13 +14,13 @@
"mixpanel-browser": "^2.45.0",
"regenerator-runtime": "^0.13.9",
"register-service-worker": "^1.7.1",
"sqlite3": "^5.0.2",
"sqlite3": "^5.1.5",
"v-tooltip": "^2.1.3",
"vue": "^2.6.11",
"vue-apollo": "^3.0.0-beta.11",
"vue-router": "^3.2.0",
"vue-timeago": "^5.1.3",
"vuetify": "^2.4.0"
"vuetify": "2.6.10"
},
"devDependencies": {
"@vue/cli-plugin-babel": "~4.5.0",
@@ -22707,9 +22707,9 @@
"dev": true
},
"node_modules/sqlite3": {
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-5.0.8.tgz",
"integrity": "sha512-f2ACsbSyb2D1qFFcqIXPfFscLtPVOWJr5GmUzYxf4W+0qelu5MWrR+FAQE1d5IUArEltBrzSDxDORG8P/IkqyQ==",
"version": "5.1.5",
"resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-5.1.5.tgz",
"integrity": "sha512-7sP16i4wI+yKnGOO2q2ijze7EjQ9US+Vw7DYYwxfFtqNZDGgBcEw0oeDaDvUTq66uJOzVd/z6MkIg+c9erSJKg==",
"hasInstallScript": true,
"dependencies": {
"@mapbox/node-pre-gyp": "^1.0.0",
@@ -25264,9 +25264,9 @@
}
},
"node_modules/vuetify": {
"version": "2.6.6",
"resolved": "https://registry.npmjs.org/vuetify/-/vuetify-2.6.6.tgz",
"integrity": "sha512-H4KtxDFmDN8QiTRiGfBySyjMhVaHAJTKB0llGGKZT5jKxtnx9gvEtMWXKtVuRP0NJJP0H6xBPJHNOH7nT18qiQ==",
"version": "2.6.10",
"resolved": "https://registry.npmjs.org/vuetify/-/vuetify-2.6.10.tgz",
"integrity": "sha512-fgUeRDDCwYkwu6WGEEKGe7IdfzOsXJCZGrqkn1pcS2ycuoDL8mR2/dejH5iFNnBY6MnsT365PAGn0J+9otjfQg==",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/johnleider"
@@ -44593,9 +44593,9 @@
"dev": true
},
"sqlite3": {
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-5.0.8.tgz",
"integrity": "sha512-f2ACsbSyb2D1qFFcqIXPfFscLtPVOWJr5GmUzYxf4W+0qelu5MWrR+FAQE1d5IUArEltBrzSDxDORG8P/IkqyQ==",
"version": "5.1.5",
"resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-5.1.5.tgz",
"integrity": "sha512-7sP16i4wI+yKnGOO2q2ijze7EjQ9US+Vw7DYYwxfFtqNZDGgBcEw0oeDaDvUTq66uJOzVd/z6MkIg+c9erSJKg==",
"requires": {
"@mapbox/node-pre-gyp": "^1.0.0",
"node-addon-api": "^4.2.0",
@@ -46606,9 +46606,9 @@
}
},
"vuetify": {
"version": "2.6.6",
"resolved": "https://registry.npmjs.org/vuetify/-/vuetify-2.6.6.tgz",
"integrity": "sha512-H4KtxDFmDN8QiTRiGfBySyjMhVaHAJTKB0llGGKZT5jKxtnx9gvEtMWXKtVuRP0NJJP0H6xBPJHNOH7nT18qiQ==",
"version": "2.6.10",
"resolved": "https://registry.npmjs.org/vuetify/-/vuetify-2.6.10.tgz",
"integrity": "sha512-fgUeRDDCwYkwu6WGEEKGe7IdfzOsXJCZGrqkn1pcS2ycuoDL8mR2/dejH5iFNnBY6MnsT365PAGn0J+9otjfQg==",
"requires": {}
},
"vuetify-loader": {
+2 -2
View File
@@ -17,13 +17,13 @@
"mixpanel-browser": "^2.45.0",
"regenerator-runtime": "^0.13.9",
"register-service-worker": "^1.7.1",
"sqlite3": "^5.0.2",
"sqlite3": "^5.1.5",
"v-tooltip": "^2.1.3",
"vue": "^2.6.11",
"vue-apollo": "^3.0.0-beta.11",
"vue-router": "^3.2.0",
"vue-timeago": "^5.1.3",
"vuetify": "^2.4.0"
"vuetify": "2.6.10"
},
"devDependencies": {
"@vue/cli-plugin-babel": "~4.5.0",
+65 -29
View File
@@ -1,25 +1,26 @@
<template>
<v-app>
<v-main>
<v-app-bar app flat>
<v-app-bar app flat height="50">
<v-img
class="mx-auto"
class="mx-auto px-0"
max-width="45"
src="@/assets/logo.svg"
style="display: inline-block"
/>
<v-text-field
v-model="streamSearchQuery"
prepend-inner-icon="mdi-magnify"
label="Search streams"
hide-details
clearable
rounded
filled
dense
flat
solo
/>
<v-tabs
v-model="tab"
align-tabs="title"
class="mx-sm-1"
>
<v-tabs-slider class="mx-sm-1"></v-tabs-slider>
<v-tab href="#streams">
{{"Streams"}}
</v-tab>
<v-tab href="#mapper">
{{"Mapper"}}
</v-tab>
</v-tabs>
<v-spacer />
<v-btn icon small class="mx-1" @click="requestRefresh">
<v-icon>mdi-refresh</v-icon>
@@ -80,19 +81,44 @@
</v-menu>
</v-app-bar>
<create-stream-dialog
v-if="accounts().length !== 0"
:account-id="activeAccount().userInfo.id"
:server-url="activeAccount().serverInfo.url"
/>
<v-container v-if="accounts().length !== 0" fluid>
<router-view :stream-search-query="streamSearchQuery" />
</v-container>
<v-container v-else>
<login/>
</v-container>
<global-toast />
<v-tabs-items v-model="tab">
<v-tab-item :key="1" value="streams">
<v-container class="ma-0 pa-0">
<v-container>
<v-text-field
v-model="streamSearchQuery"
prepend-inner-icon="mdi-magnify"
label="Search streams"
background-color="background"
hide-details
clearable
rounded
filled
dense
flat
solo
/>
</v-container>
<create-stream-dialog
v-if="accounts().length !== 0"
:account-id="activeAccount().userInfo.id"
:server-url="activeAccount().serverInfo.url"
/>
<v-container v-if="accounts().length !== 0" fluid>
<router-view :stream-search-query="streamSearchQuery" />
</v-container>
<v-container v-else>
<login/>
</v-container>
<global-toast />
</v-container>
</v-tab-item>
<v-tab-item :key="2" value="mapper">
<v-card flat>
<mapper></mapper>
</v-card>
</v-tab-item>
</v-tabs-items>
</v-main>
</v-app>
</template>
@@ -141,7 +167,8 @@ export default {
Login,
CreateStreamDialog: () => import('@/components/dialogs/CreateStreamDialog'),
SettingsDialog: () => import('@/components/dialogs/SettingsDialog'),
GlobalToast: () => import('@/components/GlobalToast')
GlobalToast: () => import('@/components/GlobalToast'),
Mapper: () => import('@/components/Mapper')
},
props: {
size: {
@@ -155,7 +182,8 @@ export default {
createNewStreamDialog: false,
createStreamByIdDialog: false,
createStreamByIdText: "",
preferences: {}
preferences: {},
tab: "streams"
}
},
computed: {
@@ -219,3 +247,11 @@ export default {
}
}
</script>
<style>
/deep/ .v-toolbar__content {
padding: 0px !important;
}
</style>
+7
View File
@@ -34,6 +34,13 @@ export default {
}
},
mounted() {
this.$eventHub.$on('success', (args) => {
this.snack = true
this.color = "green"
this.text = args.text
this.actionName = args.action ? args.action.name : null
this.url = args.action ? args.action.url : null
})
this.$eventHub.$on('notification', (args) => {
this.snack = true
this.color = "primary"
+302
View File
@@ -0,0 +1,302 @@
<template>
<v-container class="pa-0">
<v-data-table
v-model="categorySelection"
class="elevation-1 mb-2"
dense
expand
disable-filtering
disable-pagination
hide-default-footer
hide-default-header
item-key="categoryName"
:expanded.sync="mappedElementsExpandedIndexes"
:headers="mappedElementsHeaders"
:items="mappedEntitiesTableData"
:mobile-breakpoint="0"
show-select
>
<template #header="{ props: { headers } }">
<thead class="v-data-table-header">
<tr>
<th
v-for="header in headers"
:key="header.value"
:width="header.width"
scope="col"
class="text-center"
>
{{ header.text }}
</th>
</tr>
</thead>
</template>
<template v-slot:expanded-item="{ headers, item }">
<td :colspan="headers.length" class="pl-2 pr-0">
<v-data-table
v-model="elementSelection[item.categoryName]['selectedElements']"
class="elevation-0 pa-0 ma-0 mb-2"
dense
disable-filtering
disable-pagination
hide-default-footer
hide-default-header
item-key="entityId"
:headers="subMappedElementsHeaders"
:items="elementSelection[item.categoryName]['allElements']"
:mobile-breakpoint="0"
show-select
>
<template #header="{ props: { headers } }">
<thead class="v-data-table-header">
<tr>
<th
v-for="header in headers"
:key="header.value"
:width="header.width"
scope="col"
class="text-center"
>
{{ header.text }}
</th>
</tr>
</thead>
</template>
<template #[`item.data-table-select`]="slotData">
<td class="mapped-elements-check-box-row">
<v-checkbox
class="shrink ma-0"
hide-details
:input-value="slotData.isSelected"
@click="slotData.select(clickMappedElements(slotData, item.categoryName))"
/>
</td>
</template>
</v-data-table>
</td>
</template>
<template #[`header.name`]="{ header }">
<th class="header-text-color">{{ header.text.toUpperCase() }}</th>
</template>
<template #[`item.categoryName`]="slotData">
<div @click="clickMappedElementsCategory(slotData)">{{ slotData.item.categoryName }}</div>
</template>
<template #[`item.data-table-select`]="slotData">
<v-checkbox
class="shrink ma-0"
hide-details
:input-value="slotData.isSelected"
@click="slotData.select(clickMappedCategory(slotData))"
/>
</template>
</v-data-table>
<v-container class="btn-container px-0">
<v-btn
v-tooltip="'Clear Mappings'"
x-small
min-width="30px"
min-height="30px"
@click="clearMappingsFromTableSelection"
>
<v-icon left dark>
mdi-delete
</v-icon>
Clear
</v-btn>
<v-spacer></v-spacer>
<v-btn
v-tooltip=" isIsolated ? 'Show All Elements' : 'Isolate Mapped Elements'"
x-small
min-width="30px"
min-height="30px"
class="mr-2"
@click="isolateMappedElementsOnSketchup"
>
<v-icon left dark>
{{ isIsolated ? 'mdi-crop-rotate' : 'mdi-crop'}}
</v-icon>
{{ isIsolated ? 'Show All' : 'Isolate'}}
</v-btn>
<v-btn
v-tooltip="'Hide Mapped Elements'"
x-small
min-width="30px"
min-height="30px"
class="mr-2"
@click="hideMappedElementsOnSketchup"
>
<v-icon left dark>
mdi-eye-off
</v-icon>
Hide
</v-btn>
<v-btn
v-tooltip="'Select Mapped Elements'"
x-small
min-width="30px"
min-height="30px"
@click="selectMappedElementsOnSketchup"
>
<v-icon left dark>
mdi-select-all
</v-icon>
Select
</v-btn>
</v-container>
</v-container>
</template>
<script>
/*global sketchup*/
import {bus} from "@/main";
import {groupBy} from "@/utils/groupBy";
export default {
name: "MappedElements",
data(){
return {
isIsolated: false,
elementSelection: {},
mappedEntities: [],
mappedEntityCount: 0,
// Expanded indexes for mapped element table (Categories)
mappedElementsExpandedIndexes: [],
mappedElementsHeaders: [
{ text: 'Category', sortable: false, align: 'center', value: 'categoryName', width: '70%' },
{ text: 'Count', sortable: false, align: 'center', value: 'count', width: '30%' }
],
subMappedElementsHeaders: [
{ text: 'Type', sortable: false, align: 'center', value: 'entityType', width: '70%' },
{ text: 'Name/Id', sortable: false, align: 'center', value: 'nameOrId', width: '30%' },
],
mappedEntitiesTableData: [],
}
},
computed: {
categorySelection() {
return Object.keys(this.elementSelection)
},
},
mounted() {
sketchup.exec({name: "collect_mapped_entities", data: {}})
bus.$on('mapped-entities-updated', async (mappedEntities) => {
this.mappedEntityCount = mappedEntities.length
this.mappedEntities = mappedEntities
this.getMappedElementsTableData()
})
},
methods: {
// categorySelection() {
// return Object.keys(this.elementSelection)
// },
clickMappedElementsCategory(slotData) {
const indexExpanded = this.mappedElementsExpandedIndexes.findIndex(i => i === slotData.item);
if (indexExpanded > -1) {
this.mappedElementsExpandedIndexes.splice(indexExpanded, 1)
} else {
this.mappedElementsExpandedIndexes.push(slotData.item);
}
},
clickMappedCategory(slotData){
const category = this.elementSelection[slotData.item.categoryName]
if (category['allSelected'] || category['selectedElements'].length === category['entityCount']) {
this.elementSelection[slotData.item.categoryName]['allSelected'] = false
this.elementSelection[slotData.item.categoryName]['selectedElements'] = []
} else {
this.elementSelection[slotData.item.categoryName]['allSelected'] = true
this.elementSelection[slotData.item.categoryName]['selectedElements'] = slotData.item.entities
}
},
clickMappedElements(slotData, category){
const elements = this.elementSelection[category]['selectedElements'] === undefined ? [] : this.elementSelection[category]['selectedElements']
const indexSelection = elements.findIndex(i => i['entityId'] === slotData.item['entityId']);
if (indexSelection > -1) {
elements.splice(indexSelection, 1)
} else {
elements.push(slotData.item);
}
this.elementSelection[category]['selectedElements'] = elements
// FIXME: This should be the ideal UX, but there is a problem with states currently.. Need to be fixed
// if (elements.length === 0){
// this.elementSelection[category]['allSelected'] = false
// }
},
clearMappingsFromTableSelection(){
sketchup.exec({ name: "clear_mappings_from_table", data: this.elementSelection })
},
isolateMappedElementsOnSketchup(){
if (this.isIsolated){
this.isIsolated = false
sketchup.exec({ name: "show_all_entities", data: {} })
} else {
this.isIsolated = true
sketchup.exec({ name: "isolate_mappings_from_table", data: this.elementSelection })
}
},
hideMappedElementsOnSketchup(){
sketchup.exec({ name: "hide_mappings_from_table", data: this.elementSelection })
},
selectMappedElementsOnSketchup(){
sketchup.exec({ name: "select_mappings_from_table", data: this.elementSelection })
},
getMappedElementsTableData(){
let groupByCategoryName = groupBy('categoryName')
let groupedByCategoryName = groupByCategoryName(this.mappedEntities)
this.mappedEntitiesTableData = Object.entries(groupedByCategoryName).map(
(entry) => {
const [categoryName, entities] = entry
this.elementSelection[categoryName] = {
allSelected: false,
entityCount: entities.length,
selectedElements: [],
allElements: entities.map((entity) => {
return {
'entityId': entity['entityId'],
'nameOrId': entity['name'] !== "" ? entity['name'] : entity['entityId'],
'entityType': entity['entityType']
}
})
}
return {
'categoryName': categoryName,
'count': entities !== true ? entities.length : 0,
'entities': entities.map((entity) => {
return {
'entityId': entity['entityId'],
'nameOrId': entity['name'] !== "" ? entity['name'] : entity['entityId'],
'entityType': entity['entityType']
}
})
}
}
)
},
}
}
</script>
<style scoped>
.btn-container{
display: flex;
flex-wrap: wrap;
}
.mapped-elements-check-box-row {
width: 20px;
}
/* This is the header styles of child table */
.v-data-table--dense > .v-data-table__wrapper > table > thead > tr > th {
height: 32px;
}
</style>
+571
View File
@@ -0,0 +1,571 @@
<template>
<v-container fluid class="px-3 btn-container">
<v-expansion-panels
v-model="panel"
accordion
multiple
>
<v-expansion-panel key="selection">
<v-expansion-panel-header>
<div>
<v-icon>
{{ selectedEntityCount === 0 ? 'mdi-playlist-remove' : 'mdi-playlist-check' }}
</v-icon>
{{ `Selection (${selectedEntityCount})` }}
</div>
</v-expansion-panel-header>
<v-expansion-panel-content class="mx-n3">
<v-data-table
class="elevation-1"
dense
expand
disable-filtering
disable-pagination
hide-default-footer
item-key="entityType"
:expanded.sync="selectionExpandedIndexes"
:headers="selectionHeaders"
:items="selectionTableData"
:mobile-breakpoint="0"
>
<template v-slot:expanded-item="{ headers, item }">
<td :colspan="headers.length" class="pl-2 pr-0">
<v-data-table
class="elevation-0 pa-0 ma-0"
dense
disable-filtering
disable-pagination
hide-default-footer
item-key="entityId"
:headers="subSelectionHeaders"
:items="item.entities"
:mobile-breakpoint="0"
>
<template v-slot:item.isMapped="{ item }">
<v-icon :color="item.isMapped ? 'green' : 'red'">
{{ item.isMapped ? 'mdi-check-circle' : 'mdi-close-circle' }}
</v-icon>
</template>
</v-data-table>
</td>
</template>
<template v-slot:item.entityType="slotData">
<div @click="clickSelectionColumn(slotData)">{{ slotData.item.entityType }}</div>
</template>
</v-data-table>
</v-expansion-panel-content>
</v-expansion-panel>
<v-expansion-panel key="mapping">
<v-expansion-panel-header>
<div>
<v-icon>
mdi-multiplication
</v-icon>
{{ `Mapping` }}
</div>
</v-expansion-panel-header>
<v-expansion-panel-content>
<v-container v-if="entitySelected" class="btn-container pa-0 mb-5">
<v-card
variant="outlined"
class="pt-0 pl-2 mb-1 mr-2 flex"
:elevation="entityCardElevation"
:outlined="!definitionSelected"
:width="entityCardWidth"
min-width="150px"
@click="definitionSelectedHandler(false)"
>
<v-card-title class="pa-0 pb-2">
<v-icon class="mr-1 v-bottom-navigation--absolute">
{{ getSelectedEntityIcon }}
</v-icon>
{{getSelectedEntityText}}
<v-spacer></v-spacer>
<v-icon v-if="entityMapped" class="mr-n2 mt-n6" color="green">
mdi-checkbox-marked-circle
</v-icon>
</v-card-title>
<v-card-subtitle class="pb-0 pr-0 font-weight-light">
{{getSelectedEntitySubText}}
</v-card-subtitle>
</v-card>
<v-card
v-if=selectedEntitiesHasParent
variant="outlined"
class="pt-0 pl-2 mb-1 mr-2 flex"
:elevation="definitionSelected ? '6' : '1'"
:outlined="definitionSelected"
:width="entityCardWidth"
min-width="150px"
@click="definitionSelectedHandler(true)"
>
<v-card-title class="pa-0 pb-2">
<v-icon class="mr-1">
mdi-atom
</v-icon>
{{ getSelectedDefinitionText }}
<v-spacer></v-spacer>
<v-icon v-if="definitionMapped" class="mr-n2 mt-n6" color="green">
mdi-checkbox-marked-circle
</v-icon>
</v-card-title>
<v-card-subtitle class="pb-0 pr-0 font-weight-light">
{{getSelectedDefinitionSubText}}
</v-card-subtitle>
</v-card>
</v-container>
<v-autocomplete
v-model="selectedMethod"
class="pt-0"
label="Mapper Method"
:disabled="!entitySelected"
:items="enabledMethods"
density="compact"
clearable
></v-autocomplete>
<v-autocomplete
v-model="selectedCategory"
class="pt-0"
label="Category"
:items="availableCategories"
item-value="value"
item-text="key"
:disabled="!entitySelected"
density="compact"
clearable
></v-autocomplete>
<v-text-field
v-model="name"
class="pt-0"
label="Name"
:disabled="!entitySelected"
clearable
></v-text-field>
<v-container class="pa-0">
<v-row justify="center" align="center">
<v-col cols="auto" class="pa-1 pb-2">
<v-btn
:disabled="!entitySelected"
@click="applyMapping"
>
<v-icon dark left>
mdi-checkbox-marked-circle
</v-icon>Apply
</v-btn>
</v-col>
<v-col cols="auto" class="pa-1 pb-2">
<v-btn
:disabled="!entitySelected"
@click="clearMapping"
>
<v-icon dark left>
mdi-close-circle
</v-icon>Clear
</v-btn>
</v-col>
</v-row>
</v-container>
</v-expansion-panel-content>
</v-expansion-panel>
<v-expansion-panel key="mappedElements">
<v-expansion-panel-header>
<div>
<v-icon>
{{ mappedEntityCount === 0 ? 'mdi-playlist-remove' : 'mdi-playlist-check' }}
</v-icon>
{{ `Mapped Elements (${mappedEntityCount})` }}
</div>
</v-expansion-panel-header>
<v-expansion-panel-content class="mx-n3">
<mapped-elements/>
</v-expansion-panel-content>
</v-expansion-panel>
</v-expansion-panels>
<global-toast />
</v-container>
</template>
<script>
/*global sketchup*/
import {bus} from "@/main";
import {groupBy} from "@/utils/groupBy";
global.entitySelected = function (selectionParameters) {
bus.$emit('entities-selected', JSON.stringify(selectionParameters))
}
global.entitiesDeselected = function () {
bus.$emit('entities-deselected')
}
global.mappedEntitiesUpdated = function (mappedEntities) {
bus.$emit('mapped-entities-updated', mappedEntities)
}
export default {
name: "Mapper",
components: {
GlobalToast: () => import('@/components/GlobalToast'),
MappedElements: () => import('@/components/MappedElements.vue')
},
data() {
return {
// Expanded indexes for selection table (Types)
selectionExpandedIndexes: [],
// Expanded indexes for mapped element table (Categories)
mappedElementsExpandedIndexes: [],
// Whether definition card is selected to map or not.
definitionSelected: false,
// Initial entity (Group, Component, Face, Edge) that mapped or not
entityMapped: false,
// Definition of entity is mapped, it will be available for only Components.
definitionMapped: false,
// Whether an entity is selected or not.
entitySelected: false,
selectedEntityCount: 0,
selectedEntities: [],
lastSelectedEntity: null,
selectedMethod: null,
selectedCategory: null,
name: "",
enabledMethods: [],
availableCategories: [],
mappedEntityCount: 0,
mappedEntities: [],
panel: [1],
selectionHeaders: [
{ text: 'Type', sortable: false, value: 'entityType', width: '60%' },
{ text: 'Count', sortable: false, align: 'center', value: 'count', width: '20%' },
{ text: 'Mapped', sortable: false, align: 'center', value: 'mappedCount', width: '20%' },
],
subSelectionHeaders: [
{ text: 'Name/Id', sortable: false, value: 'nameOrId', width: '80%' },
{ text: 'Mapped', sortable: false, align: 'center', value: 'isMapped', width: '20%' },
],
mappedElementsHeaders: [
{ text: 'Category', sortable: false, value: 'categoryName', width: '80%' },
{ text: 'Count', sortable: false, align: 'center', value: 'count', width: '20%' }
],
subMappedElementsHeaders: [
{ text: 'Type', sortable: false, value: 'entityType', width: '80%' },
{ text: 'Name/Id', sortable: false, align: 'center', value: 'nameOrId', width: '20%' },
],
selectionTableData: [],
mappedEntitiesTableData: [],
}
},
computed:{
lastSelectedEntityHasParent(){
return this.lastSelectedEntity['entityType'] === 'Component'
},
selectedEntitiesHasParent(){
return this.selectedEntities.every((entity) => entity['entityType'] === 'Component')
},
entityCardWidth(){
if (this.lastSelectedEntityHasParent){
return '150px'
} else {
return '310px'
}
},
entityCardElevation(){
if (!this.lastSelectedEntityHasParent){
return '1'
}
return this.definitionSelected ? '1' : '6'
},
entityCardColor(){
if (!this.lastSelectedEntityHasParent){
return 'background2'
}
return this.definitionSelected ? 'background2' : 'mappingEntity'
},
getSelectedEntityIcon(){
if (this.selectedEntities.length > 1){
return 'mdi-webpack'
}else{
const type = this.lastSelectedEntity['entityType']
if (type === 'Face'){
return 'mdi-vector-square'
} else if (type === 'Edge'){
return 'mdi-vector-polyline'
} else if (type === 'Group'){
return 'mdi-border-outside'
} else if (type === 'Component'){
return 'mdi-border-inside'
} else {
return 'mdi-close'
}
}
},
getSelectedEntityText(){
if (this.selectedEntities.length > 1){
if (this.selectedEntitiesHasParent){
return 'Component'
}
return 'Multiple Selection'
}else{
return this.lastSelectedEntity["entityType"]
}
},
getSelectedDefinitionText(){
if (this.selectedEntities.length > 1 && this.selectedEntitiesHasParent){
return 'Definition'
}else{
return 'Definition'
}
},
getSelectedEntitySubText(){
if (this.selectedEntities.length > 1){
return this.getSelectionSummary()
}else{
return 'Single selected entity'
}
},
getSelectedDefinitionSubText(){
if (this.selectedEntities.length > 1){
let instances = 0
let registeredDefinitions = []
this.selectedEntities.forEach((entity) => {
if (!registeredDefinitions.includes(entity['definition']['entityId'])){
instances += entity['definition']['numberOfInstances']
registeredDefinitions.push(entity['definition']['entityId'])
}
})
return `Instances (${instances})`
}else{
return `Instances (${this.lastSelectedEntity['definition']['numberOfInstances']})`
}
}
},
methods:{
clearInputs(){
this.enabledMethods = []
this.availableCategories = []
this.selectedEntities = []
this.selectionTableData = []
this.selectedEntityCount = 0
this.name = ""
this.selectedMethod = null
this.selectedCategory = null
this.entityMapped = false
this.definitionMapped = false
},
getSelectionTableData(){
let groupByClass = groupBy('entityType')
let groupedByWithKey = groupByClass(this.selectedEntities)
this.selectionTableData = Object.entries(groupedByWithKey).map(
(entry) => {
const [className, entities] = entry
return {
'entityType': className,
'entityIds': entities.map(entity => entity['entityId']),
'count': entities !== true ? entities.length : 0,
'entities': entities.map((entity) => {
return {
'entityId': entity['entityId'],
'nameOrId': entity['name'] !== null ? entity['name'] : entity['entityId'],
'isMapped': this.isEntityMapped(entity) || this.isEntityDefinitionMapped(entity)
}
}),
'mappedCount': entities.filter((entity) => entity['schema']['category'] !== undefined).length
}
}
)
},
getSelectionSummary(){
let groupByClass = groupBy('entityType')
let groupedByWithKey = groupByClass(this.selectedEntities)
let summary = ''
Object.entries(groupedByWithKey).forEach((entry, index) => {
const [className, entities] = entry
const entityType = className === 'Component' ? 'Instance' : className
summary += `${entityType}s (${entities.length})`
if (index !== Object.entries(groupedByWithKey).length - 1){
summary += ' - '
}
})
return summary
},
setInputValuesFromSelection(){
if (!this.entitySelected){
this.name = ""
this.selectedMethod = null
this.selectedCategory = null
return
}
if (this.definitionSelected) {
if (!this.definitionMapped){
if (this.selectedEntityCount > 1){
this.name = '<Mixed>'
}else{
this.name = this.lastSelectedEntity['definition']['entityName']
}
this.selectedMethod = 'Direct Shape'
this.selectedCategory = 49
} else {
if (this.selectedEntityCount > 1){
this.name = '<Mixed>'
}else{
this.name = this.lastSelectedEntity['definition']['schema']['name']
}
this.selectedMethod = this.lastSelectedEntity['definition']['schema']['method']
this.selectedCategory = this.lastSelectedEntity['definition']['schema']['category']
}
} else {
if (!this.entityMapped){
if (this.selectedEntityCount > 1){
this.name = '<Mixed>'
}else{
this.name = this.lastSelectedEntity['entityName']
}
this.selectedMethod = 'Direct Shape'
this.selectedCategory = 49
} else {
if (this.selectedEntityCount > 1){
this.name = '<Mixed>'
}else{
this.name = this.lastSelectedEntity['schema']['name']
}
this.selectedMethod = this.lastSelectedEntity['schema']['method']
this.selectedCategory = this.lastSelectedEntity['schema']['category']
}
}
},
isEntityMapped(entity){
return entity['schema']['category'] !== undefined
},
isEntitiesMapped(entities){
return entities.every((entity) => this.isEntityMapped(entity))
},
isEntityDefinitionMapped(entity){
if (entity['definition'] === undefined){
return false
}
return entity['definition']['schema']['category'] !== undefined
},
isEntityDefinitionsMapped(entities){
return entities.every((entity) => this.isEntityDefinitionMapped(entity))
},
definitionSelectedHandler(state){
this.definitionSelected = state
this.setInputValuesFromSelection()
},
clickSelectionColumn(slotData) {
const indexExpanded = this.selectionExpandedIndexes.findIndex(i => i === slotData.item);
if (indexExpanded > -1) {
this.selectionExpandedIndexes.splice(indexExpanded, 1)
} else {
this.selectionExpandedIndexes.push(slotData.item);
}
},
clickMappedElementsColumn(slotData) {
const indexExpanded = this.mappedElementsExpandedIndexes.findIndex(i => i === slotData.item);
if (indexExpanded > -1) {
this.mappedElementsExpandedIndexes.splice(indexExpanded, 1)
} else {
this.mappedElementsExpandedIndexes.push(slotData.item);
}
},
applyMapping(){
if (this.selectedMethod === null || this.selectedCategory === null){
this.$eventHub.$emit('error', {
text: 'Method and category are not set.\n'
})
return
}
const mapping = {
entitiesToMap: this.selectedEntities.map((entity) => entity['entityId']),
method: this.selectedMethod,
category: this.selectedCategory,
name: this.name,
isDefinition: this.definitionSelected
}
sketchup.exec({name: "apply_mappings", data: mapping})
this.$eventHub.$emit('success', {
text: 'Mapping Applied.\n'
})
},
clearMapping(){
const mapping = {
entitiesToClearMap: this.selectedEntities.map((entity) => entity['entityId']),
isDefinition: this.definitionSelected
}
sketchup.exec({name: "clear_mappings", data: mapping})
this.clearInputs()
this.$eventHub.$emit('error', {
text: 'Mapping Cleared.\n'
})
}
},
mounted() {
sketchup.exec({name: "collect_mapped_entities", data: {}})
bus.$on('entities-selected', async (selectionParameters) => {
const selectionPars = JSON.parse(selectionParameters)
this.enabledMethods = selectionPars.mappingMethods
this.availableCategories = selectionPars.categories
this.selectedEntities = selectionPars.selection
this.lastSelectedEntity = this.selectedEntities[this.selectedEntities.length - 1]
this.entityMapped = this.isEntitiesMapped(this.selectedEntities)
this.definitionMapped = this.isEntityDefinitionsMapped(this.selectedEntities)
this.definitionSelected = !this.entityMapped && this.definitionMapped
this.selectedEntityCount = this.selectedEntities.length
this.entitySelected = this.selectedEntityCount !== 0
this.getSelectionTableData()
this.setInputValuesFromSelection()
})
bus.$on('entities-deselected', async () => {
this.entitySelected = false
this.clearInputs()
})
bus.$on('mapped-entities-updated', async (mappedEntities) => {
this.mappedEntityCount = mappedEntities.length
})
}
}
</script>
<style scoped>
.btn-container{
display: flex;
flex-wrap: wrap;
}
.active .entity {
border: 2px solid green;
}
.v-card__title{
font-size: 1.02rem;
}
.v-card__subtitle{
font-size: 0.78rem;
}
.v-expansion-panel--active > .v-expansion-panel-header{
min-height: 32px;
}
.v-expansion-panel-header{
min-height: 32px;
padding: 12px 16px;
}
</style>
@@ -1,13 +1,13 @@
<template>
<v-container fluid class="px-1 pb-0 pt-1">
<v-container fluid class="px-1 pb-0 pt-0">
<v-row>
<v-col>
<v-col class="py-2">
<!-- DIALOG: Create New Stream -->
<v-dialog v-model="showCreateNewStream">
<template #activator="{ on, attrs }">
<v-btn
class="ma-2 pa-3"
x-small
small
v-bind="attrs"
v-on="on"
>
@@ -96,7 +96,7 @@
<template #activator="{ on, attrs }">
<v-btn
class="ma-2 pa-3"
x-small
small
min-width="163"
v-bind="attrs"
v-on="on"
+2
View File
@@ -11,6 +11,7 @@ export default new Vuetify({
primary: '#0480FB', //speckle blue
secondary: '#01D1FD', //spark blue
accent: '#46B958', //data yellow
mappingEntity: '#B9d8b5',
error: '#FF5555', //red
warning: '#D8BF16', //lightning yellow
info: '#2D2ADA', //majestic blue
@@ -24,6 +25,7 @@ export default new Vuetify({
primary: '#0480FB', //speckle blue
secondary: '#01D1FD', //spark blue
accent: '#46B958', //data yellow
mappingEntity: '#46B958',
error: '#FF5555', //red
warning: '#D8BF16', //lightning yellow
info: '#2D2ADA', //majestic blue
+20
View File
@@ -119,3 +119,23 @@
opacity: 1;
transition: opacity 0.15s;
}
/* SCROOL BAR */
::-webkit-scrollbar {
width: 12px;
}
::-webkit-scrollbar-track {
background: #f3f3f3;
border-left: 1px solid #dadada;
}
::-webkit-scrollbar-thumb {
background: #b0b0b0;
border: solid 3px #e6e6e6;
border-radius: 7px;
}
::-webkit-scrollbar-thumb:hover {
background: black;
}
+13
View File
@@ -0,0 +1,13 @@
/*
* Group by function definition to call it on demand.
* @example
* const groupByBrand = groupBy('brand')
* const groupedByBrands1 = groupByBrand(array1)
* const groupedByBrands2 = groupByBrand(array2)
*/
export const groupBy = key => array =>
array.reduce((objectsByKeyValue, obj) => {
const value = obj[key];
objectsByKeyValue[value] = (objectsByKeyValue[value] || []).concat(obj);
return objectsByKeyValue;
}, {});