Compare commits

...

81 Commits

Author SHA1 Message Date
oguzhankoral f85bde5dee WIP 2024-02-22 16:19:13 +03:00
oguzhankoral 05e7472051 WIP 2024-02-22 15:02:23 +03:00
oguzhankoral 006691db7e WIP 2024-02-22 15:02:19 +03:00
oguzhankoral a663881368 WIPWIPWIPWIP 2024-02-22 14:52:01 +03:00
oguzhankoral c8cc8576b0 Instant message sender for App 2024-02-22 14:52:01 +03:00
oguzhankoral 31df7e77ba Merge branch 'development' into oguzhan/dui3
# Conflicts:
#	speckle_connector/src/actions/receive_objects.rb
#	speckle_connector/src/commands/speckle_menu_commands.rb
#	speckle_connector/src/ui/legacy_binding.rb
2024-02-22 14:51:17 +03:00
Oğuzhan Koral 67d4862f6b Fix (Mapper): CNX-9027 mapped edges don't send at all (#324)
* Separate native revit object definitions

* Add types for native revit objects

* Pass speckle_state to line conversion

* Remove mapper functions from model collections

* Use dictionary pattern matching for mapping conversions

* Remove unnecessary argument from Mapper.to_speckle
2024-02-21 16:59:40 +03:00
Oğuzhan Koral 6a16327c30 Fix (Cache): CNX-8976 bug on initialization on clear setup (#323)
* Call sketchup functions before mount

* Change default to app.speckle.systems

* Use account id instead uuid to get selected account before
2024-02-20 23:54:15 +03:00
Oğuzhan Koral 060b1b8f41 Fix (Attributes): Fix typos 2024-02-15 16:43:32 +03:00
Oğuzhan Koral da2e228293 Fix (FE2): CNX-8877 bug access error by fe2 model url (#321)
* Disable reload

* Enrich selected account info and fix same user id issue

* Trigger sketchup only saved stream not exists

* Fix activeAccount

* Increase number of branches limit to 100

* Check regex match
2024-02-13 16:36:35 +03:00
Oğuzhan Koral ab7397bf55 Feat (FE2): CNX-8702 fe2 urls (#320)
* Recreate ApolloProvider whenever account switches

- It is a force-push of reload page whenever user switches account. It doesn't hurt

* Fix FE2 urls and streamWrapper flag argument
2024-02-03 02:36:41 +03:00
Oğuzhan Koral f79547f781 Reset UI location to center of SketchUp (#319) 2024-01-31 23:00:46 +03:00
Oğuzhan Koral 2152f1c90e Feat (Mapper): CNX-8309 Family instances (#318)
* Implement New Revit Family option

* Filter family types for 'Family Instance'

* Adjust available mapping methods according to selection

* Set hard coded categories just once and source families when source set/updated

* Implement family instance mapping

* Pass group selection info if only group selected

* Exclude definitions from conversion escape

- Previously we were using definition mappings only for direct shape conversions but now they can live in instances

* Add direct shape option to components back

* Calculate rotation of family instance

* Fix transformation matrix

* Use insertion point for instances

* Set inputs for mapped definition
2024-01-08 18:40:55 +03:00
Oğuzhan Koral 503fb4d246 Feat (GIS): support line element 2023-12-01 01:46:53 +03:00
oguzhankoral 2befefa752 Extract utils for line and polygon element 2023-11-30 15:07:31 +03:00
oguzhankoral 85e64c5076 Add line element support 2023-11-30 00:36:47 +03:00
Oğuzhan Koral 60523dc994 Fix (Collections): Support gis collections and none unit 2023-11-29 16:06:58 +03:00
oguzhankoral 6d780bf350 Rename GisLayerCollection as generic 2023-11-29 16:06:37 +03:00
oguzhankoral eec02a1f84 Support none unit 2023-11-29 15:58:26 +03:00
oguzhankoral ace2fe6fe3 WIP: Convert from 'meters' to 'm' 2023-11-29 13:26:53 +03:00
oguzhankoral 188794af8d Add support for vector layer 2023-11-29 13:26:53 +03:00
oguzhankoral f9473f558a Merge branch 'development' into oguzhan/dui3 2023-11-20 10:51:35 +03:00
oguzhankoral 88078bad4c Zoom selection for highlight 2023-10-11 10:04:01 +03:00
oguzhankoral 1a3d6ec800 Update sqlite3 submodule hash ref 2023-10-09 13:23:24 +03:00
oguzhankoral 9b5fc2cd74 Highlight model for send cards 2023-10-04 10:17:45 +02:00
oguzhankoral c26398a13f Remove cards from model 2023-09-18 09:36:16 +03:00
oguzhankoral fdb874b616 Add action name to error message for commands 2023-09-15 18:40:52 +03:00
oguzhankoral dfd06544ec First successful receive 2023-09-15 18:40:52 +03:00
oguzhankoral 69c10ed1e7 Send progressbar with Sketchup.status_text
Thanks to library of thomthom
2023-09-15 18:40:52 +03:00
oguzhankoral 00df9800ed Send model card id via sendViaBrowserArgs 2023-09-15 18:40:51 +03:00
oguzhankoral 6494fd6b41 Get source app version via base bindings 2023-09-15 18:40:51 +03:00
oguzhankoral e9cb673f3a Send message from sender card 2023-09-15 18:40:51 +03:00
oguzhankoral 4e5cb64710 Correct send data 2023-09-15 18:40:50 +03:00
oguzhankoral aaa6ff9938 Real send via DUI3 2023-09-15 18:40:50 +03:00
oguzhankoral 099cb6b13b Trigger sendersExpired when objects modified 2023-09-15 18:40:50 +03:00
oguzhankoral 75c4065d27 Use AddModel action for updateModel command 2023-09-15 18:40:49 +03:00
oguzhankoral f3ce5c8993 Place binding classes into folder 2023-09-15 18:40:49 +03:00
oguzhankoral 9760d3f0d5 Enable error catch for command 2023-09-15 18:40:49 +03:00
oguzhankoral dab851f3d4 Update model card 2023-09-15 18:40:49 +03:00
oguzhankoral 81b3d4d6d7 Add type discriminator 2023-09-15 18:40:48 +03:00
oguzhankoral 5b09566c49 Reorganize send bindings 2023-09-15 18:40:48 +03:00
oguzhankoral 70dcc23b25 Split account binding from base 2023-09-15 18:40:48 +03:00
oguzhankoral 4c90b3fb16 Align with Dim's works 2023-09-15 18:40:47 +03:00
oguzhankoral 87931df9b6 WIP: selection bindings - add cards to model 2023-09-15 18:40:47 +03:00
oguzhankoral 5d03229d35 WIP: filters 2023-09-15 18:40:47 +03:00
oguzhankoral fb31d662cc Convert tag colors to hex 2023-09-15 18:40:46 +03:00
oguzhankoral 489ec8701f Get default filters 2023-09-15 18:40:46 +03:00
oguzhankoral e1afe66c7e Get model state and send filter for sketchup 2023-09-15 18:40:46 +03:00
oguzhankoral b920d822ef WIP: connector configs 2023-09-15 18:40:46 +03:00
oguzhankoral 0908763d33 Implement config_binding 2023-09-15 18:40:45 +03:00
oguzhankoral 847ad87b23 Introduce ruby traverse_and_construct 2023-09-15 18:40:45 +03:00
oguzhankoral 90cf165dcf Send-Receive operations test via ruby 2023-09-15 18:40:45 +03:00
oguzhankoral 8b0307bc72 Remove test bridge.js 2023-09-15 18:40:44 +03:00
oguzhankoral a645e2829a Unit test for send operation 2023-09-15 18:40:44 +03:00
oguzhankoral 33d8644dd6 Enable collect preferences 2023-09-15 18:40:44 +03:00
oguzhankoral de713a848c Emit only documentChanged instead of passing data 2023-09-15 18:40:43 +03:00
oguzhankoral e9aa91fe7f Rename views to bindings 2023-09-15 18:40:43 +03:00
oguzhankoral 9661c79bd6 Report errors to UI 2023-09-15 18:40:43 +03:00
oguzhankoral 91fab4dcd4 Resolve triggerEvent 2023-09-15 18:40:43 +03:00
oguzhankoral 29488a2d87 Implement test bindings 2023-09-15 18:40:42 +03:00
oguzhankoral a3f1dd2459 Create random sketchup binding 2023-09-15 18:40:42 +03:00
oguzhankoral ab12145e4d Implement on_document_changed action 2023-09-15 18:40:42 +03:00
oguzhankoral 442ea7e6ab Add command for get document info 2023-09-15 18:40:41 +03:00
oguzhankoral 04f0024734 Remove get_commands action and command 2023-09-15 18:40:41 +03:00
oguzhankoral 831cc60ab2 Apply same dialog-view relationship to legacy UI 2023-09-15 18:40:41 +03:00
oguzhankoral 7fc4c55362 Control views via dialog 2023-09-15 18:40:41 +03:00
oguzhankoral 78e8202d1c Remove unnecssary init from view 2023-09-15 18:40:40 +03:00
oguzhankoral f35e953531 Collect command names from related view for SketchupBridge 2023-09-15 18:40:40 +03:00
oguzhankoral f5796bdec8 Pass view to commands 2023-09-15 18:40:40 +03:00
oguzhankoral 9f87c22424 Send messages to correct view/binding 2023-09-15 18:40:39 +03:00
oguzhankoral 88ba11a836 Solve missing resolve_id arguments for commands and actions 2023-09-15 18:40:39 +03:00
oguzhankoral 53f370d24e Run legacy and dui3 at the same time 2023-09-15 18:40:39 +03:00
oguzhankoral 9e0998f054 Pass resolve id from arguments 2023-09-15 18:40:38 +03:00
oguzhankoral 0dce0cb88c Replace Bridge with inhouse resolve_id solution 2023-09-15 18:40:38 +03:00
oguzhankoral a61097bdcd Pass serialized accounts data as json object 2023-09-15 18:40:38 +03:00
oguzhankoral ec073bd544 Pass correct request id for getAccounts 2023-09-15 18:40:38 +03:00
oguzhankoral 1c23da6fb2 Request id temp hack 2023-09-15 18:40:37 +03:00
oguzhankoral 7e6b993b06 Add get commands 2023-09-15 18:40:37 +03:00
oguzhankoral 792251dfb9 Add bridge sample 2023-09-15 18:40:37 +03:00
oguzhankoral 1c4ebb5380 Check correct dui3 id ui controller 2023-09-15 18:40:36 +03:00
oguzhankoral 4ac76d23ca Add new dialog for dui3 2023-09-15 18:40:24 +03:00
277 changed files with 19335 additions and 433 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

@@ -7,6 +7,7 @@ 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)
@@ -23,9 +24,21 @@ 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, {})
state = DeactivateDiffing.update_state(state, nil, {})
puts "Diffing activated for #{@stream_id}"
speckle_entities = state.speckle_state.speckle_entities
invalid_speckle_entities = speckle_entities.select do |_id, entity|
@@ -0,0 +1,47 @@
# 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 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'] == 'ReceiverModelCard'
receive_card = Cards::ReceiveCard.new(data['id'], data['accountId'],
data['projectId'], data['projectName'],
data['modelId'], data['modelName'],
data['referencedObject'])
SketchupModel::Dictionary::ModelCardDictionaryHandler
.save_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('addSendCard', js_script)
end
send_filter = Filters::SendFilters.get_filter_from_ui_data(data['sendFilter'])
# Init card and add to the state
send_card = Cards::SendCard.new(data['id'], data['accountId'], data['projectId'], data['modelId'],
send_filter, {})
SketchupModel::Dictionary::ModelCardDictionaryHandler
.save_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
@@ -0,0 +1,30 @@
# frozen_string_literal: true
require_relative '../action'
require_relative '../../cards/send_card'
require_relative '../../filters/send_filters'
require_relative '../../sketchup_model/dictionary/model_card_dictionary_handler'
module SpeckleConnector
module Actions
# Add model to document state.
class AddModelToDocumentState < 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)
puts model.to_json
send_filter = Filters::SendFilters.get_filter_from_ui_data(model['sendFilter'])
send_card = Cards::SendCard.new(model['id'], model['accountId'], model['projectId'], model['modelId'], send_filter, {})
SketchupModel::Dictionary::ModelCardDictionaryHandler
.save_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)
js_script = "baseBinding.receiveResponse('#{resolve_id}')"
state.with_add_queue_js_command('addModelToDocumentState', js_script)
end
end
end
end
@@ -0,0 +1,40 @@
# frozen_string_literal: true
require_relative '../action'
require_relative '../../filters/send_filters'
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_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'], filter, {})
new_speckle_state = state.speckle_state.with_send_card(send_card)
state = state.with_speckle_state(new_speckle_state)
{
id: send_card.id,
accountId: send_card.account_id,
projectId: send_card.project_id,
modelId: send_card.model_id,
sendFilter: send_card.send_filter,
typeDiscriminator: send_card.type_discriminator
}
end
model_state = { models: send_cards }
js_script = "baseBinding.receiveResponse('#{resolve_id}', #{model_state.to_json})"
state.with_add_queue_js_command('getDocumentState', js_script)
end
end
end
end
@@ -0,0 +1,38 @@
# frozen_string_literal: true
require_relative '../action'
require_relative '../../filters/send_filters'
require_relative '../../sketchup_model/dictionary/model_card_dictionary_handler'
module SpeckleConnector
module Actions
# Gets model state.
class GetModelState < 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_cards_from_dict(state.sketchup_state.sketchup_model)
send_cards = send_cards_hash.collect do |id, card|
filters = Filters::SendFilters.get_filters_from_model(card['filters'])
send_card = Cards::SendCard.new(id, card['account_id'], card['project_id'], card['model_id'], filters)
new_speckle_state = state.speckle_state.with_send_card(send_card)
state = state.with_speckle_state(new_speckle_state)
{
accountId: send_card.account_id,
projectId: send_card.project_id,
modelId: send_card.model_id,
filters: send_card.filters
}
end
model_state = { sendCards: send_cards }
js_script = "baseBinding.receiveResponse('#{resolve_id}', #{model_state.to_json})"
state.with_add_queue_js_command('getModelState', js_script)
end
end
end
end
@@ -0,0 +1,19 @@
# 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
@@ -0,0 +1,17 @@
# 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
@@ -0,0 +1,17 @@
# 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
@@ -0,0 +1,45 @@
# 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)
# objects_to_highlight = if data['typeDiscriminator'] == 'ReceiverModelCard'
# # model_card = state.speckle_state.receive_cards[model_card_id]
# # TODO: return received objects
# []
# else
# model_card = state.speckle_state.send_cards[model_card_id]
# model_card.send_filter.selected_object_ids
# end
objects_to_highlight = state.speckle_state.send_cards[model_card_id].send_filter.selected_object_ids
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
@@ -0,0 +1,31 @@
# 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
@@ -0,0 +1,20 @@
# 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, _data)
def self.update_state(state, _resolve_id, _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, data)
def self.update_state(state, _resolve_id, 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, _data)
def self.update_state(state, _resolve_id, _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, _data)
def self.update_state(state, _resolve_id, _data)
versions = {
sketchup: Sketchup.version.to_i,
speckle: SpeckleConnector::CONNECTOR_VERSION
@@ -0,0 +1,22 @@
# 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 = {
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
@@ -0,0 +1,26 @@
# 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, _data)
def self.update_state(state, _resolve_id, _data)
puts 'Diffing deactivated!'
speckle_entities = state.speckle_state.speckle_entities
diffing_activated_speckle_entities = speckle_entities.reject do |_id, entity|
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require_relative 'event_action'
require_relative 'on_document_changed'
require_relative '../load_sketchup_model'
require_relative '../collect_preferences'
@@ -25,7 +26,8 @@ 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
CollectPreferences.update_state(new_state, {})
new_state = CollectPreferences.update_state(new_state, nil, {})
OnDocumentChanged.update_state(new_state)
end
end
@@ -1,6 +1,7 @@
# 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'
@@ -26,19 +27,23 @@ module SpeckleConnector
def self.update_state(state, event_data)
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
modified_entities = event_data.collect { |data| data[1] }
new_speckle_state = state.speckle_state.with_changed_object_ids(modified_entities.collect(&:persistent_id))
state = state.with_speckle_state(new_speckle_state)
state = Actions::SendCardExpirationCheck.update_state(state)
# 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
@@ -0,0 +1,15 @@
# 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,6 +2,7 @@
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'
@@ -20,9 +21,11 @@ module SpeckleConnector
# Get sketchup selection
sketchup_selection = state.sketchup_state.sketchup_model.selection
Actions::GetSelection.update_state(state)
# 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
@@ -0,0 +1,21 @@
# 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
@@ -0,0 +1,22 @@
# 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
@@ -0,0 +1,31 @@
# 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, data)
def self.update_state(state, _resolve_id, 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, _data)
def self.update_state(state, _request_id, _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,6 +7,7 @@ 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
@@ -14,7 +15,8 @@ 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)
def self.update_state(state, observers, instant_message_sender)
worker = SpeckleConnector::Worker.new()
attach_app_observer!(observers[APP_OBSERVER])
accounts = SpeckleConnector::Accounts.load_accounts
speckle_state = States::SpeckleState.new(accounts, observers, {}, {})
@@ -22,7 +24,8 @@ 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)
state = States::State.new(user_state_with_preferences, speckle_state, sketchup_state, false,
worker, &instant_message_sender)
# 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, data)
def self.update_state(state, _resolve_id, 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, _data)
def self.update_state(state, _request_id, _data)
(saved_streams = state.sketchup_state.sketchup_model
.attribute_dictionary('Speckle', true)['saved_streams']) or []
state.with_add_queue('setSavedStreams', saved_streams, [])
@@ -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, _data = nil)
def self.update_state(state, _resolve_id = nil, _data = nil)
mapped_entities = SketchupModel::Reader::MapperReader
.mapped_entity_details(state.speckle_state.speckle_mapper_state.mapped_entities.values.to_a)
@@ -0,0 +1,22 @@
# frozen_string_literal: true
require_relative 'action'
require_relative '../mapper/category/revit_category'
require_relative '../mapper/category/revit_family_category'
module SpeckleConnector
module Actions
# Collects mapper selection info.
class MapperInitialized < Action
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def self.update_state(state, _data)
init_parameters = {
categories: Mapper::Category::RevitCategory.to_a,
familyCategories: Mapper::Category::RevitFamilyCategory.to_a
}.freeze
state.with_mapper_init_queue(init_parameters)
end
end
end
end
@@ -2,6 +2,7 @@
require_relative 'action'
require_relative '../mapper/category/revit_category'
require_relative '../mapper/category/revit_family_category'
require_relative '../sketchup_model/reader/mapper_reader'
require_relative '../sketchup_model/reader/speckle_entities_reader'
require_relative '../sketchup_model/dictionary/speckle_entity_dictionary_handler'
@@ -35,6 +36,7 @@ module SpeckleConnector
end
def get_mapping_info(state, selection)
source_exist = !state.speckle_state.speckle_mapper_state.mapper_source.nil?
selection = filter_out_levels(selection)
grouped_by_type = group_by_type(selection)
@@ -45,10 +47,23 @@ module SpeckleConnector
# Return Direct Shape itself if multiple kinds of element are selected like Edge and Face.
# OR single type is equal to only direct shape supports.
return multiple_supported_selection_info(selection) if supported_entity_count > 1
# FIXME: Distinguish selection info according to selection elegantly!!!
if grouped_by_type.keys.first == Sketchup::ComponentInstance
return component_selection_info(selection, source_exist)
end
return group_selection_info(selection) if grouped_by_type.keys.first == Sketchup::Group
if supported_entity_count > 1 ||
(supported_entity_count == 1 &&
MAPPER_DIRECT_SHAPE_SUPPORTED_ENTITY_TYPES.include?(grouped_by_type.keys.first))
return direct_shape_selection_info(selection)
if source_exist
return direct_shape_selection_info_with_source(selection, [])
else
return direct_shape_selection_info(selection, source_exist)
end
end
# Only single type selections remained after this point.
@@ -73,29 +88,54 @@ module SpeckleConnector
EMPTY_SELECTION = {
selection: [],
mappingMethods: [],
categories: []
mappingMethods: []
}.freeze
def direct_shape_selection_info(selection)
def multiple_supported_selection_info(selection)
{
selection: SketchupModel::Reader::MapperReader.entities_schema_details(selection),
mappingMethods: ['Direct Shape'],
categories: Mapper::Category::RevitCategory.to_a
mappingMethods: ['Direct Shape']
}.freeze
end
def component_selection_info(selection, source_exist)
if source_exist
{
selection: SketchupModel::Reader::MapperReader.entities_schema_details(selection),
mappingMethods: ['Direct Shape', 'New Revit Family', 'Family Instance']
}.freeze
else
{
selection: SketchupModel::Reader::MapperReader.entities_schema_details(selection),
mappingMethods: ['Direct Shape', 'New Revit Family']
}.freeze
end
end
def group_selection_info(selection)
{
selection: SketchupModel::Reader::MapperReader.entities_schema_details(selection),
mappingMethods: ['Direct Shape']
}.freeze
end
def direct_shape_selection_info(selection, source_exist)
methods = ['Direct Shape', 'New Revit Family']
methods.append('Family Instance') if source_exist
{
selection: SketchupModel::Reader::MapperReader.entities_schema_details(selection),
mappingMethods: methods
}.freeze
end
def direct_shape_selection_info_with_default(selection, methods)
{
selection: SketchupModel::Reader::MapperReader.entities_schema_details(selection),
mappingMethods: ['Direct Shape'] + methods,
categories: Mapper::Category::RevitCategory.to_a
mappingMethods: ['Direct Shape'] + methods
}.freeze
end
def direct_shape_selection_info_with_source(state, filtered_selection, methods)
types = state.speckle_state.speckle_mapper_state.mapper_source.types
levels = state.speckle_state.speckle_mapper_state.mapper_source.levels
def direct_shape_selection_info_with_source(filtered_selection, methods)
instances = @selection.grep(Sketchup::ComponentInstance)
selected_level = instances.find do |i|
DICTIONARY::SpeckleEntityDictionaryHandler
@@ -109,8 +149,6 @@ module SpeckleConnector
selection: READER::MapperReader.entities_schema_details(filtered_selection),
mappingMethods: ['Direct Shape'] + methods,
categories: Mapper::Category::RevitCategory.to_a,
types: types,
levels: levels,
selectedLevelName: selected_level_name
}.freeze
end
@@ -119,13 +157,13 @@ module SpeckleConnector
def face_selection_info(state, faces)
source_exist = !state.speckle_state.speckle_mapper_state.mapper_source.nil?
grouped_by_verticality = faces.group_by { |face| face.normal.perpendicular?(VECTOR_Z) }
return direct_shape_selection_info(faces) if grouped_by_verticality.length == 2
return direct_shape_selection_info(faces, source_exist) if grouped_by_verticality.length == 2
if source_exist
if grouped_by_verticality.keys.first
direct_shape_selection_info_with_source(state, faces, ['Wall'])
direct_shape_selection_info_with_source(faces, ['Wall'])
else
direct_shape_selection_info_with_source(state, faces, ['Floor'])
direct_shape_selection_info_with_source(faces, ['Floor'])
end
else
if grouped_by_verticality.keys.first
@@ -141,7 +179,7 @@ module SpeckleConnector
if source_exist
methods = ['Column', 'Beam', 'Pipe', 'Duct']
direct_shape_selection_info_with_source(state, edges, methods)
direct_shape_selection_info_with_source(edges, methods)
else
default_methods = ['Default Column', 'Default Beam', 'Default Pipe', 'Default Duct']
direct_shape_selection_info_with_default(edges, default_methods)
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require_relative 'action'
require_relative '../constants/type_constants'
require_relative '../mapper/mapper_source'
require_relative '../speckle_objects/built_elements/revit/revit_element_type'
@@ -20,13 +21,15 @@ module SpeckleConnector
def update_state(state)
levels = convert_levels(state, @base['@Levels'])
types = convert_types(@base['@Types'])
family_instances = convert_family_instance_types(@base['@Types'])
mapper_source = Mapper::MapperSource.new(@stream_id, @commit_id, levels, types)
new_speckle_state = state.speckle_state.with_mapper_source(mapper_source)
state = state.with_speckle_state(new_speckle_state)
state.with_add_queue('mapperSourceUpdated', @stream_id, [
{ is_string: false, val: levels.to_json },
{ is_string: false, val: types.to_json }
{ is_string: false, val: types.to_json },
{ is_string: false, val: family_instances.to_json }
])
end
@@ -43,6 +46,27 @@ module SpeckleConnector
end.compact.to_h
end
def convert_family_instance_types(types)
family_instance_types = {}
types.each do |type, type_elements|
next if type_elements.nil? || !type_elements.is_a?(Array) || type == '__closure'
# skip type if there is no any revit symbol element type
symbol_element_types = type_elements.select do |t|
t['speckle_type'] == OBJECTS_BUILTELEMENTS_REVIT_REVITSYMBOLELEMENTTYPE &&
t['placementType'] == 'OneLevelBased'
end
next if symbol_element_types.empty?
elements = type_elements.map do |type_element|
SpeckleObjects::BuiltElements::Revit::RevitElementType.to_native(type_element)
end
elements = elements.group_by { |e| e[:family] }
family_instance_types.merge!(elements)
end
family_instance_types
end
def convert_levels(state, levels)
levels.collect do |level|
SpeckleObjects::BuiltElements::Level.to_native(state, level, @stream_id)
@@ -0,0 +1,18 @@
# 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
@@ -0,0 +1,18 @@
# 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
@@ -0,0 +1,91 @@
# 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
@@ -0,0 +1,26 @@
# frozen_string_literal: true
require_relative '../action'
require_relative '../../convertors/to_native'
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]
converter = Converters::ToNative.new(state, model_card.model_id, model_card.project_name,
model_card.model_name, source_application)
# Have side effects on the sketchup model. It effects directly on the entities by adding new objects.
state = converter.receive_commit_object(root_obj)
resolve_js_script = "receiveBinding.receiveResponse('#{resolve_id}')"
state.with_add_queue_js_command('receive', resolve_js_script)
end
end
end
end
@@ -0,0 +1,37 @@
# 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, source_application)
model_card = state.speckle_state.receive_cards[model_card_id]
account = Accounts.get_account_by_id(model_card.account_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,
modelId: model_card.model_id,
token: account['token'],
serverUrl: account['serverInfo']['url'],
accountId: model_card.account_id,
objectId: model_card.object_id,
sourceApplication: source_application
}
js_script = "receiveBinding.emit('receiveViaBrowser', #{args.to_json})"
state.with_add_queue_js_command('receiveViaBrowser', js_script)
end
end
end
end
@@ -3,6 +3,7 @@
require_relative 'action'
require_relative '../convertors/units'
require_relative '../convertors/to_native'
require_relative '../operations/receive'
require_relative '../convertors/clean_up'
module SpeckleConnector
@@ -10,7 +11,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)
def initialize(stream_id, base, stream_name, branch_name, branch_id, source_app, object_id)
super()
@stream_id = stream_id
@base = base
@@ -18,12 +19,15 @@ 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, _data)
def self.update_state(state, _resolve_id, _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, data)
def self.update_state(state, _resolve_id, data)
# Clear first selection
state.sketchup_state.sketchup_model.selection.clear
# Flat entities to clear mappings
# Flat entities to select mapped elements
flat_entities = SketchupModel::Query::Entity.flat_entities(state.sketchup_state.sketchup_model.entities)
# Collect entity ids to clear mappings
@@ -0,0 +1,22 @@
# 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)
selected_object_ids = state.sketchup_state.sketchup_model.selection.collect(&:persistent_id)
summary = "Selected #{selected_object_ids.length} objects."
selection_info = UiData::Sketchup::SelectionInfo.new(selected_object_ids, summary)
# js_script = "selectionBinding.receiveResponse('#{resolve_id}', #{selection_info.to_json})"
js_script = "selectionBinding.emit('setSelection', #{selection_info.to_json})"
state.with_add_queue_js_command('setSelection', js_script)
end
end
end
end
@@ -0,0 +1,22 @@
# 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
@@ -0,0 +1,22 @@
# 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
@@ -0,0 +1,86 @@
# 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'
require_relative '../../ext/worker'
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]
account = Accounts.get_account_by_id(model_card.account_id)
converter = Converters::ToSpeckle.new(state, model_card_id, model_card.send_filter)
new_speckle_state, base = converter.convert_selection_to_base(state.user_state.preferences)
id, total_children_count, batches, new_speckle_state = converter.serialize(base, new_speckle_state,
state.user_state.preferences)
# update_test(state)
puts("converted #{base.count} objects for stream #{@stream_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_test(state)
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
@@ -0,0 +1,23 @@
# 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_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_ids)
end.keys.to_a
js_script = "sendBinding.emit('sendersExpired', #{expired_send_cards_ids.to_json})"
state.with_add_queue_js_command('sendersExpired', js_script)
end
end
end
end
@@ -4,6 +4,7 @@ require_relative 'action'
require_relative 'deactivate_diffing'
require_relative '../convertors/units'
require_relative '../convertors/to_speckle'
require_relative '../operations/send'
module SpeckleConnector
module Actions
@@ -17,11 +18,14 @@ 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, {})
converter = Converters::ToSpeckle.new(state, @stream_id)
state = DeactivateDiffing.update_state(state, nil, {})
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, new_speckle_state = converter.serialize(base, new_speckle_state,
state.user_state.preferences)
# TODO: Later active send operation.
# Operations.send(@stream_id, batches)
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, _data)
def self.update_state(state, _resolve_id, _data)
# Show all entities first
state.sketchup_state.sketchup_model.entities.each do |ent|
ent.hidden = false
@@ -0,0 +1,46 @@
# 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
@@ -0,0 +1,18 @@
# 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
@@ -0,0 +1,21 @@
# 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
@@ -0,0 +1,18 @@
# 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
@@ -0,0 +1,21 @@
# 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
@@ -0,0 +1,28 @@
# 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,10 +28,6 @@ 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
@@ -40,17 +36,24 @@ module SpeckleConnector
# Send messages to HtmlDialog if any.
def send_messages!
queue = @state.speckle_state.message_queue
queue.each_value { |value| ui_controller.user_interfaces[Ui::SPECKLE_UI_ID].dialog.execute_script(value) }
queue.each_value do |value|
instant_message_sender(value)
end
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
@@ -0,0 +1,39 @@
# 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 :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(card_id, account_id, project_id, model_id)
super()
@id = card_id
@account_id = account_id
@project_id = project_id
@model_id = model_id
@valid = true
self[:id] = card_id
self[:account_id] = account_id
self[:project_id] = project_id
self[:model_id] = model_id
self[:valid] = @valid
end
end
end
end
@@ -0,0 +1,36 @@
# 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] object id to receive
attr_reader :object_id
# @return [String] name of the project
attr_reader :project_name
# @return [String] name of the model
attr_reader :model_name
def initialize(card_id, account_id, project_id, project_name, model_id, model_name, object_id)
super(card_id, account_id, project_id, model_id)
@object_id = object_id
self[:object_id] = object_id
self[:model_name] = model_name
self[:project_name] = project_name
@model_name = model_name
@project_name = project_name
@type_discriminator = 'ReceiverModelCard'
self[:type_discriminator] = @type_discriminator
end
end
end
end
+31
View File
@@ -0,0 +1,31 @@
# 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
def initialize(card_id, account_id, project_id, model_id, send_filter, send_settings)
super(card_id, account_id, project_id, model_id)
@send_filter = send_filter
@send_settings = send_settings
@type_discriminator = 'SenderModelCard'
self[:sendFilter] = send_filter
self[:sendSettings] = send_settings
self[:type_discriminator] = @type_discriminator
end
end
end
end
@@ -0,0 +1,19 @@
# 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, action)
super(app)
@app = app
def initialize(app, binding, action)
super(app, binding)
@action = action
end
@@ -7,7 +7,7 @@ module SpeckleConnector
module Commands
# Command to activate diffing for stream.
class ActivateDiffing < Command
def _run(data)
def _run(_resolve_id, 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(data)
def _run(_resolve_id, 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(data)
def _run(_resolve_id, data)
entities_to_map = data['entitiesToClearMap']
is_definition = data['isDefinition']
action = Actions::ClearMappings.new(entities_to_map, is_definition)
+16 -8
View File
@@ -1,5 +1,7 @@
# frozen_string_literal: true
require_relative '../actions/handle_error'
module SpeckleConnector
module Commands
# Base command schema to wrap common operations for all commands.
@@ -7,19 +9,25 @@ module SpeckleConnector
# @return [App::SpeckleConnectorApp] the main app object
attr_reader :app
# @return [Ui::View] view object holds dialog and it's state
attr_reader :view
# @return [Ui::Binding] binding object holds dialog and it's state
attr_reader :binding
# @@param app [App::SpeckleConnectorApp] the main app object
def initialize(app)
# @param app [App::SpeckleConnectorApp] the main app object
# @param binding [Ui::Binding] binding object holds commands to call
def initialize(app, binding)
@app = app
@view = app.ui_controller.user_interfaces[Ui::SPECKLE_UI_ID]
@binding = binding
end
def run(*parameters)
# Run here common operations that same for each command.
with_observers_disabled do
_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)
end
end
@@ -1,15 +0,0 @@
# 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
@@ -0,0 +1,85 @@
# 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,15 +2,17 @@
require_relative 'command'
require_relative '../states/initial_state'
require_relative '../ui/vue_view'
require_relative '../ui/legacy_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.
# Command to initialize old 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}"
end
@@ -19,32 +21,34 @@ module SpeckleConnector
def _run
app = self.app
unless app.state.instance_of?(States::InitialState)
vue_view = app.ui_controller.user_interfaces[Ui::SPECKLE_UI_ID]
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]
vue_view.show
return
end
initialize_speckle(app)
initialize_speckle_legacy_view(app)
end
# Do the actual Speckle initialization.
def initialize_speckle(app)
def initialize_speckle_legacy_view(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: Ui::SPECKLE_UI_ID,
dialog_id: SPECKLE_LEGACY_UI,
htm_file: Ui::VUE_UI_HTML,
dialog_title: dialog_title,
height: 950,
width: 300
}
vue_view = Ui::VueView.new(dialog_specs, app)
app.ui_controller.register_ui(Ui::SPECKLE_UI_ID, vue_view)
vue_view.show
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
end
end
end
@@ -7,7 +7,7 @@ module SpeckleConnector
module Commands
# Command to update mapper source.
class MapperSourceUpdated < Command
def _run(data)
def _run(_resolve_id, 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(data)
def _run(_resolve_id, 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(data)
def _run(_resolve_id, data)
stream_id = data['stream_id']
app.update_state!(Actions::Connected)
app.update_state!(Actions::SendFromQueue.new(stream_id))
@@ -7,14 +7,15 @@ module SpeckleConnector
module Commands
# Command to receive objects from Speckle Server.
class ReceiveObjects < Command
def _run(data)
def _run(_resolve_id, 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']
action = Actions::ReceiveObjects.new(stream_id, base, stream_name, branch_name, branch_id, source_app)
object_id = data['object_id']
action = Actions::ReceiveObjects.new(stream_id, base, stream_name, branch_name, branch_id, source_app, object_id)
app.update_state!(action)
end
end
@@ -8,7 +8,7 @@ module SpeckleConnector
module Commands
# Command to remove stream.
class RemoveStream < Command
def _run(data)
def _run(_resolve_id, data)
stream_id = data['stream_id']
action = Actions::RemoveStream.new(stream_id)
app.update_state!(action)
@@ -0,0 +1,26 @@
# frozen_string_literal: true
require_relative 'command'
require_relative '../states/initial_state'
require_relative '../actions/initialize_speckle'
require_relative '../observers/factory'
module SpeckleConnector
module Commands
# Command to reset Speckle UI window location onto center of SketchUp window.
class ResetWindowLocation < Command
private
def _run
app = self.app
vue_view = app.ui_controller.user_interfaces[Ui::SPECKLE_LEGACY_UI]
if vue_view
vue_view.dialog.reset_dialog_location
else
puts "Speckle UI didn't initialized!"
end
end
end
end
end
@@ -9,7 +9,7 @@ module SpeckleConnector
module Commands
# Command to saved stream.
class SaveStream < Command
def _run(data)
def _run(_resolve_id, 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(data)
def _run(_resolve_id, data)
stream_id = data['stream_id']
action = Actions::SendSelection.new(stream_id)
app.update_state!(action)
@@ -3,6 +3,8 @@
require_relative 'menu_command_handler'
require_relative 'action_command'
require_relative 'initialize_speckle'
require_relative 'initialize_dui3_speckle'
require_relative 'reset_window_location'
require_relative '../actions/one_click_send'
module SpeckleConnector
@@ -10,6 +12,8 @@ module SpeckleConnector
# Speckle menu commands that adds them to Sketchup menu and toolbar.
class SpeckleMenuCommands
CMD_INITIALIZE_SPECKLE = :initialize_speckle
CMD_INITIALIZE_DUI3_SPECKLE = :initialize_dui3_speckle
CMD_RESET_WINDOW_LOCATION_SPECKLE = :reset_window_location_speckle
CMD_SEND_TO_SPECKLE = :send_to_speckle
CMD_RECEIVE_FROM_SPECKLE = :receive_from_speckle
@@ -26,6 +30,13 @@ module SpeckleConnector
commands.add_to_menu!(CMD_INITIALIZE_SPECKLE, speckle_menu)
commands.add_to_toolbar!(CMD_INITIALIZE_SPECKLE, speckle_toolbar)
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_RESET_WINDOW_LOCATION_SPECKLE] = reset_window_location_command(app)
commands.add_to_menu!(CMD_RESET_WINDOW_LOCATION_SPECKLE, speckle_menu)
# 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)
@@ -33,7 +44,7 @@ module SpeckleConnector
def self.initialize_speckle_command(app)
cmd = MenuCommandHandler.sketchup_command(
InitializeSpeckle.new(app), 'Initialize Speckle'
InitializeSpeckle.new(app, nil), 'Initialize Speckle'
)
cmd.tooltip = 'Launch Connector'
cmd.status_bar_text = 'Opens the Speckle Connector window'
@@ -42,9 +53,31 @@ module SpeckleConnector
cmd
end
def self.initialize_dui3_speckle_command(app)
cmd = MenuCommandHandler.sketchup_command(
InitializeDUI3Speckle.new(app, nil), 'Initialize DUI3 Speckle'
)
cmd.tooltip = 'Launch Connector with DUI3'
cmd.status_bar_text = 'Opens the Speckle Connector DUI3 Window'
cmd.small_icon = '../../img/s2logo_dui3.png'
cmd.large_icon = '../../img/s2logo_dui3.png'
cmd
end
def self.reset_window_location_command(app)
cmd = MenuCommandHandler.sketchup_command(
ResetWindowLocation.new(app, nil), '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'
cmd.small_icon = '../../img/s2logo.png'
cmd.large_icon = '../../img/s2logo.png'
cmd
end
def self.send_command(app)
cmd = MenuCommandHandler.sketchup_command(
ActionCommand.new(app, Actions::OneClickSend), 'Send to Speckle'
ActionCommand.new(app, nil, Actions::OneClickSend), 'Send to Speckle'
)
cmd.tooltip = 'Send to Speckle'
cmd.status_bar_text = 'Send to Speckle'
@@ -8,7 +8,7 @@ module SpeckleConnector
module Commands
# Command to update preferences.
class UserPreferencesUpdated < Command
def _run(data)
def _run(_resolve_id, data)
preference_hash = data['preference_hash']
preference = data['preference']
new_value = data['value']
@@ -5,6 +5,9 @@ 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'
@@ -15,7 +15,7 @@ module SpeckleConnector
Sketchup::ComponentInstance => INCLUDE_COMPONENT_ENTITY_ATTRIBUTES,
Sketchup::Group => INCLUDE_GROUP_ENTITY_ATTRIBUTES,
Sketchup::Face => INCLUDE_FACE_ENTITY_ATTRIBUTES,
Sketchup::Face => INCLUDE_EDGE_ENTITY_ATTRIBUTES
Sketchup::Edge => INCLUDE_EDGE_ENTITY_ATTRIBUTES
}.freeze
LEVEL_SHIFT_VALUE = SpeckleObjects::Geometry.length_to_native(1.5, 'm')
@@ -4,6 +4,7 @@ module SpeckleConnector
BASE_OBJECT = 'Base'
OBJECTS_GIS_POLYGONELEMENT = 'Objects.GIS.PolygonElement'
OBJECTS_GIS_LINEELEMENT = 'Objects.GIS.LineElement'
OBJECTS_BUILTELEMENTS_VIEW3D = 'Objects.BuiltElements.View:Objects.BuiltElements.View3D'
OBJECTS_BUILTELEMENTS_NETWORK = 'Objects.BuiltElements.Network'
@@ -12,9 +13,19 @@ module SpeckleConnector
OBJECTS_BUILTELEMENTS_REVIT_FLOOR = 'Objects.BuiltElements.Floor:Objects.BuiltElements.Revit.RevitFloor'
OBJECTS_BUILTELEMENTS_DEFAULT_WALL = 'Objects.BuiltElements.Wall'
OBJECTS_BUILTELEMENTS_REVIT_WALL = 'Objects.BuiltElements.Wall:Objects.BuiltElements.Revit.RevitWall'
OBJECTS_BUILTELEMENTS_DEFAULT_COLUMN = 'Objects.BuiltElements.Column'
OBJECTS_BUILTELEMENTS_REVIT_COLUMN = 'Objects.BuiltElements.Column:Objects.BuiltElements.Revit.RevitColumn'
OBJECTS_BUILTELEMENTS_DEFAULT_BEAM = 'Objects.BuiltElements.Beam'
OBJECTS_BUILTELEMENTS_REVIT_BEAM = 'Objects.BuiltElements.Beam:Objects.BuiltElements.Revit.RevitBeam'
OBJECTS_BUILTELEMENTS_DEFAULT_PIPE = 'Objects.BuiltElements.Pipe'
OBJECTS_BUILTELEMENTS_REVIT_PIPE = 'Objects.BuiltElements.Pipe:Objects.BuiltElements.Revit.RevitPipe'
OBJECTS_BUILTELEMENTS_DEFAULT_DUCT = 'Objects.BuiltElements.Duct'
OBJECTS_BUILTELEMENTS_REVIT_DUCT = 'Objects.BuiltElements.Duct:Objects.BuiltElements.Revit.RevitDuct'
OBJECTS_BUILTELEMENTS_REVIT_DIRECTSHAPE = 'Objects.BuiltElements.Revit.DirectShape'
OBJECTS_BUILTELEMENTS_REVIT_FAMILY_INSTANCE = 'Objects.BuiltElements.Revit.FamilyInstance'
OBJECTS_BUILTELEMENTS_REVIT_PARAMETER = 'Objects.BuiltElements.Revit.Parameter'
OBJECTS_BUILTELEMENTS_REVIT_REVITELEMENTTYPE = 'Objects.BuiltElements.Revit.RevitElementType'
OBJECTS_BUILTELEMENTS_REVIT_REVITSYMBOLELEMENTTYPE = 'Objects.BuiltElements.Revit.RevitElementType:Objects.BuiltElements.Revit.RevitSymbolElementType'
OBJECTS_GEOMETRY_LINE = 'Objects.Geometry.Line'
OBJECTS_GEOMETRY_POLYLINE = 'Objects.Geometry.Polyline'
@@ -33,4 +44,6 @@ module SpeckleConnector
OBJECTS_OTHER_DISPLAYSTYLE = 'Objects.Other.DisplayStyle'
SPECKLE_CORE_MODELS_COLLECTION = 'Speckle.Core.Models.Collection'
SPECKLE_CORE_MODELS_COLLECTION_RASTER_LAYER = 'Speckle.Core.Models.Collection:Objects.GIS.RasterLayer'
SPECKLE_CORE_MODELS_COLLECTION_VECTOR_LAYER = 'Speckle.Core.Models.Collection:Objects.GIS.VectorLayer'
end
@@ -310,8 +310,12 @@ module SpeckleConnector
Digest::MD5.hexdigest(traversed_base.to_json)
end
def batch_objects
@objects
end
# rubocop:disable Metrics/MethodLength
def batch_objects(max_batch_size_mb = 1)
def batch_json_objects(max_batch_size_mb = 1)
max_size = 1000 * 1000 * max_batch_size_mb
batches = []
batch = '['
+14 -9
View File
@@ -61,19 +61,24 @@ module SpeckleConnector
face_1, face_2 = edge.faces
return false unless face_1.normal.samedirection?(face_2.normal)
begin
return false unless face_1.normal.samedirection?(face_2.normal)
return false if face_duplicate?(face_1, face_2)
# Check for troublesome faces which might lead to missing geometry if merged.
return false unless edge_safe_to_merge?(edge)
return false if face_duplicate?(face_1, face_2)
# Check for troublesome faces which might lead to missing geometry if merged.
return false unless edge_safe_to_merge?(edge)
return false unless (face_1.material == face_2.material) && (face_1.back_material == face_2.back_material)
return false unless (face_1.material == face_2.material) && (face_1.back_material == face_2.back_material)
# Check faces are coplanar or not.
return false unless faces_coplanar?(face_1, face_2)
# Check faces are coplanar or not.
return false unless faces_coplanar?(face_1, face_2)
edge.erase!
true
edge.erase!
true
rescue StandardError => e
puts "Failed to merge coplanar faces by removing edge with error: #{e}"
false
end
end
# Determines if two faces are overlapped.
@@ -21,6 +21,8 @@ module SpeckleConnector
attr_accessor :definitions
# @param state [States::State] the current state of the {SpeckleConnector::App}
def initialize(state, stream_id)
@state = state
+11 -1
View File
@@ -4,6 +4,7 @@ require_relative 'converter'
require_relative '../constants/type_constants'
require_relative '../speckle_entities/speckle_entity'
require_relative '../speckle_objects/gis/polygon_element'
require_relative '../speckle_objects/gis/line_element'
require_relative '../speckle_objects/other/transform'
require_relative '../speckle_objects/other/render_material'
require_relative '../speckle_objects/other/block_definition'
@@ -19,6 +20,7 @@ require_relative '../speckle_objects/geometry/mesh'
require_relative '../speckle_objects/built_elements/view3d'
require_relative '../speckle_objects/built_elements/network'
require_relative '../speckle_objects/speckle/core/models/collection'
require_relative '../speckle_objects/speckle/core/models/gis_layer_collection'
require_relative '../sketchup_model/dictionary/speckle_entity_dictionary_handler'
module SpeckleConnector
@@ -64,7 +66,9 @@ module SpeckleConnector
DISPLAY_VALUE = OTHER::DisplayValue
VIEW3D = BUILTELEMENTS::View3d
POLYGON_ELEMENT = GIS::PolygonElement
LINE_ELEMENT = GIS::LineElement
COLLECTION = SpeckleObjects::Speckle::Core::Models::Collection
GIS_LAYER_COLLECTION = SpeckleObjects::Speckle::Core::Models::GisLayerCollection
BASE_OBJECT_PROPS = %w[applicationId id speckle_type totalChildrenCount].freeze
CONVERTABLE_SPECKLE_TYPES = %w[
@@ -84,7 +88,10 @@ module SpeckleConnector
Objects.BuiltElements.Wall:Objects.BuiltElements.Revit.RevitWall
Objects.BuiltElements.Network
Objects.GIS.PolygonElement
Objects.GIS.LineElement
Speckle.Core.Models.Collection
Speckle.Core.Models.Collection:Objects.GIS.RasterLayer
Speckle.Core.Models.Collection:Objects.GIS.VectorLayer
].freeze
def from_revit
@@ -303,7 +310,10 @@ module SpeckleConnector
OBJECTS_BUILTELEMENTS_REVIT_DIRECTSHAPE => BUILTELEMENTS::Revit::DirectShape.method(:to_native),
OBJECTS_BUILTELEMENTS_NETWORK => BUILTELEMENTS::Network.method(:to_native),
OBJECTS_GIS_POLYGONELEMENT => POLYGON_ELEMENT.method(:to_native),
SPECKLE_CORE_MODELS_COLLECTION => COLLECTION.method(:to_native)
OBJECTS_GIS_LINEELEMENT => LINE_ELEMENT.method(:to_native),
SPECKLE_CORE_MODELS_COLLECTION => COLLECTION.method(:to_native),
SPECKLE_CORE_MODELS_COLLECTION_RASTER_LAYER => GIS_LAYER_COLLECTION.method(:to_native),
SPECKLE_CORE_MODELS_COLLECTION_VECTOR_LAYER => GIS_LAYER_COLLECTION.method(:to_native)
}.freeze
# @param state [States::State] state of the speckle application
+35 -10
View File
@@ -17,6 +17,9 @@ require_relative '../constants/path_constants'
require_relative '../sketchup_model/reader/speckle_entities_reader'
require_relative '../sketchup_model/reader/mapper_reader'
require_relative '../sketchup_model/query/entity'
require_relative '../ext/TT_Lib2/progressbar'
require_relative '../ext/TT_Lib2/model'
require_relative '../ext/TT_Lib2/entities'
module SpeckleConnector
module Converters
@@ -27,12 +30,37 @@ module SpeckleConnector
SPECKLE_ENTITIES_READER = SketchupModel::Reader::SpeckleEntitiesReader
VIEW3D = SpeckleObjects::BuiltElements::View3d
# @return [TT::ProgressBar]
attr_reader :progress_bar
attr_reader :entity_count
attr_reader :send_filter
attr_reader :worker
attr_writer :count
def initialize(state, stream_id, send_filter)
super(state, stream_id)
@count = 0
@send_filter = send_filter
model = state.sketchup_state.sketchup_model
@entity_count = if send_filter.name == 'Selection'
TT::Model.count_unique_entity(model)
else
TT::Entities.count_unique_entity(model.selection)
end
@progress_bar = TT::Progressbar.new(@entity_count, 'Converting to Speckle')
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, speckle_state,
@units, preferences, &convert)
@units, preferences, state, @stream_id, &convert)
return new_speckle_state, model_collection
end
@@ -44,7 +72,7 @@ module SpeckleConnector
serializer = SpeckleConnector::Converters::BaseObjectSerializer.new(speckle_state, stream_id, preferences)
t = Time.now.to_f
id = serializer.serialize(base_and_entity)
batches = serializer.batch_objects
batches = serializer.batch_json_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)
@@ -62,25 +90,22 @@ module SpeckleConnector
# @param speckle_state [States::SpeckleState] the current speckle state of the {States::State}
# @param parent [Symbol, String] parent of the Sketchup Entity to be converted.
def convert(entity, preferences, speckle_state, parent = :base)
progress_bar.next
convert = method(:convert)
unless SketchupModel::Reader::MapperReader.mapped_with_schema?(entity)
unless SketchupModel::Reader::MapperReader.mapped_with_schema?(entity) &&
!entity.is_a?(Sketchup::ComponentDefinition)
return from_native_to_speckle(entity, preferences, speckle_state, parent, &convert)
end
return speckle_state, nil
end
def from_mapped_to_speckle(entity, path, preferences)
direct_shape = SpeckleObjects::BuiltElements::Revit::DirectShape
.from_entity(speckle_state, entity, path, @units, preferences)
return [direct_shape, [entity]]
end
# rubocop:disable Metrics/MethodLength
def from_native_to_speckle(entity, preferences, speckle_state, parent, &convert)
if entity.is_a?(Sketchup::Edge)
line = SpeckleObjects::Geometry::Line.from_edge(entity, @units, preferences[:model]).to_h
line = SpeckleObjects::Geometry::Line.from_edge(speckle_state: speckle_state, edge: entity,
units: @units, model_preferences: preferences[:model]).to_h
return speckle_state, [line, [entity]]
end
+23
View File
@@ -0,0 +1,23 @@
#-------------------------------------------------------------------------------
#
# 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
@@ -0,0 +1,74 @@
#-----------------------------------------------------------------------------
#
# 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
#
#-----------------------------------------------------------------------------
@@ -0,0 +1,12 @@
= 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
Binary file not shown.
+70
View File
@@ -0,0 +1,70 @@
#-----------------------------------------------------------------------------
#
# Thomas Thomassen
# thomas[at]thomthom[dot]net
#
#-----------------------------------------------------------------------------
require_relative 'core.rb'
# Collection of Arc methods.
#
# @since 2.0.0
module SpeckleConnector
module TT::Arc
# Checks if a given +Curve+ is an +ArcCurve+ and not a polygon.
#
# Check for polygon requires SketchUp 7.1M1 or newer. Older SketchUp
# versions will not check for this property.
#
# @param [Sketchup::Curve] curve
# @since 2.0.0
def self.is?( curve )
return false if curve.respond_to?( :is_polygon? ) && curve.is_polygon?
curve.is_a?(Sketchup::ArcCurve)
end
# Checks if a given +Curve+ makes an circle and is not a polygon.
#
# Check for polygon requires SketchUp 7.1M1 or newer. Older SketchUp
# versions will not check for this property.
#
# SketchUp has a bug where an ArcCurve some times has 720 degrees angle
# instead of 360. This method handles this.
#
# @param [Sketchup::Curve] curve
# @since 2.0.0
def self.circle?( curve )
return false unless self.is?( curve )
return ((curve.end_angle - curve.start_angle).radians >= 360) ? true : false
end
# Based on Chris Fullmers "Exploded Arc Centerpoint Finder".
# Calculates the centre points of an arc given two edges.
#
# @param [Sketchup::Edge] e1
# @param [Sketchup::Edge] e2
#
# @return [Geom::Point3d, nil]
# @since 2.0.0
def self.exploded_center(e1, e2)
# Two edges representing an arc must have the same length and can not
# be parallel.
return nil if e1.length != e2.length
v1 = e1.line[1]
v2 = e2.line[1]
return nil if v1.parallel?( v2 )
# Get mid-point of edges from where intersecting lines will origin.
m1 = Geom.linear_combination( 0.5, e1.start.position, 0.5, e1.end.position )
m2 = Geom.linear_combination( 0.5, e2.start.position, 0.5, e2.end.position )
# Get the vectors for the intersecting lines
z_axis = v1 * v2
line1 = [m1, v1 * z_axis]
line2 = [m2, v2 * z_axis]
# Return the center
Geom.intersect_line_line(line1, line2)
end
end # module TT::Arc
end
@@ -0,0 +1,63 @@
#-----------------------------------------------------------------------------
#
# Thomas Thomassen
# thomas[at]thomthom[dot]net
#
#-----------------------------------------------------------------------------
require_relative 'core.rb'
# Collection of AttributeDictionary methods.
#
# @since 2.5.0
module SpeckleConnector
module TT::Attributes
# Compare two +AttributeDictionaries+ objects.
#
# @param [Sketchup::AttributeDictionaries] dictionaries1
# @param [Sketchup::AttributeDictionaries] dictionaries2
#
# @return [Boolean]
# @since 2.5.0
def self.dictionaries_equal?( dictionaries1, dictionaries2 )
if dictionaries1.nil? || dictionaries2.nil?
if dictionaries1.nil? && dictionaries2.nil?
return true
else
return false
end
end
for dictionary in dictionaries1
return false unless d = dictionaries2[ dictionary.name ]
return false unless self.dictionary_equal?( dictionary, d, false )
end
return true
end
# Compare two +AttributeDictionary+ objects. By defaults their names must
# match, but one can set +compare_name+ to +false+ to only compare their
# content.
#
# @param [Sketchup::AttributeDictionary] dictionary1
# @param [Sketchup::AttributeDictionary] dictionary2
# @param [Boolean] compare_name
#
# @return [Boolean]
# @since 2.5.0
def self.dictionary_equal?( dictionary1, dictionary2, compare_name = true )
if compare_name
return false unless dictionary1.name == dictionary2.name
end
return false unless dictionary1.length == dictionary2.length
return false unless dictionary1.keys == dictionary2.keys
for key in dictionary1.keys
return false unless dictionary1[key] == dictionary2[key]
end
return true
end
end # module TT::Attributes
end
@@ -0,0 +1,348 @@
#-----------------------------------------------------------------------------
#
# Thomas Thomassen
# thomas[at]thomthom[dot]net
#
#-----------------------------------------------------------------------------
require_relative 'core.rb'
# Translation dictionary. Pass strings through the +translate+ or +tr+ method
# to translate strings to the language given in to the last +load+ call.
#
# Silently eats errors and pass through the original string if a translation
# cannot be made.
#
# module MyPlugin
#
# # Assigning the instance to a constant makes it into
# # a easy to use shorthand that works in any sub-modules/classes.
# S = TT::Babelfish.new( l10n_path )
#
# def self.init
# S.load( 'fr' )
# end
#
# def self.foo
# puts S.tr( 'Hello World' )
# end
#
# end # module
#
#
# @since 2.5.0
module SpeckleConnector
class TT::Babelfish
# @since 2.5.0
attr_reader( :l10n, :default_l10n, :path, :file_ex )
# @param [String] l10n_path The path where the translation files are located.
# @param [String] default_l10n The source translation code.
# @param [String] file_ex The file extension of the translation files.
#
# @since 2.5.0
def initialize(l10n_path, default_l10n = 'en', file_ex = 'l10n')
@file_ex = file_ex.dup
@l10n = default_l10n.dup
@default_l10n = default_l10n.dup
@dictionary = {}
@metadata = {} # (!) Default data
path = File.expand_path( l10n_path )
unless File.exist?( path )
raise( ArgumentError, 'Path does not exist.' )
end
@path = path
end
# @return [String]
# @since 2.5.0
def inspect
if @metadata
name = @metadata['name']
size = @dictionary.size
"<#{self.class}:#{@l10n} - Speaks #{size} phrases in #{name}>"
else
"<#{self.class}:#{@l10n}>"
end
end
# @return [Hash] A copy of the current translation dictionary.
# @since 2.5.0
def dictionary
@dictionary.dup
end
# @return [Hash] A copy of the current translation dictionary.
# @since 2.5.0
def metadata
@metadata.dup
end
# Loads a translation dictionary.
#
# @param [String] l10n must represent the base name of a .l10n file in the
# language folder.
#
# @return [Boolean] +true+ on success, +false+ on failure.
# @since 2.5.0
def load(l10n)
# Special case for default language.
if l10n == @default_l10n
@l10n = @default_l10n
@metadata.clear # (!) Default data
@dictionary.clear
return true
end
# Work out the filename and ensure it exists.
filename = File.join(@path, "#{l10n}.#{@file_ex}")
unless File.exists?(filename)
puts "Babelfish - Failed to load l10n: #{l10n} (#{filename}). No such file."
return false
end
# Open the language file and parse the content. Unicode Regex ensures that
# UTF-8 files are parsed properly.
#
# Using a proxy hash prevents a failed appempt of loading a translation
# file from ruining the existing translation hash.
#
# Garbage data - data that doesn't match the expected format is ignored.
dictionary = {}
metadata = {}
File.open(filename, 'r') { |file|
metadata = read_metadata(file)
unless metadata
puts "Babelfish - Failed to load l10n: #{l10n} (#{filename}). Invalid data."
return false
end
file.each { |line|
next if line.match(/^\s*#/u) # Ignore comments
next if line.match(/^\s*$/u) # Ignore empty lines
if match = line.match(/^\s*"(.*)"\s*=\s*"(.*)"\s*$/u)
dictionary[ match[1] ] = match[2]
end
}
}
@l10n = l10n.dup
@metadata = metadata
@dictionary = dictionary
true
end
# Looks up the string and returns a translated string.
# If no translated string exists, the original is returned.
#
# Silently outputs any errors to the console and returns the original
# string.
#
# @param [String] string
# @param [Array] args
#
# @return [String]
# @since 2.5.0
def translate(string, *args)
translated = @dictionary[string] || string
sprintf(translated, *args)
rescue => e
p e.message
puts e.backtrace.join("\n")
sprintf(string, *args)
end
alias :tr :translate
# Used to handle singluar vs plural form.
#
# When +expression+ is a boolean, +true+ represent singular.
#
# @param [Numeric, Enumerable, Boolean] expression
# @param [String] singular
# @param [String] multiple
# @param [Array] args
#
# @return [String]
# @since 2.12.0
def plural(expression, singular, multiple, *args)
single = false
case expression
when Numeric
single = expression.to_i == 1
when Enumerable
[:size, :length, :count].each { |symbol|
single = expression.send(symbol) == 1 if expression.respond_to?(symbol)
}
else
single = !!expression
end
string = single ? singular : multiple
translate(string, *args)
end
alias :pl :plural
# Utility accessor to return a string without arguments.
#
# @param [String] string
#
# @return [String]
# @since 2.12.0
def [](string)
translate(string)
end
# Iterates the language folder for .l10n files and extracts the required
# display name in the first line of the file.
#
# Returns a hash of all the languages availible. The key is the translation
# code and the value is a hash with meta data.
#
# The meta data contains one required key: 'name' and optionally 'author'
# and/or 'contact'.
#
# {
# 'no' => {
# 'name' => '...'
# },
# 'fr' => {
# 'name' => '...',
# 'author' => '...',
# 'contact' => '...'
# }
# }
#
# How to get an array of availible language codes:
# codes = babelfish.translations.keys
#
# Silently eats errors and output any errors or warnings to the console.
#
# @return [Hash]
# @since 2.5.0
def translations
lang = {}
file_filter = File.join(@path, "*.#{@file_ex}")
Dir.glob( file_filter ) { |filename|
lang_code = File.basename(filename, ".#{@file_ex}")
File.open(filename, 'r') { |file|
# UTF-8 files might have a BOM mark, account for this.
metadata = read_metadata(file)
if metadata
lang[lang_code] = metadata
else
puts "WARNING: No @title in #{filename}"
end
}
}
lang # (?) This method appears to return false unless this is here...
rescue => e
p e.message
puts e.backtrace.join("\n")
ensure
lang
end
# Try to pick a language based on the Sketchup locale.
#
# 1. Tries exact matches.
# 2. Tries to find by language code, (if SU locale is en-GB) it tries to find
# "en.l10n".
# 3. Tries to find a similar dialect. (if SU locale is en-GB) it will consider
# "en-US.l10n" a match.
#
# (!)
# Norwegian is nn-NO and nb-NO, no
# This appear to be different from how English system works.
#
# http://msdn.microsoft.com/en-us/library/system.globalization.cultureinfo%28VS.80%29.aspx
#
# @return [String]
# @since 2.5.0
def guess_l10n
su_locale = Sketchup.get_locale.downcase
# Extract list of languages availible
file_filter = File.join( @path, "*.#{@file_ex}" )
languages = Dir.glob( file_filter ).map { |filename|
File.basename(filename, ".#{@file_ex}").downcase
}
# First search for exact match
for lang_code in languages
return lang_code if lang_code == su_locale
end
# Search for partial match - language code
su_country = su_locale.split('-').first
for lang_code in languages
return lang_code if lang_code == su_country
end
# Search for partial match - language code and dialect
for lang_code in languages
country = lang_code.split('-').first
return lang_code if country == su_country
end
# Default
return @default_l10n
end
# Debug method that compares the availible translations against a spesified
# prototype. Outputs the result to the Console.
#
# @param [String] prototype_l10n The translation to compare against.
#
# @return [String]
# @since 2.5.0
def check(prototype_l10n)
puts "\nChecking language files for missing strings against #{prototype_l10n}..."
prototype = self.new( @path, @default_l10n )
prototype.load( prototype_l10n )
temp = self.new( @path, @default_l10n )
keys = prototype.dictionary.keys
prototype.translations.each { |code, data|
next if code == prototype_l10n
temp.load(code)
puts "\n=== #{data['name']} (#{code}) ==="
missing = keys - temp.dictionary.keys
puts "> Missing #{missing.length} keys:"
missing.each { |str| p str }
}
self.load(org)
"\nDone\n\n"
end
private
def read_metadata(file)
match = file.readline.match(/^(?:\xEF\xBB\xBF)?@title:\s*"(.+)"/u)
return nil if match.nil?
# Language data is read into a hash
lang_data = {
'author' => 'unknown',
'contact' => ''
}
# The Name is required and MUST be the first line in the file.
lang_data['name'] = match[1]
# The next following lines are optional and can be in any order,
# but with comments or skipped lines.
2.times {
match = file.readline.match(/@(\w+):\s*"(.+)"/u)
if match && ['author','contact'].include?(match[1])
lang_data[ match[1] ] = match[2]
end
}
lang_data
end
end # class TT::Babelfish
end
@@ -0,0 +1,13 @@
#-----------------------------------------------------------------------------
#
# Thomas Thomassen
# thomas[at]thomthom[dot]net
#
#-----------------------------------------------------------------------------
# This file exist only as a compatibility with older version of TT_Lib when
# the implementation was in pure Ruby. It also ensures the correct version for
# the platform is loaded.
require_relative 'core.rb'
require File.join( SpeckleConnector::TT::Lib::PATH_LIBS_CEXT, 'tt_lib2' )
@@ -0,0 +1,38 @@
#-----------------------------------------------------------------------------
#
# Thomas Thomassen
# thomas[at]thomthom[dot]net
#
#-----------------------------------------------------------------------------
require_relative 'core.rb'
# ...
#
# @since 2.4.0
module SpeckleConnector
module TT::Binary
# Base64 encodes binary data.
#
# @param [Mixed] data
#
# @return [String]
# @since 2.4.0
def self.encode64(data)
return [data].pack('m')
end
# Decodes Base64 strings.
#
# @param [String] string
#
# @return [Mixed]
# @since 2.4.0
def self.decode64(string)
return string.unpack('m')[0]
end
end # module TT::Binary
end
@@ -0,0 +1,55 @@
#-----------------------------------------------------------------------------
#
# Thomas Thomassen
# thomas[at]thomthom[dot]net
#
#-----------------------------------------------------------------------------
require_relative 'core.rb'
# @example
# class Foo
# extend TT::BooleanAttributes
# battr_accessor :bar
# end
#
# @since 2.7.0
module SpeckleConnector
module TT::BooleanAttributes
# @since 2.7.0
def battr( symbol, writable = false )
self.class_eval {
attr( symbol, writable )
question = "#{symbol}?".to_sym
alias_method( question, symbol )
remove_method( symbol )
}
end
# @since 2.7.0
def battr_accessor( *args )
self.class_eval {
attr_accessor( *args )
for attribute in args
question = "#{attribute}?".to_sym
alias_method( question, attribute )
remove_method( attribute )
end
}
end
# @since 2.7.0
def battr_reader( *args )
self.class_eval {
attr_reader( *args )
for attribute in args
question = "#{attribute}?".to_sym
alias_method( question, attribute )
remove_method( attribute )
end
}
end
end # class TT::BooleanAttributes
end
+112
View File
@@ -0,0 +1,112 @@
#-----------------------------------------------------------------------------
#
# Thomas Thomassen
# thomas[at]thomthom[dot]net
#
#-----------------------------------------------------------------------------
require_relative 'core.rb'
# Collection of BoundingBox methods.
#
# @since 2.2.0
module SpeckleConnector
module TT::Bounds
# Returns a +Point3d+ from a standard position of the boundingbox.
#
# @param [Geom::BoundingBox] bounds
# @param [Integer] index
#
# @return [Geom::Point3d]
# @since 2.2.0
def self.point(bounds, index)
case index
when 0..7
pt = bounds.corner(index)
when TT::BB_CENTER_FRONT_BOTTOM
p1 = bounds.corner( TT::BB_LEFT_FRONT_BOTTOM )
p2 = bounds.corner( TT::BB_RIGHT_FRONT_BOTTOM )
pt = Geom.linear_combination( 0.5, p1, 0.5, p2 )
when TT::BB_CENTER_BACK_BOTTOM
p1 = bounds.corner( TT::BB_LEFT_BACK_BOTTOM )
p2 = bounds.corner( TT::BB_RIGHT_BACK_BOTTOM )
pt = Geom.linear_combination( 0.5, p1, 0.5, p2 )
when TT::BB_CENTER_FRONT_TOP
p1 = bounds.corner( TT::BB_LEFT_FRONT_TOP )
p2 = bounds.corner( TT::BB_RIGHT_FRONT_TOP )
pt = Geom.linear_combination( 0.5, p1, 0.5, p2 )
when TT::BB_CENTER_BACK_TOP
p1 = bounds.corner( TT::BB_LEFT_BACK_TOP )
p2 = bounds.corner( TT::BB_RIGHT_BACK_TOP )
pt = Geom.linear_combination( 0.5, p1, 0.5, p2 )
when TT::BB_LEFT_CENTER_BOTTOM
p1 = bounds.corner( TT::BB_LEFT_FRONT_BOTTOM )
p2 = bounds.corner( TT::BB_LEFT_BACK_BOTTOM )
pt = Geom.linear_combination( 0.5, p1, 0.5, p2 )
when TT::BB_LEFT_CENTER_TOP
p1 = bounds.corner( TT::BB_LEFT_FRONT_TOP )
p2 = bounds.corner( TT::BB_LEFT_BACK_TOP )
pt = Geom.linear_combination( 0.5, p1, 0.5, p2 )
when TT::BB_RIGHT_CENTER_BOTTOM
p1 = bounds.corner( TT::BB_RIGHT_FRONT_BOTTOM )
p2 = bounds.corner( TT::BB_RIGHT_BACK_BOTTOM )
pt = Geom.linear_combination( 0.5, p1, 0.5, p2 )
when TT::BB_RIGHT_CENTER_TOP
p1 = bounds.corner( TT::BB_RIGHT_FRONT_TOP )
p2 = bounds.corner( TT::BB_RIGHT_BACK_TOP )
pt = Geom.linear_combination( 0.5, p1, 0.5, p2 )
when TT::BB_LEFT_FRONT_CENTER
p1 = bounds.corner( TT::BB_LEFT_FRONT_BOTTOM )
p2 = bounds.corner( TT::BB_LEFT_FRONT_TOP )
pt = Geom.linear_combination( 0.5, p1, 0.5, p2 )
when TT::BB_RIGHT_FRONT_CENTER
p1 = bounds.corner( TT::BB_RIGHT_FRONT_BOTTOM )
p2 = bounds.corner( TT::BB_RIGHT_FRONT_TOP )
pt = Geom.linear_combination( 0.5, p1, 0.5, p2 )
when TT::BB_LEFT_BACK_CENTER
p1 = bounds.corner( TT::BB_LEFT_BACK_BOTTOM )
p2 = bounds.corner( TT::BB_LEFT_BACK_TOP )
pt = Geom.linear_combination( 0.5, p1, 0.5, p2 )
when TT::BB_RIGHT_BACK_CENTER
p1 = bounds.corner( TT::BB_RIGHT_BACK_BOTTOM )
p2 = bounds.corner( TT::BB_RIGHT_BACK_TOP )
pt = Geom.linear_combination( 0.5, p1, 0.5, p2 )
when TT::BB_LEFT_CENTER_CENTER
p1 = bounds.corner( TT::BB_LEFT_FRONT_BOTTOM )
p2 = bounds.corner( TT::BB_LEFT_BACK_TOP )
pt = Geom.linear_combination( 0.5, p1, 0.5, p2 )
when TT::BB_RIGHT_CENTER_CENTER
p1 = bounds.corner( TT::BB_RIGHT_FRONT_BOTTOM )
p2 = bounds.corner( TT::BB_RIGHT_BACK_TOP )
pt = Geom.linear_combination( 0.5, p1, 0.5, p2 )
when TT::BB_CENTER_FRONT_CENTER
p1 = bounds.corner( TT::BB_LEFT_FRONT_BOTTOM )
p2 = bounds.corner( TT::BB_RIGHT_FRONT_TOP )
pt = Geom.linear_combination( 0.5, p1, 0.5, p2 )
when TT::BB_CENTER_BACK_CENTER
p1 = bounds.corner( TT::BB_LEFT_BACK_BOTTOM )
p2 = bounds.corner( TT::BB_RIGHT_BACK_TOP )
pt = Geom.linear_combination( 0.5, p1, 0.5, p2 )
when TT::BB_CENTER_CENTER_TOP
p1 = bounds.corner( TT::BB_LEFT_FRONT_TOP )
p2 = bounds.corner( TT::BB_RIGHT_BACK_TOP )
pt = Geom.linear_combination( 0.5, p1, 0.5, p2 )
when TT::BB_CENTER_CENTER_BOTTOM
p1 = bounds.corner( TT::BB_LEFT_FRONT_BOTTOM )
p2 = bounds.corner( TT::BB_RIGHT_BACK_BOTTOM )
pt = Geom.linear_combination( 0.5, p1, 0.5, p2 )
when TT::BB_CENTER_CENTER_CENTER
pt = bounds.center
end
pt
end
end # module TT::Bounds
end

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