Compare commits

..

11 Commits

Author SHA1 Message Date
Mucahit Bilal GOKER e1308bfa21 legacy connector cosmetic changes (#430)
* replace logo

* change copy
2025-05-22 20:24:00 +03:00
Oğuzhan Koral ed1d1f5b44 Add legacy dialog to v2 (#420) 2025-03-19 11:24:47 +03:00
Oğuzhan Koral aff871baaa Chore(v2): v2 is legacy (#419)
* v2 is legacy

* correct the log
2025-03-19 10:26:54 +03:00
Oğuzhan Koral 0608e7048d fallback to application support for accounts db (#410) 2025-03-11 19:37:57 +03:00
Jedd Morgan 0875e55c1d Limit usage of deprecated FE1 api to only essentials (#398)
* basic queries to update

* aliasing

* Remove unused streams.gql

---------

Co-authored-by: oguzhankoral <oguzhankoral@gmail.com>
2025-01-23 20:42:37 +03:00
Claire Kuang 3508bcc42e Merge pull request #395 from specklesystems/claire/cnx-699-update-github-links-to-point-to-v3
Update README.md to align with main github page
2024-11-01 18:03:35 +00:00
Claire Kuang e59bfb2e12 Update README.md to align with main github page
https://linear.app/speckle/issue/CNX-699/update-github-links-to-point-to-v3
2024-11-01 18:03:11 +00:00
Oğuzhan Koral 3bb4039c78 Update transform value to matrix on send (#394) 2024-10-28 14:19:02 +03:00
Iain Sproat bd49e19c9e chore(domains): update to *.speckle.systems domains (#343)
* chore(domains): update to *.speckle.systems domains

* Upgrade xcode to 13.4.1

---------

Co-authored-by: oguzhankoral <oguzhankoral@gmail.com>
2024-07-20 12:11:45 +03:00
Oğuzhan Koral 73bcd75b78 Sketchup 2024 support (#332)
* Add sqlite3 for ruby 3.2

* Run UI from netlify URL

* Add bundle for ruby 3.2 for mac
2024-05-09 17:52:21 +03:00
Oğuzhan Koral a0578fa35d Update .gitmodules (#331)
Make https accesible to submodule
2024-05-09 15:56:27 +03:00
270 changed files with 427 additions and 18720 deletions
+1 -1
View File
@@ -85,7 +85,7 @@ jobs:
build-connector-mac:
macos:
xcode: 12.5.1
xcode: 13.4.1
parameters:
projname:
type: string
+1 -1
View File
@@ -1,3 +1,3 @@
[submodule "_sqlite3"]
path = _sqlite3
url = git@github.com:specklesystems/sketchup-sqlite3.git
url = https://github.com/specklesystems/sketchup-sqlite3.git
+15 -36
View File
@@ -2,44 +2,14 @@
<img src="https://user-images.githubusercontent.com/2679513/131189167-18ea5fe1-c578-47f6-9785-3748178e4312.png" width="150px"/><br/>
Speckle | SketchUp
</h1>
<p align="center"><a href="https://twitter.com/SpeckleSystems"><img src="https://img.shields.io/twitter/follow/SpeckleSystems?style=social" alt="Twitter Follow"></a> <a href="https://speckle.community"><img src="https://img.shields.io/discourse/users?server=https%3A%2F%2Fspeckle.community&amp;style=flat-square&amp;logo=discourse&amp;logoColor=white" alt="Community forum users"></a> <a href="https://speckle.systems"><img src="https://img.shields.io/badge/https://-speckle.systems-royalblue?style=flat-square" alt="website"></a> <a href="https://speckle.guide/dev/"><img src="https://img.shields.io/badge/docs-speckle.guide-orange?style=flat-square&amp;logo=read-the-docs&amp;logoColor=white" alt="docs"></a></p>
> Speckle is the first AEC data hub that connects with your favorite AEC tools. Speckle exists to overcome the challenges of working in a fragmented industry where communication, creative workflows, and the exchange of data are often hindered by siloed software and processes. It is here to make the industry better.
<h3 align="center">
Connector for SketchUp
</h3>
<p align="center"><b>Speckle</b> is the data infrastructure for the AEC industry.</p><br/>
<p align="center"><a href="https://twitter.com/SpeckleSystems"><img src="https://img.shields.io/twitter/follow/SpeckleSystems?style=social" alt="Twitter Follow"></a> <a href="https://speckle.community"><img src="https://img.shields.io/discourse/users?server=https%3A%2F%2Fspeckle.community&amp;style=flat-square&amp;logo=discourse&amp;logoColor=white" alt="Community forum users"></a> <a href="https://speckle.systems"><img src="https://img.shields.io/badge/https://-speckle.systems-royalblue?style=flat-square" alt="website"></a> <a href="https://speckle.guide/dev/"><img src="https://img.shields.io/badge/docs-speckle.guide-orange?style=flat-square&amp;logo=read-the-docs&amp;logoColor=white" alt="docs"></a></p>
<p align="center"><a href="https://github.com/specklesystems/speckle-blender/"><img src="https://circleci.com/gh/specklesystems/speckle-blender.svg?style=svg&amp;circle-token=76eabd350ea243575cbb258b746ed3f471f7ac29" alt="Speckle-Next"></a> </p>
# About Speckle
What is Speckle? Check our ![YouTube Video Views](https://img.shields.io/youtube/views/B9humiSpHzM?label=Speckle%20in%201%20minute%20video&style=social)
### Features
- **Object-based:** say goodbye to files! Speckle is the first object based platform for the AEC industry
- **Version control:** Speckle is the Git & Hub for geometry and BIM data
- **Collaboration:** share your designs collaborate with others
- **3D Viewer:** see your CAD and BIM models online, share and embed them anywhere
- **Interoperability:** get your CAD and BIM models into other software without exporting or importing
- **Real time:** get real time updates and notifications and changes
- **GraphQL API:** get what you need anywhere you want it
- **Webhooks:** the base for a automation and next-gen pipelines
- **Built for developers:** we are building Speckle with developers in mind and got tools for every stack
- **Built for the AEC industry:** Speckle connectors are plugins for the most common software used in the industry such as Revit, Rhino, Grasshopper, AutoCAD, Civil 3D, Excel, Unreal Engine, Unity, QGIS, Blender and more!
### Try Speckle now!
Give Speckle a try in no time by:
- [![speckle XYZ](https://img.shields.io/badge/https://-speckle.xyz-0069ff?style=flat-square&logo=hackthebox&logoColor=white)](https://speckle.xyz) ⇒ creating an account at our public server
- [![create a droplet](https://img.shields.io/badge/Create%20a%20Droplet-0069ff?style=flat-square&logo=digitalocean&logoColor=white)](https://marketplace.digitalocean.com/apps/speckle-server?refcode=947a2b5d7dc1) ⇒ deploying an instance in 1 click
### Resources
- [![Community forum users](https://img.shields.io/badge/community-forum-green?style=for-the-badge&logo=discourse&logoColor=white)](https://speckle.community) for help, feature requests or just to hang with other speckle enthusiasts, check out our community forum!
- [![website](https://img.shields.io/badge/tutorials-speckle.systems-royalblue?style=for-the-badge&logo=youtube)](https://speckle.systems) our tutorials portal is full of resources to get you started using Speckle
- [![docs](https://img.shields.io/badge/docs-speckle.guide-orange?style=for-the-badge&logo=read-the-docs&logoColor=white)](https://speckle.guide/user/blender.html) reference on almost any end-user and developer functionality
# Repo structure
@@ -66,6 +36,15 @@ This repo is split into three parts:
After building `sqlite3.sln` file, compiled `sqlite3.so` (for Windows) and `sqlite3.bundle` (for OSX) dynamic library files are created
by solution to place them into source code into `speckle_connector/src/ext`. Building this project should be only
happen when SketchUp starts to support newer Ruby versions (currently it is `2.7`).
### Other repos
Make sure to also check and ⭐️ these other Speckle next generation repositories:
- [`speckle-sharp-connectors`](https://github.com/specklesystems/speckle-sharp-connectors): .NET connectors and desktop UI
- [`speckle-sharp-sdk`](https://github.com/specklesystems/speckle-sharp-sdk): our .NET SDK for next gen connectors and development
- [`speckle-powerbi`](https://github.com/specklesystems/speckle-powerbi): PowerBi connector
- and more [connectors & tooling](https://github.com/specklesystems/)!
## Contribution Guide
@@ -194,4 +173,4 @@ To track your code quality locally,
2. To check overall state of repository by RubyCritic, run below
```ruby
rake rubycritic
```
```
+1 -1
View File
@@ -24,7 +24,7 @@ module SpeckleConnector
# Run from localhost or from build files
DEV_MODE = false
puts("Loading Speckle Connector v#{CONNECTOR_VERSION} from #{DEV_MODE ? 'dev' : 'build'}")
puts("Loading Speckle (Legacy) Connector v#{CONNECTOR_VERSION} from #{DEV_MODE ? 'dev' : 'build'}")
unless file_loaded?(__FILE__)
ex = SketchupExtension.new('Speckle SketchUp', File.join(PATH, 'bootstrap'))
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

@@ -7,7 +7,6 @@ require_relative '../constants/path_constants'
module SpeckleConnector
# Accounts to communicate with models on user's account.
module Accounts
# Load accounts from user's app data.
def self.load_accounts
db_path = SPECKLE_ACCOUNTS_DB_PATH
unless File.exist?(db_path)
@@ -24,21 +23,9 @@ module SpeckleConnector
rows.map { |row| JSON.parse(row[1]) }
end
def self.get_account_by_id(id)
accounts = load_accounts
accounts.select { |acc| acc['id'] == id }[0]
end
# Default account on the user computer.
def self.default_account
accounts = load_accounts
accounts.select { |acc| acc['isDefault'] }[0] || accounts[0]
end
# Try to get local server account for debug/test purposes.
def self.try_get_local_server_account
accounts = load_accounts
accounts.select { |acc| acc['serverInfo']['url'].include?('localhost') }[0] || nil
end
end
end
@@ -15,7 +15,7 @@ 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)
state = DeactivateDiffing.update_state(state, nil, {})
state = DeactivateDiffing.update_state(state, {})
puts "Diffing activated for #{@stream_id}"
speckle_entities = state.speckle_state.speckle_entities
invalid_speckle_entities = speckle_entities.select do |_id, entity|
@@ -1,28 +0,0 @@
# frozen_string_literal: true
require_relative 'add_send_model_card'
require_relative 'add_receive_model_card'
require_relative '../action'
require_relative '../../cards/send_card'
require_relative '../../cards/receive_card'
require_relative '../../filters/send/everything_filter'
require_relative '../../filters/send/selection_filter'
require_relative '../../filters/send_filters'
require_relative '../../sketchup_model/dictionary/model_card_dictionary_handler'
module SpeckleConnector
module Actions
# Action to add send card.
class AddModel < 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, resolve_id, data)
if data['typeDiscriminator'] == 'SenderModelCard'
Actions::AddSendModelCard.update_state(state, resolve_id, data)
else
Actions::AddReceiveModelCard.update_state(state, resolve_id, data)
end
end
end
end
end
@@ -1,50 +0,0 @@
# frozen_string_literal: true
require_relative '../action'
require_relative '../../cards/send_card'
require_relative '../../cards/receive_card'
require_relative '../../cards/receive_result'
require_relative '../../filters/send/everything_filter'
require_relative '../../filters/send/selection_filter'
require_relative '../../filters/send_filters'
require_relative '../../sketchup_model/dictionary/model_card_dictionary_handler'
module SpeckleConnector
module Actions
# Action to add receive model card.
class AddReceiveModelCard < 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, resolve_id, data)
model_card_id = data['modelCardId']
account_id = data['accountId']
project_id = data['projectId']
model_id = data['modelId']
project_name = data['projectName']
model_name = data['modelName']
expired = data['expired']
selected_version_id = data['selectedVersionId']
latest_version_id = data['latestVersionId']
has_dismissed_update_warning = data['hasDismissedUpdateWarning']
if data['receiveResult']
receive_result = Cards::ReceiveResult.new(
data['receiveResult']['bakedObjectIds'].values, # NOTE: IDK why UI sends it as object...
data['receiveResult']['display']
)
end
receive_card = Cards::ReceiveCard.new(model_card_id, account_id,
project_id, model_id,
project_name, model_name,
selected_version_id, latest_version_id,
has_dismissed_update_warning, expired, receive_result)
SketchupModel::Dictionary::ModelCardDictionaryHandler
.save_receive_card_to_model(receive_card, state.sketchup_state.sketchup_model)
new_speckle_state = state.speckle_state.with_receive_card(receive_card)
state = state.with_speckle_state(new_speckle_state)
js_script = "baseBinding.receiveResponse('#{resolve_id}')"
return state.with_add_queue_js_command('addReceiveCard', js_script)
end
end
end
end
@@ -1,34 +0,0 @@
# frozen_string_literal: true
require_relative '../action'
require_relative '../../cards/send_card'
require_relative '../../cards/receive_card'
require_relative '../../filters/send/everything_filter'
require_relative '../../filters/send/selection_filter'
require_relative '../../filters/send_filters'
require_relative '../../sketchup_model/dictionary/model_card_dictionary_handler'
module SpeckleConnector
module Actions
# Action to add send model card.
class AddSendModelCard < 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, resolve_id, data)
send_filter = Filters::SendFilters.get_filter_from_ui_data(data['sendFilter'])
# Init card and add to the state
send_card = Cards::SendCard.new(data['modelCardId'], data['accountId'], data['projectId'], data['modelId'],
data['latestCreatedVersionId'], send_filter, {})
SketchupModel::Dictionary::ModelCardDictionaryHandler
.save_send_card_to_model(send_card, state.sketchup_state.sketchup_model)
new_speckle_state = state.speckle_state.with_send_card(send_card)
state = state.with_speckle_state(new_speckle_state)
# Resolve promise
js_script = "baseBinding.receiveResponse('#{resolve_id}')"
state.with_add_queue_js_command('addSendCard', js_script)
end
end
end
end
@@ -1,17 +0,0 @@
# frozen_string_literal: true
require_relative '../action'
module SpeckleConnector
module Actions
# Get connector version.
class GetConnectorVersion < 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, resolve_id)
js_command = "baseBinding.receiveResponse('#{resolve_id}', '#{CONNECTOR_VERSION}')"
state.with_add_queue_js_command('getConnectorVersion', js_command)
end
end
end
end
@@ -1,73 +0,0 @@
# frozen_string_literal: true
require_relative '../action'
require_relative '../../filters/send_filters'
require_relative '../../cards/receive_result'
require_relative '../../sketchup_model/dictionary/model_card_dictionary_handler'
module SpeckleConnector
module Actions
# Gets document state.
class GetDocumentState < 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, resolve_id)
send_cards_hash = SketchupModel::Dictionary::ModelCardDictionaryHandler
.get_send_cards_from_dict(state.sketchup_state.sketchup_model)
send_cards = send_cards_hash.collect do |id, card|
filter = Filters::SendFilters.get_filter_from_document(card['sendFilter'])
send_card = Cards::SendCard.new(id, card['account_id'], card['project_id'], card['model_id'], card['latest_created_version_id'], filter, {})
new_speckle_state = state.speckle_state.with_send_card(send_card)
state = state.with_speckle_state(new_speckle_state)
{
modelCardId: send_card.model_card_id,
accountId: send_card.account_id,
projectId: send_card.project_id,
modelId: send_card.model_id,
sendFilter: send_card.send_filter,
latestCreatedVersionId: send_card.latest_created_version_id,
typeDiscriminator: send_card.type_discriminator
}
end
receive_cards_hash = SketchupModel::Dictionary::ModelCardDictionaryHandler
.get_receive_cards_from_dict(state.sketchup_state.sketchup_model)
receive_cards = receive_cards_hash.collect do |id, card|
receive_result = Cards::ReceiveResult.new(
card['receive_result']['bakedObjectIds'],
card['receive_result']['display']
)
receive_card = Cards::ReceiveCard.new(id, card['account_id'], card['project_id'], card['model_id'],
card['project_name'], card['model_name'], card['selected_version_id'],
card['latest_version_id'], card['has_dismissed_update_warning'],
card['expired'], receive_result)
new_speckle_state = state.speckle_state.with_receive_card(receive_card)
state = state.with_speckle_state(new_speckle_state)
{
modelCardId: receive_card.model_card_id,
accountId: receive_card.account_id,
projectId: receive_card.project_id,
modelId: receive_card.model_id,
projectName: receive_card.project_name,
modelName: receive_card.model_name,
selectedVersionId: receive_card.selected_version_id,
latestVersionId: receive_card.latest_version_id,
hasDismissedUpdateWarning: receive_card.has_dismissed_update_warning,
expired: receive_card.expired,
receiveResult: receive_card.receive_result,
typeDiscriminator: receive_card.type_discriminator
}
end
model_state = { models: send_cards + receive_cards }
js_script = "baseBinding.receiveResponse('#{resolve_id}', #{model_state.to_json})"
state.with_add_queue_js_command('getDocumentState', js_script)
end
end
end
end
@@ -1,19 +0,0 @@
# frozen_string_literal: true
require_relative '../action'
require_relative '../../filters/send_filters'
module SpeckleConnector
module Actions
# Action to get send filter.
class GetSendFilters < 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, resolve_id)
default_filters = Filters::SendFilters.get_default(state.sketchup_state.sketchup_model)
js_script = "sendBinding.receiveResponse('#{resolve_id}', #{default_filters.to_json})"
state.with_add_queue_js_command('getSendFilter', js_script)
end
end
end
end
@@ -1,17 +0,0 @@
# frozen_string_literal: true
require_relative '../action'
module SpeckleConnector
module Actions
# Get source app name.
class GetSourceAppName < 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, resolve_id)
js_command = "baseBinding.receiveResponse('#{resolve_id}', 'sketchup')"
state.with_add_queue_js_command('getSourceAppName', js_command)
end
end
end
end
@@ -1,17 +0,0 @@
# frozen_string_literal: true
require_relative '../action'
module SpeckleConnector
module Actions
# Get source app version.
class GetSourceAppVersion < 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, resolve_id)
js_command = "baseBinding.receiveResponse('#{resolve_id}', #{SKETCHUP_VERSION})"
state.with_add_queue_js_command('getSourceAppVersion', js_command)
end
end
end
end
@@ -1,45 +0,0 @@
# frozen_string_literal: true
require_relative '../action'
require_relative '../../sketchup_model/query/entity'
module SpeckleConnector
module Actions
# Action to add send card.
class HighlightModel < 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, resolve_id, model_card_id)
receiver_card = state.speckle_state.receive_cards[model_card_id]
sender_card = state.speckle_state.send_cards[model_card_id]
card = receiver_card || sender_card
objects_to_highlight = if card.type_discriminator == 'ReceiverModelCard'
state.speckle_state.receive_cards[model_card_id].receive_result.baked_object_ids
else
state.speckle_state.send_cards[model_card_id].send_filter.selected_object_ids
end
state.sketchup_state.sketchup_model.selection.clear
# Flat entities to select entities on card
flat_entities = SketchupModel::Query::Entity.flat_entities(state.sketchup_state.sketchup_model.entities)
flat_entities.each do |entity|
next unless objects_to_highlight.include?(entity.persistent_id)
if entity.is_a?(Sketchup::ComponentDefinition)
state.sketchup_state.sketchup_model.selection.add(entity.instances)
end
state.sketchup_state.sketchup_model.selection.add(entity)
end
state.sketchup_state.sketchup_model.active_view.zoom(state.sketchup_state.sketchup_model.selection)
# Resolve promise
js_script = "baseBinding.receiveResponse('#{resolve_id}')"
state.with_add_queue_js_command('highlightModel', js_script)
end
end
end
end
@@ -1,31 +0,0 @@
# frozen_string_literal: true
require_relative '../action'
require_relative '../../cards/send_card'
require_relative '../../cards/receive_card'
require_relative '../../filters/send/everything_filter'
require_relative '../../filters/send/selection_filter'
require_relative '../../filters/send_filters'
require_relative '../../sketchup_model/dictionary/model_card_dictionary_handler'
module SpeckleConnector
module Actions
# Action to remove send card.
class RemoveModel < 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, resolve_id, data)
SketchupModel::Dictionary::ModelCardDictionaryHandler.remove_card_dict(state.sketchup_state.sketchup_model, data)
new_speckle_state = if data['typeDiscriminator'] == 'ReceiverModelCard'
state.speckle_state.without_receive_card(data['id'])
else
state.speckle_state.without_send_card(data['id'])
end
state = state.with_speckle_state(new_speckle_state)
# Resolve promise
js_script = "baseBinding.receiveResponse('#{resolve_id}')"
state.with_add_queue_js_command('removeModel', js_script)
end
end
end
end
@@ -1,20 +0,0 @@
# frozen_string_literal: true
require_relative '../action'
require_relative '../../sketchup_model/dictionary/model_card_dictionary_handler'
module SpeckleConnector
module Actions
# Action to update send filter.
class UpdateSendFilter < 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, resolve_id, data, value)
SketchupModel::Dictionary::ModelCardDictionaryHandler.update_filter(state.sketchup_state.sketchup_model, data, value)
js_script = "sendBinding.receiveResponse('#{resolve_id}')"
state.with_add_queue_js_command('updateSendFilter', js_script)
end
end
end
end
@@ -9,7 +9,7 @@ module SpeckleConnector
class ClearMapperSource < 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, _resolve_id, _data)
def self.update_state(state, _data)
new_speckle_state = state.speckle_state.with_removed_mapper_source
erase_levels(state)
state.with_speckle_state(new_speckle_state)
@@ -11,7 +11,7 @@ module SpeckleConnector
class ClearMappingsFromTable < Action
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def self.update_state(state, _resolve_id, data)
def self.update_state(state, data)
# Flat entities to clear mappings
flat_entities = SketchupModel::Query::Entity.flat_entities(state.sketchup_state.sketchup_model.entities)
@@ -10,7 +10,7 @@ module SpeckleConnector
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, _resolve_id, _data)
def self.update_state(state, _data)
state.with_add_queue('collectPreferences', state.user_state.preferences.to_json, [])
end
end
@@ -8,7 +8,7 @@ module SpeckleConnector
class CollectVersions < 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, _resolve_id, _data)
def self.update_state(state, _data)
versions = {
sketchup: Sketchup.version.to_i,
speckle: SpeckleConnector::CONNECTOR_VERSION
@@ -1,29 +0,0 @@
# frozen_string_literal: true
require_relative '../action'
module SpeckleConnector
module Actions
# Action to get config.
class GetConfig < 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, resolve_id)
# Previously it was stored in user state
# config = state.user_state.preferences.to_json
config = {
global: {
},
connectors: {
sketchup: {
darkTheme: state.user_state.user_preferences[:dark_theme]
}
}
}
js_script = "configBinding.receiveResponse('#{resolve_id}', #{config.to_json})"
state.with_add_queue_js_command('getConfig', js_script)
end
end
end
end
@@ -1,26 +0,0 @@
# frozen_string_literal: true
require_relative '../action'
require_relative '../user_preferences_updated'
module SpeckleConnector
module Actions
# Action to update config.
class UpdateConfig < Action
KEY_VALUES = {
'darkTheme' => 'dark_theme'
}.freeze
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def self.update_state(state, resolve_id, config)
config.each do |key, value|
state = Actions::UserPreferencesUpdated.new('configSketchup', KEY_VALUES[key], value).update_state(state)
end
js_script = "configBinding.receiveResponse('#{resolve_id}')"
state.with_add_queue_js_command('updateConfig', js_script)
end
end
end
end
@@ -8,7 +8,7 @@ module SpeckleConnector
class DeactivateDiffing < 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, _resolve_id, _data)
def self.update_state(state, _data)
puts 'Diffing deactivated!'
speckle_entities = state.speckle_state.speckle_entities
diffing_activated_speckle_entities = speckle_entities.reject do |_id, entity|
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require_relative 'event_action'
require_relative 'on_document_changed'
require_relative '../load_sketchup_model'
require_relative '../collect_preferences'
@@ -26,8 +25,7 @@ module SpeckleConnector
# Action to let UI to render itself with new preferences state
# TODO: Later UI should be updated if any stream is invalid after
# we collected speckle_entities appropriately
new_state = CollectPreferences.update_state(new_state, nil, {})
OnDocumentChanged.update_state(new_state)
CollectPreferences.update_state(new_state, {})
end
end
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require_relative 'event_action'
require_relative '../../actions/send_actions/send_card_expiration_check'
require_relative '../../sketchup_model/utils/face_utils'
require_relative '../../constants/dict_constants'
@@ -17,90 +16,33 @@ module SpeckleConnector
modified_entities = event_data.to_a.collect { |e| e[1] }
# do not copy speckle base object specific attributes, because they are entity specific
modified_entities.each { |entity| entity.delete_attribute(SPECKLE_BASE_OBJECT) }
wrapped_entity_ids = wrapped_entity_ids(modified_entities)
state = EntitiesEventAction.run_expiration_checks(state, wrapped_entity_ids) if wrapped_entity_ids.any?
attach_edge_entity_observer(modified_entities.grep(Sketchup::Edge), state.speckle_state.observers[ENTITY_OBSERVER])
state
end
def self.wrapped_entity_ids(modified_entities)
wrapped_entity_ids = []
modified_entities.select { |e| e.respond_to?(:definition) }.each do |c|
wrapped_entity_ids += c.definition.entities.collect(&:persistent_id)
end
wrapped_entity_ids
end
# It is needed for attaching EntityObserver to newly added edges to track them with a hacky way.
# This hacky way is because of limitation on Sketchup API that observer cannot catch changes on Edges
# with EntitiesObserver.
def self.attach_edge_entity_observer(edges, observer)
edges.each do |edge|
edge.add_observer(observer)
edge.start.add_observer(observer)
edge.end.add_observer(observer)
end
end
end
# Event action when element modified.
class OnElementModified
# @param state [States::State] the current state of the SpeckleConnector Application
def self.update_state(state, event_data)
# modified_entity = event_data[0][1]
modified_entities = event_data.collect { |data| data[1] }.to_a
# near_faces = get_near_faces(modified_entities)
definition_faces = get_definition_faces(modified_entities)
modified_entity_ids = modified_entities.collect(&:persistent_id) + definition_faces.collect(&:persistent_id)
parent_ids = parent_ids(state.sketchup_state.sketchup_model)
modified_entity_ids += parent_ids
state = EntitiesEventAction.run_expiration_checks(state, modified_entity_ids)
# if modified_entity.is_a?(Sketchup::Face)
# path = state.sketchup_state.sketchup_model.active_path
# modified_faces = SketchupModel::Utils::FaceUtils.near_faces(modified_entity.edges)
# path_objects = path.nil? ? [] : path + path.collect(&:definition)
# parent_ids = path_objects.collect(&:persistent_id)
# ids_to_invalidate = modified_faces.collect(&:persistent_id) + parent_ids
# entities_to_invalidate = speckle_entities_to_invalidate(speckle_state, ids_to_invalidate)
# new_speckle_state = invalidate_speckle_entities(speckle_state, entities_to_invalidate)
# # This is the place we can send information to UI for diffing check
# diffing = state.user_state.preferences[:user][:diffing]
# new_speckle_state = new_speckle_state.with_invalid_streams_queue if diffing
# return state.with_speckle_state(new_speckle_state)
# end
speckle_state = state.speckle_state
modified_entity = event_data[0][1]
if modified_entity.is_a?(Sketchup::Face)
path = state.sketchup_state.sketchup_model.active_path
modified_faces = SketchupModel::Utils::FaceUtils.near_faces(modified_entity.edges)
path_objects = path.nil? ? [] : path + path.collect(&:definition)
parent_ids = path_objects.collect(&:persistent_id)
ids_to_invalidate = modified_faces.collect(&:persistent_id) + parent_ids
entities_to_invalidate = speckle_entities_to_invalidate(speckle_state, ids_to_invalidate)
new_speckle_state = invalidate_speckle_entities(speckle_state, entities_to_invalidate)
# This is the place we can send information to UI for diffing check
diffing = state.user_state.preferences[:user][:diffing]
new_speckle_state = new_speckle_state.with_invalid_streams_queue if diffing
return state.with_speckle_state(new_speckle_state)
end
state
end
def self.get_near_faces(modified_entities)
near_faces = []
modified_entities.each do |modified_entity|
next unless modified_entity.is_a?(Sketchup::Face)
near_faces += SketchupModel::Utils::FaceUtils.near_faces(modified_entity.edges)
end
near_faces
end
def self.get_definition_faces(modified_entities)
definition_faces = []
modified_entities.each do |modified_entity|
next unless modified_entity.is_a?(Sketchup::Face)
next unless modified_entity.parent.is_a?(Sketchup::ComponentDefinition)
definition_faces += modified_entity.parent.entities.grep(Sketchup::Face)
end
definition_faces
end
def self.parent_ids(sketchup_model)
path = sketchup_model.active_path
path_objects = path.nil? ? [] : path + path.collect(&:definition)
path_objects.collect(&:persistent_id)
end
# @param speckle_state [States::SpeckleState] the current state of the Speckle
def self.speckle_entities_to_invalidate(speckle_state, ids)
speckle_state.speckle_entities.to_h.select { |id, _| ids.include?(id) }
@@ -120,22 +62,12 @@ module SpeckleConnector
# Event action when element removed.
class OnElementRemoved
# @param state [States::State] the current state of the SpeckleConnector Application
def self.update_state(state, event_data)
modified_entity_ids = event_data.collect { |data| data[1] }.to_a
new_speckle_state = state.speckle_state.with_changed_entity_ids(modified_entity_ids)
state = state.with_speckle_state(new_speckle_state)
Actions::SendCardExpirationCheck.update_state(state)
def self.update_state(state, _event_data)
# TODO: Do state updates when element removed
state
end
end
# @param state [States::State] the current state of the SpeckleConnector Application
# @param changed_entity_ids [Array<Integer> | Array<String>] list of changed entity ids
def self.run_expiration_checks(state, changed_entity_ids)
new_speckle_state = state.speckle_state.with_changed_entity_persistent_ids(changed_entity_ids)
state = state.with_speckle_state(new_speckle_state)
Actions::SendCardExpirationCheck.update_state(state)
end
# Handlers that are used to handle specific events
ACTIONS = {
onElementRemoved: OnElementRemoved,
@@ -1,45 +0,0 @@
# frozen_string_literal: true
require_relative 'event_action'
require_relative '../../constants/dict_constants'
module SpeckleConnector
module Actions
module Events
# Event actions related to entities.
class EntityEventAction < EventAction
# Event action when entity modified/changed.
# PS: this handler action only triggers for edges and it's vertices since we attach EntityObserver to
# only edge and vertex entities. This is a limitation of the Sketchup API that can't handles edges with
# EntitiesObserver.
class OnChangeEntity
# @param state [States::State] the current state of the SpeckleConnector Application
def self.update_state(state, event_data)
edges = []
event_data.each do |event_d|
event_d.each do |d|
next if d.deleted?
edges.append(d) if d.is_a?(Sketchup::Edge)
edges += d.edges if d.is_a?(Sketchup::Vertex) && d.edges
end
end
edges.uniq!
edge_ids = edges.collect(&:persistent_id)
new_speckle_state = state.speckle_state.with_changed_entity_persistent_ids(edge_ids)
state.with_speckle_state(new_speckle_state)
end
end
# Handlers that are used to handle specific events
ACTIONS = {
onChangeEntity: OnChangeEntity
}.freeze
def self.actions
ACTIONS
end
end
end
end
end
@@ -18,21 +18,12 @@ module SpeckleConnector
sketchup_state = state.sketchup_state
active_path = sketchup_state.sketchup_model.active_path
observers = state.speckle_state.observers
update_entity_observers(active_path, observers)
update_object_observers(active_path, observers)
return state
end
def self.update_entity_observers(path, observers)
unless path.nil?
new_path_entities = path[-1].definition.entities
new_path_entities.add_observer(observers[ENTITIES_OBSERVER])
edges = new_path_entities.grep(Sketchup::Edge)
edges.each do |edge|
edge.add_observer(observers[ENTITY_OBSERVER])
edge.start.add_observer(observers[ENTITY_OBSERVER])
edge.end.add_observer(observers[ENTITY_OBSERVER])
end
end
def self.update_object_observers(path, observers)
path[-1].definition.entities.add_observer(observers[ENTITIES_OBSERVER]) unless path.nil?
end
end
@@ -1,15 +0,0 @@
# frozen_string_literal: true
module SpeckleConnector
module Actions
# Triggers whenever document has changed.
class OnDocumentChanged < 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)
js_command = "baseBinding.emit('documentChanged')"
state.with_add_queue_js_command('documentChanged', js_command)
end
end
end
end
@@ -2,7 +2,6 @@
require_relative 'event_action'
require_relative '../mapper_selection_changed'
require_relative '../selection_actions/get_selection'
require_relative '../../mapper/category/revit_category'
require_relative '../../sketchup_model/reader/speckle_entities_reader'
require_relative '../../sketchup_model/reader/mapper_reader'
@@ -18,18 +17,12 @@ module SpeckleConnector
def self.update_state(state, event_data)
return state unless event_data&.any?
# POC: Not happy with it. We log also entity.entityID property since
# onElementRemoved observer only return them! :/ Reconsider this in BETA!
selected_object_ids = state.sketchup_state.sketchup_model.selection.collect(&:persistent_id) +
state.sketchup_state.sketchup_model.selection.collect(&:entityID)
summary = "Selected #{selected_object_ids.length / 2} objects." # POC: OFFF. I'll fix it
selection_info = UiData::Sketchup::SelectionInfo.new(selected_object_ids, summary)
js_script = "selectionBinding.emit('setSelection', #{selection_info.to_json})"
state.with_add_queue_js_command('setSelection', js_script)
# Get sketchup selection
sketchup_selection = state.sketchup_state.sketchup_model.selection
# Collect and return mapper selection info.
# Later we can add more selection info for different scopes.
# MapperSelectionChanged.new(sketchup_selection).update_state(state)
MapperSelectionChanged.new(sketchup_selection).update_state(state)
end
end
end
@@ -1,21 +0,0 @@
# frozen_string_literal: true
require_relative 'action'
require_relative '../accounts/accounts'
require_relative 'load_saved_streams'
module SpeckleConnector
module Actions
# Action to initialize local accounts from database.
class GetAccounts < 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, resolve_id)
puts 'Initialisation of Speckle accounts requested by plugin'
accounts_data = state.speckle_state.accounts
js_script = "accountsBinding.receiveResponse('#{resolve_id}', #{accounts_data.to_json})"
state.with_add_queue_js_command('getAccounts', js_script)
end
end
end
end
@@ -1,22 +0,0 @@
# frozen_string_literal: true
require_relative 'action'
module SpeckleConnector
module Actions
# Get document info.
class GetDocumentInfo < 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, resolve_id)
document_info = {
location: state.sketchup_state.sketchup_model.path,
name: state.sketchup_state.sketchup_model.name,
id: state.sketchup_state.sketchup_model.guid
}
js_command = "baseBinding.receiveResponse('#{resolve_id}', #{document_info.to_json})"
state.with_add_queue_js_command('getDocumentInfo', js_command)
end
end
end
end
@@ -1,31 +0,0 @@
# frozen_string_literal: true
module SpeckleConnector
module Actions
# Action to return error message to UI.
class HandleError < Action
# @param error [String] error
# @param view_name [String] name of the view (binding)
# @param action [Action] action that error happened
# @param parameters [Array<String>] arguments
def initialize(error, view_name, action, parameters)
super()
@error = error
@view_name = view_name
@action = action
@args = parameters
end
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def update_state(state)
error_message = "Error: #{@error}\nBinding: #{@view_name}\nAction:#{@action}\nArgs: #{@args}\n"
error = {
error: error_message
}
js_error_script = "#{@view_name}.receiveResponse('#{@args.first}', #{error.to_json})"
state.with_add_queue_js_command("error_#{@view_name}", js_error_script)
end
end
end
end
@@ -10,7 +10,7 @@ module SpeckleConnector
class HideMappingsFromTable < Action
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def self.update_state(state, _resolve_id, data)
def self.update_state(state, data)
# Flat entities to clear mappings
flat_entities = SketchupModel::Query::Entity.flat_entities(state.sketchup_state.sketchup_model.entities)
@@ -10,7 +10,7 @@ module SpeckleConnector
class InitLocalAccounts < 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, _request_id, _data)
def self.update_state(state, _data)
puts 'Initialisation of Speckle accounts requested by plugin'
accounts_data = state.speckle_state.accounts
state.with_add_queue('loadAccounts', accounts_data.to_json, [])
@@ -7,7 +7,6 @@ require_relative '../states/sketchup_state'
require_relative '../accounts/accounts'
require_relative '../preferences/preferences'
require_relative '../constants/observer_constants'
require_relative '../ext/worker'
module SpeckleConnector
module Actions
@@ -15,8 +14,7 @@ module SpeckleConnector
class InitializeSpeckle < 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, observers, instant_message_sender)
worker = SpeckleConnector::Worker.new([])
def self.update_state(state, observers)
attach_app_observer!(observers[APP_OBSERVER])
accounts = SpeckleConnector::Accounts.load_accounts
speckle_state = States::SpeckleState.new(accounts, observers, {}, {})
@@ -24,8 +22,7 @@ module SpeckleConnector
sketchup_state = States::SketchupState.new(Sketchup.active_model)
preferences = Preferences.read_preferences(sketchup_state.sketchup_model)
user_state_with_preferences = state.user_state.with_preferences(preferences)
state = States::State.new(user_state_with_preferences, speckle_state, sketchup_state, false,
worker, &instant_message_sender)
state = States::State.new(user_state_with_preferences, speckle_state, sketchup_state, false)
# This is where we attach observers to related model objects like selection, entities..
Actions::LoadSketchupModel.update_state(state, sketchup_state.sketchup_model)
end
@@ -14,7 +14,7 @@ module SpeckleConnector
# rubocop:disable Metrics/MethodLength
# rubocop:disable Metrics/PerceivedComplexity
# rubocop:disable Metrics/CyclomaticComplexity
def self.update_state(state, _resolve_id, data)
def self.update_state(state, data)
sketchup_model = state.sketchup_state.sketchup_model
# Hide all entities first
@@ -8,7 +8,7 @@ module SpeckleConnector
class LoadSavedStreams < 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, _request_id, _data)
def self.update_state(state, _data)
(saved_streams = state.sketchup_state.sketchup_model
.attribute_dictionary('Speckle', true)['saved_streams']) or []
state.with_add_queue('setSavedStreams', saved_streams, [])
@@ -29,8 +29,6 @@ module SpeckleConnector
# Read speckle entities
new_speckle_entities = SketchupModel::Reader::SpeckleEntitiesReader.read(sketchup_model.entities)
new_speckle_state = new_state.speckle_state.with_speckle_entities(Immutable::Hash.new(new_speckle_entities))
# POC: Reconsider it when we will do caching between sessions!
new_speckle_state = new_speckle_state.with_empty_object_references
# Read mapped entities
new_mapped_entities = SketchupModel::Reader::MapperReader.read_mapped_entities(sketchup_model.entities)
new_speckle_state = new_speckle_state.with_mapped_entities(Immutable::Hash.new(new_mapped_entities))
@@ -53,8 +51,6 @@ module SpeckleConnector
# layers = sketchup_model.layers
# layers.add_observer(observers[LAYERS_OBSERVER_NAME])
entities = sketchup_model.entities
edges = entities.grep(Sketchup::Edge)
edges.each { |edge| edge.add_observer(observers[ENTITY_OBSERVER]) }
entities.add_observer(observers[ENTITIES_OBSERVER])
sketchup_model.add_observer(observers[MODEL_OBSERVER])
# materials = sketchup_model.materials
@@ -9,7 +9,7 @@ module SpeckleConnector
class MappedEntitiesUpdated < Action
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def self.update_state(state, _resolve_id = nil, _data = nil)
def self.update_state(state, _data = nil)
mapped_entities = SketchupModel::Reader::MapperReader
.mapped_entity_details(state.speckle_state.speckle_mapper_state.mapped_entities.values.to_a)
@@ -3,7 +3,6 @@
require_relative 'action'
require_relative 'events/app_event_action'
require_relative 'events/entities_event_action'
require_relative 'events/entity_event_action'
require_relative 'events/model_event_action'
require_relative 'events/selection_event_action'
require_relative '../constants/observer_constants'
@@ -14,7 +13,6 @@ module SpeckleConnector
class OnEventsAction < Action
RUN_ORDER = {
APP_OBSERVER => Events::AppEventAction,
ENTITY_OBSERVER => Events::EntityEventAction,
ENTITIES_OBSERVER => Events::EntitiesEventAction,
MODEL_OBSERVER => Events::ModelEventAction,
# MATERIALS_OBSERVER => Events::MaterialsEventAction,
@@ -1,18 +0,0 @@
# frozen_string_literal: true
require_relative '../action'
module SpeckleConnector
module Actions
# Action to let sketchup know receive from server is finished..
class AfterReceive < 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, resolve_id, stream_id, root_id)
puts "receive finished for: #{root_id}"
js_script = "sketchupReceiveBinding.receiveResponse('#{resolve_id}')"
state.with_add_queue_js_command('afterReceive', js_script)
end
end
end
end
@@ -1,18 +0,0 @@
# frozen_string_literal: true
require_relative '../action'
module SpeckleConnector
module Actions
# Action to let sketchup know receive will be started.
class BeforeReceive < 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, resolve_id, stream_id, root_id)
puts "receive started for: #{root_id}"
js_script = "sketchupReceiveBinding.receiveResponse('#{resolve_id}')"
state.with_add_queue_js_command('beforeReceive', js_script)
end
end
end
end
@@ -1,91 +0,0 @@
# frozen_string_literal: true
require 'json'
require_relative '../action'
require_relative '../../convertors/units'
require_relative '../../convertors/to_native'
module SpeckleConnector
module Actions
# Clear mappings for selected entities.
class ReceiveSingleObject < 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, resolve_id, stream_id, root_id, speckle_objects)
puts "object receive #{speckle_objects.length}"
buffer = speckle_objects.collect { |obj| [obj['id'], obj] }.to_h
t_0 = Time.now.to_f
root_obj = traverse_and_construct(speckle_objects.first, buffer)
puts root_obj
puts "Elapsed traverse and construct #{Time.now.to_f - t_0}"
# File.open("#{ENV['HOME']}/OneDrive/Masaüstü/root.json", 'w') do |f|
# f.write(JSON.pretty_generate(root_obj))
# end
# converter = Converters::ToNative.new(state, stream_id, 'test', 'testt', 'test')
# state = converter.receive_commit_object(root_obj)
js_script = "sketchupReceiveBinding.receiveResponse('#{resolve_id}')"
state.with_add_queue_js_command('receiveObject', js_script)
end
def self.traverse_and_construct(obj, buffer)
return if obj.nil?
return obj if !obj.is_a?(Hash) && !obj.is_a?(Array)
# Handle arrays
if obj.is_a?(Array) && !obj.empty?
arr = handle_array(buffer, obj)
# De-chunk, if array is a set of datachunk, flat them into single data chunk.
arr = try_dechunk(arr)
return arr
end
# Handle object
obj = handle_hash(buffer, obj)
return obj
rescue StandardError => e
puts "#{e} -> #{obj}"
return nil
end
def self.handle_array(buffer, obj)
arr = []
obj.collect do |element|
next if element.nil?
deref = element.is_a?(Hash) && !element['referencedId'].nil? ? buffer[element['referencedId']] : element
arr.append(traverse_and_construct(deref, buffer))
end
arr
end
def self.try_dechunk(arr)
if arr[0].is_a?(Hash) && !arr[0]['speckle_type'].nil? && arr[0]['speckle_type'].downcase.include?('datachunk')
sum_arr = []
arr.each do |chunk|
sum_arr += chunk['data']
end
sum_arr
else
arr
end
end
def self.handle_hash(buffer, obj)
obj.each do |prop, value|
next if value.nil? || (!value.is_a?(Hash) && !value.is_a?(Array))
obj[prop] = buffer[value['referencedId']] if value.is_a?(Hash) && value['referencedId']
obj[prop] = traverse_and_construct(obj[prop], buffer)
end
obj
end
end
end
end
@@ -1,42 +0,0 @@
# frozen_string_literal: true
require_relative '../action'
require_relative '../../convertors/to_native'
require_relative '../../cards/receive_result'
require_relative '../../ext/TT_Lib2/progressbar'
module SpeckleConnector
module Actions
# Receive from server.
class AfterGetObjects < 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, resolve_id, model_card_id, source_application, root_obj)
model_card = state.speckle_state.receive_cards[model_card_id]
state.sketchup_state.sketchup_model.start_operation('Receive Speckle Objects', true)
converter = Converters::ToNative.new(state, model_card.model_id, model_card.project_name,
model_card.model_name, source_application, model_card_id)
start_time = Time.now.to_f
# Have side effects on the sketchup model. It effects directly on the entities by adding new objects.
state = converter.receive_commit_object(root_obj)
elapsed_time = (Time.now.to_f - start_time).round(3)
state.sketchup_state.sketchup_model.commit_operation
puts "==== Converting to Native executed in #{elapsed_time} sec ===="
puts "==== Source application is #{@source_app}. ===="
# Where we send info about received top level (for the sake of handling with less) objects.
top_objects = converter.converted_entities.reject(&:deleted?).select { |e| e.parent.is_a?(Sketchup::Model) }
top_object_ids = top_objects.collect(&:persistent_id)
args = {
modelCardId: model_card_id,
receiveResult: Cards::ReceiveResult.new(top_object_ids, true)
}
receive_result_js_script = "receiveBinding.emit('setModelReceiveResult', #{args.to_json})"
state = state.with_add_queue_js_command('setModelReceiveResult', receive_result_js_script)
resolve_js_script = "receiveBinding.receiveResponse('#{resolve_id}')"
state.with_add_queue_js_command('receive', resolve_js_script)
end
end
end
end
@@ -1,32 +0,0 @@
# frozen_string_literal: true
require_relative '../action'
require_relative '../../accounts/accounts'
require_relative '../../convertors/units'
require_relative '../../convertors/to_speckle'
require_relative '../../operations/send'
require_relative '../../ext/TT_Lib2/progressbar'
module SpeckleConnector
module Actions
# Receive from server.
class Receive < 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, resolve_id, model_card_id)
model_card = state.speckle_state.receive_cards[model_card_id]
resolve_js_script = "receiveBinding.receiveResponse('#{resolve_id}')"
state = state.with_add_queue_js_command('receive', resolve_js_script)
args = {
modelCardId: model_card_id,
projectId: model_card.project_id,
accountId: model_card.account_id,
modelId: model_card.model_id,
selectedVersionId: model_card.selected_version_id
}
js_script = "receiveBinding.emit('receiveViaBrowser', #{args.to_json})"
state.with_add_queue_js_command('receiveViaBrowser', js_script)
end
end
end
end
@@ -3,7 +3,6 @@
require_relative 'action'
require_relative '../convertors/units'
require_relative '../convertors/to_native'
require_relative '../operations/receive'
require_relative '../convertors/clean_up'
module SpeckleConnector
@@ -11,7 +10,7 @@ module SpeckleConnector
# Action to receive objects from Speckle Server.
class ReceiveObjects < Action
# rubocop:disable Metrics/ParameterLists
def initialize(stream_id, base, stream_name, branch_name, branch_id, source_app, object_id)
def initialize(stream_id, base, stream_name, branch_name, branch_id, source_app)
super()
@stream_id = stream_id
@base = base
@@ -19,15 +18,12 @@ module SpeckleConnector
@branch_name = branch_name
@branch_id = branch_id
@source_app = source_app
@object_id = object_id
end
# rubocop:enable Metrics/ParameterLists
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def update_state(state)
# content = Operations.receive(@stream_id, @object_id)
converter = Converters::ToNative.new(state, @stream_id, @stream_name, @branch_name, @source_app)
# Have side effects on the sketchup model. It effects directly on the entities by adding new objects.
start_time = Time.now.to_f
@@ -10,7 +10,7 @@ module SpeckleConnector
class ReloadAccounts < 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, _resolve_id, _data)
def self.update_state(state, _data)
puts 'Reload of Speckle accounts requested by plugin'
new_speckle_state = state.speckle_state.with_accounts(Accounts.load_accounts)
state = state.with_speckle_state(new_speckle_state)
@@ -10,11 +10,11 @@ module SpeckleConnector
class SelectMappingsFromTable < Action
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def self.update_state(state, _resolve_id, data)
def self.update_state(state, data)
# Clear first selection
state.sketchup_state.sketchup_model.selection.clear
# Flat entities to select mapped elements
# Flat entities to clear mappings
flat_entities = SketchupModel::Query::Entity.flat_entities(state.sketchup_state.sketchup_model.entities)
# Collect entity ids to clear mappings
@@ -1,24 +0,0 @@
# frozen_string_literal: true
require_relative '../action'
require_relative '../../ui_data/sketchup/selection_info'
module SpeckleConnector
module Actions
# Action to get selection.
class GetSelection < 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, resolve_id)
# POC: Not happy with it. We log also entity.entityID property since
# onElementRemoved observer only return them! :/ Reconsider this in BETA!
selected_object_ids = state.sketchup_state.sketchup_model.selection.collect(&:persistent_id) +
state.sketchup_state.sketchup_model.selection.collect(&:entityID) # That's bad
summary = "Selected #{selected_object_ids.length / 2} objects." # POC: OFFF. I'll fix it
selection_info = UiData::Sketchup::SelectionInfo.new(selected_object_ids, summary)
js_script = "selectionBinding.receiveResponse('#{resolve_id}', #{selection_info.to_json})"
state.with_add_queue_js_command('getSelection', js_script)
end
end
end
end
@@ -1,22 +0,0 @@
# frozen_string_literal: true
require_relative '../action'
require_relative '../../sketchup_model/dictionary/model_card_dictionary_handler'
module SpeckleConnector
module Actions
# Action to activate send filter.
class ActivateSendFilter < 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, resolve_id, data, value)
SketchupModel::Dictionary::ModelCardDictionaryHandler.update_filter(state.sketchup_state.sketchup_model, data, value)
card_id = "#{data['accountId']}-#{data['projectId']}-#{data['modelId']}"
send_card = state.speckle_state.send_cards[card_id]
puts "Send card filter updated -> #{card_id} -> #{send_card}"
js_script = "sendBindingOld.receiveResponse('#{resolve_id}')"
state.with_add_queue_js_command('activateSendFilter', js_script)
end
end
end
end
@@ -1,22 +0,0 @@
# frozen_string_literal: true
require_relative '../action'
require_relative '../../sketchup_model/dictionary/model_card_dictionary_handler'
module SpeckleConnector
module Actions
# Action to activate send filter tag.
class ActivateSendFilterTag < 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, resolve_id, data, value)
SketchupModel::Dictionary::ModelCardDictionaryHandler.update_tag_filter(state.sketchup_state.sketchup_model, data, value)
card_id = "#{data['accountId']}-#{data['projectId']}-#{data['modelId']}"
send_card = state.speckle_state.send_cards[card_id]
puts "Send card filter updated -> #{card_id} -> #{send_card}"
js_script = "sendBindingOld.receiveResponse('#{resolve_id}')"
state.with_add_queue_js_command('activateSendFilterTag', js_script)
end
end
end
end
@@ -1,96 +0,0 @@
# frozen_string_literal: true
require_relative '../action'
require_relative '../../accounts/accounts'
require_relative '../../convertors/units'
require_relative '../../convertors/to_speckle'
require_relative '../../operations/send'
require_relative '../../ext/TT_Lib2/progressbar'
module SpeckleConnector
module Actions
# Send to server.
class Send < 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, resolve_id, model_card_id)
model_card = state.speckle_state.send_cards[model_card_id]
unless model_card.send_filter.selected_object_ids.any?
resolve_js_script = "sendBinding.receiveResponse('#{resolve_id}')"
state = state.with_add_queue_js_command('resolveSend', resolve_js_script)
args = {
modelCardId: model_card_id,
error: 'No objects were found. Please update your send filter!'
}
js_script = "sendBinding.emit('setModelError', #{args.to_json})"
return state.with_add_queue_js_command('setModelsError', js_script)
end
account = Accounts.get_account_by_id(model_card.account_id)
converter = Converters::ToSpeckle.new(state, model_card.project_id, model_card.send_filter, model_card_id)
new_speckle_state, base = converter.convert_entities_to_base(model_card.send_filter.selected_object_ids,
state.user_state.preferences)
id, total_children_count, batches, refs = converter.serialize(base, state.user_state.preferences)
new_speckle_state = new_speckle_state.with_object_references(model_card.project_id, refs)
new_speckle_state = new_speckle_state.with_empty_changed_entity_persistent_ids
new_speckle_state = new_speckle_state.with_empty_changed_entity_ids
puts("converted #{base.count} objects for stream #{model_card.project_id}")
state = state.with_speckle_state(new_speckle_state)
resolve_js_script = "sendBinding.receiveResponse('#{resolve_id}')"
state = state.with_add_queue_js_command('send', resolve_js_script)
args = {
modelCardId: model_card_id,
projectId: model_card.project_id,
modelId: model_card.model_id,
token: account['token'],
serverUrl: account['serverInfo']['url'],
accountId: model_card.account_id,
message: model_card.message,
sendObject: {
id: id,
totalChildrenCount: total_children_count,
batches: batches
}
}
js_script = "sendBinding.emit('sendViaBrowser', #{args.to_json})"
state.with_add_queue_js_command('sendViaBrowser', js_script)
end
def self.update_state_test(state, resolve_id, model_card_id)
dialog = UI::HtmlDialog.new(
{
:dialog_title => 'Dialog Example',
:preferences_key => 'com.sample.plugin',
:scrollable => true,
:resizable => true,
:width => 600,
:height => 400,
:left => 10,
:top => 10,
:min_width => 50,
:min_height => 50,
:max_width =>1000,
:max_height => 1000,
:style => UI::HtmlDialog::STYLE_DIALOG
})
html = '<div id="hi"><b>Hello world!</b></div>'
dialog.set_html(html)
dialog.show
action = Proc.new do |status|
js_command = "document.getElementById('hi').innerHTML = '<b>#{status}</b>'"
log_js_command = "console.log('test')"
dialog.execute_script(js_command)
dialog.execute_script(log_js_command)
end
selected_object_ids = state.sketchup_state.sketchup_model.selection.collect(&:persistent_id)
state.worker.add_jobs(1000.times.to_a.map { |i| Job.new(i, &action) })
state.worker.do_work(Time.now.to_f, &action)
end
end
end
end
@@ -1,24 +0,0 @@
# frozen_string_literal: true
require_relative '../action'
require_relative '../../sketchup_model/dictionary/model_card_dictionary_handler'
module SpeckleConnector
module Actions
# Action to check send card expirations.
class SendCardExpirationCheck < 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)
return state unless state.speckle_state.changed_entity_persistent_ids.any? || state.speckle_state.changed_entity_ids.any?
expired_send_cards_ids = state.speckle_state.send_cards.select do |_id, send_card|
send_card.send_filter.check_expiry(state.speckle_state.changed_entity_persistent_ids) ||
send_card.send_filter.check_expiry(state.speckle_state.changed_entity_ids)
end.keys.to_a
js_script = "sendBinding.emit('setModelsExpired', #{expired_send_cards_ids.to_json})"
state.with_add_queue_js_command('setModelsExpired', js_script)
end
end
end
end
@@ -4,7 +4,6 @@ require_relative 'action'
require_relative 'deactivate_diffing'
require_relative '../convertors/units'
require_relative '../convertors/to_speckle'
require_relative '../operations/send'
module SpeckleConnector
module Actions
@@ -18,13 +17,11 @@ 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)
state = DeactivateDiffing.update_state(state, nil, {})
converter = Converters::ToSpeckle.new(state, @stream_id, {})
state = DeactivateDiffing.update_state(state, {})
converter = Converters::ToSpeckle.new(state, @stream_id)
new_speckle_state, base = converter.convert_selection_to_base(state.user_state.preferences)
id, total_children_count, batches = converter.serialize(base, state.user_state.preferences)
# TODO: Later active send operation.
# Operations.send(@stream_id, batches)
id, total_children_count, batches, new_speckle_state = converter.serialize(base, new_speckle_state,
state.user_state.preferences)
puts("converted #{base.count} objects for stream #{@stream_id}")
# This is the place we can send information to UI for diffing check
@@ -8,7 +8,7 @@ module SpeckleConnector
class ShowAllEntities < Action
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def self.update_state(state, _resolve_id, _data)
def self.update_state(state, _data)
# Show all entities first
state.sketchup_state.sketchup_model.entities.each do |ent|
ent.hidden = false
@@ -1,46 +0,0 @@
# frozen_string_literal: true
require_relative '../action'
module SpeckleConnector
module Actions
# Action to get user config.
class GetUserConfig < 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, resolve_id)
# Previously it was stored in user state
# config = state.user_state.preferences.to_json
config = [
{
key: 'darkTheme',
title: 'Theme',
type: 'toggle',
config: {
value: state.user_state.user_preferences[:dark_theme]
}
},
{
key: 'diffing',
title: 'Diffing',
type: 'toggle',
config: {
value: state.user_state.user_preferences[:diffing]
}
},
{
key: 'referencePoint',
title: 'Reference Point',
type: 'dropdown',
config: {
value: 'test',
items: ['test', 'test1', 'test2']
}
}
]
js_script = "connectorConfigBinding.receiveResponse('#{resolve_id}', #{config.to_json})"
state.with_add_queue_js_command('getUserConfig', js_script)
end
end
end
end
@@ -1,18 +0,0 @@
# frozen_string_literal: true
require_relative '../action'
module SpeckleConnector
module Actions
# Action to get user config.
class UpdateUserConfig < 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, resolve_id, new_config)
puts new_config.values
js_script = "connectorConfigBinding.receiveResponse('#{resolve_id}')"
state.with_add_queue_js_command('updateUserConfig', js_script)
end
end
end
end
@@ -1,21 +0,0 @@
# frozen_string_literal: true
require_relative '../action'
module SpeckleConnector
module Actions
# Test purpose action.
class GetComplexType < 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, resolve_id)
complex_type = {
id: 'complex_type_id',
count: 3
}
js_script = "testBinding.receiveResponse('#{resolve_id}', #{complex_type.to_json})"
state.with_add_queue_js_command('getComplexType', js_script)
end
end
end
end
@@ -1,18 +0,0 @@
# frozen_string_literal: true
require_relative '../action'
module SpeckleConnector
module Actions
# Test purpose action.
class GoAway < 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, resolve_id)
puts 'SketchUp went away :('
js_script = "testBinding.receiveResponse('#{resolve_id}')"
state.with_add_queue_js_command('goAway', js_script)
end
end
end
end
@@ -1,21 +0,0 @@
# frozen_string_literal: true
require_relative '../action'
module SpeckleConnector
module Actions
# Test purpose action.
class SayHi < 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, resolve_id, name, count, say_hello_not_hi)
said_hi = []
count.times do
said_hi.append("#{say_hello_not_hi ? 'Hello' : 'Hi'} #{name}!")
end
js_script = "testBinding.receiveResponse('#{resolve_id}', #{said_hi})"
state.with_add_queue_js_command('sayHi', js_script)
end
end
end
end
@@ -1,28 +0,0 @@
# frozen_string_literal: true
require_relative '../action'
module SpeckleConnector
module Actions
# Test purpose action.
class TriggerEvent < 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, resolve_id, event_name)
if event_name == 'emptyTestEvent'
js_script = "testBinding.emit('#{event_name}')"
else
args = {
name: 'Oguzhan',
isOk: true,
count: 3
}
js_script = "testBinding.emit('#{event_name}', #{args.to_json})"
end
resolve_js_script = "testBinding.receiveResponse('#{resolve_id}')"
state = state.with_add_queue_js_command('triggerEventResolve', resolve_js_script)
state.with_add_queue_js_command('triggerEvent', js_script)
end
end
end
end
@@ -28,6 +28,10 @@ module SpeckleConnector
state.speckle_state?
end
def update_ui!
ui_controller.update_ui(state)
end
# Attach observers to application when speckle initialized via menu commands.
def add_observer_handler!(observer_handler)
@observer_handler = observer_handler
@@ -36,24 +40,17 @@ module SpeckleConnector
# Send messages to HtmlDialog if any.
def send_messages!
queue = @state.speckle_state.message_queue
queue.each_value do |value|
instant_message_sender(value)
end
queue.each_value { |value| ui_controller.user_interfaces[Ui::SPECKLE_UI_ID].dialog.execute_script(value) }
update_state!(Actions::ClearQueue)
end
def instant_message_sender(message)
ui_controller.user_interfaces.each_value do |dialog|
dialog.execute_script(message)
end
end
# This is the only function application state will be switched by calling upcoming action with it's parameters
# if any.
def update_state!(action, *parameters)
old_state = @state
@state = action.update_state(old_state, *parameters)
send_messages! if @state.speckle_state.message_queue.any?
update_ui! unless @state.equal?(old_state)
end
end
end
-39
View File
@@ -1,39 +0,0 @@
# frozen_string_literal: true
require_relative '../speckle_objects/other/color'
module SpeckleConnector
module Cards
# Card for sketchup connector to communicate speckle.
class Card < Hash
# @return [String] id of the card.
attr_reader :model_card_id
# @return [String] account id of the card.
attr_reader :account_id
# @return [String] project id of the card.
attr_reader :project_id
# @return [String] model id of the card.
attr_reader :model_id
# @return [Boolean] card is valid or not.
attr_reader :valid
def initialize(model_card_id, account_id, project_id, model_id)
super()
@model_card_id = model_card_id
@account_id = account_id
@project_id = project_id
@model_id = model_id
@valid = true
self[:model_card_id] = model_card_id
self[:account_id] = account_id
self[:project_id] = project_id
self[:model_id] = model_id
self[:valid] = @valid
end
end
end
end
@@ -1,68 +0,0 @@
# frozen_string_literal: true
require_relative 'card'
module SpeckleConnector
module Cards
# Receive card for sketchup connector to communicate speckle.
class ReceiveCard < Card
attr_reader :type_discriminator
# @return [String, NilClass] message to send
attr_reader :message
# @return [String] selected version id to receive
attr_reader :selected_version_id
# @return [String] latest version id to receive
attr_reader :latest_version_id
# @return [Boolean] whether new version notification is dismissed or not
attr_reader :has_dismissed_update_warning
# @return [String] name of the project
attr_reader :project_name
# @return [String] name of the model
attr_reader :model_name
# @return [String] whether card is expired or not
attr_reader :expired
# @return [SpeckleConnector::Cards::ReceiveResult] result of the result
attr_reader :receive_result
def initialize(
card_id,
account_id,
project_id,
model_id,
project_name,
model_name,
selected_version_id,
latest_version_id,
has_dismissed_update_warning,
expired,
receive_result = nil
)
super(card_id, account_id, project_id, model_id)
@selected_version_id = selected_version_id
@latest_version_id = latest_version_id
@has_dismissed_update_warning = has_dismissed_update_warning
self[:selected_version_id] = selected_version_id
self[:has_dismissed_update_warning] = has_dismissed_update_warning
self[:latest_version_id] = latest_version_id
self[:model_name] = model_name
self[:project_name] = project_name
self[:expired] = expired
self[:receive_result] = receive_result
@receive_result = receive_result
@expired = expired
@model_name = model_name
@project_name = project_name
@type_discriminator = 'ReceiverModelCard'
self[:type_discriminator] = @type_discriminator
end
end
end
end
@@ -1,25 +0,0 @@
# frozen_string_literal: true
require_relative 'card'
module SpeckleConnector
module Cards
# Receive result that attached to the receiver card.
class ReceiveResult < Hash
# @return [Boolean] whether display them or not.
attr_reader :display
# @return [Array<Integer>] object ids that baked after receive.
attr_reader :baked_object_ids
# @param baked_object_ids [Array<Integer>] object ids that baked after receive.
def initialize(baked_object_ids, display)
super()
@baked_object_ids = baked_object_ids
@display = display
self[:bakedObjectIds] = baked_object_ids
self[:display] = display
end
end
end
end
-38
View File
@@ -1,38 +0,0 @@
# frozen_string_literal: true
require_relative 'card'
module SpeckleConnector
module Cards
# Send card for sketchup connector to communicate speckle.
class SendCard < Card
# @return [Filters::Send::EverythingFilter | Filters::Send::SelectionFilter | Filters::Send::LayerFilter] filter of the card.
attr_reader :send_filter
# @return [Object] send settings of the card.
attr_reader :send_settings
attr_reader :type_discriminator
# @return [String, NilClass] message to send
attr_reader :message
# @return [Boolean] whether sending is happening or not
attr_reader :sending
attr_reader :latest_created_version_id
def initialize(model_card_id, account_id, project_id, model_id, latest_created_version_id, send_filter, send_settings)
super(model_card_id, account_id, project_id, model_id)
@send_filter = send_filter
@send_settings = send_settings
@latest_created_version_id = latest_created_version_id
@type_discriminator = 'SenderModelCard'
self[:sendFilter] = send_filter
self[:sendSettings] = send_settings
self[:latestCreatedVersionId] = latest_created_version_id
self[:type_discriminator] = @type_discriminator
end
end
end
end
@@ -1,19 +0,0 @@
# frozen_string_literal: true
require_relative 'card'
module SpeckleConnector
module Cards
# Send card for sketchup connector to communicate speckle.
class SendCardMultipleFilters < Card
# @return [Hash{String=>Filter}] filters of the card.
attr_reader :filters
def initialize(card_id, account_id, project_id, model_id, filters)
super(card_id, account_id, project_id, model_id)
@filters = filters
self[:filters] = filters
end
end
end
end
@@ -7,10 +7,10 @@ module SpeckleConnector
# Command to update state of the application.
class ActionCommand < Command
# @param app [App::SpeckleConnectorApp] the app object to run command on
# @param binding [Ui::Binding] binding object holds commands to call
# @param action [#update_state] the action that knows how to change the state of the speckle app
def initialize(app, binding, action)
super(app, binding)
def initialize(app, action)
super(app)
@app = app
@action = action
end
@@ -7,7 +7,7 @@ module SpeckleConnector
module Commands
# Command to activate diffing for stream.
class ActivateDiffing < Command
def _run(_resolve_id, data)
def _run(data)
stream_id = data['stream_id']
action = Actions::ActivateDiffing.new(stream_id)
app.update_state!(action)
@@ -7,7 +7,7 @@ module SpeckleConnector
module Commands
# Command to apply mapping for selected entities.
class ApplyMappings < Command
def _run(_resolve_id, data)
def _run(data)
entities_to_map = data['entitiesToMap']
method = data['method']
category = data['category']
@@ -7,7 +7,7 @@ module SpeckleConnector
module Commands
# Command to clear mapping for selected entities.
class ClearMappings < Command
def _run(_resolve_id, data)
def _run(data)
entities_to_map = data['entitiesToClearMap']
is_definition = data['isDefinition']
action = Actions::ClearMappings.new(entities_to_map, is_definition)
+8 -16
View File
@@ -1,7 +1,5 @@
# frozen_string_literal: true
require_relative '../actions/handle_error'
module SpeckleConnector
module Commands
# Base command schema to wrap common operations for all commands.
@@ -9,25 +7,19 @@ module SpeckleConnector
# @return [App::SpeckleConnectorApp] the main app object
attr_reader :app
# @return [Ui::Binding] binding object holds dialog and it's state
attr_reader :binding
# @return [Ui::View] view object holds dialog and it's state
attr_reader :view
# @param app [App::SpeckleConnectorApp] the main app object
# @param binding [Ui::Binding] binding object holds commands to call
def initialize(app, binding)
# @@param app [App::SpeckleConnectorApp] the main app object
def initialize(app)
@app = app
@binding = binding
@view = app.ui_controller.user_interfaces[Ui::SPECKLE_UI_ID]
end
def run(*parameters)
begin
# Run here common operations that same for each command.
with_observers_disabled do
_run(*parameters)
end
rescue StandardError => e
action = Actions::HandleError.new(e, @binding.name, @action, parameters)
app.update_state!(action)
# Run here common operations that same for each command.
with_observers_disabled do
_run(*parameters)
end
end
@@ -0,0 +1,15 @@
# frozen_string_literal: true
require_relative 'command'
module SpeckleConnector
module Commands
# Run this command when the UI is ready to get data
class DialogReady < Command
# Update the selected user interface
def _run(_data)
view.update_view(app.state)
end
end
end
end
@@ -1,85 +0,0 @@
# frozen_string_literal: true
require_relative 'command'
require_relative '../ui/dui3_dialog'
require_relative '../states/initial_state'
require_relative '../ui/legacy_binding'
require_relative '../ui/bindings/accounts_binding'
require_relative '../ui/bindings/base_binding'
require_relative '../ui/bindings/send_binding'
require_relative '../ui/bindings/receive_binding'
require_relative '../ui/bindings/selection_binding'
require_relative '../ui/test_binding'
require_relative '../ui/bindings/config_binding'
require_relative '../ui/sketchup_config_binding'
require_relative '../actions/initialize_speckle'
require_relative '../observers/factory'
module SpeckleConnector
module Commands
# Command to initialize Speckle UI and register it to ui_controller.
# This is the command where we show UI to user.
class InitializeDUI3Speckle < Command
SPECKLE_DUI3 = 'speckle_dui3'
def dialog_title
"Speckle #{CONNECTOR_VERSION}"
end
private
def _run
app = self.app
if !app.state.instance_of?(States::InitialState) && app.ui_controller.user_interfaces[SPECKLE_DUI3]
dialog = app.ui_controller.user_interfaces[SPECKLE_DUI3]
dialog.show
return
end
initialize_speckle_dui3(app)
end
# Do the actual Speckle initialization.
# rubocop:disable Naming/VariableNumber
def initialize_speckle_dui3(app)
# TODO: Initialize here speckle states and observers.
observer_handler = Observers::Factory.create_handler(app)
app.add_observer_handler!(observer_handler)
observers = Observers::Factory.create_observers(observer_handler)
app.update_state!(Actions::InitializeSpeckle, observers, app.method(:instant_message_sender))
dialog_specs = {
dialog_id: SPECKLE_DUI3,
dialog_title: dialog_title,
height: 950,
width: 300
}
# Init bindings
base_binding = Ui::BaseBinding.new(app, Ui::BASE_BINDING_NAME)
accounts_binding = Ui::AccountsBinding.new(app, Ui::ACCOUNTS_BINDING_NAME)
send_binding = Ui::SendBinding.new(app, Ui::SEND_BINDING_NAME)
receive_binding = Ui::ReceiveBinding.new(app, Ui::RECEIVE_BINDING_NAME)
selection_binding = Ui::SelectionBinding.new(app, Ui::SELECTION_BINDING_NAME)
test_bindings = Ui::TestBinding.new(app, Ui::TEST_BINDINGS_NAME)
config_bindings = Ui::ConfigBinding.new(app, Ui::CONFIG_BINDING_NAME)
connector_config_bindings = Ui::SketchupConfigBinding.new(app, Ui::CONNECTOR_CONFIG_BINDING_NAME)
# Init dialog
dui3_dialog = SpeckleConnector::Ui::DUI3Dialog.new(**dialog_specs)
# Register bindings to dialog
dui3_dialog.bindings[Ui::BASE_BINDING_NAME] = base_binding
dui3_dialog.bindings[Ui::ACCOUNTS_BINDING_NAME] = accounts_binding
dui3_dialog.bindings[Ui::SEND_BINDING_NAME] = send_binding
dui3_dialog.bindings[Ui::RECEIVE_BINDING_NAME] = receive_binding
dui3_dialog.bindings[Ui::TEST_BINDINGS_NAME] = test_bindings
dui3_dialog.bindings[Ui::CONFIG_BINDING_NAME] = config_bindings
dui3_dialog.bindings[Ui::CONNECTOR_CONFIG_BINDING_NAME] = connector_config_bindings
dui3_dialog.bindings[Ui::SELECTION_BINDING_NAME] = selection_binding
app.ui_controller.register_ui(SPECKLE_DUI3, dui3_dialog)
dui3_dialog.show
end
# rubocop:enable Naming/VariableNumber
end
end
end
@@ -2,53 +2,49 @@
require_relative 'command'
require_relative '../states/initial_state'
require_relative '../ui/legacy_binding'
require_relative '../ui/vue_view'
require_relative '../actions/initialize_speckle'
require_relative '../observers/factory'
module SpeckleConnector
module Commands
# Command to initialize old Speckle UI and register it to ui_controller.
# Command to initialize Speckle UI and register it to ui_controller.
# This is the command where we show UI to user.
class InitializeSpeckle < Command
SPECKLE_LEGACY_UI = 'speckle_legacy_ui'
def dialog_title
"Speckle #{CONNECTOR_VERSION}"
"Speckle (Legacy) #{CONNECTOR_VERSION}"
end
private
def _run
app = self.app
if !app.state.instance_of?(States::InitialState) && app.ui_controller.user_interfaces[SPECKLE_LEGACY_UI]
vue_view = app.ui_controller.user_interfaces[SPECKLE_LEGACY_UI]
unless app.state.instance_of?(States::InitialState)
vue_view = app.ui_controller.user_interfaces[Ui::SPECKLE_UI_ID]
vue_view.show
return
end
initialize_speckle_legacy_view(app)
initialize_speckle(app)
end
# Do the actual Speckle initialization.
def initialize_speckle_legacy_view(app)
def initialize_speckle(app)
# TODO: Initialize here speckle states and observers.
observer_handler = Observers::Factory.create_handler(app)
app.add_observer_handler!(observer_handler)
observers = Observers::Factory.create_observers(observer_handler)
app.update_state!(Actions::InitializeSpeckle, observers)
dialog_specs = {
dialog_id: SPECKLE_LEGACY_UI,
dialog_id: Ui::SPECKLE_UI_ID,
htm_file: Ui::VUE_UI_HTML,
dialog_title: dialog_title,
height: 950,
width: 300
}
legacy_ui_dialog = SpeckleConnector::Ui::Dialog.new(**dialog_specs)
legacy_binding = Ui::LegacyBinding.new(app, 'legacy_ui')
legacy_ui_dialog.bindings[Ui::SPECKLE_LEGACY_BINDING_NAME] = legacy_binding
app.ui_controller.register_ui(SPECKLE_LEGACY_UI, legacy_ui_dialog)
legacy_ui_dialog.show
vue_view = Ui::VueView.new(dialog_specs, app)
app.ui_controller.register_ui(Ui::SPECKLE_UI_ID, vue_view)
vue_view.show
end
end
end
@@ -7,7 +7,7 @@ module SpeckleConnector
module Commands
# Command to update mapper source.
class MapperSourceUpdated < Command
def _run(_resolve_id, data)
def _run(data)
base = data['base']
stream_id = data['stream_id']
commit_id = data['commit_id']
@@ -8,7 +8,7 @@ module SpeckleConnector
module Commands
# Command to update theme.
class ModelPreferencesUpdated < Command
def _run(_resolve_id, data)
def _run(data)
preference = data['preference']
new_value = data['value']
app.update_state!(Actions::ModelPreferencesUpdated.new(preference, new_value))
@@ -8,7 +8,7 @@ module SpeckleConnector
module Commands
# Command to notify connected.
class NotifyConnected < Command
def _run(_resolve_id, data)
def _run(data)
stream_id = data['stream_id']
app.update_state!(Actions::Connected)
app.update_state!(Actions::SendFromQueue.new(stream_id))
@@ -7,15 +7,14 @@ module SpeckleConnector
module Commands
# Command to receive objects from Speckle Server.
class ReceiveObjects < Command
def _run(_resolve_id, data)
def _run(data)
stream_id = data['stream_id']
base = data['base']
branch_name = data['branch_name']
branch_id = data['branch_id']
stream_name = data['stream_name']
source_app = data['source_app']
object_id = data['object_id']
action = Actions::ReceiveObjects.new(stream_id, base, stream_name, branch_name, branch_id, source_app, object_id)
action = Actions::ReceiveObjects.new(stream_id, base, stream_name, branch_name, branch_id, source_app)
app.update_state!(action)
end
end
@@ -8,7 +8,7 @@ module SpeckleConnector
module Commands
# Command to remove stream.
class RemoveStream < Command
def _run(_resolve_id, data)
def _run(data)
stream_id = data['stream_id']
action = Actions::RemoveStream.new(stream_id)
app.update_state!(action)
@@ -2,6 +2,7 @@
require_relative 'command'
require_relative '../states/initial_state'
require_relative '../ui/vue_view'
require_relative '../actions/initialize_speckle'
require_relative '../observers/factory'
@@ -14,7 +15,7 @@ module SpeckleConnector
def _run
app = self.app
vue_view = app.ui_controller.user_interfaces[Ui::SPECKLE_LEGACY_UI]
vue_view = app.ui_controller.user_interfaces[Ui::SPECKLE_UI_ID]
if vue_view
vue_view.dialog.reset_dialog_location
else
@@ -9,7 +9,7 @@ module SpeckleConnector
module Commands
# Command to saved stream.
class SaveStream < Command
def _run(_resolve_id, data)
def _run(data)
stream_id = data['stream_id']
app.update_state!(Actions::SaveStream.new(stream_id))
end
@@ -7,7 +7,7 @@ module SpeckleConnector
module Commands
# Command to send selection to Speckle Server.
class SendSelection < Command
def _run(_resolve_id, data)
def _run(data)
stream_id = data['stream_id']
action = Actions::SendSelection.new(stream_id)
app.update_state!(action)
@@ -4,7 +4,6 @@ require_relative 'menu_command_handler'
require_relative 'action_command'
require_relative 'initialize_speckle'
require_relative 'reset_window_location'
require_relative 'initialize_dui3_speckle'
require_relative '../actions/one_click_send'
module SpeckleConnector
@@ -13,7 +12,6 @@ module SpeckleConnector
class SpeckleMenuCommands
CMD_INITIALIZE_SPECKLE = :initialize_speckle
CMD_RESET_WINDOW_LOCATION_SPECKLE = :reset_window_location_speckle
CMD_INITIALIZE_DUI3_SPECKLE = :initialize_dui3_speckle
CMD_SEND_TO_SPECKLE = :send_to_speckle
CMD_RECEIVE_FROM_SPECKLE = :receive_from_speckle
@@ -26,28 +24,20 @@ module SpeckleConnector
speckle_menu = sketchup_ui.speckle_menu
speckle_toolbar = sketchup_ui.speckle_toolbar
# commands[CMD_INITIALIZE_SPECKLE] = initialize_speckle_command(app)
# commands.add_to_menu!(CMD_INITIALIZE_SPECKLE, speckle_menu)
# commands.add_to_toolbar!(CMD_INITIALIZE_SPECKLE, speckle_toolbar)
commands[CMD_INITIALIZE_SPECKLE] = initialize_speckle_command(app)
commands.add_to_menu!(CMD_INITIALIZE_SPECKLE, speckle_menu)
commands.add_to_toolbar!(CMD_INITIALIZE_SPECKLE, speckle_toolbar)
commands[CMD_RESET_WINDOW_LOCATION_SPECKLE] = reset_window_location_command(app)
commands.add_to_menu!(CMD_RESET_WINDOW_LOCATION_SPECKLE, speckle_menu)
commands[CMD_INITIALIZE_DUI3_SPECKLE] = initialize_dui3_speckle_command(app)
commands.add_to_menu!(CMD_INITIALIZE_DUI3_SPECKLE, speckle_menu)
commands.add_to_toolbar!(CMD_INITIALIZE_DUI3_SPECKLE, speckle_toolbar)
# commands[CMD_SEND_TO_SPECKLE] = send_command(app)
# commands.add_to_menu!(CMD_SEND_TO_SPECKLE, speckle_menu)
# commands.add_to_toolbar!(CMD_SEND_TO_SPECKLE, speckle_toolbar)
end
def self.initialize_speckle_command(app)
cmd = MenuCommandHandler.sketchup_command(
InitializeSpeckle.new(app, nil), 'Initialize Speckle'
InitializeSpeckle.new(app), 'Initialize Speckle (Legacy)'
)
cmd.tooltip = 'Launch Connector'
cmd.status_bar_text = 'Opens the Speckle Connector window'
cmd.status_bar_text = 'Opens the Speckle (Legacy) Connector window'
cmd.small_icon = '../../img/s2logo.png'
cmd.large_icon = '../../img/s2logo.png'
cmd
@@ -55,7 +45,7 @@ module SpeckleConnector
def self.reset_window_location_command(app)
cmd = MenuCommandHandler.sketchup_command(
ResetWindowLocation.new(app, nil), 'Reset Window Location'
ResetWindowLocation.new(app), 'Reset Window Location'
)
cmd.tooltip = 'Bring Speckle window onto center of SketchUp window'
cmd.status_bar_text = 'Bring Speckle window onto center of SketchUp window'
@@ -63,29 +53,6 @@ module SpeckleConnector
cmd.large_icon = '../../img/s2logo.png'
cmd
end
def self.initialize_dui3_speckle_command(app)
cmd = MenuCommandHandler.sketchup_command(
InitializeDUI3Speckle.new(app, nil), 'Initialize Speckle with New UI'
)
cmd.tooltip = 'Launch Connector with New UI'
cmd.status_bar_text = 'Opens the Speckle Connector with New UI'
cmd.small_icon = '../../img/s2logo_dui3.png'
cmd.large_icon = '../../img/s2logo_dui3.png'
cmd
end
def self.send_command(app)
cmd = MenuCommandHandler.sketchup_command(
ActionCommand.new(app, nil, Actions::OneClickSend), 'Send to Speckle'
)
cmd.tooltip = 'Send to Speckle'
cmd.status_bar_text = 'Send to Speckle'
cmd.small_icon = '../../img/Sender.png'
cmd.large_icon = '../../img/Sender.png'
cmd.set_validation_proc { MenuCommandHandler.speckle_started(app) }
cmd
end
end
end
end
@@ -8,7 +8,7 @@ module SpeckleConnector
module Commands
# Command to update preferences.
class UserPreferencesUpdated < Command
def _run(_resolve_id, data)
def _run(data)
preference_hash = data['preference_hash']
preference = data['preference']
new_value = data['value']
@@ -5,9 +5,6 @@ module SpeckleConnector
SPECKLE_MAPPING_TOOL_SCHEMA = 'Speckle_Mapping_Tool_Schema'
SPECKLE_SCHEMA = 'Speckle_Schema'
SPECKLE_SEND_CARDS = 'Speckle_Send_Cards'
SPECKLE_RECEIVE_CARDS = 'Speckle_Receive_Cards'
SPECKLE_ID = 'speckle_id'
SPECKLE_TYPE = 'speckle_type'
APPLICATION_ID = 'application_id'
@@ -3,7 +3,6 @@
module SpeckleConnector
APP_OBSERVER = 'SpeckleConnector::Observers::AppObserver'
ENTITIES_OBSERVER = 'SpeckleConnector::Observers::EntitiesObserver'
ENTITY_OBSERVER = 'SpeckleConnector::Observers::EntityObserver'
MODEL_OBSERVER = 'SpeckleConnector::Observers::ModelObserver'
SELECTION_OBSERVER = 'SpeckleConnector::Observers::SelectionObserver'
end
@@ -14,7 +14,9 @@ module SpeckleConnector
path = ENV.fetch('APPDATA')
Pathname.new(File.join(path, 'Speckle')).cleanpath.to_s
when OS_MAC
File.join(Dir.home, '.config/Speckle')
primary_path = File.join(Dir.home, '.config/Speckle')
fallback_path = File.join(Dir.home, 'Library/Application Support/Speckle')
Dir.exist?(primary_path) ? primary_path : fallback_path
else
raise 'Speckle could not determine your Appdata path'
end
@@ -5,8 +5,6 @@ require 'securerandom'
# rubocop:enable SketchupPerformance/OpenSSL
require 'digest'
require_relative 'converter'
require_relative '../speckle_objects/base'
require_relative '../speckle_objects/object_reference'
require_relative '../speckle_entities/speckle_entity'
require_relative '../relations/many_to_one_relation'
@@ -18,17 +16,23 @@ module SpeckleConnector
# @return [Integer] default chunk size the determine splitting base prop into chucks
attr_reader :default_chunk_size
attr_reader :object_references
# @return [String] stream id to send conversion
attr_reader :stream_id
def initialize(preferences, default_chunk_size = 1000)
attr_accessor :speckle_state
# @param stream_id [String] stream id to send conversion
def initialize(speckle_state, stream_id, preferences, default_chunk_size = 1000)
@speckle_state = speckle_state
@stream_id = stream_id
@preferences = preferences
@default_chunk_size = default_chunk_size
@detach_lineage = []
@lineage = []
@family_tree = {}
@family_tree_relation = Relations::ManyToOneRelation.new
@closure_table = {}
@objects = {}
@object_references = {}
end
# @param base [Object] top base object to populate all children and their relationship
@@ -43,15 +47,36 @@ module SpeckleConnector
@objects[id][:totalChildrenCount]
end
# @param base [Object] base object to populate all children and their relationship
# @param base_and_entities [Object] base object to populate all children and their relationship
# rubocop:disable Metrics/MethodLength
def traverse_base(base)
# rubocop:disable Metrics/PerceivedComplexity
# rubocop:disable Metrics/CyclomaticComplexity
# rubocop:disable Metrics/AbcSize
def traverse_base(base_and_entities)
base, entities = base_and_entities
# 1. Create random string for lineage tracking.
@lineage.append(SecureRandom.hex)
# 2. Get last item from detach_lineage array
is_detached = @detach_lineage.pop
# unless entities.nil?
# is_sent_before = entities.all? do |entity|
# check_base_available_on_state(entity, speckle_state)
# end
# if is_sent_before
# speckle_entity = speckle_state.speckle_entities[entities.first.persistent_id]
# ref_object = detach_helper(speckle_entity.id)
# parent = @lineage[-1]
# unless @family_tree[parent].nil?
# @family_tree[parent] = @family_tree[parent].merge(speckle_entity.speckle_object[:__closure])
# end
# @objects[speckle_entity.id] = ref_object if is_detached
# return speckle_entity.id, ref_object
# end
# end
# 3. Initialize traversed base object that will be filled with traversed values or
# traversed base objects as props.
traversed_base = SpeckleObjects::Base.new(speckle_type: base[:speckle_type], id: '')
@@ -60,6 +85,7 @@ module SpeckleConnector
traversed_base.delete(:applicationId)
# 4. Iterate all entries (key, value) of the base {Base > Hash} object
# speckle_state = traverse_base_props(base, traversed_base)
traverse_base_props(base, traversed_base)
# this is where all props are done for current `traversed_base`
@@ -90,15 +116,19 @@ module SpeckleConnector
# 10. Save object string if detached
@objects[id] = traversed_base if is_detached
if traversed_base[:applicationId]
@object_references[traversed_base[:applicationId].to_s] = SpeckleObjects::ObjectReference.new(
id, traversed_base[:applicationId].to_s, traversed_base[:__closure]
)
if @preferences[:user][:register_speckle_entity] && !entities.nil?
entities.uniq.each do |entity|
speckle_entity = create_or_update_speckle_entity(entity, id, traversed_base)
@speckle_state = speckle_state.with_speckle_entity(speckle_entity)
end
end
return id, traversed_base
end
# rubocop:enable Metrics/MethodLength
# rubocop:enable Metrics/PerceivedComplexity
# rubocop:enable Metrics/CyclomaticComplexity
# rubocop:enable Metrics/AbcSize
# rubocop:disable Metrics/MethodLength
# rubocop:disable Metrics/AbcSize
@@ -125,7 +155,7 @@ module SpeckleConnector
chunked_detach_match = prop.match(/^@\((\d*)\)/)
# 3.5. If split chunk is needed and prop value is array, then run chunking process
if value.is_a?(Array) && chunked_detach_match
if value.is_a?(Array) && !base_and_entities?(value) && chunked_detach_match
# 3.5.1. Determine chunk size, get it from prop if defined. ex: '@(31250)faces' -> 31250 = chunk size
chunk_size = chunked_detach_match[1] == '' ? default_chunk_size : chunked_detach_match[1].to_i
@@ -174,15 +204,12 @@ module SpeckleConnector
child = traverse_value(value, is_detach_prop)
is_base = value.is_a?(Hash) && !value[:speckle_type].nil?
is_base = (value.is_a?(Hash) && !value[:speckle_type].nil?) ||
(base_and_entities?(value) && value[0].is_a?(Hash) && !value[0][:speckle_type].nil?)
# 3.6. traverse value according to value is a speckle object or not
traversed_base[prop] = if is_base
if child[:referencedId] && child[:speckle_type] == 'reference'
is_detach_prop ? detach_helper(child[:referencedId]) : child
else
is_detach_prop ? detach_helper(child[:id]) : child
end
is_detach_prop ? detach_helper(child[:id]) : child
else
child
end
@@ -194,6 +221,18 @@ module SpeckleConnector
# rubocop:enable Metrics/CyclomaticComplexity
# rubocop:enable Metrics/PerceivedComplexity
# Whether value has a pattern [<converted>, [<entity>, <entity>, ... <entity>]] or not.
def base_and_entities?(value)
is_array = value.is_a?(Array)
return false unless is_array
return false unless is_array && value.length == 2
return false if value[1].nil?
value[1].all? { |v| v.is_a?(Sketchup::Entity) }
end
# rubocop:disable Metrics/MethodLength
# rubocop:disable Metrics/CyclomaticComplexity
# rubocop:disable Metrics/PerceivedComplexity
@@ -203,8 +242,8 @@ module SpeckleConnector
# 1. Return same value if value is primitive type (string, numeric, boolean)
return value unless value.is_a?(Hash) || value.is_a?(Array)
# 2. For pure arrays
if value.is_a?(Array)
# 2. For pure arrays (Without referencing any Sketchup Entity)
if value.is_a?(Array) && !base_and_entities?(value)
# 2.1. If it is not detached then iterate array by traversing with their value
unless is_detach
@@ -218,17 +257,7 @@ module SpeckleConnector
# 2.2. If it is detached than collect them into detached_list
detached_list = []
value.each do |el|
if el.is_a?(SpeckleObjects::ObjectReference)
if el.closure
el.closure.each_key do |k|
detach_helper(k)
end
end
detached_list.append(detach_helper(el.referenced_id))
next
end
if el.is_a?(Hash) && !el[:speckle_type].nil?
if (el.is_a?(Hash) && !el[:speckle_type].nil?) || base_and_entities?(el)
@detach_lineage.append(is_detach)
id, _traversed_base = traverse_base(el)
detached_list.append(detach_helper(id))
@@ -240,21 +269,11 @@ module SpeckleConnector
return detached_list
end
# 3. ObjectReference
if value.is_a?(SpeckleObjects::ObjectReference)
if value.closure
value.closure.each_key do |k|
detach_helper(k)
end
end
return detach_helper(value.referenced_id)
end
# 4. Hash
# 3. Hash
return value if value.is_a?(Hash) && value[:speckle_type].nil?
# 5. Base objects
if value.is_a?(Hash) && !value[:speckle_type].nil?
# 4. Base objects
if (value.is_a?(Hash) && !value[:speckle_type].nil?) || base_and_entities?(value)
@detach_lineage.append(is_detach)
_id, traversed_base = traverse_base(value)
return traversed_base
@@ -291,12 +310,8 @@ module SpeckleConnector
Digest::MD5.hexdigest(traversed_base.to_json)
end
def batch_objects
@objects
end
# rubocop:disable Metrics/MethodLength
def batch_json_objects(max_batch_size_mb = 1)
def batch_objects(max_batch_size_mb = 1)
max_size = 1000 * 1000 * max_batch_size_mb
batches = []
batch = '['
@@ -320,6 +335,32 @@ module SpeckleConnector
batches
end
# rubocop:enable Metrics/MethodLength
# @param entity [Sketchup::Entity] source entity object
# @param speckle_state [States::SpeckleState] the current speckle state of the {States::State}
def check_base_available_on_state(entity, speckle_state)
is_exist = speckle_state.speckle_entities.keys.include?(entity.persistent_id)
return is_exist unless is_exist
speckle_state.speckle_entities[entity.persistent_id].valid_stream_ids.include?(stream_id)
end
# Creates or updates speckle entity.
# If speckle entity exist in state, creates new one by updating old one.
# Else creates new one
# @return [SpeckleEntity] speckle entity that collects both speckle and sketchup information.
def create_or_update_speckle_entity(entity, id, traversed_base)
if speckle_state.speckle_entities.keys.include?(entity.persistent_id)
speckle_state.speckle_entities[entity.persistent_id].with_valid_stream_id(stream_id)
else
children = traversed_base[:__closure].nil? ? {} : traversed_base[:__closure]
speckle_entity = SpeckleEntities::SpeckleEntity.new(entity, id, entity.persistent_id,
traversed_base[:speckle_type],
children.keys, [stream_id])
speckle_entity.write_initial_base_data
speckle_entity
end
end
end
end
end
@@ -1,268 +0,0 @@
# frozen_string_literal: true
# rubocop:disable SketchupPerformance/OpenSSL
require 'securerandom'
# rubocop:enable SketchupPerformance/OpenSSL
require 'digest'
require_relative 'converter'
require_relative '../relations/many_to_one_relation'
module SpeckleConnector
module Converters
# Serializer of the base object.
# Responsible to create id (hash) of the objects by holding their lineage and detaching relationships.
class BaseObjectSerializerV2
# @return [Integer] default chunk size the determine splitting base prop into chucks
attr_reader :default_chunk_size
def initialize(default_chunk_size = 1000)
@default_chunk_size = default_chunk_size
@detach_lineage = []
@lineage = []
@family_tree = {}
@family_tree_relation = Relations::ManyToOneRelation.new
@closure_table = {}
@objects = {}
end
# @param base [Object] top base object to populate all children and their relationship
# @return [String, String] id (hash) and traversed hash
def serialize(base)
id, traversed = traverse_base(base)
@objects[id] = traversed
return id, traversed
end
def total_children_count(id)
@objects[id][:totalChildrenCount]
end
# @param base [Object] base object to populate all children and their relationship
# rubocop:disable Metrics/MethodLength
def traverse_base(base)
# 1. Create random string for lineage tracking.
@lineage.append(SecureRandom.hex)
# 2. Initialize traversed base object that will be filled with traversed values or
# traversed base objects as props.
traversed_base = SpeckleObjects::Base.new(speckle_type: base[:speckle_type], id: '')
traversed_base.delete(:applicationId)
# 3. Iterate all entries (key, value) of the base {Base > Hash} object
traverse_base_props(base, traversed_base)
# this is where all props are done for current `traversed_base`
# 4. Get last item from detach_lineage array
is_detached = @detach_lineage.pop
# 5. Add closures
closure = {}
parent = @lineage.pop
unless @family_tree[parent].nil?
@family_tree[parent].each do |ref, depth|
closure[ref] = depth - @detach_lineage.length
end
end
# 6. Add total children count
traversed_base[:totalChildrenCount] = closure.keys.length
# 7. Finally create id
id = get_id(traversed_base)
# 8. Add id to traversed base
traversed_base[:id] = id
# 9. Update __closure table on the traversed base
unless traversed_base[:totalChildrenCount].nil?
@closure_table[id] = closure
traversed_base[:__closure] = closure unless closure.empty?
end
# 10. Save object string if detached
@objects[id] = traversed_base if is_detached
return id, traversed_base
end
# rubocop:enable Metrics/MethodLength
# rubocop:disable Metrics/MethodLength
# rubocop:disable Metrics/AbcSize
# rubocop:disable Metrics/BlockLength
# rubocop:disable Metrics/CyclomaticComplexity
# rubocop:disable Metrics/PerceivedComplexity
def traverse_base_props(base, traversed_base)
base.each do |prop, value|
# 3.1. Ignore nil, starts with '_' and 'id'
next if value.nil? || prop[0] == '_' || prop == 'id' || prop == :id
# 3.2. Pass primitives without any operation (string, numeric, boolean)
unless value.is_a?(Hash) || value.is_a?(Array)
traversed_base[prop] = value
next
end
# 3.3. Determine prop is detached or not
is_prop_detach = prop[0] == '@'
# 3.4. Check prop needs to split into chunks
chunked_detach_match = prop.match(/^@\((\d*)\)/)
# 3.5. If split chunk is needed and prop value is array, then run chunking process
if value.is_a?(Array) && chunked_detach_match
# 3.5.1. Determine chunk size, get it from prop if defined. ex: '@(31250)faces' -> 31250 = chunk size
chunk_size = chunked_detach_match[1] == '' ? default_chunk_size : chunked_detach_match[1].to_i
# 3.5.2. Init empty array for chunks
chunks = []
# 3.5.3. Init empty data chunk core object
chunk = {
speckle_type: 'Speckle.Core.Models.DataChunk',
data: []
}
# 3.5.4. Iterate each element on array to fill them into chunks
value.each_with_index do |el, index|
# 3.5.4.1. If current index is the multiplier of the chunk size, then need to append chunk into chunks
# and reinitialize empty chunk for next batch
if (index % chunk_size == 0) && index != 0
chunks.append(chunk)
chunk = {
speckle_type: 'Speckle.Core.Models.DataChunk',
data: []
}
end
# 3.5.4.2. Add element into chunk
chunk[:data].append(el)
end
# 3.5.5. Add trailing batch to the chunks also unless is empty
chunks.append(chunk) unless chunk[:data].empty?
# 3.5.6. Initialize empty chunk reference array
chunk_references = []
chunks.each do |chunk_element|
@detach_lineage.append(is_prop_detach)
id, _traversed = traverse_base(chunk_element)
chunk_references.append(detach_helper(id))
end
# 3.5.7. Add chunk references to the traversed base prop without @(<chunk_size>)
traversed_base[prop.to_s.sub(chunked_detach_match[0], '')] = chunk_references
# 3.5.8. We are done chunking, good to go next
next
end
# 3.6. traverse value according to value is a speckle object or not
if value.is_a?(Hash) && !value[:speckle_type].nil?
child = traverse_value(value, is_prop_detach)
traversed_base[prop] = is_prop_detach ? detach_helper(child[:id]) : child
else
traversed_base[prop] = traverse_value(value, is_prop_detach)
end
end
end
# rubocop:enable Metrics/MethodLength
# rubocop:enable Metrics/AbcSize
# rubocop:enable Metrics/BlockLength
# rubocop:enable Metrics/CyclomaticComplexity
# rubocop:enable Metrics/PerceivedComplexity
# rubocop:disable Metrics/MethodLength
# rubocop:disable Metrics/CyclomaticComplexity
# rubocop:disable Metrics/PerceivedComplexity
# rubocop:disable Style/OptionalBooleanParameter
def traverse_value(value, is_detach = false)
# 1. Return same value if value is primitive type (string, numeric, boolean)
return value unless value.is_a?(Hash) || value.is_a?(Array)
# 2. Arrays
if value.is_a?(Array)
# 2.1. If it is not detached then iterate array by traversing with their value
return value.collect { |el| traverse_value(el) } unless is_detach
# 2.2. If it is detached than collect them into detached_list
detached_list = []
value.each do |el|
if (el.is_a?(Array) || el.is_a?(Hash)) && !el[:speckle_type].nil?
@detach_lineage.append(is_detach)
id, _traversed_base = traverse_base(el)
detached_list.append(detach_helper(id))
else
detached_list.append(traverse_value(el, is_detach))
end
end
return detached_list
end
# 3. Hash
return value if value[:speckle_type].nil?
# 4. Base objects
unless value[:speckle_type].nil?
@detach_lineage.append(is_detach)
_id, traversed_base = traverse_base(value)
return traversed_base
end
# 5. If it is not returned until here then there is unsupported type
raise StandardError "Unsupported type #{value.class} : #{value}"
end
# rubocop:enable Metrics/MethodLength
# rubocop:enable Metrics/CyclomaticComplexity
# rubocop:enable Metrics/PerceivedComplexity
# rubocop:enable Style/OptionalBooleanParameter
def detach_helper(reference_id)
@lineage.each do |parent|
# init parent on the family tree unless exist
@family_tree[parent] = {} if @family_tree[parent].nil?
is_ref_exist = !@family_tree[parent].nil? && !@family_tree[parent][reference_id].nil?
if !is_ref_exist || @family_tree[parent][reference_id] > @detach_lineage.length
@family_tree[parent][reference_id] = @detach_lineage.length
end
end
{
referencedId: reference_id,
speckle_type: 'reference'
}
end
# @param traversed_base [SpeckleConnector::SpeckleObjects::Base] traversed base object.
def get_id(traversed_base)
Digest::MD5.hexdigest(traversed_base.to_json)
end
# rubocop:disable Metrics/MethodLength
def batch_objects(max_batch_size_mb = 1)
max_size = 1000 * 1000 * max_batch_size_mb
batches = []
batch = '['
batch_size = 0
objects = @objects.values
objects.each do |obj|
obj_json = obj.to_json
if batch_size + obj_json.length < max_size
batch += obj_json
batch += ','
batch_size += obj_json.length
else
batch = batch.chop
batches.append("#{batch}]")
batch = "[#{obj_json},"
batch_size = obj_json.length
end
end
batch = batch.chop
batches.append("#{batch}]")
batches
end
# rubocop:enable Metrics/MethodLength
end
end
end
@@ -19,14 +19,11 @@ module SpeckleConnector
# @return [String] speckle units
attr_reader :units
attr_reader :model_card_id
attr_accessor :definitions
# @param state [States::State] the current state of the {SpeckleConnector::App}
def initialize(state, stream_id, model_card_id)
def initialize(state, stream_id)
@state = state
@model_card_id = model_card_id
@speckle_state = state.speckle_state
@sketchup_model = state.sketchup_state.sketchup_model
@stream_id = stream_id
@@ -36,15 +36,12 @@ module SpeckleConnector
attr_reader :converted_faces
attr_reader :converted_entities
def initialize(state, stream_id, stream_name, branch_name, source_app, model_card_id)
super(state, stream_id, model_card_id)
def initialize(state, stream_id, stream_name, branch_name, source_app)
super(state, stream_id)
@stream_name = stream_name
@branch_name = branch_name
@source_app = source_app.downcase
@converted_faces = []
@converted_entities = []
end
# Module aliases
@@ -149,7 +146,6 @@ module SpeckleConnector
def try_create_instance
if !from_sketchup && (!@is_update_commit || @branch_definition.instances.empty?)
instance = sketchup_model.entities.add_instance(@branch_definition, Geom::Transformation.new)
@converted_entities.append(instance)
BLOCK_INSTANCE.align_instance_axes(instance) if from_qgis
end
end
@@ -329,7 +325,6 @@ module SpeckleConnector
# Call 'to_native' method by passing this method itself to handle nested 'to_native' conversions.
# It returns updated state and converted entities.
state, converted_entities = to_native_method.call(state, obj, layer, entities, &convert_to_native)
@converted_entities += converted_entities
faces = converted_entities.select { |e| e.is_a?(Sketchup::Face) }
@converted_faces += faces if faces.any?
if from_revit
+14 -50
View File
@@ -27,32 +27,12 @@ module SpeckleConnector
SPECKLE_ENTITIES_READER = SketchupModel::Reader::SpeckleEntitiesReader
VIEW3D = SpeckleObjects::BuiltElements::View3d
attr_reader :send_filter
def initialize(state, stream_id, send_filter, model_card_id)
super(state, stream_id, model_card_id)
@send_filter = send_filter
end
# @return [States::SpeckleState, SpeckleObjects::Speckle::Core::Models::ModelCollection]
def convert_entities_to_base(entity_ids, preferences)
convert = method(:convert)
entities = sketchup_model.entities.select { |e| entity_ids.any?(e.persistent_id) }
new_speckle_state, model_collection = MODEL_COLLECTION.from_entities(entities, sketchup_model, state,
@units, preferences, model_card_id,
&convert)
return new_speckle_state, model_collection
end
# 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(preferences)
convert = method(:convert)
new_speckle_state, model_collection = MODEL_COLLECTION.from_sketchup_model(sketchup_model, state,
@units, preferences, model_card_id,
&convert)
new_speckle_state, model_collection = MODEL_COLLECTION.from_sketchup_model(sketchup_model, speckle_state,
@units, preferences, &convert)
return new_speckle_state, model_collection
end
@@ -60,15 +40,15 @@ module SpeckleConnector
# Serialized and traversed information to send batches.
# @param base_and_entity [SpeckleObjects::Base] base object to serialize.
# @return [String, Integer, Array<Object>] base id, total_children_count of base and batches
def serialize(base_and_entity, preferences)
serializer = SpeckleConnector::Converters::BaseObjectSerializer.new(preferences)
def serialize(base_and_entity, speckle_state, preferences)
serializer = SpeckleConnector::Converters::BaseObjectSerializer.new(speckle_state, stream_id, preferences)
t = Time.now.to_f
id = serializer.serialize(base_and_entity)
batches = serializer.batch_json_objects
write_to_speckle_folder(id, batches)
puts "Generating traversed object elapsed #{(Time.now.to_f - t).round(5)} s"
batches = serializer.batch_objects
# write_to_speckle_folder(id, batches)
puts "Generating traversed object elapsed #{Time.now.to_f - t} s"
base_total_children_count = serializer.total_children_count(id)
return id, base_total_children_count, batches, serializer.object_references
return id, base_total_children_count, batches, serializer.speckle_state
end
def write_to_speckle_folder(id, batches)
@@ -92,34 +72,18 @@ module SpeckleConnector
return speckle_state, nil
end
# @param entity [Sketchup::Entity]
def entity_has_changed?(entity)
speckle_state.changed_entity_persistent_ids.include?(entity.persistent_id) ||
speckle_state.changed_entity_ids.include?(entity.entityID)
end
# @param entity [Sketchup::Entity]
# @param speckle_state [States::SpeckleState]
# rubocop:disable Metrics/MethodLength
def from_native_to_speckle(entity, preferences, speckle_state, parent, &convert)
# Where we do send caching!
if !entity_has_changed?(entity) &&
speckle_state.object_references_by_project[@stream_id] &&
speckle_state.object_references_by_project[@stream_id].keys.include?(entity.persistent_id.to_s)
reference = speckle_state.object_references_by_project[@stream_id][entity.persistent_id.to_s]
return speckle_state, reference
end
if entity.is_a?(Sketchup::Edge)
line = SpeckleObjects::Geometry::Line.from_edge(speckle_state: speckle_state, edge: entity,
units: @units, model_preferences: preferences[:model]).to_h
return speckle_state, line
return speckle_state, [line, [entity]]
end
if entity.is_a?(Sketchup::Face)
mesh = SpeckleObjects::Geometry::Mesh.from_face(speckle_state: speckle_state, face: entity, units: @units,
model_preferences: preferences[:model])
return speckle_state, mesh
return speckle_state, [mesh, [entity]]
end
if entity.is_a?(Sketchup::Group)
@@ -127,7 +91,7 @@ module SpeckleConnector
entity, @units, preferences, speckle_state, &convert
)
speckle_state = new_speckle_state
return speckle_state, block_instance
return speckle_state, [block_instance, [entity]]
end
if entity.is_a?(Sketchup::ComponentInstance)
@@ -135,19 +99,19 @@ module SpeckleConnector
entity, @units, preferences, speckle_state, &convert
)
speckle_state = new_speckle_state
return speckle_state, block_instance
return speckle_state, [block_instance, [entity]]
end
if entity.is_a?(Sketchup::ComponentDefinition)
# Local caching
return speckle_state, definitions[entity.guid] if definitions.key?(entity.guid)
return speckle_state, [definitions[entity.guid], [entity]] if definitions.key?(entity.guid)
new_speckle_state, block_definition = SpeckleObjects::Other::BlockDefinition.from_definition(
entity, @units, preferences, speckle_state, parent, &convert
)
definitions[entity.guid] = block_definition
speckle_state = new_speckle_state
return speckle_state, block_definition
return speckle_state, [block_definition, [entity]]
end
return speckle_state, nil
-23
View File
@@ -1,23 +0,0 @@
#-------------------------------------------------------------------------------
#
# Thomas Thomassen
# thomas[at]thomthom[dot]net
#
#-------------------------------------------------------------------------------
#-------------------------------------------------------------------------------
module SpeckleConnector
module TT
module Lib
### CONSTANTS ### ------------------------------------------------------------
# Plugin information
PLUGIN_ID = 'TT_Lib2'.freeze
PLUGIN_NAME = 'TT_Lib²'.freeze
PLUGIN_VERSION = '2.13.1'.freeze
end # module Lib
end # module TT
end
@@ -1,74 +0,0 @@
#-----------------------------------------------------------------------------
#
# CHANGELOG
# 2.5.0 - 18.10.2010
# * Upgraded JQuery to 1.4.4
# * Now bundles JQuery minimized
# * Added Win32Utils' Win32::API under TT::Win32::API
# * Added module: TT::Bezier
# * Added module: TT::Edge
# * Added module: TT::Edges
# * Added module: TT::Faces
# * Added module: TT::Materials
# * Added module: TT::Gizmo::Axis
# * Added module: TT::Win32
# * Added class: TT::Babelfish
# * Added class: TT::Dimension
# * Added class: TT::GUI::ToolWindow
# * Added method: TT::debug
# * Added method: TT::Point3d.douglas_peucker
# * Added method: TT::Point3d.simplify_curve
# * Added method: TT::Geom3d.interpolate_linear
# * Added method: TT::Geom3d.average_point
# * Added method: TT::GUI::Button.custom_properties
# * Added method: TT::GUI::Control.add_event
# * Added method: TT::GUI::Control.call_event
# * Added method: TT::GUI::Control.positioned?
# * Added method: TT::GUI::Control.properties
# * Added method: TT::GUI::ContainerElement.add_controls_to_webdialog
# * Added method: TT::GUI::ContainerElement.get_control_by_ui_id
# * Added methods: TT::GUI::Listbox
# * Added method: TT::GUI::Window.add_action_callback
# * Added method: TT::GUI::Window.add_control_to_webdialog
# * Added method: TT::GUI::Window.set_client_size
# * Added method: TT::System.is_windows?
# * Changed: TT::Gizmo::Manipulator
# * Changed: TT::GUI::Inputbox now inherits from TT::GUI::ToolWindow
# * Changed: TT::GUI::Inputbox.add_control accepts a +key+ argument a control id.
# * Changed: TT::GUI::Inputbox.prompt returns a Hash instead of Array.
# * Changed: TT::UV_Plane
# * Fixed: TT::JSON.to_s - Now handles Symbols as values.
# * Plus lots lots more - lots track of it all.
#
# 2.4.0 - 22.09.2010
# * Added module: TT::Binary
# * Added module: TT::GUI
# * Added class: TT::GUI::Window
# * Added class: TT::GUI::Inputbox
# * Added class: TT::JSON
# * Added module: TT::System
# * Added module: TT::Locale
# * Added module: TT::Cursor
#
# 2.3.0 - 08.09.2010
# * Added method: TT::Geom3d.spiral_sphere
# * Fixed: TT::Ray.test
#
# 2.2.0 - 06.09.2010
# * Added module: TT::Bounds
#
# 2.1.1 - 04.09.2010
# * Fixed bug: Ray.test (Workaround for SU8.0 bug)
#
# 2.1.0 - 03.09.2010
# * Added module: TT::Selection
# * Added module: TT::UVQ
# * Added class: TT::UV_Plane
# * Added class: TT::Gizmo::Manipulator
# * Added method: TT::Entities.bounds
# * Fixed: TT::Ray.test
#
# 2.0.0 - 01.09.2010
# * Initial Release
#
#-----------------------------------------------------------------------------
@@ -1,12 +0,0 @@
= TT_Lib2
* {SCF Thread}[http://forums.sketchucation.com/viewtopic.php?f=323&t=23307]
== Description
Library of common methods and classes for Google SketchUp Ruby plugins.
== Author
* Thomas Thomassen
* thomas[at]thomthom[dot]net

Some files were not shown because too many files have changed in this diff Show More