Compare commits
47 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 903cecc107 | |||
| a8c5bd573f | |||
| 1d2fe047bf | |||
| 3465f86605 | |||
| 540e727b73 | |||
| 8c576f820c | |||
| df3b673064 | |||
| 69bde5539c | |||
| aa12b7b11b | |||
| e0b3b3cca2 | |||
| ea9a5741d7 | |||
| 1fe1c8d5a8 | |||
| ac58560a69 | |||
| d0b234bf3d | |||
| 981fe05c15 | |||
| 918550465b | |||
| 963d4f1738 | |||
| d809732280 | |||
| c4e38a11b7 | |||
| 7abe215bc2 | |||
| 0f269430bc | |||
| b8a298c54b | |||
| e1d33cd250 | |||
| 4563dec9bc | |||
| de284083fd | |||
| 85ee66ca4d | |||
| 5365dc858e | |||
| 69f9544638 | |||
| bf5409cf90 | |||
| bd4f668526 | |||
| d21df0c6fd | |||
| d808fdaa09 | |||
| b32d23b9d6 | |||
| d1b8ba95df | |||
| 5e6825bbdb | |||
| 3e5ba136be | |||
| 294b49a11c | |||
| 9e0cca447f | |||
| bc3f23d8dd | |||
| 4fcf11eaa8 | |||
| 1f45902837 | |||
| bc55543e23 | |||
| c1b8ec7036 | |||
| abe9de1b4a | |||
| 20528f72df | |||
| ad9af9bb3d | |||
| 133308141b |
@@ -0,0 +1,17 @@
|
||||
version: 2.1
|
||||
|
||||
# Define the jobs we want to run for this project
|
||||
jobs:
|
||||
build:
|
||||
docker:
|
||||
- image: cimg/base:2023.03
|
||||
steps:
|
||||
- run: echo "so long and thanks for all the fish"
|
||||
|
||||
# Orchestrate our job run sequence
|
||||
workflows:
|
||||
build_and_test:
|
||||
when:
|
||||
false
|
||||
jobs:
|
||||
- build
|
||||
+25
-20
@@ -8,52 +8,57 @@ on:
|
||||
workflow_call:
|
||||
outputs:
|
||||
semver:
|
||||
description: "The computed version number for this run"
|
||||
description: "The full SemVer 2.0 version of this build, e.g. '3.0.0-alpha.1234' (note: no 'v'-prefix)"
|
||||
value: ${{ jobs.build.outputs.semver }}
|
||||
file_version:
|
||||
description: "The assembly info version for this run"
|
||||
description: "The file info version, e.g. '3.0.0.1234'"
|
||||
value: ${{ jobs.build.outputs.file_version }}
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
semver: ${{ steps.set-version.outputs.semver }}
|
||||
file_version: ${{ steps.set-info-version.outputs.file-version }}
|
||||
file_version: ${{ steps.set-version.outputs.file-version }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Python 3.10
|
||||
uses: actions/setup-python@v3
|
||||
with:
|
||||
python-version: "3.10"
|
||||
|
||||
- name: Install GitVersion
|
||||
uses: gittools/actions/gitversion/setup@v3.0.0
|
||||
with:
|
||||
versionSpec: "5.x"
|
||||
- id: set-version
|
||||
name: Set version to output
|
||||
shell: bash
|
||||
run: |
|
||||
TAG=${{ github.ref_name }}
|
||||
if [[ "${{ github.ref }}" != refs/tags/* ]]; then
|
||||
TAG="v3.0.99.${{ github.run_number }}"
|
||||
fi
|
||||
SEMVER="${TAG#v}"
|
||||
FILE_VERSION=$(echo "$TAG" | sed -E 's/^v([0-9]+\.[0-9]+\.[0-9]+).*/\1/')
|
||||
FILE_VERSION="$FILE_VERSION.${{ github.run_number }}"
|
||||
|
||||
- name: Determine Version
|
||||
id: gitversion
|
||||
uses: gittools/actions/gitversion/execute@v3.0.0
|
||||
echo "semver=$SEMVER" >> "$GITHUB_OUTPUT"
|
||||
echo "file-version=$FILE_VERSION" >> "$GITHUB_OUTPUT"
|
||||
|
||||
echo $SEMVER
|
||||
echo $FILE_VERSION
|
||||
|
||||
- name: Set connector version
|
||||
run: |
|
||||
python patch_version.py ${{steps.gitversion.outputs.semVer}}
|
||||
python patch_version.py ${{steps.set-version.outputs.semver}}
|
||||
|
||||
- uses: montudor/action-zip@v1
|
||||
with:
|
||||
args: zip -q -r sketchup.zip vendor speckle_connector_3/ speckle_connector_3.rb
|
||||
|
||||
- name: ⬆️ Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: output-${{steps.gitversion.outputs.semVer}}
|
||||
name: output-${{steps.set-version.outputs.semver}}
|
||||
path: sketchup.zip
|
||||
retention-days: 1
|
||||
if-no-files-found: error
|
||||
compression-level: 0 # no compression
|
||||
- id: set-version
|
||||
name: Set version to output
|
||||
run: echo "semver=${{steps.gitversion.outputs.semVer}}" >> "$GITHUB_OUTPUT" # version will be retrieved from tag?
|
||||
- id: set-info-version
|
||||
name: Set version to output
|
||||
run: echo "file-version=${{steps.gitversion.outputs.AssemblySemVer}}" >> "$GITHUB_OUTPUT" # version will be retrieved from tag?
|
||||
|
||||
@@ -5,8 +5,8 @@ name: Build and deploy
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["dui3/alpha", "deploy/*"] # Continuous delivery on every long-lived branch
|
||||
tags: ["v3.*"] # Manual delivery on every 3.x tag
|
||||
branches: ["main", "installer-test/**"]
|
||||
tags: ["v3.*.*"] # Manual delivery on every 3.x tag
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@@ -16,18 +16,27 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
env:
|
||||
IS_TAG_BUILD: ${{ github.ref_type == 'tag' }}
|
||||
IS_PUBLIC_RELEASE: ${{ github.ref_type == 'tag' }}
|
||||
steps:
|
||||
- name: 🔫 Trigger Build Installers
|
||||
uses: ALEEF02/workflow-dispatch@v3.0.0
|
||||
- name: 🔫 Trigger Build Installer(s)
|
||||
uses: the-actions-org/workflow-dispatch@v4.0.0
|
||||
with:
|
||||
workflow: Build Sketchup
|
||||
workflow: Build Installers
|
||||
repo: specklesystems/connector-installers
|
||||
token: ${{ secrets.CONNECTORS_GH_TOKEN }}
|
||||
inputs: '{ "run_id": "${{ github.run_id }}", "semver": "${{ needs.build.outputs.semver }}", "file_version": "${{ needs.build.outputs.file_version }}", "public_release": ${{ env.IS_TAG_BUILD }} }'
|
||||
ref: main
|
||||
inputs: '{
|
||||
"run_id": "${{ github.run_id }}",
|
||||
"semver": "${{ needs.build.outputs.semver }}",
|
||||
"file_version": "${{ needs.build.outputs.file_version }}",
|
||||
"repo": "${{ github.repository }}",
|
||||
"is_public_release": ${{ env.IS_PUBLIC_RELEASE }}
|
||||
}'
|
||||
ref: jrm/installer-warnings
|
||||
wait-for-completion: true
|
||||
wait-for-completion-interval: 10s
|
||||
wait-for-completion-timeout: 10m
|
||||
display-workflow-run-url: true
|
||||
display-workflow-run-url-interval: 10s
|
||||
- uses: geekyeggo/delete-artifact@v5
|
||||
with:
|
||||
name: output-*
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
# This workflow uses actions that are not certified by GitHub.
|
||||
# They are provided by a third-party and are governed by
|
||||
# separate terms of service, privacy policy, and support
|
||||
# documentation.
|
||||
# This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake
|
||||
# For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby
|
||||
|
||||
name: Ruby
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
test:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
ruby-version: ['2.7']
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Ruby
|
||||
# To automatically get bug fixes and new Ruby versions for ruby/setup-ruby,
|
||||
# change this to (see https://github.com/ruby/setup-ruby#versioning):
|
||||
# uses: ruby/setup-ruby@v1
|
||||
uses: ruby/setup-ruby@0a29871fe2b0200a17a4497bae54fe5df0d973aa # v1.115.3
|
||||
with:
|
||||
ruby-version: ${{ matrix.ruby-version }}
|
||||
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
|
||||
- name: Run tests
|
||||
run: bundle exec rake
|
||||
+6
-13
@@ -1,18 +1,11 @@
|
||||
workflow: GitFlow/v1
|
||||
next-version: 3.0.0
|
||||
mode: ContinuousDeployment
|
||||
assembly-informational-format: "{Major}.{Minor}.{Patch}-{PreReleaseTag}"
|
||||
mode: ManualDeployment
|
||||
branches:
|
||||
main:
|
||||
regex: ^main$
|
||||
tag: rc
|
||||
label: rc
|
||||
develop:
|
||||
regex: ^development$
|
||||
tag: dev
|
||||
pull-request:
|
||||
tag: pr
|
||||
beta:
|
||||
regex: ^dui3/alpha$
|
||||
tag: beta
|
||||
source-branches:
|
||||
- develop
|
||||
- main
|
||||
label: beta
|
||||
unknown:
|
||||
increment: None
|
||||
|
||||
@@ -115,6 +115,20 @@ You can now open up the repo in VS Code or you can use JetBrains' tools RubyMine
|
||||
|
||||
If you will use VS Code, make sure you've installed the Ruby extension for VS Code.
|
||||
|
||||
#### RubyMine
|
||||
|
||||
To debug:
|
||||
- Add configuration as **'Ruby remote debug'**
|
||||
- Remote host: localhost
|
||||
- Remote port: 7000
|
||||
- Remote root folder: <repo_path>
|
||||
- Local port: 26162
|
||||
- Local root folder: <repo_path>
|
||||
- Run below script
|
||||
|
||||
bundle exec skippy sketchup:debug 2024
|
||||
- When sketchup opened, click Debug button on RubyMine
|
||||
|
||||
### Loading the Speckle Connector Plugin
|
||||
|
||||
1. Find already prepared `speckle_connector_3_loader.rb` file on the `_tools`
|
||||
|
||||
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
@@ -3,6 +3,7 @@
|
||||
require 'JSON'
|
||||
require_relative '../ext/sqlite3'
|
||||
require_relative '../constants/path_constants'
|
||||
require_relative '../preferences/preferences'
|
||||
|
||||
module SpeckleConnector3
|
||||
# Accounts to communicate with models on user's account.
|
||||
@@ -11,11 +12,11 @@ module SpeckleConnector3
|
||||
def self.load_accounts
|
||||
db_path = SPECKLE_ACCOUNTS_DB_PATH
|
||||
unless File.exist?(db_path)
|
||||
raise(
|
||||
IOError,
|
||||
"No Accounts db found. Please read the guide for different options for adding your account:\n
|
||||
https://speckle.guide/user/manager.html#adding-accounts"
|
||||
)
|
||||
File.new(SPECKLE_ACCOUNTS_DB_PATH, "w")
|
||||
db = Sqlite3::Database.new(SPECKLE_ACCOUNTS_DB_PATH)
|
||||
Preferences.create_objects_table(db)
|
||||
db.close
|
||||
return []
|
||||
end
|
||||
|
||||
db = Sqlite3::Database.new(db_path)
|
||||
@@ -24,6 +25,27 @@ module SpeckleConnector3
|
||||
rows.map { |row| JSON.parse(row[1]) }
|
||||
end
|
||||
|
||||
def self.remove_account(account_id)
|
||||
db_path = SPECKLE_ACCOUNTS_DB_PATH
|
||||
unless File.exist?(db_path)
|
||||
raise(
|
||||
IOError,
|
||||
"No Accounts db found. Please read the guide for different options for adding your account:\n
|
||||
https://speckle.guide/user/manager.html#adding-accounts"
|
||||
)
|
||||
end
|
||||
db = Sqlite3::Database.new(db_path)
|
||||
|
||||
begin
|
||||
db.exec("DELETE FROM objects WHERE hash = '#{account_id}'")
|
||||
puts "Account with hash #{account_id} has been removed."
|
||||
rescue StandardError => e
|
||||
puts "An error occurred: #{e}"
|
||||
ensure
|
||||
db.close
|
||||
end
|
||||
end
|
||||
|
||||
def self.get_account_by_id(id)
|
||||
accounts = load_accounts
|
||||
accounts.select { |acc| acc['id'] == id }[0]
|
||||
|
||||
+4
-4
@@ -1,8 +1,8 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative 'action'
|
||||
require_relative '../accounts/accounts'
|
||||
require_relative 'load_saved_streams'
|
||||
require_relative '../action'
|
||||
require_relative '../../accounts/accounts'
|
||||
require_relative '../load_saved_streams'
|
||||
|
||||
module SpeckleConnector3
|
||||
module Actions
|
||||
@@ -12,7 +12,7 @@ module SpeckleConnector3
|
||||
# @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
|
||||
accounts_data = SpeckleConnector3::Accounts.load_accounts
|
||||
js_script = "accountsBinding.receiveResponse('#{resolve_id}', #{accounts_data.to_json})"
|
||||
state.with_add_queue_js_command('getAccounts', js_script)
|
||||
end
|
||||
@@ -0,0 +1,20 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative '../action'
|
||||
require_relative '../../accounts/accounts'
|
||||
require_relative '../load_saved_streams'
|
||||
|
||||
module SpeckleConnector3
|
||||
module Actions
|
||||
# Action to remove account from database.
|
||||
class RemoveAccount < 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, account_id)
|
||||
SpeckleConnector3::Accounts.remove_account(account_id)
|
||||
js_script = "accountsBinding.receiveResponse('#{resolve_id}')"
|
||||
state.with_add_queue_js_command('removeAccount', js_script)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -17,20 +17,28 @@ module SpeckleConnector3
|
||||
def self.update_state(state, resolve_id, data)
|
||||
model_card_id = data['modelCardId']
|
||||
account_id = data['accountId']
|
||||
server_url = data['serverUrl']
|
||||
workspace_id = data['workspaceId']
|
||||
workspace_slug = data['workspaceSlug']
|
||||
project_id = data['projectId']
|
||||
model_id = data['modelId']
|
||||
project_name = data['projectName']
|
||||
model_name = data['modelName']
|
||||
expired = data['expired']
|
||||
selected_version_id = data['selectedVersionId']
|
||||
selected_version_source_app = data['selectedVersionSourceApp']
|
||||
selected_version_user_id = data['selectedVersionUserId']
|
||||
latest_version_id = data['latestVersionId']
|
||||
latest_version_source_app = data['latestVersionSourceApp']
|
||||
latest_version_user_id = data['latestVersionUserId']
|
||||
has_dismissed_update_warning = data['hasDismissedUpdateWarning']
|
||||
baked_object_ids = data['bakedObjectIds'].nil? ? nil : data['bakedObjectIds'].values
|
||||
|
||||
receive_card = Cards::ReceiveCard.new(model_card_id, account_id,
|
||||
receive_card = Cards::ReceiveCard.new(model_card_id, account_id, server_url, workspace_id, workspace_slug,
|
||||
project_id, model_id,
|
||||
project_name, model_name,
|
||||
selected_version_id, latest_version_id,
|
||||
selected_version_id, selected_version_source_app, selected_version_user_id,
|
||||
latest_version_id, latest_version_source_app, latest_version_user_id,
|
||||
has_dismissed_update_warning, expired, baked_object_ids)
|
||||
SketchupModel::Dictionary::ModelCardDictionaryHandler
|
||||
.save_receive_card_to_model(receive_card, state.sketchup_state.sketchup_model)
|
||||
|
||||
@@ -22,6 +22,9 @@ module SpeckleConnector3
|
||||
send_card = Cards::SendCard.new(
|
||||
data['modelCardId'],
|
||||
data['accountId'],
|
||||
data['serverUrl'],
|
||||
data['workspaceId'],
|
||||
data['workspaceSlug'],
|
||||
data['projectId'],
|
||||
data['projectName'],
|
||||
data['modelId'],
|
||||
|
||||
@@ -21,6 +21,9 @@ module SpeckleConnector3
|
||||
send_card = Cards::SendCard.new(
|
||||
id,
|
||||
card['account_id'],
|
||||
card['server_url'],
|
||||
card['workspace_id'],
|
||||
card['workspace_slug'],
|
||||
card['project_id'],
|
||||
card['project_name'],
|
||||
card['model_id'],
|
||||
@@ -35,6 +38,9 @@ module SpeckleConnector3
|
||||
{
|
||||
modelCardId: send_card.model_card_id,
|
||||
accountId: send_card.account_id,
|
||||
serverUrl: send_card.server_url,
|
||||
workspaceId: send_card.workspace_id,
|
||||
workspaceSlug: send_card.workspace_slug,
|
||||
projectId: send_card.project_id,
|
||||
modelId: send_card.model_id,
|
||||
sendFilter: send_card.send_filter,
|
||||
@@ -49,9 +55,11 @@ module SpeckleConnector3
|
||||
|
||||
# TODO: CONVERTER_V2: Extract into new actions
|
||||
receive_cards = receive_cards_hash.collect do |id, card|
|
||||
receive_card = Cards::ReceiveCard.new(id, card['account_id'], card['project_id'], card['model_id'],
|
||||
receive_card = Cards::ReceiveCard.new(id, card['account_id'], card['server_url'], card['workspace_id'], card['workspace_slug'], card['project_id'], card['model_id'],
|
||||
card['project_name'], card['model_name'], card['selected_version_id'],
|
||||
card['latest_version_id'], card['has_dismissed_update_warning'],
|
||||
card['selected_version_source_app'], card['selected_version_user_id'],
|
||||
card['latest_version_id'], card['latest_version_source_app'],
|
||||
card['latest_version_user_id'], card['has_dismissed_update_warning'],
|
||||
card['expired'], card['baked_object_ids'])
|
||||
|
||||
new_speckle_state = state.speckle_state.with_receive_card(receive_card)
|
||||
@@ -59,12 +67,19 @@ module SpeckleConnector3
|
||||
{
|
||||
modelCardId: receive_card.model_card_id,
|
||||
accountId: receive_card.account_id,
|
||||
serverUrl: receive_card.server_url,
|
||||
workspaceId: receive_card.workspace_id,
|
||||
workspaceSlug: receive_card.workspace_slug,
|
||||
projectId: receive_card.project_id,
|
||||
modelId: receive_card.model_id,
|
||||
projectName: receive_card.project_name,
|
||||
modelName: receive_card.model_name,
|
||||
selectedVersionId: receive_card.selected_version_id,
|
||||
selectedVersionSourceApp: receive_card.selected_version_source_app,
|
||||
selectedVersionUserId: receive_card.selected_version_user_id,
|
||||
latestVersionId: receive_card.latest_version_id,
|
||||
latestVersionSourceApp: receive_card.latest_version_source_app,
|
||||
latestVersionUserId: receive_card.latest_version_user_id,
|
||||
hasDismissedUpdateWarning: receive_card.has_dismissed_update_warning,
|
||||
expired: receive_card.expired,
|
||||
bakedObjectIds: receive_card.baked_object_ids,
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
# 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 SpeckleConnector3
|
||||
module Actions
|
||||
# Action to remove cards.
|
||||
class RemoveModels < 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)
|
||||
data.each do |model_card|
|
||||
SketchupModel::Dictionary::ModelCardDictionaryHandler.remove_card_dict(state.sketchup_state.sketchup_model, model_card)
|
||||
new_speckle_state = if model_card['typeDiscriminator'] == 'ReceiverModelCard'
|
||||
state.speckle_state.without_receive_card(model_card['id'])
|
||||
else
|
||||
state.speckle_state.without_send_card(model_card['id'])
|
||||
end
|
||||
state = state.with_speckle_state(new_speckle_state)
|
||||
end
|
||||
|
||||
# Resolve promise
|
||||
js_script = "baseBinding.receiveResponse('#{resolve_id}')"
|
||||
state.with_add_queue_js_command('removeModels', js_script)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,21 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative '../action'
|
||||
require_relative '../../preferences/preferences'
|
||||
|
||||
module SpeckleConnector3
|
||||
module Actions
|
||||
class GetUserSelectedAccountId < 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)
|
||||
user_selected_account_id = Preferences.get_user_selected_account_id
|
||||
accountsConfig = {
|
||||
userSelectedAccountId: user_selected_account_id
|
||||
}
|
||||
js_script = "configBinding.receiveResponse('#{resolve_id}', #{accountsConfig.to_json})"
|
||||
state.with_add_queue_js_command('getUserSelectedAccountId', js_script)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,18 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative '../action'
|
||||
require_relative '../../preferences/preferences'
|
||||
|
||||
module SpeckleConnector3
|
||||
module Actions
|
||||
class SetUserSelectedAccountId < 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, account_id)
|
||||
Preferences.set_user_selected_account_id(account_id)
|
||||
js_script = "configBinding.receiveResponse('#{resolve_id}')"
|
||||
state.with_add_queue_js_command('setUserSelectedAccountId', js_script)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -26,7 +26,8 @@ module SpeckleConnector3
|
||||
unless path.nil?
|
||||
new_path_entities = path[-1].definition.entities
|
||||
new_path_entities.add_observer(observers[ENTITIES_OBSERVER])
|
||||
edges = new_path_entities.grep(Sketchup::Edge)
|
||||
# attach observers to only orphan edges since face edges can be detected via face changes.
|
||||
edges = new_path_entities.grep(Sketchup::Edge).filter { |edge| edge.faces.none? }
|
||||
edges.each do |edge|
|
||||
edge.add_observer(observers[ENTITY_OBSERVER])
|
||||
edge.start.add_observer(observers[ENTITY_OBSERVER])
|
||||
|
||||
@@ -27,14 +27,14 @@ module SpeckleConnector3
|
||||
new_state = InitializeMaterials.update_state(new_state)
|
||||
|
||||
# Read speckle entities
|
||||
new_speckle_entities = SketchupModel::Reader::SpeckleEntitiesReader.read(sketchup_model.entities)
|
||||
new_speckle_state = new_state.speckle_state.with_speckle_entities(Immutable::Hash.new(new_speckle_entities))
|
||||
#new_speckle_entities = SketchupModel::Reader::SpeckleEntitiesReader.read(sketchup_model.entities)
|
||||
#new_speckle_state = new_state.speckle_state.with_speckle_entities(Immutable::Hash.new(new_speckle_entities))
|
||||
# POC: Reconsider it when we will do caching between sessions!
|
||||
new_speckle_state = new_speckle_state.with_empty_object_references
|
||||
# new_speckle_state = new_speckle_state.with_empty_object_references
|
||||
# Read mapped entities
|
||||
new_mapped_entities = SketchupModel::Reader::MapperReader.read_mapped_entities(sketchup_model.entities)
|
||||
new_speckle_state = new_speckle_state.with_mapped_entities(Immutable::Hash.new(new_mapped_entities))
|
||||
new_state = new_state.with_speckle_state(new_speckle_state)
|
||||
# new_mapped_entities = SketchupModel::Reader::MapperReader.read_mapped_entities(sketchup_model.entities)
|
||||
# new_speckle_state = new_speckle_state.with_mapped_entities(Immutable::Hash.new(new_mapped_entities))
|
||||
# new_state = new_state.with_speckle_state(new_speckle_state)
|
||||
|
||||
# Read preferences from database and model.
|
||||
preferences = Preferences.read_preferences(new_state.sketchup_state.sketchup_model)
|
||||
|
||||
@@ -18,6 +18,7 @@ module SpeckleConnector3
|
||||
converter = Converters::ToNativeV2.new(state,
|
||||
root_obj['instanceDefinitionProxies'] || [],
|
||||
root_obj['renderMaterialProxies'] || [],
|
||||
root_obj['levelProxies'] || [],
|
||||
source_application,
|
||||
model_card)
|
||||
start_time = Time.now.to_f
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative '../action'
|
||||
require_relative '../../convertors/to_native'
|
||||
require_relative '../../convertors/to_native_v2'
|
||||
|
||||
module SpeckleConnector3
|
||||
module Actions
|
||||
# After objects send to server.
|
||||
class AfterSendObjects < 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, referenced_object_id)
|
||||
model_card = state.speckle_state.send_cards[model_card_id]
|
||||
account = Accounts.get_account_by_id(model_card.account_id)
|
||||
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,
|
||||
referencedObjectId: referenced_object_id,
|
||||
sendConversionResults: state.speckle_state.conversion_results[model_card_id]
|
||||
}
|
||||
|
||||
after_send_object_js_script = "sendBinding.emit('createVersionViaBrowser', #{args.to_json})"
|
||||
state = state.with_add_queue_js_command('createVersionViaBrowser', after_send_object_js_script)
|
||||
|
||||
resolve_js_script = "sendBinding.receiveResponse('#{resolve_id}')"
|
||||
state.with_add_queue_js_command('afterSendObject', resolve_js_script)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -17,7 +17,9 @@ module SpeckleConnector3
|
||||
# @return [States::State] the new updated state object
|
||||
def self.update_state(state, resolve_id, model_card_id)
|
||||
# Set active path always to model to be safe always. Later we can address it
|
||||
start_time = Time.now.to_f
|
||||
state.sketchup_state.sketchup_model.active_path = nil
|
||||
units = Converters::SKETCHUP_UNITS[state.sketchup_state.length_units]
|
||||
model_card = state.speckle_state.send_cards[model_card_id]
|
||||
unless model_card.send_filter.selected_object_ids.any?
|
||||
resolve_js_script = "sendBinding.receiveResponse('#{resolve_id}')"
|
||||
@@ -35,7 +37,7 @@ module SpeckleConnector3
|
||||
end
|
||||
|
||||
unpacked_entities = SketchupModel::Definitions::DefinitionManager
|
||||
.new(Converters::SKETCHUP_UNITS[state.sketchup_state.length_units])
|
||||
.new(units)
|
||||
.unpack_entities(entities)
|
||||
|
||||
unpacked_materials = SketchupModel::Materials::MaterialManager.new.unpack_materials(entities)
|
||||
@@ -50,34 +52,75 @@ module SpeckleConnector3
|
||||
base[:instanceDefinitionProxies] = unpacked_entities.instance_definition_proxies
|
||||
base[:renderMaterialProxies] = unpacked_materials
|
||||
base[:colorProxies] = unpacked_colors
|
||||
base[:units] = units
|
||||
|
||||
elapsed_time = (Time.now.to_f - start_time).round(3)
|
||||
puts "==== Converting objects executed in #{elapsed_time} sec ===="
|
||||
|
||||
start_time = Time.now.to_f
|
||||
|
||||
sender_progress_args = {
|
||||
modelCardId: model_card_id,
|
||||
progress: {
|
||||
progress: nil,
|
||||
status: 'Serializing'
|
||||
}
|
||||
}
|
||||
state.instant_message_sender.call("sendBinding.emit('setModelProgress', #{sender_progress_args.to_json})")
|
||||
|
||||
id, batches, refs = converter.serialize(base, state.user_state.preferences)
|
||||
elapsed_time = (Time.now.to_f - start_time).round(3)
|
||||
puts "==== Serializing objects executed in #{elapsed_time} sec ===="
|
||||
new_speckle_state = new_speckle_state.with_object_references(model_card.project_id, refs)
|
||||
new_speckle_state = new_speckle_state.with_empty_changed_entity_persistent_ids
|
||||
new_speckle_state = new_speckle_state.with_empty_changed_entity_ids
|
||||
|
||||
puts "Cached/Total object: #{converter.cached_object_count}/#{converter.object_count}"
|
||||
|
||||
puts("converted #{base.count} objects for stream #{model_card.project_id}")
|
||||
|
||||
state = state.with_speckle_state(new_speckle_state)
|
||||
|
||||
resolve_js_script = "sendBinding.receiveResponse('#{resolve_id}')"
|
||||
state = state.with_add_queue_js_command('send', resolve_js_script)
|
||||
args = {
|
||||
modelCardId: model_card_id,
|
||||
projectId: model_card.project_id,
|
||||
modelId: model_card.model_id,
|
||||
token: account['token'],
|
||||
serverUrl: account['serverInfo']['url'],
|
||||
accountId: model_card.account_id,
|
||||
message: model_card.message,
|
||||
sendConversionResults: converter.conversion_results,
|
||||
sendObject: {
|
||||
id: id,
|
||||
batches: batches
|
||||
# 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,
|
||||
# sendConversionResults: converter.conversion_results,
|
||||
# sendObject: {
|
||||
# id: id,
|
||||
# batches: batches
|
||||
# }
|
||||
# }
|
||||
# js_script = "sendBinding.emit('sendViaBrowser', #{args.to_json})"
|
||||
# state.with_add_queue_js_command('sendViaBrowser', js_script)
|
||||
|
||||
# store conversion results in state to pick up later
|
||||
new_speckle_state = state.speckle_state.with_conversion_results(model_card_id, converter.conversion_results)
|
||||
state = state.with_speckle_state(new_speckle_state)
|
||||
|
||||
total_batch_count = batches.count
|
||||
batches.each_with_index do |batch, i|
|
||||
current_batch = i + 1
|
||||
args = {
|
||||
modelCardId: model_card_id,
|
||||
projectId: model_card.project_id,
|
||||
token: account['token'],
|
||||
serverUrl: account['serverInfo']['url'],
|
||||
batch: batch,
|
||||
currentBatch:current_batch,
|
||||
totalBatch: total_batch_count,
|
||||
referencedObjectId: id
|
||||
}
|
||||
}
|
||||
js_script = "sendBinding.emit('sendViaBrowser', #{args.to_json})"
|
||||
state.with_add_queue_js_command('sendViaBrowser', js_script)
|
||||
js_script = "sendBinding.emit('sendBatchViaBrowser', #{args.to_json})"
|
||||
state = state.with_add_queue_js_command("sendBatchViaBrowser_#{current_batch}", js_script)
|
||||
end
|
||||
state
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -24,25 +24,40 @@ module SpeckleConnector3
|
||||
# @return [String] model name of the card.
|
||||
attr_reader :model_name
|
||||
|
||||
# @return [String] workspace id of the card.
|
||||
attr_reader :workspace_id
|
||||
|
||||
# @return [String] workspace slug of the card.
|
||||
attr_reader :workspace_slug
|
||||
|
||||
# @return [String] server url of the card.
|
||||
attr_reader :server_url
|
||||
|
||||
# @return [Boolean] card is valid or not.
|
||||
attr_reader :valid
|
||||
|
||||
# rubocop:disable Metrics/ParameterLists
|
||||
def initialize(model_card_id, account_id, project_id, project_name, model_id, model_name)
|
||||
def initialize(model_card_id, account_id, server_url, workspace_id, workspace_slug, project_id, project_name, model_id, model_name)
|
||||
super()
|
||||
@model_card_id = model_card_id
|
||||
@account_id = account_id
|
||||
@workspace_id = workspace_id
|
||||
@workspace_slug = workspace_slug
|
||||
@project_id = project_id
|
||||
@project_name = project_name
|
||||
@model_id = model_id
|
||||
@model_name = model_name
|
||||
@server_url = server_url
|
||||
@valid = true
|
||||
self[:model_card_id] = model_card_id
|
||||
self[:account_id] = account_id
|
||||
self[:workspace_id] = workspace_id
|
||||
self[:workspace_slug] = workspace_slug
|
||||
self[:project_id] = project_id
|
||||
self[:project_name] = project_name
|
||||
self[:model_id] = model_id
|
||||
self[:model_name] = model_name
|
||||
self[:server_url] = server_url
|
||||
self[:valid] = @valid
|
||||
end
|
||||
# rubocop:enable Metrics/ParameterLists
|
||||
|
||||
@@ -14,9 +14,21 @@ module SpeckleConnector3
|
||||
# @return [String] selected version id to receive
|
||||
attr_reader :selected_version_id
|
||||
|
||||
# @return [String] selected version source app
|
||||
attr_reader :selected_version_source_app
|
||||
|
||||
# @return [String] selected version user id
|
||||
attr_reader :selected_version_user_id
|
||||
|
||||
# @return [String] latest version id to receive
|
||||
attr_reader :latest_version_id
|
||||
|
||||
# @return [String] latest version source app
|
||||
attr_reader :latest_version_source_app
|
||||
|
||||
# @return [String] latest version user id
|
||||
attr_reader :latest_version_user_id
|
||||
|
||||
# @return [Boolean] whether new version notification is dismissed or not
|
||||
attr_reader :has_dismissed_update_warning
|
||||
|
||||
@@ -36,32 +48,47 @@ module SpeckleConnector3
|
||||
def initialize(
|
||||
model_card_id,
|
||||
account_id,
|
||||
server_url,
|
||||
workspace_id,
|
||||
workspace_slug,
|
||||
project_id,
|
||||
model_id,
|
||||
project_name,
|
||||
model_name,
|
||||
selected_version_id,
|
||||
selected_version_source_app,
|
||||
selected_version_user_id,
|
||||
latest_version_id,
|
||||
latest_version_source_app,
|
||||
latest_version_user_id,
|
||||
has_dismissed_update_warning,
|
||||
expired,
|
||||
baked_object_ids = nil
|
||||
)
|
||||
super(model_card_id, account_id, project_id, project_name, model_id, model_name)
|
||||
super(model_card_id, account_id, server_url, workspace_id, workspace_slug, project_id, project_name, model_id, model_name)
|
||||
@selected_version_id = selected_version_id
|
||||
@selected_version_source_app = selected_version_source_app
|
||||
@selected_version_user_id = selected_version_user_id
|
||||
@latest_version_id = latest_version_id
|
||||
@latest_version_source_app = latest_version_source_app
|
||||
@latest_version_user_id = latest_version_user_id
|
||||
@has_dismissed_update_warning = has_dismissed_update_warning
|
||||
@baked_object_ids = baked_object_ids
|
||||
self[:selected_version_id] = selected_version_id
|
||||
self[:has_dismissed_update_warning] = has_dismissed_update_warning
|
||||
self[:latest_version_id] = latest_version_id
|
||||
self[:model_name] = model_name
|
||||
self[:project_name] = project_name
|
||||
self[:expired] = expired
|
||||
self[:baked_object_ids] = @baked_object_ids
|
||||
@expired = expired
|
||||
@model_name = model_name
|
||||
@project_name = project_name
|
||||
@type_discriminator = 'ReceiverModelCard'
|
||||
self[:selected_version_id] = selected_version_id
|
||||
self[:selected_version_source_app] = selected_version_source_app
|
||||
self[:selected_version_user_id] = selected_version_user_id
|
||||
self[:has_dismissed_update_warning] = has_dismissed_update_warning
|
||||
self[:latest_version_id] = latest_version_id
|
||||
self[:latest_version_source_app] = latest_version_source_app
|
||||
self[:latest_version_user_id] = latest_version_user_id
|
||||
self[:model_name] = model_name
|
||||
self[:project_name] = project_name
|
||||
self[:expired] = expired
|
||||
self[:baked_object_ids] = @baked_object_ids
|
||||
self[:type_discriminator] = @type_discriminator
|
||||
end
|
||||
# rubocop:enable Metrics/ParameterLists
|
||||
|
||||
@@ -26,6 +26,9 @@ module SpeckleConnector3
|
||||
def initialize(
|
||||
model_card_id,
|
||||
account_id,
|
||||
server_url,
|
||||
workspace_id,
|
||||
workspace_slug,
|
||||
project_id,
|
||||
project_name,
|
||||
model_id,
|
||||
@@ -34,7 +37,7 @@ module SpeckleConnector3
|
||||
send_filter,
|
||||
send_settings
|
||||
)
|
||||
super(model_card_id, account_id, project_id, project_name, model_id, model_name)
|
||||
super(model_card_id, account_id, server_url, workspace_id, workspace_slug, project_id, project_name, model_id, model_name)
|
||||
@send_filter = send_filter
|
||||
@send_settings = send_settings
|
||||
@latest_created_version_id = latest_created_version_id
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative 'card'
|
||||
|
||||
module SpeckleConnector3
|
||||
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
|
||||
# # frozen_string_literal: true
|
||||
#
|
||||
# require_relative 'card'
|
||||
#
|
||||
# module SpeckleConnector3
|
||||
# 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
|
||||
|
||||
@@ -26,7 +26,7 @@ module SpeckleConnector3
|
||||
_run(*parameters)
|
||||
end
|
||||
rescue StandardError => e
|
||||
action = Actions::HandleError.new(e, @binding.name, @action, parameters)
|
||||
action = Actions::HandleError.new(e, @binding.nil? ? "unknown binding" : @binding.name, @action, parameters)
|
||||
app.update_state!(action)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -22,7 +22,7 @@ module SpeckleConnector3
|
||||
SPECKLE_DUI3 = 'speckle_dui3'
|
||||
|
||||
def dialog_title
|
||||
"Speckle (Beta) for SketchUp"
|
||||
"Speckle for SketchUp"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
@@ -9,6 +9,8 @@ module SpeckleConnector3
|
||||
# @return [UI::Command] the command that can be added to Sketchup menu or toolbar
|
||||
def self.sketchup_command(command, menu_text)
|
||||
UI::Command.new(menu_text) do
|
||||
puts '### COMMAND CALLED BY MENU ###'
|
||||
puts "Name: #{menu_text}"
|
||||
command.run
|
||||
end
|
||||
end
|
||||
|
||||
@@ -65,10 +65,10 @@ module SpeckleConnector3
|
||||
|
||||
def self.initialize_dui3_speckle_command(app)
|
||||
cmd = MenuCommandHandler.sketchup_command(
|
||||
InitializeDUI3Speckle.new(app, nil), 'Initialize Speckle (Beta)'
|
||||
InitializeDUI3Speckle.new(app, nil), 'Initialize Speckle'
|
||||
)
|
||||
cmd.tooltip = 'Speckle (Beta) for SketchUp'
|
||||
cmd.status_bar_text = 'Opens the Speckle Connector (Beta)'
|
||||
cmd.tooltip = 'Speckle for SketchUp'
|
||||
cmd.status_bar_text = 'Opens the Speckle Connector'
|
||||
cmd.small_icon = '../../img/s2logo_dui3.png'
|
||||
cmd.large_icon = '../../img/s2logo_dui3.png'
|
||||
cmd
|
||||
|
||||
@@ -53,5 +53,10 @@ module SpeckleConnector3
|
||||
SPECKLE_CORE_MODELS_INSTANCES_INSTANCE_PROXY = 'Speckle.Core.Models.Instances.InstanceProxy'
|
||||
SPECKLE_CORE_MODELS_INSTANCES_INSTANCE_DEFINITION_PROXY = 'Speckle.Core.Models.Instances.InstanceDefinitionProxy'
|
||||
SPECKLE_CORE_OTHER_RENDER_MATERIAL_PROXY = 'Objects.Other.RenderMaterialProxy'
|
||||
SPECKLE_CORE_OTHER_LEVEL_PROXY = 'Objects.Other.LevelProxy'
|
||||
SPECKLE_CORE_OTHER_COLOR_PROXY = 'Speckle.Core.Models.Proxies.ColorProxy'
|
||||
|
||||
# DATA OBJECTS
|
||||
SPECKLE_OBJECT_DATA_OBJECT = 'Objects.Data.DataObject'
|
||||
SPECKLE_OBJECT_DATA_OBJECT_REVIT = 'Objects.Data.DataObject:Objects.Data.RevitObject'
|
||||
end
|
||||
|
||||
@@ -22,6 +22,7 @@ require_relative '../speckle_objects/built_elements/network'
|
||||
require_relative '../speckle_objects/speckle/core/models/collection'
|
||||
require_relative '../speckle_objects/speckle/core/models/gis_layer_collection'
|
||||
require_relative '../speckle_objects/instance_definition_proxy'
|
||||
require_relative '../speckle_objects/data/revit_data_object'
|
||||
require_relative '../sketchup_model/dictionary/speckle_entity_dictionary_handler'
|
||||
require_relative '../ui_data/report/conversion_result'
|
||||
require_relative '../convertors/conversion_error'
|
||||
@@ -48,16 +49,19 @@ module SpeckleConnector3
|
||||
# @return [Array<SpeckleObjects::InstanceDefinitionProxy>]
|
||||
attr_reader :root_definition_proxies
|
||||
|
||||
attr_reader :level_proxies
|
||||
|
||||
# @return [Array<SpeckleObjects::RenderMaterialProxy>]
|
||||
attr_accessor :root_render_material_proxies
|
||||
|
||||
# @param definition_proxies [Array<SpeckleObjects::InstanceDefinitionProxy>]
|
||||
# @param render_material_proxies [Array<SpeckleObjects::RenderMaterialProxy>]
|
||||
# @param model_card [SpeckleConnector3::Cards::ReceiveCard]
|
||||
def initialize(state, definition_proxies, render_material_proxies, source_app, model_card)
|
||||
def initialize(state, definition_proxies, render_material_proxies, level_proxies, source_app, model_card)
|
||||
super(state, model_card)
|
||||
@root_definition_proxies = definition_proxies
|
||||
@root_render_material_proxies = render_material_proxies
|
||||
@level_proxies = level_proxies
|
||||
@definition_proxies = {}
|
||||
@source_app = source_app.downcase
|
||||
@converted_faces = []
|
||||
@@ -94,6 +98,8 @@ module SpeckleConnector3
|
||||
LAYER_COLLECTION = SpeckleObjects::Speckle::Core::Models::LayerCollection
|
||||
GIS_LAYER_COLLECTION = SpeckleObjects::Speckle::Core::Models::GisLayerCollection
|
||||
|
||||
REVIT_DATA_OBJECT = SpeckleObjects::RevitDataObject
|
||||
|
||||
BASE_OBJECT_PROPS = %w[applicationId id speckle_type].freeze
|
||||
CONVERTABLE_SPECKLE_TYPES = %w[
|
||||
Objects.Geometry.Line
|
||||
@@ -117,6 +123,7 @@ module SpeckleConnector3
|
||||
Speckle.Core.Models.Collections.Collection:Speckle.Core.Models.Collections.Layer
|
||||
Speckle.Core.Models.Collections.Collection:Objects.GIS.RasterLayer
|
||||
Speckle.Core.Models.Collections.Collection:Objects.GIS.VectorLayer
|
||||
Objects.Data.DataObject:Objects.Data.RevitObject
|
||||
].freeze
|
||||
|
||||
def from_revit
|
||||
@@ -153,6 +160,11 @@ module SpeckleConnector3
|
||||
|
||||
definition_name = proxy['name']
|
||||
definition = state.sketchup_state.sketchup_model.definitions.add(definition_name)
|
||||
properties = proxy['properties'] || proxy['sketchup_attributes']
|
||||
unless properties.nil?
|
||||
SketchupModel::Dictionary::BaseDictionaryHandler
|
||||
.attribute_dictionaries_to_native(definition, properties['dictionaries'])
|
||||
end
|
||||
definition.behavior.always_face_camera = proxy['alwaysFaceCamera'] if proxy['alwaysFaceCamera']
|
||||
@definition_proxies[proxy['applicationId']] = SpeckleObjects::InstanceDefinitionProxy.new(
|
||||
definition,
|
||||
@@ -190,6 +202,7 @@ module SpeckleConnector3
|
||||
default_commit_layer = sketchup_model.layers.layers.find { |layer| layer.display_name == '@Untagged' }
|
||||
|
||||
traverse_commit_object(obj, default_commit_layer, @entities_to_fill)
|
||||
create_levels
|
||||
create_levels_from_section_planes
|
||||
check_hiding_layers_needed
|
||||
try_create_instance
|
||||
@@ -372,6 +385,10 @@ module SpeckleConnector3
|
||||
# rubocop:enable Metrics/PerceivedComplexity
|
||||
|
||||
def speckle_object_to_native(obj)
|
||||
if SPECKLE_OBJECTS_WITH_NATIVE_CONVERSION[obj['speckle_type']]
|
||||
return SPECKLE_OBJECTS_WITH_NATIVE_CONVERSION[obj['speckle_type']]
|
||||
end
|
||||
|
||||
return DISPLAY_VALUE.method(:to_native) unless obj['displayValue'].nil? && obj['@displayValue'].nil?
|
||||
|
||||
SPECKLE_OBJECT_TO_NATIVE[obj['speckle_type']]
|
||||
@@ -404,6 +421,10 @@ module SpeckleConnector3
|
||||
SPECKLE_CORE_MODELS_COLLECTION_VECTOR_LAYER => GIS_LAYER_COLLECTION.method(:to_native)
|
||||
}.freeze
|
||||
|
||||
SPECKLE_OBJECTS_WITH_NATIVE_CONVERSION = {
|
||||
SPECKLE_OBJECT_DATA_OBJECT_REVIT => REVIT_DATA_OBJECT.method(:to_native)
|
||||
}
|
||||
|
||||
def entities_to_bake(obj, entities)
|
||||
entities_to_bake = entities
|
||||
object_id = obj['applicationId'].to_s # TODO: CONVERTER_V2: faces have integer application id..!!?
|
||||
@@ -446,15 +467,15 @@ module SpeckleConnector3
|
||||
end
|
||||
faces = converted_entities.select { |e| e.is_a?(Sketchup::Face) }
|
||||
@converted_faces += faces if faces.any?
|
||||
if from_revit
|
||||
# Create levels as section planes if they exists
|
||||
create_levels(state, obj)
|
||||
# Create layers from category of object and place object in it
|
||||
# create_layers_from_categories(state, obj, converted_entities)
|
||||
end
|
||||
# Create speckle entities from sketchup entities to achieve continuous traversal.
|
||||
|
||||
converted_entities.each do |converted|
|
||||
if converted.is_a?(Sketchup::ComponentDefinition)
|
||||
next # no need to report definitions
|
||||
end
|
||||
if !from_sketchup && converted.is_a?(Sketchup::Face)
|
||||
next # Otherwise we have many noise in report and causing delay post-receive
|
||||
end
|
||||
@conversion_results.push(UiData::Report::ConversionResult.new(UiData::Report::ConversionStatus::SUCCESS,
|
||||
obj['id'],
|
||||
obj['speckle_type'],
|
||||
@@ -500,25 +521,27 @@ module SpeckleConnector3
|
||||
nil
|
||||
end
|
||||
|
||||
# @param state [States::State] state of the speckle application
|
||||
def create_levels(state, speckle_object)
|
||||
level = speckle_object['level']
|
||||
return state if level.nil?
|
||||
return state unless level['speckle_type'].include?('Objects.BuiltElements.Level')
|
||||
def create_levels
|
||||
return if level_proxies.empty?
|
||||
|
||||
level_name = level['name'] || level['id']
|
||||
is_exist = @entities_to_fill.grep(Sketchup::SectionPlane).any? { |sp| sp.name == level_name }
|
||||
return state if is_exist
|
||||
level_proxies.each do |level_proxy|
|
||||
level_data_object = level_proxy['value']
|
||||
name = level_data_object['name'] || level_data_object['id']
|
||||
units = level_data_object['units']
|
||||
elevation_raw = level_data_object['elevation']
|
||||
|
||||
elevation = SpeckleObjects::Geometry.length_to_native(level['elevation'], level['units'])
|
||||
is_exist = @entities_to_fill.grep(Sketchup::SectionPlane).any? { |sp| sp.name == name }
|
||||
next if is_exist
|
||||
|
||||
section_plane = @entities_to_fill.add_section_plane([0, 0, elevation + LEVEL_SHIFT_VALUE], [0, 0, -1])
|
||||
section_plane.name = level_name
|
||||
section_plane.layer = levels_layer
|
||||
SketchupModel::Dictionary::SpeckleEntityDictionaryHandler.write_initial_base_data(
|
||||
section_plane, level['applicationId'], level['id'], level['speckle_type'], [], model_card.project_id
|
||||
)
|
||||
state
|
||||
elevation = SpeckleObjects::Geometry.length_to_native(elevation_raw, units)
|
||||
|
||||
section_plane = @entities_to_fill.add_section_plane([0, 0, elevation + LEVEL_SHIFT_VALUE], [0, 0, -1])
|
||||
section_plane.name = name
|
||||
section_plane.layer = levels_layer
|
||||
SketchupModel::Dictionary::SpeckleEntityDictionaryHandler.write_initial_base_data(
|
||||
section_plane, level_data_object['applicationId'], level_data_object['id'], level_data_object['speckle_type'], [], model_card.project_id
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
# rubocop:enable Metrics/ClassLength
|
||||
|
||||
@@ -37,12 +37,17 @@ module SpeckleConnector3
|
||||
# @return [SketchupModel::Definitions::UnpackResult]
|
||||
attr_reader :unpacked_entities
|
||||
|
||||
attr_reader :object_count
|
||||
attr_reader :cached_object_count
|
||||
|
||||
# @param model_card [Cards::SendCard] sender card
|
||||
def initialize(state, unpacked_entities, model_card)
|
||||
super(state, model_card)
|
||||
@send_filter = model_card.send_filter
|
||||
@conversion_results = []
|
||||
@unpacked_entities = unpacked_entities
|
||||
@object_count = 0
|
||||
@cached_object_count = 0
|
||||
end
|
||||
|
||||
def convert_entities_to_base_blocks_poc
|
||||
@@ -64,11 +69,9 @@ module SpeckleConnector3
|
||||
# @return [String, Integer, Array<Object>] base id of base and batches
|
||||
def serialize(base_and_entity, preferences)
|
||||
serializer = SpeckleConnector3::Converters::BaseObjectSerializer.new(preferences)
|
||||
t = Time.now.to_f
|
||||
id = serializer.serialize(base_and_entity)
|
||||
batches = serializer.batch_json_objects
|
||||
write_to_speckle_folder(id, batches)
|
||||
puts "Generating traversed object elapsed #{(Time.now.to_f - t).round(5)} s"
|
||||
return id, batches, serializer.object_references
|
||||
end
|
||||
|
||||
@@ -122,26 +125,28 @@ module SpeckleConnector3
|
||||
# @param speckle_state [States::SpeckleState]
|
||||
# rubocop:disable Metrics/MethodLength
|
||||
def from_native_to_speckle(entity, preferences, speckle_state, parent, ignore_cache, &convert)
|
||||
@object_count += 1
|
||||
persistent_id = entity.is_a?(SpeckleObjects::Geometry::GroupedMesh) ? entity.faces.first.persistent_id.to_s : entity.persistent_id.to_s
|
||||
# Where we do send caching!
|
||||
if !ignore_cache && !entity_has_changed?(entity) &&
|
||||
speckle_state.object_references_by_project[model_card.project_id] &&
|
||||
speckle_state.object_references_by_project[model_card.project_id].keys.include?(entity.persistent_id.to_s)
|
||||
reference = speckle_state.object_references_by_project[model_card.project_id][entity.persistent_id.to_s]
|
||||
speckle_state.object_references_by_project.keys.include?(model_card.project_id) &&
|
||||
speckle_state.object_references_by_project[model_card.project_id].keys.include?(persistent_id)
|
||||
reference = speckle_state.object_references_by_project[model_card.project_id][persistent_id]
|
||||
add_to_report(entity, reference)
|
||||
@cached_object_count += 1
|
||||
return speckle_state, reference
|
||||
end
|
||||
|
||||
if entity.is_a?(SpeckleObjects::Geometry::GroupedMesh)
|
||||
mesh = SpeckleObjects::Geometry::Mesh.from_faces(speckle_state: speckle_state, faces: entity.faces,
|
||||
units: @units, model_preferences: preferences[:model])
|
||||
add_to_report(entity, mesh)
|
||||
return speckle_state, mesh
|
||||
end
|
||||
|
||||
if entity.is_a?(Sketchup::Edge)
|
||||
line = SpeckleObjects::Geometry::Line.from_edge(speckle_state: speckle_state, edge: entity,
|
||||
units: @units, model_preferences: preferences[:model]).to_h
|
||||
add_to_report(entity, line)
|
||||
add_to_report(entity, line) unless entity.parent.is_a?(Sketchup::ComponentDefinition)
|
||||
return speckle_state, line
|
||||
end
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ module SpeckleConnector3
|
||||
super('selection', 'Selection', nil, summary)
|
||||
@selected_object_ids = selected_object_ids
|
||||
self[:selectedObjectIds] = selected_object_ids
|
||||
self[:isDefault] = true # hard coded default value, that's fair
|
||||
end
|
||||
|
||||
def check_expiry(ids)
|
||||
|
||||
@@ -18,6 +18,11 @@ module SpeckleConnector3
|
||||
|
||||
# @param sketchup_model [Sketchup::Model] active model.
|
||||
def self.read_preferences(sketchup_model)
|
||||
unless File.exist?(SPECKLE_CONFIG_DB_PATH)
|
||||
File.new(SPECKLE_CONFIG_DB_PATH, "w")
|
||||
db = Sqlite3::Database.new(SPECKLE_CONFIG_DB_PATH)
|
||||
create_objects_table(db)
|
||||
end
|
||||
db = Sqlite3::Database.new(SPECKLE_CONFIG_DB_PATH)
|
||||
user_preferences = validate_user_preferences(db)
|
||||
model_preferences = validate_model_preferences(sketchup_model)
|
||||
@@ -29,6 +34,52 @@ module SpeckleConnector3
|
||||
)
|
||||
end
|
||||
|
||||
# this is needed to get last selected account
|
||||
def self.get_user_selected_account_id
|
||||
db = Sqlite3::Database.new(SPECKLE_CONFIG_DB_PATH)
|
||||
row_data = db.exec("SELECT content FROM 'objects' WHERE hash = 'accounts'")
|
||||
is_config_accounts_exist = !row_data.empty?
|
||||
return nil unless is_config_accounts_exist
|
||||
|
||||
# Select data
|
||||
row_data = db.exec("SELECT content FROM 'objects' WHERE hash = 'accounts'").first.first
|
||||
# Parse string to hash
|
||||
data_hash = JSON.parse(row_data).to_h
|
||||
# Get current theme value
|
||||
data_hash['userSelectedAccountId']
|
||||
end
|
||||
|
||||
def self.format_user_selected_accounts_insert(account_id)
|
||||
"('accounts', '{\"userSelectedAccountId\": \"#{account_id}\"}');"
|
||||
end
|
||||
|
||||
def self.format_user_selected_accounts_update(account_id)
|
||||
"{\"userSelectedAccountId\": \"#{account_id}\"}"
|
||||
end
|
||||
|
||||
def self.set_user_selected_account_id(account_id)
|
||||
db = Sqlite3::Database.new(SPECKLE_CONFIG_DB_PATH)
|
||||
row_data = db.exec("SELECT content FROM 'objects' WHERE hash = 'accounts'")
|
||||
is_config_accounts_exist = !row_data.empty?
|
||||
|
||||
if !is_config_accounts_exist
|
||||
db.exec("INSERT INTO 'objects' VALUES #{format_user_selected_accounts_insert(account_id)}")
|
||||
else
|
||||
db.exec("UPDATE 'objects' SET content = '#{format_user_selected_accounts_update(account_id)}' WHERE hash = 'accounts'")
|
||||
end
|
||||
end
|
||||
|
||||
# Creates the 'objects' table in the database if it doesn't already exist.
|
||||
# @param db [Sqlite3::Database] the SQLite3 database instance.
|
||||
def self.create_objects_table(db)
|
||||
db.exec <<-SQL
|
||||
CREATE TABLE IF NOT EXISTS objects (
|
||||
hash TEXT PRIMARY KEY,
|
||||
content TEXT
|
||||
);
|
||||
SQL
|
||||
end
|
||||
|
||||
# Whether row data is complete with preference or not.
|
||||
# It is useful for backward compatibility, when we add new preferences it should be reset when user initialize it.
|
||||
def self.data_complete?(row_data)
|
||||
|
||||
@@ -46,11 +46,19 @@ module SpeckleConnector3
|
||||
instance_id = entity.persistent_id.to_s
|
||||
definition_id = entity.definition.persistent_id.to_s
|
||||
|
||||
instance_dictionaries = SketchupModel::Dictionary::BaseDictionaryHandler
|
||||
.attribute_dictionaries_to_speckle(entity)
|
||||
instance_att = instance_dictionaries.any? ? { dictionaries: instance_dictionaries } : {}
|
||||
|
||||
instance_proxies[instance_id] = SpeckleObjects::InstanceProxy.new(
|
||||
definition_id,
|
||||
entity.name,
|
||||
entity.definition.name,
|
||||
entity.layer.display_name,
|
||||
SpeckleObjects::Other::Transform.from_transformation(entity.transformation, @units).value,
|
||||
depth,
|
||||
@units,
|
||||
sketchup_attributes: instance_att,
|
||||
application_id: instance_id
|
||||
)
|
||||
|
||||
@@ -70,7 +78,17 @@ module SpeckleConnector3
|
||||
return
|
||||
end
|
||||
|
||||
definition_proxy = SpeckleObjects::InstanceDefinitionProxy.new(entity.definition, [], depth, application_id: definition_id)
|
||||
definition_dictionaries = SketchupModel::Dictionary::BaseDictionaryHandler
|
||||
.attribute_dictionaries_to_speckle(entity.definition)
|
||||
definition_att = definition_dictionaries.any? ? { dictionaries: definition_dictionaries } : {}
|
||||
|
||||
definition_proxy = SpeckleObjects::InstanceDefinitionProxy.new(
|
||||
entity.definition,
|
||||
[],
|
||||
depth,
|
||||
sketchup_attributes: definition_att,
|
||||
application_id: definition_id
|
||||
)
|
||||
definition_proxy[:name] = entity.definition.name
|
||||
definition_proxy[:description] = entity.definition.description
|
||||
|
||||
|
||||
@@ -16,7 +16,20 @@ module SpeckleConnector3
|
||||
].freeze
|
||||
|
||||
# @param entity [Sketchup::Entity] entity to get attribute dictionaries
|
||||
def self.attribute_dictionaries_to_speckle(entity, model_preferences)
|
||||
def self.attribute_dictionaries_to_speckle(entity)
|
||||
dictionaries = {}
|
||||
return dictionaries if entity.attribute_dictionaries.nil?
|
||||
|
||||
entity.attribute_dictionaries.each do |att_dict|
|
||||
dict_name = att_dict == '' ? 'empty_dictionary_name' : att_dict.name
|
||||
dictionaries[dict_name] = att_dict.to_h unless IGNORED_DICTIONARY_NAMES.include?(att_dict.name)
|
||||
end
|
||||
dictionaries
|
||||
end
|
||||
|
||||
# @param entity [Sketchup::Entity] entity to get attribute dictionaries
|
||||
# @note v2 logic
|
||||
def self.attribute_dictionaries_to_speckle_by_settings(entity, model_preferences)
|
||||
dictionaries = {}
|
||||
return dictionaries unless model_preferences[INCLUDE_ENTITY_ATTRIBUTES]
|
||||
|
||||
|
||||
@@ -72,6 +72,22 @@ module SpeckleConnector3
|
||||
hash
|
||||
end
|
||||
|
||||
# @param obj [Object] object to write
|
||||
# @param dict [Sketchup::AttributeDictionary] attribute dictionary to write data.
|
||||
def self.hash_to_dict(dict_name, obj, dict)
|
||||
dict_to_write = dict.attribute_dictionary(dict_name, true)
|
||||
|
||||
obj.each do |key, value|
|
||||
# value = obj.instance_variable_get(var)
|
||||
# var_name = var.to_s[1..-1]
|
||||
if value.is_a?(Hash) # FIXME or not, depends:-> This doesn't cover arrays that has objects in it.
|
||||
hash_to_dict(key.to_s, value, dict_to_write)
|
||||
else
|
||||
dict_to_write[key] = value
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# @return [String] the name of the dictionary to read from
|
||||
def self.dictionary_name
|
||||
raise NotImplementedError 'Implement this in subclass'
|
||||
|
||||
@@ -13,30 +13,14 @@ module SpeckleConnector3
|
||||
# @param sketchup_model [Sketchup::Model] sketchup model to save cards into it's attribute dictionary
|
||||
def self.save_send_card_to_model(send_card, sketchup_model)
|
||||
send_cards_dict = send_cards_dict(sketchup_model)
|
||||
serialize_obj_to_dict(send_card.model_card_id, send_card, send_cards_dict)
|
||||
hash_to_dict(send_card.model_card_id, send_card, send_cards_dict)
|
||||
end
|
||||
|
||||
# @param receive_card [Cards::ReceiveCard] card to save model
|
||||
# @param sketchup_model [Sketchup::Model] sketchup model to save cards into it's attribute dictionary
|
||||
def self.save_receive_card_to_model(receive_card, sketchup_model)
|
||||
receive_cards_dict = receive_cards_dict(sketchup_model)
|
||||
serialize_obj_to_dict(receive_card.model_card_id, receive_card, receive_cards_dict)
|
||||
end
|
||||
|
||||
# @param obj [Object] object to write
|
||||
# @param dict [Sketchup::AttributeDictionary] attribute dictionary to write data.
|
||||
def self.serialize_obj_to_dict(dict_name, obj, dict)
|
||||
dict_to_write = dict.attribute_dictionary(dict_name, true)
|
||||
|
||||
obj.each do |key, value|
|
||||
# value = obj.instance_variable_get(var)
|
||||
# var_name = var.to_s[1..-1]
|
||||
if value.is_a?(Hash) # FIXME or not depends: This doesn't cover arrays that has objects in it.
|
||||
serialize_obj_to_dict(key.to_s, value, dict_to_write)
|
||||
else
|
||||
dict_to_write[key] = value
|
||||
end
|
||||
end
|
||||
hash_to_dict(receive_card.model_card_id, receive_card, receive_cards_dict)
|
||||
end
|
||||
|
||||
def self.remove_card_dict(sketchup_model, data)
|
||||
@@ -101,7 +85,7 @@ module SpeckleConnector3
|
||||
dict_to_write = dict_to_write.attribute_dictionary(dict_name, true)
|
||||
dict_to_write = dict_to_write.attribute_dictionary(var_name, true)
|
||||
value.each do |key, hash_value|
|
||||
serialize_obj_to_dict(key.to_s, hash_value, dict_to_write)
|
||||
hash_to_dict(key.to_s, hash_value, dict_to_write)
|
||||
end
|
||||
else
|
||||
dict_to_write.set_attribute(dict_name, var_name, value)
|
||||
|
||||
@@ -34,7 +34,7 @@ module SpeckleConnector3
|
||||
|
||||
unless material.nil?
|
||||
if render_material_proxies.has_key?(material.persistent_id.to_s)
|
||||
render_material_proxies[material.persistent_id.to_s].add_object_id(entity.persistent_id.to_s)
|
||||
render_material_proxies[material.persistent_id.to_s].try_add_object_id(entity.persistent_id.to_s)
|
||||
else
|
||||
convert_material_and_add_to_proxies(material, entity)
|
||||
end
|
||||
@@ -43,7 +43,7 @@ module SpeckleConnector3
|
||||
unless back_material.nil?
|
||||
if render_material_proxies.has_key?(back_material.persistent_id.to_s)
|
||||
render_material_proxies[back_material.persistent_id.to_s]
|
||||
.add_object_id("#{entity.persistent_id.to_s}_back")
|
||||
.try_add_object_id("#{entity.persistent_id.to_s}_back")
|
||||
else
|
||||
convert_material_and_add_to_proxies(back_material, entity, true)
|
||||
end
|
||||
|
||||
@@ -12,10 +12,13 @@ module SpeckleConnector3
|
||||
def self.highlight_entities(sketchup_model, entity_ids)
|
||||
sketchup_model.selection.clear
|
||||
|
||||
# below code causing huge performance bottleneck since iterate nestedly, I was initially considering to cover all cases,
|
||||
# but not worth to have highlighting sub elements since we already highlight the parent. This can be only an issue when user sends
|
||||
# only the sub elements
|
||||
# Flat entities to select entities on card
|
||||
flat_entities = SketchupModel::Query::Entity.flat_entities(sketchup_model.entities)
|
||||
# flat_entities = SketchupModel::Query::Entity.flat_entities(sketchup_model.entities)
|
||||
|
||||
flat_entities.each do |entity|
|
||||
sketchup_model.entities.each do |entity|
|
||||
next unless entity_ids.include?(entity.persistent_id.to_s)
|
||||
|
||||
sketchup_model.selection.add(entity.instances) if entity.is_a?(Sketchup::ComponentDefinition)
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative '../base'
|
||||
require_relative '../../constants/type_constants'
|
||||
|
||||
module SpeckleConnector3
|
||||
module SpeckleObjects
|
||||
class DataObject < Base
|
||||
SPECKLE_TYPE = SPECKLE_OBJECT_DATA_OBJECT
|
||||
|
||||
def self.to_native(state, data_object, layer, _entities, &convert_to_native)
|
||||
properties = data_object['properties']
|
||||
return state
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,25 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative '../base'
|
||||
require_relative '../../constants/type_constants'
|
||||
require_relative '../other/display_value'
|
||||
require_relative '../../sketchup_model/dictionary/base_dictionary_handler'
|
||||
|
||||
module SpeckleConnector3
|
||||
module SpeckleObjects
|
||||
class RevitDataObject < Base
|
||||
SPECKLE_TYPE = SPECKLE_OBJECT_DATA_OBJECT_REVIT
|
||||
|
||||
def self.to_native(state, revit_data_object, layer, entities, &convert_to_native)
|
||||
properties = revit_data_object['properties']
|
||||
|
||||
new_state, instance_and_definition = SpeckleObjects::Other::DisplayValue.to_native(state, revit_data_object, layer, entities, &convert_to_native)
|
||||
instance, _definition = instance_and_definition
|
||||
attr = instance.attribute_dictionary('Speckle', true)
|
||||
|
||||
SketchupModel::Dictionary::BaseDictionaryHandler.hash_to_dict('Revit Parameters', properties, attr) if properties
|
||||
return new_state, instance_and_definition
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -18,21 +18,24 @@ module SpeckleConnector3
|
||||
plane = arc['plane']
|
||||
units = arc['units']
|
||||
origin = Point.to_native(plane['origin']['x'], plane['origin']['y'], plane['origin']['z'], units)
|
||||
normal = Vector.to_native(plane['normal']['x'], plane['normal']['y'], plane['normal']['z'], units)
|
||||
x_axis = Vector.to_native(plane['xdir']['x'], plane['xdir']['y'], plane['xdir']['z'], units)
|
||||
start_point = Point.to_native(arc['startPoint']['x'], arc['startPoint']['y'], arc['startPoint']['z'], units)
|
||||
end_point = Point.to_native(arc['endPoint']['x'], arc['endPoint']['y'], arc['endPoint']['z'], units)
|
||||
normal = Vector.to_native(plane['normal']['x'], plane['normal']['y'], plane['normal']['z'], units).normalize
|
||||
x_axis = Vector.to_native(plane['xdir']['x'], plane['xdir']['y'], plane['xdir']['z'], units).normalize
|
||||
radius = Geometry.length_to_native(arc['radius'], units)
|
||||
start_angle = arc['startAngle']
|
||||
end_angle = arc['endAngle']
|
||||
|
||||
# Normalize angles to range 0 to 2π
|
||||
start_angle %= 2 * Math::PI
|
||||
end_angle %= 2 * Math::PI
|
||||
start_vector = (start_point - origin).normalize
|
||||
end_vector = (end_point - origin).normalize
|
||||
|
||||
# Ensure start angle is less than end angle for proper drawing
|
||||
if start_angle > end_angle
|
||||
end_angle += 2 * Math::PI
|
||||
start_angle = Math.atan2(start_vector.cross(normal).dot(x_axis), start_vector.dot(x_axis))
|
||||
end_angle = Math.atan2(end_vector.cross(normal).dot(x_axis), end_vector.dot(x_axis))
|
||||
|
||||
measure = arc['measure'] # this is in radians!
|
||||
if measure # for backward compatibilty
|
||||
end_angle = start_angle + measure
|
||||
else
|
||||
end_angle += 2 * Math::PI if end_angle < start_angle
|
||||
end
|
||||
|
||||
edges = entities.add_arc(origin, x_axis, normal, radius, start_angle, end_angle)
|
||||
edges.each { |edge| edge.layer = layer }
|
||||
return state, edges
|
||||
|
||||
@@ -36,7 +36,7 @@ module SpeckleConnector3
|
||||
self[:units] = units
|
||||
self[:layer] = layer unless layer.nil?
|
||||
self['@SpeckleSchema'] = speckle_schema if speckle_schema.any?
|
||||
self[:sketchup_attributes] = sketchup_attributes if sketchup_attributes.any?
|
||||
self[:properties] = sketchup_attributes if sketchup_attributes.any?
|
||||
end
|
||||
# rubocop:enable Metrics/ParameterLists
|
||||
|
||||
@@ -59,7 +59,7 @@ module SpeckleConnector3
|
||||
# @param edge [Sketchup::Edge] edge to convert line.
|
||||
def self.from_edge(speckle_state:, edge:, units:, model_preferences:, global_transformation: nil)
|
||||
dictionaries = SketchupModel::Dictionary::BaseDictionaryHandler
|
||||
.attribute_dictionaries_to_speckle(edge, model_preferences)
|
||||
.attribute_dictionaries_to_speckle_by_settings(edge, model_preferences)
|
||||
att = dictionaries.any? ? { dictionaries: dictionaries } : {}
|
||||
speckle_schema = Mapper.to_speckle(speckle_state, edge, units, global_transformation: global_transformation)
|
||||
start_pt = Geometry::Point.from_vertex(edge.start.position, units)
|
||||
@@ -141,7 +141,10 @@ module SpeckleConnector3
|
||||
edges.each do |edge|
|
||||
edge.layer = layer
|
||||
# edge.layer = line_layer.nil? ? layer : line_layer
|
||||
unless line['sketchup_attributes'].nil?
|
||||
if !line['properties'].nil?
|
||||
SketchupModel::Dictionary::BaseDictionaryHandler
|
||||
.attribute_dictionaries_to_native(edge, line['properties']['dictionaries'])
|
||||
elsif !line['sketchup_attributes'].nil? # backward compatibility
|
||||
SketchupModel::Dictionary::BaseDictionaryHandler
|
||||
.attribute_dictionaries_to_native(edge, line['sketchup_attributes']['dictionaries'])
|
||||
end
|
||||
|
||||
@@ -49,7 +49,7 @@ module SpeckleConnector3
|
||||
# self[:renderMaterial] = render_material
|
||||
self[:'@(31250)vertices'] = vertices
|
||||
self[:'@(62500)faces'] = faces
|
||||
self[:sketchup_attributes] = sketchup_attributes if sketchup_attributes.any?
|
||||
self[:properties] = sketchup_attributes if sketchup_attributes.any?
|
||||
self['@SpeckleSchema'] = speckle_schema if speckle_schema.any?
|
||||
end
|
||||
# rubocop:enable Metrics/ParameterLists
|
||||
@@ -129,7 +129,10 @@ module SpeckleConnector3
|
||||
# Smooth edges if they already soft
|
||||
# FIXME: Below line should be reconsidered. It might be a good to know here mesh comes from NURBS or not.
|
||||
face.edges.each { |edge| edge.smooth = true if edge.soft? } if has_any_non_planar_quad_mesh
|
||||
unless mesh['sketchup_attributes'].nil?
|
||||
if !mesh['properties'].nil?
|
||||
SketchupModel::Dictionary::BaseDictionaryHandler
|
||||
.attribute_dictionaries_to_native(face, mesh['properties']['dictionaries'])
|
||||
elsif !mesh['sketchup_attributes'].nil? # backward compatibility
|
||||
SketchupModel::Dictionary::BaseDictionaryHandler
|
||||
.attribute_dictionaries_to_native(face, mesh['sketchup_attributes']['dictionaries'])
|
||||
end
|
||||
@@ -151,7 +154,7 @@ module SpeckleConnector3
|
||||
# rubocop:disable Metrics/ParameterLists
|
||||
def self.from_face(speckle_state:, face:, units:, model_preferences:, global_transform: nil, parent_material: nil)
|
||||
dictionaries = SketchupModel::Dictionary::BaseDictionaryHandler
|
||||
.attribute_dictionaries_to_speckle(face, model_preferences)
|
||||
.attribute_dictionaries_to_speckle_by_settings(face, model_preferences)
|
||||
has_any_soften_edge = face.edges.any?(&:soft?)
|
||||
att = dictionaries.any? ? { is_soften: has_any_soften_edge, dictionaries: dictionaries }
|
||||
: { is_soften: has_any_soften_edge }
|
||||
@@ -278,7 +281,9 @@ module SpeckleConnector3
|
||||
# @param mesh [Object] speckle mesh object
|
||||
# @param entities [Sketchup::Entities] sketchup entities that mesh will be created in it as face.
|
||||
def self.get_soften_setting(mesh, entities)
|
||||
unless mesh['sketchup_attributes'].nil?
|
||||
if !mesh['properties'].nil?
|
||||
return mesh['properties']['is_soften'].nil? ? true : mesh['properties']['is_soften']
|
||||
elsif !mesh['sketchup_attributes'].nil? # backward compatibility
|
||||
return mesh['sketchup_attributes']['is_soften'].nil? ? true : mesh['sketchup_attributes']['is_soften']
|
||||
end
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ module SpeckleConnector3
|
||||
|
||||
geometries.each do |geo|
|
||||
if geo['speckle_type'] && geo['speckle_type'] == OBJECTS_GEOMETRY_MESH
|
||||
geo['sketchup_attributes'] = { 'is_soften' => false }
|
||||
geo['properties'] = { 'is_soften' => false }
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ module SpeckleConnector3
|
||||
|
||||
geometries.each do |geo|
|
||||
if geo['speckle_type'] && geo['speckle_type'] == OBJECTS_GEOMETRY_MESH
|
||||
geo['sketchup_attributes'] = { 'is_soften' => false }
|
||||
geo['properties'] = { 'is_soften' => false }
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ module SpeckleConnector3
|
||||
# @param object_ids [Array<String>]
|
||||
# @param max_depth [Integer]
|
||||
# @param application_id [String | NilClass]
|
||||
def initialize(definition, object_ids, max_depth, application_id: nil)
|
||||
def initialize(definition, object_ids, max_depth, sketchup_attributes: {}, application_id: nil)
|
||||
super(
|
||||
speckle_type: SPECKLE_TYPE,
|
||||
application_id: application_id,
|
||||
@@ -31,6 +31,7 @@ module SpeckleConnector3
|
||||
self[:objects] = object_ids
|
||||
self[:maxDepth] = max_depth
|
||||
self[:alwaysFaceCamera] = definition.behavior.always_face_camera?
|
||||
self[:properties] = sketchup_attributes if sketchup_attributes.any?
|
||||
end
|
||||
|
||||
def add_object_id(object_id)
|
||||
|
||||
@@ -8,24 +8,37 @@ module SpeckleConnector3
|
||||
module SpeckleObjects
|
||||
class InstanceProxy < Base
|
||||
SPECKLE_TYPE = SPECKLE_CORE_MODELS_INSTANCES_INSTANCE_PROXY
|
||||
def initialize(definition_id, transform, max_depth, units, application_id: nil)
|
||||
def initialize(definition_id, name, definitionName, layerName, transform, max_depth, units, sketchup_attributes: {}, application_id: nil)
|
||||
super(
|
||||
speckle_type: SPECKLE_TYPE,
|
||||
application_id: application_id,
|
||||
id: nil
|
||||
)
|
||||
self[:name] = name if name != ""
|
||||
self[:definition] = definitionName if definitionName != ""
|
||||
self[:layer] = layerName if layerName != ""
|
||||
self[:units] = units
|
||||
self[:definitionId] = definition_id
|
||||
self[:maxDepth] = max_depth
|
||||
self[:transform] = transform
|
||||
self[:properties] = sketchup_attributes if sketchup_attributes.any?
|
||||
end
|
||||
|
||||
def self.to_native(state, instance_proxy, layer, entities, definition_proxies, &_convert_to_native)
|
||||
definition_id = instance_proxy['definitionId']
|
||||
proxy_transform = instance_proxy['transform']
|
||||
name = instance_proxy['name']
|
||||
transform = Other::Transform.to_native(proxy_transform, instance_proxy['units'])
|
||||
definition = definition_proxies[definition_id].definition
|
||||
instance = entities.add_instance(definition, transform)
|
||||
instance.name = name if name
|
||||
|
||||
unless instance_proxy['properties'].nil?
|
||||
SketchupModel::Dictionary::BaseDictionaryHandler
|
||||
.attribute_dictionaries_to_native(instance, instance_proxy['properties']['dictionaries'])
|
||||
end
|
||||
|
||||
|
||||
instance.layer = layer if layer # TODO: CONVERTER_V2 check
|
||||
# TODO: CONVERTER_V2 handle groups
|
||||
return state, [instance, definition]
|
||||
|
||||
@@ -32,7 +32,7 @@ module SpeckleConnector3
|
||||
self[:units] = units
|
||||
self[:name] = name
|
||||
self[:always_face_camera] = always_face_camera
|
||||
self[:sketchup_attributes] = sketchup_attributes if sketchup_attributes.any?
|
||||
self[:properties] = sketchup_attributes if sketchup_attributes.any?
|
||||
self[:SpeckleSchema] = speckle_schema if speckle_schema.any?
|
||||
# '@@' means that it is a detached property.
|
||||
self['@@geometry'] = geometry
|
||||
@@ -47,7 +47,7 @@ module SpeckleConnector3
|
||||
# rubocop:disable Metrics/ParameterLists
|
||||
def self.from_definition(definition, units, preferences, speckle_state, parent, &convert)
|
||||
dictionaries = SketchupModel::Dictionary::BaseDictionaryHandler
|
||||
.attribute_dictionaries_to_speckle(definition, preferences[:model])
|
||||
.attribute_dictionaries_to_speckle_by_settings(definition, preferences[:model])
|
||||
att = dictionaries.any? ? { dictionaries: dictionaries } : {}
|
||||
speckle_schema = SketchupModel::Dictionary::SpeckleSchemaDictionaryHandler
|
||||
.speckle_schema_to_speckle(definition)
|
||||
@@ -118,7 +118,7 @@ module SpeckleConnector3
|
||||
geometry = definition_obj['geometry'] || definition_obj['@geometry'] || definition_obj['displayValue']
|
||||
|
||||
always_face_camera = definition_obj['always_face_camera'].nil? ? false : definition_obj['always_face_camera']
|
||||
sketchup_attributes = definition_obj['sketchup_attributes']
|
||||
properties = definition_obj['properties'] || definition_obj['sketchup_attributes']
|
||||
definition&.entities&.clear!
|
||||
definition ||= sketchup_model.definitions.add(definition_name)
|
||||
|
||||
@@ -143,9 +143,9 @@ module SpeckleConnector3
|
||||
# puts("definition finished: #{name} (#{application_id})")
|
||||
# puts(" entity count: #{definition.entities.count}")
|
||||
definition.behavior.always_face_camera = always_face_camera
|
||||
unless sketchup_attributes.nil?
|
||||
unless properties.nil?
|
||||
SketchupModel::Dictionary::BaseDictionaryHandler
|
||||
.attribute_dictionaries_to_native(definition, sketchup_attributes['dictionaries'])
|
||||
.attribute_dictionaries_to_native(definition, properties['dictionaries'])
|
||||
end
|
||||
return state, [definition]
|
||||
end
|
||||
|
||||
@@ -40,7 +40,7 @@ module SpeckleConnector3
|
||||
self[:is_sketchup_group] = is_sketchup_group
|
||||
# self[:renderMaterial] = render_material
|
||||
self[:transform] = transform
|
||||
self[:sketchup_attributes] = sketchup_attributes if sketchup_attributes.any?
|
||||
self[:properties] = sketchup_attributes if sketchup_attributes.any?
|
||||
self[:speckle_schema] = speckle_schema if speckle_schema.any?
|
||||
# FIXME: Since blockDefinition sends with @ as detached, block basePlane renders on viewer.
|
||||
self['@@definition'] = block_definition
|
||||
@@ -53,7 +53,7 @@ module SpeckleConnector3
|
||||
group.persistent_id.to_s)
|
||||
speckle_state = new_speckle_state
|
||||
dictionaries = SketchupModel::Dictionary::BaseDictionaryHandler
|
||||
.attribute_dictionaries_to_speckle(group, preferences[:model])
|
||||
.attribute_dictionaries_to_speckle_by_settings(group, preferences[:model])
|
||||
att = dictionaries.any? ? { dictionaries: dictionaries } : {}
|
||||
speckle_schema = SketchupModel::Dictionary::SpeckleSchemaDictionaryHandler.speckle_schema_to_speckle(group)
|
||||
block_instance = BlockInstance.new(
|
||||
@@ -83,7 +83,7 @@ module SpeckleConnector3
|
||||
speckle_state = new_speckle_state
|
||||
|
||||
dictionaries = SketchupModel::Dictionary::BaseDictionaryHandler
|
||||
.attribute_dictionaries_to_speckle(component_instance, preferences[:model])
|
||||
.attribute_dictionaries_to_speckle_by_settings(component_instance, preferences[:model])
|
||||
att = dictionaries.any? ? { dictionaries: dictionaries } : {}
|
||||
speckle_schema = SketchupModel::Dictionary::SpeckleSchemaDictionaryHandler
|
||||
.speckle_schema_to_speckle(component_instance)
|
||||
@@ -245,10 +245,15 @@ module SpeckleConnector3
|
||||
end
|
||||
|
||||
instance.name = block['name'] unless block['name'].nil?
|
||||
unless block['sketchup_attributes'].nil?
|
||||
|
||||
if !block['properties'].nil?
|
||||
SketchupModel::Dictionary::BaseDictionaryHandler
|
||||
.attribute_dictionaries_to_native(instance, block['properties']['dictionaries'])
|
||||
elsif !block['sketchup_attributes'].nil? # backward compatibility
|
||||
SketchupModel::Dictionary::BaseDictionaryHandler
|
||||
.attribute_dictionaries_to_native(instance, block['sketchup_attributes']['dictionaries'])
|
||||
end
|
||||
|
||||
return state, [instance, definition]
|
||||
end
|
||||
# rubocop:enable Metrics/AbcSize
|
||||
|
||||
@@ -32,7 +32,7 @@ module SpeckleConnector3
|
||||
self[:emissive] = emissive
|
||||
self[:metalness] = metalness
|
||||
self[:roughness] = roughness
|
||||
self[:sketchup_attributes] = sketchup_attributes
|
||||
self[:properties] = sketchup_attributes
|
||||
end
|
||||
# rubocop:enable Metrics/ParameterLists
|
||||
|
||||
|
||||
@@ -114,7 +114,8 @@ module SpeckleConnector3
|
||||
speckle_folders = layers_relation[:elements].reject { |layer_or_fol| layer_or_fol[:elements].nil? }
|
||||
|
||||
speckle_folders.each do |speckle_folder|
|
||||
sub_folder = folder.add_folder(speckle_folder[:name])
|
||||
folder_name = speckle_folder[:name] == '' ? 'Unnamed' : speckle_folder[:name]
|
||||
sub_folder = folder.add_folder(folder_name)
|
||||
sub_folder.visible = speckle_folder[:visible] unless speckle_folder[:visible].nil?
|
||||
to_native_layer_folder(speckle_folder, color_proxies, sub_folder, sketchup_model, project_id, model_id)
|
||||
end
|
||||
|
||||
@@ -35,9 +35,11 @@ module SpeckleConnector3
|
||||
end
|
||||
|
||||
# @param object_id [String] application id of the object to add into proxy list
|
||||
def add_object_id(object_id)
|
||||
object_ids.append(object_id)
|
||||
self[:objects] = object_ids
|
||||
def try_add_object_id(object_id)
|
||||
unless object_ids.include?(object_id)
|
||||
object_ids.append(object_id)
|
||||
self[:objects] = object_ids
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -12,6 +12,23 @@ module SpeckleConnector3
|
||||
module Speckle
|
||||
module Core
|
||||
module Models
|
||||
class Debouncer
|
||||
def initialize(wait_time)
|
||||
@wait_time = wait_time
|
||||
@mutex = Mutex.new
|
||||
@thread = nil
|
||||
end
|
||||
|
||||
def call(&block)
|
||||
@mutex.synchronize do
|
||||
@thread&.kill # Cancel the previous execution
|
||||
@thread = Thread.new do
|
||||
sleep @wait_time
|
||||
block.call
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
# ModelCollection object that collect other speckle objects under it's elements.
|
||||
class ModelCollection < Collection
|
||||
DIRECT_SHAPE = SpeckleObjects::BuiltElements::Revit::DirectShape
|
||||
@@ -61,6 +78,8 @@ module SpeckleConnector3
|
||||
application_id: sketchup_model.guid
|
||||
)
|
||||
|
||||
last_sent_time = Time.now
|
||||
|
||||
count = 0
|
||||
entities.each do |entity|
|
||||
layer_collection = LayerCollection.get_or_create_layer_collection(entity.layer, model_collection)
|
||||
@@ -83,13 +102,10 @@ module SpeckleConnector3
|
||||
status: progress == 1 ? 'Completed' : 'Converting'
|
||||
}
|
||||
}
|
||||
|
||||
action = Proc.new do
|
||||
if Time.now - last_sent_time >= 1
|
||||
state.instant_message_sender.call("sendBinding.emit('setModelProgress', #{sender_progress_args.to_json})")
|
||||
last_sent_time = Time.now
|
||||
end
|
||||
|
||||
state.worker.add_job(Job.new(entity.persistent_id.to_s, &action))
|
||||
state.worker.do_work(Time.now.to_f, &action)
|
||||
end
|
||||
|
||||
return speckle_state, model_collection
|
||||
|
||||
@@ -17,6 +17,9 @@ module SpeckleConnector3
|
||||
# @return [Immutable::Hash{String=>Cards::ReceiveCard}] receive cards.
|
||||
attr_reader :receive_cards
|
||||
|
||||
# @return [Immutable::Hash{String=>Array<UiData::Report::ConversionResult>}] receive cards.
|
||||
attr_reader :conversion_results
|
||||
|
||||
# @return [Immutable::Hash{String=>Immutable::Hash{String=>SpeckleObjects::ObjectReference}}] object references that sent before server.
|
||||
attr_reader :object_references_by_project
|
||||
|
||||
@@ -70,6 +73,7 @@ module SpeckleConnector3
|
||||
@definitions = Immutable::EmptyHash
|
||||
@send_cards = Immutable::EmptyHash
|
||||
@receive_cards = Immutable::EmptyHash
|
||||
@conversion_results = Immutable::EmptyHash
|
||||
@relation = Relations::ManyToOneRelation.new
|
||||
@speckle_mapper_state = SpeckleMapperState.new
|
||||
end
|
||||
@@ -218,6 +222,16 @@ module SpeckleConnector3
|
||||
with(:@object_references_by_project => object_references_by_project.put(project_id, new_project_references))
|
||||
end
|
||||
|
||||
def with_conversion_results(model_card_id, results)
|
||||
new_conversion_results = conversion_results.put(model_card_id, results)
|
||||
with(:@conversion_results => new_conversion_results)
|
||||
end
|
||||
|
||||
def without_conversion_results(model_card_id)
|
||||
new_conversion_results = conversion_results.delete(model_card_id)
|
||||
with(:@conversion_results => new_conversion_results)
|
||||
end
|
||||
|
||||
def invalid_streams
|
||||
speckle_entities.collect do |_id, speckle_entity|
|
||||
speckle_entity.invalid_stream_ids
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative 'binding'
|
||||
require_relative '../../actions/get_accounts'
|
||||
require_relative '../../actions/account_actions/get_accounts'
|
||||
require_relative '../../actions/account_actions/remove_account'
|
||||
|
||||
module SpeckleConnector3
|
||||
module Ui
|
||||
@@ -11,7 +12,8 @@ module SpeckleConnector3
|
||||
class AccountsBinding < Binding
|
||||
def commands
|
||||
@commands ||= {
|
||||
getAccounts: Commands::ActionCommand.new(@app, self, Actions::GetAccounts)
|
||||
getAccounts: Commands::ActionCommand.new(@app, self, Actions::GetAccounts),
|
||||
removeAccount: Commands::ActionCommand.new(@app, self, Actions::RemoveAccount)
|
||||
}.freeze
|
||||
end
|
||||
end
|
||||
|
||||
@@ -11,6 +11,7 @@ require_relative '../../actions/base_actions/add_model'
|
||||
require_relative '../../actions/base_actions/highlight_model'
|
||||
require_relative '../../actions/base_actions/highlight_objects'
|
||||
require_relative '../../actions/base_actions/remove_model'
|
||||
require_relative '../../actions/base_actions/remove_models'
|
||||
require_relative '../../actions/base_actions/get_send_filters'
|
||||
require_relative '../../actions/base_actions/update_send_filter'
|
||||
require_relative '../../actions/base_actions/get_document_state'
|
||||
@@ -27,6 +28,7 @@ module SpeckleConnector3
|
||||
highlightModel: Commands::ActionCommand.new(@app, self, Actions::HighlightModel),
|
||||
highlightObjects: Commands::ActionCommand.new(@app, self, Actions::HighlightObjects),
|
||||
removeModel: Commands::ActionCommand.new(@app, self, Actions::RemoveModel),
|
||||
removeModels: Commands::ActionCommand.new(@app, self, Actions::RemoveModels),
|
||||
# Since we send exact model card with updateModel, I can use directly AddModel action, it will replace
|
||||
updateModel: Commands::ActionCommand.new(@app, self, Actions::AddModel),
|
||||
getSourceApplicationName: Commands::ActionCommand.new(@app, self, Actions::GetSourceAppName),
|
||||
|
||||
@@ -4,6 +4,8 @@ require_relative 'binding'
|
||||
require_relative '../../actions/config_actions/get_is_dev_mode'
|
||||
require_relative '../../actions/config_actions/get_config'
|
||||
require_relative '../../actions/config_actions/update_config'
|
||||
require_relative '../../actions/config_actions/get_user_selected_account_id'
|
||||
require_relative '../../actions/config_actions/set_user_selected_account_id'
|
||||
|
||||
module SpeckleConnector3
|
||||
module Ui
|
||||
@@ -15,7 +17,9 @@ module SpeckleConnector3
|
||||
@commands ||= {
|
||||
getIsDevMode: Commands::ActionCommand.new(@app, self, Actions::GetIsDevMode),
|
||||
getConfig: Commands::ActionCommand.new(@app, self, Actions::GetConfig),
|
||||
updateConfig: Commands::ActionCommand.new(@app, self, Actions::UpdateConfig)
|
||||
updateConfig: Commands::ActionCommand.new(@app, self, Actions::UpdateConfig),
|
||||
getUserSelectedAccountId: Commands::ActionCommand.new(@app, self, Actions::GetUserSelectedAccountId),
|
||||
setUserSelectedAccountId: Commands::ActionCommand.new(@app, self, Actions::SetUserSelectedAccountId)
|
||||
}.freeze
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
require_relative 'binding'
|
||||
require_relative '../../actions/send_actions/send'
|
||||
require_relative '../../actions/send_actions/after_send_objects'
|
||||
require_relative '../../actions/base_actions/get_send_filters'
|
||||
require_relative '../../actions/base_actions/get_send_settings'
|
||||
require_relative '../../actions/base_actions/update_send_filter'
|
||||
@@ -17,7 +18,8 @@ module SpeckleConnector3
|
||||
send: Commands::ActionCommand.new(@app, self, Actions::Send),
|
||||
getSendFilters: Commands::ActionCommand.new(@app, self, Actions::GetSendFilters),
|
||||
getSendSettings: Commands::ActionCommand.new(@app, self, Actions::GetSendSettings),
|
||||
updateSendFilter: Commands::ActionCommand.new(@app, self, Actions::UpdateSendFilter)
|
||||
updateSendFilter: Commands::ActionCommand.new(@app, self, Actions::UpdateSendFilter),
|
||||
afterSendObjects: Commands::ActionCommand.new(@app, self, Actions::AfterSendObjects)
|
||||
}.freeze
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user