Compare commits

..

4 Commits

Author SHA1 Message Date
oguzhankoral 18caf16621 WIP 2024-02-12 18:09:31 +03:00
oguzhankoral c781534950 Trigger sketchup only saved stream not exists 2024-02-12 11:52:18 +03:00
oguzhankoral 886ede61e1 Enrich selected account info and fix same user id issue 2024-02-12 11:31:02 +03:00
oguzhankoral 5a42ea39ce Disable reload 2024-02-12 11:30:39 +03:00
262 changed files with 421 additions and 18527 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 KiB

@@ -7,7 +7,6 @@ require_relative '../constants/path_constants'
module SpeckleConnector
# Accounts to communicate with models on user's account.
module Accounts
# Load accounts from user's app data.
def self.load_accounts
db_path = SPECKLE_ACCOUNTS_DB_PATH
unless File.exist?(db_path)
@@ -24,21 +23,9 @@ module SpeckleConnector
rows.map { |row| JSON.parse(row[1]) }
end
def self.get_account_by_id(id)
accounts = load_accounts
accounts.select { |acc| acc['id'] == id }[0]
end
# Default account on the user computer.
def self.default_account
accounts = load_accounts
accounts.select { |acc| acc['isDefault'] }[0] || accounts[0]
end
# Try to get local server account for debug/test purposes.
def self.try_get_local_server_account
accounts = load_accounts
accounts.select { |acc| acc['serverInfo']['url'].include?('localhost') }[0] || nil
end
end
end
@@ -15,7 +15,7 @@ module SpeckleConnector
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def update_state(state)
state = DeactivateDiffing.update_state(state, nil, {})
state = DeactivateDiffing.update_state(state, {})
puts "Diffing activated for #{@stream_id}"
speckle_entities = state.speckle_state.speckle_entities
invalid_speckle_entities = speckle_entities.select do |_id, entity|
@@ -1,47 +0,0 @@
# frozen_string_literal: true
require_relative '../action'
require_relative '../../cards/send_card'
require_relative '../../cards/receive_card'
require_relative '../../filters/send/everything_filter'
require_relative '../../filters/send/selection_filter'
require_relative '../../filters/send_filters'
require_relative '../../sketchup_model/dictionary/model_card_dictionary_handler'
module SpeckleConnector
module Actions
# Action to add send 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
@@ -1,30 +0,0 @@
# 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
@@ -1,40 +0,0 @@
# 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
@@ -1,38 +0,0 @@
# 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
@@ -1,19 +0,0 @@
# frozen_string_literal: true
require_relative '../action'
require_relative '../../filters/send_filters'
module SpeckleConnector
module Actions
# Action to get send filter.
class GetSendFilters < Action
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def self.update_state(state, resolve_id)
default_filters = Filters::SendFilters.get_default(state.sketchup_state.sketchup_model)
js_script = "sendBinding.receiveResponse('#{resolve_id}', #{default_filters.to_json})"
state.with_add_queue_js_command('getSendFilter', js_script)
end
end
end
end
@@ -1,17 +0,0 @@
# frozen_string_literal: true
require_relative '../action'
module SpeckleConnector
module Actions
# Get source app name.
class GetSourceAppName < Action
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def self.update_state(state, resolve_id)
js_command = "baseBinding.receiveResponse('#{resolve_id}', 'Sketchup')"
state.with_add_queue_js_command('getSourceAppName', js_command)
end
end
end
end
@@ -1,17 +0,0 @@
# frozen_string_literal: true
require_relative '../action'
module SpeckleConnector
module Actions
# Get source app version.
class GetSourceAppVersion < Action
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def self.update_state(state, resolve_id)
js_command = "baseBinding.receiveResponse('#{resolve_id}', #{SKETCHUP_VERSION})"
state.with_add_queue_js_command('getSourceAppVersion', js_command)
end
end
end
end
@@ -1,45 +0,0 @@
# frozen_string_literal: true
require_relative '../action'
require_relative '../../sketchup_model/query/entity'
module SpeckleConnector
module Actions
# Action to add send card.
class HighlightModel < Action
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def self.update_state(state, resolve_id, model_card_id)
# 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
@@ -1,31 +0,0 @@
# frozen_string_literal: true
require_relative '../action'
require_relative '../../cards/send_card'
require_relative '../../cards/receive_card'
require_relative '../../filters/send/everything_filter'
require_relative '../../filters/send/selection_filter'
require_relative '../../filters/send_filters'
require_relative '../../sketchup_model/dictionary/model_card_dictionary_handler'
module SpeckleConnector
module Actions
# Action to remove send card.
class RemoveModel < Action
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def self.update_state(state, resolve_id, data)
SketchupModel::Dictionary::ModelCardDictionaryHandler.remove_card_dict(state.sketchup_state.sketchup_model, data)
new_speckle_state = if data['typeDiscriminator'] == 'ReceiverModelCard'
state.speckle_state.without_receive_card(data['id'])
else
state.speckle_state.without_send_card(data['id'])
end
state = state.with_speckle_state(new_speckle_state)
# Resolve promise
js_script = "baseBinding.receiveResponse('#{resolve_id}')"
state.with_add_queue_js_command('removeModel', js_script)
end
end
end
end
@@ -1,20 +0,0 @@
# frozen_string_literal: true
require_relative '../action'
require_relative '../../sketchup_model/dictionary/model_card_dictionary_handler'
module SpeckleConnector
module Actions
# Action to update send filter.
class UpdateSendFilter < Action
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def self.update_state(state, resolve_id, data, value)
SketchupModel::Dictionary::ModelCardDictionaryHandler.update_filter(state.sketchup_state.sketchup_model, data, value)
js_script = "sendBinding.receiveResponse('#{resolve_id}')"
state.with_add_queue_js_command('updateSendFilter', js_script)
end
end
end
end
@@ -9,7 +9,7 @@ module SpeckleConnector
class ClearMapperSource < Action
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def self.update_state(state, _resolve_id, _data)
def self.update_state(state, _data)
new_speckle_state = state.speckle_state.with_removed_mapper_source
erase_levels(state)
state.with_speckle_state(new_speckle_state)
@@ -11,7 +11,7 @@ module SpeckleConnector
class ClearMappingsFromTable < Action
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def self.update_state(state, _resolve_id, data)
def self.update_state(state, data)
# Flat entities to clear mappings
flat_entities = SketchupModel::Query::Entity.flat_entities(state.sketchup_state.sketchup_model.entities)
@@ -10,7 +10,7 @@ module SpeckleConnector
class CollectPreferences < Action
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def self.update_state(state, _resolve_id, _data)
def self.update_state(state, _data)
state.with_add_queue('collectPreferences', state.user_state.preferences.to_json, [])
end
end
@@ -8,7 +8,7 @@ module SpeckleConnector
class CollectVersions < Action
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def self.update_state(state, _resolve_id, _data)
def self.update_state(state, _data)
versions = {
sketchup: Sketchup.version.to_i,
speckle: SpeckleConnector::CONNECTOR_VERSION
@@ -1,22 +0,0 @@
# frozen_string_literal: true
require_relative '../action'
module SpeckleConnector
module Actions
# Action to get config.
class GetConfig < Action
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def self.update_state(state, resolve_id)
# Previously it was stored in user state
# config = state.user_state.preferences.to_json
config = {
darkTheme: state.user_state.user_preferences[:dark_theme]
}
js_script = "configBinding.receiveResponse('#{resolve_id}', #{config.to_json})"
state.with_add_queue_js_command('getConfig', js_script)
end
end
end
end
@@ -1,26 +0,0 @@
# frozen_string_literal: true
require_relative '../action'
require_relative '../user_preferences_updated'
module SpeckleConnector
module Actions
# Action to update config.
class UpdateConfig < Action
KEY_VALUES = {
'darkTheme' => 'dark_theme'
}.freeze
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def self.update_state(state, resolve_id, config)
config.each do |key, value|
state = Actions::UserPreferencesUpdated.new('configSketchup', KEY_VALUES[key], value).update_state(state)
end
js_script = "configBinding.receiveResponse('#{resolve_id}')"
state.with_add_queue_js_command('updateConfig', js_script)
end
end
end
end
@@ -8,7 +8,7 @@ module SpeckleConnector
class DeactivateDiffing < Action
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def self.update_state(state, _resolve_id, _data)
def self.update_state(state, _data)
puts 'Diffing deactivated!'
speckle_entities = state.speckle_state.speckle_entities
diffing_activated_speckle_entities = speckle_entities.reject do |_id, entity|
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require_relative 'event_action'
require_relative 'on_document_changed'
require_relative '../load_sketchup_model'
require_relative '../collect_preferences'
@@ -26,8 +25,7 @@ module SpeckleConnector
# Action to let UI to render itself with new preferences state
# TODO: Later UI should be updated if any stream is invalid after
# we collected speckle_entities appropriately
new_state = CollectPreferences.update_state(new_state, nil, {})
OnDocumentChanged.update_state(new_state)
CollectPreferences.update_state(new_state, {})
end
end
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require_relative 'event_action'
require_relative '../../actions/send_actions/send_card_expiration_check'
require_relative '../../sketchup_model/utils/face_utils'
require_relative '../../constants/dict_constants'
@@ -27,23 +26,19 @@ module SpeckleConnector
def self.update_state(state, event_data)
speckle_state = state.speckle_state
modified_entity = event_data[0][1]
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
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
@@ -1,15 +0,0 @@
# frozen_string_literal: true
module SpeckleConnector
module Actions
# Triggers whenever document has changed.
class OnDocumentChanged < Action
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def self.update_state(state)
js_command = "baseBinding.emit('documentChanged')"
state.with_add_queue_js_command('documentChanged', js_command)
end
end
end
end
@@ -2,7 +2,6 @@
require_relative 'event_action'
require_relative '../mapper_selection_changed'
require_relative '../selection_actions/get_selection'
require_relative '../../mapper/category/revit_category'
require_relative '../../sketchup_model/reader/speckle_entities_reader'
require_relative '../../sketchup_model/reader/mapper_reader'
@@ -21,11 +20,9 @@ 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
@@ -1,21 +0,0 @@
# frozen_string_literal: true
require_relative 'action'
require_relative '../accounts/accounts'
require_relative 'load_saved_streams'
module SpeckleConnector
module Actions
# Action to initialize local accounts from database.
class GetAccounts < Action
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def self.update_state(state, resolve_id)
puts 'Initialisation of Speckle accounts requested by plugin'
accounts_data = state.speckle_state.accounts
js_script = "accountsBinding.receiveResponse('#{resolve_id}', #{accounts_data.to_json})"
state.with_add_queue_js_command('getAccounts', js_script)
end
end
end
end
@@ -1,22 +0,0 @@
# frozen_string_literal: true
require_relative 'action'
module SpeckleConnector
module Actions
# Get document info.
class GetDocumentInfo < Action
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def self.update_state(state, resolve_id)
document_info = {
location: state.sketchup_state.sketchup_model.path,
name: state.sketchup_state.sketchup_model.name,
id: state.sketchup_state.sketchup_model.guid
}
js_command = "baseBinding.receiveResponse('#{resolve_id}', #{document_info.to_json})"
state.with_add_queue_js_command('getDocumentInfo', js_command)
end
end
end
end
@@ -1,31 +0,0 @@
# frozen_string_literal: true
module SpeckleConnector
module Actions
# Action to return error message to UI.
class HandleError < Action
# @param error [String] error
# @param view_name [String] name of the view (binding)
# @param action [Action] action that error happened
# @param parameters [Array<String>] arguments
def initialize(error, view_name, action, parameters)
super()
@error = error
@view_name = view_name
@action = action
@args = parameters
end
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def update_state(state)
error_message = "Error: #{@error}\nBinding: #{@view_name}\nAction:#{@action}\nArgs: #{@args}\n"
error = {
error: error_message
}
js_error_script = "#{@view_name}.receiveResponse('#{@args.first}', #{error.to_json})"
state.with_add_queue_js_command("error_#{@view_name}", js_error_script)
end
end
end
end
@@ -10,7 +10,7 @@ module SpeckleConnector
class HideMappingsFromTable < Action
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def self.update_state(state, _resolve_id, data)
def self.update_state(state, data)
# Flat entities to clear mappings
flat_entities = SketchupModel::Query::Entity.flat_entities(state.sketchup_state.sketchup_model.entities)
@@ -10,7 +10,7 @@ module SpeckleConnector
class InitLocalAccounts < Action
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def self.update_state(state, _request_id, _data)
def self.update_state(state, _data)
puts 'Initialisation of Speckle accounts requested by plugin'
accounts_data = state.speckle_state.accounts
state.with_add_queue('loadAccounts', accounts_data.to_json, [])
@@ -7,7 +7,6 @@ require_relative '../states/sketchup_state'
require_relative '../accounts/accounts'
require_relative '../preferences/preferences'
require_relative '../constants/observer_constants'
require_relative '../ext/worker'
module SpeckleConnector
module Actions
@@ -15,8 +14,7 @@ module SpeckleConnector
class InitializeSpeckle < Action
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def self.update_state(state, observers, instant_message_sender)
worker = SpeckleConnector::Worker.new()
def self.update_state(state, observers)
attach_app_observer!(observers[APP_OBSERVER])
accounts = SpeckleConnector::Accounts.load_accounts
speckle_state = States::SpeckleState.new(accounts, observers, {}, {})
@@ -24,8 +22,7 @@ module SpeckleConnector
sketchup_state = States::SketchupState.new(Sketchup.active_model)
preferences = Preferences.read_preferences(sketchup_state.sketchup_model)
user_state_with_preferences = state.user_state.with_preferences(preferences)
state = States::State.new(user_state_with_preferences, speckle_state, sketchup_state, false,
worker, &instant_message_sender)
state = States::State.new(user_state_with_preferences, speckle_state, sketchup_state, false)
# This is where we attach observers to related model objects like selection, entities..
Actions::LoadSketchupModel.update_state(state, sketchup_state.sketchup_model)
end
@@ -14,7 +14,7 @@ module SpeckleConnector
# rubocop:disable Metrics/MethodLength
# rubocop:disable Metrics/PerceivedComplexity
# rubocop:disable Metrics/CyclomaticComplexity
def self.update_state(state, _resolve_id, data)
def self.update_state(state, data)
sketchup_model = state.sketchup_state.sketchup_model
# Hide all entities first
@@ -8,7 +8,7 @@ module SpeckleConnector
class LoadSavedStreams < Action
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def self.update_state(state, _request_id, _data)
def self.update_state(state, _data)
(saved_streams = state.sketchup_state.sketchup_model
.attribute_dictionary('Speckle', true)['saved_streams']) or []
state.with_add_queue('setSavedStreams', saved_streams, [])
@@ -9,7 +9,7 @@ module SpeckleConnector
class MappedEntitiesUpdated < Action
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def self.update_state(state, _resolve_id = nil, _data = nil)
def self.update_state(state, _data = nil)
mapped_entities = SketchupModel::Reader::MapperReader
.mapped_entity_details(state.speckle_state.speckle_mapper_state.mapped_entities.values.to_a)
@@ -1,18 +0,0 @@
# frozen_string_literal: true
require_relative '../action'
module SpeckleConnector
module Actions
# Action to let sketchup know receive from server is finished..
class AfterReceive < Action
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def self.update_state(state, resolve_id, stream_id, root_id)
puts "receive finished for: #{root_id}"
js_script = "sketchupReceiveBinding.receiveResponse('#{resolve_id}')"
state.with_add_queue_js_command('afterReceive', js_script)
end
end
end
end
@@ -1,18 +0,0 @@
# frozen_string_literal: true
require_relative '../action'
module SpeckleConnector
module Actions
# Action to let sketchup know receive will be started.
class BeforeReceive < Action
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def self.update_state(state, resolve_id, stream_id, root_id)
puts "receive started for: #{root_id}"
js_script = "sketchupReceiveBinding.receiveResponse('#{resolve_id}')"
state.with_add_queue_js_command('beforeReceive', js_script)
end
end
end
end
@@ -1,91 +0,0 @@
# frozen_string_literal: true
require 'json'
require_relative '../action'
require_relative '../../convertors/units'
require_relative '../../convertors/to_native'
module SpeckleConnector
module Actions
# Clear mappings for selected entities.
class ReceiveSingleObject < Action
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def self.update_state(state, resolve_id, stream_id, root_id, speckle_objects)
puts "object receive #{speckle_objects.length}"
buffer = speckle_objects.collect { |obj| [obj['id'], obj] }.to_h
t_0 = Time.now.to_f
root_obj = traverse_and_construct(speckle_objects.first, buffer)
puts root_obj
puts "Elapsed traverse and construct #{Time.now.to_f - t_0}"
# File.open("#{ENV['HOME']}/OneDrive/Masaüstü/root.json", 'w') do |f|
# f.write(JSON.pretty_generate(root_obj))
# end
# converter = Converters::ToNative.new(state, stream_id, 'test', 'testt', 'test')
# state = converter.receive_commit_object(root_obj)
js_script = "sketchupReceiveBinding.receiveResponse('#{resolve_id}')"
state.with_add_queue_js_command('receiveObject', js_script)
end
def self.traverse_and_construct(obj, buffer)
return if obj.nil?
return obj if !obj.is_a?(Hash) && !obj.is_a?(Array)
# Handle arrays
if obj.is_a?(Array) && !obj.empty?
arr = handle_array(buffer, obj)
# De-chunk, if array is a set of datachunk, flat them into single data chunk.
arr = try_dechunk(arr)
return arr
end
# Handle object
obj = handle_hash(buffer, obj)
return obj
rescue StandardError => e
puts "#{e} -> #{obj}"
return nil
end
def self.handle_array(buffer, obj)
arr = []
obj.collect do |element|
next if element.nil?
deref = element.is_a?(Hash) && !element['referencedId'].nil? ? buffer[element['referencedId']] : element
arr.append(traverse_and_construct(deref, buffer))
end
arr
end
def self.try_dechunk(arr)
if arr[0].is_a?(Hash) && !arr[0]['speckle_type'].nil? && arr[0]['speckle_type'].downcase.include?('datachunk')
sum_arr = []
arr.each do |chunk|
sum_arr += chunk['data']
end
sum_arr
else
arr
end
end
def self.handle_hash(buffer, obj)
obj.each do |prop, value|
next if value.nil? || (!value.is_a?(Hash) && !value.is_a?(Array))
obj[prop] = buffer[value['referencedId']] if value.is_a?(Hash) && value['referencedId']
obj[prop] = traverse_and_construct(obj[prop], buffer)
end
obj
end
end
end
end
@@ -1,26 +0,0 @@
# 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
@@ -1,37 +0,0 @@
# frozen_string_literal: true
require_relative '../action'
require_relative '../../accounts/accounts'
require_relative '../../convertors/units'
require_relative '../../convertors/to_speckle'
require_relative '../../operations/send'
require_relative '../../ext/TT_Lib2/progressbar'
module SpeckleConnector
module Actions
# Receive from server.
class Receive < Action
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def self.update_state(state, resolve_id, model_card_id, 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,7 +3,6 @@
require_relative 'action'
require_relative '../convertors/units'
require_relative '../convertors/to_native'
require_relative '../operations/receive'
require_relative '../convertors/clean_up'
module SpeckleConnector
@@ -11,7 +10,7 @@ module SpeckleConnector
# Action to receive objects from Speckle Server.
class ReceiveObjects < Action
# rubocop:disable Metrics/ParameterLists
def initialize(stream_id, base, stream_name, branch_name, branch_id, source_app, object_id)
def initialize(stream_id, base, stream_name, branch_name, branch_id, source_app)
super()
@stream_id = stream_id
@base = base
@@ -19,15 +18,12 @@ module SpeckleConnector
@branch_name = branch_name
@branch_id = branch_id
@source_app = source_app
@object_id = object_id
end
# rubocop:enable Metrics/ParameterLists
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def update_state(state)
# content = Operations.receive(@stream_id, @object_id)
converter = Converters::ToNative.new(state, @stream_id, @stream_name, @branch_name, @source_app)
# Have side effects on the sketchup model. It effects directly on the entities by adding new objects.
start_time = Time.now.to_f
@@ -10,7 +10,7 @@ module SpeckleConnector
class ReloadAccounts < Action
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def self.update_state(state, _resolve_id, _data)
def self.update_state(state, _data)
puts 'Reload of Speckle accounts requested by plugin'
new_speckle_state = state.speckle_state.with_accounts(Accounts.load_accounts)
state = state.with_speckle_state(new_speckle_state)
@@ -10,11 +10,11 @@ module SpeckleConnector
class SelectMappingsFromTable < Action
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def self.update_state(state, _resolve_id, data)
def self.update_state(state, data)
# Clear first selection
state.sketchup_state.sketchup_model.selection.clear
# Flat entities to select mapped elements
# Flat entities to clear mappings
flat_entities = SketchupModel::Query::Entity.flat_entities(state.sketchup_state.sketchup_model.entities)
# Collect entity ids to clear mappings
@@ -1,22 +0,0 @@
# frozen_string_literal: true
require_relative '../action'
require_relative '../../ui_data/sketchup/selection_info'
module SpeckleConnector
module Actions
# Action to get selection.
class GetSelection < Action
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def self.update_state(state)
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
@@ -1,22 +0,0 @@
# frozen_string_literal: true
require_relative '../action'
require_relative '../../sketchup_model/dictionary/model_card_dictionary_handler'
module SpeckleConnector
module Actions
# Action to activate send filter.
class ActivateSendFilter < Action
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def self.update_state(state, resolve_id, data, value)
SketchupModel::Dictionary::ModelCardDictionaryHandler.update_filter(state.sketchup_state.sketchup_model, data, value)
card_id = "#{data['accountId']}-#{data['projectId']}-#{data['modelId']}"
send_card = state.speckle_state.send_cards[card_id]
puts "Send card filter updated -> #{card_id} -> #{send_card}"
js_script = "sendBindingOld.receiveResponse('#{resolve_id}')"
state.with_add_queue_js_command('activateSendFilter', js_script)
end
end
end
end
@@ -1,22 +0,0 @@
# frozen_string_literal: true
require_relative '../action'
require_relative '../../sketchup_model/dictionary/model_card_dictionary_handler'
module SpeckleConnector
module Actions
# Action to activate send filter tag.
class ActivateSendFilterTag < Action
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def self.update_state(state, resolve_id, data, value)
SketchupModel::Dictionary::ModelCardDictionaryHandler.update_tag_filter(state.sketchup_state.sketchup_model, data, value)
card_id = "#{data['accountId']}-#{data['projectId']}-#{data['modelId']}"
send_card = state.speckle_state.send_cards[card_id]
puts "Send card filter updated -> #{card_id} -> #{send_card}"
js_script = "sendBindingOld.receiveResponse('#{resolve_id}')"
state.with_add_queue_js_command('activateSendFilterTag', js_script)
end
end
end
end
@@ -1,86 +0,0 @@
# frozen_string_literal: true
require_relative '../action'
require_relative '../../accounts/accounts'
require_relative '../../convertors/units'
require_relative '../../convertors/to_speckle'
require_relative '../../operations/send'
require_relative '../../ext/TT_Lib2/progressbar'
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
@@ -1,23 +0,0 @@
# frozen_string_literal: true
require_relative '../action'
require_relative '../../sketchup_model/dictionary/model_card_dictionary_handler'
module SpeckleConnector
module Actions
# Action to check send card expirations.
class SendCardExpirationCheck < Action
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def self.update_state(state)
return state unless state.speckle_state.changed_entity_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,7 +4,6 @@ require_relative 'action'
require_relative 'deactivate_diffing'
require_relative '../convertors/units'
require_relative '../convertors/to_speckle'
require_relative '../operations/send'
module SpeckleConnector
module Actions
@@ -18,14 +17,11 @@ module SpeckleConnector
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def update_state(state)
state = DeactivateDiffing.update_state(state, nil, {})
converter = Converters::ToSpeckle.new(state, @stream_id, {})
state = DeactivateDiffing.update_state(state, {})
converter = Converters::ToSpeckle.new(state, @stream_id)
new_speckle_state, base = converter.convert_selection_to_base(state.user_state.preferences)
id, total_children_count, batches, 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, _resolve_id, _data)
def self.update_state(state, _data)
# Show all entities first
state.sketchup_state.sketchup_model.entities.each do |ent|
ent.hidden = false
@@ -1,46 +0,0 @@
# frozen_string_literal: true
require_relative '../action'
module SpeckleConnector
module Actions
# Action to get user config.
class GetUserConfig < Action
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def self.update_state(state, resolve_id)
# Previously it was stored in user state
# config = state.user_state.preferences.to_json
config = [
{
key: 'darkTheme',
title: 'Theme',
type: 'toggle',
config: {
value: state.user_state.user_preferences[:dark_theme]
}
},
{
key: 'diffing',
title: 'Diffing',
type: 'toggle',
config: {
value: state.user_state.user_preferences[:diffing]
}
},
{
key: 'referencePoint',
title: 'Reference Point',
type: 'dropdown',
config: {
value: 'test',
items: ['test', 'test1', 'test2']
}
}
]
js_script = "connectorConfigBinding.receiveResponse('#{resolve_id}', #{config.to_json})"
state.with_add_queue_js_command('getUserConfig', js_script)
end
end
end
end
@@ -1,18 +0,0 @@
# frozen_string_literal: true
require_relative '../action'
module SpeckleConnector
module Actions
# Action to get user config.
class UpdateUserConfig < Action
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def self.update_state(state, resolve_id, new_config)
puts new_config.values
js_script = "connectorConfigBinding.receiveResponse('#{resolve_id}')"
state.with_add_queue_js_command('updateUserConfig', js_script)
end
end
end
end
@@ -1,21 +0,0 @@
# frozen_string_literal: true
require_relative '../action'
module SpeckleConnector
module Actions
# Test purpose action.
class GetComplexType < Action
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def self.update_state(state, resolve_id)
complex_type = {
id: 'complex_type_id',
count: 3
}
js_script = "testBinding.receiveResponse('#{resolve_id}', #{complex_type.to_json})"
state.with_add_queue_js_command('getComplexType', js_script)
end
end
end
end
@@ -1,18 +0,0 @@
# frozen_string_literal: true
require_relative '../action'
module SpeckleConnector
module Actions
# Test purpose action.
class GoAway < Action
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def self.update_state(state, resolve_id)
puts 'SketchUp went away :('
js_script = "testBinding.receiveResponse('#{resolve_id}')"
state.with_add_queue_js_command('goAway', js_script)
end
end
end
end
@@ -1,21 +0,0 @@
# frozen_string_literal: true
require_relative '../action'
module SpeckleConnector
module Actions
# Test purpose action.
class SayHi < Action
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def self.update_state(state, resolve_id, name, count, say_hello_not_hi)
said_hi = []
count.times do
said_hi.append("#{say_hello_not_hi ? 'Hello' : 'Hi'} #{name}!")
end
js_script = "testBinding.receiveResponse('#{resolve_id}', #{said_hi})"
state.with_add_queue_js_command('sayHi', js_script)
end
end
end
end
@@ -1,28 +0,0 @@
# frozen_string_literal: true
require_relative '../action'
module SpeckleConnector
module Actions
# Test purpose action.
class TriggerEvent < Action
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def self.update_state(state, resolve_id, event_name)
if event_name == 'emptyTestEvent'
js_script = "testBinding.emit('#{event_name}')"
else
args = {
name: 'Oguzhan',
isOk: true,
count: 3
}
js_script = "testBinding.emit('#{event_name}', #{args.to_json})"
end
resolve_js_script = "testBinding.receiveResponse('#{resolve_id}')"
state = state.with_add_queue_js_command('triggerEventResolve', resolve_js_script)
state.with_add_queue_js_command('triggerEvent', js_script)
end
end
end
end
@@ -28,6 +28,10 @@ module SpeckleConnector
state.speckle_state?
end
def update_ui!
ui_controller.update_ui(state)
end
# Attach observers to application when speckle initialized via menu commands.
def add_observer_handler!(observer_handler)
@observer_handler = observer_handler
@@ -36,24 +40,17 @@ module SpeckleConnector
# Send messages to HtmlDialog if any.
def send_messages!
queue = @state.speckle_state.message_queue
queue.each_value do |value|
instant_message_sender(value)
end
queue.each_value { |value| ui_controller.user_interfaces[Ui::SPECKLE_UI_ID].dialog.execute_script(value) }
update_state!(Actions::ClearQueue)
end
def instant_message_sender(message)
ui_controller.user_interfaces.each_value do |dialog|
dialog.execute_script(message)
end
end
# This is the only function application state will be switched by calling upcoming action with it's parameters
# if any.
def update_state!(action, *parameters)
old_state = @state
@state = action.update_state(old_state, *parameters)
send_messages! if @state.speckle_state.message_queue.any?
update_ui! unless @state.equal?(old_state)
end
end
end
-39
View File
@@ -1,39 +0,0 @@
# frozen_string_literal: true
require_relative '../speckle_objects/other/color'
module SpeckleConnector
module Cards
# Card for sketchup connector to communicate speckle.
class Card < Hash
# @return [String] id of the card.
attr_reader :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
@@ -1,36 +0,0 @@
# frozen_string_literal: true
require_relative 'card'
module SpeckleConnector
module Cards
# Receive card for sketchup connector to communicate speckle.
class ReceiveCard < Card
attr_reader :type_discriminator
# @return [String, NilClass] message to send
attr_reader :message
# @return [String] 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
@@ -1,31 +0,0 @@
# frozen_string_literal: true
require_relative 'card'
module SpeckleConnector
module Cards
# Send card for sketchup connector to communicate speckle.
class SendCard < Card
# @return [Filters::Send::EverythingFilter | Filters::Send::SelectionFilter | Filters::Send::LayerFilter] filter of the card.
attr_reader :send_filter
# @return [Object] send settings of the card.
attr_reader :send_settings
attr_reader :type_discriminator
# @return [String, NilClass] message to send
attr_reader :message
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
@@ -1,19 +0,0 @@
# frozen_string_literal: true
require_relative 'card'
module SpeckleConnector
module Cards
# Send card for sketchup connector to communicate speckle.
class SendCardMultipleFilters < Card
# @return [Hash{String=>Filter}] filters of the card.
attr_reader :filters
def initialize(card_id, account_id, project_id, model_id, filters)
super(card_id, account_id, project_id, model_id)
@filters = filters
self[:filters] = filters
end
end
end
end
@@ -7,10 +7,10 @@ module SpeckleConnector
# Command to update state of the application.
class ActionCommand < Command
# @param app [App::SpeckleConnectorApp] the app object to run command on
# @param binding [Ui::Binding] binding object holds commands to call
# @param action [#update_state] the action that knows how to change the state of the speckle app
def initialize(app, binding, action)
super(app, binding)
def initialize(app, action)
super(app)
@app = app
@action = action
end
@@ -7,7 +7,7 @@ module SpeckleConnector
module Commands
# Command to activate diffing for stream.
class ActivateDiffing < Command
def _run(_resolve_id, data)
def _run(data)
stream_id = data['stream_id']
action = Actions::ActivateDiffing.new(stream_id)
app.update_state!(action)
@@ -7,7 +7,7 @@ module SpeckleConnector
module Commands
# Command to apply mapping for selected entities.
class ApplyMappings < Command
def _run(_resolve_id, data)
def _run(data)
entities_to_map = data['entitiesToMap']
method = data['method']
category = data['category']
@@ -7,7 +7,7 @@ module SpeckleConnector
module Commands
# Command to clear mapping for selected entities.
class ClearMappings < Command
def _run(_resolve_id, data)
def _run(data)
entities_to_map = data['entitiesToClearMap']
is_definition = data['isDefinition']
action = Actions::ClearMappings.new(entities_to_map, is_definition)
+8 -16
View File
@@ -1,7 +1,5 @@
# frozen_string_literal: true
require_relative '../actions/handle_error'
module SpeckleConnector
module Commands
# Base command schema to wrap common operations for all commands.
@@ -9,25 +7,19 @@ module SpeckleConnector
# @return [App::SpeckleConnectorApp] the main app object
attr_reader :app
# @return [Ui::Binding] binding object holds dialog and it's state
attr_reader :binding
# @return [Ui::View] view object holds dialog and it's state
attr_reader :view
# @param app [App::SpeckleConnectorApp] the main app object
# @param binding [Ui::Binding] binding object holds commands to call
def initialize(app, binding)
# @@param app [App::SpeckleConnectorApp] the main app object
def initialize(app)
@app = app
@binding = binding
@view = app.ui_controller.user_interfaces[Ui::SPECKLE_UI_ID]
end
def run(*parameters)
begin
# Run here common operations that same for each command.
with_observers_disabled do
_run(*parameters)
end
rescue StandardError => e
action = Actions::HandleError.new(e, @binding.name, @action, parameters)
app.update_state!(action)
# Run here common operations that same for each command.
with_observers_disabled do
_run(*parameters)
end
end
@@ -0,0 +1,15 @@
# frozen_string_literal: true
require_relative 'command'
module SpeckleConnector
module Commands
# Run this command when the UI is ready to get data
class DialogReady < Command
# Update the selected user interface
def _run(_data)
view.update_view(app.state)
end
end
end
end
@@ -1,85 +0,0 @@
# frozen_string_literal: true
require_relative 'command'
require_relative '../ui/dui3_dialog'
require_relative '../states/initial_state'
require_relative '../ui/legacy_binding'
require_relative '../ui/bindings/accounts_binding'
require_relative '../ui/bindings/base_binding'
require_relative '../ui/bindings/send_binding'
require_relative '../ui/bindings/receive_binding'
require_relative '../ui/bindings/selection_binding'
require_relative '../ui/test_binding'
require_relative '../ui/bindings/config_binding'
require_relative '../ui/sketchup_config_binding'
require_relative '../actions/initialize_speckle'
require_relative '../observers/factory'
module SpeckleConnector
module Commands
# Command to initialize Speckle UI and register it to ui_controller.
# This is the command where we show UI to user.
class InitializeDUI3Speckle < Command
SPECKLE_DUI3 = 'speckle_dui3'
def dialog_title
"Speckle #{CONNECTOR_VERSION}"
end
private
def _run
app = self.app
if !app.state.instance_of?(States::InitialState) && app.ui_controller.user_interfaces[SPECKLE_DUI3]
dialog = app.ui_controller.user_interfaces[SPECKLE_DUI3]
dialog.show
return
end
initialize_speckle_dui3(app)
end
# Do the actual Speckle initialization.
# rubocop:disable Naming/VariableNumber
def initialize_speckle_dui3(app)
# TODO: Initialize here speckle states and observers.
observer_handler = Observers::Factory.create_handler(app)
app.add_observer_handler!(observer_handler)
observers = Observers::Factory.create_observers(observer_handler)
app.update_state!(Actions::InitializeSpeckle, observers, app.method(:instant_message_sender))
dialog_specs = {
dialog_id: SPECKLE_DUI3,
dialog_title: dialog_title,
height: 950,
width: 300
}
# Init bindings
base_binding = Ui::BaseBinding.new(app, Ui::BASE_BINDING_NAME)
accounts_binding = Ui::AccountsBinding.new(app, Ui::ACCOUNTS_BINDING_NAME)
send_binding = Ui::SendBinding.new(app, Ui::SEND_BINDING_NAME)
receive_binding = Ui::ReceiveBinding.new(app, Ui::RECEIVE_BINDING_NAME)
selection_binding = Ui::SelectionBinding.new(app, Ui::SELECTION_BINDING_NAME)
test_bindings = Ui::TestBinding.new(app, Ui::TEST_BINDINGS_NAME)
config_bindings = Ui::ConfigBinding.new(app, Ui::CONFIG_BINDING_NAME)
connector_config_bindings = Ui::SketchupConfigBinding.new(app, Ui::CONNECTOR_CONFIG_BINDING_NAME)
# Init dialog
dui3_dialog = SpeckleConnector::Ui::DUI3Dialog.new(**dialog_specs)
# Register bindings to dialog
dui3_dialog.bindings[Ui::BASE_BINDING_NAME] = base_binding
dui3_dialog.bindings[Ui::ACCOUNTS_BINDING_NAME] = accounts_binding
dui3_dialog.bindings[Ui::SEND_BINDING_NAME] = send_binding
dui3_dialog.bindings[Ui::RECEIVE_BINDING_NAME] = receive_binding
dui3_dialog.bindings[Ui::TEST_BINDINGS_NAME] = test_bindings
dui3_dialog.bindings[Ui::CONFIG_BINDING_NAME] = config_bindings
dui3_dialog.bindings[Ui::CONNECTOR_CONFIG_BINDING_NAME] = connector_config_bindings
dui3_dialog.bindings[Ui::SELECTION_BINDING_NAME] = selection_binding
app.ui_controller.register_ui(SPECKLE_DUI3, dui3_dialog)
dui3_dialog.show
end
# rubocop:enable Naming/VariableNumber
end
end
end
@@ -2,17 +2,15 @@
require_relative 'command'
require_relative '../states/initial_state'
require_relative '../ui/legacy_binding'
require_relative '../ui/vue_view'
require_relative '../actions/initialize_speckle'
require_relative '../observers/factory'
module SpeckleConnector
module Commands
# Command to initialize old Speckle UI and register it to ui_controller.
# Command to initialize Speckle UI and register it to ui_controller.
# This is the command where we show UI to user.
class InitializeSpeckle < Command
SPECKLE_LEGACY_UI = 'speckle_legacy_ui'
def dialog_title
"Speckle #{CONNECTOR_VERSION}"
end
@@ -21,34 +19,32 @@ module SpeckleConnector
def _run
app = self.app
if !app.state.instance_of?(States::InitialState) && app.ui_controller.user_interfaces[SPECKLE_LEGACY_UI]
vue_view = app.ui_controller.user_interfaces[SPECKLE_LEGACY_UI]
unless app.state.instance_of?(States::InitialState)
vue_view = app.ui_controller.user_interfaces[Ui::SPECKLE_UI_ID]
vue_view.show
return
end
initialize_speckle_legacy_view(app)
initialize_speckle(app)
end
# Do the actual Speckle initialization.
def initialize_speckle_legacy_view(app)
def initialize_speckle(app)
# TODO: Initialize here speckle states and observers.
observer_handler = Observers::Factory.create_handler(app)
app.add_observer_handler!(observer_handler)
observers = Observers::Factory.create_observers(observer_handler)
app.update_state!(Actions::InitializeSpeckle, observers)
dialog_specs = {
dialog_id: SPECKLE_LEGACY_UI,
dialog_id: Ui::SPECKLE_UI_ID,
htm_file: Ui::VUE_UI_HTML,
dialog_title: dialog_title,
height: 950,
width: 300
}
legacy_ui_dialog = SpeckleConnector::Ui::Dialog.new(**dialog_specs)
legacy_binding = Ui::LegacyBinding.new(app, 'legacy_ui')
legacy_ui_dialog.bindings[Ui::SPECKLE_LEGACY_BINDING_NAME] = legacy_binding
app.ui_controller.register_ui(SPECKLE_LEGACY_UI, legacy_ui_dialog)
legacy_ui_dialog.show
vue_view = Ui::VueView.new(dialog_specs, app)
app.ui_controller.register_ui(Ui::SPECKLE_UI_ID, vue_view)
vue_view.show
end
end
end
@@ -7,7 +7,7 @@ module SpeckleConnector
module Commands
# Command to update mapper source.
class MapperSourceUpdated < Command
def _run(_resolve_id, data)
def _run(data)
base = data['base']
stream_id = data['stream_id']
commit_id = data['commit_id']
@@ -8,7 +8,7 @@ module SpeckleConnector
module Commands
# Command to update theme.
class ModelPreferencesUpdated < Command
def _run(_resolve_id, data)
def _run(data)
preference = data['preference']
new_value = data['value']
app.update_state!(Actions::ModelPreferencesUpdated.new(preference, new_value))
@@ -8,7 +8,7 @@ module SpeckleConnector
module Commands
# Command to notify connected.
class NotifyConnected < Command
def _run(_resolve_id, data)
def _run(data)
stream_id = data['stream_id']
app.update_state!(Actions::Connected)
app.update_state!(Actions::SendFromQueue.new(stream_id))
@@ -7,15 +7,14 @@ module SpeckleConnector
module Commands
# Command to receive objects from Speckle Server.
class ReceiveObjects < Command
def _run(_resolve_id, data)
def _run(data)
stream_id = data['stream_id']
base = data['base']
branch_name = data['branch_name']
branch_id = data['branch_id']
stream_name = data['stream_name']
source_app = data['source_app']
object_id = data['object_id']
action = Actions::ReceiveObjects.new(stream_id, base, stream_name, branch_name, branch_id, source_app, object_id)
action = Actions::ReceiveObjects.new(stream_id, base, stream_name, branch_name, branch_id, source_app)
app.update_state!(action)
end
end
@@ -8,7 +8,7 @@ module SpeckleConnector
module Commands
# Command to remove stream.
class RemoveStream < Command
def _run(_resolve_id, data)
def _run(data)
stream_id = data['stream_id']
action = Actions::RemoveStream.new(stream_id)
app.update_state!(action)
@@ -2,6 +2,7 @@
require_relative 'command'
require_relative '../states/initial_state'
require_relative '../ui/vue_view'
require_relative '../actions/initialize_speckle'
require_relative '../observers/factory'
@@ -14,7 +15,7 @@ module SpeckleConnector
def _run
app = self.app
vue_view = app.ui_controller.user_interfaces[Ui::SPECKLE_LEGACY_UI]
vue_view = app.ui_controller.user_interfaces[Ui::SPECKLE_UI_ID]
if vue_view
vue_view.dialog.reset_dialog_location
else
@@ -9,7 +9,7 @@ module SpeckleConnector
module Commands
# Command to saved stream.
class SaveStream < Command
def _run(_resolve_id, data)
def _run(data)
stream_id = data['stream_id']
app.update_state!(Actions::SaveStream.new(stream_id))
end
@@ -7,7 +7,7 @@ module SpeckleConnector
module Commands
# Command to send selection to Speckle Server.
class SendSelection < Command
def _run(_resolve_id, data)
def _run(data)
stream_id = data['stream_id']
action = Actions::SendSelection.new(stream_id)
app.update_state!(action)
@@ -3,7 +3,6 @@
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'
@@ -12,7 +11,6 @@ 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
@@ -30,10 +28,6 @@ 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)
@@ -44,7 +38,7 @@ module SpeckleConnector
def self.initialize_speckle_command(app)
cmd = MenuCommandHandler.sketchup_command(
InitializeSpeckle.new(app, nil), 'Initialize Speckle'
InitializeSpeckle.new(app), 'Initialize Speckle'
)
cmd.tooltip = 'Launch Connector'
cmd.status_bar_text = 'Opens the Speckle Connector window'
@@ -53,20 +47,9 @@ 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'
ResetWindowLocation.new(app), 'Reset Window Location'
)
cmd.tooltip = 'Bring Speckle window onto center of SketchUp window'
cmd.status_bar_text = 'Bring Speckle window onto center of SketchUp window'
@@ -77,7 +60,7 @@ module SpeckleConnector
def self.send_command(app)
cmd = MenuCommandHandler.sketchup_command(
ActionCommand.new(app, nil, Actions::OneClickSend), 'Send to Speckle'
ActionCommand.new(app, 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(_resolve_id, data)
def _run(data)
preference_hash = data['preference_hash']
preference = data['preference']
new_value = data['value']
@@ -5,9 +5,6 @@ module SpeckleConnector
SPECKLE_MAPPING_TOOL_SCHEMA = 'Speckle_Mapping_Tool_Schema'
SPECKLE_SCHEMA = 'Speckle_Schema'
SPECKLE_SEND_CARDS = 'Speckle_Send_Cards'
SPECKLE_RECEIVE_CARDS = 'Speckle_Receive_Cards'
SPECKLE_ID = 'speckle_id'
SPECKLE_TYPE = 'speckle_type'
APPLICATION_ID = 'application_id'
@@ -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::Edge => INCLUDE_EDGE_ENTITY_ATTRIBUTES
Sketchup::Face => INCLUDE_EDGE_ENTITY_ATTRIBUTES
}.freeze
LEVEL_SHIFT_VALUE = SpeckleObjects::Geometry.length_to_native(1.5, 'm')
@@ -13,14 +13,6 @@ 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'
@@ -310,12 +310,8 @@ module SpeckleConnector
Digest::MD5.hexdigest(traversed_base.to_json)
end
def batch_objects
@objects
end
# rubocop:disable Metrics/MethodLength
def batch_json_objects(max_batch_size_mb = 1)
def batch_objects(max_batch_size_mb = 1)
max_size = 1000 * 1000 * max_batch_size_mb
batches = []
batch = '['
@@ -21,8 +21,6 @@ module SpeckleConnector
attr_accessor :definitions
# @param state [States::State] the current state of the {SpeckleConnector::App}
def initialize(state, stream_id)
@state = state
+3 -33
View File
@@ -17,9 +17,6 @@ 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
@@ -30,37 +27,12 @@ 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, state, @stream_id, &convert)
@units, preferences, &convert)
return new_speckle_state, model_collection
end
@@ -72,7 +44,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_json_objects
batches = serializer.batch_objects
# write_to_speckle_folder(id, batches)
puts "Generating traversed object elapsed #{Time.now.to_f - t} s"
base_total_children_count = serializer.total_children_count(id)
@@ -90,7 +62,6 @@ 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) &&
@@ -104,8 +75,7 @@ module SpeckleConnector
# 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(speckle_state: speckle_state, edge: entity,
units: @units, model_preferences: preferences[:model]).to_h
line = SpeckleObjects::Geometry::Line.from_edge(entity, @units, preferences[:model]).to_h
return speckle_state, [line, [entity]]
end
-23
View File
@@ -1,23 +0,0 @@
#-------------------------------------------------------------------------------
#
# Thomas Thomassen
# thomas[at]thomthom[dot]net
#
#-------------------------------------------------------------------------------
#-------------------------------------------------------------------------------
module SpeckleConnector
module TT
module Lib
### CONSTANTS ### ------------------------------------------------------------
# Plugin information
PLUGIN_ID = 'TT_Lib2'.freeze
PLUGIN_NAME = 'TT_Lib²'.freeze
PLUGIN_VERSION = '2.13.1'.freeze
end # module Lib
end # module TT
end
@@ -1,74 +0,0 @@
#-----------------------------------------------------------------------------
#
# CHANGELOG
# 2.5.0 - 18.10.2010
# * Upgraded JQuery to 1.4.4
# * Now bundles JQuery minimized
# * Added Win32Utils' Win32::API under TT::Win32::API
# * Added module: TT::Bezier
# * Added module: TT::Edge
# * Added module: TT::Edges
# * Added module: TT::Faces
# * Added module: TT::Materials
# * Added module: TT::Gizmo::Axis
# * Added module: TT::Win32
# * Added class: TT::Babelfish
# * Added class: TT::Dimension
# * Added class: TT::GUI::ToolWindow
# * Added method: TT::debug
# * Added method: TT::Point3d.douglas_peucker
# * Added method: TT::Point3d.simplify_curve
# * Added method: TT::Geom3d.interpolate_linear
# * Added method: TT::Geom3d.average_point
# * Added method: TT::GUI::Button.custom_properties
# * Added method: TT::GUI::Control.add_event
# * Added method: TT::GUI::Control.call_event
# * Added method: TT::GUI::Control.positioned?
# * Added method: TT::GUI::Control.properties
# * Added method: TT::GUI::ContainerElement.add_controls_to_webdialog
# * Added method: TT::GUI::ContainerElement.get_control_by_ui_id
# * Added methods: TT::GUI::Listbox
# * Added method: TT::GUI::Window.add_action_callback
# * Added method: TT::GUI::Window.add_control_to_webdialog
# * Added method: TT::GUI::Window.set_client_size
# * Added method: TT::System.is_windows?
# * Changed: TT::Gizmo::Manipulator
# * Changed: TT::GUI::Inputbox now inherits from TT::GUI::ToolWindow
# * Changed: TT::GUI::Inputbox.add_control accepts a +key+ argument a control id.
# * Changed: TT::GUI::Inputbox.prompt returns a Hash instead of Array.
# * Changed: TT::UV_Plane
# * Fixed: TT::JSON.to_s - Now handles Symbols as values.
# * Plus lots lots more - lots track of it all.
#
# 2.4.0 - 22.09.2010
# * Added module: TT::Binary
# * Added module: TT::GUI
# * Added class: TT::GUI::Window
# * Added class: TT::GUI::Inputbox
# * Added class: TT::JSON
# * Added module: TT::System
# * Added module: TT::Locale
# * Added module: TT::Cursor
#
# 2.3.0 - 08.09.2010
# * Added method: TT::Geom3d.spiral_sphere
# * Fixed: TT::Ray.test
#
# 2.2.0 - 06.09.2010
# * Added module: TT::Bounds
#
# 2.1.1 - 04.09.2010
# * Fixed bug: Ray.test (Workaround for SU8.0 bug)
#
# 2.1.0 - 03.09.2010
# * Added module: TT::Selection
# * Added module: TT::UVQ
# * Added class: TT::UV_Plane
# * Added class: TT::Gizmo::Manipulator
# * Added method: TT::Entities.bounds
# * Fixed: TT::Ray.test
#
# 2.0.0 - 01.09.2010
# * Initial Release
#
#-----------------------------------------------------------------------------
@@ -1,12 +0,0 @@
= TT_Lib2
* {SCF Thread}[http://forums.sketchucation.com/viewtopic.php?f=323&t=23307]
== Description
Library of common methods and classes for Google SketchUp Ruby plugins.
== Author
* Thomas Thomassen
* thomas[at]thomthom[dot]net
Binary file not shown.
-70
View File
@@ -1,70 +0,0 @@
#-----------------------------------------------------------------------------
#
# 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
@@ -1,63 +0,0 @@
#-----------------------------------------------------------------------------
#
# 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
@@ -1,348 +0,0 @@
#-----------------------------------------------------------------------------
#
# 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
@@ -1,13 +0,0 @@
#-----------------------------------------------------------------------------
#
# 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' )
@@ -1,38 +0,0 @@
#-----------------------------------------------------------------------------
#
# 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
@@ -1,55 +0,0 @@
#-----------------------------------------------------------------------------
#
# 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
@@ -1,112 +0,0 @@
#-----------------------------------------------------------------------------
#
# 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
@@ -1,241 +0,0 @@
#-------------------------------------------------------------------------------
#
# Thomas Thomassen
# thomas[at]thomthom[dot]net
#
#-------------------------------------------------------------------------------
module SpeckleConnector
module TT
# Loads the appropriate C Extension loader after ensuring the appropriate
# version has been copied from the staging area.
#
# @since 2.9.0
class CExtensionManager
class IncompatibleVersion < RuntimeError; end
VERSION_PATTERN = /\d+\.\d+\.\d+$/
# The `path` argument should point to the path where a 'stage' folder is
# located with the following folder structure:
#
# + `path`
# +-+ stage
# +-+ 1.8
# | +-+ HelloWorld.so
# | + HelloWorld.bundle
# +-+ 2.0
# +-+ HelloWorld.so
# + HelloWorld.bundle
#
# The appropriate file will be copied on demand to a folder structure like:
# `path`/<EXTENSION_VERSION>/<RUBY_VERSION>/HelloWorld.so
#
# When a new version is deployed the files will be copied again from the
# staging area to a new folder named with the new extension version.
#
# The old versions are cleaned up if possible. This attempt is done upon
# each time #prepare_path is called.
#
# This way the C extensions can be updated because they are never loaded
# from the staging folder directly.
#
# @param [String] path The location where the C Extensions are located.
# @since 2.9.0
def initialize( path, version )
# ENV, __FILE__, $LOAD_PATH, $LOADED_FEATURE and more might return an
# encoding different from UTF-8. It's often ASCII-US or ASCII-8BIT.
# If the developer has derived from these strings the encoding sticks with
# it and will often lead to errors further down the road when trying to
# load the files. To work around this the path is attempted to be
# relabeled as UTF-8 if we can produce a valid UTF-8 string.
# I'm forcing an encoding instead of converting because the encoding label
# of the strings seem to be consistently mislabeled - the data is in
# fact UTF-8.
if path.respond_to?(:encoding)
test_path = path.dup.force_encoding("UTF-8")
path = test_path if test_path.valid_encoding?
end
unless version =~ VERSION_PATTERN
raise ArgumentError, 'Version must be in "X.Y.Z" format'
end
unless File.directory?( path )
raise IOError, "Stage path not found: #{path}"
end
@version = version
@path = path
@stage = File.join( path, 'stage' )
@target = File.join( path, version )
@log = []
# See method comments for more info.
#require_file_utils()
end
# Copies the necessary C Extension libraries to a version dependent folder
# from where they can be loaded. This will allow the SketchUp RBZ installer
# to update the extension without running into errors when trying to
# overwrite files from previous installation.
#
# @return [String] The path where the extensions are located.
# @since 2.9.0
def prepare_path
log("prepare_path")
pointer_size = ['a'].pack('P').size * 8 # 32 or 64
ruby = RUBY_VERSION.split('.')[0..1].join('.') # Get Major.Minor string.
platform = ( TT::System::PLATFORM_IS_OSX ) ? 'osx' : 'win'
platform = "#{platform}#{pointer_size}"
stage_path = File.join( @stage, ruby, platform )
target_path = File.join( @target, ruby, platform )
fallback = false
log("> stage_path: #{stage_path}")
log("> target_path: #{target_path}")
begin
# Copy files if target doesn't exist.
unless File.directory?(stage_path)
raise IncompatibleVersion, "Staging directory not found: #{stage_path}"
end
unless File.directory?( target_path )
log("MKDIR: #{target_path}")
require_file_utils() # See method comments for more info.
log(FileUtils.mkdir_p( target_path ))
end
stage_content = Dir.entries( stage_path )
target_content = Dir.entries( target_path )
log("> stage_content: #{stage_content}")
log("> target_content: #{target_content}")
unless (stage_content - target_content).empty?
log("COPY: #{stage_path} => #{target_path}")
require_file_utils() # See method comments for more info.
log(FileUtils.copy_entry( stage_path, target_path ))
end
# Clean up old versions.
version_pattern = /\d+\.\d+\.\d+$/
filter = File.join( @path, '*' )
log("> cleanup: #{filter}")
Dir.glob( filter ).each { |entry|
log(">>> entry: #{entry}")
next unless File.directory?( entry )
log(">>> @target: #{@target} (#{entry.downcase == @target.downcase})")
log(">>> @stage: #{@stage} (#{entry.downcase == @stage.downcase})")
log(">>>>> @target vs entry")
log(">>>>> #{entry.class.name}: #{entry.bytes}") if entry.respond_to?(:bytes)
log(">>>>> #{@target.class.name}: #{@target.bytes}") if @target.respond_to?(:bytes)
next if entry.downcase == @stage.downcase || entry.downcase == @target.downcase
log(">>> match: #{entry =~ version_pattern}")
next unless entry =~ version_pattern
begin
log("REMOVE: #{entry}")
require_file_utils # See method comments for more info.
log(FileUtils.rm_r( entry ))
rescue
log_warn("#{TT::Lib::PLUGIN_NAME} - Unable to clean up: #{entry}")
end
}
rescue Errno::EACCES
if fallback
UI.messagebox(
"Failed to load #{TT::Lib::PLUGIN_NAME}. Missing permissions to " <<
"Plugins and temp folder."
)
raise
else
# Even though the temp folder contains the username, it appear to be
# returned in DOS 8.3 format which Ruby 1.8 can open. Fall back to
# using the temp folder for these kind of systems.
log_warn("#{TT::Lib::PLUGIN_NAME} - Unable to access: #{target_path}")
temp_tt_lib_path = File.join( temp_path, TT::Lib::PLUGIN_ID )
target_path = File.join( temp_tt_lib_path, @version, ruby, platform )
log_warn("#{TT::Lib::PLUGIN_NAME} - Falling back to: #{target_path}")
fallback = true
retry
end
end
target_path
end
# @return [String]
# @since 2.9.0
def to_s
object_hex_id = "0x%x" % (self.object_id << 1)
"<##{self.class}::#{object_hex_id}>"
end
alias :inspect :to_s
# @since 2.10.7
def print_log
puts @log.join("\n")
end
private
# @since 2.10.7
def log(value)
string = value.is_a?(String) ? value : value.inspect
@log << string
string
end
# @since 2.10.7
def log_warn(value)
puts log(value)
end
# Return the system temp path from the environment variables.
#
# @since 2.10.7
def temp_path
File.expand_path( ENV['TMPDIR'] || ENV['TMP'] || ENV['TEMP'] )
end
# Attempt to load the Standard Library FileUtils module. Fall back to
# bundled 1.8 copy.
#
# @since 2.9.0
def require_file_utils
if RUBY_VERSION.to_i == 1
path = File.dirname( __FILE__ ) # rubocop:disable SketchupSuggestions/FileEncoding
require File.join( path, 'thirdparty', 'fileutils.rb' )
else
begin
require 'fileutils'
rescue LoadError
# A bug in SketchUp 2014 M0 caused the drive letter for the Ruby
# Standard Library to be incorrect if SketchUp was started by
# clicking a SKP file on a drive (network drive?) different from
# where SketchUp was installed.
#
# This cause the fileutils to fail to load. To work around this the
# file is required right before it is needed. That should make the
# file needed only the first time after installing a new version.
# Makes the code awkward and ugly, but alas. :(
std_lib_path = Sketchup.find_support_file( 'Tools/RubyStdLib' ) # rubocop:disable SketchupSuggestions/SketchupFindSupportFile
unless $LOAD_PATH.include?( std_lib_path )
UI.messagebox(
'Due to a bug in SketchUp 2014 M0 the standard library was ' <<
'not loaded. Please start SketchUp from a link on the drive ' <<
'it was installed to instead of from clicking an SKP on a ' <<
'different drive.'
) unless @load_error_displayed
@load_error_displayed = true
end
puts $LOAD_PATH.join("\n")
raise
end
end # if RUBY_VERSION
end
end # class
end # module
end
@@ -1,28 +0,0 @@
#-----------------------------------------------------------------------------
#
# Thomas Thomassen
# thomas[at]thomthom[dot]net
#
#-----------------------------------------------------------------------------
require_relative 'core.rb'
# Collection of Color methods.
#
# @since 2.5.0
module SpeckleConnector
module TT::Color
# Safely clones a Sketchup::Color object. Sketchup::Color.clone appear to
# be bugged and prone to crash SU.
#
# @param [Sketchup::Color] color
#
# @return [Sketchup::Color]
# @since 2.5.0
def self.clone(color)
Sketchup::Color.new( *color.to_a )
end
end # module TT::Instance
end
-537
View File
@@ -1,537 +0,0 @@
#-------------------------------------------------------------------------------
#
# Thomas Thomassen
# thomas[at]thomthom[dot]net
#
#-------------------------------------------------------------------------------
Sketchup::require 'sketchup.rb'
# Sketchup::require '../TT_Lib2.rb'
require_relative 'system.rb'
require_relative 'c_extension_manager.rb'
#-------------------------------------------------------------------------------
# Root namespace for Thomas Thomassen (ThomThom, TT)
#
# Do not modify or extend!
#
# @since 2.0.0
module SpeckleConnector
module TT
### CONSTANTS ### ------------------------------------------------------------
# BoundingBox Constants
# @since 2.0.0
BB_LEFT_FRONT_BOTTOM = 0
BB_RIGHT_FRONT_BOTTOM = 1
BB_LEFT_BACK_BOTTOM = 2
BB_RIGHT_BACK_BOTTOM = 3
BB_LEFT_FRONT_TOP = 4
BB_RIGHT_FRONT_TOP = 5
BB_LEFT_BACK_TOP = 6
BB_RIGHT_BACK_TOP = 7
BB_CENTER_FRONT_BOTTOM = 8
BB_CENTER_BACK_BOTTOM = 9
BB_CENTER_FRONT_TOP = 10
BB_CENTER_BACK_TOP = 11
BB_LEFT_CENTER_BOTTOM = 12
BB_LEFT_CENTER_TOP = 13
BB_RIGHT_CENTER_BOTTOM = 14
BB_RIGHT_CENTER_TOP = 15
BB_LEFT_FRONT_CENTER = 16
BB_RIGHT_FRONT_CENTER = 17
BB_LEFT_BACK_CENTER = 18
BB_RIGHT_BACK_CENTER = 19
BB_LEFT_CENTER_CENTER = 20
BB_RIGHT_CENTER_CENTER = 21
BB_CENTER_FRONT_CENTER = 22
BB_CENTER_BACK_CENTER = 23
BB_CENTER_CENTER_TOP = 24
BB_CENTER_CENTER_BOTTOM = 25
BB_CENTER_CENTER_CENTER = 26
BB_CENTER = 26
# UI.messagebox Constants
# @since 2.4.0
MB_ICONHAND = 0x00000010
MB_ICONSTOP = 0x00000010
MB_ICONERROR = 0x00000010
MB_ICONQUESTION = 0x00000020
MB_ICONEXCLAMATION = 0x00000030
MB_ICONWARNING = 0x00000030
MB_ICONASTERISK = 0x00000040
MB_ICONINFORMATION = 0x00000040
MB_ICON_NONE = 80
MB_DEFBUTTON1 = 0x00000000
MB_DEFBUTTON2 = 0x00000100
MB_DEFBUTTON3 = 0x00000200
MB_DEFBUTTON4 = 0x00000300
# PolygonMesh
# @since 2.5.0
MESH_SHARP = 0
MESH_SOFT = 4
MESH_SMOOTH = 8
MESH_SOFT_SMOOTH = 12
# view.draw_points
# @since 2.5.0
POINT_OPEN_SQUARE = 1
POINT_FILLED_SQUARE = 2
POINT_CROSS = 3
POINT_X = 4
POINT_STAR = 5
POINT_OPEN_TRIANGLE = 6
POINT_FILLED_TRIANGLE = 7
# Handle to error message window.
# @since 2.7.0
@lib2_update = nil
def self.lib2_update; @lib2_update; end
def self.lib2_update=(window); @lib2_update = window; end
# Defers execution of the given block.
#
# @param [Numeric] time
#
# @return [Nil]
# @since 2.5.0
def self.defer( time = 0 )
done = false
timer = UI.start_timer( time, false ) {
# (i) Unless the timer is stopped before the messagebox it will
# continue to trigger until the frist messagebox is closed.
unless done
done = true
yield
end
}
nil
end
### LIBRARY ### --------------------------------------------------------------
# TT_Lib related methods.
#
# @since 2.0.0
module Lib
# Library version number.
# @since 2.0.0
VERSION = PLUGIN_VERSION
# Library preference key.
# @since 2.5.0
PREF_KEY = 'TT_Lib2'.freeze
# @since 2.8.0
file = File.expand_path( __FILE__ ) # rubocop:disable SketchupSuggestions/FileEncoding
file.force_encoding( "UTF-8" ) if file.respond_to?( :force_encoding )
PATH = File.dirname( file ).freeze
PATH_LIBS = File.join( PATH, 'libraries' ).freeze
# TT::Lib.cext_manager
def self.cext_manager; @cext_manager; end
begin
@cext_manager = CExtensionManager.new( PATH_LIBS, PLUGIN_VERSION )
PATH_LIBS_CEXT = @cext_manager.prepare_path.freeze
rescue CExtensionManager::IncompatibleVersion => error
unless @compatibility_alert
# Avoid this message being called for every extension that rely on TT_Lib.
@compatibility_alert = true
message = "%{extension_name} version %{version} is not "\
"compatible with this version of SketchUp and could not be loaded. "\
"Please check for updates to the extension."
message %= { extension_name: PLUGIN_NAME, version: PLUGIN_VERSION }
TT.defer(1.0) {
UI.messagebox(message)
}
end
end
# Call this method to check if the installed +TT_Lib+ version is the same
# or newer than +version+. If it's not then a messagebox will appear
# informing the user that a newer +TT_Lib+ is required.
#
# @param [String] version a string with the minimun version required in
# the format 'x.x.x'.
# @param [String] plugin_name a string describing to the user which plugin
# require a newer TT_Lib version.
#
# @return [Boolean]
# @since 2.0.0
def self.compatible?(version, plugin_name = 'A plugin installed')
major, minor, revision = TT::Lib::VERSION.split('.').map { |s| s.to_i }
min_major, min_minor, min_revision = version.split('.').map { |s| s.to_i }
return true if major > min_major
return true if major == min_major && minor > min_minor
return true if major == min_major && minor == min_minor && revision >= min_revision
#UI.messagebox("#{plugin_name} requires a newer version, #{version}, of TT_Lib.")
if TT.lib2_update.nil?
url = 'http://www.thomthom.net/software/sketchup/tt_lib2/errors/outdated'
options = {
:dialog_title => 'TT_Lib² Outdated',
:scrollable => false, :resizable => false, :left => 200, :top => 200
}
w = UI::WebDialog.new( options )
w.set_size( 500, 300 )
arguments = "plugin=#{plugin_name}"
arguments << "&version=#{TT::Lib::VERSION}"
arguments << "&minimum=#{version}"
w.set_url( "#{url}?#{arguments}" )
w.show
TT.lib2_update = w
end
return false
end
# Compiles a list of the current .rb and .rbs files in the library. This is
# used to verify the integirty of the installation upon first run.
#
# @private
# @return [Nil]
# @since 2.5.0
def self.compile_integrity_list
result = UI.messagebox( <<MSG, MB_OKCANCEL )
This method is only intended for development purposes. Don't mess about with it!
Press Cancel.
MSG
#'# Silly Sublime Text doesn't handle HereDoc arguments properly.
return if result == 2 # CANCEL
files = Dir.glob( File.join(self.path, '*.{rb,rbs}') ).map! { |file|
File.basename( file )
}
File.open( self.integrity_check_file, 'w' ) { |output|
output.puts( files )
}
nil
end
# @private
# @since 2.9.5
class IntegrityCheck
attr_reader :unexpected_files, :missing_files
def initialize( file_with_list_of_expected_files )
integrity_file = file_with_list_of_expected_files
@expected_files = IO.readlines( integrity_file ).map! { |file|
file.strip
}
@missing_files = @expected_files.select { |file|
filename = File.join( PATH, file )
!File.exist?( filename )
}
filter = File.join(PATH, '*.{rb,rbs}' )
@existing_files = Dir.glob( filter ).map! { |file|
File.basename( file )
}
@unexpected_files = @existing_files - @expected_files
end
def ok?
@missing_files.empty? && @unexpected_files.empty?
end
def missing_files?
!@missing_files.empty?
end
def unexpected_files?
!@unexpected_files.empty?
end
end # class IntegrityCheck
# Called upon startup. If it's the first time this library is loaded a file
# integrity check is run to ensure all requires files are present, and that
# there are no old remains in case of an update.
#
# @private
# @return [Boolean]
# @since 2.5.0
def self.integrity_check
version = "VerifiedIntegrity-#{self::VERSION}"
verified = ::Sketchup.read_default( PREF_KEY, version )
return true if verified
integrity = IntegrityCheck.new( self.integrity_check_file )
if integrity.ok?
::Sketchup.write_default( PREF_KEY, version, true )
true
else
message = <<MSG
TT_Lib² appear to be incorrectly installed. Please remove TT_Lib² and then
install it again. If this error message persist, contant the author for
assistance.
MSG
message.gsub!( /\s+/, ' ' ) # Collapse whitespace.
if integrity.missing_files?
missing_files = integrity.missing_files.join("\n")
message << "\n\nMissing files:\n" << missing_files
end
if integrity.unexpected_files?
unexpected_files = integrity.unexpected_files.join("\n")
message << "\n\nUnexpected files found:\n" << unexpected_files
end
# Defer execution to allow remaining plugins to load.
TT.defer { UI.messagebox( message, MB_OK ) }
false
end
end
# Checks for VirtualStore conflict.
#
# @private
# @return [Nil]
# @since 2.9.0
def self.virtualstore_check
return nil unless TT::System.is_windows?
require 'TT_Lib2/win32.rb'
plugins_folder = Sketchup.find_support_file( 'Plugins' ) # rubocop:disable SketchupSuggestions/SketchupFindSupportFile
virtual_folder = TT::System.get_virtual_file( plugins_folder )
return nil if virtual_folder.nil?
return nil if plugins_folder == virtual_folder
return nil unless File.exist?( virtual_folder )
if Dir.entries( virtual_folder ).to_a.size > 2
message = <<MSG
TT_Lib² detected that some of the files in SketchUp's plugin folder has ended up
in Window's Virtual Store. It happens because of insufficint permissions for the
plugin folder. Please move the files into the real Plugins folder.
MSG
message.gsub!( /\s+/, ' ' ) # Collapse whitespace.
# Defer execution to allow remaining plugins to load.
TT.defer {
result = UI.messagebox( message, MB_OKCANCEL )
if result == IDOK
UI.openURL( virtual_folder )
end
}
end
nil
end
# Returns the full path to the integrity file.
#
# @private
# @return [Nil]
# @since 2.5.0
def self.integrity_check_file
File.join( self.path, 'integrity_list.dat' )
end
# @return [String] The file path where the library is installed.
# @since 2.0.0
def self.path
PATH.dup
end
# Debug method to reload the library modules.
#
# @param [Boolean] return_files Determines if the method should return
# the number of files reloaded or an array of the files reloaded.
#
# @return [Integer, Array]
# @since 2.0.0
def self.reload( return_files=false )
original_verbose = $VERBOSE
$VERBOSE = nil # Mute warnings caused by constant redefining.
x = Dir.glob( File.join(self.path, '*.{rb,rbs}') ).each { |file|
load file
}
(return_files) ? x : x.length
ensure
$VERBOSE = original_verbose
end
end # module TT::Lib
### MENUS ### ----------------------------------------------------------------
# If TT's Menu is installed this method will return a custom Menu item
# instead of the requested root menu.
#
# @param [String] name The prefered root menu in Sketchup.
#
# @return [Sketchup::Menu]
# @since 2.0.0
def self.menu( name )
if global_variables.include?( :$tt_menu ) && $tt_menu
$tt_menu
else
UI.menu( name )
end
end
### NAMESPACE ### ------------------------------------------------------------
# Namespace for plugins to be wrapped into.
# Example:
#
# require 'TT_Lib2/core.rb'
# module TT::Plugins::FooBar
# ...
# end
#
# Reserved for Thomas Thomassen.
#
# @since 2.0.0
module Plugins; end
### MACROS ### ---------------------------------------------------------------
# @param [Integer] number
#
# @return [Boolean]
# @since 2.5.0
def self.even?(number)
number % 2 == 0
end
# @param [Integer] number
#
# @return [Boolean]
# @since 2.5.0
def self.odd?(number)
number % 2 > 0
end
# Format the given +time+ into a human readable string.
#
# @param [Numeric] time
#
# @return [String]
# @since 2.5.0
def self.format_time( time )
time = (time.finite?) ? time : 0.0
hours = (time / 3600).to_i
minutes = (time/60 - hours * 60).to_i
seconds = (time - (minutes * 60 + hours * 3600)).to_i
if hours > 0 && minutes > 0
"#{hours}h #{minutes}m #{seconds}s"
elsif minutes > 0
"#{minutes}m #{seconds}s"
else
"#{seconds}s"
end
end
# Returns the given square meters +area_meters+ in square inches.
#
# @param [Numeric] area_meters
#
# @return [Numeric]
# @since 2.5.0
def self.m2( area_meters )
ratio = 1.m ** 2
area_meters * ratio
end
# Returns the given square inches +area_inches+ in square meters.
#
# @param [Numeric] area_inches
#
# @return [Numeric]
# @since 2.5.0
def self.to_m2( area_inches )
ratio = 1.0 / self.m2(1)
area_inches * ratio
end
# @param [Object] object
#
# @return [String]
# @since 2.6.0
def self.object_id_hex( object )
"0x%x" % (object.object_id << 1)
end
# @param [Array] array
#
# @return [Hash]
# @since 2.6.0
def self.array_to_hash( array )
h = {}
for key, value in array
h[ key ] = value
end
h
end
### ENVIRONMENT ###-----------------------------------------------------------
# Set up load paths.
#path = File.join( TT::Lib.path, 'libraries' )
#$LOAD_PATH << path unless $LOAD_PATH.include?( path )
end # module TT
end
if SpeckleConnector::TT::System.platform_supported?
# Check the integrity of the library.
SpeckleConnector::TT::Lib.integrity_check
#TT::Lib.virtualstore_check() # Disabled until a better guide can be made.
# Require remaining modules.
Dir.glob( File.join(SpeckleConnector::TT::Lib.path, '*.{rb}') ).each { |filename|
file = __FILE__.dup
file.force_encoding( "UTF-8" ) if file.respond_to?(:force_encoding)
unless File.basename( filename ) == File.basename( file )
relative_file = File.join( SpeckleConnector::TT::Lib.path, File.basename( filename ) )
require( relative_file )
end
}
else
# Disable the extension if it's not supported by the platform. This is done to
# avoid potential crashes when loading the binaries.
#if Sketchup.respond_to?(:extensions)
# extension = Sketchup.extensions[TT::Lib::PLUGIN_NAME]
# extension.uncheck
#end
# Alert the user that the extension is not compatible with the running system.
message = "#{SpeckleConnector::TT::Lib::PLUGIN_NAME} is not supported for this platform."
UI.messagebox(message)
end # if TT::System.platform_supported?
-269
View File
@@ -1,269 +0,0 @@
#-----------------------------------------------------------------------------
#
# Thomas Thomassen
# thomas[at]thomthom[dot]net
#
#-----------------------------------------------------------------------------
require_relative 'core.rb'
# @since 2.4.0
module SpeckleConnector
module TT::Cursor
# Path to the cursor resources.
PATH = File.join( TT::Lib.path, 'cursors')
# Definitions of cursor resources.
# :symbol_id => ['filename.png', x, y]
@cursors = {
:default => 0,
:invalid => 663,
:hand => 671,
:hand_invalid => 918,
:link => 670,
:erase => 645,
:pencil => 632,
:freehand => 655,
:arc_1 => 629,
:arc_2 => 631,
:arc_3 => 630,
:man => 612,
:position_camera => 653,
:position_camera_3d => 902,
:walk => 420,
:walk_3d => 904,
:look_around => 418,
:look_around_3d => 903,
:orbit => 419,
:orbit_3d => 900,
:pan => 1003,
:pan_2d => 901,
:zoom => 421,
:zoom_region => 422,
:zoom_3d => 905,
:zoom_2d => 907,
:zoom_2d_region => 906,
:offset => 646,
:offset_invalid => 679,
:dropper => 651,
:dropper_texture => 652,
:dropper_invalid => ['dropper_invalid.png', 2, 29],
:paint => 681, # 647
:paint_same => 650,
:paint_object => 649,
:paint_connected => 648,
:paint_invalid => 680,
:text => 678,
:follow_me => 640,
:follow_me_invalid => 678,
:pushpull => 639,
:pushpull_add => 755,
:pushpull_invalid => 707,
:tape => 638,
:tape_add => 731,
:select => 633,
:select_add => 634,
:select_remove => 636,
:select_toggle => 635,
:select_step_1 => 924,
:select_step_2 => 925,
:select_invalid => 926,
:vertex => ['Vertex.png', 12, 19],
:vertex_add => ['Vertex_Add.png', 12, 19],
:vertex_remove => ['Vertex_Remove.png', 12, 19],
:vertex_toggle => ['Vertex_Toggle.png', 12, 19],
:rectangle => 637,
:move => 641,
:move_copy => 642,
:move_fold => 672,
:move_invalid => 673,
:position => 658,
:position_invalid => 673,
:scale => 736,
:scale_invalid => 730,
:scale_n_s => 659,
:scale_n_ne => 666,
:scale_ne => 661,
:scale_ne_e => 667,
:scale_w_e => 660,
:scale_n_nw => 665,
:scale_nw => 662,
:scale_nw_w => 664,
:rotate => 643,
:rotate_copy => 644,
:rotate_invalid => 713
}
# Creates cursor ids for the requested cursor +id+. Cursors are created on demand and
# reused to save resources.
#
# Valid +id+ arguments
# * +:default+
# * +:invalid+ (2.7.0)
# * +:hand+ (2.7.0)
# * +:hand_invalid+ (2.7.0)
# * +:link+ (2.7.0)
# * +:erase+ (2.7.0)
# * +:pencil+ (2.7.0)
# * +:freehand+ (2.7.0)
# * +:arc_1+ (2.7.0)
# * +:arc_2+ (2.7.0)
# * +:arc_3+ (2.7.0)
# * +:man+ (2.7.0)
# * +:position_camera_3d+ (2.7.0)
# * +:orbit+ (2.7.0)
# * +:orbit_3d+ (2.7.0)
# * +:pan_2d+ (2.7.0)
# * +:pan+ (2.7.0)
# * +:walk+ (2.7.0)
# * +:walk_3d+ (2.7.0)
# * +:look_around+ (2.7.0)
# * +:look_around_903+ (2.7.0)
# * +:zoom+ (2.7.0)
# * +:zoom_region+ (2.7.0)
# * +:zoom_3d+ (2.7.0)
# * +:zoom_2d+ (2.7.0)
# * +:zoom_2d_region+ (2.7.0)
# * +:offset+
# * +:offset_invalid+
# * +:dropper+
# * +:dropper_texture+ (2.7.0)
# * +:dropper_invalid+
# * +:paint+ (2.7.0)
# * +:paint_same+ (2.7.0)
# * +:paint_object+ (2.7.0)
# * +:paint_connected+ (2.7.0)
# * +:paint_invalid+ (2.7.0)
# * +:text+ (2.7.0)
# * +:follow+ (2.7.0)
# * +:follow_me+ (2.7.0)
# * +:pushpull+ (2.7.0)
# * +:pushpull_add+ (2.7.0)
# * +:pushpull_invalid+ (2.7.0)
# * +:tape+ (2.7.0)
# * +:tape_add+ (2.7.0)
# * +:select+
# * +:select_add+
# * +:select_remove+
# * +:select_toggle+
# * +:select_step_1+ (2.7.0)
# * +:select_step_2+ (2.7.0)
# * +:select_invalid+ (2.7.0)
# * +:vertex+ (2.5.0)
# * +:vertex_add+ (2.5.0)
# * +:vertex_remove+ (2.5.0)
# * +:vertex_toggle+ (2.5.0)
# * +:rectangle+ (2.6.0)
# * +:move+ (2.6.0)
# * +:move_copy+ (2.7.0)
# * +:move_fold+ (2.7.0)
# * +:move_invalid+ (2.7.0)
# * +:position+ (2.7.0)
# * +:position_invalid+ (2.7.0)
# * +:rotate+ (2.6.0)
# * +:rotate_copy+ (2.6.0)
# * +:rotate_invalid+ (2.7.0)
# * +:scale+ (2.6.0)
# * +:scale_invalid+ (2.7.0)
# * +:scale_n_s+ (2.7.0)
# * +:scale_n_ne+ (2.7.0)
# * +:scale_ne+ (2.7.0)
# * +:scale_ne_e+ (2.7.0)
# * +:scale_w_e+ (2.7.0)
# * +:scale_n_nw+ (2.7.0)
# * +:scale_nw+ (2.7.0)
# * +:scale_nw_w+ (2.7.0)
#
# @param [Symbol] id
#
# @return [Integer, nil] +Integer+ of a cursor resource uon success, +nil+ upon failure.
# @since 2.4.0
def self.get_id(id)
return nil unless @cursors.key?(id)
# Load cursors on demand
if @cursors[id].is_a?(Array)
cursor_file, x, y = @cursors[id]
filename = File.join( TT::Cursor::PATH, cursor_file )
@cursors[id] = UI.create_cursor( filename, x, y )
end
return @cursors[id]
end
# Returns a cursor ID to a scaling direction cursor based on a 2D vector in
# screen space.
#
# @param [Geom::Vector3d] screen_vector
# @param [Sketchup::View] view
#
# @return [Integer, nil] +Integer+ of a cursor resource uon success, +nil+ upon failure.
# @since 2.7.0
def self.get_vector2d_cursor( screen_vector, view )
cursors = self.scale_handles
cursor_id = nil
nearest_angle = nil
for vector, cursor in cursors
a1 = vector.angle_between( screen_vector ).abs
a2 = vector.angle_between( screen_vector.reverse ).abs
angle = [ a1, a2 ].min
if nearest_angle.nil? || angle < nearest_angle
nearest_angle = angle
cursor_id = cursor
end
end
cursor_id
end
# Returns a cursor ID to a scaling direction cursor based on a 3D vector in
# model space.
#
# @param [Geom::Vector3d] vector
# @param [Sketchup::View] view
#
# @return [Integer, nil] +Integer+ of a cursor resource uon success, +nil+ upon failure.
# @since 2.7.0
def self.get_vector3d_cursor( vector, view )
pt1 = ORIGIN
pt2 = ORIGIN.offset( vector )
spt1 = view.screen_coords( pt1 )
spt2 = view.screen_coords( pt2 )
spt1.z = 0
spt2.z = 0
screen_vector = spt1.vector_to( spt2 )
self.get_vector2d_cursor( screen_vector, view )
end
# @return [Hash]
# @since 2.7.0
def self.scale_handles
@scale_handles ||= self.compute_scale_handles
@scale_handles
end
# @private
#
# @return [Hash]
# @since 2.7.0
def self.compute_scale_handles
cursor_ids = [
TT::Cursor.get_id( :scale_nw_w ),
TT::Cursor.get_id( :scale_nw ),
TT::Cursor.get_id( :scale_n_nw ),
TT::Cursor.get_id( :scale_n_s ),
TT::Cursor.get_id( :scale_n_ne ),
TT::Cursor.get_id( :scale_ne ),
TT::Cursor.get_id( :scale_ne_e ),
TT::Cursor.get_id( :scale_w_e )
].reverse
cursors = {}
angle = ( 180.0 / cursor_ids.size ).degrees
cursor_ids.each_with_index { |id, index|
tr = Geom::Transformation.rotation( ORIGIN, Z_AXIS, -angle * index )
vector = X_AXIS.transform( tr )
cursors[ vector ] = id
}
cursors
end
end # module TT::Cursor
end
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

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