Compare commits

...

105 Commits

Author SHA1 Message Date
Oğuzhan Koral 8b824f5342 Update innosetup workflow
There were issues with innosetup workflow which does not run one powershell.exe,
so workflows splitted into chunks according to shells
2023-02-08 21:20:17 +03:00
oguzhankoral d052d5e8a1 Change set env variable to run with powershell 2023-02-08 21:12:00 +03:00
oguzhankoral 610c22dd02 Update innosetup workflow 2023-02-08 21:05:02 +03:00
Oğuzhan Koral a5496ab6a9 Feat (CI): Run CI step to create env variables for innosetup 2023-02-08 20:14:23 +03:00
oguzhankoral c29c8f009c Remove duplicated context: innosetup on yaml file 2023-02-02 10:34:34 +02:00
oguzhankoral fb7e9f2a6c Run CI step to create env variables for innosetup 2023-02-01 23:42:25 +02:00
Oğuzhan Koral 62c2bbb9fa Chore (Notification): Add error notification to GlobalToast 2023-01-23 15:34:03 +03:00
oguzhankoral 7656772194 Add error notification to GlobalToast 2023-01-23 15:31:13 +03:00
Oğuzhan Koral f22ff050e0 Fix (Stream, Attributes): Follow up fixes add stream by url and attributes 2023-01-23 12:35:22 +03:00
oguzhankoral 708f0b44fd Check face entity attributes settings before merging faces into mesh 2023-01-23 12:01:47 +03:00
oguzhankoral 2ee4581f17 Check stream role is null first before split 2023-01-23 12:00:13 +03:00
Oğuzhan Koral e337fb869f Feat (Settings) Advanced settings for entity specific attributes 2023-01-22 00:09:05 +03:00
oguzhankoral 4a8b0147e1 Create UI components for entity specific settings 2023-01-22 00:03:18 +03:00
oguzhankoral c95a1c7e1f Consider entity settings on speckle objects 2023-01-22 00:02:51 +03:00
oguzhankoral a07cd5c3f5 Extend model preferences with entity specific settings 2023-01-22 00:02:24 +03:00
Oğuzhan Koral 4d1473582e Feat (Stream): Add streams by URL or id 2023-01-21 17:33:35 +03:00
oguzhankoral 3bc9f4c452 Fix rubocop issue 2023-01-21 17:33:15 +03:00
oguzhankoral 11377038a0 Implement and make functional adding streams by url 2023-01-20 22:26:36 +03:00
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
41 changed files with 1531 additions and 229 deletions
+43 -11
View File
@@ -36,19 +36,41 @@ jobs:
- attach_workspace:
at: ./
- run:
name: Patch
name: Create Innosetup signing cert
shell: powershell.exe
command:
| # If no tag, use 0.0.0.1 and don't make any YML (for testing only!)
$tag = if([string]::IsNullOrEmpty($env:CIRCLE_TAG)) { "0.0.0" } else { $env:CIRCLE_TAG }
$semver = if($tag.Contains('/')) {$tag.Split("/")[1] } else { $tag }
command: |
echo $env:PFX_B64 > "speckle-sharp-ci-tools\SignTool\AEC Systems Ltd.txt"
certutil -decode "speckle-sharp-ci-tools\SignTool\AEC Systems Ltd.txt" "speckle-sharp-ci-tools\SignTool\AEC Systems Ltd.pfx"
- run:
name: Set Environment Variable
shell: powershell.exe
command: |
$tag = if([string]::IsNullOrEmpty($env:CIRCLE_TAG)) { "2.0.999" } else { $env:CIRCLE_TAG }
$semver = if($tag.Contains('/')) {$tag.Split("/")[0] } else { $tag }
$ver = if($semver.Contains('-')) {$semver.Split("-")[0] } else { $semver }
$channel = if($semver.Contains('-')) {$semver.Split("-")[1] } else { "latest" }
$version = "$($ver).$($env:CIRCLE_BUILD_NUM)"
New-Item -Force "speckle-sharp-ci-tools/Installers/sketchup/$channel.yml" -ItemType File -Value "version: $semver"
echo $version
$version = "$($ver).$($env:WORKFLOW_NUM)"
python patch_version.py $semver
speckle-sharp-ci-tools\InnoSetup\ISCC.exe speckle-sharp-ci-tools\sketchup.iss
environment:
WORKFLOW_NUM: << pipeline.number >>
- run:
name: Build Installer
command: speckle-sharp-ci-tools\InnoSetup\ISCC.exe speckle-sharp-ci-tools\sketchup.iss /Sbyparam=$p
shell: cmd.exe #does not work in powershell
#- run:
# name: Patch
# shell: powershell.exe
# command:
# | # If no tag, use 0.0.0.1 and don't make any YML (for testing only!)
# $tag = if([string]::IsNullOrEmpty($env:CIRCLE_TAG)) { "0.0.0" } else { $env:CIRCLE_TAG }
# $semver = if($tag.Contains('/')) {$tag.Split("/")[1] } else { $tag }
# $ver = if($semver.Contains('-')) {$semver.Split("-")[0] } else { $semver }
# $channel = if($semver.Contains('-')) {$semver.Split("-")[1] } else { "latest" }
# $version = "$($ver).$($env:CIRCLE_BUILD_NUM)"
# New-Item -Force "speckle-sharp-ci-tools/Installers/sketchup/$channel.yml" -ItemType File -Value "version: $semver"
# echo $version
# python patch_version.py $semver
# speckle-sharp-ci-tools\InnoSetup\ISCC.exe speckle-sharp-ci-tools\sketchup.iss
- persist_to_workspace:
root: ./
paths:
@@ -58,9 +80,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:
@@ -114,8 +144,10 @@ workflows:
filters:
tags:
only: /.*/
context: innosetup
- 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
@@ -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
@@ -17,7 +18,9 @@ module SpeckleConnector
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
@@ -9,7 +9,8 @@ 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, _data)
(saved_streams = state.sketchup_state.sketchup_model.attribute_dictionary('speckle', true)['streams']) or []
(saved_streams = state.sketchup_state.sketchup_model
.attribute_dictionary('Speckle', true)['saved_streams']) or []
state.with_add_queue('setSavedStreams', saved_streams, [])
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, [])
@@ -18,10 +18,10 @@ module SpeckleConnector
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def update_state(state)
speckle_dict = state.sketchup_state.sketchup_model.attribute_dictionary('speckle', true)
saved = speckle_dict['streams'] || []
speckle_dict = state.sketchup_state.sketchup_model.attribute_dictionary('Speckle', true)
saved = speckle_dict['saved_streams'] || []
saved -= [@stream_id]
speckle_dict['streams'] = saved
speckle_dict['saved_streams'] = saved
state
end
end
+3 -3
View File
@@ -16,10 +16,10 @@ module SpeckleConnector
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def update_state(state)
speckle_dict = state.sketchup_state.sketchup_model.attribute_dictionary('speckle', true)
saved = speckle_dict['streams'] || []
speckle_dict = state.sketchup_state.sketchup_model.attribute_dictionary('Speckle', true)
saved = speckle_dict['saved_streams'] || []
saved = saved.empty? ? [@stream_id] : saved.unshift(@stream_id)
speckle_dict['streams'] = saved
speckle_dict['saved_streams'] = saved
state
end
end
@@ -16,11 +16,10 @@ module SpeckleConnector
# @return [States::State] the new updated state object
def update_state(state)
to_send_stream_id = state.speckle_state.stream_queue[:stream_id]
return state if to_send_stream_id == @stream_id
return state if to_send_stream_id == @stream_id || to_send_stream_id.nil?
to_send_converted = state.speckle_state.stream_queue[:converted].to_json
new_state = state.with_add_queue('convertedFromSketchup', to_send_stream_id, [to_send_converted])
new_state = new_state.with_add_queue('oneClickSend', to_send_stream_id, [])
new_state.with_empty_stream_queue
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,108 @@
# 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'
),
include_face_entity_attributes: DICT_HANDLER.get_attribute(
sketchup_model,
:include_face_entity_attributes,
'Speckle'
),
include_edge_entity_attributes: DICT_HANDLER.get_attribute(
sketchup_model,
:include_edge_entity_attributes,
'Speckle'
),
include_group_entity_attributes: DICT_HANDLER.get_attribute(
sketchup_model,
:include_group_entity_attributes,
'Speckle'
),
include_component_entity_attributes: DICT_HANDLER.get_attribute(
sketchup_model,
:include_component_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,
include_face_entity_attributes: true,
include_edge_entity_attributes: true,
include_group_entity_attributes: true,
include_component_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] && model_preferences[:include_edge_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,59 @@ 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
# rubocop:disable Metrics/CyclomaticComplexity
def self.from_face(face, units, model_preferences)
dictionaries = {}
if model_preferences[:include_entity_attributes] && model_preferences[:include_face_entity_attributes]
dictionaries = SketchupModel::Dictionary::DictionaryHandler.attribute_dictionaries_to_speckle(face)
end
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
# rubocop:enable Metrics/CyclomaticComplexity
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 +132,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,23 +33,44 @@ 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
# rubocop:disable Metrics/AbcSize
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]
if definition.group?
if preferences[:model][:include_group_entity_attributes]
dictionaries = SketchupModel::Dictionary::DictionaryHandler
.attribute_dictionaries_to_speckle(definition)
end
elsif preferences[:model][:include_component_entity_attributes]
dictionaries = SketchupModel::Dictionary::DictionaryHandler.attribute_dictionaries_to_speckle(definition)
end
end
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 do |entity|
convert.call(entity) unless entity.is_a?(Sketchup::Edge) && entity.faces.any?
convert.call(entity, preferences) unless entity.is_a?(Sketchup::Edge) && entity.faces.any?
end
end
@@ -56,82 +80,108 @@ 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
# rubocop:enable Metrics/AbcSize
# 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] && model_preferences[:include_face_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,47 @@ 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] && preferences[:model][:include_group_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] &&
preferences[:model][:include_component_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 +83,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 +97,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 +142,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
+4
View File
@@ -44,6 +44,10 @@ module SpeckleConnector
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
+24 -12
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">
@@ -82,7 +80,11 @@
</v-menu>
</v-app-bar>
<create-stream v-if="accounts().length !== 0"/>
<create-stream-dialog
v-if="accounts().length !== 0"
:account-id="activeAccount().userInfo.id"
:server-url="activeAccount().serverInfo.url"
/>
<v-container v-if="accounts().length !== 0" fluid>
<router-view :stream-search-query="streamSearchQuery" />
@@ -102,11 +104,14 @@ import userQuery from './graphql/user.gql'
import { onLogin } from './vue-apollo'
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')
console.log(accounts.length)
if (accounts.length !== 0){
if (uuid) {
global.setSelectedAccount(accounts.find((acct) => acct['userInfo']['id'] == uuid))
@@ -128,7 +133,8 @@ export default {
name: 'App',
components: {
Login,
CreateStream: () => import('@/components/CreateStream'),
CreateStreamDialog: () => import('@/components/dialogs/CreateStreamDialog'),
SettingsDialog: () => import('@/components/dialogs/SettingsDialog'),
GlobalToast: () => import('@/components/GlobalToast')
},
props: {
@@ -142,7 +148,8 @@ export default {
streamSearchQuery: null,
createNewStreamDialog: false,
createStreamByIdDialog: false,
createStreamByIdText: ""
createStreamByIdText: "",
preferences: {}
}
},
computed: {
@@ -167,17 +174,22 @@ 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: {
accounts() {
return JSON.parse(localStorage.getItem('localAccounts'))
},
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' })
activeAccount(){
return this.accounts().find((account) => account['isDefault'])
},
switchAccount(account) {
this.$mixpanel.track('Connector Action', { name: 'Account Select' })
+13 -2
View File
@@ -1,6 +1,8 @@
<template>
<v-snackbar v-model="snack" app bottom color="primary">
{{ text }}
<v-snackbar v-if="text" v-model="snack" app bottom :color="color">
<div v-for="(line, index) in text.split('\n')" :key="index">
{{ line }}
</div>
<template #action="{}">
<v-btn v-if="actionName" small outlined @click="openUrl(url)" @click:append="snack = false">
{{ actionName }}
@@ -16,6 +18,7 @@ export default {
data() {
return {
snack: false,
color: "primary",
text: null,
actionName: null,
url: null
@@ -33,6 +36,14 @@ export default {
mounted() {
this.$eventHub.$on('notification', (args) => {
this.snack = true
this.color = "primary"
this.text = args.text
this.actionName = args.action ? args.action.name : null
this.url = args.action ? args.action.url : null
})
this.$eventHub.$on('error', (args) => {
this.snack = true
this.color = "#CC3300"
this.text = args.text
this.actionName = args.action ? args.action.name : null
this.url = args.action ? args.action.url : null
+37 -10
View File
@@ -55,16 +55,22 @@
<timeago class="mr-1" :datetime="stream.updatedAt" />
|
<v-icon class="ml-1" small>mdi-account-key-outline</v-icon>
{{ stream.role.split(':')[1] }}
{{ stream.role === null ? "" : stream.role.split(':')[1] }}
</v-card-text>
<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,11 +397,11 @@ 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', {
text: 'No objects selected. Nothing was sent.'
text: 'No objects selected. Nothing was sent.\n'
})
return
}
@@ -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,
@@ -428,7 +455,7 @@ export default {
})
console.log('>>> SpeckleSketchUp: Sent to stream: ' + this.streamId, commit)
this.$eventHub.$emit('notification', {
text: 'Model selection sent!',
text: 'Model selection sent!\n',
action: {
name: 'View in Web',
url: `${localStorage.getItem('serverUrl')}/streams/${this.streamId}/commits/${
@@ -0,0 +1,134 @@
<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
}
},
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>
@@ -92,7 +92,6 @@
</v-dialog>
<!-- DIALOG: Add a Stream by ID or URL -->
<!--
<v-dialog v-model="showCreateStreamById">
<template #activator="{ on, attrs }">
<v-btn
@@ -141,14 +140,13 @@
:disabled="createStreamByIdText === ''"
color="blue darken-1"
text
@click="showCreateStreamById = false"
@click="getStream"
>
Add
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
-->
</v-col>
</v-row>
</v-container>
@@ -159,9 +157,20 @@
import gql from "graphql-tag";
import {bus} from "@/main";
import userQuery from "@/graphql/user.gql";
import {StreamWrapper} from "@/utils/streamWrapper";
export default {
name: "CreateStream",
name: "CreateStreamDialog",
props: {
accountId: {
type: String,
default: null
},
serverUrl: {
type: String,
default: null
}
},
data() {
return {
showCreateNewStream: false,
@@ -177,10 +186,7 @@ export default {
computed: {
loggedIn() {
return localStorage.getItem('SpeckleSketchup.AuthToken') !== null
},
accounts() {
return JSON.parse(localStorage.getItem('localAccounts'))
},
}
},
apollo: {
user: {
@@ -188,16 +194,70 @@ export default {
}
},
methods: {
activeAccount(){
return JSON.parse(localStorage.getItem('selectedAccount'))
},
switchAccountToCreateStream(account) {
this.accountToCreateStream = account
},
refresh() {
this.$apollo.queries.user.refetch()
bus.$emit('refresh-streams')
},
async getStream(){
try {
const streamWrapper = new StreamWrapper(this.createStreamByIdText, this.accountId, this.serverUrl)
let res = await this.$apollo.query({
query: gql`
query Stream($id: String!){
stream(id: $id){
id
name
description
isPublic
role
createdAt
updatedAt
commentCount
favoritedDate
favoritesCount
collaborators {
id
name
role
avatar
},
branches (limit: ${10}){
totalCount,
cursor,
items {
id,
name,
description,
commits {
totalCount
}
}
}
}
}
`,
variables: {
id: streamWrapper.streamId
}
})
let stream = res.data.stream
this.$eventHub.$emit('notification', {
text: 'Stream Added by URL!\n',
})
bus.$emit('stream-added-by-id-or-url', stream.id)
}
catch (e){
this.$eventHub.$emit('error', {
text: 'The stream you are trying to add might;\n' +
'- lies on different server, \n' +
'- be private, \n' +
'- not be existed anymore.',
})
}
this.showCreateStreamById = false
},
async createStream(){
let res = await this.$apollo.mutate({
mutation: gql`
@@ -216,6 +276,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,216 @@
<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'"
/>
<v-icon class="ml-3" style="line-height: 0;">mdi-arrow-right-bottom</v-icon>
<v-switch
v-model="includeEdgeAttributes"
class="pt-1 my-n5 ml-10"
:label="'Edge'"
:disabled="!includeAttributes"
/>
<v-icon class="ml-3" style="line-height: 0;">mdi-arrow-right-bottom</v-icon>
<v-switch
v-model="includeFaceAttributes"
class="pt-1 my-n5 ml-10"
:label="'Face'"
:disabled="!includeAttributes"
/>
<v-icon class="ml-3" style="line-height: 0;">mdi-arrow-right-bottom</v-icon>
<v-switch
v-model="includeGroupAttributes"
class="pt-1 my-n5 ml-10"
:label="'Group'"
:disabled="!includeAttributes"
/>
<v-icon class="ml-3" style="line-height: 0;">mdi-arrow-right-bottom</v-icon>
<v-switch
v-model="includeComponentAttributes"
class="pt-1 my-n5 ml-10"
:label="'Component'"
:disabled="!includeAttributes"
/>
<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,
includeFaceAttributes: this.preferences.model.include_face_entity_attributes,
includeEdgeAttributes: this.preferences.model.include_edge_entity_attributes,
includeGroupAttributes: this.preferences.model.include_group_entity_attributes,
includeComponentAttributes: this.preferences.model.include_component_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
},
'includeFaceAttributes': {
handler(newValue) {
sketchup.exec({
name: "model_preferences_updated",
data: {preference: "include_face_entity_attributes", value: newValue}
})
this.$mixpanel.track('Connector Action', { name: 'Include Face Entity Attributes Option' })
},
deep: true
},
'includeEdgeAttributes': {
handler(newValue) {
sketchup.exec({
name: "model_preferences_updated",
data: {preference: "include_edge_entity_attributes", value: newValue}
})
this.$mixpanel.track('Connector Action', { name: 'Include Edge Entity Attributes Option' })
},
deep: true
},
'includeGroupAttributes': {
handler(newValue) {
sketchup.exec({
name: "model_preferences_updated",
data: {preference: "include_group_entity_attributes", value: newValue}
})
this.$mixpanel.track('Connector Action', { name: 'Include Group Entity Attributes Option' })
},
deep: true
},
'includeComponentAttributes': {
handler(newValue) {
sketchup.exec({
name: "model_preferences_updated",
data: {preference: "include_component_entity_attributes", value: newValue}
})
this.$mixpanel.track('Connector Action', { name: 'Include Component 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
+77
View File
@@ -0,0 +1,77 @@
require('url')
export class StreamWrapper {
constructor(streamIdOrUrl, accountId, serverUrl) {
this.originalOutput = streamIdOrUrl
try {
this.streamWrapperFromUrl(streamIdOrUrl)
}
catch (e){
this.serverUrl = serverUrl
this.userId = accountId
this.streamId = streamIdOrUrl
}
}
streamWrapperFromUrl(streamUrl){
this.url = new URL(streamUrl)
this.segments = this.url.pathname.split('/').map((segment) => segment + '/')
this.serverUrl = this.url.origin
if (this.segments.length >= 4 && this.segments[3]?.toLowerCase() === "branches/"){
this.streamId = this.segments[2].replace("/", "")
if (this.segments.length > 5)
{
let branchSegments = this.segments.slice(4, this.segments.length - 1);
this.branchName = branchSegments.join("")
}
else
{
this.branchName = this.segments[4]
}
} else {
switch (this.segments.length){
case 3: // ie http://speckle.server/streams/8fecc9aa6d
if (this.segments[1].toLowerCase() === "streams/")
this.streamId = this.segments[2].replace("/", "");
else
throw new Error(`Cannot parse ${this.originalOutput} into a stream wrapper class`);
break;
case 4: // ie https://speckle.server/streams/0c6ad366c4/globals/
if (this.segments[3].toLowerCase().startsWith("globals"))
{
this.streamId = this.segments[2].replace("/", "");
this.branchName = this.segments[3].replace("/", "");
}
else
throw new Error(`Cannot parse ${this.originalOutput} into a stream wrapper class`);
break;
case 5: // ie http://speckle.server/streams/8fecc9aa6d/commits/76a23d7179
switch (this.segments[3].toLowerCase()){
case "commits/":
this.streamId = this.segments[2].replace("/", "");
this.commitId = this.segments[4].replace("/", "");
break;
case "globals/":
this.streamId = this.segments[2].replace("/", "");
this.branchName = this.segments[3].replace("/", "");
this.commitId = this.segments[4].replace("/", "");
break;
case "branches/":
this.streamId = this.segments[2].replace("/", "");
this.branchName = this.segments[4].replace("/", "");
break;
case "objects/":
this.streamId = this.segments[2].replace("/", "");
this.objectId = this.segments[4].replace("/", "");
break;
default:
throw new Error(`Cannot parse ${this.originalOutput} into a stream wrapper class`);
}
break;
default:
throw new Error(`Cannot parse ${this.originalOutput} into a stream wrapper class`);
}
}
}
}
+13 -1
View File
@@ -73,12 +73,24 @@ 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) => {
this.savedStreams = streamIds
})
bus.$on('stream-added-by-id-or-url', (streamId) => {
if (!this.savedStreams){
this.savedStreams = []
this.savedStreams.push(streamId)
} else {
if (!this.savedStreams.includes(streamId)){
this.savedStreams.push(streamId)
}
}
sketchup.exec({name: "save_stream", data: {stream_id: streamId}})
})
sketchup.exec({name: "load_saved_streams", data: {}})
console.log('LAUNCHED')
this.$mixpanel.track('Connector Action', { name: 'Launched' })