Compare commits
91 Commits
2.11.0-rc2
...
2.11.2
| Author | SHA1 | Date | |
|---|---|---|---|
| 6053d3eac1 | |||
| 529830f36b | |||
| 505cf6265c | |||
| 8cd9673eec | |||
| ac9cb28558 | |||
| 6eefe0698c | |||
| d0113532b6 | |||
| 5a1d2ad5f4 | |||
| dfe02f4c74 | |||
| 5349d556b9 | |||
| 7f7d8a501b | |||
| cbee0e465b | |||
| faf00f0b0d | |||
| fa97da5781 | |||
| e7bab546db | |||
| e2f4a30b5b | |||
| ccff1df041 | |||
| 2037cfc25a | |||
| 268a091d8a | |||
| 6b52dfab3e | |||
| 839999851f | |||
| a87470b7b5 | |||
| e76aeb80fd | |||
| 28292e59e2 | |||
| 25dda481b2 | |||
| bbda233fd8 | |||
| 349218f0b5 | |||
| f18d00a69d | |||
| 25ea6504de | |||
| 43081c70e2 | |||
| 0fde1c2026 | |||
| b35383571e | |||
| 45a84847a2 | |||
| 70d92f26d6 | |||
| 737ed86e69 | |||
| 3865057b7a | |||
| 42a84dcd86 | |||
| e2d819c59d | |||
| bfee6a88dc | |||
| 68f3be17df | |||
| 929c97ff5e | |||
| 4b66a2e4d0 | |||
| 46e740154e | |||
| 05e89f49da | |||
| 358e9071e3 | |||
| e37b6a1cc0 | |||
| 266721973b | |||
| 7c27ac85cb | |||
| 4b79732e38 | |||
| 2ceeea5298 | |||
| 3ec659a59b | |||
| 4309056851 | |||
| b768f20f7a | |||
| b3a71bcf53 | |||
| 50c199bc03 | |||
| d6302ac128 | |||
| 6a5d9e1394 | |||
| ac5fc3e6ea | |||
| aa6cbceeb9 | |||
| 46a7395382 | |||
| a782811dad | |||
| d22039bc96 | |||
| 15539c258e | |||
| f9ca4acf16 | |||
| 66d2a9b7fe | |||
| 6dff8c3221 | |||
| f13c65e083 | |||
| 56a7d5cb86 | |||
| c63c0675d5 | |||
| 22bc4b8c9e | |||
| ead17b8906 | |||
| 1a211daac2 | |||
| c7e502da4e | |||
| 70df5e6cec | |||
| 793f287c35 | |||
| 8e8b1c60b8 | |||
| ba2cd51852 | |||
| 3986a4ef60 | |||
| c99d89fb11 | |||
| 0a9c33de91 | |||
| 88c940fc53 | |||
| 88c861cbde | |||
| cd071ca144 | |||
| 9970b8ec36 | |||
| ffc564becd | |||
| 6289fd5941 | |||
| 7f44fe76c7 | |||
| ea86dc6785 | |||
| 754f9e1ed1 | |||
| 9a1a02e664 | |||
| 1e92195355 |
+11
-1
@@ -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
|
||||
|
||||
+1
-1
Submodule _sqlite3 updated: cbf81b6c70...800dd5e366
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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' })
|
||||
|
||||
@@ -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>
|
||||
+2
-1
@@ -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>
|
||||
@@ -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
|
||||
|
||||
@@ -8,7 +8,7 @@ const routes = [
|
||||
path: '*',
|
||||
name: 'Streams',
|
||||
component: () => import('../views/Streams.vue')
|
||||
}
|
||||
},
|
||||
]
|
||||
|
||||
const router = new VueRouter({
|
||||
|
||||
@@ -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>
|
||||
@@ -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) => {
|
||||
|
||||
Reference in New Issue
Block a user