Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e3dceec63c | |||
| e9d34d87cc | |||
| 5187fded02 | |||
| 370825838a | |||
| bc8eece488 | |||
| 4222b1721d | |||
| b475bc96af | |||
| f9ac7319ae | |||
| 2ce3e9150f |
@@ -1,17 +1,162 @@
|
||||
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"
|
||||
orbs:
|
||||
# Using windows for builds
|
||||
win: circleci/windows@2.4.0
|
||||
# Upload artifacts to s3
|
||||
aws-s3: circleci/aws-s3@2.0.0
|
||||
|
||||
jobs:
|
||||
build-ui:
|
||||
docker:
|
||||
- image: "circleci/node:16"
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
command: "npm install"
|
||||
working_directory: "ui"
|
||||
- run:
|
||||
command: "npm run build"
|
||||
working_directory: "ui"
|
||||
- persist_to_workspace:
|
||||
root: ./
|
||||
paths:
|
||||
- speckle_connector/vue_ui
|
||||
|
||||
build-connector: # Reusable job for basic connectors
|
||||
executor:
|
||||
name: win/default # comes with python 3.7.3
|
||||
shell: cmd.exe
|
||||
parameters:
|
||||
slug:
|
||||
type: string
|
||||
default: ""
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: ./
|
||||
- run:
|
||||
name: Create Innosetup signing cert
|
||||
shell: powershell.exe
|
||||
command: |
|
||||
echo $env:PFX_B64 > "speckle-sharp-ci-tools\SignTool\AEC Systems Ltd.txt"
|
||||
certutil -decode "speckle-sharp-ci-tools\SignTool\AEC Systems Ltd.txt" "speckle-sharp-ci-tools\SignTool\AEC Systems Ltd.pfx"
|
||||
- run:
|
||||
name: Set Environment Variable
|
||||
shell: powershell.exe
|
||||
command: |
|
||||
$tag = if([string]::IsNullOrEmpty($env:CIRCLE_TAG)) { "2.0.999" } else { $env:CIRCLE_TAG }
|
||||
$semver = if($tag.Contains('/')) {$tag.Split("/")[0] } else { $tag }
|
||||
$ver = if($semver.Contains('-')) {$semver.Split("-")[0] } else { $semver }
|
||||
$version = "$($ver).$($env:WORKFLOW_NUM)"
|
||||
python patch_version.py $semver
|
||||
environment:
|
||||
WORKFLOW_NUM: << pipeline.number >>
|
||||
- run:
|
||||
name: Build Installer
|
||||
command: speckle-sharp-ci-tools\InnoSetup\ISCC.exe speckle-sharp-ci-tools\sketchup.iss /Sbyparam=$p
|
||||
shell: cmd.exe #does not work in powershell
|
||||
|
||||
#- run:
|
||||
# name: Patch
|
||||
# shell: powershell.exe
|
||||
# command:
|
||||
# | # If no tag, use 0.0.0.1 and don't make any YML (for testing only!)
|
||||
# $tag = if([string]::IsNullOrEmpty($env:CIRCLE_TAG)) { "0.0.0" } else { $env:CIRCLE_TAG }
|
||||
# $semver = if($tag.Contains('/')) {$tag.Split("/")[1] } else { $tag }
|
||||
# $ver = if($semver.Contains('-')) {$semver.Split("-")[0] } else { $semver }
|
||||
# $channel = if($semver.Contains('-')) {$semver.Split("-")[1] } else { "latest" }
|
||||
# $version = "$($ver).$($env:CIRCLE_BUILD_NUM)"
|
||||
# New-Item -Force "speckle-sharp-ci-tools/Installers/sketchup/$channel.yml" -ItemType File -Value "version: $semver"
|
||||
# echo $version
|
||||
# python patch_version.py $semver
|
||||
# speckle-sharp-ci-tools\InnoSetup\ISCC.exe speckle-sharp-ci-tools\sketchup.iss
|
||||
- persist_to_workspace:
|
||||
root: ./
|
||||
paths:
|
||||
- speckle-sharp-ci-tools/Installers
|
||||
|
||||
get-ci-tools: # Clones our ci tools and persists them to the workspace
|
||||
docker:
|
||||
- image: cimg/base:2021.01
|
||||
steps:
|
||||
- add_ssh_keys:
|
||||
fingerprints:
|
||||
- "03:2e:ee:4f:14:67:2b:88:32:e8:cc:f0:cb:df:92:29"
|
||||
- run:
|
||||
name: I know Github as a host
|
||||
command: |
|
||||
mkdir ~/.ssh
|
||||
ssh-keyscan github.com >> ~/.ssh/known_hosts
|
||||
- run:
|
||||
name: Clone
|
||||
command: git clone git@github.com:specklesystems/speckle-sharp-ci-tools.git speckle-sharp-ci-tools
|
||||
- persist_to_workspace:
|
||||
root: ./
|
||||
paths:
|
||||
- speckle-sharp-ci-tools
|
||||
- persist_to_workspace:
|
||||
root: ./
|
||||
paths:
|
||||
- speckle-sharp-ci-tools
|
||||
deploy-manager2:
|
||||
docker:
|
||||
- image: mcr.microsoft.com/dotnet/sdk:6.0
|
||||
parameters:
|
||||
slug:
|
||||
type: string
|
||||
os:
|
||||
type: string
|
||||
extension:
|
||||
type: string
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: ./
|
||||
- run:
|
||||
name: Install Manager Feed CLI
|
||||
command: dotnet tool install --global Speckle.Manager.Feed
|
||||
- run:
|
||||
name: Upload new version
|
||||
command: |
|
||||
TAG=$(if [ "${CIRCLE_TAG}" ]; then echo $CIRCLE_TAG; else echo "0.0.0"; fi;)
|
||||
SEMVER=$(echo "$TAG" | sed -e 's/\/[a-zA-Z-]*//')
|
||||
/root/.dotnet/tools/Speckle.Manager.Feed deploy -s << parameters.slug >> -v ${SEMVER} -u https://releases.speckle.dev/installers/<< parameters.slug >>/<< parameters.slug >>-${SEMVER}.<< parameters.extension >> -o << parameters.os >> -f speckle-sharp-ci-tools/Installers/<< parameters.slug >>/<< parameters.slug >>-${SEMVER}.<< parameters.extension >>
|
||||
|
||||
# Orchestrate our job run sequence
|
||||
workflows:
|
||||
build_and_test:
|
||||
when:
|
||||
false
|
||||
build-and-deploy:
|
||||
jobs:
|
||||
- build
|
||||
- get-ci-tools:
|
||||
filters:
|
||||
tags:
|
||||
only: /.*/
|
||||
|
||||
- build-ui:
|
||||
filters:
|
||||
tags:
|
||||
only: /.*/
|
||||
|
||||
- build-connector:
|
||||
slug: sketchup
|
||||
requires:
|
||||
- get-ci-tools
|
||||
- build-ui
|
||||
filters:
|
||||
tags:
|
||||
only: /.*/
|
||||
context: innosetup
|
||||
|
||||
- deploy-manager2:
|
||||
context: do-spaces-speckle-releases
|
||||
slug: sketchup
|
||||
os: Win
|
||||
extension: exe
|
||||
requires:
|
||||
- get-ci-tools
|
||||
- build-ui
|
||||
- build-connector
|
||||
filters:
|
||||
tags:
|
||||
only: /([0-9]+)\.([0-9]+)\.([0-9]+)(?:-\w+)?$/
|
||||
branches:
|
||||
ignore: /.*/ # For testing only! /ci\/.*/
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
# This workflow will install Python dependencies, run tests and lint with a single version of Python
|
||||
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python
|
||||
|
||||
name: Build
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
workflow_call:
|
||||
outputs:
|
||||
semver:
|
||||
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 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-version.outputs.file-version }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python 3.10
|
||||
uses: actions/setup-python@v3
|
||||
with:
|
||||
python-version: "3.10"
|
||||
|
||||
- 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 }}"
|
||||
|
||||
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.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.set-version.outputs.semver}}
|
||||
path: sketchup.zip
|
||||
retention-days: 1
|
||||
if-no-files-found: error
|
||||
compression-level: 0 # no compression
|
||||
@@ -0,0 +1,78 @@
|
||||
name: Update issue Status
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [closed]
|
||||
|
||||
jobs:
|
||||
update_issue:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Get project data
|
||||
env:
|
||||
GITHUB_TOKEN: ${{secrets.GHPROJECT_TOKEN}}
|
||||
ORGANIZATION: specklesystems
|
||||
PROJECT_NUMBER: 9
|
||||
run: |
|
||||
gh api graphql --header 'GraphQL-Features: projects_next_graphql' -f query='
|
||||
query($org: String!, $number: Int!) {
|
||||
organization(login: $org){
|
||||
projectNext(number: $number) {
|
||||
id
|
||||
fields(first:20) {
|
||||
nodes {
|
||||
id
|
||||
name
|
||||
settings
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}' -f org=$ORGANIZATION -F number=$PROJECT_NUMBER > project_data.json
|
||||
|
||||
echo 'PROJECT_ID='$(jq '.data.organization.projectNext.id' project_data.json) >> $GITHUB_ENV
|
||||
echo 'STATUS_FIELD_ID='$(jq '.data.organization.projectNext.fields.nodes[] | select(.name== "Status") | .id' project_data.json) >> $GITHUB_ENV
|
||||
|
||||
echo "$PROJECT_ID"
|
||||
echo "$STATUS_FIELD_ID"
|
||||
|
||||
echo 'DONE_ID='$(jq '.data.organization.projectNext.fields.nodes[] | select(.name== "Status") | .settings | fromjson | .options[] | select(.name== "Done") | .id' project_data.json) >> $GITHUB_ENV
|
||||
echo "$DONE_ID"
|
||||
|
||||
- name: Add Issue to project #it's already in the project, but we do this to get its node id!
|
||||
env:
|
||||
GITHUB_TOKEN: ${{secrets.GHPROJECT_TOKEN}}
|
||||
ISSUE_ID: ${{ github.event.issue.node_id }}
|
||||
run: |
|
||||
item_id="$( gh api graphql --header 'GraphQL-Features: projects_next_graphql' -f query='
|
||||
mutation($project:ID!, $id:ID!) {
|
||||
addProjectNextItem(input: {projectId: $project, contentId: $id}) {
|
||||
projectNextItem {
|
||||
id
|
||||
}
|
||||
}
|
||||
}' -f project=$PROJECT_ID -f id=$ISSUE_ID --jq '.data.addProjectNextItem.projectNextItem.id')"
|
||||
|
||||
echo 'ITEM_ID='$item_id >> $GITHUB_ENV
|
||||
|
||||
- name: Update Status
|
||||
env:
|
||||
GITHUB_TOKEN: ${{secrets.GHPROJECT_TOKEN}}
|
||||
ISSUE_ID: ${{ github.event.issue.node_id }}
|
||||
run: |
|
||||
gh api graphql --header 'GraphQL-Features: projects_next_graphql' -f query='
|
||||
mutation($project:ID!, $status:ID!, $id:ID!, $value:String!) {
|
||||
set_status: updateProjectNextItemField(
|
||||
input: {
|
||||
projectId: $project
|
||||
itemId: $id
|
||||
fieldId: $status
|
||||
value: $value
|
||||
}
|
||||
) {
|
||||
projectNextItem {
|
||||
id
|
||||
}
|
||||
}
|
||||
}' -f project=$PROJECT_ID -f status=$STATUS_FIELD_ID -f id=$ITEM_ID -f value=${{ env.DONE_ID }}
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
# This workflow will install Python dependencies, run tests and lint with a single version of Python
|
||||
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python
|
||||
|
||||
name: Build and deploy
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["main", "installer-test/**"]
|
||||
tags: ["v3.*.*"] # Manual delivery on every 3.x tag
|
||||
|
||||
jobs:
|
||||
build:
|
||||
uses: ./.github/workflows/build.yml
|
||||
|
||||
deploy-installers:
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
env:
|
||||
IS_PUBLIC_RELEASE: ${{ github.ref_type == 'tag' }}
|
||||
steps:
|
||||
- name: 🔫 Trigger Build Installer(s)
|
||||
uses: the-actions-org/workflow-dispatch@v4.0.0
|
||||
with:
|
||||
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 }}",
|
||||
"repo": "${{ github.repository }}",
|
||||
"is_public_release": ${{ env.IS_PUBLIC_RELEASE }}
|
||||
}'
|
||||
ref: main
|
||||
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-*
|
||||
@@ -0,0 +1,50 @@
|
||||
name: Move new issues into Project
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [opened]
|
||||
|
||||
jobs:
|
||||
track_issue:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Get project data
|
||||
env:
|
||||
GITHUB_TOKEN: ${{secrets.GHPROJECT_TOKEN}}
|
||||
ORGANIZATION: specklesystems
|
||||
PROJECT_NUMBER: 9
|
||||
run: |
|
||||
gh api graphql --header 'GraphQL-Features: projects_next_graphql' -f query='
|
||||
query($org: String!, $number: Int!) {
|
||||
organization(login: $org){
|
||||
projectNext(number: $number) {
|
||||
id
|
||||
fields(first:20) {
|
||||
nodes {
|
||||
id
|
||||
name
|
||||
settings
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}' -f org=$ORGANIZATION -F number=$PROJECT_NUMBER > project_data.json
|
||||
|
||||
echo 'PROJECT_ID='$(jq '.data.organization.projectNext.id' project_data.json) >> $GITHUB_ENV
|
||||
echo 'STATUS_FIELD_ID='$(jq '.data.organization.projectNext.fields.nodes[] | select(.name== "Status") | .id' project_data.json) >> $GITHUB_ENV
|
||||
|
||||
- name: Add Issue to project
|
||||
env:
|
||||
GITHUB_TOKEN: ${{secrets.GHPROJECT_TOKEN}}
|
||||
ISSUE_ID: ${{ github.event.issue.node_id }}
|
||||
run: |
|
||||
item_id="$( gh api graphql --header 'GraphQL-Features: projects_next_graphql' -f query='
|
||||
mutation($project:ID!, $id:ID!) {
|
||||
addProjectNextItem(input: {projectId: $project, contentId: $id}) {
|
||||
projectNextItem {
|
||||
id
|
||||
}
|
||||
}
|
||||
}' -f project=$PROJECT_ID -f id=$ISSUE_ID --jq '.data.addProjectNextItem.projectNextItem.id')"
|
||||
|
||||
echo 'ITEM_ID='$item_id >> $GITHUB_ENV
|
||||
@@ -0,0 +1,38 @@
|
||||
# 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
|
||||
@@ -10,8 +10,8 @@
|
||||
settings.json
|
||||
|
||||
# vue app build dist folder
|
||||
speckle_connector_3/vue_ui
|
||||
speckle_connector_3/html
|
||||
speckle_connector/vue_ui
|
||||
speckle_connector/html
|
||||
|
||||
# speckle-sharp-ci-tools
|
||||
/speckle-sharp-ci-tools
|
||||
|
||||
@@ -18,7 +18,7 @@ AllCops:
|
||||
- '_tools/su_attributes/**/*.rb'
|
||||
- '_sqlite3/**/*.rb'
|
||||
- 'ui/**/*'
|
||||
- 'speckle_connector_3/src/ext/**/*.rb'
|
||||
- 'speckle_connector/src/ext/**/*.rb'
|
||||
- 'vendor/bundle/**/*'
|
||||
- 'tests/**/*.rb'
|
||||
SketchUp:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
require_paths:
|
||||
- "C:/Program Files/SketchUp/SketchUp 2021/Tools"
|
||||
- speckle_connector_3
|
||||
- speckle_connector
|
||||
|
||||
require:
|
||||
- sketchup-api-stubs
|
||||
|
||||
@@ -24,8 +24,6 @@ group :development do
|
||||
gem 'rubycritic', '~> 4.3', '>= 4.3.3', require: false
|
||||
# Auto completions for SketchUp API.
|
||||
gem 'sketchup-api-stubs'
|
||||
# Runtime dependency of skippy for Ruby 3.2. Have it!
|
||||
gem 'sorted_set', '~> 1.0'
|
||||
# Aid with common SketchUp extension tasks.
|
||||
gem 'skippy', '~> 0.5.2.a'
|
||||
gem 'skippy', '~> 0.4.1.a'
|
||||
end
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
addressable (2.8.4)
|
||||
addressable (2.8.1)
|
||||
public_suffix (>= 2.0.2, < 6.0)
|
||||
ast (2.4.2)
|
||||
axiom-types (0.1.1)
|
||||
@@ -26,7 +26,7 @@ GEM
|
||||
path_expander (~> 1.0)
|
||||
ruby_parser (~> 3.1, > 3.1.0)
|
||||
sexp_processor (~> 4.8)
|
||||
git (1.19.1)
|
||||
git (1.12.0)
|
||||
addressable (~> 2.8)
|
||||
rchardet (~> 1.8)
|
||||
ice_nine (0.11.2)
|
||||
@@ -48,10 +48,10 @@ GEM
|
||||
pry (0.14.1)
|
||||
coderay (~> 1.1)
|
||||
method_source (~> 1.0)
|
||||
public_suffix (5.0.1)
|
||||
psych (3.3.4)
|
||||
public_suffix (5.0.0)
|
||||
rainbow (3.1.1)
|
||||
rake (13.0.6)
|
||||
rbtree (0.4.6)
|
||||
rchardet (1.8.0)
|
||||
reek (6.1.1)
|
||||
kwalify (~> 0.7.0)
|
||||
@@ -90,7 +90,6 @@ GEM
|
||||
simplecov (>= 0.17.0)
|
||||
tty-which (~> 0.4.0)
|
||||
virtus (~> 1.0)
|
||||
set (1.1.0)
|
||||
sexp_processor (4.16.1)
|
||||
simplecov (0.21.2)
|
||||
docile (~> 1.1)
|
||||
@@ -99,15 +98,11 @@ GEM
|
||||
simplecov-html (0.12.3)
|
||||
simplecov_json_formatter (0.1.4)
|
||||
sketchup-api-stubs (0.7.8)
|
||||
skippy (0.5.2.a)
|
||||
skippy (0.4.3.a)
|
||||
git (~> 1.3)
|
||||
naturally (~> 2.1)
|
||||
sorted_set (~> 1.0)
|
||||
thor (>= 0.19, < 2.0)
|
||||
sorted_set (1.0.3)
|
||||
rbtree
|
||||
set (~> 1.0)
|
||||
thor (1.3.1)
|
||||
thor (~> 0.19)
|
||||
thor (0.20.3)
|
||||
thread_safe (0.3.6)
|
||||
tty-which (0.4.2)
|
||||
unicode-display_width (1.8.0)
|
||||
@@ -118,7 +113,6 @@ GEM
|
||||
equalizer (~> 0.0, >= 0.0.9)
|
||||
|
||||
PLATFORMS
|
||||
x64-mingw-ucrt
|
||||
x64-mingw32
|
||||
x64-unknown
|
||||
x86_64-linux
|
||||
@@ -134,8 +128,7 @@ DEPENDENCIES
|
||||
rubocop-sketchup
|
||||
rubycritic (~> 4.3, >= 4.3.3)
|
||||
sketchup-api-stubs
|
||||
skippy (~> 0.5.2.a)
|
||||
sorted_set (~> 1.0)
|
||||
skippy (~> 0.4.1.a)
|
||||
|
||||
BUNDLED WITH
|
||||
2.3.25
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
workflow: GitFlow/v1
|
||||
next-version: 3.0.0
|
||||
mode: ManualDeployment
|
||||
branches:
|
||||
main:
|
||||
label: rc
|
||||
develop:
|
||||
regex: ^dui3/alpha$
|
||||
label: beta
|
||||
unknown:
|
||||
increment: None
|
||||
@@ -49,9 +49,9 @@ This repo is split into three parts:
|
||||
### 1. **Speckle Connector extension**
|
||||
|
||||
Includes the `ruby` source files to run extension on SketchUp environment. SketchUp Extensions are composed of
|
||||
a **.rb** file as entry and **folder** that .rb file refers to. In our case entry file is `speckle_connector_3.rb`
|
||||
a **.rb** file as entry and **folder** that .rb file refers to. In our case entry file is `speckle_connector.rb`
|
||||
that responsible to register Speckle Connector extension to SketchUp and also it shows address to where extension
|
||||
will start to read extension. Source folder is `speckle_connector_3`.
|
||||
will start to read extension. Source folder is `speckle_connector`.
|
||||
|
||||
### 2. **User Interface**
|
||||
|
||||
@@ -64,7 +64,7 @@ This repo is split into three parts:
|
||||
we use extensions as native part of the source `ruby` code.
|
||||
|
||||
After building `sqlite3.sln` file, compiled `sqlite3.so` (for Windows) and `sqlite3.bundle` (for OSX) dynamic library files are created
|
||||
by solution to place them into source code into `speckle_connector_3/src/ext`. Building this project should be only
|
||||
by solution to place them into source code into `speckle_connector/src/ext`. Building this project should be only
|
||||
happen when SketchUp starts to support newer Ruby versions (currently it is `2.7`).
|
||||
|
||||
## Contribution Guide
|
||||
@@ -115,23 +115,9 @@ 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`
|
||||
1. Find already prepared `speckle_connector_loader.rb` file on the `_tools`
|
||||
folder.
|
||||
2. Copy this Ruby file into your SketchUp Plugins directory. You will likely find this at:
|
||||
`C:\Users\{YOU}\AppData\Roaming\SketchUp\SketchUp 20XX\SketchUp\Plugins`
|
||||
|
||||
@@ -32,12 +32,12 @@ end
|
||||
|
||||
# Glob pattern to match source files. Defaults to FileList['.'].
|
||||
ruby_critic_paths = FileList[
|
||||
'speckle_connector_3/**/*.rb',
|
||||
'speckle_connector_3.rb',
|
||||
'speckle_connector/**/*.rb',
|
||||
'speckle_connector.rb',
|
||||
'tests/**/*.rb'] -
|
||||
FileList[
|
||||
'_tools/**/*.rb',
|
||||
'speckle_connector_3/src/ext/**/*.rb',
|
||||
'speckle_connector/src/ext/**/*.rb',
|
||||
]
|
||||
|
||||
# for local
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
# This is for automated pre-debugger configuration.
|
||||
# We run skippy first, then activate debugger.
|
||||
# The purpose of this file to wait till skp is live
|
||||
|
||||
# To establish a configuration
|
||||
# 1. Create 'Run External Tool' before lunch step
|
||||
# 2. Program -> C:\Ruby32-x64\bin\ruby.exe or whatever
|
||||
# 3. Arguments -> C:\Users\KORAL\Documents\Git\Speckle\speckle-sketchup\_tools\debugger\bundle_exec_2024.rb or whatever
|
||||
# 4. Working directory -> C:\Users\KORAL\Documents\Git\Speckle\speckle-sketchup or whatever
|
||||
|
||||
# Add a delay of 10 seconds, it is arbitrary, do not hesitate to change for what works best for you
|
||||
sleep(10)
|
||||
|
||||
# Execute the original command
|
||||
exec('bundle exec skippy sketchup:debug 2024')
|
||||
@@ -24,7 +24,7 @@ module JF_RubyToolbar
|
||||
def self.load_toolbar
|
||||
@last_dir = "#{$LOAD_PATH[0]}/"
|
||||
@last_dir = @last_dir.gsub('/', '\\\\\\\\')
|
||||
@last_dir = File.join($JF_RUBYTOOLBAR, 'speckle_connector_3')
|
||||
@last_dir = File.join($JF_RUBYTOOLBAR, 'speckle_connector')
|
||||
curdir = File.dirname __FILE__
|
||||
|
||||
# create toolbar
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
# Create a link to Plugins folder with this command
|
||||
|
||||
# rubocop:disable Layout/LineLength
|
||||
# New-Item -ItemType SymbolicLink -Path '~\AppData\Roaming\SketchUp\SketchUp 2022\SketchUp\Plugins\speckle_connector_3_loader.rb' -Target ~\Git\Speckle\speckle-sketchup\_tools\speckle_connector_3_loader.rb
|
||||
# New-Item -ItemType SymbolicLink -Path '~\AppData\Roaming\SketchUp\SketchUp 2022\SketchUp\Plugins\speckle_connector_loader.rb' -Target ~\Git\Speckle\speckle-sketchup\_tools\speckle_connector_loader.rb
|
||||
# rubocop:enable Layout/LineLength
|
||||
|
||||
SKETCHUP_CONSOLE.show # if you want to show Ruby console on startup
|
||||
@@ -32,7 +32,7 @@ $LOAD_PATH << File.join(speckle_path, '_tools')
|
||||
$JF_RUBYTOOLBAR = speckle_path
|
||||
# rubocop:enable Style/GlobalVars
|
||||
|
||||
files = %w[speckle_connector_3 jf_RubyPanel su_attributes]
|
||||
files = %w[speckle_connector jf_RubyPanel su_attributes]
|
||||
|
||||
files.each do |ruby_file|
|
||||
puts "Loading #{ruby_file}"
|
||||
@@ -4,7 +4,7 @@ import sys
|
||||
|
||||
def patch_connector(tag):
|
||||
"""Patches the connector version within the connector file"""
|
||||
rb_file = "speckle_connector_3.rb"
|
||||
rb_file = "speckle_connector.rb"
|
||||
|
||||
with open(rb_file, "r") as file:
|
||||
lines = file.readlines()
|
||||
@@ -15,12 +15,6 @@ def patch_connector(tag):
|
||||
print(f"Patched connector version number in {rb_file}")
|
||||
break
|
||||
|
||||
for (index, line) in enumerate(lines):
|
||||
if 'DEV_MODE = ' in line:
|
||||
lines[index] = f' DEV_MODE = false\n'
|
||||
print(f"Patched dev mode to false in {rb_file}")
|
||||
break
|
||||
|
||||
with open(rb_file, "w") as file:
|
||||
file.writelines(lines)
|
||||
|
||||
@@ -51,7 +45,7 @@ def main():
|
||||
|
||||
print(f"Patching version: {tag}")
|
||||
patch_connector(tag)
|
||||
# patch_installer(tag)
|
||||
patch_installer(tag)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -4,7 +4,7 @@ require 'sketchup'
|
||||
require 'extensions'
|
||||
|
||||
# Speckle connector module to enable multiplayer mode ON!
|
||||
module SpeckleConnector3
|
||||
module SpeckleConnector
|
||||
# Version - patched by CI
|
||||
CONNECTOR_VERSION = '0.0.0'
|
||||
|
||||
@@ -23,11 +23,11 @@ module SpeckleConnector3
|
||||
PATH = File.join(PATH_ROOT, folder_name).freeze
|
||||
|
||||
# Run from localhost or from build files
|
||||
DEV_MODE = true
|
||||
DEV_MODE = false
|
||||
puts("Loading Speckle Connector v#{CONNECTOR_VERSION} from #{DEV_MODE ? 'dev' : 'build'}")
|
||||
|
||||
unless file_loaded?(__FILE__)
|
||||
ex = SketchupExtension.new('Speckle SketchUp v3', File.join(PATH, 'bootstrap'))
|
||||
ex = SketchupExtension.new('Speckle SketchUp', File.join(PATH, 'bootstrap'))
|
||||
ex.description = 'Speckle Connector for SketchUp'
|
||||
ex.version = CONNECTOR_VERSION
|
||||
ex.copyright = 'AEC Systems Ltd.'
|
||||
@@ -2,8 +2,7 @@
|
||||
|
||||
require 'sketchup'
|
||||
require 'pathname'
|
||||
require 'speckle_connector_3/debug'
|
||||
require_relative 'src/log/log'
|
||||
require 'speckle_connector/debug'
|
||||
require_relative 'src/ui/sketchup_ui'
|
||||
require_relative 'src/ui/ui_controller'
|
||||
require_relative 'src/commands/menu_command_handler'
|
||||
@@ -13,7 +12,7 @@ require_relative 'src/states/initial_state'
|
||||
require_relative 'src/commands/speckle_menu_commands'
|
||||
|
||||
# Speckle Connector on SketchUp to enable Multiplayer mode ON!
|
||||
module SpeckleConnector3
|
||||
module SpeckleConnector
|
||||
SKETCHUP_VERSION = Sketchup.version.to_i
|
||||
|
||||
dir = __dir__.dup
|
||||
@@ -24,9 +23,9 @@ module SpeckleConnector3
|
||||
sketchup_ui = Ui::SketchupUi.new
|
||||
ui_controller = Ui::UiController.new(sketchup_ui)
|
||||
menu_commands = Commands::MenuCommandHandler.new
|
||||
user_state = SpeckleConnector3::States::UserState.new({})
|
||||
initial_state = SpeckleConnector3::States::InitialState.new(user_state)
|
||||
app = SpeckleConnector3::App::SpeckleConnectorApp.new(menu_commands, initial_state, ui_controller)
|
||||
user_state = SpeckleConnector::States::UserState.new({})
|
||||
initial_state = SpeckleConnector::States::InitialState.new(user_state)
|
||||
app = SpeckleConnector::App::SpeckleConnectorApp.new(menu_commands, initial_state, ui_controller)
|
||||
# Add menu commands to SketchUp and Speckle application
|
||||
Commands::SpeckleMenuCommands.add_initial_commands!(app)
|
||||
app
|
||||
@@ -1,7 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Speckle connector module to enable multiplayer mode ON!
|
||||
module SpeckleConnector3
|
||||
module SpeckleConnector
|
||||
# from thomthom
|
||||
# https://github.com/thomthom/true-bend/blob/master/src/tt_truebend/debug.rb
|
||||
|
||||
@@ -15,9 +15,7 @@ module SpeckleConnector3
|
||||
def self.reload
|
||||
load(__FILE__)
|
||||
pattern = File.join(__dir__, '**/*.rb')
|
||||
# TODO: Here is a opportunity to improve reloading process.
|
||||
# We can cache last edited time of the each file later to check which file need to be reloaded.
|
||||
Dir.glob(pattern).each { |file| load(file) unless file.include?('bootstrap') }
|
||||
Dir.glob(pattern).each { |file| load(file) }
|
||||
.size
|
||||
end
|
||||
# rubocop:enable SketchupSuggestions/FileEncoding
|
||||
|
Before Width: | Height: | Size: 798 B After Width: | Height: | Size: 798 B |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 665 B After Width: | Height: | Size: 665 B |
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 639 B After Width: | Height: | Size: 639 B |
|
After Width: | Height: | Size: 2.4 KiB |
@@ -0,0 +1,31 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'JSON'
|
||||
require_relative '../ext/sqlite3'
|
||||
require_relative '../constants/path_constants'
|
||||
|
||||
module SpeckleConnector
|
||||
# Accounts to communicate with models on user's account.
|
||||
module Accounts
|
||||
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"
|
||||
)
|
||||
end
|
||||
|
||||
db = Sqlite3::Database.new(db_path)
|
||||
rows = db.exec('SELECT * FROM objects')
|
||||
db.close
|
||||
rows.map { |row| JSON.parse(row[1]) }
|
||||
end
|
||||
|
||||
def self.default_account
|
||||
accounts = load_accounts
|
||||
accounts.select { |acc| acc['isDefault'] }[0] || accounts[0]
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,6 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module SpeckleConnector3
|
||||
module SpeckleConnector
|
||||
module Actions
|
||||
# State changer object.
|
||||
class Action
|
||||
@@ -3,7 +3,7 @@
|
||||
require_relative 'action'
|
||||
require_relative 'deactivate_diffing'
|
||||
|
||||
module SpeckleConnector3
|
||||
module SpeckleConnector
|
||||
module Actions
|
||||
# Deactivate diffing for stream.
|
||||
class ActivateDiffing < Action
|
||||
@@ -15,7 +15,7 @@ module SpeckleConnector3
|
||||
# @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|
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
require_relative 'action'
|
||||
|
||||
module SpeckleConnector3
|
||||
module SpeckleConnector
|
||||
module Actions
|
||||
# Adds material to speckle state and Sketchup.
|
||||
class AddMaterial < Action
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
require_relative 'action'
|
||||
|
||||
module SpeckleConnector3
|
||||
module SpeckleConnector
|
||||
module Actions
|
||||
# Clear queue from state.
|
||||
class ClearQueue < Action
|
||||
@@ -4,13 +4,13 @@ require_relative 'action'
|
||||
require_relative '../ext/sqlite3'
|
||||
require_relative '../constants/path_constants'
|
||||
|
||||
module SpeckleConnector3
|
||||
module SpeckleConnector
|
||||
module Actions
|
||||
# Action to collect preferences from database to UI.
|
||||
class CollectPreferences < Action
|
||||
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
|
||||
# @return [States::State] the new updated state object
|
||||
def self.update_state(state, _resolve_id, _data)
|
||||
def self.update_state(state, _data)
|
||||
state.with_add_queue('collectPreferences', state.user_state.preferences.to_json, [])
|
||||
end
|
||||
end
|
||||
@@ -2,16 +2,16 @@
|
||||
|
||||
require_relative 'action'
|
||||
|
||||
module SpeckleConnector3
|
||||
module SpeckleConnector
|
||||
module Actions
|
||||
# Action to collect versions from sketchup and connector to track user's version by mixpanel.
|
||||
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: SpeckleConnector3::CONNECTOR_VERSION
|
||||
speckle: SpeckleConnector::CONNECTOR_VERSION
|
||||
}
|
||||
state.with_add_queue('collectVersions', versions.to_json, [])
|
||||
end
|
||||
@@ -1,6 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module SpeckleConnector3
|
||||
module SpeckleConnector
|
||||
module Actions
|
||||
# Action to update connected state of application.
|
||||
class Connected < Action
|
||||
@@ -6,7 +6,7 @@ require_relative '../actions/save_stream'
|
||||
require_relative '../actions/queue_send'
|
||||
require_relative '../convertors/converter'
|
||||
|
||||
module SpeckleConnector3
|
||||
module SpeckleConnector
|
||||
module Actions
|
||||
# Create stream.
|
||||
class CreateStream < Action
|
||||
@@ -2,13 +2,13 @@
|
||||
|
||||
require_relative 'action'
|
||||
|
||||
module SpeckleConnector3
|
||||
module SpeckleConnector
|
||||
module Actions
|
||||
# Deactivate diffing.
|
||||
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,11 +1,10 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative 'event_action'
|
||||
require_relative 'on_document_changed'
|
||||
require_relative '../load_sketchup_model'
|
||||
require_relative '../collect_preferences'
|
||||
|
||||
module SpeckleConnector3
|
||||
module SpeckleConnector
|
||||
module Actions
|
||||
module Events
|
||||
# Handle events that are triggered by the {AppObserver}.
|
||||
@@ -26,8 +25,7 @@ module SpeckleConnector3
|
||||
# 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
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative 'event_action'
|
||||
require_relative '../../sketchup_model/utils/face_utils'
|
||||
require_relative '../../constants/dict_constants'
|
||||
|
||||
module SpeckleConnector
|
||||
module Actions
|
||||
module Events
|
||||
# Event actions related to entities.
|
||||
class EntitiesEventAction < EventAction
|
||||
# Event action when element added.
|
||||
class OnElementAdded
|
||||
# @param state [States::State] the current state of the SpeckleConnector Application
|
||||
def self.update_state(state, event_data)
|
||||
modified_entities = event_data.to_a.collect { |e| e[1] }
|
||||
# do not copy speckle base object specific attributes, because they are entity specific
|
||||
modified_entities.each { |entity| entity.delete_attribute(SPECKLE_BASE_OBJECT) }
|
||||
state
|
||||
end
|
||||
end
|
||||
|
||||
# Event action when element modified.
|
||||
class OnElementModified
|
||||
# @param state [States::State] the current state of the SpeckleConnector Application
|
||||
def self.update_state(state, event_data)
|
||||
speckle_state = state.speckle_state
|
||||
modified_entity = event_data[0][1]
|
||||
if modified_entity.is_a?(Sketchup::Face)
|
||||
path = state.sketchup_state.sketchup_model.active_path
|
||||
modified_faces = SketchupModel::Utils::FaceUtils.near_faces(modified_entity.edges)
|
||||
path_objects = path.nil? ? [] : path + path.collect(&:definition)
|
||||
parent_ids = path_objects.collect(&:persistent_id)
|
||||
ids_to_invalidate = modified_faces.collect(&:persistent_id) + parent_ids
|
||||
entities_to_invalidate = speckle_entities_to_invalidate(speckle_state, ids_to_invalidate)
|
||||
new_speckle_state = invalidate_speckle_entities(speckle_state, entities_to_invalidate)
|
||||
# This is the place we can send information to UI for diffing check
|
||||
diffing = state.user_state.preferences[:user][:diffing]
|
||||
new_speckle_state = new_speckle_state.with_invalid_streams_queue if diffing
|
||||
return state.with_speckle_state(new_speckle_state)
|
||||
end
|
||||
|
||||
state
|
||||
end
|
||||
|
||||
# @param speckle_state [States::SpeckleState] the current state of the Speckle
|
||||
def self.speckle_entities_to_invalidate(speckle_state, ids)
|
||||
speckle_state.speckle_entities.to_h.select { |id, _| ids.include?(id) }
|
||||
end
|
||||
|
||||
# @param speckle_state [States::SpeckleState] the current state of the Speckle
|
||||
def self.invalidate_speckle_entities(speckle_state, entities_to_invalidate)
|
||||
speckle_entities = speckle_state.speckle_entities
|
||||
entities_to_invalidate.each do |id, speckle_entity|
|
||||
edited_speckle_entity = speckle_entity.with_invalid
|
||||
speckle_entities = speckle_entities.put(id, edited_speckle_entity)
|
||||
end
|
||||
speckle_state.with_speckle_entities(speckle_entities)
|
||||
end
|
||||
end
|
||||
|
||||
# Event action when element removed.
|
||||
class OnElementRemoved
|
||||
# @param state [States::State] the current state of the SpeckleConnector Application
|
||||
def self.update_state(state, _event_data)
|
||||
# TODO: Do state updates when element removed
|
||||
state
|
||||
end
|
||||
end
|
||||
|
||||
# Handlers that are used to handle specific events
|
||||
ACTIONS = {
|
||||
onElementRemoved: OnElementRemoved,
|
||||
onElementAdded: OnElementAdded,
|
||||
onElementModified: OnElementModified
|
||||
}.freeze
|
||||
|
||||
def self.actions
|
||||
ACTIONS
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,6 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module SpeckleConnector3
|
||||
module SpeckleConnector
|
||||
module Actions
|
||||
# This module contains actions that are performed to handle events triggered by observers in Sketchup.
|
||||
module Events
|
||||
@@ -3,7 +3,7 @@
|
||||
require_relative 'event_action'
|
||||
require_relative '../load_sketchup_model'
|
||||
|
||||
module SpeckleConnector3
|
||||
module SpeckleConnector
|
||||
module Actions
|
||||
module Events
|
||||
# Handle events that are triggered by the {ModelObserver}.
|
||||
@@ -18,22 +18,12 @@ module SpeckleConnector3
|
||||
sketchup_state = state.sketchup_state
|
||||
active_path = sketchup_state.sketchup_model.active_path
|
||||
observers = state.speckle_state.observers
|
||||
update_entity_observers(active_path, observers)
|
||||
update_object_observers(active_path, observers)
|
||||
return state
|
||||
end
|
||||
|
||||
def self.update_entity_observers(path, observers)
|
||||
unless path.nil?
|
||||
new_path_entities = path[-1].definition.entities
|
||||
new_path_entities.add_observer(observers[ENTITIES_OBSERVER])
|
||||
# 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])
|
||||
edge.end.add_observer(observers[ENTITY_OBSERVER])
|
||||
end
|
||||
end
|
||||
def self.update_object_observers(path, observers)
|
||||
path[-1].definition.entities.add_observer(observers[ENTITIES_OBSERVER]) unless path.nil?
|
||||
end
|
||||
end
|
||||
|
||||
@@ -4,13 +4,13 @@ require_relative 'action'
|
||||
require_relative '../accounts/accounts'
|
||||
require_relative 'load_saved_streams'
|
||||
|
||||
module SpeckleConnector3
|
||||
module SpeckleConnector
|
||||
module Actions
|
||||
# Action to initialize local accounts from database.
|
||||
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, [])
|
||||
@@ -4,9 +4,9 @@ require_relative 'action'
|
||||
require_relative 'add_material'
|
||||
require_relative '../constants/mat_constants'
|
||||
|
||||
module SpeckleConnector3
|
||||
module SpeckleConnector
|
||||
module Actions
|
||||
# Action to initialize materials (legacy for diff colors)
|
||||
# Action to initialize materials
|
||||
class InitializeMaterials < Action
|
||||
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
|
||||
# @return [States::State] the new updated state object
|
||||
@@ -7,26 +7,22 @@ require_relative '../states/sketchup_state'
|
||||
require_relative '../accounts/accounts'
|
||||
require_relative '../preferences/preferences'
|
||||
require_relative '../constants/observer_constants'
|
||||
require_relative '../ext/worker'
|
||||
|
||||
module SpeckleConnector3
|
||||
module SpeckleConnector
|
||||
module Actions
|
||||
# Initialization of the real state of the speckle.
|
||||
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 = SpeckleConnector3::Worker.new([])
|
||||
def self.update_state(state, observers)
|
||||
attach_app_observer!(observers[APP_OBSERVER])
|
||||
accounts = SpeckleConnector3::Accounts.load_accounts
|
||||
accounts = SpeckleConnector::Accounts.load_accounts
|
||||
speckle_state = States::SpeckleState.new(accounts, observers, {}, {})
|
||||
# This should be the only point that `Sketchup_active_model` passed to application state.
|
||||
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)
|
||||
# This is where we attach observers to related model objects like selection, entities..
|
||||
state = States::State.new(user_state_with_preferences, speckle_state, sketchup_state, false)
|
||||
Actions::LoadSketchupModel.update_state(state, sketchup_state.sketchup_model)
|
||||
end
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
|
||||
require_relative 'action'
|
||||
|
||||
module SpeckleConnector3
|
||||
module SpeckleConnector
|
||||
module Actions
|
||||
# Action to load saved streams.
|
||||
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, [])
|
||||
@@ -3,13 +3,12 @@
|
||||
require_relative 'action'
|
||||
require_relative 'initialize_materials'
|
||||
require_relative '../sketchup_model/reader/speckle_entities_reader'
|
||||
require_relative '../sketchup_model/reader/mapper_reader'
|
||||
require_relative '../preferences/preferences'
|
||||
require_relative '../states/state'
|
||||
require_relative '../states/sketchup_state'
|
||||
require_relative '../constants/observer_constants'
|
||||
|
||||
module SpeckleConnector3
|
||||
module SpeckleConnector
|
||||
module Actions
|
||||
# Switch sketchup model wit a new one
|
||||
class LoadSketchupModel < Action
|
||||
@@ -23,16 +22,13 @@ module SpeckleConnector3
|
||||
new_sketchup_state = States::SketchupState.new(sketchup_model)
|
||||
sketchup_model.rendering_options['DisplaySectionPlanes'] = true
|
||||
new_state = state.with(:@sketchup_state => new_sketchup_state)
|
||||
# Init materials again
|
||||
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))
|
||||
# POC: Reconsider it when we will do caching between sessions!
|
||||
# new_speckle_state = new_speckle_state.with_empty_object_references
|
||||
# Read mapped entities
|
||||
# new_mapped_entities = SketchupModel::Reader::MapperReader.read_mapped_entities(sketchup_model.entities)
|
||||
# new_speckle_state = new_speckle_state.with_mapped_entities(Immutable::Hash.new(new_mapped_entities))
|
||||
# new_state = new_state.with_speckle_state(new_speckle_state)
|
||||
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_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)
|
||||
@@ -46,13 +42,11 @@ module SpeckleConnector3
|
||||
# @param sketchup_model [Sketchup::Model] the model to attach observers to
|
||||
# @param observers [Hash{Class=>}] the observer objects indexed by their class that will be attached
|
||||
def self.attach_observers(sketchup_model, observers)
|
||||
selection = sketchup_model.selection
|
||||
selection.add_observer(observers[SELECTION_OBSERVER])
|
||||
# selection = sketchup_model.selection
|
||||
# selection.add_observer(observers[SELECTION_OBSERVER_NAME])
|
||||
# layers = sketchup_model.layers
|
||||
# layers.add_observer(observers[LAYERS_OBSERVER_NAME])
|
||||
entities = sketchup_model.entities
|
||||
edges = entities.grep(Sketchup::Edge)
|
||||
edges.each { |edge| edge.add_observer(observers[ENTITY_OBSERVER]) }
|
||||
entities.add_observer(observers[ENTITIES_OBSERVER])
|
||||
sketchup_model.add_observer(observers[MODEL_OBSERVER])
|
||||
# materials = sketchup_model.materials
|
||||
@@ -6,7 +6,7 @@ require_relative '../accounts/accounts'
|
||||
require_relative '../constants/path_constants'
|
||||
require_relative '../sketchup_model/dictionary/speckle_model_dictionary_handler'
|
||||
|
||||
module SpeckleConnector3
|
||||
module SpeckleConnector
|
||||
module Actions
|
||||
# When preference updated by UI.
|
||||
class ModelPreferencesUpdated < Action
|
||||
@@ -3,24 +3,21 @@
|
||||
require_relative 'action'
|
||||
require_relative 'events/app_event_action'
|
||||
require_relative 'events/entities_event_action'
|
||||
require_relative 'events/entity_event_action'
|
||||
require_relative 'events/model_event_action'
|
||||
require_relative 'events/selection_event_action'
|
||||
require_relative '../constants/observer_constants'
|
||||
|
||||
module SpeckleConnector3
|
||||
module SpeckleConnector
|
||||
module Actions
|
||||
# Handle events that were collected by observers
|
||||
class OnEventsAction < Action
|
||||
RUN_ORDER = {
|
||||
APP_OBSERVER => Events::AppEventAction,
|
||||
ENTITY_OBSERVER => Events::EntityEventAction,
|
||||
ENTITIES_OBSERVER => Events::EntitiesEventAction,
|
||||
MODEL_OBSERVER => Events::ModelEventAction,
|
||||
MODEL_OBSERVER => Events::ModelEventAction
|
||||
# MATERIALS_OBSERVER => Events::MaterialsEventAction,
|
||||
# LAYERS_OBSERVER => Events::LayerEventAction,
|
||||
# PAGES_OBSERVER => Events::PagesEventAction,
|
||||
SELECTION_OBSERVER => Events::SelectionEventAction
|
||||
# SELECTION_OBSERVER => Events::SelectionEventAction
|
||||
}.freeze
|
||||
|
||||
def self.update_state(state, events)
|
||||
@@ -0,0 +1,45 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative 'action'
|
||||
require_relative '../accounts/accounts'
|
||||
require_relative '../actions/create_stream'
|
||||
require_relative '../actions/queue_send'
|
||||
require_relative '../convertors/to_speckle'
|
||||
|
||||
module SpeckleConnector
|
||||
module Actions
|
||||
# Sends to speckle.
|
||||
class OneClickSend < 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)
|
||||
puts 'send to speckle'
|
||||
default_account = Accounts.default_account
|
||||
if default_account.nil?
|
||||
puts 'No local account found. Please refer to speckle.guide for more information.'
|
||||
return state
|
||||
end
|
||||
sketchup_model = state.sketchup_state.sketchup_model
|
||||
to_convert = sketchup_model.selection.count > 0 ? sketchup_model.selection : sketchup_model.entities
|
||||
first_saved_stream = first_saved_stream(sketchup_model)
|
||||
action = if first_saved_stream.nil?
|
||||
Actions::CreateStream.new
|
||||
else
|
||||
Actions::QueueSend.new(first_saved_stream, convert_to_speckle(sketchup_model, to_convert))
|
||||
end
|
||||
|
||||
action.update_state(state)
|
||||
end
|
||||
|
||||
def self.first_saved_stream(model)
|
||||
(saved_streams = model.attribute_dictionary('speckle', true)['streams']) or []
|
||||
saved_streams.nil? || saved_streams.empty? ? nil : saved_streams[0]
|
||||
end
|
||||
|
||||
def self.convert_to_speckle(sketchup_model, to_convert)
|
||||
converter = Converters::ToSpeckle.new(sketchup_model)
|
||||
to_convert.map { |entity| converter.convert(entity) }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -5,7 +5,7 @@ require_relative '../states/state'
|
||||
require_relative '../states/speckle_state'
|
||||
require_relative '../actions/send_from_queue'
|
||||
|
||||
module SpeckleConnector3
|
||||
module SpeckleConnector
|
||||
module Actions
|
||||
# Send queue from state.
|
||||
class QueueSend < Action
|
||||
@@ -3,15 +3,13 @@
|
||||
require_relative 'action'
|
||||
require_relative '../convertors/units'
|
||||
require_relative '../convertors/to_native'
|
||||
require_relative '../operations/receive'
|
||||
require_relative '../convertors/clean_up'
|
||||
|
||||
module SpeckleConnector3
|
||||
module SpeckleConnector
|
||||
module Actions
|
||||
# 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,24 +17,16 @@ module SpeckleConnector3
|
||||
@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
|
||||
state.sketchup_state.sketchup_model.start_operation('Receive Speckle Objects', true)
|
||||
state = converter.receive_commit_object(@base)
|
||||
if state.user_state.model_preferences[:merge_coplanar_faces]
|
||||
Converters::CleanUp.merge_coplanar_faces(converter.converted_faces)
|
||||
end
|
||||
state.sketchup_state.sketchup_model.commit_operation
|
||||
elapsed_time = (Time.now.to_f - start_time).round(3)
|
||||
puts "==== Converting to Native executed in #{elapsed_time} sec ===="
|
||||
puts "==== Source application is #{@source_app}. ===="
|
||||
@@ -4,13 +4,13 @@ require_relative 'action'
|
||||
require_relative '../accounts/accounts'
|
||||
require_relative 'load_saved_streams'
|
||||
|
||||
module SpeckleConnector3
|
||||
module SpeckleConnector
|
||||
module Actions
|
||||
# Action to reload accounts from database.
|
||||
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)
|
||||
@@ -5,7 +5,7 @@ require_relative '../accounts/accounts'
|
||||
require_relative '../convertors/units'
|
||||
require_relative '../convertors/converter'
|
||||
|
||||
module SpeckleConnector3
|
||||
module SpeckleConnector
|
||||
module Actions
|
||||
# Action to remove stream.
|
||||
# Currently it is not a state changer.
|
||||
@@ -3,7 +3,7 @@
|
||||
require_relative 'action'
|
||||
require_relative '../accounts/accounts'
|
||||
|
||||
module SpeckleConnector3
|
||||
module SpeckleConnector
|
||||
module Actions
|
||||
# Save stream.
|
||||
# Currently it is not a state changer.
|
||||
@@ -3,7 +3,7 @@
|
||||
require_relative 'action'
|
||||
require_relative '../accounts/accounts'
|
||||
|
||||
module SpeckleConnector3
|
||||
module SpeckleConnector
|
||||
module Actions
|
||||
# Send already converted objects from queue if exist on stream.
|
||||
class SendFromQueue < Action
|
||||
@@ -4,9 +4,8 @@ require_relative 'action'
|
||||
require_relative 'deactivate_diffing'
|
||||
require_relative '../convertors/units'
|
||||
require_relative '../convertors/to_speckle'
|
||||
require_relative '../operations/send'
|
||||
|
||||
module SpeckleConnector3
|
||||
module SpeckleConnector
|
||||
module Actions
|
||||
# Send selection to server.
|
||||
class SendSelection < Action
|
||||
@@ -18,13 +17,11 @@ module SpeckleConnector3
|
||||
# @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, {})
|
||||
new_speckle_state, base = converter.convert_selection_to_base
|
||||
id, batches = converter.serialize(base, state.user_state.preferences)
|
||||
# TODO: Later active send operation.
|
||||
# Operations.send(@stream_id, batches)
|
||||
|
||||
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)
|
||||
puts("converted #{base.count} objects for stream #{@stream_id}")
|
||||
|
||||
# This is the place we can send information to UI for diffing check
|
||||
@@ -35,7 +32,7 @@ module SpeckleConnector3
|
||||
new_state.with_add_queue('convertedFromSketchup', @stream_id, [
|
||||
{ is_string: false, val: batches },
|
||||
{ is_string: true, val: id },
|
||||
{ is_string: false, val: 0 }
|
||||
{ is_string: false, val: total_children_count }
|
||||
])
|
||||
end
|
||||
end
|
||||
@@ -5,7 +5,7 @@ require_relative '../ext/sqlite3'
|
||||
require_relative '../accounts/accounts'
|
||||
require_relative '../constants/path_constants'
|
||||
|
||||
module SpeckleConnector3
|
||||
module SpeckleConnector
|
||||
module Actions
|
||||
# When preference updated by UI.
|
||||
class UserPreferencesUpdated < Action
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
require_relative '../actions/clear_queue'
|
||||
|
||||
module SpeckleConnector3
|
||||
module SpeckleConnector
|
||||
module App
|
||||
# Application for the Speckle Connector.
|
||||
class SpeckleConnectorApp
|
||||
@@ -28,32 +28,25 @@ module SpeckleConnector3
|
||||
state.speckle_state?
|
||||
end
|
||||
|
||||
# Attach observers to application when speckle initialized via menu commands.
|
||||
def update_ui!
|
||||
ui_controller.update_ui(state)
|
||||
end
|
||||
|
||||
def send_messages!
|
||||
queue = @state.speckle_state.message_queue
|
||||
queue.each_value { |value| ui_controller.user_interfaces[Ui::SPECKLE_UI_ID].dialog.execute_script(value) }
|
||||
update_state!(Actions::ClearQueue)
|
||||
end
|
||||
|
||||
def add_observer_handler!(observer_handler)
|
||||
@observer_handler = observer_handler
|
||||
end
|
||||
|
||||
# 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
|
||||
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
|
||||
@@ -1,6 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module SpeckleConnector3
|
||||
module SpeckleConnector
|
||||
module Callbacks
|
||||
# Helper class to serialize messages to send dialog.
|
||||
class CallbackMessage
|
||||
@@ -2,15 +2,15 @@
|
||||
|
||||
require_relative 'command'
|
||||
|
||||
module SpeckleConnector3
|
||||
module SpeckleConnector
|
||||
module Commands
|
||||
# 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
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
require_relative 'command'
|
||||
require_relative '../actions/activate_diffing'
|
||||
|
||||
module SpeckleConnector3
|
||||
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)
|
||||
@@ -0,0 +1,42 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module SpeckleConnector
|
||||
module Commands
|
||||
# Base command schema to wrap common operations for all commands.
|
||||
class Command
|
||||
# @return [App::SpeckleConnectorApp] the main app object
|
||||
attr_reader :app
|
||||
|
||||
# @return [Ui::View] view object holds dialog and it's state
|
||||
attr_reader :view
|
||||
|
||||
# @@param app [App::SpeckleConnectorApp] the main app object
|
||||
def initialize(app)
|
||||
@app = app
|
||||
@view = app.ui_controller.user_interfaces[Ui::SPECKLE_UI_ID]
|
||||
end
|
||||
|
||||
def run(*parameters)
|
||||
# Run here common operations that same for each command.
|
||||
with_observers_disabled do
|
||||
_run(*parameters)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def with_observers_disabled(&block)
|
||||
observer_handler = @app.observer_handler
|
||||
if observer_handler
|
||||
observer_handler.with_observers_disabled(&block)
|
||||
else
|
||||
block.call
|
||||
end
|
||||
end
|
||||
|
||||
def _run(*_parameters)
|
||||
raise NotImplementedError, 'Implement in subclass'
|
||||
end
|
||||
end
|
||||
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
|
||||
@@ -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 SpeckleConnector3
|
||||
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 SpeckleConnector3
|
||||
|
||||
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 = SpeckleConnector3::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
|
||||
@@ -1,6 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module SpeckleConnector3
|
||||
module SpeckleConnector
|
||||
module Commands
|
||||
# Helper class to register, handle menu and toolbar commands.
|
||||
class MenuCommandHandler
|
||||
@@ -9,8 +9,6 @@ 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
|
||||
@@ -4,11 +4,11 @@ require_relative 'command'
|
||||
require_relative '../accounts/accounts'
|
||||
require_relative '../actions/model_preference_updated'
|
||||
|
||||
module SpeckleConnector3
|
||||
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))
|
||||
@@ -4,11 +4,11 @@ require_relative 'command'
|
||||
require_relative '../actions/connected'
|
||||
require_relative '../actions/send_from_queue'
|
||||
|
||||
module SpeckleConnector3
|
||||
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))
|
||||
@@ -3,19 +3,18 @@
|
||||
require_relative 'command'
|
||||
require_relative '../actions/receive_objects'
|
||||
|
||||
module SpeckleConnector3
|
||||
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
|
||||
@@ -4,11 +4,11 @@ require_relative 'command'
|
||||
require_relative '../actions/remove_stream'
|
||||
require_relative '../actions/load_saved_streams'
|
||||
|
||||
module SpeckleConnector3
|
||||
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)
|
||||
@@ -5,11 +5,11 @@ require_relative '../accounts/accounts'
|
||||
require_relative '../actions/save_stream'
|
||||
require_relative '../actions/load_saved_streams'
|
||||
|
||||
module SpeckleConnector3
|
||||
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
|
||||
@@ -3,11 +3,11 @@
|
||||
require_relative 'command'
|
||||
require_relative '../actions/send_selection'
|
||||
|
||||
module SpeckleConnector3
|
||||
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)
|
||||
@@ -0,0 +1,58 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative 'menu_command_handler'
|
||||
require_relative 'action_command'
|
||||
require_relative 'initialize_speckle'
|
||||
require_relative '../actions/one_click_send'
|
||||
|
||||
module SpeckleConnector
|
||||
module Commands
|
||||
# Speckle menu commands that adds them to Sketchup menu and toolbar.
|
||||
class SpeckleMenuCommands
|
||||
CMD_INITIALIZE_SPECKLE = :initialize_speckle
|
||||
CMD_SEND_TO_SPECKLE = :send_to_speckle
|
||||
CMD_RECEIVE_FROM_SPECKLE = :receive_from_speckle
|
||||
|
||||
# Add initial set of commands to Speckle application object and to Sketchup menu and toolbar
|
||||
# @param app [App::SpeckleConnectorApp] the application object
|
||||
def self.add_initial_commands!(app)
|
||||
commands = app.menu_commands
|
||||
ui_controller = app.ui_controller
|
||||
sketchup_ui = ui_controller.sketchup_ui
|
||||
speckle_menu = sketchup_ui.speckle_menu
|
||||
speckle_toolbar = sketchup_ui.speckle_toolbar
|
||||
|
||||
commands[CMD_INITIALIZE_SPECKLE] = initialize_speckle_command(app)
|
||||
commands.add_to_menu!(CMD_INITIALIZE_SPECKLE, speckle_menu)
|
||||
commands.add_to_toolbar!(CMD_INITIALIZE_SPECKLE, speckle_toolbar)
|
||||
|
||||
# commands[CMD_SEND_TO_SPECKLE] = send_command(app)
|
||||
# commands.add_to_menu!(CMD_SEND_TO_SPECKLE, speckle_menu)
|
||||
# commands.add_to_toolbar!(CMD_SEND_TO_SPECKLE, speckle_toolbar)
|
||||
end
|
||||
|
||||
def self.initialize_speckle_command(app)
|
||||
cmd = MenuCommandHandler.sketchup_command(
|
||||
InitializeSpeckle.new(app), 'Initialize Speckle'
|
||||
)
|
||||
cmd.tooltip = 'Launch Connector'
|
||||
cmd.status_bar_text = 'Opens the Speckle Connector window'
|
||||
cmd.small_icon = '../../img/s2logo.png'
|
||||
cmd.large_icon = '../../img/s2logo.png'
|
||||
cmd
|
||||
end
|
||||
|
||||
def self.send_command(app)
|
||||
cmd = MenuCommandHandler.sketchup_command(
|
||||
ActionCommand.new(app, Actions::OneClickSend), 'Send to Speckle'
|
||||
)
|
||||
cmd.tooltip = 'Send to Speckle'
|
||||
cmd.status_bar_text = 'Send to Speckle'
|
||||
cmd.small_icon = '../../img/Sender.png'
|
||||
cmd.large_icon = '../../img/Sender.png'
|
||||
cmd.set_validation_proc { MenuCommandHandler.speckle_started(app) }
|
||||
cmd
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -4,11 +4,11 @@ require_relative 'command'
|
||||
require_relative '../accounts/accounts'
|
||||
require_relative '../actions/user_preferences_updated'
|
||||
|
||||
module SpeckleConnector3
|
||||
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']
|
||||
@@ -1,16 +1,11 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module SpeckleConnector3
|
||||
module SpeckleConnector
|
||||
SPECKLE_BASE_OBJECT = 'Speckle_Base_Object'
|
||||
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'
|
||||
TOTAL_CHILDREN_COUNT = 'total_children_count'
|
||||
CHILDREN = 'children'
|
||||
PARENT = 'parent'
|
||||
VALID_STREAM_IDS = 'valid_stream_ids'
|
||||
@@ -1,6 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module SpeckleConnector3
|
||||
module SpeckleConnector
|
||||
MAT_DICTIONARY = 'Speckle_Connector_Materials'
|
||||
MAT_ID = 'Speckle_Connector_Material_Id'
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module SpeckleConnector
|
||||
APP_OBSERVER = 'SpeckleConnector::Observers::AppObserver'
|
||||
ENTITIES_OBSERVER = 'SpeckleConnector::Observers::EntitiesObserver'
|
||||
MODEL_OBSERVER = 'SpeckleConnector::Observers::ModelObserver'
|
||||
end
|
||||
@@ -4,7 +4,7 @@ require 'pathname'
|
||||
require_relative 'platform_constants'
|
||||
|
||||
# Speckle connector module to enable multiplayer mode ON!
|
||||
module SpeckleConnector3
|
||||
module SpeckleConnector
|
||||
dir = __dir__.dup
|
||||
dir.force_encoding('UTF-8') if dir.respond_to?(:force_encoding)
|
||||
HOME_PATH = (ENV['HOME']).to_s
|
||||
@@ -19,6 +19,6 @@ module SpeckleConnector3
|
||||
raise 'Speckle could not determine your Appdata path'
|
||||
end
|
||||
SPECKLE_ACCOUNTS_DB_PATH = File.join(SPECKLE_APPDATA_PATH, 'Accounts.db')
|
||||
SPECKLE_CONFIG_DB_PATH = File.join(SPECKLE_APPDATA_PATH, 'DUI3Config.db')
|
||||
SPECKLE_CONFIG_DB_PATH = File.join(SPECKLE_APPDATA_PATH, 'Config.db')
|
||||
SPECKLE_TEST_DB_PATH = File.join(SPECKLE_APPDATA_PATH, 'sketchup_test.db')
|
||||
end
|
||||
@@ -1,7 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# rubocop:disable Style/Documentation
|
||||
module SpeckleConnector3
|
||||
module SpeckleConnector
|
||||
host_os = RbConfig::CONFIG['host_os']
|
||||
OS_WIN = :windows
|
||||
OS_MAC = :macos
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
require_relative '../speckle_objects/geometry/length'
|
||||
|
||||
module SpeckleConnector3
|
||||
module SpeckleConnector
|
||||
COMBINE_FACES_BY_MATERIAL = :combine_faces_by_material
|
||||
INCLUDE_ENTITY_ATTRIBUTES = :include_entity_attributes
|
||||
INCLUDE_FACE_ENTITY_ATTRIBUTES = :include_face_entity_attributes
|
||||
@@ -11,13 +11,6 @@ module SpeckleConnector3
|
||||
INCLUDE_COMPONENT_ENTITY_ATTRIBUTES = :include_component_entity_attributes
|
||||
MERGE_COPLANAR_FACES = :merge_coplanar_faces
|
||||
|
||||
ENTITY_KEYS_FOR_INCLUDING_ATTRIBUTES = {
|
||||
Sketchup::ComponentInstance => INCLUDE_COMPONENT_ENTITY_ATTRIBUTES,
|
||||
Sketchup::Group => INCLUDE_GROUP_ENTITY_ATTRIBUTES,
|
||||
Sketchup::Face => INCLUDE_FACE_ENTITY_ATTRIBUTES,
|
||||
Sketchup::Edge => INCLUDE_EDGE_ENTITY_ATTRIBUTES
|
||||
}.freeze
|
||||
|
||||
LEVEL_SHIFT_VALUE = SpeckleObjects::Geometry.length_to_native(1.5, 'm')
|
||||
|
||||
DEFAULT_MODEL_PREFERENCES = {
|
||||
@@ -0,0 +1,19 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module SpeckleConnector
|
||||
BASE_OBJECT = 'Base'
|
||||
|
||||
OBJECTS_BUILTELEMENTS_VIEW3D = 'Objects.BuiltElements.View:Objects.BuiltElements.View3D'
|
||||
|
||||
OBJECTS_GEOMETRY_LINE = 'Objects.Geometry.Line'
|
||||
OBJECTS_GEOMETRY_POLYLINE = 'Objects.Geometry.Polyline'
|
||||
OBJECTS_GEOMETRY_MESH = 'Objects.Geometry.Mesh'
|
||||
OBJECTS_GEOMETRY_BREP = 'Objects.Geometry.Brep'
|
||||
|
||||
OBJECTS_OTHER_BLOCKINSTANCE = 'Objects.Other.BlockInstance'
|
||||
OBJECTS_OTHER_BLOCKINSTANCE_FULL = 'Objects.Other.Instance:Objects.Other.BlockInstance'
|
||||
OBJECTS_OTHER_INSTANCE = 'Objects.Other.Instance:Objects.Other.Instance'
|
||||
OBJECTS_OTHER_REVIT_REVITINSTANCE = 'Objects.Other.Revit.RevitInstance'
|
||||
OBJECTS_OTHER_BLOCKDEFINITION = 'Objects.Other.BlockDefinition'
|
||||
OBJECTS_OTHER_RENDERMATERIAL = 'Objects.Other.RenderMaterial'
|
||||
end
|
||||
@@ -5,12 +5,10 @@ require 'securerandom'
|
||||
# rubocop:enable SketchupPerformance/OpenSSL
|
||||
require 'digest'
|
||||
require_relative 'converter'
|
||||
require_relative '../speckle_objects/base'
|
||||
require_relative '../speckle_objects/object_reference'
|
||||
require_relative '../speckle_entities/speckle_entity'
|
||||
require_relative '../relations/many_to_one_relation'
|
||||
|
||||
module SpeckleConnector3
|
||||
module SpeckleConnector
|
||||
module Converters
|
||||
# Serializer of the base object.
|
||||
# Responsible to create id (hash) of the objects by holding their lineage and detaching relationships.
|
||||
@@ -18,17 +16,23 @@ module SpeckleConnector3
|
||||
# @return [Integer] default chunk size the determine splitting base prop into chucks
|
||||
attr_reader :default_chunk_size
|
||||
|
||||
attr_reader :object_references
|
||||
# @return [String] stream id to send conversion
|
||||
attr_reader :stream_id
|
||||
|
||||
def initialize(preferences, default_chunk_size = 1000)
|
||||
attr_accessor :speckle_state
|
||||
|
||||
# @param stream_id [String] stream id to send conversion
|
||||
def initialize(speckle_state, stream_id, preferences, default_chunk_size = 1000)
|
||||
@speckle_state = speckle_state
|
||||
@stream_id = stream_id
|
||||
@preferences = preferences
|
||||
@default_chunk_size = default_chunk_size
|
||||
@detach_lineage = []
|
||||
@lineage = []
|
||||
@family_tree = {}
|
||||
@family_tree_relation = Relations::ManyToOneRelation.new
|
||||
@closure_table = {}
|
||||
@objects = {}
|
||||
@object_references = {}
|
||||
end
|
||||
|
||||
# @param base [Object] top base object to populate all children and their relationship
|
||||
@@ -39,15 +43,40 @@ module SpeckleConnector3
|
||||
id
|
||||
end
|
||||
|
||||
# @param base [Object] base object to populate all children and their relationship
|
||||
def total_children_count(id)
|
||||
@objects[id][:totalChildrenCount]
|
||||
end
|
||||
|
||||
# @param base_and_entities [Object] base object to populate all children and their relationship
|
||||
# rubocop:disable Metrics/MethodLength
|
||||
def traverse_base(base)
|
||||
# rubocop:disable Metrics/PerceivedComplexity
|
||||
# rubocop:disable Metrics/CyclomaticComplexity
|
||||
# rubocop:disable Metrics/AbcSize
|
||||
def traverse_base(base_and_entities)
|
||||
base, entities = base_and_entities
|
||||
|
||||
# 1. Create random string for lineage tracking.
|
||||
@lineage.append(SecureRandom.hex)
|
||||
|
||||
# 2. Get last item from detach_lineage array
|
||||
is_detached = @detach_lineage.pop
|
||||
|
||||
# unless entities.nil?
|
||||
# is_sent_before = entities.all? do |entity|
|
||||
# check_base_available_on_state(entity, speckle_state)
|
||||
# end
|
||||
# if is_sent_before
|
||||
# speckle_entity = speckle_state.speckle_entities[entities.first.persistent_id]
|
||||
# ref_object = detach_helper(speckle_entity.id)
|
||||
# parent = @lineage[-1]
|
||||
# unless @family_tree[parent].nil?
|
||||
# @family_tree[parent] = @family_tree[parent].merge(speckle_entity.speckle_object[:__closure])
|
||||
# end
|
||||
# @objects[speckle_entity.id] = ref_object if is_detached
|
||||
# return speckle_entity.id, ref_object
|
||||
# end
|
||||
# end
|
||||
|
||||
# 3. Initialize traversed base object that will be filled with traversed values or
|
||||
# traversed base objects as props.
|
||||
traversed_base = SpeckleObjects::Base.new(speckle_type: base[:speckle_type], id: '')
|
||||
@@ -56,6 +85,7 @@ module SpeckleConnector3
|
||||
traversed_base.delete(:applicationId)
|
||||
|
||||
# 4. Iterate all entries (key, value) of the base {Base > Hash} object
|
||||
# speckle_state = traverse_base_props(base, traversed_base)
|
||||
traverse_base_props(base, traversed_base)
|
||||
# this is where all props are done for current `traversed_base`
|
||||
|
||||
@@ -68,6 +98,9 @@ module SpeckleConnector3
|
||||
end
|
||||
end
|
||||
|
||||
# 6. Add total children count
|
||||
traversed_base[:totalChildrenCount] = closure.keys.length
|
||||
|
||||
# 7. Finally create id
|
||||
id = get_id(traversed_base)
|
||||
|
||||
@@ -75,7 +108,7 @@ module SpeckleConnector3
|
||||
traversed_base[:id] = id
|
||||
|
||||
# 9. Update __closure table on the traversed base
|
||||
unless closure.empty?
|
||||
unless traversed_base[:totalChildrenCount].nil?
|
||||
@closure_table[id] = closure
|
||||
traversed_base[:__closure] = closure unless closure.empty?
|
||||
end
|
||||
@@ -83,15 +116,19 @@ module SpeckleConnector3
|
||||
# 10. Save object string if detached
|
||||
@objects[id] = traversed_base if is_detached
|
||||
|
||||
if traversed_base[:applicationId]
|
||||
@object_references[traversed_base[:applicationId].to_s] = SpeckleObjects::ObjectReference.new(
|
||||
id, traversed_base[:applicationId].to_s, traversed_base[:__closure]
|
||||
)
|
||||
if @preferences[:user][:register_speckle_entity] && !entities.nil?
|
||||
entities.uniq.each do |entity|
|
||||
speckle_entity = create_or_update_speckle_entity(entity, id, traversed_base)
|
||||
@speckle_state = speckle_state.with_speckle_entity(speckle_entity)
|
||||
end
|
||||
end
|
||||
|
||||
return id, traversed_base
|
||||
end
|
||||
# rubocop:enable Metrics/MethodLength
|
||||
# rubocop:enable Metrics/PerceivedComplexity
|
||||
# rubocop:enable Metrics/CyclomaticComplexity
|
||||
# rubocop:enable Metrics/AbcSize
|
||||
|
||||
# rubocop:disable Metrics/MethodLength
|
||||
# rubocop:disable Metrics/AbcSize
|
||||
@@ -109,15 +146,16 @@ module SpeckleConnector3
|
||||
next
|
||||
end
|
||||
|
||||
# 3.3. Check prop needs to split into chunks
|
||||
chunked_detach_match = prop.match(/^@\((\d*)\)/)
|
||||
|
||||
# 3.4. Determine prop is dynamically detached or not
|
||||
# 3.3. Determine prop is dynamically detached or not
|
||||
is_detach_prop = prop[0] == '@'
|
||||
is_dynamically_detached = prop[0] == '@' && prop.length > 2 && prop[1] == '@'
|
||||
prop = prop[2..-1] if is_dynamically_detached
|
||||
|
||||
# 3.4. Check prop needs to split into chunks
|
||||
chunked_detach_match = prop.match(/^@\((\d*)\)/)
|
||||
|
||||
# 3.5. If split chunk is needed and prop value is array, then run chunking process
|
||||
if value.is_a?(Array) && chunked_detach_match
|
||||
if value.is_a?(Array) && !base_and_entities?(value) && chunked_detach_match
|
||||
# 3.5.1. Determine chunk size, get it from prop if defined. ex: '@(31250)faces' -> 31250 = chunk size
|
||||
chunk_size = chunked_detach_match[1] == '' ? default_chunk_size : chunked_detach_match[1].to_i
|
||||
|
||||
@@ -164,24 +202,14 @@ module SpeckleConnector3
|
||||
next
|
||||
end
|
||||
|
||||
# 3.6 we cleanup the semantic @ or @@ we used so far for detaching or chunking
|
||||
if is_dynamically_detached
|
||||
prop = prop[2..-1]
|
||||
else
|
||||
prop = prop[1..-1] if is_detach_prop
|
||||
end
|
||||
|
||||
child = traverse_value(value, is_detach_prop)
|
||||
|
||||
is_base = value.is_a?(Hash) && !value[:speckle_type].nil?
|
||||
is_base = (value.is_a?(Hash) && !value[:speckle_type].nil?) ||
|
||||
(base_and_entities?(value) && value[0].is_a?(Hash) && !value[0][:speckle_type].nil?)
|
||||
|
||||
# 3.7. traverse value according to value is a speckle object or not
|
||||
# 3.6. traverse value according to value is a speckle object or not
|
||||
traversed_base[prop] = if is_base
|
||||
if child[:referencedId] && child[:speckle_type] == 'reference'
|
||||
is_detach_prop ? detach_helper(child[:referencedId]) : child
|
||||
else
|
||||
is_detach_prop ? detach_helper(child[:id]) : child
|
||||
end
|
||||
is_detach_prop ? detach_helper(child[:id]) : child
|
||||
else
|
||||
child
|
||||
end
|
||||
@@ -193,6 +221,16 @@ module SpeckleConnector3
|
||||
# rubocop:enable Metrics/CyclomaticComplexity
|
||||
# rubocop:enable Metrics/PerceivedComplexity
|
||||
|
||||
# Whether value has a pattern [<converted>, [<entity>, <entity>, ... <entity>]] or not.
|
||||
def base_and_entities?(value)
|
||||
is_array = value.is_a?(Array)
|
||||
return false unless is_array
|
||||
|
||||
return false unless is_array && value.length == 2
|
||||
|
||||
value[1].all? { |v| v.is_a?(Sketchup::Entity) }
|
||||
end
|
||||
|
||||
# rubocop:disable Metrics/MethodLength
|
||||
# rubocop:disable Metrics/CyclomaticComplexity
|
||||
# rubocop:disable Metrics/PerceivedComplexity
|
||||
@@ -202,8 +240,8 @@ module SpeckleConnector3
|
||||
# 1. Return same value if value is primitive type (string, numeric, boolean)
|
||||
return value unless value.is_a?(Hash) || value.is_a?(Array)
|
||||
|
||||
# 2. For pure arrays
|
||||
if value.is_a?(Array)
|
||||
# 2. For pure arrays (Without referencing any Sketchup Entity)
|
||||
if value.is_a?(Array) && !base_and_entities?(value)
|
||||
|
||||
# 2.1. If it is not detached then iterate array by traversing with their value
|
||||
unless is_detach
|
||||
@@ -217,17 +255,7 @@ module SpeckleConnector3
|
||||
# 2.2. If it is detached than collect them into detached_list
|
||||
detached_list = []
|
||||
value.each do |el|
|
||||
if el.is_a?(SpeckleObjects::ObjectReference)
|
||||
if el.closure
|
||||
el.closure.each_key do |k|
|
||||
detach_helper(k)
|
||||
end
|
||||
end
|
||||
detached_list.append(detach_helper(el.referenced_id))
|
||||
next
|
||||
end
|
||||
|
||||
if el.is_a?(Hash) && !el[:speckle_type].nil?
|
||||
if (el.is_a?(Hash) && !el[:speckle_type].nil?) || base_and_entities?(el)
|
||||
@detach_lineage.append(is_detach)
|
||||
id, _traversed_base = traverse_base(el)
|
||||
detached_list.append(detach_helper(id))
|
||||
@@ -239,21 +267,11 @@ module SpeckleConnector3
|
||||
return detached_list
|
||||
end
|
||||
|
||||
# 3. ObjectReference
|
||||
if value.is_a?(SpeckleObjects::ObjectReference)
|
||||
if value.closure
|
||||
value.closure.each_key do |k|
|
||||
detach_helper(k)
|
||||
end
|
||||
end
|
||||
return detach_helper(value.referenced_id)
|
||||
end
|
||||
|
||||
# 4. Hash
|
||||
# 3. Hash
|
||||
return value if value.is_a?(Hash) && value[:speckle_type].nil?
|
||||
|
||||
# 5. Base objects
|
||||
if value.is_a?(Hash) && !value[:speckle_type].nil?
|
||||
# 4. Base objects
|
||||
if (value.is_a?(Hash) && !value[:speckle_type].nil?) || base_and_entities?(value)
|
||||
@detach_lineage.append(is_detach)
|
||||
_id, traversed_base = traverse_base(value)
|
||||
return traversed_base
|
||||
@@ -285,17 +303,13 @@ module SpeckleConnector3
|
||||
}
|
||||
end
|
||||
|
||||
# @param traversed_base [SpeckleConnector3::SpeckleObjects::Base] traversed base object.
|
||||
# @param traversed_base [SpeckleConnector::SpeckleObjects::Base] traversed base object.
|
||||
def get_id(traversed_base)
|
||||
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 = '['
|
||||
@@ -319,6 +333,32 @@ module SpeckleConnector3
|
||||
batches
|
||||
end
|
||||
# rubocop:enable Metrics/MethodLength
|
||||
|
||||
# @param entity [Sketchup::Entity] source entity object
|
||||
# @param speckle_state [States::SpeckleState] the current speckle state of the {States::State}
|
||||
def check_base_available_on_state(entity, speckle_state)
|
||||
is_exist = speckle_state.speckle_entities.keys.include?(entity.persistent_id)
|
||||
return is_exist unless is_exist
|
||||
|
||||
speckle_state.speckle_entities[entity.persistent_id].valid_stream_ids.include?(stream_id)
|
||||
end
|
||||
|
||||
# Creates or updates speckle entity.
|
||||
# If speckle entity exist in state, creates new one by updating old one.
|
||||
# Else creates new one
|
||||
# @return [SpeckleEntity] speckle entity that collects both speckle and sketchup information.
|
||||
def create_or_update_speckle_entity(entity, id, traversed_base)
|
||||
if speckle_state.speckle_entities.keys.include?(entity.persistent_id)
|
||||
speckle_state.speckle_entities[entity.persistent_id].with_valid_stream_id(stream_id)
|
||||
else
|
||||
children = traversed_base[:__closure].nil? ? {} : traversed_base[:__closure]
|
||||
speckle_entity = SpeckleEntities::SpeckleEntity.new(entity, id, entity.persistent_id,
|
||||
traversed_base[:speckle_type],
|
||||
children.keys, [stream_id])
|
||||
speckle_entity.write_initial_base_data
|
||||
speckle_entity
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -3,7 +3,7 @@
|
||||
# rubocop:disable Metrics/CyclomaticComplexity
|
||||
# rubocop:disable Metrics/PerceivedComplexity
|
||||
|
||||
module SpeckleConnector3
|
||||
module SpeckleConnector
|
||||
module Converters
|
||||
# CleanUp is a plugin developed by [Thomas Thomassen](https://github.com/thomthom).
|
||||
module CleanUp
|
||||
@@ -11,41 +11,12 @@ module SpeckleConnector3
|
||||
# @param entities [Sketchup::Entities] entities to remove edges between that make entities coplanar.
|
||||
# @note Merging coplanar faces idea originated from [CleanUp](https://github.com/thomthom/cleanup) plugin
|
||||
# which is developed by [Thomas Thomassen](https://github.com/thomthom).
|
||||
def self.merge_coplanar_entities(entities)
|
||||
def self.merge_coplanar_faces(entities)
|
||||
edges = []
|
||||
faces = entities.collect { |entity| entity if entity.is_a? Sketchup::Face }.compact
|
||||
faces = merged_faces(faces)
|
||||
faces.each { |face| face.edges.each { |edge| edges << edge } }
|
||||
edges.uniq!
|
||||
edges.each { |edge| remove_edge_have_coplanar_faces(edge, faces, false) }
|
||||
# Remove remaining orphan edges
|
||||
edges.reject(&:deleted?).select { |edge| edge.faces.empty? }.each(&:erase!)
|
||||
merged_faces(faces)
|
||||
end
|
||||
|
||||
def self.merge_coplanar_faces(faces)
|
||||
edges = []
|
||||
faces = faces.reject(&:deleted?)
|
||||
|
||||
faces.each { |face| face.edges.each { |edge| edges << edge } }
|
||||
|
||||
edges.uniq!
|
||||
|
||||
edges.each { |edge| remove_edge_have_coplanar_faces(edge) }
|
||||
|
||||
# Remove remaining orphan edges
|
||||
# the commented out code throws an exception over deleted elements
|
||||
# even if they are rejected by deleted? flag
|
||||
# edges.reject(&:deleted?).select { |edge| edge.faces.empty? }.each(&:erase!)
|
||||
edges.each do |edge|
|
||||
next if edge.deleted?
|
||||
begin
|
||||
edge.erase! if edge.faces.empty?
|
||||
rescue Sketchup::DeletedEntityError
|
||||
# Ignore deleted edge
|
||||
end
|
||||
end
|
||||
|
||||
merged_faces(faces)
|
||||
end
|
||||
|
||||
@@ -62,34 +33,43 @@ module SpeckleConnector3
|
||||
# - Whether UV texture map is aligned between faces or not.
|
||||
# - Finally, if faces are coplanar by correcting these checks, then removes edge from Sketchup.active_model.
|
||||
# @param edge [Sketchup::Edge] edge to check.
|
||||
# @param faces [Array<Sketchup::Face>] scoped faces to check 'edge.faces' both (first and second)
|
||||
# belongs to this faces or not. If any of this faces does not involve this scoped faces, then do not delete.
|
||||
# @param ignore_materials [Boolean] whether ignore materials or not.
|
||||
# Returns true if the given edge separating two coplanar faces.
|
||||
# Return false otherwise.
|
||||
def self.remove_edge_have_coplanar_faces(edge)
|
||||
# rubocop:disable Metrics/AbcSize
|
||||
def self.remove_edge_have_coplanar_faces(edge, faces, ignore_materials)
|
||||
return false unless edge.valid? && edge.is_a?(Sketchup::Edge)
|
||||
return false unless edge.faces.size == 2
|
||||
|
||||
# Check scoped faces have this edges
|
||||
if edge.faces.size == 2
|
||||
is_first = faces.include?(edge.faces[0])
|
||||
is_second = faces.include?(edge.faces[1])
|
||||
return false unless is_first && is_second
|
||||
end
|
||||
|
||||
face_1, face_2 = edge.faces
|
||||
|
||||
begin
|
||||
return false unless face_1.normal.samedirection?(face_2.normal)
|
||||
|
||||
return false if face_duplicate?(face_1, face_2)
|
||||
# Check for troublesome faces which might lead to missing geometry if merged.
|
||||
return false unless edge_safe_to_merge?(edge)
|
||||
return false if face_duplicate?(face_1, face_2)
|
||||
# Check for troublesome faces which might lead to missing geometry if merged.
|
||||
return false unless edge_safe_to_merge?(edge)
|
||||
|
||||
# Check materials match.
|
||||
unless ignore_materials
|
||||
return false unless (face_1.material == face_2.material) && (face_1.back_material == face_2.back_material)
|
||||
|
||||
# Check faces are coplanar or not.
|
||||
return false unless faces_coplanar?(face_1, face_2)
|
||||
|
||||
edge.erase!
|
||||
true
|
||||
rescue StandardError => e
|
||||
puts "Failed to merge coplanar faces by removing edge with error: #{e}"
|
||||
false
|
||||
# Verify UV mapping match.
|
||||
return false if !face_1.material.nil? && !continuous_uv?(face_1, face_2, edge) && face_1.material.texture.nil?
|
||||
end
|
||||
# Check faces are coplanar or not.
|
||||
return false unless faces_coplanar?(face_1, face_2)
|
||||
|
||||
edge.erase!
|
||||
true
|
||||
end
|
||||
# rubocop:enable Metrics/AbcSize
|
||||
|
||||
# Determines if two faces are overlapped.
|
||||
def self.face_duplicate?(face_1, face_2, overlapping: false)
|
||||
@@ -1,10 +1,10 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module SpeckleConnector3
|
||||
module SpeckleConnector
|
||||
module Converters
|
||||
# Helper class to convert geometries between server and Sketchup.
|
||||
class Converter
|
||||
# @return [States::State] the current state of the {SpeckleConnector3::App}
|
||||
# @return [States::State] the current state of the {SpeckleConnector::App}
|
||||
attr_reader :state
|
||||
|
||||
# @return [States::SpeckleState] the current speckle state of the {States::State}
|
||||
@@ -21,14 +21,14 @@ module SpeckleConnector3
|
||||
|
||||
attr_accessor :definitions
|
||||
|
||||
# @param state [States::State] the current state of the {SpeckleConnector3::App}
|
||||
# @param state [States::State] the current state of the {SpeckleConnector::App}
|
||||
def initialize(state, stream_id)
|
||||
@state = state
|
||||
@speckle_state = state.speckle_state
|
||||
@sketchup_model = state.sketchup_state.sketchup_model
|
||||
@stream_id = stream_id
|
||||
su_unit = state.sketchup_state.length_units
|
||||
@units = Converters::SKETCHUP_UNITS[su_unit]
|
||||
@units = Converters::SKETCHUP_UNITS[su_unit]
|
||||
@definitions = {}
|
||||
end
|
||||
end
|
||||
@@ -2,29 +2,18 @@
|
||||
|
||||
require_relative 'converter'
|
||||
require_relative '../constants/type_constants'
|
||||
require_relative '../speckle_entities/speckle_entity'
|
||||
require_relative '../speckle_objects/gis/polygon_element'
|
||||
require_relative '../speckle_objects/gis/line_element'
|
||||
require_relative '../speckle_objects/other/transform'
|
||||
require_relative '../speckle_objects/other/render_material'
|
||||
require_relative '../speckle_objects/other/block_definition'
|
||||
require_relative '../speckle_objects/other/block_instance'
|
||||
require_relative '../speckle_objects/other/display_value'
|
||||
require_relative '../speckle_objects/revit/revit_instance'
|
||||
require_relative '../speckle_objects/other/revit/revit_instance'
|
||||
require_relative '../speckle_objects/geometry/point'
|
||||
require_relative '../speckle_objects/geometry/line'
|
||||
require_relative '../speckle_objects/geometry/polycurve'
|
||||
require_relative '../speckle_objects/geometry/arc'
|
||||
require_relative '../speckle_objects/geometry/circle'
|
||||
require_relative '../speckle_objects/geometry/mesh'
|
||||
require_relative '../speckle_objects/built_elements/view3d'
|
||||
require_relative '../speckle_objects/built_elements/network'
|
||||
require_relative '../speckle_objects/speckle/core/models/collection'
|
||||
require_relative '../speckle_objects/speckle/core/models/gis_layer_collection'
|
||||
require_relative '../sketchup_model/dictionary/speckle_entity_dictionary_handler'
|
||||
require_relative '../ui_data/report/conversion_result'
|
||||
|
||||
module SpeckleConnector3
|
||||
module SpeckleConnector
|
||||
module Converters
|
||||
# Converts sketchup entities to speckle objects.
|
||||
# rubocop:disable Metrics/ClassLength
|
||||
@@ -35,55 +24,31 @@ module SpeckleConnector3
|
||||
# @return [String] source application of received object that will be converted to native
|
||||
attr_reader :source_app
|
||||
|
||||
attr_reader :converted_faces
|
||||
|
||||
attr_reader :converted_entities
|
||||
|
||||
attr_reader :conversion_results
|
||||
|
||||
def initialize(state, stream_id, stream_name, branch_name, source_app, model_card_id)
|
||||
super(state, stream_id, model_card_id)
|
||||
def initialize(state, stream_id, stream_name, branch_name, source_app)
|
||||
super(state, stream_id)
|
||||
@stream_name = stream_name
|
||||
@branch_name = branch_name
|
||||
@source_app = source_app.downcase
|
||||
@converted_faces = []
|
||||
@converted_entities = []
|
||||
@conversion_results = []
|
||||
end
|
||||
|
||||
# Module aliases
|
||||
GEOMETRY = SpeckleObjects::Geometry
|
||||
OTHER = SpeckleObjects::Other
|
||||
REVIT = SpeckleObjects::Revit
|
||||
BUILTELEMENTS = SpeckleObjects::BuiltElements
|
||||
GIS = SpeckleObjects::GIS
|
||||
|
||||
# Class aliases
|
||||
POINT = GEOMETRY::Point
|
||||
LINE = GEOMETRY::Line
|
||||
POLYCURVE = GEOMETRY::Polycurve
|
||||
ARC = GEOMETRY::Arc
|
||||
CIRCLE = GEOMETRY::Circle
|
||||
MESH = GEOMETRY::Mesh
|
||||
BLOCK_DEFINITION = OTHER::BlockDefinition
|
||||
BLOCK_INSTANCE = OTHER::BlockInstance
|
||||
REVIT_INSTANCE = REVIT::Other::RevitInstance
|
||||
REVIT_WALL = BUILTELEMENTS::RevitWall
|
||||
REVIT_INSTANCE = OTHER::Revit::RevitInstance
|
||||
RENDER_MATERIAL = OTHER::RenderMaterial
|
||||
DISPLAY_VALUE = OTHER::DisplayValue
|
||||
VIEW3D = BUILTELEMENTS::View3d
|
||||
POLYGON_ELEMENT = GIS::PolygonElement
|
||||
LINE_ELEMENT = GIS::LineElement
|
||||
COLLECTION = SpeckleObjects::Speckle::Core::Models::Collection
|
||||
GIS_LAYER_COLLECTION = SpeckleObjects::Speckle::Core::Models::GisLayerCollection
|
||||
|
||||
BASE_OBJECT_PROPS = %w[applicationId id speckle_type].freeze
|
||||
BASE_OBJECT_PROPS = %w[applicationId id speckle_type totalChildrenCount].freeze
|
||||
CONVERTABLE_SPECKLE_TYPES = %w[
|
||||
Objects.Geometry.Line
|
||||
Objects.Geometry.Polyline
|
||||
Objects.Geometry.Polycurve
|
||||
Objects.Geometry.Arc
|
||||
Objects.Geometry.Circle
|
||||
Objects.Geometry.Mesh
|
||||
Objects.Geometry.Brep
|
||||
Objects.Other.BlockInstance
|
||||
@@ -91,93 +56,44 @@ module SpeckleConnector3
|
||||
Objects.Other.BlockDefinition
|
||||
Objects.Other.RenderMaterial
|
||||
Objects.Other.Instance:Objects.Other.BlockInstance
|
||||
Objects.BuiltElements.View:Objects.BuiltElements.View3D
|
||||
Objects.BuiltElements.Wall:Objects.BuiltElements.Revit.RevitWall
|
||||
Objects.BuiltElements.Network
|
||||
Objects.GIS.PolygonElement
|
||||
Objects.GIS.LineElement
|
||||
Speckle.Core.Models.Collection
|
||||
Speckle.Core.Models.Collection:Objects.GIS.RasterLayer
|
||||
Speckle.Core.Models.Collection:Objects.GIS.VectorLayer
|
||||
].freeze
|
||||
|
||||
def from_revit
|
||||
@from_revit ||= source_app.include?('revit')
|
||||
end
|
||||
|
||||
def from_rhino
|
||||
@from_rhino ||= source_app.include?('rhino')
|
||||
end
|
||||
|
||||
def from_sketchup
|
||||
@from_sketchup ||= source_app.include?('sketchup')
|
||||
end
|
||||
|
||||
def from_qgis
|
||||
@from_qgis ||= source_app.include?('qgis')
|
||||
end
|
||||
|
||||
# ReceiveObjects action call this method by giving everything that comes from server.
|
||||
# Upcoming object is a referencedObject of selected commit to receive.
|
||||
# UI is responsible currently to fetch objects from ObjectLoader module by calling getAndConstruct method.
|
||||
# @param obj [Object] speckle commit object.
|
||||
def receive_commit_object(obj)
|
||||
unless from_revit
|
||||
# Create layers and it's folders from layers relation on the model collection.
|
||||
SpeckleObjects::Relations::Layers.to_native(obj, source_app, sketchup_model)
|
||||
end
|
||||
|
||||
# By default entities to fill is sketchup model's entities.
|
||||
@entities_to_fill = sketchup_model.entities
|
||||
|
||||
# Navigate to branch entities if commit doesn't come from sketchup
|
||||
unless from_sketchup
|
||||
@branch_definition = branch_definition
|
||||
@entities_to_fill = @branch_definition.entities
|
||||
end
|
||||
|
||||
# First create layers on the sketchup before starting traversing
|
||||
# @Named Views are exception here. It does not mean a layer. But it is anti-pattern for now.
|
||||
filtered_layer_containers = obj.keys.filter_map { |key| key if key.start_with?('@') && key != '@Named Views' }
|
||||
create_layers(filtered_layer_containers, sketchup_model.layers) unless from_revit
|
||||
# Convert views to sketchup scenes
|
||||
SpeckleObjects::BuiltElements::View3d.to_native(obj, sketchup_model)
|
||||
# Get default commit layer from sketchup model which will be used as fallback
|
||||
default_commit_layer = sketchup_model.layers.layers.find { |layer| layer.display_name == '@Untagged' }
|
||||
|
||||
traverse_commit_object(obj, default_commit_layer, @entities_to_fill)
|
||||
@entities_to_fill = entities_to_fill(obj)
|
||||
traverse_commit_object(obj, sketchup_model.layers, default_commit_layer, @entities_to_fill)
|
||||
create_levels_from_section_planes
|
||||
check_hiding_layers_needed
|
||||
try_create_instance
|
||||
@state
|
||||
end
|
||||
|
||||
# Creating instance from @branch_definition only available for non-sketchup commits since we wrap commits
|
||||
# under instance.
|
||||
# There is also another use case that maybe definition is exist in file but user might be deleted it before.
|
||||
# If this is the case we can add instance by checking number of instances.
|
||||
# rubocop:disable Style/GuardClause
|
||||
def try_create_instance
|
||||
if !from_sketchup && (!@is_update_commit || @branch_definition.instances.empty?)
|
||||
instance = sketchup_model.entities.add_instance(@branch_definition, Geom::Transformation.new)
|
||||
@converted_entities.append(instance)
|
||||
BLOCK_INSTANCE.align_instance_axes(instance) if from_qgis
|
||||
end
|
||||
end
|
||||
# rubocop:enable Style/GuardClause
|
||||
|
||||
def levels_layer
|
||||
@levels_layer ||= sketchup_model.layers.add('Levels')
|
||||
end
|
||||
|
||||
def clear_levels
|
||||
instances = @entities_to_fill.grep(Sketchup::ComponentInstance)
|
||||
instances.each do |instance|
|
||||
speckle_type = instance.get_attribute(SPECKLE_BASE_OBJECT, 'speckle_type')
|
||||
next if speckle_type.nil?
|
||||
|
||||
sketchup_model.definitions.remove(instance.definition) if speckle_type == OBJECTS_BUILTELEMENTS_REVIT_LEVEL
|
||||
end
|
||||
end
|
||||
|
||||
# Create levels from section planes that already created for this commit object.
|
||||
# rubocop:disable Metrics/AbcSize
|
||||
# rubocop:disable Metrics/MethodLength
|
||||
def create_levels_from_section_planes
|
||||
clear_levels if @is_update_commit
|
||||
return unless from_revit
|
||||
|
||||
section_planes = @entities_to_fill.grep(Sketchup::SectionPlane)
|
||||
@@ -189,9 +105,7 @@ module SpeckleConnector3
|
||||
section_planes.each do |section_plane|
|
||||
level_name = "#{@definition_name}-#{section_plane.name}"
|
||||
definition = sketchup_model.definitions.add(level_name)
|
||||
instance = @entities_to_fill.add_instance(definition, Geom::Transformation.new)
|
||||
att = section_plane.attribute_dictionary(SPECKLE_BASE_OBJECT).to_h
|
||||
SketchupModel::Dictionary::SpeckleEntityDictionaryHandler.set_hash(instance, att)
|
||||
@entities_to_fill.add_instance(definition, Geom::Transformation.new)
|
||||
elevation = section_plane.bounds.center.z
|
||||
c1_e = Geom::Point3d.new(c_1.x, c_1.y, elevation - LEVEL_SHIFT_VALUE)
|
||||
c2_e = Geom::Point3d.new(c_2.x, c_2.y, elevation - LEVEL_SHIFT_VALUE)
|
||||
@@ -208,17 +122,8 @@ module SpeckleConnector3
|
||||
# rubocop:enable Metrics/AbcSize
|
||||
# rubocop:enable Metrics/MethodLength
|
||||
|
||||
# @return [Sketchup::ComponentDefinition] branch definition to fill objects in it.
|
||||
def branch_definition
|
||||
@definition_name = "#{@branch_name}-#{@stream_name}"
|
||||
definition = sketchup_model.definitions.find { |d| d.name == @definition_name }
|
||||
@is_update_commit = !definition.nil?
|
||||
definition = sketchup_model.definitions.add(@definition_name) if definition.nil?
|
||||
definition
|
||||
end
|
||||
|
||||
def entities_to_fill(_obj)
|
||||
return sketchup_model.entities unless from_revit
|
||||
return sketchup_model.entities if from_sketchup
|
||||
|
||||
@definition_name = "#{@branch_name}-#{@stream_name}"
|
||||
definition = sketchup_model.definitions.find { |d| d.name == @definition_name }
|
||||
@@ -226,7 +131,7 @@ module SpeckleConnector3
|
||||
definition = sketchup_model.definitions.add(@definition_name)
|
||||
sketchup_model.entities.add_instance(definition, Geom::Transformation.new)
|
||||
end
|
||||
definition
|
||||
definition.entities
|
||||
end
|
||||
|
||||
LAYERS_WILL_BE_HIDDEN = [
|
||||
@@ -264,14 +169,62 @@ module SpeckleConnector3
|
||||
['Objects.BuiltElements.Revit.Parameter'].include?(obj['speckle_type'])
|
||||
end
|
||||
|
||||
# Create actual Sketchup layers from layer_paths that taken from Speckle base object.
|
||||
# @param layer_paths [Array<String>] layer paths to decompose it to folders and it's layers.
|
||||
# @param folder [Sketchup::Layers, Sketchup::LayerFolder] folder to create folders and layers under it.
|
||||
def create_layers(layer_paths, folder)
|
||||
# Strip leading '@'
|
||||
layers_with_folders = layer_paths.map { |layer| layer[1..-1] }
|
||||
# Split layer_paths according to having parent folder or not.
|
||||
layers_with_head_folder, headless_layers = layers_with_folders.partition { |layer| layer.include?('::') }
|
||||
# Create array of array that split with '::'
|
||||
folder_layer_arrays = layers_with_head_folder.collect { |folder_layer| folder_layer.split('::') }
|
||||
# Add headless layers into `Sketchup.active_model.layers`
|
||||
create_headless_layers(headless_layers, folder)
|
||||
# Create layers that have parent folder(s)- this method is recursive until all tree is created.
|
||||
create_folder_layers(folder_layer_arrays, folder)
|
||||
end
|
||||
|
||||
# @param headless_layers [Array<String>] headless layer names.
|
||||
# @param folder [Sketchup::Layers, Sketchup::LayerFolder] layer folder to create commit layers under it.
|
||||
def create_headless_layers(headless_layers, folder)
|
||||
headless_layers.each do |layer_name|
|
||||
# Add layer first to the layers object of sketchup model.
|
||||
layer = sketchup_model.layers.add(layer_name)
|
||||
folder.add_layer(layer) unless folder.layers.any? { |l| l.display_name == layer_name }
|
||||
end
|
||||
end
|
||||
|
||||
# Create layers with it's parent folders.
|
||||
# @param folder [Sketchup::LayerFolder] layer folder to create commit layers under it.
|
||||
def create_folder_layers(folder_layer_arrays, folder)
|
||||
folder_layer_arrays.each do |folder_layer_array|
|
||||
create_folder_layer(folder_layer_array, folder)
|
||||
end
|
||||
end
|
||||
|
||||
# Create layers that have parent folder(s)- this method is recursive (self-caller) until all tree is created.
|
||||
def create_folder_layer(folder_array, folder)
|
||||
if folder_array.length > 1
|
||||
# add folder if it is not exist.
|
||||
folder.add_folder(folder_array[0]) unless folder.folders.any? { |f| f.display_name == folder_array[0] }
|
||||
new_folder = folder.folders.find { |f| f.display_name == folder_array[0] }
|
||||
create_folder_layer(folder_array[1..-1], new_folder)
|
||||
else
|
||||
# Add layer first to the layers object of sketchup model.
|
||||
layer = sketchup_model.layers.add(folder_array[0])
|
||||
folder.add_layer(layer) unless folder.layers.any? { |l| l.display_name == layer }
|
||||
end
|
||||
end
|
||||
|
||||
# Traversal method to create Sketchup objects from upcoming base object.
|
||||
# @param obj [Hash, Array] object might be source base object or it's sub objects, because this method is a
|
||||
# self-caller method means that call itself according to conditions inside of it.
|
||||
# rubocop:disable Metrics/CyclomaticComplexity
|
||||
# rubocop:disable Metrics/PerceivedComplexity
|
||||
def traverse_commit_object(obj, layer, entities)
|
||||
def traverse_commit_object(obj, commit_folder, layer, entities)
|
||||
if convertible_to_native?(obj)
|
||||
@state, _converted_entities = convert_to_native(@state, obj, layer, entities)
|
||||
@state = convert_to_native(@state, obj, layer, entities)
|
||||
elsif obj.is_a?(Hash) && obj.key?('speckle_type')
|
||||
return if ignored_speckle_type?(obj)
|
||||
|
||||
@@ -279,23 +232,57 @@ module SpeckleConnector3
|
||||
# puts(">>> Found #{obj['speckle_type']}: #{obj['id']}. Continuing traversal.")
|
||||
props = obj.keys.filter_map { |key| key unless key.start_with?('_') }
|
||||
props.each do |prop|
|
||||
traverse_commit_object(obj[prop], layer, entities)
|
||||
layer_path = prop if prop.start_with?('@') && obj[prop].is_a?(Array)
|
||||
layer = find_layer(layer_path, commit_folder, layer)
|
||||
traverse_commit_object(obj[prop], commit_folder, layer, entities)
|
||||
end
|
||||
else
|
||||
# puts(">>> Found #{obj['speckle_type']}: #{obj['id']} with displayValue.")
|
||||
@state, _converted_entities = convert_to_native(@state, obj, layer, entities)
|
||||
@state = convert_to_native(@state, obj, layer, entities)
|
||||
end
|
||||
elsif obj.is_a?(Hash)
|
||||
obj.each_value { |value| traverse_commit_object(value, layer, entities) }
|
||||
obj.each_value { |value| traverse_commit_object(value, commit_folder, layer, entities) }
|
||||
elsif obj.is_a?(Array)
|
||||
obj.each { |value| traverse_commit_object(value, layer, entities) }
|
||||
obj.each { |value| traverse_commit_object(value, commit_folder, layer, entities) }
|
||||
end
|
||||
end
|
||||
# rubocop:enable Metrics/CyclomaticComplexity
|
||||
# rubocop:enable Metrics/PerceivedComplexity
|
||||
|
||||
# Find layer of the Speckle object by checking iteratively into folder.
|
||||
# @param layer_path [String] complete layer_path to retrieve
|
||||
# @param folder [Sketchup::LayerFolder, Sketchup::Layers] entry folder to search layer
|
||||
# @param fallback_layer [Sketchup::Layer] fallback layer to assign object later if any error occur.
|
||||
# @return [Sketchup::Layer] layer according to path
|
||||
# @example
|
||||
# "@folder_1::folder_2::layer_1"
|
||||
# # it will return the layer object which has display name as `layer_1`.
|
||||
def find_layer(layer_path, folder, fallback_layer)
|
||||
begin
|
||||
# Split folders and it's tail layer (last one is layer, others are folders.)
|
||||
layer_path_array = layer_path[1..-1].split('::')
|
||||
# Get sub folders as array, might be empty if `layer_path_array` has only 1 entry
|
||||
sub_folders = layer_path_array.length > 1 ? layer_path_array[0..-2] : []
|
||||
# Get exact layer name from last entry
|
||||
layer_name = layer_path_array.last
|
||||
# Iterate sub folders to find new sub folder to switch it.
|
||||
# It help to search in the tree by switching the target search folder.
|
||||
# Finally we can reach the layer name.
|
||||
sub_folders.each do |sub_folder|
|
||||
# Try to find sub folder into source folder passes by argument
|
||||
s_f = folder.folders.find { |f| f.display_name == sub_folder }
|
||||
# Switch source folder if any exist
|
||||
folder = s_f unless s_f.nil?
|
||||
end
|
||||
# Find finally the layer into related folder
|
||||
folder.layers.find { |l| l.display_name == layer_name }
|
||||
rescue StandardError
|
||||
return fallback_layer
|
||||
end
|
||||
end
|
||||
|
||||
def speckle_object_to_native(obj)
|
||||
return DISPLAY_VALUE.method(:to_native) unless obj['displayValue'].nil? && obj['@displayValue'].nil?
|
||||
return DISPLAY_VALUE.method(:to_native) unless obj['displayValue'].nil?
|
||||
|
||||
SPECKLE_OBJECT_TO_NATIVE[obj['speckle_type']]
|
||||
end
|
||||
@@ -303,25 +290,13 @@ module SpeckleConnector3
|
||||
SPECKLE_OBJECT_TO_NATIVE = {
|
||||
OBJECTS_GEOMETRY_LINE => LINE.method(:to_native),
|
||||
OBJECTS_GEOMETRY_POLYLINE => LINE.method(:to_native),
|
||||
OBJECTS_GEOMETRY_POLYCURVE => POLYCURVE.method(:to_native),
|
||||
OBJECTS_GEOMETRY_ARC => ARC.method(:to_native),
|
||||
OBJECTS_GEOMETRY_CIRCLE => CIRCLE.method(:to_native),
|
||||
OBJECTS_GEOMETRY_MESH => MESH.method(:to_native),
|
||||
OBJECTS_GEOMETRY_BREP => MESH.method(:to_native),
|
||||
OBJECTS_OTHER_BLOCKDEFINITION => BLOCK_DEFINITION.method(:to_native),
|
||||
OBJECTS_OTHER_BLOCKINSTANCE => BLOCK_INSTANCE.method(:to_native),
|
||||
OBJECTS_OTHER_BLOCKINSTANCE_FULL => BLOCK_INSTANCE.method(:to_native),
|
||||
OBJECTS_OTHER_REVIT_REVITINSTANCE => REVIT_INSTANCE.method(:to_native),
|
||||
OBJECTS_OTHER_RENDERMATERIAL => RENDER_MATERIAL.method(:to_native),
|
||||
OBJECTS_BUILTELEMENTS_VIEW3D => VIEW3D.method(:to_native),
|
||||
OBJECTS_BUILTELEMENTS_REVIT_WALL => REVIT_WALL.method(:to_native),
|
||||
OBJECTS_BUILTELEMENTS_REVIT_DIRECTSHAPE => BUILTELEMENTS::Revit::DirectShape.method(:to_native),
|
||||
OBJECTS_BUILTELEMENTS_NETWORK => BUILTELEMENTS::Network.method(:to_native),
|
||||
OBJECTS_GIS_POLYGONELEMENT => POLYGON_ELEMENT.method(:to_native),
|
||||
OBJECTS_GIS_LINEELEMENT => LINE_ELEMENT.method(:to_native),
|
||||
SPECKLE_CORE_MODELS_COLLECTION => COLLECTION.method(:to_native),
|
||||
SPECKLE_CORE_MODELS_COLLECTION_RASTER_LAYER => GIS_LAYER_COLLECTION.method(:to_native),
|
||||
SPECKLE_CORE_MODELS_COLLECTION_VECTOR_LAYER => GIS_LAYER_COLLECTION.method(:to_native)
|
||||
OBJECTS_OTHER_RENDERMATERIAL => RENDER_MATERIAL.method(:to_native)
|
||||
}.freeze
|
||||
|
||||
# @param state [States::State] state of the speckle application
|
||||
@@ -333,9 +308,6 @@ module SpeckleConnector3
|
||||
# Call 'to_native' method by passing this method itself to handle nested 'to_native' conversions.
|
||||
# It returns updated state and converted entities.
|
||||
state, converted_entities = to_native_method.call(state, obj, layer, entities, &convert_to_native)
|
||||
@converted_entities += converted_entities
|
||||
faces = converted_entities.select { |e| e.is_a?(Sketchup::Face) }
|
||||
@converted_faces += faces if faces.any?
|
||||
if from_revit
|
||||
# Create levels as section planes if they exists
|
||||
create_levels(state, obj)
|
||||
@@ -343,26 +315,11 @@ module SpeckleConnector3
|
||||
create_layers_from_categories(state, obj, converted_entities)
|
||||
end
|
||||
# Create speckle entities from sketchup entities to achieve continuous traversal.
|
||||
|
||||
converted_entities.each do |converted|
|
||||
@conversion_results.push(UiData::Report::ConversionResult.new(UiData::Report::ConversionStatus::SUCCESS,
|
||||
obj['id'],
|
||||
obj['speckle_type'],
|
||||
converted.persistent_id.to_s,
|
||||
converted.class))
|
||||
|
||||
end
|
||||
SpeckleEntities::SpeckleEntity.from_speckle_object(state, obj, converted_entities, stream_id)
|
||||
convert_to_speckle_entities(state, obj, converted_entities)
|
||||
rescue StandardError => e
|
||||
puts("Failed to convert #{obj['speckle_type']} (id: #{obj['id']})")
|
||||
puts(e)
|
||||
@conversion_results.push(UiData::Report::ConversionResult.new(UiData::Report::ConversionStatus::ERROR,
|
||||
obj['id'],
|
||||
obj['speckle_type'],
|
||||
nil,
|
||||
nil,
|
||||
e))
|
||||
return state, []
|
||||
return state
|
||||
end
|
||||
|
||||
# rubocop:disable Metrics/CyclomaticComplexity
|
||||
@@ -372,13 +329,13 @@ module SpeckleConnector3
|
||||
|
||||
layer = sketchup_model.layers.find { |l| l.display_name == speckle_object['category'] }
|
||||
unless layer.nil?
|
||||
entities.each { |entity| entity.layer = layer if entity.respond_to?(:layer) } if layer
|
||||
entities.each { |entity| entity.layer = layer } if layer
|
||||
return state
|
||||
end
|
||||
|
||||
layer = sketchup_model.layers.add(speckle_object['category'])
|
||||
unless layer.nil?
|
||||
entities.each { |entity| entity.layer = layer if entity.respond_to?(:layer) } if layer
|
||||
entities.each { |entity| entity.layer = layer } if layer
|
||||
state
|
||||
end
|
||||
state
|
||||
@@ -388,27 +345,39 @@ module SpeckleConnector3
|
||||
|
||||
# @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')
|
||||
return state if speckle_object['level'].nil?
|
||||
return state unless speckle_object['level']['speckle_type'].include?('Objects.BuiltElements.Level')
|
||||
|
||||
level_name = level['name'] || level['id']
|
||||
level_name = speckle_object['level']['name'] || speckle_object['level']['id']
|
||||
is_exist = @entities_to_fill.grep(Sketchup::SectionPlane).any? { |sp| sp.name == level_name }
|
||||
return state if is_exist
|
||||
|
||||
elevation = SpeckleObjects::Geometry.length_to_native(level['elevation'], level['units'])
|
||||
elevation = SpeckleObjects::Geometry.length_to_native(speckle_object['level']['elevation'],
|
||||
speckle_object['level']['units'])
|
||||
|
||||
section_plane = @entities_to_fill.add_section_plane([0, 0, elevation + LEVEL_SHIFT_VALUE], [0, 0, -1])
|
||||
section_plane.name = level_name
|
||||
SketchupModel::Dictionary::SpeckleEntityDictionaryHandler.write_initial_base_data(
|
||||
section_plane, level['applicationId'], level['id'], level['speckle_type'], [], @stream_id
|
||||
)
|
||||
state
|
||||
end
|
||||
|
||||
# @param state [States::State] state of the application
|
||||
def convert_to_speckle_entities(state, speckle_objects_with_entities)
|
||||
return state if speckle_objects_with_entities.empty?
|
||||
def convert_to_speckle_entities(state, speckle_object, entities)
|
||||
speckle_id = speckle_object['id']
|
||||
application_id = speckle_object['applicationId']
|
||||
speckle_type = speckle_object['speckle_type']
|
||||
children = speckle_object['__closure'].nil? ? [] : speckle_object['__closure']
|
||||
speckle_state = state.speckle_state
|
||||
entities.each do |entity|
|
||||
next if entity.is_a?(Sketchup::Material)
|
||||
next if (entity.is_a?(Sketchup::Face) || entity.is_a?(Sketchup::Edge)) &&
|
||||
!state.user_state.user_preferences[:register_speckle_entity]
|
||||
|
||||
ent = SpeckleEntities::SpeckleEntity.new(entity, speckle_id, application_id, speckle_type, children,
|
||||
[stream_id])
|
||||
ent.write_initial_base_data
|
||||
speckle_state = speckle_state.with_speckle_entity(ent)
|
||||
end
|
||||
state.with_speckle_state(speckle_state)
|
||||
end
|
||||
end
|
||||
# rubocop:enable Metrics/ClassLength
|
||||
@@ -0,0 +1,230 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative 'converter'
|
||||
require_relative 'base_object_serializer'
|
||||
require_relative '../speckle_objects/base'
|
||||
require_relative '../speckle_objects/geometry/line'
|
||||
require_relative '../speckle_objects/geometry/length'
|
||||
require_relative '../speckle_objects/geometry/mesh'
|
||||
require_relative '../speckle_objects/other/block_instance'
|
||||
require_relative '../speckle_objects/other/block_definition'
|
||||
require_relative '../speckle_objects/other/rendering_options'
|
||||
require_relative '../speckle_objects/built_elements/view3d'
|
||||
require_relative '../constants/path_constants'
|
||||
|
||||
module SpeckleConnector
|
||||
module Converters
|
||||
# Converts sketchup entities to speckle objects.
|
||||
class ToSpeckle < Converter
|
||||
# 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)
|
||||
layers = add_all_layers
|
||||
state = speckle_state
|
||||
sketchup_model.selection.each do |entity|
|
||||
new_speckle_state, converted_object_with_entity = convert(entity, preferences, state)
|
||||
state = new_speckle_state
|
||||
layer_name = entity_layer_path(entity)
|
||||
layers[layer_name].push(converted_object_with_entity)
|
||||
end
|
||||
# send only+ layers that have any object
|
||||
base_object_properties = layers.reject { |_layer_name, objects| objects.empty? }
|
||||
add_views(base_object_properties) if sketchup_model.pages.any?
|
||||
return state, SpeckleObjects::Base.with_detached_layers(base_object_properties)
|
||||
end
|
||||
|
||||
# Add views from pages.
|
||||
# @param base_object_properties [Hash] dynamically attached base object properties.
|
||||
def add_views(base_object_properties)
|
||||
views = []
|
||||
sketchup_model.pages.each do |page|
|
||||
cam = page.camera
|
||||
origin = get_camera_origin(cam)
|
||||
target = get_camera_target(cam)
|
||||
direction = get_camera_direction(cam)
|
||||
update_properties = get_scene_update_properties(page)
|
||||
rendering_options = SpeckleObjects::Others::RenderingOptions.to_speckle(page.rendering_options)
|
||||
view = SpeckleObjects::BuiltElements::View3d.new(
|
||||
page.name, origin, target, direction, SpeckleObjects::Geometry::Vector.new(0, 0, 1, @units),
|
||||
cam.perspective?, cam.fov, @units, page.name, update_properties, rendering_options
|
||||
)
|
||||
views.append(view)
|
||||
end
|
||||
base_object_properties['@Named Views'] = views
|
||||
end
|
||||
|
||||
# Get scene properties
|
||||
# @param page [Sketchup::Page] page on sketchup.
|
||||
def get_scene_update_properties(page)
|
||||
{
|
||||
use_axes: page.use_axes?,
|
||||
use_camera: page.use_camera?,
|
||||
use_hidden_geometry: page.use_hidden_geometry?,
|
||||
use_hidden_layers: page.use_hidden_layers?,
|
||||
use_hidden_objects: page.use_hidden_objects?,
|
||||
use_rendering_options: page.use_rendering_options?,
|
||||
use_section_planes: page.use_section_planes?,
|
||||
use_shadow_info: page.use_shadow_info?,
|
||||
use_style: page.use_style?
|
||||
}
|
||||
end
|
||||
|
||||
# Serialized and traversed information to send batches.
|
||||
# @param base_and_entity [SpeckleObjects::Base] base object to serialize.
|
||||
# @return [String, Integer, Array<Object>] base id, total_children_count of base and batches
|
||||
def serialize(base_and_entity, speckle_state, preferences)
|
||||
serializer = SpeckleConnector::Converters::BaseObjectSerializer.new(speckle_state, stream_id, preferences)
|
||||
t = Time.now.to_f
|
||||
id = serializer.serialize(base_and_entity)
|
||||
batches = serializer.batch_objects
|
||||
# write_to_speckle_folder(id, batches)
|
||||
puts "Generating traversed object elapsed #{Time.now.to_f - t} s"
|
||||
base_total_children_count = serializer.total_children_count(id)
|
||||
return id, base_total_children_count, batches, serializer.speckle_state
|
||||
end
|
||||
|
||||
def write_to_speckle_folder(id, batches)
|
||||
folder_path = "#{HOME_PATH}/Speckle"
|
||||
file_path = "#{folder_path}/#{id}.json"
|
||||
FileUtils.mkdir_p(folder_path) unless File.exist?(folder_path)
|
||||
File.write(file_path, batches.first)
|
||||
end
|
||||
|
||||
# @param entity [Sketchup::Entity] sketchup entity to convert Speckle.
|
||||
# @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.
|
||||
# rubocop:disable Metrics/MethodLength
|
||||
def convert(entity, preferences, speckle_state, parent = :base)
|
||||
convert = method(:convert)
|
||||
|
||||
if entity.is_a?(Sketchup::Edge)
|
||||
line = SpeckleObjects::Geometry::Line.from_edge(entity, @units, preferences[:model]).to_h
|
||||
return speckle_state, [line, [entity]]
|
||||
end
|
||||
|
||||
if entity.is_a?(Sketchup::Face)
|
||||
mesh = SpeckleObjects::Geometry::Mesh.from_face(entity, @units, preferences[:model])
|
||||
return speckle_state, [mesh, [entity]]
|
||||
end
|
||||
|
||||
if entity.is_a?(Sketchup::Group)
|
||||
new_speckle_state, block_instance = SpeckleObjects::Other::BlockInstance.from_group(
|
||||
entity, @units, preferences, speckle_state, &convert
|
||||
)
|
||||
speckle_state = new_speckle_state
|
||||
return speckle_state, [block_instance, [entity]]
|
||||
end
|
||||
|
||||
if entity.is_a?(Sketchup::ComponentInstance)
|
||||
new_speckle_state, block_instance = SpeckleObjects::Other::BlockInstance.from_component_instance(
|
||||
entity, @units, preferences, speckle_state, &convert
|
||||
)
|
||||
speckle_state = new_speckle_state
|
||||
return speckle_state, [block_instance, [entity]]
|
||||
end
|
||||
|
||||
if entity.is_a?(Sketchup::ComponentDefinition)
|
||||
new_speckle_state, block_definition = SpeckleObjects::Other::BlockDefinition.from_definition(
|
||||
entity, @units, @definitions, preferences, speckle_state, parent, &convert
|
||||
)
|
||||
speckle_state = new_speckle_state
|
||||
return speckle_state, [block_definition, [entity]]
|
||||
end
|
||||
|
||||
return speckle_state, nil
|
||||
end
|
||||
# rubocop:enable Metrics/MethodLength
|
||||
|
||||
# Create layers -> {Hash{Symbol=>Array}} from sketchup model with empty array as hash entry values.
|
||||
# This method add first headless layers (not belong to any folder),
|
||||
# then goes through each folder, their sub-folders and their layers.
|
||||
# @return [Hash{Symbol=>Array}] layers from sketchup model with empty array as hash entry values.
|
||||
def add_all_layers
|
||||
# add headless layers
|
||||
layer_objects = add_layers(sketchup_model.layers.layers)
|
||||
# add layers from folders
|
||||
add_layers_from_folders(sketchup_model.layers.folders, layer_objects)
|
||||
layer_objects
|
||||
end
|
||||
|
||||
# @param layers [Array<Sketchup::Layer>] layers in sketchup model
|
||||
# @return [Hash{Symbol=>Array}] layers with empty array value.
|
||||
def add_layers(layers, layer_objects = {}, parent_name = '')
|
||||
layers.each do |layer|
|
||||
layer_name = parent_name.empty? ? "@#{layer.display_name}" : "#{parent_name}::#{layer.display_name}"
|
||||
layer_objects[layer_name] = []
|
||||
end
|
||||
layer_objects
|
||||
end
|
||||
|
||||
# @param folders [Array<Sketchup::LayerFolder>] layer folders in sketchup model.
|
||||
# @param layer_objects [Hash{Symbol=>Array}] layer objects to fill in.
|
||||
# @param parent_name [String] parent folder name to structure layer path before send to Speckle.
|
||||
# ex: "@#{parent_name}::#{layer_name}"
|
||||
def add_layers_from_folders(folders, layer_objects, parent_name = '')
|
||||
folders.each do |folder|
|
||||
folder_name = parent_name.empty? ? "@#{folder.display_name}" : "#{parent_name}::#{folder.display_name}"
|
||||
add_layers(folder.layers, layer_objects, folder_name)
|
||||
add_layers_from_folders(folder.folders, layer_objects, folder_name) unless folder.folders.empty?
|
||||
end
|
||||
end
|
||||
|
||||
# Find layer path of given Sketchup entity.
|
||||
# @param entity [Sketchup::Entity] entity to find root layer.
|
||||
# @return [String] layer path of Sketchup entity.
|
||||
def entity_layer_path(entity)
|
||||
layer_name = entity.layer.display_name
|
||||
if entity.layer.folder.nil?
|
||||
"@#{layer_name}"
|
||||
else
|
||||
folders = folder_name(entity.layer.folder)
|
||||
path = ''
|
||||
folders.reverse.each do |folder|
|
||||
path += "#{folder}::"
|
||||
end
|
||||
"@#{path}#{layer_name}"
|
||||
end
|
||||
end
|
||||
|
||||
# Nested method to retrieve sub-folders until nothing found.
|
||||
# @return [Array<String>] folder names as list from bottom to top. Might need to be reversed if you want to see
|
||||
# from top to bottom.
|
||||
def folder_name(folder, folders = [])
|
||||
if folder.folder.nil?
|
||||
folders.push(folder.display_name)
|
||||
else
|
||||
folder_name(folder.folder, folders.push(folder.display_name))
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def get_camera_direction(cam)
|
||||
SpeckleObjects::Geometry::Vector.new(
|
||||
SpeckleObjects::Geometry.length_to_speckle(cam.direction[0], @units),
|
||||
SpeckleObjects::Geometry.length_to_speckle(cam.direction[1], @units),
|
||||
SpeckleObjects::Geometry.length_to_speckle(cam.direction[2], @units),
|
||||
@units
|
||||
)
|
||||
end
|
||||
|
||||
def get_camera_target(cam)
|
||||
SpeckleObjects::Geometry::Point.new(
|
||||
SpeckleObjects::Geometry.length_to_speckle(cam.target[0], @units),
|
||||
SpeckleObjects::Geometry.length_to_speckle(cam.target[1], @units),
|
||||
SpeckleObjects::Geometry.length_to_speckle(cam.target[2], @units),
|
||||
@units
|
||||
)
|
||||
end
|
||||
|
||||
def get_camera_origin(camera)
|
||||
SpeckleObjects::Geometry::Point.new(
|
||||
SpeckleObjects::Geometry.length_to_speckle(camera.eye[0], @units),
|
||||
SpeckleObjects::Geometry.length_to_speckle(camera.eye[1], @units),
|
||||
SpeckleObjects::Geometry.length_to_speckle(camera.eye[2], @units),
|
||||
@units
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,6 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module SpeckleConnector3
|
||||
module SpeckleConnector
|
||||
module Converters
|
||||
SKETCHUP_UNITS = { 0 => 'in', 1 => 'ft', 2 => 'mm', 3 => 'cm', 4 => 'm', 5 => 'yd' }.freeze
|
||||
SKETCHUP_UNIT_STRINGS = { 'm' => 'm', 'mm' => 'mm', 'ft' => 'feet', 'in' => 'inch', 'yd' => 'yard',
|
||||
@@ -6,7 +6,7 @@ require_relative 'vector'
|
||||
# Add json conversion methods
|
||||
require_relative 'json'
|
||||
|
||||
module SpeckleConnector3
|
||||
module SpeckleConnector
|
||||
module Immutable
|
||||
class Hash
|
||||
# Return a new {Set} containing the keys from this `Hash`.
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
require_relative 'list'
|
||||
|
||||
module SpeckleConnector3
|
||||
module SpeckleConnector
|
||||
module Immutable
|
||||
|
||||
# A `Deque` (or double-ended queue) is an ordered, sequential collection of
|
||||
@@ -288,6 +288,6 @@ module SpeckleConnector3
|
||||
# one rather than creating many empty deques using `Deque.new`.
|
||||
#
|
||||
# @private
|
||||
EmptyDeque = SpeckleConnector3::Immutable::Deque.empty
|
||||
EmptyDeque = SpeckleConnector::Immutable::Deque.empty
|
||||
end
|
||||
end
|
||||
@@ -1,4 +1,4 @@
|
||||
module SpeckleConnector3
|
||||
module SpeckleConnector
|
||||
module Immutable
|
||||
|
||||
# Helper module for immutable-ruby's sequential collections
|
||||
@@ -74,7 +74,7 @@ module SpeckleConnector3
|
||||
# with an added element.
|
||||
def group_by_with(empty_group, &block)
|
||||
block ||= lambda { |item| item }
|
||||
reduce(SpeckleConnector3::Immutable::EmptyHash) do |hash, item|
|
||||
reduce(SpeckleConnector::Immutable::EmptyHash) do |hash, item|
|
||||
key = block.call(item)
|
||||
group = hash.get(key) || empty_group
|
||||
hash.put(key, group.add(item))
|
||||
@@ -126,7 +126,7 @@ module SpeckleConnector3
|
||||
|
||||
# Convert this collection to a {Set}.
|
||||
def to_set
|
||||
SpeckleConnector3::Immutable::Set.new(self)
|
||||
SpeckleConnector::Immutable::Set.new(self)
|
||||
end
|
||||
|
||||
# Convert this collection to a programmer-readable `String` representation.
|
||||
@@ -4,7 +4,7 @@ require_relative 'undefined'
|
||||
require_relative 'enumerable'
|
||||
require_relative 'trie'
|
||||
|
||||
module SpeckleConnector3
|
||||
module SpeckleConnector
|
||||
module Immutable
|
||||
|
||||
# An `Immutable::Hash` maps a set of unique keys to corresponding values, much
|
||||
@@ -59,7 +59,7 @@ module Immutable
|
||||
# hash = Immutable::Hash.new { |missing_key| missing_key * 10 }
|
||||
# hash[5] # => 50
|
||||
class Hash
|
||||
include SpeckleConnector3::Immutable::Enumerable
|
||||
include SpeckleConnector::Immutable::Enumerable
|
||||
|
||||
class << self
|
||||
# Create a new `Hash` populated with the given key/value pairs.
|
||||
@@ -916,6 +916,6 @@ module Immutable
|
||||
# one rather than creating many empty hashes using `Hash.new`.
|
||||
#
|
||||
# @private
|
||||
EmptyHash = SpeckleConnector3::Immutable::Hash.empty
|
||||
EmptyHash = SpeckleConnector::Immutable::Hash.empty
|
||||
end
|
||||
end
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
# Define the method to export immutable structures to JSON. Default exporter would only export the name of the class as
|
||||
# a string. In order to export it properly, we need to add `to_json` methods to the classes we want to serialize as JSON.
|
||||
module SpeckleConnector3
|
||||
module SpeckleConnector
|
||||
module Immutable
|
||||
class Vector
|
||||
# Convert the object to JSON
|
||||
@@ -9,7 +9,7 @@ require_relative 'enumerable'
|
||||
require_relative 'hash'
|
||||
require_relative 'set'
|
||||
|
||||
module SpeckleConnector3
|
||||
module SpeckleConnector
|
||||
module Immutable
|
||||
class << self
|
||||
|
||||
@@ -120,7 +120,7 @@ module Immutable
|
||||
# traversed to find the element.
|
||||
#
|
||||
module List
|
||||
include SpeckleConnector3::Immutable::Enumerable
|
||||
include SpeckleConnector::Immutable::Enumerable
|
||||
|
||||
# @private
|
||||
CADR = /^c([ad]+)r$/
|
||||
@@ -4,7 +4,7 @@ require_relative 'trie'
|
||||
require_relative 'sorted_set'
|
||||
require 'set'
|
||||
|
||||
module SpeckleConnector3
|
||||
module SpeckleConnector
|
||||
module Immutable
|
||||
|
||||
# `Immutable::Set` is a collection of unordered values with no duplicates. Testing whether
|
||||
@@ -47,7 +47,7 @@ module Immutable
|
||||
# set1.subset?(set3) # => true
|
||||
#
|
||||
class Set
|
||||
include SpeckleConnector3::Immutable::Enumerable
|
||||
include SpeckleConnector::Immutable::Enumerable
|
||||
|
||||
class << self
|
||||
# Create a new `Set` populated with the given items.
|
||||
@@ -288,7 +288,7 @@ module Immutable
|
||||
# @param other [Enumerable] The collection to merge with
|
||||
# @return [Set]
|
||||
def union(other)
|
||||
if other.is_a?(SpeckleConnector3::Immutable::Set)
|
||||
if other.is_a?(SpeckleConnector::Immutable::Set)
|
||||
if other.size > size
|
||||
small_set_pairs = @trie
|
||||
large_set_trie = other.instance_variable_get(:@trie)
|
||||
@@ -322,7 +322,7 @@ module Immutable
|
||||
# @return [Set]
|
||||
def intersection(other)
|
||||
if other.size < @trie.size
|
||||
if other.is_a?(SpeckleConnector3::Immutable::Set)
|
||||
if other.is_a?(SpeckleConnector::Immutable::Set)
|
||||
trie = other.instance_variable_get(:@trie).select { |key, _| include?(key) }
|
||||
else
|
||||
trie = Trie.new(0)
|
||||
@@ -344,7 +344,7 @@ module Immutable
|
||||
# @param other [Enumerable] The collection to subtract from this set
|
||||
# @return [Set]
|
||||
def difference(other)
|
||||
trie = if (@trie.size <= other.size) && (other.is_a?(SpeckleConnector3::Immutable::Set) || (defined?(::Set) && other.is_a?(::Set)))
|
||||
trie = if (@trie.size <= other.size) && (other.is_a?(SpeckleConnector::Immutable::Set) || (defined?(::Set) && other.is_a?(::Set)))
|
||||
@trie.select { |key, _| !other.include?(key) }
|
||||
else
|
||||
@trie.bulk_delete(other)
|
||||
@@ -386,7 +386,7 @@ module Immutable
|
||||
# After doing some benchmarking to estimate the constants, it appears break-even is at ~190 items
|
||||
# We also check other.size, to avoid the more expensive #is_a? checks in cases where it doesn't matter
|
||||
#
|
||||
if other.size >= 150 && @trie.size >= 190 && !(other.is_a?(SpeckleConnector3::Immutable::Set) || other.is_a?(::Set))
|
||||
if other.size >= 150 && @trie.size >= 190 && !(other.is_a?(SpeckleConnector::Immutable::Set) || other.is_a?(::Set))
|
||||
other = ::Set.new(other)
|
||||
end
|
||||
all? { |item| other.include?(item) }
|
||||
@@ -417,7 +417,7 @@ module Immutable
|
||||
def proper_subset?(other)
|
||||
return false if other.size <= size
|
||||
# See comments above
|
||||
if other.size >= 150 && @trie.size >= 190 && !(other.is_a?(SpeckleConnector3::Immutable::Set) || other.is_a?(::Set))
|
||||
if other.size >= 150 && @trie.size >= 190 && !(other.is_a?(SpeckleConnector::Immutable::Set) || other.is_a?(::Set))
|
||||
other = ::Set.new(other)
|
||||
end
|
||||
all? { |item| other.include?(item) }
|
||||
@@ -450,7 +450,7 @@ module Immutable
|
||||
other.each { |item| return false if include?(item) }
|
||||
else
|
||||
# See comment on #subset?
|
||||
if other.size >= 150 && @trie.size >= 190 && !(other.is_a?(SpeckleConnector3::Immutable::Set) || other.is_a?(::Set))
|
||||
if other.size >= 150 && @trie.size >= 190 && !(other.is_a?(SpeckleConnector::Immutable::Set) || other.is_a?(::Set))
|
||||
other = ::Set.new(other)
|
||||
end
|
||||
each { |item| return false if other.include?(item) }
|
||||
@@ -584,6 +584,6 @@ module Immutable
|
||||
# one rather than creating many empty sets using `Set.new`.
|
||||
#
|
||||
# @private
|
||||
EmptySet = SpeckleConnector3::Immutable::Set.empty
|
||||
EmptySet = SpeckleConnector::Immutable::Set.empty
|
||||
end
|
||||
end
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
require_relative 'enumerable'
|
||||
|
||||
module SpeckleConnector3
|
||||
module SpeckleConnector
|
||||
module Immutable
|
||||
|
||||
# A `SortedSet` is a collection of ordered values with no duplicates. Unlike a
|
||||
@@ -52,7 +52,7 @@ module Immutable
|
||||
# is a constant time operation.
|
||||
#
|
||||
class SortedSet
|
||||
include SpeckleConnector3::Immutable::Enumerable
|
||||
include SpeckleConnector::Immutable::Enumerable
|
||||
|
||||
class << self
|
||||
# Create a new `SortedSet` populated with the given items. This method does not
|
||||
@@ -1501,6 +1501,6 @@ module Immutable
|
||||
# this one rather than creating many empty sorted sets using `SortedSet.new`.
|
||||
#
|
||||
# @private
|
||||
EmptySortedSet = SpeckleConnector3::Immutable::SortedSet.empty
|
||||
EmptySortedSet = SpeckleConnector::Immutable::SortedSet.empty
|
||||
end
|
||||
end
|
||||