Compare commits

...

91 Commits

Author SHA1 Message Date
Oğuzhan Koral 6053d3eac1 Fix (Attributes): Rescue from problematic dictionaries 2023-01-20 11:50:18 +03:00
oguzhankoral 529830f36b Rescue from problematic dictionaries 2023-01-20 11:47:34 +03:00
Oğuzhan Koral 505cf6265c Feat (Scene): Send and receive scenes 2023-01-19 20:09:19 +03:00
oguzhankoral 8cd9673eec Send scenes to speckle 2023-01-19 20:05:44 +03:00
oguzhankoral ac9cb28558 Receive named views as scene 2023-01-19 16:38:32 +03:00
Oğuzhan Koral 6eefe0698c Fix (Branch): Activate created branch 2023-01-19 12:17:58 +03:00
oguzhankoral d0113532b6 Activate created branch 2023-01-19 11:09:23 +03:00
Oğuzhan Koral 5a1d2ad5f4 Feat (Branch): Create branch button
Thanks Fabians for helps
2023-01-18 17:02:20 +03:00
oguzhankoral dfe02f4c74 Rename CreateBranchDialog 2023-01-18 16:58:05 +03:00
oguzhankoral 5349d556b9 Group dialogs to folder 2023-01-18 16:21:43 +03:00
oguzhankoral 7f7d8a501b Add notes about refresh 2023-01-18 15:58:43 +03:00
oguzhankoral cbee0e465b Remove unused combineFacesByMaterialHandler method 2023-01-18 15:44:20 +03:00
oguzhankoral faf00f0b0d Refetch stream on StreamCard whenever branch created 2023-01-18 15:42:33 +03:00
oguzhankoral fa97da5781 Track create stream and branch via mixpanel 2023-01-18 15:03:14 +03:00
oguzhankoral e7bab546db Remove loggings 2023-01-18 14:16:52 +03:00
oguzhankoral e2f4a30b5b Fix updating problem on created branch
- Thanks to Fabians
2023-01-18 11:53:50 +03:00
oguzhankoral ccff1df041 Add tooltip to create branch method 2023-01-18 11:53:11 +03:00
oguzhankoral 2037cfc25a Import and use CreateBranchDialog per stream card 2023-01-17 19:24:53 +03:00
oguzhankoral 268a091d8a Add dialog for branch creation 2023-01-17 19:23:44 +03:00
Oğuzhan Koral 6b52dfab3e Chore (Theming): Store light mode setting separately 2023-01-17 16:16:39 +03:00
oguzhankoral 839999851f Correct preference hash for theming 2023-01-17 16:12:21 +03:00
oguzhankoral a87470b7b5 Split Sketchup settings 2023-01-17 16:11:56 +03:00
Oğuzhan Koral e76aeb80fd Fix (attributes): Use from_face method to face consider attributes 2023-01-11 12:05:47 +03:00
oguzhankoral 28292e59e2 Use from_face method to consider attributes 2023-01-11 11:59:50 +03:00
Oğuzhan Koral 25dda481b2 Comment out vertex count log 2023-01-10 16:47:42 +03:00
Oğuzhan Koral bbda233fd8 Comment out vertex count log 2023-01-10 16:46:56 +03:00
Oğuzhan Koral 349218f0b5 Feat (Mesh): mesh improvements
Mesh grouping methods are improved with options:

Shared vertices (It is not supported by viewer currently, but when available it is ready to approach)
Separated vertices
2023-01-10 14:36:11 +03:00
oguzhankoral f18d00a69d Remove disable rubocop issues 2023-01-10 14:30:28 +03:00
oguzhankoral 25ea6504de Note about when viewer supports shared vertices 2023-01-10 11:33:56 +03:00
oguzhankoral 43081c70e2 Send vertices separately 2023-01-10 10:51:03 +03:00
oguzhankoral 0fde1c2026 Optimize meshes with dynamic vertex adding 2023-01-10 09:14:11 +03:00
Oğuzhan Koral b35383571e Merge pull request #126 from specklesystems/gergo/updateCiContext
use innosetup context in the windows build
2023-01-09 14:57:18 +03:00
Oğuzhan Koral 45a84847a2 Fix (block): Base point for block definition 2023-01-08 17:41:56 +03:00
oguzhankoral 70d92f26d6 Note for reason to having block definition base points 2023-01-08 17:40:57 +03:00
oguzhankoral 737ed86e69 Comparison method for point object 2023-01-08 15:15:17 +03:00
oguzhankoral 3865057b7a Fallback geometry for block definition 2023-01-08 14:50:47 +03:00
oguzhankoral 42a84dcd86 Receive blocks from rhino 2023-01-07 00:35:02 +03:00
Gergő Jedlicska e2d819c59d Merge branch 'main' of github.com:specklesystems/speckle-sketchup into gergo/updateCiContext 2023-01-06 14:30:16 +01:00
oguzhankoral bfee6a88dc Add base point for block definition 2023-01-06 16:28:32 +03:00
Oğuzhan Koral 68f3be17df Fix (UI): closing UI cause state loss 2023-01-06 16:25:52 +03:00
oguzhankoral 929c97ff5e Bring to front dialog if it is minimized when user reclicked UI button 2023-01-06 16:20:53 +03:00
oguzhankoral 4b66a2e4d0 Reset dialog if it's closed 2023-01-06 16:20:32 +03:00
Gergő Jedlicska 46e740154e use innosetup context in the windows build 2023-01-06 14:03:50 +01:00
Oğuzhan Koral 05e89f49da Feat (attributes): Send/receive entity attributes 2023-01-06 04:39:47 +03:00
oguzhankoral 358e9071e3 Disable nested groups for now 2023-01-06 04:36:43 +03:00
oguzhankoral e37b6a1cc0 Fix receive groups as group
Previously groups were receiving as component
2023-01-06 03:45:21 +03:00
oguzhankoral 266721973b Check block instance and definition's sketchup_attributes 2023-01-06 03:17:45 +03:00
oguzhankoral 7c27ac85cb Check having sketchup_attributes already in line and mesh before apply dicts 2023-01-06 02:36:06 +03:00
oguzhankoral 4b79732e38 Comment out UI dev mode 2023-01-06 01:51:45 +03:00
oguzhankoral 2ceeea5298 Send attributes according to model preference 2023-01-06 01:41:41 +03:00
oguzhankoral 3ec659a59b Write definition dictionaries on receive 2023-01-06 01:22:58 +03:00
oguzhankoral 4309056851 Return if dictionaries nil 2023-01-06 01:22:58 +03:00
oguzhankoral b768f20f7a Include entity attributes on send/receive 2023-01-06 01:22:58 +03:00
oguzhankoral b3a71bcf53 Stage sqlite3 2 2023-01-06 01:22:58 +03:00
oguzhankoral 50c199bc03 Stage sqlite3 2023-01-06 01:22:58 +03:00
Oğuzhan Koral d6302ac128 Feat (settings): Settings dialog implemented 2023-01-06 01:22:36 +03:00
oguzhankoral 6a5d9e1394 Fix rubocop issues 2023-01-05 22:56:53 +03:00
oguzhankoral ac5fc3e6ea Improve positioning for switches 2023-01-05 22:50:18 +03:00
oguzhankoral aa6cbceeb9 Consider strategies on send/receive 2023-01-05 22:29:56 +03:00
oguzhankoral 46a7395382 Sync speckle user_state with UI 2023-01-05 22:29:56 +03:00
oguzhankoral a782811dad Assign preferences to data of App 2023-01-05 22:29:56 +03:00
oguzhankoral d22039bc96 Get rid of storing theming on localStorage
It stores now on database and sync with it
2023-01-05 22:29:56 +03:00
oguzhankoral 15539c258e Update config.db when theme has changed 2023-01-05 22:29:56 +03:00
oguzhankoral f9ca4acf16 Add unit tests for sqlite3 2023-01-05 22:29:56 +03:00
oguzhankoral 66d2a9b7fe Update sqlite3 submodule reference hash 2023-01-05 22:29:56 +03:00
oguzhankoral 6dff8c3221 Create test.db file for unit tests 2023-01-05 22:29:56 +03:00
oguzhankoral f13c65e083 Update sqlite3_27.so with read/write database 2023-01-05 22:29:56 +03:00
oguzhankoral 56a7d5cb86 Move theme to settings 2023-01-05 22:29:56 +03:00
oguzhankoral c63c0675d5 Init settings dialog 2023-01-05 22:29:56 +03:00
oguzhankoral 22bc4b8c9e Log upload time 2023-01-05 22:29:56 +03:00
Gergő Jedlicska ead17b8906 Merge pull request #122 from specklesystems/gergo/updateCiContext
add CI context reference to deploy job
2023-01-05 18:21:52 +01:00
Gergő Jedlicska 1a211daac2 make sure dir 2023-01-05 17:58:52 +01:00
Gergő Jedlicska c7e502da4e remove gh bot context 2023-01-05 17:56:53 +01:00
Gergő Jedlicska 70df5e6cec how touching 2023-01-05 17:55:56 +01:00
Gergő Jedlicska 793f287c35 make sure to create the known hosts 2023-01-05 17:53:46 +01:00
Gergő Jedlicska 8e8b1c60b8 use ssh cloning 2023-01-05 17:51:58 +01:00
Gergő Jedlicska ba2cd51852 rename gh token env var 2023-01-05 17:05:53 +01:00
Gergő Jedlicska 3986a4ef60 add gh devbot context 2023-01-05 17:03:38 +01:00
Gergő Jedlicska c99d89fb11 add CI context reference to deploy job 2023-01-05 16:59:23 +01:00
Oğuzhan Koral 0a9c33de91 Fix (Material): Send back material as fallback for front 2023-01-03 22:01:13 +03:00
oguzhankoral 88c940fc53 Send back material as fallback for front 2023-01-03 21:59:58 +03:00
Oğuzhan Koral 88c861cbde Fix (group): Add missing subgroup conversions 2023-01-03 21:19:44 +03:00
oguzhankoral cd071ca144 Fix missing group conversions 2023-01-03 21:17:37 +03:00
Oğuzhan Koral 9970b8ec36 Feat (Component): Store always face camera option for definitions 2023-01-02 22:24:58 +03:00
oguzhankoral ffc564becd Store always face camera option for defitions 2023-01-02 22:22:01 +03:00
Oğuzhan Koral 6289fd5941 Fix (blocks): Remove bbox and base_point from block objects 2023-01-02 18:05:12 +03:00
oguzhankoral 7f44fe76c7 Remove bbox and base_point from block objects 2023-01-02 18:03:50 +03:00
Oğuzhan Koral ea86dc6785 Fix (edge): Check all definition entities has any orphan edge 2023-01-02 15:56:33 +03:00
oguzhankoral 754f9e1ed1 Check all definition entities has any orphan edge 2023-01-02 15:51:54 +03:00
Oğuzhan Koral 9a1a02e664 Fix (accounts): Disable streams when there are no account and show message 2022-12-21 11:31:18 +03:00
oguzhankoral 1e92195355 Disable streams when there are no account and show message 2022-12-21 11:24:11 +03:00
40 changed files with 1257 additions and 209 deletions
+11 -1
View File
@@ -58,9 +58,17 @@ jobs:
docker:
- image: cimg/base:2021.01
steps:
- add_ssh_keys:
fingerprints:
- "03:2e:ee:4f:14:67:2b:88:32:e8:cc:f0:cb:df:92:29"
- run:
name: I know Github as a host
command: |
mkdir ~/.ssh
ssh-keyscan github.com >> ~/.ssh/known_hosts
- run:
name: Clone
command: git clone https://$GITHUB_TOKEN@github.com/specklesystems/speckle-sharp-ci-tools.git speckle-sharp-ci-tools
command: git clone git@github.com:specklesystems/speckle-sharp-ci-tools.git speckle-sharp-ci-tools
- persist_to_workspace:
root: ./
paths:
@@ -107,6 +115,7 @@ workflows:
only: /.*/
- build-connector:
context: innosetup
slug: sketchup
requires:
- get-ci-tools
@@ -116,6 +125,7 @@ workflows:
only: /.*/
- deploy-manager2:
context: do-spaces-speckle-releases
slug: sketchup
os: Win
extension: exe
@@ -0,0 +1,18 @@
# frozen_string_literal: true
require_relative 'action'
require_relative '../ext/sqlite3'
require_relative '../constants/path_constants'
module SpeckleConnector
module Actions
# Action to collect preferences from database to UI.
class CollectPreferences < 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)
state.with_add_queue('collectPreferences', state.user_state.preferences.to_json, [])
end
end
end
end
@@ -12,8 +12,8 @@ module SpeckleConnector
# @return [States::State] the new updated state object
def self.update_state(state, _data)
puts 'Initialisation of Speckle accounts requested by plugin'
accounts_data = Accounts.load_accounts.to_json
state.with_add_queue('loadAccounts', accounts_data, [])
accounts_data = state.speckle_state.accounts
state.with_add_queue('loadAccounts', accounts_data.to_json, [])
end
end
end
@@ -5,6 +5,7 @@ require_relative '../states/state'
require_relative '../states/speckle_state'
require_relative '../states/sketchup_state'
require_relative '../accounts/accounts'
require_relative '../preferences/preferences'
module SpeckleConnector
module Actions
@@ -13,11 +14,13 @@ module SpeckleConnector
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def self.update_state(state)
accounts = SpeckleConnector::Accounts.load_accounts.to_json
accounts = SpeckleConnector::Accounts.load_accounts
speckle_state = States::SpeckleState.new(accounts, {}, {})
# This should be the only point that `Sketchup_active_model` passed to application state.
sketchup_state = States::SketchupState.new(Sketchup.active_model)
States::State.new(state.user_state, speckle_state, sketchup_state, false)
preferences = Preferences.init_preferences(sketchup_state.sketchup_model)
user_state_with_preferences = state.user_state.with_preferences(preferences)
States::State.new(user_state_with_preferences, speckle_state, sketchup_state, false)
end
end
end
@@ -0,0 +1,36 @@
# frozen_string_literal: true
require_relative 'action'
require_relative '../ext/sqlite3'
require_relative '../accounts/accounts'
require_relative '../constants/path_constants'
require_relative '../sketchup_model/dictionary/speckle_model_dictionary_handler'
module SpeckleConnector
module Actions
# When preference updated by UI.
class ModelPreferencesUpdated < Action
def initialize(pref, value)
super()
@preference = pref
@value = value
end
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def update_state(state)
model = state.user_state.preferences[:model].dup
model[@preference.to_sym] = @value
new_preferences = state.user_state.preferences.put(:model, model)
SketchupModel::Dictionary::SpeckleModelDictionaryHandler.set_attribute(
state.sketchup_state.sketchup_model,
@preference.to_sym,
@value,
'Speckle'
)
new_user_state = state.user_state.with_preferences(new_preferences)
state.with_user_state(new_user_state)
end
end
end
end
@@ -23,7 +23,7 @@ module SpeckleConnector
converter = Converters::ToNative.new(state.sketchup_state.sketchup_model)
# Have side effects on the sketchup model. It effects directly on the entities by adding new objects.
start_time = Time.now.to_f
converter.receive_commit_object(@base)
converter.receive_commit_object(@base, state.user_state.preferences[:model])
elapsed_time = (Time.now.to_f - start_time).round(3)
puts "==== Converting to Native executed in #{elapsed_time} sec ===="
state.with_add_queue('finishedReceiveInSketchup', @stream_id, [])
@@ -12,8 +12,10 @@ module SpeckleConnector
# @return [States::State] the new updated state object
def self.update_state(state, _data)
puts 'Reload of Speckle accounts requested by plugin'
accounts_data = Accounts.load_accounts.to_json
state.with_add_queue('loadAccounts', accounts_data, [])
new_speckle_state = state.speckle_state.with_accounts(Accounts.load_accounts)
state = state.with_speckle_state(new_speckle_state)
accounts_data = state.speckle_state.accounts
state.with_add_queue('loadAccounts', accounts_data.to_json, [])
end
end
end
@@ -18,7 +18,7 @@ module SpeckleConnector
def update_state(state)
sketchup_model = state.sketchup_state.sketchup_model
converter = Converters::ToSpeckle.new(sketchup_model)
base = converter.convert_selection_to_base
base = converter.convert_selection_to_base(state.user_state.preferences)
id, total_children_count, batches = converter.send_info(base)
puts("converted #{base.count} objects for stream #{@stream_id}")
state.with_add_queue('convertedFromSketchup', @stream_id, [
@@ -0,0 +1,53 @@
# frozen_string_literal: true
require_relative 'action'
require_relative '../ext/sqlite3'
require_relative '../accounts/accounts'
require_relative '../constants/path_constants'
module SpeckleConnector
module Actions
# When preference updated by UI.
class UserPreferencesUpdated < Action
def initialize(pref_hash, pref, value)
super()
@preference_hash = pref_hash
@preference = pref
@value = value
end
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def update_state(state)
# Init sqlite database
db = Sqlite3::Database.new(SPECKLE_CONFIG_DB_PATH)
# Select data
data = db.exec("SELECT content FROM 'objects' WHERE hash = '#{@preference_hash}'").first.first
# Parse string to hash
data_hash = JSON.parse(data).to_h
# Get current preference value
old_preference_value = data_hash[@preference]
# Return old state if it is equal to new one
return state if @value == old_preference_value
data_hash[@preference] = @value
# Update entry unless equal old to new
db.exec("UPDATE 'objects' SET content = '#{data_hash.to_json}' WHERE hash = '#{@preference_hash}'")
# Close db when process done
db.close
user = state.user_state.preferences[:user].dup
user[@preference.to_sym] = @value
new_preferences = state.user_state.preferences.put(:user, user)
new_user_state = state.user_state.with_preferences(new_preferences)
state.with_user_state(new_user_state)
end
end
end
end
@@ -0,0 +1,18 @@
# frozen_string_literal: true
require_relative 'command'
require_relative '../accounts/accounts'
require_relative '../actions/model_preference_updated'
module SpeckleConnector
module Commands
# Command to update theme.
class ModelPreferencesUpdated < Command
def _run(data)
preference = data['preference']
new_value = data['value']
app.update_state!(Actions::ModelPreferencesUpdated.new(preference, new_value))
end
end
end
end
@@ -0,0 +1,19 @@
# frozen_string_literal: true
require_relative 'command'
require_relative '../accounts/accounts'
require_relative '../actions/user_preferences_updated'
module SpeckleConnector
module Commands
# Command to update preferences.
class UserPreferencesUpdated < Command
def _run(data)
preference_hash = data['preference_hash']
preference = data['preference']
new_value = data['value']
app.update_state!(Actions::UserPreferencesUpdated.new(preference_hash, preference, new_value))
end
end
end
end
@@ -18,4 +18,6 @@ module SpeckleConnector
raise 'Speckle could not determine your Appdata path'
end
SPECKLE_ACCOUNTS_DB_PATH = File.join(SPECKLE_APPDATA_PATH, 'Accounts.db')
SPECKLE_CONFIG_DB_PATH = File.join(SPECKLE_APPDATA_PATH, 'Config.db')
SPECKLE_TEST_DB_PATH = File.join(SPECKLE_APPDATA_PATH, 'sketchup_test.db')
end
+64 -16
View File
@@ -46,12 +46,14 @@ module SpeckleConnector
end
# @param obj [Object] speckle commit object.
def receive_commit_object(obj)
def receive_commit_object(obj, model_preferences)
# First create layers on the sketchup before starting traversing
create_layers(obj.keys.filter_map { |key| key if key.start_with?('@') }, sketchup_model.layers)
filtered_layer_containers = obj.keys.filter_map { |key| key if key.start_with?('@') && key != '@Named Views' }
create_layers(filtered_layer_containers, sketchup_model.layers)
create_views(obj.filter_map { |key, value| value if key == '@Named Views' }, sketchup_model)
# Define default commit layer which is the fallback
default_commit_layer = sketchup_model.layers.layers.find { |layer| layer.display_name == '@Untagged' }
traverse_commit_object(obj, sketchup_model.layers, default_commit_layer)
traverse_commit_object(obj, sketchup_model.layers, default_commit_layer, model_preferences)
end
# Create actual Sketchup layers from layer_paths that taken from Speckle base object.
@@ -70,6 +72,23 @@ module SpeckleConnector
create_folder_layers(folder_layer_arrays, folder)
end
# @param views [Array] views.
# @param sketchup_model [Sketchup::Model] active sketchup model.
def create_views(views, sketchup_model)
return if views.empty?
views.first.each do |view|
origin = view['origin']
target = view['target']
origin = SpeckleObjects::Geometry::Point.to_native(origin['x'], origin['y'], origin['z'], origin['units'])
target = SpeckleObjects::Geometry::Point.to_native(target['x'], target['y'], target['z'], target['units'])
# Set camera position before creating scene on it.
my_camera = Sketchup::Camera.new(origin, target, [0, 0, 1], !view['isOrthogonal'], view['lens'])
sketchup_model.active_view.camera = my_camera
sketchup_model.pages.add(view['name'])
end
end
# @param headless_layers [Array<String>] headless layer names.
# @param folder [Sketchup::Layers, Sketchup::LayerFolder] layer folder to create commit layers under it.
def create_headless_layers(headless_layers, folder)
@@ -107,9 +126,9 @@ module SpeckleConnector
# self-caller method means that call itself according to conditions inside of it.
# rubocop:disable Metrics/CyclomaticComplexity
# rubocop:disable Metrics/PerceivedComplexity
def traverse_commit_object(obj, commit_folder, layer)
def traverse_commit_object(obj, commit_folder, layer, model_preferences)
if can_convert_to_native(obj)
convert_to_native(obj, layer)
convert_to_native(obj, layer, model_preferences)
elsif obj.is_a?(Hash) && obj.key?('speckle_type')
return if ignored_speckle_type?(obj)
@@ -119,16 +138,16 @@ module SpeckleConnector
props.each do |prop|
layer_path = prop if prop.start_with?('@') && obj[prop].is_a?(Array)
layer = find_layer(layer_path, commit_folder, layer)
traverse_commit_object(obj[prop], commit_folder, layer)
traverse_commit_object(obj[prop], commit_folder, layer, model_preferences)
end
else
# puts(">>> Found #{obj['speckle_type']}: #{obj['id']} with displayValue.")
convert_to_native(obj, layer)
convert_to_native(obj, layer, model_preferences)
end
elsif obj.is_a?(Hash)
obj.each_value { |value| traverse_commit_object(value, commit_folder, layer) }
obj.each_value { |value| traverse_commit_object(value, commit_folder, layer, model_preferences) }
elsif obj.is_a?(Array)
obj.each { |value| traverse_commit_object(value, commit_folder, layer) }
obj.each { |value| traverse_commit_object(value, commit_folder, layer, model_preferences) }
end
end
# rubocop:enable Metrics/CyclomaticComplexity
@@ -167,19 +186,27 @@ module SpeckleConnector
end
# rubocop:disable Metrics/CyclomaticComplexity
def convert_to_native(obj, layer, entities = sketchup_model.entities)
# rubocop:disable Metrics/MethodLength
def convert_to_native(obj, layer, model_preferences, entities = sketchup_model.entities)
convert = method(:convert_to_native)
return display_value_to_native_component(obj, layer, entities, &convert) unless obj['displayValue'].nil?
unless obj['displayValue'].nil?
return display_value_to_native_component(obj, layer, entities, model_preferences, &convert)
end
case obj['speckle_type']
when 'Objects.Geometry.Line', 'Objects.Geometry.Polyline' then LINE.to_native(obj, layer, entities)
when 'Objects.Other.BlockInstance' then BLOCK_INSTANCE.to_native(sketchup_model, obj, layer, entities, &convert)
when 'Objects.Other.BlockInstance' then BLOCK_INSTANCE.to_native(sketchup_model, obj, layer, entities,
model_preferences, &convert)
when 'Objects.Other.BlockDefinition' then BLOCK_DEFINITION.to_native(sketchup_model, obj, layer,
obj['name'],
entities,
obj['always_face_camera'],
model_preferences,
obj['sketchup_attributes'],
obj['applicationId'],
&convert)
when 'Objects.Geometry.Mesh' then MESH.to_native(sketchup_model, obj, layer, entities)
when 'Objects.Geometry.Brep' then MESH.to_native(sketchup_model, obj['displayValue'], layer, entities)
when 'Objects.Geometry.Mesh' then MESH.to_native(sketchup_model, obj, layer, entities, model_preferences)
when 'Objects.Geometry.Brep' then MESH.to_native(sketchup_model, obj['displayValue'], layer, entities,
model_preferences)
end
rescue StandardError => e
puts("Failed to convert #{obj['speckle_type']} (id: #{obj['id']})")
@@ -187,15 +214,33 @@ module SpeckleConnector
nil
end
# rubocop:enable Metrics/CyclomaticComplexity
# rubocop:enable Metrics/MethodLength
# Creates a component definition and instance from a speckle object with a display value
def display_value_to_native_component(obj, layer, entities, &convert)
# rubocop:disable Metrics/PerceivedComplexity
# rubocop:disable Metrics/CyclomaticComplexity
# rubocop:disable Metrics/MethodLength
def display_value_to_native_component(obj, layer, entities, model_preferences, &convert)
obj_id = obj['applicationId'].to_s.empty? ? obj['id'] : obj['applicationId']
block_definition = obj['@blockDefinition'] || obj['blockDefinition']
definition = BLOCK_DEFINITION.to_native(
sketchup_model,
obj['displayValue'],
layer,
"def::#{obj_id}",
if block_definition.nil?
false
else
block_definition['always_face_camera'].nil? ? false : block_definition['always_face_camera']
end,
model_preferences,
if block_definition.nil?
nil
else
block_definition['sketchup_attributes'].nil? ? nil : block_definition['sketchup_attributes']
end,
obj_id,
&convert
)
@@ -207,6 +252,9 @@ module SpeckleConnector
instance.name = obj_id
instance
end
# rubocop:enable Metrics/PerceivedComplexity
# rubocop:enable Metrics/CyclomaticComplexity
# rubocop:enable Metrics/MethodLength
# Takes a component definition and finds and erases the first instance with the matching name
# (and optionally the applicationId)
+59 -9
View File
@@ -4,9 +4,11 @@ require_relative 'converter'
require_relative 'base_object_serializer'
require_relative '../speckle_objects/base'
require_relative '../speckle_objects/geometry/line'
require_relative '../speckle_objects/geometry/length'
require_relative '../speckle_objects/geometry/mesh'
require_relative '../speckle_objects/other/block_instance'
require_relative '../speckle_objects/other/block_definition'
require_relative '../speckle_objects/built_elements/view3d'
module SpeckleConnector
module Converters
@@ -22,17 +24,56 @@ module SpeckleConnector
# Convert selected objects by putting them into related array that grouped by layer.
# @return [Hash{Symbol=>Array}] layers -which only have objects- to hold it's objects under the base object.
def convert_selection_to_base
def convert_selection_to_base(preferences)
sketchup_model.selection.each do |entity|
converted_object = convert(entity)
converted_object = convert(entity, preferences)
layer_name = entity_layer_path(entity)
layers[layer_name].push(converted_object)
end
# send only layers that have any object
base_object_properties = layers.reject { |_layer_name, objects| objects.empty? }
add_views(base_object_properties) if sketchup_model.pages.any?
SpeckleObjects::Base.with_detached_layers(base_object_properties)
end
# Add views from pages.
# @param base_object_properties [Hash] dynamically attached base object properties.
# rubocop:disable Metrics/AbcSize
# rubocop:disable Metrics/MethodLength
def add_views(base_object_properties)
views = []
sketchup_model.pages.each do |page|
cam = page.camera
origin = SpeckleObjects::Geometry::Point.new(
SpeckleObjects::Geometry.length_to_speckle(cam.eye[0], @units),
SpeckleObjects::Geometry.length_to_speckle(cam.eye[1], @units),
SpeckleObjects::Geometry.length_to_speckle(cam.eye[2], @units),
@units
)
target = SpeckleObjects::Geometry::Point.new(
SpeckleObjects::Geometry.length_to_speckle(cam.target[0], @units),
SpeckleObjects::Geometry.length_to_speckle(cam.target[1], @units),
SpeckleObjects::Geometry.length_to_speckle(cam.target[2], @units),
@units
)
direction = SpeckleObjects::Geometry::Vector.new(
SpeckleObjects::Geometry.length_to_speckle(cam.direction[0], @units),
SpeckleObjects::Geometry.length_to_speckle(cam.direction[1], @units),
SpeckleObjects::Geometry.length_to_speckle(cam.direction[2], @units),
@units
)
view = SpeckleObjects::BuiltElements::View3d.new(
page.name,
origin, target, direction, SpeckleObjects::Geometry::Vector.new(0, 0, 1, @units),
cam.perspective?, cam.fov, @units, page.name
)
views.append(view)
end
base_object_properties['@Named Views'] = views
end
# rubocop:enable Metrics/AbcSize
# rubocop:enable Metrics/MethodLength
# Serialized and traversed information to send batches.
# @param base [SpeckleObjects::Base] base object to serialize.
# @return [String, Integer, Array<Object>] base id, total_children_count of base and batches
@@ -46,18 +87,27 @@ module SpeckleConnector
end
# @param entity [Sketchup::Entity] sketchup entity to convert Speckle.
def convert(entity)
def convert(entity, preferences)
convert = method(:convert)
return SpeckleObjects::Geometry::Line.from_edge(entity, @units).to_h if entity.is_a?(Sketchup::Edge)
return SpeckleObjects::Geometry::Mesh.from_face(entity, @units) if entity.is_a?(Sketchup::Face)
if entity.is_a?(Sketchup::Group)
return SpeckleObjects::Other::BlockInstance.from_group(entity, @units, @definitions, &convert)
if entity.is_a?(Sketchup::Edge)
return SpeckleObjects::Geometry::Line.from_edge(entity, @units, preferences[:model]).to_h
end
if entity.is_a?(Sketchup::Face)
return SpeckleObjects::Geometry::Mesh.from_face(entity, @units, preferences[:model])
end
if entity.is_a?(Sketchup::Group)
return SpeckleObjects::Other::BlockInstance.from_group(entity, @units, @definitions, preferences, &convert)
end
if entity.is_a?(Sketchup::ComponentInstance)
return SpeckleObjects::Other::BlockInstance.from_component_instance(entity, @units, @definitions, &convert)
return SpeckleObjects::Other::BlockInstance.from_component_instance(entity, @units, @definitions,
preferences, &convert)
end
if entity.is_a?(Sketchup::ComponentDefinition)
return SpeckleObjects::Other::BlockDefinition.from_definition(entity, @units, @definitions, &convert)
return SpeckleObjects::Other::BlockDefinition.from_definition(entity, @units, @definitions, preferences,
&convert)
end
nil
Binary file not shown.
@@ -0,0 +1,75 @@
# frozen_string_literal: true
require_relative '../ext/sqlite3'
require_relative '../immutable/immutable'
require_relative '../constants/path_constants'
require_relative '../sketchup_model/dictionary/speckle_model_dictionary_handler'
module SpeckleConnector
# Preferences that stored on config database and sketchup_model.
module Preferences
include Immutable::ImmutableUtils
DICT_HANDLER = SketchupModel::Dictionary::SpeckleModelDictionaryHandler
DEFAULT_PREFERENCES = "('configSketchup', '{\"DarkTheme\":false}');"
# @param sketchup_model [Sketchup::Model] active model.
# rubocop:disable Metrics/MethodLength
def self.init_preferences(sketchup_model)
# Init sqlite database
db = Sqlite3::Database.new(SPECKLE_CONFIG_DB_PATH)
# Check configSketchup key is valid or not, otherwise init with default settings
if db.exec("SELECT content FROM 'objects' WHERE hash = 'configSketchup'").empty?
db.exec("INSERT INTO 'objects' VALUES #{DEFAULT_PREFERENCES}")
end
# Select data
data = db.exec("SELECT content FROM 'objects' WHERE hash = 'configSketchup'").first.first
# Parse string to hash
data_hash = JSON.parse(data).to_h
# Get current theme value
dark_theme = data_hash['DarkTheme']
speckle_dictionary = sketchup_model.attribute_dictionary('Speckle')
if speckle_dictionary
Immutable::Hash.new(
{
user: {
dark_theme: dark_theme
},
model: {
combine_faces_by_material: DICT_HANDLER.get_attribute(sketchup_model,
:combine_faces_by_material, 'Speckle'),
include_entity_attributes: DICT_HANDLER.get_attribute(sketchup_model,
:include_entity_attributes, 'Speckle'),
merge_coplanar_faces: DICT_HANDLER.get_attribute(sketchup_model,
:merge_coplanar_faces, 'Speckle')
}
}
)
else
DICT_HANDLER.write_initial_model_data(sketchup_model, default_model_preferences)
Immutable::Hash.new(
{
user: {
dark_theme: dark_theme
},
model: default_model_preferences
}
)
end
end
# rubocop:enable Metrics/MethodLength
def self.default_model_preferences
{
combine_faces_by_material: true,
include_entity_attributes: true,
merge_coplanar_faces: true
}
end
end
end
@@ -9,6 +9,33 @@ module SpeckleConnector
class DictionaryHandler
DICTIONARY_NAME = 'Speckle_Base_Object'
# @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.to_json unless att_dict.name == 'Speckle_Base_Object'
end
dictionaries
end
# @param entity [Sketchup::Entity] entity to set attribute dictionaries
def self.attribute_dictionaries_to_native(entity, dictionaries)
return if dictionaries.nil?
dictionaries.each do |dict_name, entries|
dict_name = dict_name == 'empty_dictionary_name' ? '' : dict_name
JSON.parse(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
# @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
@@ -0,0 +1,21 @@
# frozen_string_literal: true
require_relative 'dictionary_handler'
require_relative '../../constants/dict_constants'
require_relative '../../constants/type_constants'
module SpeckleConnector
module SketchupModel
module Dictionary
# Dictionary handler of the speckle model.
class SpeckleModelDictionaryHandler < DictionaryHandler
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.
def self.write_initial_model_data(sketchup_model, default_preferences)
set_hash(sketchup_model, default_preferences, DICTIONARY_NAME)
end
end
end
end
end
@@ -0,0 +1,45 @@
# frozen_string_literal: true
require_relative '../base'
require_relative '../../speckle_objects/geometry/point'
require_relative '../../speckle_objects/geometry/vector'
module SpeckleConnector
module SpeckleObjects
module BuiltElements
# View3d object represents scenes on Sketchup.
class View3d < Base
SPECKLE_TYPE = 'Objects.BuiltElements.View:Objects.BuiltElements.View3D'
# @param name [String] name of the scene
# @param origin [SpeckleObjects::Geometry::Point] origin (eye) of the view.
# @param target [SpeckleObjects::Geometry::Point] target of the view.
# @param direction [SpeckleObjects::Geometry::Vector] direction of the view from eye to target.
# @param up_direction [SpeckleObjects::Geometry::Vector] up direction of the view.
# @param is_perspective [Boolean] whether view is perspective or not.
# @param lens [Boolean] fov value of the view camera.
# @param units [String] units of the camera.
# @param application_id [String] application_id of the view.
# rubocop:disable Metrics/ParameterLists
def initialize(name, origin, target, direction, up_direction,
is_perspective, lens, units, application_id)
super(
speckle_type: SPECKLE_TYPE,
total_children_count: 0,
application_id: application_id,
id: nil
)
self[:name] = name
self[:origin] = origin
self[:target] = target
self[:forwardDirection] = direction
self[:upDirection] = up_direction
self[:isOrthogonal] = !is_perspective
self[:lens] = lens
self[:units] = units
end
# rubocop:enable Metrics/ParameterLists
end
end
end
end
@@ -5,6 +5,7 @@ require_relative 'point'
require_relative 'bounding_box'
require_relative '../base'
require_relative '../primitive/interval'
require_relative '../../sketchup_model/dictionary/dictionary_handler'
module SpeckleConnector
module SpeckleObjects
@@ -20,7 +21,7 @@ module SpeckleConnector
# @param units [String] units of the speckle line.
# @param application_id [String, nil] entity id of the {Sketchup::Edge} that represents to the speckle line.
# rubocop:disable Metrics/ParameterLists
def initialize(start_pt:, end_pt:, domain:, bbox:, units:, application_id: nil)
def initialize(start_pt:, end_pt:, domain:, bbox:, units:, sketchup_attributes: {}, application_id: nil)
super(
speckle_type: 'Objects.Geometry.Line',
total_children_count: 0,
@@ -32,11 +33,17 @@ module SpeckleConnector
self[:domain] = domain
self[:bbox] = bbox
self[:units] = units
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)
def self.from_edge(edge, units, model_preferences)
dictionaries = {}
if model_preferences[:include_entity_attributes]
dictionaries = SketchupModel::Dictionary::DictionaryHandler.attribute_dictionaries_to_speckle(edge)
end
att = dictionaries.any? ? { dictionaries: dictionaries } : {}
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)
@@ -46,8 +53,9 @@ module SpeckleConnector
end_pt: end_pt,
domain: domain,
bbox: bbox,
application_id: edge.persistent_id.to_s,
units: units
units: units,
sketchup_attributes: att,
application_id: edge.persistent_id.to_s
)
end
@@ -66,7 +74,13 @@ module SpeckleConnector
end_pt = Point.to_native(line['end']['x'], line['end']['y'], line['end']['z'], line['units'])
edges = entities.add_edges(start_pt, end_pt)
end
edges.each { |edge| edge.layer = layer }
edges.each do |edge|
edge.layer = layer
unless line['sketchup_attributes'].nil?
SketchupModel::Dictionary::DictionaryHandler
.attribute_dictionaries_to_native(edge, line['sketchup_attributes']['dictionaries'])
end
end
end
# rubocop:enable Metrics/AbcSize
@@ -4,6 +4,7 @@ require_relative '../base'
require_relative '../geometry/bounding_box'
require_relative '../other/render_material'
require_relative '../../convertors/clean_up'
require_relative '../../sketchup_model/dictionary/dictionary_handler'
module SpeckleConnector
module SpeckleObjects
@@ -13,33 +14,46 @@ module SpeckleConnector
class Mesh < Base
SPECKLE_TYPE = 'Objects.Geometry.Mesh'
# @return [Array<Geom::Point3d>] points that construct mesh.
attr_accessor :vertices
# @return [Array] polygons
attr_accessor :polygons
# @return [String] speckle units.
attr_reader :units
# @param units [String] units of the speckle mesh.
# @param render_material [Other::RenderMaterial, nil] render material of the speckle mesh.
# @param bbox [Geometry::BoundingBox] bounding box speckle object of the speckle mesh.
# @param vertices [Array] vertices of the speckle mesh.
# @param faces [Array] faces of the speckle mesh.
# @param face_edge_flags [Array] face edge flags of the speckle mesh.
# @param sketchup_attributes [Hash] additional information about speckle mesh.
# rubocop:disable Metrics/ParameterLists
def initialize(units:, render_material:, bbox:, vertices:, faces:, face_edge_flags:, sketchup_attributes:)
def initialize(units:, render_material:, bbox:, vertices:, faces:, sketchup_attributes:)
super(
speckle_type: SPECKLE_TYPE,
total_children_count: 0,
application_id: nil,
id: nil
)
@vertices = []
@polygons = []
@units = units
self[:units] = units
self[:renderMaterial] = render_material
self[:bbox] = bbox
self[:'@(31250)vertices'] = vertices
self[:'@(62500)faces'] = faces
self[:'@(31250)faceEdgeFlags'] = face_edge_flags
self[:sketchup_attributes] = sketchup_attributes
self[:sketchup_attributes] = sketchup_attributes if sketchup_attributes.any?
end
# rubocop:enable Metrics/ParameterLists
# @param entities [Sketchup::Entities] entities to add
def self.to_native(sketchup_model, mesh, layer, entities)
# rubocop:disable Metrics/MethodLength
# rubocop:disable Metrics/AbcSize
# rubocop:disable Metrics/CyclomaticComplexity
def self.to_native(sketchup_model, mesh, layer, entities, model_preferences)
# Get soft? flag of {Sketchup::Edge} object to understand smoothness of edge.
is_soften = get_soften_setting(mesh)
smooth_flags = is_soften ? 4 : 1
@@ -58,32 +72,57 @@ module SpeckleConnector
material = Other::RenderMaterial.to_native(sketchup_model, mesh['renderMaterial'])
entities.add_faces_from_mesh(native_mesh, smooth_flags, material)
added_faces = entities.grep(Sketchup::Face).last(native_mesh.polygons.length)
added_faces.each { |face| face.layer = layer }
added_faces.each do |face|
face.layer = layer
unless mesh['sketchup_attributes'].nil?
SketchupModel::Dictionary::DictionaryHandler
.attribute_dictionaries_to_native(face, mesh['sketchup_attributes']['dictionaries'])
end
end
# Merge only added faces in this scope
Converters::CleanUp.merge_coplanar_faces(added_faces)
Converters::CleanUp.merge_coplanar_faces(added_faces) if model_preferences[:merge_coplanar_faces]
native_mesh
end
# rubocop:enable Metrics/MethodLength
# rubocop:enable Metrics/AbcSize
# rubocop:enable Metrics/CyclomaticComplexity
# @param face [Sketchup::Face] face to convert mesh
def self.from_face(face, units)
mesh = face.loops.count > 1 ? face.mesh : nil
# rubocop:disable Style/MultilineTernaryOperator
def self.from_face(face, units, model_preferences)
dictionaries = {}
if model_preferences[:include_entity_attributes]
dictionaries = SketchupModel::Dictionary::DictionaryHandler.attribute_dictionaries_to_speckle(face)
end
has_any_soften_edge = face.edges.any?(&:soft?)
Mesh.new(
att = dictionaries.any? ? { is_soften: has_any_soften_edge, dictionaries: dictionaries }
: { is_soften: has_any_soften_edge }
speckle_mesh = Mesh.new(
units: units,
render_material: face.material.nil? ? nil : Other::RenderMaterial.from_material(face.material),
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),
vertices: mesh.nil? ? face_vertices_to_array(face, units) : mesh_points_to_array(mesh, units),
faces: mesh.nil? ? face_indices_to_array(face, 0) : mesh_faces_to_array(mesh, -1),
face_edge_flags: mesh.nil? ? face_edge_flags_to_array(face) : mesh_edge_flags_to_array(mesh),
sketchup_attributes: { is_soften: has_any_soften_edge }
vertices: [], # mesh.nil? ? face_vertices_to_array(face, units) : mesh_points_to_array(mesh, units),
faces: [], # mesh.nil? ? face_indices_to_array(face, 0) : mesh_faces_to_array(mesh, -1),
# face_edge_flags: [], # mesh.nil? ? face_edge_flags_to_array(face) : mesh_edge_flags_to_array(mesh),
sketchup_attributes: att
)
speckle_mesh.face_to_mesh(face)
speckle_mesh.update_mesh
speckle_mesh
end
# rubocop:enable Style/MultilineTernaryOperator
def face_to_mesh(face)
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)
end
# get a flat array of vertices from a list of sketchup vertices
def self.face_vertices_to_array(face, units)
# Collects indexed Sketchup vertices into flat array for Speckle use.
def vertices_to_array(units)
pts_array = []
face.vertices.each do |v|
pt = v.position
vertices.each do |pt|
pts_array.push(Geometry.length_to_speckle(pt[0], units),
Geometry.length_to_speckle(pt[1], units),
Geometry.length_to_speckle(pt[2], units))
@@ -91,49 +130,58 @@ module SpeckleConnector
pts_array
end
# get a flat array of vertices from a sketchup polygon mesh
def self.mesh_points_to_array(mesh, units)
pts_array = []
def update_mesh
# puts "Vertex count on mesh #{vertices.length}"
self['@(31250)vertices'] = vertices_to_array(units)
self[:'@(62500)faces'] = polygons
end
# Get a flat array of vertices from a list of sketchup vertices
# @param face [Sketchup::Face] face to get vertices.
def face_vertices_to_array(face)
face.vertices.each do |v|
pt = v.position
# FIXME: Enable previous line when viewer supports shared vertices
# vertices.push(pt) unless vertices.any? { |point| point == pt }
vertices.push(pt)
end
end
# Get a flat array of face indices from a sketchup face
def face_indices_to_array(face)
polygons.push(face.vertices.count)
face.vertices.each do |v|
pt = v.position
# FIXME: Enable previous line when viewer supports shared vertices
# global_vertex_index = vertices.reverse.find_index(pt)
global_vertex_index = vertices.length - vertices.reverse.find_index(pt) - 1
polygons.push(global_vertex_index)
end
end
# 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)
mesh.points.each do |pt|
pts_array.push(
Geometry.length_to_speckle(pt[0], units),
Geometry.length_to_speckle(pt[1], units),
Geometry.length_to_speckle(pt[2], units)
)
# FIXME: Enable previous line when viewer supports shared vertices
# vertices.push(pt) unless vertices.any? { |point| point == pt }
vertices.push(pt)
end
pts_array
end
# get a flat array of face indices from a sketchup face
def self.face_indices_to_array(face, offset)
face_array = [face.vertices.count]
face_array.push(*face.vertices.count.times.map { |index| index + offset })
face_array
end
# get an array of face indices from a sketchup polygon mesh
def self.mesh_faces_to_array(mesh, offset = 0)
faces = []
# 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)
mesh.polygons.each do |poly|
faces.push(
poly.count, *poly.map { |index| index.abs + offset }
)
global_polygon_array = [poly.count]
poly.each do |index|
# FIXME: Enable previous line when viewer supports shared vertices
# global_vertex_index = vertices.reverse.find_index(mesh.points[index.abs - 1])
global_vertex_index = vertices.length - vertices.reverse.find_index(mesh.points[index.abs - 1]) - 1
global_polygon_array.push(global_vertex_index)
end
polygons.push(*global_polygon_array)
end
faces
end
def self.face_edge_flags_to_array(face)
face.outer_loop.edges.map(&:soft?)
end
def self.mesh_edge_flags_to_array(mesh)
edge_flags = []
mesh.polygons.each do |poly|
edge_flags.push(
*poly.map(&:negative?)
)
end
edge_flags
end
def self.get_soften_setting(mesh)
@@ -28,6 +28,17 @@ module SpeckleConnector
self[:units] = units
end
# Compare this point with other point those are reference to same coordinate.
# @param other [SpeckleObjects::Geometry::Point] other point to compare.
def ==(other, tolerance: 1e-15)
return false if (self[:x] - other[:x]).abs > tolerance
return false if (self[:y] - other[:y]).abs > tolerance
return false if (self[:z] - other[:z]).abs > tolerance
return false if self[:units] != other[:units]
true
end
# @param vertex [Geom::Point3d] sketchup point to convert speckle point.
# @param units [String] unit of the point.
def self.from_vertex(vertex, units)
@@ -7,6 +7,7 @@ require_relative '../base'
require_relative '../geometry/point'
require_relative '../geometry/mesh'
require_relative '../geometry/bounding_box'
require_relative '../../sketchup_model/dictionary/dictionary_handler'
module SpeckleConnector
module SpeckleObjects
@@ -20,7 +21,9 @@ module SpeckleConnector
# @param name [String] name of the block definition.
# @param units [String] units of the block definition.
# @param application_id [String, NilClass] application id of the block definition.
def initialize(geometry:, base_point:, name:, units:, application_id: nil)
# rubocop:disable Metrics/ParameterLists
def initialize(geometry:, base_point:, name:, units:, always_face_camera:, sketchup_attributes: {},
application_id: nil)
super(
speckle_type: SPECKLE_TYPE,
total_children_count: 0,
@@ -30,22 +33,37 @@ module SpeckleConnector
self[:units] = units
self[:name] = name
self[:basePoint] = base_point
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['@geometry'] = geometry
end
# rubocop:enable Metrics/ParameterLists
# @param definition [Sketchup::ComponentDefinition] component definition might be belong to group or component
# instance
# @param units [String] units of the Sketchup model
# @param definitions [Hash{String=>BlockDefinition}] all converted {BlockDefinition}s on the converter.
def self.from_definition(definition, units, definitions, &convert)
# rubocop:disable Metrics/CyclomaticComplexity
# rubocop:disable Metrics/PerceivedComplexity
# rubocop:disable Metrics/MethodLength
def self.from_definition(definition, units, definitions, preferences, &convert)
guid = definition.guid
return definitions[guid] if definitions.key?(guid)
dictionaries = {}
if preferences[:model][:include_entity_attributes]
dictionaries = SketchupModel::Dictionary::DictionaryHandler.attribute_dictionaries_to_speckle(definition)
end
att = dictionaries.any? ? { dictionaries: dictionaries } : {}
# TODO: Solve logic
geometry = if definition.entities[0].is_a?(Sketchup::Edge) || definition.entities[0].is_a?(Sketchup::Face)
group_entities_to_speckle(definition, units, definitions, &convert)
group_entities_to_speckle(definition, units, definitions, preferences, &convert)
else
definition.entities.map { |entity| convert.call(entity) }
definition.entities.map do |entity|
convert.call(entity, preferences) unless entity.is_a?(Sketchup::Edge) && entity.faces.any?
end
end
# FIXME: Decide how to approach base point of the definition instead origin.
@@ -54,82 +72,107 @@ module SpeckleConnector
name: definition.name,
base_point: Geometry::Point.new(0, 0, 0, units),
geometry: geometry,
always_face_camera: definition.behavior.always_face_camera?,
sketchup_attributes: att,
application_id: guid
)
end
# rubocop:enable Metrics/CyclomaticComplexity
# rubocop:enable Metrics/PerceivedComplexity
# rubocop:enable Metrics/MethodLength
# Finds or creates a component definition from the geometry and the given name
# @param sketchup_model [Sketchup::Model] sketchup model to check block definitions.
# rubocop:disable Metrics/CyclomaticComplexity
# rubocop:disable Metrics/PerceivedComplexity
# rubocop:disable Metrics/ParameterLists
def self.to_native(sketchup_model, geometry, layer, name, application_id = '', &convert)
def self.to_native(sketchup_model, geometry, layer, name, always_face_camera, model_preferences,
sketchup_attributes, application_id = '', &convert)
definition = sketchup_model.definitions[name]
return definition if definition && (definition.name == name || definition.guid == application_id)
definition&.entities&.clear!
definition ||= sketchup_model.definitions.add(name)
definition.layer = layer
geometry.each { |obj| convert.call(obj, layer, definition.entities) } if geometry.is_a?(Array)
convert.call(geometry, layer, definition.entities) if geometry.is_a?(Hash) && !geometry['speckle_type'].nil?
if geometry.is_a?(Array)
geometry.each { |obj| convert.call(obj, layer, model_preferences, definition.entities) }
end
if geometry.is_a?(Hash) && !geometry['speckle_type'].nil?
convert.call(geometry, layer, model_preferences, definition.entities)
end
# puts("definition finished: #{name} (#{application_id})")
# puts(" entity count: #{definition.entities.count}")
definition.behavior.always_face_camera = always_face_camera
unless sketchup_attributes.nil?
SketchupModel::Dictionary::DictionaryHandler
.attribute_dictionaries_to_native(definition, sketchup_attributes['dictionaries'])
end
definition
end
# rubocop:enable Metrics/CyclomaticComplexity
# rubocop:enable Metrics/PerceivedComplexity
# rubocop:enable Metrics/ParameterLists
def self.group_entities_to_speckle(definition, units, definitions, &convert)
# rubocop:disable Metrics/AbcSize
# rubocop:disable Metrics/MethodLength
# rubocop:disable Metrics/CyclomaticComplexity
# rubocop:disable Metrics/PerceivedComplexity
def self.group_entities_to_speckle(definition, units, definitions, preferences, &convert)
orphan_edges = definition.entities.grep(Sketchup::Edge).filter { |edge| edge.faces.none? }
lines = orphan_edges.collect do |orphan_edge|
Geometry::Line.from_edge(orphan_edge, units)
Geometry::Line.from_edge(orphan_edge, units, preferences[:model])
end
nested_blocks = definition.entities.grep(Sketchup::ComponentInstance).collect do |component_instance|
BlockInstance.from_component_instance(component_instance, units, definitions, &convert)
BlockInstance.from_component_instance(component_instance, units, definitions, preferences, &convert)
end
meshes = definition.entities.grep(Sketchup::Face).collect do |face|
Geometry::Mesh.from_face(face, units)
nested_groups = definition.entities.grep(Sketchup::Group).collect do |group|
BlockInstance.from_group(group, units, definitions, preferences, &convert)
end
lines + nested_blocks + meshes
end
if preferences[:model][:combine_faces_by_material]
mesh_groups = {}
definition.entities.grep(Sketchup::Face).collect do |face|
group_meshes_by_material(face, mesh_groups, units, preferences[:model])
end
# Update mesh overwrites points and polygons into base object.
mesh_groups.each { |_, mesh| mesh.update_mesh }
# rubocop:disable Metrics/AbcSize
def self.group_meshes_by_material(definition, face, mat_groups, units)
# convert material
mat_id = face.material.nil? ? 'none' : face.material.entityID
mat_groups[mat_id] = initialise_group_mesh(face, definition.bounds, units) unless mat_groups.key?(mat_id)
mat_group = mat_groups[mat_id]
if face.loops.size > 1
mesh = face.mesh
mat_group[:'@(31250)vertices'].push(*Geometry::Mesh.mesh_points_to_array(mesh, units))
mat_group[:'@(62500)faces'].push(*Geometry::Mesh.mesh_faces_to_array(mesh, mat_group[:pt_count] - 1))
mat_group[:'@(31250)faceEdgeFlags'].push(*Geometry::Mesh.mesh_edge_flags_to_array(mesh))
lines + nested_blocks + nested_groups + mesh_groups.values
else
mat_group[:'@(31250)vertices'].push(*Geometry::Mesh.face_vertices_to_array(face, units))
mat_group[:'@(62500)faces'].push(*Geometry::Mesh.face_indices_to_array(face, mat_group[:pt_count]))
mat_group[:'@(31250)faceEdgeFlags'].push(*Geometry::Mesh.face_edge_flags_to_array(face))
meshes = definition.entities.grep(Sketchup::Face).collect do |face|
Geometry::Mesh.from_face(face, units, preferences[:model])
end
lines + nested_blocks + nested_groups + meshes
end
mat_group[:pt_count] += face.vertices.count
end
# rubocop:enable Metrics/AbcSize
# rubocop:enable Metrics/MethodLength
# rubocop:enable Metrics/CyclomaticComplexity
# rubocop:enable Metrics/PerceivedComplexity
def self.initialise_group_mesh(face, bounds, units)
has_any_soften_edge = face.edges.any?(&:soft?)
mesh = Geometry::Mesh.new(
units: units,
render_material: face.material.nil? ? nil : RenderMaterial.from_material(face.material),
bbox: Geometry::BoundingBox.from_bounds(bounds, units),
vertices: [],
faces: [],
face_edge_flags: [],
sketchup_attributes: { is_soften: has_any_soften_edge }
)
mesh[:pt_count] = 0
mesh
def self.group_meshes_by_material(face, mat_groups, units, model_preferences)
# convert material
mat_id = get_mesh_group_id(face, model_preferences)
mat_groups[mat_id] = Geometry::Mesh.from_face(face, units, model_preferences) unless mat_groups.key?(mat_id)
mat_group = mat_groups[mat_id]
mat_group.face_to_mesh(face)
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)
if model_preferences[:include_entity_attributes]
has_attribute_dictionary = !(face.attribute_dictionaries.nil? || face.attribute_dictionaries.first.nil?)
return face.persistent_id.to_s if has_attribute_dictionary
end
material = face.material || face.back_material
return 'none' if material.nil?
return material.entityID.to_s
end
end
end
@@ -5,6 +5,7 @@ require_relative 'transform'
require_relative 'block_definition'
require_relative '../base'
require_relative '../geometry/bounding_box'
require_relative '../../sketchup_model/dictionary/dictionary_handler'
module SpeckleConnector
module SpeckleObjects
@@ -16,14 +17,13 @@ module SpeckleConnector
# @param units [String] units of the block instance.
# @param is_sketchup_group [Boolean] whether is sketchup group or not. Sketchup Groups represented as
# block instance on Speckle.
# @param bbox [Geometry::BoundingBox] bounding box of the block instance.
# @param name [String] name of the block instance.
# @param transform [Other::Transform] transform of the block instance.
# @param block_definition [Other::BlockDefinition] definition of the block instance.
# @param sketchup_attributes [Other::BlockDefinition] sketchup attributes of the block instance.
# @param sketchup_attributes [Hash{Symbol=>Object}] sketchup attributes of the block instance.
# @param application_id [String] application id of the block instance.
# rubocop:disable Metrics/ParameterLists
def initialize(units:, is_sketchup_group:, bbox:, name:, render_material:, transform:, block_definition:,
def initialize(units:, is_sketchup_group:, name:, render_material:, transform:, block_definition:,
sketchup_attributes: {}, application_id: nil)
super(
speckle_type: SPECKLE_TYPE,
@@ -34,35 +34,46 @@ module SpeckleConnector
self[:units] = units
self[:name] = name
self[:is_sketchup_group] = is_sketchup_group
self[:bbox] = bbox
self[:renderMaterial] = render_material
self[:transform] = transform
self[:sketchup_attributes] = sketchup_attributes
self[:sketchup_attributes] = sketchup_attributes if sketchup_attributes.any?
# FIXME: Since blockDefinition sends with @ as detached, block basePlane renders on viewer.
self['@blockDefinition'] = block_definition
end
# rubocop:enable Metrics/ParameterLists
# @param group [Sketchup::Group] group to convert Speckle BlockInstance
def self.from_group(group, units, component_defs, &convert)
def self.from_group(group, units, component_defs, preferences, &convert)
dictionaries = {}
if preferences[:model][:include_entity_attributes]
dictionaries = SketchupModel::Dictionary::DictionaryHandler.attribute_dictionaries_to_speckle(group)
end
att = dictionaries.any? ? { dictionaries: dictionaries } : {}
BlockInstance.new(
units: units,
application_id: group.guid,
is_sketchup_group: true,
bbox: Geometry::BoundingBox.from_bounds(group.bounds, units),
name: group.name == '' ? nil : group.name,
render_material: group.material.nil? ? nil : RenderMaterial.from_material(group.material),
transform: Other::Transform.from_transformation(group.transformation, units),
block_definition: BlockDefinition.from_definition(group.definition, units, component_defs, &convert)
block_definition: BlockDefinition.from_definition(group.definition, units, component_defs,
preferences, &convert),
sketchup_attributes: att,
application_id: group.guid
)
end
# @param component_instance [Sketchup::ComponentInstance] component instance to convert Speckle BlockInstance
def self.from_component_instance(component_instance, units, component_defs, &convert)
# rubocop:disable Metrics/MethodLength
def self.from_component_instance(component_instance, units, component_defs, preferences, &convert)
dictionaries = {}
if preferences[:model][:include_entity_attributes]
dictionaries = SketchupModel::Dictionary::DictionaryHandler
.attribute_dictionaries_to_speckle(component_instance)
end
att = dictionaries.any? ? { dictionaries: dictionaries } : {}
BlockInstance.new(
units: units,
application_id: component_instance.guid,
is_sketchup_group: false,
bbox: Geometry::BoundingBox.from_bounds(component_instance.bounds, units),
name: component_instance.name == '' ? nil : component_instance.name,
render_material: if component_instance.material.nil?
nil
@@ -71,9 +82,12 @@ module SpeckleConnector
end,
transform: Other::Transform.from_transformation(component_instance.transformation, units),
block_definition: BlockDefinition.from_definition(component_instance.definition, units, component_defs,
&convert)
preferences, &convert),
sketchup_attributes: att,
application_id: component_instance.guid
)
end
# rubocop:enable Metrics/MethodLength
# Creates a component instance from a block
# @param sketchup_model [Sketchup::Model] sketchup model to check block definitions.
@@ -82,34 +96,44 @@ module SpeckleConnector
# @param entities [Sketchup::Entities] entities collection to add {Sketchup::Edge} into it.
# rubocop:disable Metrics/AbcSize
# rubocop:disable Metrics/MethodLength
def self.to_native(sketchup_model, block, layer, entities, &convert)
# rubocop:disable Metrics/ParameterLists
# rubocop:disable Metrics/CyclomaticComplexity
# rubocop:disable Metrics/PerceivedComplexity
def self.to_native(sketchup_model, block, layer, entities, model_preferences, &convert)
# is_group = block.key?("is_sketchup_group") && block["is_sketchup_group"]
# something about this conversion is freaking out if nested block geo is a group
# so this is set to false always until I can figure this out
is_group = false
# is_group = block['is_sketchup_group']
block_definition = block['@blockDefinition'] || block['blockDefinition']
geometry = block_definition['@geometry'] || block_definition['geometry']
definition = BlockDefinition.to_native(
sketchup_model,
block['@blockDefinition']['@geometry'],
geometry,
layer,
block['@blockDefinition']['name'],
block['@blockDefinition']['applicationId'],
block_definition['name'],
block_definition['always_face_camera'].nil? ? false : block_definition['always_face_camera'],
model_preferences,
block_definition['sketchup_attributes'],
block_definition['applicationId'],
&convert
)
instance_name = block['name'].nil? || block['name'].empty? ? block['id'] : block['name']
t_arr = block['transform'].is_a?(Hash) ? block['transform']['value'] : block['transform']
transform = Other::Transform.to_native(t_arr, block['units'])
instance =
if is_group
# rubocop:disable SketchupSuggestions/AddGroup
group = entities.add_group(definition.entities.to_a)
group.layer = layer
# rubocop:enable SketchupSuggestions/AddGroup
else
instance = entities.add_instance(definition, transform)
instance.layer = layer
instance
end
instance = if is_group
# rubocop:disable SketchupSuggestions/AddGroup
group = entities.add_group(definition.entities.to_a)
group.layer = layer
group
# rubocop:enable SketchupSuggestions/AddGroup
else
instance = entities.add_instance(definition, transform)
instance.layer = layer
instance
end
# erase existing instances after creation and before rename because you can't have definitions
# without instances
find_and_erase_existing_instance(definition, instance_name, block['applicationId'])
@@ -117,22 +141,23 @@ module SpeckleConnector
instance.transformation = transform if is_group
instance.material = Other::RenderMaterial.to_native(sketchup_model, block['renderMaterial'])
instance.name = instance_name
unless block['sketchup_attributes'].nil?
SketchupModel::Dictionary::DictionaryHandler
.attribute_dictionaries_to_native(instance, block['sketchup_attributes']['dictionaries'])
end
instance
end
# rubocop:enable Metrics/AbcSize
# rubocop:enable Metrics/MethodLength
# rubocop:enable Metrics/ParameterLists
# rubocop:enable Metrics/CyclomaticComplexity
# rubocop:enable Metrics/PerceivedComplexity
# takes a component definition and finds and erases the first instance with the matching name
# (and optionally the applicationId)
def self.find_and_erase_existing_instance(definition, name, app_id = '')
definition.instances.find { |ins| ins.name == name || ins.guid == app_id }&.erase!
end
private
def attribute_types
ATTRIBUTE_TYPES
end
end
end
end
@@ -9,7 +9,7 @@ module SpeckleConnector
class SpeckleState
include Immutable::ImmutableUtils
# @return [Hash] accounts on appdata.
# @return [Array] accounts on appdata.
attr_reader :accounts
# @return [Hash] queue to send to server.
@@ -32,6 +32,10 @@ module SpeckleConnector
new_queue = message_queue.merge({ "#{callback_name}": next_queue_message })
with(:@message_queue => new_queue)
end
def with_accounts(new_accounts)
with(:@accounts => new_accounts)
end
end
end
end
+8
View File
@@ -40,6 +40,14 @@ module SpeckleConnector
new_speckle_state = speckle_state.with(:@stream_queue => {})
with(:@speckle_state => new_speckle_state)
end
def with_speckle_state(new_speckle_state)
with(:@speckle_state => new_speckle_state)
end
def with_user_state(new_user_state)
with(:@user_state => new_user_state)
end
end
end
end
+5 -1
View File
@@ -8,12 +8,16 @@ module SpeckleConnector
class UserState
include Immutable::ImmutableUtils
# @return [Hash{Symbol => Object}] user specific preferences
# @return [ImmutableHash{Symbol => Object}] user specific preferences
attr_reader :preferences
def initialize(preferences)
@preferences = preferences
end
def with_preferences(new_preferences)
with(:@preferences => new_preferences)
end
end
end
end
+5 -1
View File
@@ -29,11 +29,12 @@ module SpeckleConnector
# Show dialog if it's not visible yet
def show
bring_to_front if html_dialog.visible?
return if html_dialog.visible?
# reset dialog only if it is marked ready, otherwise
# add_exec_callback is triggered twice upon first initialization
reset_dialog! if @ready
reset_dialog!
html_dialog.show
end
@@ -73,6 +74,9 @@ module SpeckleConnector
# @return [UI::HtmlDialog] the Sketchup interface to html dialog
def init_dialog
dialog = UI::HtmlDialog.new(@dialog_specs)
dialog.set_can_close do
true
end
File.exist?(@htm_file) ? dialog.set_file(@htm_file) : dialog.set_url('http://localhost:8081')
# dialog.set_url('http://localhost:8081') # uncomment this line if you want to use your local version of ui
add_exec_callback(dialog)
+7 -1
View File
@@ -11,10 +11,13 @@ require_relative '../commands/dialog_ready'
require_relative '../commands/save_stream'
require_relative '../commands/remove_stream'
require_relative '../commands/notify_connected'
require_relative '../commands/user_preferences_updated'
require_relative '../commands/model_preferences_updated'
require_relative '../actions/reload_accounts'
require_relative '../actions/load_saved_streams'
require_relative '../actions/init_local_accounts'
require_relative '../actions/collect_preferences'
module SpeckleConnector
module Ui
@@ -60,7 +63,10 @@ module SpeckleConnector
load_saved_streams: Commands::ActionCommand.new(@app, Actions::LoadSavedStreams),
save_stream: Commands::SaveStream.new(@app),
remove_stream: Commands::RemoveStream.new(@app),
notify_connected: Commands::NotifyConnected.new(@app)
notify_connected: Commands::NotifyConnected.new(@app),
collect_preferences: Commands::ActionCommand.new(@app, Actions::CollectPreferences),
user_preferences_updated: Commands::UserPreferencesUpdated.new(@app),
model_preferences_updated: Commands::ModelPreferencesUpdated.new(@app)
}.freeze
end
end
+103
View File
@@ -0,0 +1,103 @@
# frozen_string_literal: true
require_relative '../../test_helper'
require_relative '../../../speckle_connector/src/ext/sqlite3'
require_relative '../../../speckle_connector/src/constants/path_constants'
module SpeckleConnector
class Sqlite3Test < Minitest::Test
TABLE_NAME = "sketchup_test"
TABLE_COLUMNS = "(hash TEXT PRIMARY KEY NOT NULL, content TEXT NOT NULL);"
INSERT_DATA = "('oguzhan', 'koral');"
def setup
# Do nothing
end
def teardown
# Do nothing
end
def test_table_exists
db_path = SPECKLE_TEST_DB_PATH
db = Sqlite3::Database.new(db_path)
exists = db.table_exist?('objects')
db.close
assert_equal(exists, false)
end
def test_create_table
# Init file and close it
test_database_file = File.new(SPECKLE_TEST_DB_PATH, "w")
test_database_file.close
# Init sqlite database and create table
db = Sqlite3::Database.new(test_database_file.path)
db.exec("CREATE TABLE #{TABLE_NAME} #{TABLE_COLUMNS}")
exists = db.table_exist?(TABLE_NAME)
db.close
# Assert
assert_equal(exists, true)
# Delete test database file
File.delete(test_database_file.path) if File.exist?(test_database_file.path)
end
def test_insert_to_table
# Init file and close it
test_database_file = File.new(SPECKLE_TEST_DB_PATH, "w")
test_database_file.close
# Init sqlite database
db = Sqlite3::Database.new(test_database_file.path)
# Create table
db.exec("CREATE TABLE #{TABLE_NAME} #{TABLE_COLUMNS}")
# Insert data
db.exec("INSERT INTO #{TABLE_NAME} VALUES #{INSERT_DATA}")
# Select data
data = db.exec("SELECT content FROM #{TABLE_NAME} WHERE hash = 'oguzhan'")
# Close database
db.close
assert_equal(data, [["koral"]])
# Delete test database file
File.delete(test_database_file.path) if File.exist?(test_database_file.path)
end
def test_update_value
# Init file and close it
test_database_file = File.new(SPECKLE_TEST_DB_PATH, "w")
test_database_file.close
# Init sqlite database
db = Sqlite3::Database.new(test_database_file.path)
# Create table
db.exec("CREATE TABLE #{TABLE_NAME} #{TABLE_COLUMNS}")
# Insert data
db.exec("INSERT INTO #{TABLE_NAME} VALUES #{INSERT_DATA}")
# Update data
db.exec("UPDATE #{TABLE_NAME} SET content = 'updated_koral' WHERE hash = 'oguzhan'")
# Select data
data = db.exec("SELECT content FROM #{TABLE_NAME} WHERE hash = 'oguzhan'")
# Close database
db.close
assert_equal(data, [["updated_koral"]])
# Delete test database file
File.delete(test_database_file.path) if File.exist?(test_database_file.path)
end
end
end
+35 -22
View File
@@ -21,12 +21,10 @@
solo
/>
<v-spacer />
<v-btn icon small class="mx-1" @click="switchTheme">
<v-icon>mdi-theme-light-dark</v-icon>
</v-btn>
<v-btn icon small class="mx-1" @click="requestRefresh">
<v-icon>mdi-refresh</v-icon>
</v-btn>
<settings-dialog :preferences="preferences"/>
<v-menu v-if="loggedIn" bottom min-width="200px" rounded offset-y>
<template #activator="{ on, attrs }">
<v-btn class="ml-1" icon x-large v-on="on">
@@ -58,10 +56,10 @@
{{ user.bio ? 'Bio: ' + user.bio : '' }}
</div>
</v-card-text>
<v-card-text v-if="accounts">
<v-card-text v-if="accounts()">
<v-divider class="my-3" />
<div v-for="account in accounts" :key="account.id">
<div v-for="account in accounts()" :key="account.id">
<v-btn
v-if="account.userInfo.id != user.id"
rounded
@@ -82,11 +80,14 @@
</v-menu>
</v-app-bar>
<create-stream/>
<create-stream-dialog v-if="accounts().length !== 0"/>
<v-container fluid>
<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-main>
</v-app>
@@ -97,16 +98,22 @@
import { bus } from './main'
import userQuery from './graphql/user.gql'
import { onLogin } from './vue-apollo'
import CreateStream from "@/components/CreateStream";
import Login from "@/views/Login";
global.collectPreferences = function (preferences) {
bus.$emit('update-preferences', preferences)
}
global.loadAccounts = function (accounts) {
console.log('>>> SpeckleSketchup: Loading accounts', accounts)
localStorage.setItem('localAccounts', JSON.stringify(accounts))
let uuid = localStorage.getItem('uuid')
if (uuid) {
global.setSelectedAccount(accounts.find((acct) => acct['userInfo']['id'] == uuid))
} else {
global.setSelectedAccount(accounts.find((acct) => acct['isDefault']))
if (accounts.length !== 0){
if (uuid) {
global.setSelectedAccount(accounts.find((acct) => acct['userInfo']['id'] == uuid))
} else {
global.setSelectedAccount(accounts.find((acct) => acct['isDefault']))
}
}
}
@@ -121,7 +128,9 @@ global.setSelectedAccount = function (account) {
export default {
name: 'App',
components: {
CreateStream: () => import('@/components/CreateStream'),
Login,
CreateStreamDialog: () => import('@/components/dialogs/CreateStreamDialog'),
SettingsDialog: () => import('@/components/dialogs/SettingsDialog'),
GlobalToast: () => import('@/components/GlobalToast')
},
props: {
@@ -135,15 +144,13 @@ export default {
streamSearchQuery: null,
createNewStreamDialog: false,
createStreamByIdDialog: false,
createStreamByIdText: ""
createStreamByIdText: "",
preferences: {}
}
},
computed: {
loggedIn() {
return localStorage.getItem('SpeckleSketchup.AuthToken') !== null
},
accounts() {
return JSON.parse(localStorage.getItem('localAccounts'))
}
},
apollo: {
@@ -163,14 +170,20 @@ export default {
if (!this.user) this.$apollo.queries.user.refetch()
})
this.$vuetify.theme.dark = localStorage.getItem('theme') == 'dark'
bus.$on('update-preferences', async (preferences) => {
let prefs = JSON.parse(preferences)
this.preferences = prefs
this.$vuetify.theme.dark = this.preferences.user.dark_theme
})
sketchup.exec({name: "collect_preferences", data: {}})
// this.$vuetify.theme.dark = localStorage.getItem('theme') == 'dark'
sketchup.exec({name: "init_local_accounts", data: {}})
},
methods: {
switchTheme() {
this.$vuetify.theme.dark = !this.$vuetify.theme.dark
localStorage.setItem('theme', this.$vuetify.theme.dark ? 'dark' : 'light')
this.$mixpanel.track('Connector Action', { name: 'Toggle Theme' })
accounts() {
return JSON.parse(localStorage.getItem('localAccounts'))
},
switchAccount(account) {
this.$mixpanel.track('Connector Action', { name: 'Account Select' })
+34 -7
View File
@@ -60,11 +60,17 @@
<v-card-text class="d-flex align-center pb-5 mb-5 -mt-2" style="height: 50px">
<v-menu offset-y>
<template #activator="{ on, attrs }">
<v-slide-x-transition>
<div v-show="hover">
<create-branch-dialog :stream-name="stream.name" :stream-id="streamId"/>
</div>
</v-slide-x-transition>
<v-chip v-if="stream.branches" small v-bind="attrs" class="mr-1" v-on="on">
<v-icon small class="mr-1 float-left">mdi-source-branch</v-icon>
{{ branchName }}
</v-chip>
</template>
<!-- Branch list -->
<v-list dense>
<v-list-item
v-for="(branch, index) in stream.branches.items"
@@ -73,7 +79,7 @@
@click="switchBranch(branch.name)"
>
<v-list-item-title class="text-caption font-weight-regular">
<v-icon v-if="branch.name == branchName" small class="mr-1 float-left">
<v-icon v-if="branch.name === branchName" small class="mr-1 float-left">
mdi-check
</v-icon>
<v-icon v-else small class="mr-1 float-left">mdi-source-branch</v-icon>
@@ -171,6 +177,9 @@ global.oneClickSend = function (streamId) {
export default {
name: 'StreamCard',
components: {
CreateBranchDialog: () => import('@/components/dialogs/CreateBranchDialog'),
},
props: {
streamId: {
type: String,
@@ -261,16 +270,28 @@ export default {
},
computed: {
selectedBranch() {
if (this.$apollo.loading) return
return this.stream.branches.items.find((branch) => branch.name == this.branchName)
if (!this.stream) return
return this.stream.branches.items.find((branch) => branch.name === this.branchName)
},
selectedCommit() {
if (this.$apollo.loading) return
if (this.commitId == 'latest') return this.selectedBranch.commits.items[0]
return this.selectedBranch.commits.items.find((commit) => commit.id == this.commitId)
if (!this.selectedBranch) return
if (this.commitId === 'latest') return this.selectedBranch.commits.items[0]
return this.selectedBranch.commits.items.find((commit) => commit.id === this.commitId)
}
},
mounted() {
bus.$on(`refresh-stream-${this.streamId}`, () => {
let oldBranchName = this.branchName
let oldCommitId = this.commitId
this.$apollo.queries.stream.refetch()
this.branchName = oldBranchName
this.commitId = oldCommitId
})
bus.$on(`create-branch-${this.streamId}`, (branchName) => {
this.$apollo.queries.stream.refetch()
this.branchName = branchName
this.commitId = 'latest'
})
bus.$on(`sketchup-objects-${this.streamId}`, async (batches, commitId, totalChildrenCount) => {
console.log('>>> SpeckleSketchUp: Received objects from sketchup')
@@ -376,7 +397,7 @@ export default {
await this.sleep(2000)
},
async createCommit(batches, commitId, totalChildrenCount) {
if (batches.length == 0) {
if (batches.length === 0) {
this.loadingSend = false
this.loadingStage = null
this.$eventHub.$emit('notification', {
@@ -401,6 +422,8 @@ export default {
const totBatches = batches.length
console.log(`>>> SpeckleSketchUp: ${totBatches} batches ready for sending`)
let batchesSent = 0
const t0 = Date.now()
for (const batch of batches) {
let res = await this.sendBatch(batch)
if (res.status !== 201) throw `Upload request failed: ${res.status}`
@@ -408,6 +431,10 @@ export default {
this.loadingStage = `uploading: ${Math.round((batchesSent / totBatches) * 100)}%`
}
const t1 = Date.now()
const elapsedTime = (t1-t0) / 1000
console.log(`Upload time: ${elapsedTime} second`)
let commit = {
streamId: this.streamId,
branchName: this.branchName,
@@ -0,0 +1,137 @@
<template>
<!-- DIALOG: Create Branch -->
<v-dialog v-model="showCreateBranch">
<template #activator="{ on: dialog, attrs }">
<v-btn
v-tooltip="'Create Branch'"
icon x-small class="ml-0 mr-1"
v-bind="attrs"
v-on="{...dialog}"
>
<v-icon>
mdi-plus-circle
</v-icon>
</v-btn>
</template>
<v-card>
<v-card-title class="text-h5 mb-1">
Create a New Branch
</v-card-title>
<v-card-subtitle class="py-0 my-0 font-italic">
under {{ streamName }} stream
</v-card-subtitle>
<v-container class="px-6" pb-0>
<v-text-field
v-model="branchName"
xxxclass="small-text-field"
hide-details
dense
flat
placeholder="Branch Name"
/>
<v-text-field
v-model="description"
xxxclass="small-text-field"
hide-details
dense
flat
placeholder="Description (Optional)"
/>
</v-container>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn
color="blue darken-1"
text
@click="showCreateBranch = false"
>
Cancel
</v-btn>
<v-btn
:disabled="branchName === ''"
color="blue darken-1"
text
@click="createBranch"
>
Create
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>
<script>
import gql from "graphql-tag";
import {bus} from "@/main";
export default {
name: "CreateBranchDialog",
props: {
streamId: {
type: String,
default: null
},
streamName: {
type: String,
default: null
}
},
data() {
return {
showCreateBranch: false,
branchName: "",
description: "",
defaultDescription: "Stream created from SketchUp",
accountToCreateStream: null
}
},
computed: {
loggedIn() {
return localStorage.getItem('SpeckleSketchup.AuthToken') !== null
},
accounts() {
return JSON.parse(localStorage.getItem('localAccounts'))
},
},
methods: {
async createBranch(){
let res = await this.$apollo.mutate({
mutation: gql`
mutation branchCreate($branch: BranchCreateInput!) {
branchCreate(branch: $branch)
}
`,
variables: {
branch: {
streamId: this.streamId,
name: this.branchName,
description: this.description === '' ? this.defaultDescription : this.description,
}
}
})
bus.$emit(`create-branch-${this.streamId}`, this.branchName)
this.showCreateBranch = false
this.branchName = ""
this.description = ""
this.$mixpanel.track('Connector Action', { name: 'Create Branch' })
return res
}
}
}
</script>
<style>
.v-dialog {
max-width: 390px
}
.v-text-field >>> input {
font-size: 0.9em;
}
.v-text-field >>> label {
font-size: 0.9em;
}
</style>
@@ -161,7 +161,7 @@ import {bus} from "@/main";
import userQuery from "@/graphql/user.gql";
export default {
name: "CreateStream",
name: "CreateStreamDialog",
data() {
return {
showCreateNewStream: false,
@@ -216,6 +216,7 @@ export default {
this.showCreateNewStream = false
this.streamName = ""
this.description = ""
this.$mixpanel.track('Connector Action', { name: 'Create Stream' })
sketchup.exec({name: "save_stream", data: {stream_id: res["data"]["streamCreate"]}})
this.refresh()
return res
@@ -0,0 +1,143 @@
<template>
<!-- DIALOG: Settings -->
<v-dialog v-model="showSettings">
<template #activator="{ on, attrs }">
<v-btn
icon small class="mx-1"
v-bind="attrs"
v-on="on"
>
<v-icon>mdi-cog</v-icon>
</v-btn>
</template>
<v-card>
<v-card-title class="text-h5">
Settings
</v-card-title>
<v-container class="px-6" pb-0>
<!-- Switch Theme -->
<div class="sm1 mt-3">User Preferences</div>
<v-divider class="mb-2"/>
<v-btn icon small class="mx-1" @click="switchTheme">
<v-icon>mdi-theme-light-dark</v-icon>
</v-btn>
<span>Color Mode</span>
<div class="sm1 mt-3">Send Strategy</div>
<v-divider class="mb-2"/>
<v-switch
v-model="combineFacesByMaterial"
class="pt-1 mt-n2 mb-n2"
:label="'Combine faces by material under mesh'"
/>
<v-switch
v-model="includeAttributes"
class="pt-1 my-n5"
:label="'Include entity attributes'"
/>
<div class="sm1 mt-3">Receive Strategy</div>
<v-divider class="mb-2"/>
<v-switch
v-model="mergeCoplanarFaces"
class="pt-1 mt-n2 mb-n2"
:label="'Merge co-planar faces on receive'"
/>
</v-container>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn
color="blue darken-1"
text
@click="showSettings = false"
>
Close
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>
<script>
/*global sketchup*/
import {bus} from "@/main";
export default {
name: "ShowSettings",
props: {
preferences: {
type: Object,
default: null
}
},
data() {
return {
showSettings: false,
streamName: "",
description: "",
combineFacesByMaterial: this.preferences.model.combine_faces_by_material,
includeAttributes: this.preferences.model.include_entity_attributes,
mergeCoplanarFaces: this.preferences.model.merge_coplanar_faces,
}
},
watch: {
'showSettings': {
handler(newValue) {
if (newValue){
this.$mixpanel.track('Connector Action', { name: 'Open Settings Dialog' })
}
},
deep: true
},
'combineFacesByMaterial': {
handler(newValue) {
sketchup.exec({
name: "model_preferences_updated",
data: {preference: "combine_faces_by_material", value: newValue}
})
this.$mixpanel.track('Connector Action', { name: 'Combine Faces By Material Option' })
},
deep: true
},
'includeAttributes': {
handler(newValue) {
sketchup.exec({
name: "model_preferences_updated",
data: {preference: "include_entity_attributes", value: newValue}
})
this.$mixpanel.track('Connector Action', { name: 'Include Entity Attributes Option' })
},
deep: true
},
'mergeCoplanarFaces': {
handler(newValue) {
sketchup.exec({
name: "model_preferences_updated",
data: {preference: "merge_coplanar_faces", value: newValue}
})
this.$mixpanel.track('Connector Action', { name: 'Merge Co-Planar Faces Option' })
},
deep: true
}
},
methods: {
switchTheme() {
this.$vuetify.theme.dark = !this.$vuetify.theme.dark
sketchup.exec({
name: "user_preferences_updated",
data: {preference_hash: "configSketchup", preference: "DarkTheme", value: this.$vuetify.theme.dark}
})
this.$mixpanel.track('Connector Action', { name: 'Toggle Theme' })
},
refresh() {
this.$apollo.queries.user.refetch()
bus.$emit('refresh-streams')
}
}
}
</script>
<style>
</style>
+1 -1
View File
@@ -5,7 +5,7 @@ Vue.use(Vuetify)
export default new Vuetify({
theme: {
dark: localStorage.getItem('theme') === 'dark',
dark: false,
themes: {
light: {
primary: '#0480FB', //speckle blue
+1 -1
View File
@@ -8,7 +8,7 @@ const routes = [
path: '*',
name: 'Streams',
component: () => import('../views/Streams.vue')
}
},
]
const router = new VueRouter({
+28
View File
@@ -0,0 +1,28 @@
<template>
<v-container style="height: 100%">
<v-row align="center" style="height: 100%">
<v-col class="py-2" align="center">
<span class="subtitle">
Hello!
<br />
Welcome to the Speckle connector for SketchUp!
<br />
<br />
</span>
<span class="caption">
You have no logged in account.
Please login or register via Speckle Manager then refresh UI.
</span>
<br />
<span class="caption">
<br />
</span>
</v-col>
</v-row>
</v-container>
</template>
<script>
export default {
name: 'Login'
}
</script>
+3 -1
View File
@@ -61,7 +61,7 @@ export default {
},
computed: {
streamsFound() {
return (this.streams && this.streams?.items?.length != 0) || this.savedStreams?.length !== 0
return (this.streams && this.streams?.items?.length !== 0) || this.savedStreams?.length !== 0
},
isSavedStream(streamId) {
return this.savedStreams?.includes(streamId)
@@ -73,7 +73,9 @@ export default {
},
mounted() {
bus.$on('refresh-streams', () => {
// TODO: We should remember selected branches and commits before refetch
this.$apollo.queries.streams.refetch()
// TODO: We should set previously selected branches and commits after refetch
})
bus.$on('set-saved-streams', (streamIds) => {