Compare commits

...

97 Commits

Author SHA1 Message Date
oguzhankoral bc3f58a600 Check families and levels before set 2024-01-22 16:16:50 +03:00
Oğuzhan Koral 2152f1c90e Feat (Mapper): CNX-8309 Family instances (#318)
* Implement New Revit Family option

* Filter family types for 'Family Instance'

* Adjust available mapping methods according to selection

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

* Implement family instance mapping

* Pass group selection info if only group selected

* Exclude definitions from conversion escape

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

* Add direct shape option to components back

* Calculate rotation of family instance

* Fix transformation matrix

* Use insertion point for instances

* Set inputs for mapped definition
2024-01-08 18:40:55 +03:00
Oğuzhan Koral 503fb4d246 Feat (GIS): support line element 2023-12-01 01:46:53 +03:00
oguzhankoral 2befefa752 Extract utils for line and polygon element 2023-11-30 15:07:31 +03:00
oguzhankoral 85e64c5076 Add line element support 2023-11-30 00:36:47 +03:00
Oğuzhan Koral 60523dc994 Fix (Collections): Support gis collections and none unit 2023-11-29 16:06:58 +03:00
oguzhankoral 6d780bf350 Rename GisLayerCollection as generic 2023-11-29 16:06:37 +03:00
oguzhankoral eec02a1f84 Support none unit 2023-11-29 15:58:26 +03:00
oguzhankoral ace2fe6fe3 WIP: Convert from 'meters' to 'm' 2023-11-29 13:26:53 +03:00
oguzhankoral 188794af8d Add support for vector layer 2023-11-29 13:26:53 +03:00
Oğuzhan Koral 92a941a944 Fix (Instancing): correct id of definition for speckle entity 2023-11-29 13:25:51 +03:00
oguzhankoral 0e1ddf2b11 Tweak definition speckle entity checks 2023-11-29 13:15:07 +03:00
Oğuzhan Koral b57fa010d1 Fix (Config): Reset configs if configSketchup somehow corrupted 2023-11-28 22:38:56 +03:00
oguzhankoral f816452b78 Reset configs if configSketchup somehow corrupted 2023-11-28 22:36:03 +03:00
Oğuzhan Koral 120083bb31 Feat (Performance): receive performance improvements 2023-11-28 13:54:36 +03:00
oguzhankoral a5bb5c4686 Remove logging 2023-11-28 13:31:19 +03:00
oguzhankoral e5e2729f0a Wrap receive into sketchup operation for performance improvement 2023-11-28 11:57:17 +03:00
oguzhankoral ba8b902f48 Merge coplanar faces at the end of the operation 2023-11-28 11:23:30 +03:00
oguzhankoral 2d67815ae6 Remove unused lines 2023-11-27 23:36:10 +03:00
oguzhankoral ec0c9066d2 Merge coplanar faces remove scoping and check normals first 2023-11-27 21:44:15 +03:00
Oğuzhan Koral 58ae858077 Update dev with changes in main 2023-11-13 19:45:20 +03:00
Oğuzhan Koral 613e7938b3 Fix (Scene): missing views from Revit 2023-11-13 14:35:09 +03:00
oguzhankoral e07ff1a445 Fix missing views from Revit
- Previously it was assuming views arrive in elements of collection
2023-11-13 14:15:32 +03:00
Oğuzhan Koral de7dd34ea2 Feat (FE2): Support fe2 terminology and urls 2023-11-13 12:03:19 +03:00
oguzhankoral 0552f695f9 Update FE2 terms for Mapper Tool 2023-11-10 16:49:51 +03:00
oguzhankoral b8d4f3d946 Parse FE2 urls to add projects via ADD BY ID OR URL 2023-11-10 15:52:06 +03:00
oguzhankoral fa112a70b1 Switch to FE2 as user preferences 2023-11-10 15:13:58 +03:00
oguzhankoral 97309ebb88 Merge remote-tracking branch 'origin/development' 2023-09-19 08:31:29 +03:00
Oğuzhan Koral 556ddc0b6f Feat (deploy): Mac support 🍎 2023-09-11 10:40:08 +03:00
Oğuzhan Koral a0dde690ea Fix intendation 2023-09-08 18:17:59 +03:00
Oğuzhan Koral a76dab5be6 Remove mac suffix from deploy manager 2023-09-08 18:16:15 +03:00
Oğuzhan Koral 2d10bc5bbf Deploy manager for mac 2023-09-08 18:13:15 +03:00
Oğuzhan Koral 4042632e0b Fix (Revit): Support curtain walls from revit 2023-09-08 13:37:07 +03:00
oguzhankoral 7ccf83e1a4 Consider speckle id for revit definition 2023-09-08 13:17:16 +03:00
oguzhankoral 019cd0756f Convert revit walls to native instead with display value 2023-09-08 13:16:50 +03:00
József L. Kiss 0e5f9f80be CI integration of mac build (#301)
* patcher

* patch-version

* fix 1

* fix 2

* patch_version

* revert

* mac installer

* slname

* checkout

* powershell

* SEMVER

* python

* installername

* git checkout

* cd speckle-sharp-ci-tools

* zip

* zip location

* white space

* remove checktou

---------

Co-authored-by: József L. Kiss <>
2023-09-07 17:05:02 +03:00
József L. Kiss fc6767860a Add release version of sqlit3 bundle 2023-08-17 13:05:13 +02:00
oguzhankoral 5b5b4be7b2 Mac AppData folder 2023-08-16 17:22:25 +03:00
oguzhankoral 45351d082e Add sqlite3_27.bundle for mac 2023-08-16 16:30:43 +03:00
Oğuzhan Koral 22ccd07491 Release 2.15 2023-07-25 14:48:22 +03:00
Oğuzhan Koral 2cf9ee647b Fix (Mapper): Add offset parameter to walls 2023-07-25 14:26:21 +03:00
oguzhankoral efb567824b Add offset parameter to walls 2023-07-12 13:01:16 +03:00
Oğuzhan Koral f0aac39486 Chore (Mixpanel): tracking for mapper actions 2023-07-06 10:03:08 +03:00
oguzhankoral f278055805 Correct mappings set and applied 2023-07-03 12:27:23 +03:00
oguzhankoral 6f2e36fd11 Add mixpanel tracking for mapper 2023-07-03 12:03:37 +03:00
Oğuzhan Koral 119d80ffc8 Fix (Mapper): Definition mapping 2023-06-29 22:42:36 +03:00
oguzhankoral 771c3df864 Fix definition mapping 2023-06-29 22:39:31 +03:00
Oğuzhan Koral 7d1963e458 Feat (Mapper): Revit and default wall support 2023-06-26 12:23:52 +03:00
oguzhankoral dde85972b3 Add default wall 2023-06-26 12:20:32 +03:00
oguzhankoral 5e061da910 Correct tooltips according to source state 2023-06-26 11:49:34 +03:00
oguzhankoral 46bea345de RevitWall from baseline 2023-06-26 11:33:09 +03:00
Oğuzhan Koral bc53462ad6 Chore (Mapper): Rename Floor with RevitFloor 2023-06-26 10:47:50 +03:00
oguzhankoral 884df40a1d Rename Floor with RevitFloor 2023-06-26 10:45:40 +03:00
Oğuzhan Koral b23168c067 Fix (Mapper): evaluate mapping source family data 2023-06-26 10:07:48 +03:00
oguzhankoral 5568212f15 Correct type names from source 2023-06-25 20:48:16 +03:00
oguzhankoral 79db79d799 Remove braces from mapper 2023-06-25 20:47:54 +03:00
oguzhankoral 18a4008efd Set selected level name string instead of object 2023-06-25 20:20:17 +03:00
oguzhankoral 83e4abd1ee Update family types when family updated 2023-06-23 18:37:47 +03:00
oguzhankoral 243bcfba72 Make dropdowns functional 2023-06-23 18:33:39 +03:00
Oğuzhan Koral af2c8c560f Feat (Mapper): mapper method by selection and floor creation 2023-06-22 17:27:49 +03:00
oguzhankoral 6a37f3871c Add new types as constants 2023-06-22 17:18:00 +03:00
oguzhankoral 759a388448 Register levels as speckle entity 2023-06-22 17:17:47 +03:00
oguzhankoral 360e89d7ce Separate default floor with native floor 2023-06-22 17:17:23 +03:00
oguzhankoral 64655a3284 Pass speckle state to speckle conversions to detect levels 2023-06-22 17:16:47 +03:00
oguzhankoral c58356bde8 Rename with_mapper_selection_queue with better one 2023-06-22 17:16:06 +03:00
oguzhankoral f74117632d Pass stream id to level object to create it as speckle entity 2023-06-22 17:15:23 +03:00
oguzhankoral 5f2b8b8e2b Parse family type level info from command to action 2023-06-22 17:15:00 +03:00
oguzhankoral f0ce7481fd Register clear mapper source action to commands 2023-06-22 17:14:38 +03:00
oguzhankoral 6e7a5c6140 Apply and clear mapping source by buttons instead of auto-apply 2023-06-22 17:13:52 +03:00
oguzhankoral 079c18ee19 Apply mappings also for family, type and level 2023-06-22 17:13:25 +03:00
oguzhankoral 26ef6a3815 Add clearing mapper source action 2023-06-22 17:12:18 +03:00
oguzhankoral cf6dcefe6c Separate mapper data collection from Sketchup selection 2023-06-22 17:11:41 +03:00
oguzhankoral 35590eb979 Add revit parameter object 2023-06-22 17:11:07 +03:00
oguzhankoral 583a8b8a76 Separate mapper related read functions 2023-06-20 08:18:08 +03:00
Oğuzhan Koral c6e8a664de Feat (Mapper): Fetch mapper source 2023-06-19 15:10:43 +03:00
oguzhankoral 24a5e0a579 Update levels geometrically when we have new commit 2023-06-19 12:57:48 +03:00
oguzhankoral a242c197fb Store mapper source in ruby state 2023-06-16 23:10:50 +03:00
oguzhankoral 08bdd23149 Add state for mapper 2023-06-16 21:57:13 +03:00
oguzhankoral c5b35b2d98 Notify user when selected source has new update 2023-06-16 16:23:27 +03:00
oguzhankoral 5407fecd1f Fetch source branch last commit and pass to ruby 2023-06-15 16:10:10 +03:00
oguzhankoral 12d2821d26 Add mapping source selection 2023-06-15 12:09:39 +03:00
Oğuzhan Koral 2e6d58e6a3 Feat (Mapper): mapping for default floor 2023-06-14 22:20:21 +03:00
oguzhankoral 0f0c0fd5ae Check material nil 2023-06-14 16:50:04 +03:00
oguzhankoral ef9ec1c223 Send floors with global coordinates as flat list 2023-06-14 01:11:37 +03:00
oguzhankoral f4387bae30 Send @SpeckleSchema as detached property 2023-06-13 11:50:35 +03:00
oguzhankoral 49b238a23a Extract mapped elements on selection 2023-06-13 11:49:24 +03:00
oguzhankoral 4b75c01b28 Convert sketchup faces to built elements' floor 2023-06-12 13:36:09 +03:00
Oğuzhan Koral e7f641046b Fix (Send): Do not send hidden geometries in components 2023-06-09 15:26:16 +03:00
oguzhankoral 9aaabe0fab Do not send hidden geometries in components 2023-06-09 15:24:40 +03:00
Oğuzhan Koral b6e4b711bf Feat (Edge): support rhino curve types 2023-06-09 11:19:57 +03:00
oguzhankoral 58fcfd210b Support arc and circle 2023-06-09 11:12:04 +03:00
oguzhankoral 5868b9c234 Support polycurves as displayValues 2023-06-09 09:55:52 +03:00
Oğuzhan Koral 0dc6d9cf9d Fix (DisplayValue): Use name of display value first 2023-06-08 23:42:05 +03:00
oguzhankoral 67f50cf2fd If displayValue has name use it 2023-06-08 23:39:54 +03:00
Oğuzhan Koral 8b26a4d49a Chore (DisplayValue): Better component names for displayValues 2023-06-08 23:15:07 +03:00
oguzhankoral a1d0bb0aa1 Instance name as speckle_type only 2023-06-08 23:08:25 +03:00
oguzhankoral bfe08560b1 Use speckle type instead def 2023-06-08 22:31:28 +03:00
70 changed files with 2921 additions and 283 deletions
+116 -2
View File
@@ -76,6 +76,99 @@ jobs:
paths:
- speckle-sharp-ci-tools/Installers
build-connector-mac:
macos:
xcode: 12.5.1
parameters:
projname:
type: string
default: ""
slug:
type: string
default: ""
installer:
type: boolean
default: false
converter-files:
type: string
default: ""
installername:
type: string
default: ""
build-config:
type: string
default: Release
bundlename:
type: string
default: ""
steps:
- checkout
- attach_workspace:
at: ./
- run:
name: Install dotnet
command: |
curl -sSL https://dot.net/v1/dotnet-install.sh | bash /dev/stdin --channel Current
$HOME/.dotnet/dotnet --version
$HOME/.dotnet/dotnet --list-runtimes
$HOME/.dotnet/dotnet --list-sdks
- run:
name: Create installer target dir
command: |
mkdir -p speckle-sharp-ci-tools/Installers/<< parameters.slug >>
- run:
name: Set Environment Variable
command: |
TAG=$(if [ "${CIRCLE_TAG}" ]; then echo $CIRCLE_TAG; else echo "2.0.999"; fi;)
SEMVER=$(echo "$TAG" | sed -e 's/\/[a-zA-Z-]*//')
VER=$(echo "$SEMVER" | sed -e 's/-.*//')
VERSION=$(echo $VER.$WORKFLOW_NUM)
python3 patch_version.py $SEMVER
environment:
WORKFLOW_NUM: << pipeline.number >>
- run:
name: Zip Connector files
command: |
zip -r << parameters.slug >>-mac.zip "./speckle_connector" "./speckle_connector.rb"
# Copy installer files
- run:
name: Copy files to installer
command: |
mkdir -p speckle-sharp-ci-tools/Mac/<< parameters.installername >>/.installationFiles/
cp << parameters.slug >>-mac.zip speckle-sharp-ci-tools/Mac/<<parameters.installername>>/.installationFiles
# Create installer
- run:
name: Exit if External PR
command: if [ "$CIRCLE_PR_REPONAME" ]; then circleci-agent step halt; fi
- run:
name: Build Mac installer
command: ~/.dotnet/dotnet publish speckle-sharp-ci-tools/Mac/<<parameters.installername>>/<<parameters.installername>>.sln -r osx-x64 -c Release
- run:
name: Zip installer
command: |
cd speckle-sharp-ci-tools/Mac/<<parameters.installername>>/bin/Release/net6.0/osx-x64/publish/
zip -r <<parameters.slug>>.zip ./
- store_artifacts:
path: speckle-sharp-ci-tools/Mac/<<parameters.installername>>/bin/Release/net6.0/osx-x64/publish/<<parameters.slug>>.zip
- run:
name: Copy to installer location
command: |
TAG=$(if [ "${CIRCLE_TAG}" ]; then echo $CIRCLE_TAG; else echo "2.0.999"; fi;)
SEMVER=$(echo "$TAG" | sed -e 's/\/[a-zA-Z-]*//')
VER=$(echo "$SEMVER" | sed -e 's/-.*//')
VERSION=$(echo $VER.$WORKFLOW_NUM)
cp speckle-sharp-ci-tools/Mac/<<parameters.installername>>/bin/Release/net6.0/osx-x64/publish/<<parameters.slug>>.zip speckle-sharp-ci-tools/Installers/<< parameters.slug >>/<<parameters.slug>>-$SEMVER.zip
environment:
WORKFLOW_NUM: << pipeline.number >>
- when:
condition: << pipeline.git.tag >>
steps:
- 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
@@ -99,6 +192,7 @@ jobs:
root: ./
paths:
- speckle-sharp-ci-tools
deploy-manager2:
docker:
- image: mcr.microsoft.com/dotnet/sdk:6.0
@@ -146,17 +240,37 @@ workflows:
only: /.*/
context: innosetup
- build-connector-mac:
slug: sketchup
requires:
- get-ci-tools
- build-ui
filters:
tags:
only: /.*/
installername: SpeckleSketchUpInstall
- 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\/.*/
- deploy-manager2:
context: do-spaces-speckle-releases
slug: sketchup
os: OSX
extension: zip
requires:
- build-connector-mac
filters:
tags:
only: /([0-9]+)\.([0-9]+)\.([0-9]+)(?:-\w+)?$/
branches:
ignore: /.*/ # For testing only! /ci\/.*/
@@ -9,12 +9,16 @@ module SpeckleConnector
module Actions
# Apply mappings for selected entities.
class ApplyMappings < Action
def initialize(entities_to_map, method, category, name, is_definition)
def initialize(entities_to_map, method, category, family,
family_type, level, name, is_definition)
super()
@entities_to_map = entities_to_map
@method = method
@category = category
@name = name
@family = family
@family_type = family_type
@level = level
@is_definition = is_definition
end
@@ -51,6 +55,9 @@ module SpeckleConnector
SketchupModel::Dictionary::SpeckleSchemaDictionaryHandler.set_attribute(entity, :category, @category)
SketchupModel::Dictionary::SpeckleSchemaDictionaryHandler.set_attribute(entity, :name, name)
SketchupModel::Dictionary::SpeckleSchemaDictionaryHandler.set_attribute(entity, :method, @method)
SketchupModel::Dictionary::SpeckleSchemaDictionaryHandler.set_attribute(entity, :family, @family)
SketchupModel::Dictionary::SpeckleSchemaDictionaryHandler.set_attribute(entity, :family_type, @family_type)
SketchupModel::Dictionary::SpeckleSchemaDictionaryHandler.set_attribute(entity, :level, @level)
speckle_state = speckle_state.with_mapped_entity(entity)
end
@@ -0,0 +1,31 @@
# frozen_string_literal: true
require_relative 'action'
require_relative '../sketchup_model/dictionary/speckle_entity_dictionary_handler'
module SpeckleConnector
module Actions
# Clear mapper source.
class ClearMapperSource < Action
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def self.update_state(state, _data)
new_speckle_state = state.speckle_state.with_removed_mapper_source
erase_levels(state)
state.with_speckle_state(new_speckle_state)
end
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
def self.erase_levels(state)
levels = state.sketchup_state.sketchup_model.definitions.select do |definition|
SketchupModel::Dictionary::SpeckleEntityDictionaryHandler.get_attribute(definition, :speckle_type) ==
OBJECTS_BUILTELEMENTS_REVIT_LEVEL
end
levels.each do |level|
level.entities.clear!
level.instances.each(&:erase!)
end
end
end
end
end
@@ -1,31 +1,28 @@
# frozen_string_literal: true
require_relative 'event_action'
require_relative '../../mapping/category/revit_category'
require_relative '../mapper_selection_changed'
require_relative '../../mapper/category/revit_category'
require_relative '../../sketchup_model/reader/speckle_entities_reader'
require_relative '../../sketchup_model/reader/mapper_reader'
require_relative '../../sketchup_model/query/entity'
module SpeckleConnector
module Actions
module Events
# Update selected speckle objects when the selection changes for mapping tool.
# Update selected speckle objects when the selection changes for mapper tool.
class SelectionEventAction < EventAction
# @param state [States::State] the current state of Speckle application.
# @return [States::State] the new updated state object
def self.update_state(state, event_data)
return state unless event_data&.any?
# Get sketchup selection
sketchup_selection = state.sketchup_state.sketchup_model.selection
selection = {
selection: SketchupModel::Reader::SpeckleEntitiesReader.entities_schema_details(sketchup_selection),
mappingMethods: [
'Direct Shape'
],
categories: Mapping::Category::RevitCategory.to_a
}
selection = { selection: [], mappingMethods: [], categories: [] } if sketchup_selection.none?
state.with_selection_queue(selection)
# Collect and return mapper selection info.
# Later we can add more selection info for different scopes.
MapperSelectionChanged.new(sketchup_selection).update_state(state)
end
end
end
@@ -3,6 +3,7 @@
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'
@@ -29,7 +30,7 @@ module SpeckleConnector
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))
# Read mapped entities
new_mapped_entities = SketchupModel::Reader::SpeckleEntitiesReader.read_mapped_entities(sketchup_model.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)
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require_relative 'action'
require_relative '../sketchup_model/reader/speckle_entities_reader'
require_relative '../sketchup_model/reader/mapper_reader'
module SpeckleConnector
module Actions
@@ -10,8 +10,8 @@ module SpeckleConnector
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def self.update_state(state, _data = nil)
mapped_entities = SketchupModel::Reader::SpeckleEntitiesReader
.mapped_entity_details(state.speckle_state.mapped_entities.values.to_a)
mapped_entities = SketchupModel::Reader::MapperReader
.mapped_entity_details(state.speckle_state.speckle_mapper_state.mapped_entities.values.to_a)
state.with_mapped_entities_queue(mapped_entities)
end
@@ -0,0 +1,22 @@
# frozen_string_literal: true
require_relative 'action'
require_relative '../mapper/category/revit_category'
require_relative '../mapper/category/revit_family_category'
module SpeckleConnector
module Actions
# Collects mapper selection info.
class MapperInitialized < Action
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def self.update_state(state, _data)
init_parameters = {
categories: Mapper::Category::RevitCategory.to_a,
familyCategories: Mapper::Category::RevitFamilyCategory.to_a
}.freeze
state.with_mapper_init_queue(init_parameters)
end
end
end
end
@@ -0,0 +1,200 @@
# frozen_string_literal: true
require_relative 'action'
require_relative '../mapper/category/revit_category'
require_relative '../mapper/category/revit_family_category'
require_relative '../sketchup_model/reader/mapper_reader'
require_relative '../sketchup_model/reader/speckle_entities_reader'
require_relative '../sketchup_model/dictionary/speckle_entity_dictionary_handler'
module SpeckleConnector
module Actions
# Collects mapper selection info.
class MapperSelectionChanged < Action
READER = SketchupModel::Reader
DICTIONARY = SketchupModel::Dictionary
def initialize(selection)
super()
@selection = selection
end
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def update_state(state)
# Get mapping info from selection.
mapping = get_mapping_info(state, @selection)
state.with_mapper_selection_queue(mapping)
end
def filter_out_levels(selection)
selection.reject do |e|
DICTIONARY::SpeckleEntityDictionaryHandler
.get_attribute(e, :speckle_type) == OBJECTS_BUILTELEMENTS_REVIT_LEVEL
end
end
def get_mapping_info(state, selection)
source_exist = !state.speckle_state.speckle_mapper_state.mapper_source.nil?
selection = filter_out_levels(selection)
grouped_by_type = group_by_type(selection)
supported_entity_count = grouped_by_type.length
# Return empty method list if there is no supported entity to map.
return EMPTY_SELECTION if supported_entity_count == 0
# Return Direct Shape itself if multiple kinds of element are selected like Edge and Face.
# OR single type is equal to only direct shape supports.
return multiple_supported_selection_info(selection) if supported_entity_count > 1
# FIXME: Distinguish selection info according to selection elegantly!!!
if grouped_by_type.keys.first == Sketchup::ComponentInstance
return component_selection_info(selection, source_exist)
end
return group_selection_info(selection) if grouped_by_type.keys.first == Sketchup::Group
if supported_entity_count > 1 ||
(supported_entity_count == 1 &&
MAPPER_DIRECT_SHAPE_SUPPORTED_ENTITY_TYPES.include?(grouped_by_type.keys.first))
if source_exist
return direct_shape_selection_info_with_source(selection, [])
else
return direct_shape_selection_info(selection, source_exist)
end
end
# Only single type selections remained after this point.
return face_selection_info(state, grouped_by_type.values.first) if grouped_by_type.keys.first == Sketchup::Face
return edge_selection_info(state, grouped_by_type.values.first) if grouped_by_type.keys.first == Sketchup::Edge
EMPTY_SELECTION
end
MAPPER_SUPPORTED_ENTITY_TYPES = [
Sketchup::ComponentInstance,
Sketchup::Group,
Sketchup::Face,
Sketchup::Edge
].freeze
MAPPER_DIRECT_SHAPE_SUPPORTED_ENTITY_TYPES = [
Sketchup::ComponentInstance,
Sketchup::Group
].freeze
EMPTY_SELECTION = {
selection: [],
mappingMethods: []
}.freeze
def multiple_supported_selection_info(selection)
{
selection: SketchupModel::Reader::MapperReader.entities_schema_details(selection),
mappingMethods: ['Direct Shape']
}.freeze
end
def component_selection_info(selection, source_exist)
if source_exist
{
selection: SketchupModel::Reader::MapperReader.entities_schema_details(selection),
mappingMethods: ['Direct Shape', 'New Revit Family', 'Family Instance']
}.freeze
else
{
selection: SketchupModel::Reader::MapperReader.entities_schema_details(selection),
mappingMethods: ['Direct Shape', 'New Revit Family']
}.freeze
end
end
def group_selection_info(selection)
{
selection: SketchupModel::Reader::MapperReader.entities_schema_details(selection),
mappingMethods: ['Direct Shape']
}.freeze
end
def direct_shape_selection_info(selection, source_exist)
methods = ['Direct Shape', 'New Revit Family']
methods.append('Family Instance') if source_exist
{
selection: SketchupModel::Reader::MapperReader.entities_schema_details(selection),
mappingMethods: methods
}.freeze
end
def direct_shape_selection_info_with_default(selection, methods)
{
selection: SketchupModel::Reader::MapperReader.entities_schema_details(selection),
mappingMethods: ['Direct Shape'] + methods
}.freeze
end
def direct_shape_selection_info_with_source(filtered_selection, methods)
instances = @selection.grep(Sketchup::ComponentInstance)
selected_level = instances.find do |i|
DICTIONARY::SpeckleEntityDictionaryHandler
.get_attribute(i, :speckle_type) == OBJECTS_BUILTELEMENTS_REVIT_LEVEL
end
selected_level_name = nil
if selected_level
selected_level_name = DICTIONARY::SpeckleEntityDictionaryHandler.get_attribute(selected_level, :name)
end
{
selection: READER::MapperReader.entities_schema_details(filtered_selection),
mappingMethods: ['Direct Shape'] + methods,
categories: Mapper::Category::RevitCategory.to_a,
selectedLevelName: selected_level_name
}.freeze
end
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
def face_selection_info(state, faces)
source_exist = !state.speckle_state.speckle_mapper_state.mapper_source.nil?
grouped_by_verticality = faces.group_by { |face| face.normal.perpendicular?(VECTOR_Z) }
return direct_shape_selection_info(faces, source_exist) if grouped_by_verticality.length == 2
if source_exist
if grouped_by_verticality.keys.first
direct_shape_selection_info_with_source(faces, ['Wall'])
else
direct_shape_selection_info_with_source(faces, ['Floor'])
end
else
if grouped_by_verticality.keys.first
direct_shape_selection_info_with_default(faces, ['Default Wall'])
else
direct_shape_selection_info_with_default(faces, ['Default Floor'])
end
end
end
def edge_selection_info(state, edges)
source_exist = !state.speckle_state.speckle_mapper_state.mapper_source.nil?
if source_exist
methods = ['Column', 'Beam', 'Pipe', 'Duct']
direct_shape_selection_info_with_source(edges, methods)
else
default_methods = ['Default Column', 'Default Beam', 'Default Pipe', 'Default Duct']
direct_shape_selection_info_with_default(edges, default_methods)
end
end
def group_by_type_old(selection)
selection.group_by(&:class).filter_map do |group|
[group.first, group] if MAPPER_SUPPORTED_ENTITY_TYPES.include?(group.first)
end.to_h
end
def group_by_type(selection)
selection.select { |s| MAPPER_SUPPORTED_ENTITY_TYPES.include?(s.class) }.group_by(&:class)
end
end
end
end
@@ -0,0 +1,77 @@
# frozen_string_literal: true
require_relative 'action'
require_relative '../constants/type_constants'
require_relative '../mapper/mapper_source'
require_relative '../speckle_objects/built_elements/revit/revit_element_type'
module SpeckleConnector
module Actions
# Action to update mapper source.
class MapperSourceUpdated < Action
def initialize(base, stream_id, commit_id)
super()
@base = base
@stream_id = stream_id
@commit_id = commit_id
end
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def update_state(state)
levels = convert_levels(state, @base['@Levels'])
types = convert_types(@base['@Types'])
family_instances = convert_family_instance_types(@base['@Types'])
mapper_source = Mapper::MapperSource.new(@stream_id, @commit_id, levels, types)
new_speckle_state = state.speckle_state.with_mapper_source(mapper_source)
state = state.with_speckle_state(new_speckle_state)
state.with_add_queue('mapperSourceUpdated', @stream_id, [
{ is_string: false, val: levels.to_json },
{ is_string: false, val: types.to_json },
{ is_string: false, val: family_instances.to_json }
])
end
def convert_types(types)
types.collect do |type, type_elements|
next if type_elements.nil? || !type_elements.is_a?(Array) || type == '__closure'
type = type[1..-1] if type[0] == '@'
elements = type_elements.map do |type_element|
SpeckleObjects::BuiltElements::Revit::RevitElementType.to_native(type_element)
end
elements = elements.group_by { |e| e[:family] }
[type, elements]
end.compact.to_h
end
def convert_family_instance_types(types)
family_instance_types = {}
types.each do |type, type_elements|
next if type_elements.nil? || !type_elements.is_a?(Array) || type == '__closure'
# skip type if there is no any revit symbol element type
symbol_element_types = type_elements.select do |t|
t['speckle_type'] == OBJECTS_BUILTELEMENTS_REVIT_REVITSYMBOLELEMENTTYPE &&
t['placementType'] == 'OneLevelBased'
end
next if symbol_element_types.empty?
elements = type_elements.map do |type_element|
SpeckleObjects::BuiltElements::Revit::RevitElementType.to_native(type_element)
end
elements = elements.group_by { |e| e[:family] }
family_instance_types.merge!(elements)
end
family_instance_types
end
def convert_levels(state, levels)
levels.collect do |level|
SpeckleObjects::BuiltElements::Level.to_native(state, level, @stream_id)
end
end
end
end
end
@@ -3,6 +3,7 @@
require_relative 'action'
require_relative '../convertors/units'
require_relative '../convertors/to_native'
require_relative '../convertors/clean_up'
module SpeckleConnector
module Actions
@@ -26,7 +27,12 @@ module SpeckleConnector
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}. ===="
@@ -11,9 +11,13 @@ module SpeckleConnector
entities_to_map = data['entitiesToMap']
method = data['method']
category = data['category']
family = data['family']
family_type = data['familyType']
level = data['level']
name = data['name']
is_definition = data['isDefinition']
action = Actions::ApplyMappings.new(entities_to_map, method, category, name, is_definition)
action = Actions::ApplyMappings.new(entities_to_map, method, category, family,
family_type, level, name, is_definition)
app.update_state!(action)
end
end
@@ -0,0 +1,19 @@
# frozen_string_literal: true
require_relative 'command'
require_relative '../actions/mapper_source_updated'
module SpeckleConnector
module Commands
# Command to update mapper source.
class MapperSourceUpdated < Command
def _run(data)
base = data['base']
stream_id = data['stream_id']
commit_id = data['commit_id']
action = Actions::MapperSourceUpdated.new(base, stream_id, commit_id)
app.update_state!(action)
end
end
end
end
@@ -0,0 +1,5 @@
# frozen_string_literal: true
module SpeckleConnector
VECTOR_Z = Geom::Vector3d.new(0, 0, 1)
end
@@ -14,7 +14,7 @@ module SpeckleConnector
path = ENV.fetch('APPDATA')
Pathname.new(File.join(path, 'Speckle')).cleanpath.to_s
when OS_MAC
File.join(Dir.home, 'Library/Application Support/Speckle')
File.join(Dir.home, '.config/Speckle')
else
raise 'Speckle could not determine your Appdata path'
end
@@ -4,14 +4,26 @@ module SpeckleConnector
BASE_OBJECT = 'Base'
OBJECTS_GIS_POLYGONELEMENT = 'Objects.GIS.PolygonElement'
OBJECTS_GIS_LINEELEMENT = 'Objects.GIS.LineElement'
OBJECTS_BUILTELEMENTS_VIEW3D = 'Objects.BuiltElements.View:Objects.BuiltElements.View3D'
OBJECTS_BUILTELEMENTS_NETWORK = 'Objects.BuiltElements.Network'
OBJECTS_BUILTELEMENTS_REVIT_DIRECTSHAPE = 'Objects.BuiltElements.Revit.DirectShape'
OBJECTS_BUILTELEMENTS_REVIT_LEVEL = 'Objects.BuiltElements.Level:Objects.BuiltElements.Revit.RevitLevel'
OBJECTS_BUILTELEMENTS_DEFAULT_FLOOR = 'Objects.BuiltElements.Floor'
OBJECTS_BUILTELEMENTS_REVIT_FLOOR = 'Objects.BuiltElements.Floor:Objects.BuiltElements.Revit.RevitFloor'
OBJECTS_BUILTELEMENTS_DEFAULT_WALL = 'Objects.BuiltElements.Wall'
OBJECTS_BUILTELEMENTS_REVIT_WALL = 'Objects.BuiltElements.Wall:Objects.BuiltElements.Revit.RevitWall'
OBJECTS_BUILTELEMENTS_REVIT_DIRECTSHAPE = 'Objects.BuiltElements.Revit.DirectShape'
OBJECTS_BUILTELEMENTS_REVIT_FAMILY_INSTANCE = 'Objects.BuiltElements.Revit.FamilyInstance'
OBJECTS_BUILTELEMENTS_REVIT_PARAMETER = 'Objects.BuiltElements.Revit.Parameter'
OBJECTS_BUILTELEMENTS_REVIT_REVITELEMENTTYPE = 'Objects.BuiltElements.Revit.RevitElementType'
OBJECTS_BUILTELEMENTS_REVIT_REVITSYMBOLELEMENTTYPE = 'Objects.BuiltElements.Revit.RevitElementType:Objects.BuiltElements.Revit.RevitSymbolElementType'
OBJECTS_GEOMETRY_LINE = 'Objects.Geometry.Line'
OBJECTS_GEOMETRY_POLYLINE = 'Objects.Geometry.Polyline'
OBJECTS_GEOMETRY_POLYCURVE = 'Objects.Geometry.Polycurve'
OBJECTS_GEOMETRY_ARC = 'Objects.Geometry.Arc'
OBJECTS_GEOMETRY_CIRCLE = 'Objects.Geometry.Circle'
OBJECTS_GEOMETRY_MESH = 'Objects.Geometry.Mesh'
OBJECTS_GEOMETRY_BREP = 'Objects.Geometry.Brep'
@@ -24,4 +36,6 @@ module SpeckleConnector
OBJECTS_OTHER_DISPLAYSTYLE = 'Objects.Other.DisplayStyle'
SPECKLE_CORE_MODELS_COLLECTION = 'Speckle.Core.Models.Collection'
SPECKLE_CORE_MODELS_COLLECTION_RASTER_LAYER = 'Speckle.Core.Models.Collection:Objects.GIS.RasterLayer'
SPECKLE_CORE_MODELS_COLLECTION_VECTOR_LAYER = 'Speckle.Core.Models.Collection:Objects.GIS.VectorLayer'
end
+33 -25
View File
@@ -11,9 +11,10 @@ module SpeckleConnector
# @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_faces(entities)
def self.merge_coplanar_entities(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) }
@@ -22,6 +23,22 @@ module SpeckleConnector
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
# edges.reject(&:deleted?).select { |edge| edge.faces.empty? }.each(&:erase!)
merged_faces(faces)
end
def self.merged_faces(faces)
faces.reject(&:deleted?)
end
@@ -35,43 +52,34 @@ module SpeckleConnector
# - 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.
# rubocop:disable Metrics/AbcSize
def self.remove_edge_have_coplanar_faces(edge, faces, ignore_materials)
def self.remove_edge_have_coplanar_faces(edge)
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
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)
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)
# Check materials match.
unless ignore_materials
return false unless (face_1.material == face_2.material) && (face_1.back_material == face_2.back_material)
# 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)
# Check faces are coplanar or not.
return false unless faces_coplanar?(face_1, face_2)
edge.erase!
true
edge.erase!
true
rescue StandardError => e
puts "Failed to merge coplanar faces by removing edge with error: #{e}"
false
end
end
# rubocop:enable Metrics/AbcSize
# Determines if two faces are overlapped.
def self.face_duplicate?(face_1, face_2, overlapping: false)
+31 -1
View File
@@ -4,6 +4,7 @@ require_relative 'converter'
require_relative '../constants/type_constants'
require_relative '../speckle_entities/speckle_entity'
require_relative '../speckle_objects/gis/polygon_element'
require_relative '../speckle_objects/gis/line_element'
require_relative '../speckle_objects/other/transform'
require_relative '../speckle_objects/other/render_material'
require_relative '../speckle_objects/other/block_definition'
@@ -12,10 +13,14 @@ require_relative '../speckle_objects/other/display_value'
require_relative '../speckle_objects/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'
module SpeckleConnector
@@ -29,11 +34,14 @@ module SpeckleConnector
# @return [String] source application of received object that will be converted to native
attr_reader :source_app
attr_reader :converted_faces
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 = []
end
# Module aliases
@@ -46,20 +54,29 @@ module SpeckleConnector
# 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
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 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
@@ -68,9 +85,13 @@ module SpeckleConnector
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
@@ -274,6 +295,9 @@ module SpeckleConnector
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),
@@ -282,10 +306,14 @@ module SpeckleConnector
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),
SPECKLE_CORE_MODELS_COLLECTION => COLLECTION.method(:to_native)
OBJECTS_GIS_LINEELEMENT => LINE_ELEMENT.method(:to_native),
SPECKLE_CORE_MODELS_COLLECTION => COLLECTION.method(:to_native),
SPECKLE_CORE_MODELS_COLLECTION_RASTER_LAYER => GIS_LAYER_COLLECTION.method(:to_native),
SPECKLE_CORE_MODELS_COLLECTION_VECTOR_LAYER => GIS_LAYER_COLLECTION.method(:to_native)
}.freeze
# @param state [States::State] state of the speckle application
@@ -297,6 +325,8 @@ module SpeckleConnector
# Call 'to_native' method by passing this method itself to handle nested 'to_native' conversions.
# It returns updated state and converted entities.
state, converted_entities = to_native_method.call(state, obj, layer, entities, &convert_to_native)
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)
@@ -15,6 +15,7 @@ require_relative '../speckle_objects/relations/layers'
require_relative '../speckle_objects/speckle/core/models/model_collection'
require_relative '../constants/path_constants'
require_relative '../sketchup_model/reader/speckle_entities_reader'
require_relative '../sketchup_model/reader/mapper_reader'
require_relative '../sketchup_model/query/entity'
module SpeckleConnector
@@ -63,19 +64,14 @@ module SpeckleConnector
def convert(entity, preferences, speckle_state, parent = :base)
convert = method(:convert)
unless SketchupModel::Reader::SpeckleEntitiesReader.mapped_with_schema?(entity)
unless SketchupModel::Reader::MapperReader.mapped_with_schema?(entity) &&
!entity.is_a?(Sketchup::ComponentDefinition)
return from_native_to_speckle(entity, preferences, speckle_state, parent, &convert)
end
return speckle_state, nil
end
def from_mapped_to_speckle(entity, path, preferences)
direct_shape = SpeckleObjects::BuiltElements::Revit::DirectShape
.from_entity(entity, path, @units, preferences)
return [direct_shape, [entity]]
end
# rubocop:disable Metrics/MethodLength
def from_native_to_speckle(entity, preferences, speckle_state, parent, &convert)
if entity.is_a?(Sketchup::Edge)
@@ -84,7 +80,7 @@ module SpeckleConnector
end
if entity.is_a?(Sketchup::Face)
mesh = SpeckleObjects::Geometry::Mesh.from_face(face: entity, units: @units,
mesh = SpeckleObjects::Geometry::Mesh.from_face(speckle_state: speckle_state, face: entity, units: @units,
model_preferences: preferences[:model])
return speckle_state, [mesh, [entity]]
end
Binary file not shown.
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module SpeckleConnector
module Mapping
module Mapper
module Category
# Revit categories.
class RevitCategory < Hash
@@ -118,7 +118,8 @@ module SpeckleConnector
VibrationManagement: 106,
Walls: 107,
StructConnectionWelds: 108,
Windows: 109
Windows: 109,
Railings: 110
}.freeze
end
# rubocop:enable Metrics/MethodLength
@@ -0,0 +1,79 @@
# frozen_string_literal: true
module SpeckleConnector
module Mapper
module Category
# Revit categories for families.
class RevitFamilyCategory < Hash
class << self
# rubocop:disable Metrics/MethodLength
def dictionary
{
AudioVisualDevices: 9,
CableTrayFitting: 16,
Casework: 19,
Columns: 21,
CommunicationDevices: 22,
ConduitFitting: 23,
DataDevices: 30,
Doors: 32,
DuctAccessory: 33,
ElectricalEquipment: 38,
ElectricalFixtures: 39,
Entourage: 40,
FireAlarmDevices: 42,
FireProtection: 43,
FoodServiceEquipment: 45,
Furniture: 46,
FurnitureSystems: 47,
GenericAnnotation: 48,
GenericModel: 49,
Hardscape: 51,
LightingDevices: 52,
LightingFixtures: 53,
Lines: 54,
Mass: 55,
MechanicalEquipment: 56,
MedicalEquipment: 57,
NurseCallDevices: 58,
Parking: 59,
PipeAccessory: 68,
PipeFitting: 69,
Planting: 74,
PlumbingFixtures: 76,
Roads: 80,
SecurityDevices: 82,
Signage: 84,
Site: 85,
SpecialityEquipment: 86,
Sprinklers: 87,
StructuralFramingSystem: 89,
StructuralColumns: 90,
StructConnections: 91,
StructuralFoundation: 93,
StructuralFraming: 94,
StructuralStiffener: 97,
TemporaryStructure: 100,
VerticalCirculation: 103,
Windows: 109,
Railings: 110
}.freeze
end
# rubocop:enable Metrics/MethodLength
def reverse_dictionary
dictionary.collect { |k, v| [v, k] }.to_h
end
def to_a
dictionary.collect { |k, v| { key: k, value: v } }.to_a
end
def reverse_to_a
dictionary.collect { |k, v| { key: v, value: k } }.to_a
end
end
end
end
end
end
+62
View File
@@ -0,0 +1,62 @@
# frozen_string_literal: true
require_relative '../speckle_objects/built_elements/revit/revit_floor'
require_relative '../speckle_objects/built_elements/revit/revit_wall'
require_relative '../speckle_objects/built_elements/default_floor'
require_relative '../speckle_objects/built_elements/default_wall'
require_relative '../speckle_objects/other/mapped_block_wrapper'
require_relative '../sketchup_model/query/entity'
require_relative '../sketchup_model/reader/mapper_reader'
require_relative '../sketchup_model/dictionary/speckle_schema_dictionary_handler'
module SpeckleConnector
# Mapper is a tool to convert SketchUp entities to other applications' native objects.
module Mapper
# Collects mapped entities on selection as flat list.
def self.mapped_entities_on_selection(sketchup_model)
flat_selection_with_path = SketchupModel::Query::Entity.flat_entities_with_path(
sketchup_model.selection,
[Sketchup::Face, Sketchup::ComponentInstance, Sketchup::Group], [sketchup_model]
)
mapped_selection = []
flat_selection_with_path.each do |entities|
entity = entities[0]
is_entity_mapped = SketchupModel::Reader::MapperReader.mapped_with_schema?(entity)
if entity.respond_to?(:definition)
is_definition_mapped = SketchupModel::Reader::MapperReader.mapped_with_schema?(entity.definition)
mapped_selection.append(entities) if is_entity_mapped || is_definition_mapped
next
end
mapped_selection.append(entities) if is_entity_mapped
end
mapped_selection
end
def self.to_speckle(speckle_state, entity, units, model_preferences, global_transformation: nil, path: nil)
speckle_schema = SketchupModel::Dictionary::SpeckleSchemaDictionaryHandler.speckle_schema_to_speckle(entity)
return speckle_schema if speckle_schema.nil?
if speckle_schema['method'] == 'Default Floor'
return SpeckleObjects::BuiltElements::DefaultFloor
.to_speckle_schema(entity, units, global_transformation: global_transformation)
end
if speckle_schema['method'] == 'Floor'
return SpeckleObjects::BuiltElements::RevitFloor
.to_speckle_schema(speckle_state, entity, units, global_transformation: global_transformation)
end
if speckle_schema['method'] == 'Default Wall'
return SpeckleObjects::BuiltElements::DefaultWall
.to_speckle_schema(entity, units, global_transformation: global_transformation)
end
if speckle_schema['method'] == 'Wall'
return SpeckleObjects::BuiltElements::RevitWall
.to_speckle_schema(speckle_state, entity, units, global_transformation: global_transformation)
end
return speckle_schema
end
end
end
@@ -0,0 +1,34 @@
# frozen_string_literal: true
require_relative '../immutable/immutable'
require_relative '../speckle_objects/built_elements/level'
require_relative '../speckle_objects/built_elements/revit/revit_element_type'
module SpeckleConnector
# Mapper is a tool to convert SketchUp entities to other applications' native objects.
module Mapper
# Mapper source object that collects information about stream id and commit id to identify source in the branch,
# also contains levels and family types to be able to map objects with them.
class MapperSource
# @return [String] stream id of the mapper source.
attr_reader :stream_id
# @return [String] commit id of the mapper source.
attr_reader :commit_id
# @return [Array<SpeckleObjects::BuiltElements::Level>] levels in the source branch.
attr_reader :levels
# @return [ImmutableHash{String=>Array<SpeckleObjects::BuiltElements::Revit::RevitElementType>}] revit element
# types.
attr_reader :types
def initialize(stream_id, commit_id, levels, types)
@stream_id = stream_id
@commit_id = commit_id
@levels = levels
@types = types
end
end
end
end
@@ -12,9 +12,9 @@ module SpeckleConnector
include Immutable::ImmutableUtils
DICT_HANDLER = SketchupModel::Dictionary::SpeckleModelDictionaryHandler
# rubocop:disable Layout/LineLength
DEFAULT_CONFIG = "('configSketchup', '{\"dark_theme\":false, \"diffing\":false, \"register_speckle_entity\":false}');"
DEFAULT_CONFIG = "('configSketchup', '{\"dark_theme\":false, \"diffing\":false, \"register_speckle_entity\":false, \"fe2\":false}');"
# rubocop:enable Layout/LineLength
DEFAULT_PREFERENCES = '{"dark_theme":false, "diffing":false, "register_speckle_entity": false}'
DEFAULT_PREFERENCES = '{"dark_theme":false, "diffing":false, "register_speckle_entity": false, "fe2": false}'
# @param sketchup_model [Sketchup::Model] active model.
def self.read_preferences(sketchup_model)
@@ -34,10 +34,16 @@ module SpeckleConnector
def self.data_complete?(row_data)
return false if row_data.empty?
data = JSON.parse(row_data.first.first)
return false if data['dark_theme'].nil? || data['diffing'].nil? || data['register_speckle_entity'].nil?
begin
data = JSON.parse(row_data.first.first)
if data['dark_theme'].nil? || data['fe2'].nil? || data['diffing'].nil? || data['register_speckle_entity'].nil?
return false
end
true
true
rescue StandardError
false
end
end
# Validates current preferences. If there are incomplete data then this method resets it with default preferences.
@@ -65,11 +71,13 @@ module SpeckleConnector
data_hash = JSON.parse(row_data).to_h
# Get current theme value
dark_theme = data_hash['dark_theme']
fe2 = data_hash['fe2']
diffing = data_hash['diffing']
register_speckle_entity = data_hash['register_speckle_entity']
{
dark_theme: dark_theme,
fe2: fe2,
diffing: diffing,
register_speckle_entity: register_speckle_entity
}.freeze
@@ -0,0 +1,95 @@
# frozen_string_literal: true
require_relative '../dictionary/speckle_schema_dictionary_handler'
require_relative '../../speckle_entities/speckle_entity'
require_relative '../../mapper/category/revit_category'
require_relative '../../constants/dict_constants'
module SpeckleConnector
# Operations related to {SketchupModel}.
module SketchupModel
# Reader model for sketchup model.
module Reader
# Reader module for mapper.
module MapperReader
# @param entities [Sketchup::Entities] entities to collect mapped entities.
# @return [Hash{String=>Sketchup::Entity}] mapped entities with persistent id.
def self.read_mapped_entities(entities)
mapped_entities = {}
Query::Entity.flat_entities(entities).each do |entity|
mapped_entities[entity.persistent_id] = entity if mapped_with_schema?(entity)
end
mapped_entities
end
# @param entity [Sketchup::Entity] sketchup entity to check whether mapped with speckle schema or not.
def self.mapped_with_schema?(entity)
is_entity_mapped = !Dictionary::SpeckleSchemaDictionaryHandler.attribute_dictionary(entity).nil?
return is_entity_mapped if is_entity_mapped
return is_entity_mapped unless entity.is_a?(Sketchup::ComponentInstance)
!Dictionary::SpeckleSchemaDictionaryHandler.attribute_dictionary(entity.definition).nil?
end
def self.get_schema(entity)
Dictionary::SpeckleSchemaDictionaryHandler.speckle_schema_to_speckle(entity)
end
def self.entities_schema_details(entities)
entities.collect do |entity|
entity_selection_details = entity_selection_details(entity)
if entity.is_a?(Sketchup::ComponentInstance)
entity_selection_details = entity_selection_details.merge(
{ definition: entity_selection_details(entity.definition) }
)
end
entity_selection_details
end
end
def self.entity_selection_details(entity)
sanitized_type = entity.class.name.split('::').last.gsub(/(?<=[a-z])(?=[A-Z])/, ' ').split
is_definition = entity.is_a?(Sketchup::ComponentDefinition)
entity_type = is_definition ? sanitized_type.last : sanitized_type.first
speckle_schema = get_schema(entity)
{
name: speckle_schema['name'],
entityName: entity.respond_to?(:name) ? entity.name : '',
entityId: entity.persistent_id,
entityType: entity_type,
schema: speckle_schema,
numberOfInstances: is_definition ? entity.instances.length : 1
}
end
def self.mapped_entity_details(entities)
reverse_category_dictionary = Mapper::Category::RevitCategory.reverse_dictionary
entities.collect do |entity|
speckle_schema = get_schema(entity)
speckle_schema_definition = entity.respond_to?(:definition) ? get_schema(entity.definition) : nil
entity_type = entity.class.name.split('::').last.gsub(/(?<=[a-z])(?=[A-Z])/, ' ').split.first
category = get_map_attribute(speckle_schema, speckle_schema_definition, 'category')
{
name: get_map_attribute(speckle_schema, speckle_schema_definition, 'name'),
category: category,
categoryName: category.nil? ? '' : reverse_category_dictionary[category],
method: get_map_attribute(speckle_schema, speckle_schema_definition, 'method'),
entityName: entity.respond_to?(:name) ? entity.name : '',
entityId: entity.persistent_id,
entityType: entity.is_a?(Sketchup::ComponentDefinition) ? 'Definition' : entity_type,
schema: speckle_schema,
definitionSchema: speckle_schema_definition
}
end
end
def self.get_map_attribute(schema, definition_schema, attribute)
return schema[attribute] if schema[attribute]
return definition_schema[attribute] if !definition_schema.nil? && definition_schema[attribute]
nil
end
end
end
end
end
@@ -2,7 +2,7 @@
require_relative '../dictionary/speckle_schema_dictionary_handler'
require_relative '../../speckle_entities/speckle_entity'
require_relative '../../mapping/category/revit_category'
require_relative '../../mapper/category/revit_category'
require_relative '../../constants/dict_constants'
module SpeckleConnector
@@ -29,16 +29,6 @@ module SpeckleConnector
speckle_entities
end
# @param entities [Sketchup::Entities] entities to collect mapped entities.
# @return [Hash{String=>Sketchup::Entity}] mapped entities with persistent id.
def self.read_mapped_entities(entities)
mapped_entities = {}
Query::Entity.flat_entities(entities).each do |entity|
mapped_entities[entity.persistent_id] = entity if mapped_with_schema?(entity)
end
mapped_entities
end
# @param entity [Sketchup::Entity] sketchup entity to read from attribute dictionary.
def self.read_speckle_entity(entity)
dict = entity.attribute_dictionaries.to_a.find { |d| d.name == SPECKLE_BASE_OBJECT }
@@ -60,70 +50,7 @@ module SpeckleConnector
entity.attribute_dictionaries.to_a.any? { |dict| dict.name == SPECKLE_BASE_OBJECT }
end
# @param entity [Sketchup::Entity] sketchup entity to check whether mapped with speckle schema or not.
def self.mapped_with_schema?(entity)
is_entity_mapped = !Dictionary::SpeckleSchemaDictionaryHandler.attribute_dictionary(entity).nil?
return is_entity_mapped if is_entity_mapped
return is_entity_mapped unless entity.is_a?(Sketchup::ComponentInstance)
!Dictionary::SpeckleSchemaDictionaryHandler.attribute_dictionary(entity.definition).nil?
end
def self.get_schema(entity)
Dictionary::SpeckleSchemaDictionaryHandler.speckle_schema_to_speckle(entity)
end
def self.entities_schema_details(entities)
entities.collect do |entity|
entity_selection_details = entity_selection_details(entity)
if entity.is_a?(Sketchup::ComponentInstance)
entity_selection_details = entity_selection_details.merge(
{ definition: entity_selection_details(entity.definition) }
)
end
entity_selection_details
end
end
def self.entity_selection_details(entity)
sanitized_type = entity.class.name.split('::').last.gsub(/(?<=[a-z])(?=[A-Z])/, ' ').split
is_definition = entity.is_a?(Sketchup::ComponentDefinition)
entity_type = is_definition ? sanitized_type.last : sanitized_type.first
speckle_schema = get_schema(entity)
{
name: speckle_schema['name'],
entityName: entity.respond_to?(:name) ? entity.name : '',
entityId: entity.persistent_id,
entityType: entity_type,
schema: speckle_schema,
numberOfInstances: is_definition ? entity.instances.length : 1
}
end
# rubocop:disable Metrics/CyclomaticComplexity
# rubocop:disable Metrics/PerceivedComplexity
def self.mapped_entity_details(entities)
reverse_category_dictionary = Mapping::Category::RevitCategory.reverse_dictionary
entities.collect do |entity|
speckle_schema = get_schema(entity)
speckle_schema_definition = entity.respond_to?(:definition) ? get_schema(entity.definition) : nil
entity_type = entity.class.name.split('::').last.gsub(/(?<=[a-z])(?=[A-Z])/, ' ').split.first
{
name: speckle_schema['name'] || speckle_schema_definition['name'],
category: speckle_schema['category'] || speckle_schema_definition['category'],
categoryName: reverse_category_dictionary[speckle_schema['category']] ||
reverse_category_dictionary[speckle_schema_definition['category']],
method: speckle_schema['method'] || speckle_schema_definition['method'],
entityName: entity.respond_to?(:name) ? entity.name : '',
entityId: entity.persistent_id,
entityType: entity.is_a?(Sketchup::ComponentDefinition) ? 'Definition' : entity_type,
schema: speckle_schema,
definitionSchema: speckle_schema_definition
}
end
end
# rubocop:enable Metrics/CyclomaticComplexity
# rubocop:enable Metrics/PerceivedComplexity
end
end
end
@@ -1,5 +1,7 @@
# frozen_string_literal: true
require_relative '../../constants/geo_constants'
module SpeckleConnector
# Operations related to {SketchupModel}.
module SketchupModel
@@ -24,6 +26,13 @@ module SpeckleConnector
end
adj_faces.uniq
end
# @param face [Sketchup::Face] face to get max z distance for all vertices.
def self.max_z(face)
points = face.vertices.collect(&:position)
points_z_values = points.collect(&:z)
points_z_values.max - points_z_values.min
end
end
end
end
@@ -136,6 +136,15 @@ module SpeckleConnector
next if (entity.is_a?(Sketchup::Face) || entity.is_a?(Sketchup::Edge)) &&
!state.user_state.user_preferences[:register_speckle_entity]
if entity.is_a?(Sketchup::ComponentDefinition)
definition = speckle_object['definition'] || speckle_object['@block_definition'] ||
speckle_object['block_definition']
if definition
speckle_id = definition['id']
speckle_type = definition['speckle_type']
end
end
ent = 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)
@@ -0,0 +1,53 @@
# frozen_string_literal: true
require_relative '../base'
require_relative '../built_elements/revit/parameter'
require_relative '../other/render_material'
require_relative '../geometry/line'
require_relative '../geometry/polyline'
require_relative '../../constants/type_constants'
require_relative '../../sketchup_model/dictionary/speckle_schema_dictionary_handler'
module SpeckleConnector
module SpeckleObjects
module BuiltElements
# Default Floor object.
class DefaultFloor < Base
SPECKLE_TYPE = OBJECTS_BUILTELEMENTS_DEFAULT_FLOOR
def initialize(outline:, voids:, units:, material:, application_id: nil)
super(
speckle_type: SPECKLE_TYPE,
total_children_count: 0,
application_id: application_id,
id: nil
)
self[:outline] = outline
self[:voids] = voids
self[:units] = units
self[:renderMaterial] = material
end
# @param face [Sketchup::Face] face to get speckle schema for floor.
def self.to_speckle_schema(face, units, global_transformation: nil)
outline = Geometry::Polyline.from_loop(face.loops.first, units, global_transformation: global_transformation)
voids = []
if face.loops.length > 1
voids = face.loops[1..face.loops.length - 1].collect do |loop|
Geometry::Polyline.from_loop(loop, units, global_transformation: global_transformation)
end
end
material = face.material || face.back_material
DefaultFloor.new(
outline: outline,
voids: voids,
units: units,
material: material.nil? ? nil : Other::RenderMaterial.from_material(face.material || face.back_material),
application_id: face.persistent_id
)
end
end
end
end
end
@@ -0,0 +1,52 @@
# frozen_string_literal: true
require_relative '../base'
require_relative '../built_elements/revit/parameter'
require_relative '../other/render_material'
require_relative '../geometry/length'
require_relative '../geometry/line'
require_relative '../geometry/polyline'
require_relative '../../constants/type_constants'
require_relative '../../sketchup_model/dictionary/speckle_schema_dictionary_handler'
require_relative '../../sketchup_model/utils/face_utils'
module SpeckleConnector
module SpeckleObjects
module BuiltElements
# Default Wall object.
class DefaultWall < Base
SPECKLE_TYPE = OBJECTS_BUILTELEMENTS_DEFAULT_WALL
def initialize(base_line:, height:, flipped:, units:, material:, application_id: nil)
super(
speckle_type: SPECKLE_TYPE,
total_children_count: 0,
application_id: application_id,
id: nil
)
self[:baseLine] = base_line
self[:height] = height
self[:flipped] = flipped
self[:units] = units
self[:renderMaterial] = material
end
# @param face [Sketchup::Face] face to get speckle schema for floor.
def self.to_speckle_schema(face, units, global_transformation: nil)
base_line = Geometry::Line.base_line_from_face(face, units, global_transformation: global_transformation)
material = face.material || face.back_material
DefaultWall.new(
base_line: base_line,
height: Geometry.length_to_speckle(SketchupModel::Utils::FaceUtils.max_z(face), units),
flipped: false,
units: units,
material: material.nil? ? nil : Other::RenderMaterial.from_material(face.material || face.back_material),
application_id: face.persistent_id
)
end
end
end
end
end
@@ -0,0 +1,84 @@
# frozen_string_literal: true
require_relative '../base'
require_relative '../other/render_material'
require_relative '../geometry/line'
require_relative '../geometry/length'
require_relative '../geometry/polyline'
require_relative '../../constants/type_constants'
require_relative '../../sketchup_model/dictionary/speckle_entity_dictionary_handler'
module SpeckleConnector
module SpeckleObjects
module BuiltElements
# Level object.
class Level < Base
SPECKLE_TYPE = OBJECTS_BUILTELEMENTS_REVIT_LEVEL
def initialize(name:, elevation:, units:, element_id:, application_id: nil, id: nil)
super(
speckle_type: SPECKLE_TYPE,
total_children_count: 0,
application_id: application_id,
id: id
)
self[:name] = name
self[:elevation] = elevation
self[:units] = units
self[:elementId] = element_id
self[:referenceOnly] = true
self[:createView] = false
end
# @param state [States::State] state of the application.
def self.to_native(state, speckle_level, stream_id)
sketchup_model = state.sketchup_state.sketchup_model
levels_layer = sketchup_model.layers.layers.find { |layer| layer.display_name == 'Levels' }
levels_layer = sketchup_model.layers.add('Levels') if levels_layer.nil?
name = speckle_level['name']
elevation = speckle_level['elevation']
units = speckle_level['units']
element_id = speckle_level['elementId']
application_id = speckle_level['applicationId']
id = speckle_level['id']
skp_elevation = Geometry.length_to_native(elevation, units)
definition_name = "#{name}-#{application_id}"
definition = sketchup_model.definitions.find { |definition| definition.name == definition_name }
definition.entities.clear! unless definition.nil?
definition = sketchup_model.definitions.add(definition_name) if definition.nil?
instance = sketchup_model.entities.add_instance(definition, Geom::Transformation.new)
instance.locked = true
SketchupModel::Dictionary::SpeckleEntityDictionaryHandler.write_initial_base_data(
instance, application_id, id, SPECKLE_TYPE, [], stream_id
)
SketchupModel::Dictionary::SpeckleEntityDictionaryHandler.set_attribute(instance, :name, name)
SketchupModel::Dictionary::SpeckleEntityDictionaryHandler.write_initial_base_data(
definition, application_id, id, SPECKLE_TYPE, [], stream_id
)
SketchupModel::Dictionary::SpeckleEntityDictionaryHandler.set_attribute(definition, :name, name)
c1_e = Geom::Point3d.new(0, 10.m, skp_elevation)
c2_e = Geom::Point3d.new(0, 0, skp_elevation)
c3_e = Geom::Point3d.new(10.m, 0, skp_elevation)
cline_1 = definition.entities.add_cline(c1_e, c2_e)
cline_2 = definition.entities.add_cline(c2_e, c3_e)
text = definition.entities.add_text(" #{name}", c1_e)
[cline_1, cline_2, text, definition, instance].each { |o| o.layer = levels_layer }
Level.new(
name: name,
elevation: elevation,
units: units,
element_id: element_id,
application_id: application_id,
id: speckle_level['id']
)
end
end
end
end
end
@@ -7,7 +7,7 @@ require_relative '../../other/block_definition'
require_relative '../../other/transform'
require_relative '../../../constants/type_constants'
require_relative '../../../sketchup_model/query/entity'
require_relative '../../../sketchup_model/reader/speckle_entities_reader'
require_relative '../../../sketchup_model/reader/mapper_reader'
require_relative '../../../sketchup_model/dictionary/speckle_schema_dictionary_handler'
module SpeckleConnector
@@ -78,9 +78,9 @@ module SpeckleConnector
mapped_selection = []
flat_selection_with_path.each do |entities|
entity = entities[0]
is_entity_mapped = READER::SpeckleEntitiesReader.mapped_with_schema?(entity)
is_entity_mapped = READER::MapperReader.mapped_with_schema?(entity)
if entity.respond_to?(:definition)
is_definition_mapped = READER::SpeckleEntitiesReader.mapped_with_schema?(entity.definition)
is_definition_mapped = READER::MapperReader.mapped_with_schema?(entity.definition)
mapped_selection.append(entities) if is_entity_mapped || is_definition_mapped
next
end
@@ -89,7 +89,7 @@ module SpeckleConnector
mapped_selection
end
def self.from_entity(entity, path, units, preferences)
def self.from_entity(speckle_state, entity, path, units, model_preferences)
schema = DICTIONARY::SpeckleSchemaDictionaryHandler.attribute_dictionary(entity)
if schema.nil? && entity.respond_to?(:definition)
schema = DICTIONARY::SpeckleSchemaDictionaryHandler.attribute_dictionary(entity.definition)
@@ -103,7 +103,7 @@ module SpeckleConnector
entity.definition.entities, [Sketchup::Face], path.append(entity)
)
end
base_geometries = group_faces_under_mesh_by_material(entities_with_path, units, preferences)
base_geometries = group_faces_under_mesh_by_material(speckle_state, entities_with_path, units, model_preferences)
DirectShape.new(
name: schema[:name], category: schema[:category], units: units,
base_geometries: base_geometries, application_id: entity.persistent_id
@@ -111,13 +111,13 @@ module SpeckleConnector
end
# rubocop:disable Metrics/MethodLength
def self.group_faces_under_mesh_by_material(faces_with_path, units, preferences)
def self.group_faces_under_mesh_by_material(speckle_state, faces_with_path, units, model_preferences)
mesh_groups = {}
faces_with_path.each do |face_with_path|
face = face_with_path[0]
entity_path = face_with_path[1..-1]
parent_material = QUERY::Entity.parent_material(entity_path)
mesh_group_id = Geometry::Mesh.get_mesh_group_id(face, preferences[:model], parent_material)
mesh_group_id = Geometry::Mesh.get_mesh_group_id(face, model_preferences, parent_material)
if mesh_groups.key?(mesh_group_id)
mesh_group = mesh_groups[mesh_group_id]
@@ -125,7 +125,8 @@ module SpeckleConnector
mesh_group[1].append(face)
else
mesh = Geometry::Mesh.from_face(
face: face, units: units, model_preferences: preferences[:model],
speckle_state: speckle_state,
face: face, units: units, model_preferences: model_preferences,
global_transform: QUERY::Entity.global_transformation(face, entity_path),
parent_material: parent_material
)
@@ -0,0 +1,37 @@
# frozen_string_literal: true
require_relative '../../base'
require_relative '../../../constants/type_constants'
module SpeckleConnector
module SpeckleObjects
module BuiltElements
module Revit
# Family instance for Revit mappings.
class FamilyInstance < Base
SPECKLE_TYPE = OBJECTS_BUILTELEMENTS_REVIT_FAMILY_INSTANCE
READER = SketchupModel::Reader
QUERY = SketchupModel::Query
DICTIONARY = SketchupModel::Dictionary
# rubocop:disable Metrics/ParameterLists
def initialize(family:, type:, level:, units:, base_point:, rotation:, application_id: nil)
super(
speckle_type: SPECKLE_TYPE,
total_children_count: 0,
application_id: application_id,
id: nil
)
self[:family] = family
self[:type] = type
self[:level] = level
self[:units] = units
self[:basePoint] = base_point
self[:rotation] = rotation
end
# rubocop:enable Metrics/ParameterLists
end
end
end
end
end
@@ -0,0 +1,26 @@
# frozen_string_literal: true
require_relative '../../base'
require_relative '../../../constants/type_constants'
module SpeckleConnector
module SpeckleObjects
module BuiltElements
module Revit
# Revit parameter.
class Parameter < Base
SPECKLE_TYPE = OBJECTS_BUILTELEMENTS_REVIT_PARAMETER
def initialize(name:, application_id: nil)
super(
speckle_type: SPECKLE_TYPE,
total_children_count: 0,
application_id: application_id,
id: id
)
self[:name] = name
end
end
end
end
end
end
@@ -0,0 +1,42 @@
# frozen_string_literal: true
require_relative '../../base'
module SpeckleConnector
module SpeckleObjects
module BuiltElements
module Revit
# Revit element type.
class RevitElementType < Base
SPECKLE_TYPE = OBJECTS_BUILTELEMENTS_REVIT_REVITELEMENTTYPE
# rubocop:disable Metrics/ParameterLists
def initialize(category:, family:, type:, element_id:, application_id: nil, id: nil)
super(
speckle_type: SPECKLE_TYPE,
total_children_count: 0,
application_id: application_id,
id: id
)
self[:category] = category
self[:family] = family
self[:type] = type
self[:elementId] = element_id
end
# rubocop:enable Metrics/ParameterLists
def self.to_native(revit_element_type)
RevitElementType.new(
category: revit_element_type['category'],
family: revit_element_type['family'],
type: revit_element_type['type'],
element_id: revit_element_type['elementId'],
application_id: revit_element_type['applicationId'],
id: revit_element_type['id']
)
end
end
end
end
end
end
@@ -0,0 +1,77 @@
# frozen_string_literal: true
require_relative '../../base'
require_relative '../../built_elements/revit/parameter'
require_relative '../../other/render_material'
require_relative '../../geometry/line'
require_relative '../../geometry/polyline'
require_relative '../../../constants/type_constants'
require_relative '../../../sketchup_model/dictionary/speckle_schema_dictionary_handler'
module SpeckleConnector
module SpeckleObjects
module BuiltElements
# Revit floor object.
class RevitFloor < Base
SPECKLE_TYPE = OBJECTS_BUILTELEMENTS_REVIT_FLOOR
# rubocop:disable Metrics/ParameterLists
def initialize(family:, type:, outline:, voids:, level:, units:, material:, parameters:nil, application_id: nil)
super(
speckle_type: SPECKLE_TYPE,
total_children_count: 0,
application_id: application_id,
id: nil
)
self[:family] = family
self[:type] = type
self[:level] = level
self[:outline] = outline
self[:voids] = voids
self[:units] = units
self[:parameters] = parameters
self[:renderMaterial] = material
end
# rubocop:enable Metrics/ParameterLists
# @param face [Sketchup::Face] face to get speckle schema for floor.
def self.to_speckle_schema(speckle_state, face, units, global_transformation: nil)
outline = Geometry::Polyline.from_loop(face.loops.first, units, global_transformation: global_transformation)
voids = []
if face.loops.length > 1
voids = face.loops[1..face.loops.length - 1].collect do |loop|
Geometry::Polyline.from_loop(loop, units, global_transformation: global_transformation)
end
end
material = face.material || face.back_material
schema = SketchupModel::Dictionary::SpeckleSchemaDictionaryHandler.speckle_schema_to_speckle(face).to_h
source_exist = !speckle_state.speckle_mapper_state.mapper_source.nil?
level = nil
parameters = nil
if source_exist
level = speckle_state.speckle_mapper_state.mapper_source.levels.find { |l| l[:name] == schema['level'] }
parameters = Base.new
offset_parameter = BuiltElements::Revit::Parameter.new(name: 'Height Offset From Level')
level_z = Geometry.length_to_native(level[:elevation], level[:units])
min_z = face.vertices.collect(&:position).map(&:z).min
offset_parameter['value'] = Geometry.length_to_speckle(min_z - level_z, units)
offset_parameter['units'] = units
parameters['Height Offset From Level'] = offset_parameter
end
RevitFloor.new(
family: schema['family'],
type: schema['family_type'],
outline: outline,
voids: voids,
level: level,
units: units,
parameters: parameters,
material: material.nil? ? nil : Other::RenderMaterial.from_material(face.material || face.back_material),
application_id: face.persistent_id
)
end
end
end
end
end
@@ -0,0 +1,102 @@
# frozen_string_literal: true
require_relative '../../base'
require_relative '../../built_elements/revit/parameter'
require_relative '../../other/render_material'
require_relative '../../geometry/line'
require_relative '../../geometry/length'
require_relative '../../../constants/type_constants'
require_relative '../../../sketchup_model/dictionary/speckle_schema_dictionary_handler'
require_relative '../../../sketchup_model/utils/face_utils'
module SpeckleConnector
module SpeckleObjects
module BuiltElements
# Revit wall object.
class RevitWall < Base
SPECKLE_TYPE = OBJECTS_BUILTELEMENTS_REVIT_WALL
# rubocop:disable Metrics/ParameterLists
def initialize(family:, type:, base_line:, height:, flipped:, level:, units:, material:, parameters: nil, application_id: nil)
super(
speckle_type: SPECKLE_TYPE,
total_children_count: 0,
application_id: application_id,
id: nil
)
self[:family] = family
self[:type] = type
self[:height] = height
self[:flipped] = flipped
self[:level] = level
self[:baseLine] = base_line
self[:units] = units
self[:parameters] = parameters
self[:renderMaterial] = material
end
# rubocop:enable Metrics/ParameterLists
def self.to_native(state, wall, layer, entities, &convert_to_native)
obj = Other::DisplayValue.collect_definition_geometries(wall)
obj['name'] = Other::DisplayValue.get_definition_name(obj)
state, _definitions = Other::BlockDefinition.to_native(
state, obj, layer, entities, &convert_to_native
)
definition = state.sketchup_state.sketchup_model.definitions[Other::BlockDefinition.get_definition_name(obj)]
Other::BlockInstance.find_and_erase_existing_instance(definition, obj['id'], obj['applicationId'])
t_arr = obj['transform']
transform = t_arr.nil? ? Geom::Transformation.new : Other::Transform.to_native(t_arr, obj['units'])
instance = entities.add_instance(definition, transform)
instance.name = Other::DisplayValue.get_instance_name(obj['name']) unless obj['name'].nil?
instance.layer = layer unless layer.nil?
# Align instance axes that created from display value. (without any transform)
# BlockInstance.align_instance_axes(instance)
return state, [instance, definition]
end
# @param face [Sketchup::Face] face to get speckle schema for wall.
def self.to_speckle_schema(speckle_state, face, units, global_transformation: nil)
base_line = Geometry::Line.base_line_from_face(face, units, global_transformation: global_transformation)
material = face.material || face.back_material
schema = SketchupModel::Dictionary::SpeckleSchemaDictionaryHandler.speckle_schema_to_speckle(face).to_h
source_exist = !speckle_state.speckle_mapper_state.mapper_source.nil?
level = nil
parameters = nil
if source_exist
level = speckle_state.speckle_mapper_state.mapper_source.levels.find { |l| l[:name] == schema['level'] }
parameters = Base.new
offset_parameter = BuiltElements::Revit::Parameter.new(name: 'Height Offset From Level')
level_z = Geometry.length_to_native(level[:elevation], level[:units])
min_z = face.vertices.collect(&:position).map(&:z).min
offset_parameter['value'] = Geometry.length_to_speckle(min_z - level_z, units)
offset_parameter['units'] = units
parameters['Height Offset From Level'] = offset_parameter
end
RevitWall.new(
family: schema['family'],
type: schema['family_type'],
base_line: base_line,
height: Geometry.length_to_speckle(SketchupModel::Utils::FaceUtils.max_z(face), units),
flipped: false,
level: level,
units: units,
parameters: parameters,
material: material.nil? ? nil : Other::RenderMaterial.from_material(face.material || face.back_material),
application_id: face.persistent_id
)
end
def self.get_wall_height(face, units)
points = face.vertices.collect(&:position)
points_z_values = points.collect(&:z)
Geometry.length_to_speckle(points_z_values.max - points_z_values.min, units)
end
end
end
end
end
@@ -0,0 +1,31 @@
# frozen_string_literal: true
require_relative '../base'
require_relative '../../constants/type_constants'
module SpeckleConnector
module SpeckleObjects
module Geometry
# Arc object definition for Speckle.
class Arc < Base
SPECKLE_TYPE = OBJECTS_GEOMETRY_ARC
# @param [States::State] state of the current application.
# @param arc [Object] object represents Speckle Arc.
# @param layer [Sketchup::Layer] layer to add {Sketchup::Edge} into it.
# @param entities [Sketchup::Entities] entities collection to add {Sketchup::Edge} into it.
def self.to_native(state, arc, layer, entities, &_convert_to_native)
plane = arc['plane']
units = arc['units']
origin = Point.to_native(plane['origin']['x'], plane['origin']['y'], plane['origin']['z'], units)
normal = Vector.to_native(plane['normal']['x'], plane['normal']['y'], plane['normal']['z'], units)
x_axis = Vector.to_native(plane['xdir']['x'], plane['xdir']['y'], plane['xdir']['z'], units)
radius = Geometry.length_to_native(arc['radius'], units)
edges = entities.add_arc(origin, x_axis, normal, radius, arc['startAngle'], arc['endAngle'])
edges.each { |edge| edge.layer = layer }
return state, edges
end
end
end
end
end
@@ -0,0 +1,33 @@
# frozen_string_literal: true
require_relative 'point'
require_relative 'vector'
require_relative 'length'
require_relative '../base'
require_relative '../../constants/type_constants'
module SpeckleConnector
module SpeckleObjects
module Geometry
# Circle object definition for Speckle.
class Circle < Base
SPECKLE_TYPE = OBJECTS_GEOMETRY_CIRCLE
# @param [States::State] state of the current application.
# @param circle [Object] object represents Speckle Circle.
# @param layer [Sketchup::Layer] layer to add {Sketchup::Edge} into it.
# @param entities [Sketchup::Entities] entities collection to add {Sketchup::Edge} into it.
def self.to_native(state, circle, layer, entities, &_convert_to_native)
plane = circle['plane']
units = circle['units']
origin = Point.to_native(plane['origin']['x'], plane['origin']['y'], plane['origin']['z'], units)
normal = Vector.to_native(plane['normal']['x'], plane['normal']['y'], plane['normal']['z'], units)
radius = Geometry.length_to_native(circle['radius'], units)
edges = entities.add_circle(origin, normal, radius)
edges.each { |edge| edge.layer = layer }
return state, edges
end
end
end
end
end
@@ -11,6 +11,10 @@ module SpeckleConnector
end
def self.length_to_native(length, units)
if units == 'none'
units = SpeckleConnector::Converters::
SKETCHUP_UNITS[Sketchup.active_model.options['UnitsOptions']['LengthUnit']]
end
length.__send__(SpeckleConnector::Converters::SKETCHUP_UNIT_STRINGS[units])
end
end
@@ -40,7 +40,7 @@ module SpeckleConnector
# rubocop:enable Metrics/ParameterLists
# @param edge [Sketchup::Edge] edge to convert line.
def self.from_edge(edge, units, model_preferences)
def self.from_edge(edge, units, model_preferences, global_transformation: nil)
dictionaries = SketchupModel::Dictionary::BaseDictionaryHandler
.attribute_dictionaries_to_speckle(edge, model_preferences)
att = dictionaries.any? ? { dictionaries: dictionaries } : {}
@@ -60,7 +60,42 @@ module SpeckleConnector
)
end
# @param _state [States::State] state of the application.
# @param edge [Sketchup::Face] face to get base line from face.
def self.base_line_from_face(face, units, global_transformation: nil)
points = face.vertices.collect(&:position)
points_z_values = points.collect(&:z)
height = Geometry.length_to_speckle(points_z_values.max - points_z_values.min, units)
min_z = points_z_values.min
projected_points = points.map { |p| Geom::Point3d.new(p.x, p.y, min_z) }
distance_with_points = Struct.new(:distance, :point_1, :point_2)
lines_with_distances = []
projected_points.each do |p|
projected_points.each do |p_other|
next if p_other == p
lines_with_distances.append(distance_with_points.new(p.distance(p_other), p, p_other))
end
end
lines_with_distances.sort_by!(&:distance).reverse!
p_1 = lines_with_distances.first.point_1
p_2 = lines_with_distances.first.point_2
unless global_transformation.nil?
p_1 = p_1.transform!(global_transformation)
p_2 = p_2.transform!(global_transformation)
end
Line.new(
start_pt: Geometry::Point.from_vertex(p_1, units),
end_pt: Geometry::Point.from_vertex(p_2, units),
domain: Primitive::Interval.from_numeric(0, Geometry.length_to_speckle(p_1.distance(p_2), units), units),
units: units,
layer: SketchupModel::Query::Layer.entity_path(face),
sketchup_attributes: {},
speckle_schema: {},
application_id: face.persistent_id.to_s
)
end
# @param state [States::State] state of the application.
# @param line [Object] object represents Speckle line.
# @param layer [Sketchup::Layer] layer to add {Sketchup::Edge} into it.
# @param entities [Sketchup::Entities] entities collection to add {Sketchup::Edge} into it.
@@ -3,6 +3,7 @@
require_relative '../base'
require_relative '../geometry/bounding_box'
require_relative '../other/render_material'
require_relative '../../mapper/mapper'
require_relative '../../sketchup_model/query/entity'
require_relative '../../convertors/clean_up'
require_relative '../../sketchup_model/dictionary/base_dictionary_handler'
@@ -51,7 +52,7 @@ module SpeckleConnector
self[:'@(31250)vertices'] = vertices
self[:'@(62500)faces'] = faces
self[:sketchup_attributes] = sketchup_attributes if sketchup_attributes.any?
self[:SpeckleSchema] = speckle_schema if speckle_schema.any?
self['@SpeckleSchema'] = speckle_schema if speckle_schema.any?
end
# rubocop:enable Metrics/ParameterLists
@@ -120,9 +121,9 @@ module SpeckleConnector
mesh_layer_name = SketchupModel::Query::Layer.entity_layer_from_path(mesh['layer'])
mesh_layer = state.sketchup_state.sketchup_model.layers.to_a.find { |l| l.display_name == mesh_layer_name }
# Merge only added faces in this scope
if model_preferences[:merge_coplanar_faces]
added_faces = Converters::CleanUp.merge_coplanar_faces(added_faces)
end
# if model_preferences[:merge_coplanar_faces]
# added_faces = Converters::CleanUp.merge_coplanar_faces(added_faces)
# end
added_faces.each do |face|
face.layer = mesh_layer unless mesh_layer.nil?
# Smooth edges if they already soft
@@ -147,13 +148,13 @@ module SpeckleConnector
# @param global_transform [Geom::Transformation, nil] global transformation value of face if it is not included
# into any component.
# rubocop:disable Style/MultilineTernaryOperator
def self.from_face(face:, units:, model_preferences:, global_transform: nil, parent_material: nil)
def self.from_face(speckle_state:, face:, units:, model_preferences:, global_transform: nil, parent_material: nil)
dictionaries = SketchupModel::Dictionary::BaseDictionaryHandler
.attribute_dictionaries_to_speckle(face, model_preferences)
has_any_soften_edge = face.edges.any?(&:soft?)
att = dictionaries.any? ? { is_soften: has_any_soften_edge, dictionaries: dictionaries }
: { is_soften: has_any_soften_edge }
speckle_schema = SketchupModel::Dictionary::SpeckleSchemaDictionaryHandler.speckle_schema_to_speckle(face)
speckle_schema = Mapper.to_speckle(speckle_state, face, units, model_preferences, global_transformation: global_transform)
material = face.material || face.back_material || parent_material
speckle_mesh = Mesh.new(
units: units,
@@ -0,0 +1,21 @@
# frozen_string_literal: true
require_relative '../base'
require_relative '../../constants/type_constants'
module SpeckleConnector
module SpeckleObjects
module Geometry
# Polycurve object definition for Speckle.
# It basically groups the lines-curves under it's `segments` property.
class Polycurve < Base
SPECKLE_TYPE = OBJECTS_GEOMETRY_POLYCURVE
def self.to_native(state, polycurve, layer, entities, &convert_to_native)
polycurve['displayValue'] = polycurve['segments']
convert_to_native.call(state, polycurve, layer, entities)
end
end
end
end
end
@@ -0,0 +1,65 @@
# frozen_string_literal: true
require_relative 'length'
require_relative 'point'
require_relative 'bounding_box'
require_relative '../base'
require_relative '../primitive/interval'
module SpeckleConnector
module SpeckleObjects
module Geometry
# Polyline object definition for Speckle.
class Polyline < Base
SPECKLE_TYPE = OBJECTS_GEOMETRY_POLYLINE
# @param value [Array<Numeric>] polygon vertex coordinates as flat list.
# @param domain [Primitive::Interval] domain of the polyline.
# @param length [Numeric] length of the polyline.
# @param closed [Boolean] whether polyline is closed or not.
# @param units [String] units of the polyline.
# @param application_id [String] application id of the polyline which corresponds to persistent_id of the Loop.
# rubocop:disable Metrics/ParameterLists
def initialize(value:, domain:, length:, closed:, units:, application_id: nil)
super(
speckle_type: SPECKLE_TYPE,
total_children_count: 0,
application_id: application_id,
id: nil
)
self[:value] = value
self[:domain] = domain
self[:length] = length
self[:closed] = closed
self[:units] = units
end
# rubocop:enable Metrics/ParameterLists
# @param loop [Sketchup::Loop] loop to convert closed speckle polyline.
def self.from_loop(loop, units, global_transformation: nil)
points = loop.vertices.collect do |vertex|
position = vertex.position
position = vertex.position.transform!(global_transformation) unless global_transformation.nil?
position
end
values = points.collect do |p|
[Geometry.length_to_speckle(p.x, units),
Geometry.length_to_speckle(p.y, units),
Geometry.length_to_speckle(p.z, units)]
end.flatten
loop_length = loop.edges.sum(&:length)
length = Geometry.length_to_speckle(loop_length, units)
domain = Primitive::Interval.from_lengths(0, loop_length, units)
Polyline.new(
value: values,
domain: domain,
length: length,
units: units,
closed: true,
application_id: loop.persistent_id.to_s
)
end
end
end
end
end
@@ -0,0 +1,207 @@
# frozen_string_literal: true
module SpeckleConnector
module SpeckleObjects
module Geometry
module Units
MILLIMETERS = 'mm'
CENTIMETERS = 'cm'
METERS = 'm'
KILOMETERS = 'km'
INCHES = 'in'
FEET = 'ft'
YARDS = 'yd'
MILES = 'mi'
NONE = 'none'
# USInches = "us_in" the smelliest ones, can add later if people scream "USA #1"
USFEET = 'us_ft' # it happened, absolutely gross
SUPPORTED_UNITS = [MILLIMETERS, CENTIMETERS, METERS, KILOMETERS,
INCHES, FEET, USFEET, YARDS, MILES, NONE].freeze
CONVERSION_TABLE = {
MILLIMETERS => {
CENTIMETERS => 0.1,
METERS => 0.001,
KILOMETERS => 1e-6,
INCHES => 0.0393701,
FEET => 0.00328084,
USFEET => 0.0032808333,
YARDS => 0.00109361,
MILES => 6.21371e-7
},
CENTIMETERS => {
MILLIMETERS => 10,
METERS => 0.01,
KILOMETERS => 1e-5,
INCHES => 0.393701,
FEET => 0.0328084,
USFEET => 0.0328083333,
YARDS => 0.0109361,
MILES => 6.21371e-6
},
METERS => {
MILLIMETERS => 1000,
CENTIMETERS => 100,
KILOMETERS => 0.001,
INCHES => 39.3701,
FEET => 3.28084,
USFEET => 3.28083333,
YARDS => 1.09361,
MILES => 0.000621371
},
KILOMETERS => {
MILLIMETERS => 1e6,
CENTIMETERS => 100000,
METERS => 1000,
INCHES => 39370.1,
FEET => 3280.84,
USFEET => 3280.83333,
YARDS => 1093.61,
MILES => 0.621371
},
INCHES => {
MILLIMETERS => 25.4,
CENTIMETERS => 2.54,
METERS => 0.0254,
KILOMETERS => 2.54e-5,
FEET => 0.0833333,
USFEET => 0.0833331667,
YARDS => 0.027777694,
MILES => 1.57828e-5
},
FEET => {
MILLIMETERS => 304.8,
CENTIMETERS => 30.48,
METERS => 0.3048,
KILOMETERS => 0.0003048,
INCHES => 12,
USFEET => 0.999998,
YARDS => 0.333332328,
MILES => 0.000189394
},
USFEET => {
MILLIMETERS => 120000.0 / 3937.0,
CENTIMETERS => 12000.0 / 3937.0,
METERS => 1200.0 / 3937.0,
KILOMETERS => 1.2 / 3937.0,
INCHES => 12.000024,
FEET => 1.000002,
YARDS => 1.000002 / 3.0,
MILES => 1.000002 / 5280.0
},
YARDS => {
MILLIMETERS => 914.4,
CENTIMETERS => 91.44,
METERS => 0.9144,
KILOMETERS => 0.0009144,
INCHES => 36,
FEET => 3,
USFEET => 2.999994,
MILES => 1.0 / 1760.0
},
MILES => {
MILLIMETERS => 1.609e6,
CENTIMETERS => 160934,
METERS => 1609.34,
KILOMETERS => 1.60934,
INCHES => 63360,
FEET => 5280,
USFEET => 5279.98944002112,
YARDS => 1759.99469184
},
NONE => { NONE => 1 }
}.freeze
def self.unit_supported?(unit)
SUPPORTED_UNITS.include?(unit)
end
# USYards = "us_yd" the smelliest ones, can add later if people scream "USA #1"
# USMiles = "us_mi" the smelliest ones, can add later if people scream "USA #1"
def self.get_conversion_factor(from, to)
from = get_units_from_string(from)
to = get_units_from_string(to)
CONVERSION_TABLE[from][to] || 1
end
def self.get_units_from_string(unit)
return nil if unit.nil?
case unit.downcase
when 'mm', 'mil', 'millimeter', 'millimeters', 'millimetres'
MILLIMETERS
when 'cm', 'centimetre', 'centimeter', 'centimetres', 'centimeters'
CENTIMETERS
when 'm', 'meter', 'metre', 'meters', 'metres'
METERS
when 'inches', 'inch', 'in'
INCHES
when 'feet', 'foot', 'ft'
FEET
when 'ussurveyfeet'
USFEET
when 'yard', 'yards', 'yd'
YARDS
when 'miles', 'mile', 'mi'
MILES
when 'kilometers', 'kilometer', 'km'
KILOMETERS
when 'none'
NONE
else
raise "Cannot understand what unit #{unit} is."
end
end
def self.get_encoding_from_unit(unit)
case unit
when MILLIMETERS
1
when CENTIMETERS
2
when METERS
3
when KILOMETERS
4
when INCHES
5
when FEET
6
when YARDS
7
when MILES
8
else
0
end
end
def self.get_unit_from_encoding(unit)
case unit
when 1
MILLIMETERS
when 2
CENTIMETERS
when 3
METERS
when 4
KILOMETERS
when 5
INCHES
when 6
FEET
when 7
YARDS
when 8
MILES
else
NONE
end
end
end
end
end
end
@@ -1,5 +1,6 @@
# frozen_string_literal: true
require_relative 'length'
require_relative '../base'
module SpeckleConnector
@@ -25,6 +26,14 @@ module SpeckleConnector
self[:z] = z
self[:units] = units
end
def self.to_native(x, y, z, units)
Geom::Vector3d.new(
Geometry.length_to_native(x, units),
Geometry.length_to_native(y, units),
Geometry.length_to_native(z, units)
)
end
end
end
end
@@ -0,0 +1,68 @@
# frozen_string_literal: true
require_relative 'utils'
require_relative '../base'
require_relative '../other/transform'
require_relative '../other/block_definition'
require_relative '../other/block_instance'
require_relative '../../constants/type_constants'
require_relative '../../sketchup_model/dictionary/dictionary_handler'
module SpeckleConnector
module SpeckleObjects
module GIS
# Line element in GIS tools.
class LineElement < Base
SPECKLE_TYPE = OBJECTS_GIS_LINEELEMENT
# Handles polygon element differently from display value.
def self.to_native(state, obj, layer, entities, &convert_to_native)
attributes = GIS.get_qgis_attributes(obj)
obj = collect_definition_geometries(obj)
obj['name'] = GIS.get_definition_name(obj, attributes)
state, _definitions = Other::BlockDefinition.to_native(
state, obj, layer, entities, &convert_to_native
)
definition = state.sketchup_state.sketchup_model
.definitions[Other::BlockDefinition.get_definition_name(obj)]
Other::BlockInstance.find_and_erase_existing_instance(definition, obj['id'], obj['applicationId'])
t_arr = obj['transform']
transform = t_arr.nil? ? Geom::Transformation.new : Other::Transform.to_native(t_arr, obj['units'])
instance = entities.add_instance(definition, transform)
instance.name = obj['name'] unless obj['name'].nil?
SketchupModel::Dictionary::DictionaryHandler.set_hash(instance, attributes, 'qgis')
SketchupModel::Dictionary::DictionaryHandler.set_hash(definition, attributes, 'qgis')
# Align instance axes that created from display value. (without any transform)
Other::BlockInstance.align_instance_axes(instance)
return state, [instance, definition]
end
def self.collect_definition_geometries(obj)
geometries = []
# FIXME: This type check needed because of QGIS. It can send geometries both way, object or array..
# This is something need to be fixed by QGIS.
if obj['geometry'].is_a?(Array)
obj['geometry'].each do |geometry|
geometries << geometry
end
else
geometries += obj['geometry']
end
geometries.each do |geo|
if geo['speckle_type'] && geo['speckle_type'] == OBJECTS_GEOMETRY_MESH
geo['sketchup_attributes'] = { 'is_soften' => false }
end
end
obj['geometry'] = geometries
obj
end
end
end
end
end
@@ -1,5 +1,6 @@
# frozen_string_literal: true
require_relative 'utils'
require_relative '../base'
require_relative '../other/transform'
require_relative '../other/block_definition'
@@ -14,26 +15,11 @@ module SpeckleConnector
class PolygonElement < Base
SPECKLE_TYPE = OBJECTS_GIS_POLYGONELEMENT
def self.get_definition_name(obj, attributes)
return obj['name'] unless obj['name'].nil?
return attributes['name'] unless attributes['name'].nil?
return "def::#{obj['id']}"
end
def self.get_qgis_attributes(obj)
attributes = obj['attributes'].to_h
speckle_properties = %w[id speckle_type totalChildrenCount units applicationId]
speckle_properties.each { |key| attributes.delete(key) }
attributes
end
# Handles polygon element differently from display value.
def self.to_native(state, obj, layer, entities, &convert_to_native)
attributes = get_qgis_attributes(obj)
attributes = GIS.get_qgis_attributes(obj)
obj = collect_definition_geometries(obj)
obj['name'] = get_definition_name(obj, attributes)
obj['name'] = GIS.get_definition_name(obj, attributes)
state, _definitions = Other::BlockDefinition.to_native(
state, obj, layer, entities, &convert_to_native
@@ -0,0 +1,29 @@
# frozen_string_literal: true
require_relative '../base'
require_relative '../other/transform'
require_relative '../other/block_definition'
require_relative '../other/block_instance'
require_relative '../../constants/type_constants'
require_relative '../../sketchup_model/dictionary/dictionary_handler'
module SpeckleConnector
module SpeckleObjects
module GIS
def self.get_definition_name(obj, attributes)
return obj['name'] unless obj['name'].nil?
return attributes['name'] unless attributes['name'].nil?
return "def::#{obj['id']}"
end
def self.get_qgis_attributes(obj)
attributes = obj['attributes'].to_h
speckle_properties = %w[id speckle_type totalChildrenCount units applicationId]
speckle_properties.each { |key| attributes.delete(key) }
attributes
end
end
end
end
@@ -159,6 +159,7 @@ module SpeckleConnector
# rubocop:disable Metrics/CyclomaticComplexity
# rubocop:disable Metrics/PerceivedComplexity
def self.group_entities_to_speckle(entities, preferences, speckle_state, parent, &convert)
entities = entities.reject(&:hidden?)
orphan_edges = entities.grep(Sketchup::Edge).filter { |edge| edge.faces.none? }
lines = orphan_edges.collect do |orphan_edge|
new_speckle_state, converted = convert.call(orphan_edge, preferences, speckle_state, parent)
@@ -5,6 +5,8 @@ require_relative 'transform'
require_relative 'block_definition'
require_relative '../base'
require_relative '../geometry/bounding_box'
require_relative '../other/mapped_block_wrapper'
require_relative '../built_elements/revit/family_instance'
require_relative '../../sketchup_model/dictionary/base_dictionary_handler'
require_relative '../../sketchup_model/dictionary/speckle_schema_dictionary_handler'
require_relative '../../sketchup_model/query/layer'
@@ -40,7 +42,7 @@ module SpeckleConnector
self[:renderMaterial] = render_material
self[:transform] = transform
self[:sketchup_attributes] = sketchup_attributes if sketchup_attributes.any?
self[:SpeckleSchema] = speckle_schema if speckle_schema.any?
self[:speckle_schema] = speckle_schema if speckle_schema.any?
# FIXME: Since blockDefinition sends with @ as detached, block basePlane renders on viewer.
self['@@definition'] = block_definition
end
@@ -72,7 +74,7 @@ module SpeckleConnector
# @param component_instance [Sketchup::ComponentInstance] component instance to convert Speckle BlockInstance
# rubocop:disable Metrics/MethodLength
def self.from_component_instance(component_instance, units, preferences, speckle_state, &convert)
def self.from_component_instance(component_instance, units, preferences, speckle_state, path: nil, &convert)
new_speckle_state, block_definition = convert.call(
component_instance.definition,
preferences,
@@ -82,11 +84,20 @@ module SpeckleConnector
speckle_state = new_speckle_state
dictionaries = SketchupModel::Dictionary::BaseDictionaryHandler
.attribute_dictionaries_to_speckle(component_instance, preferences[:model])
.attribute_dictionaries_to_speckle(component_instance, preferences)
att = dictionaries.any? ? { dictionaries: dictionaries } : {}
speckle_schema = SketchupModel::Dictionary::SpeckleSchemaDictionaryHandler
.speckle_schema_to_speckle(component_instance)
if speckle_schema.empty?
speckle_schema = SketchupModel::Dictionary::SpeckleSchemaDictionaryHandler
.speckle_schema_to_speckle(component_instance.definition)
end
# transform into global if any path provided
transformation = component_instance.transformation
transformation = SketchupModel::Query::Entity.global_transformation(component_instance, path) if path
block_instance = BlockInstance.new(
units: units,
is_sketchup_group: false,
@@ -96,13 +107,45 @@ module SpeckleConnector
else
RenderMaterial.from_material(component_instance.material)
end,
transform: Other::Transform.from_transformation(component_instance.transformation, units),
transform: Other::Transform.from_transformation(transformation, units),
block_definition: block_definition,
layer: SketchupModel::Query::Layer.entity_path(component_instance),
sketchup_attributes: att,
speckle_schema: speckle_schema,
application_id: component_instance.persistent_id.to_s
)
if speckle_schema
case speckle_schema['method']
when 'New Revit Family'
# duplicate already converted one to attach without speckle schema into mapped block wrapper
copy_block_instance = block_instance.clone(freeze: true)
block_instance['@SpeckleSchema'] = SpeckleObjects::Other::MappedBlockWrapper.new(
category: speckle_schema['category'],
units: units,
instance: copy_block_instance,
application_id: component_instance.persistent_id.to_s
)
when 'Family Instance'
level = speckle_state.speckle_mapper_state.mapper_source
.levels.find { |l| l[:name] == speckle_schema['level'] }
family = speckle_schema['family']
type = speckle_schema['family_type']
block_instance['@SpeckleSchema'] = SpeckleObjects::BuiltElements::Revit::FamilyInstance.new(
family: family,
type: type,
level: level,
units: units,
base_point: SpeckleObjects::Geometry::Point.from_vertex(
component_instance.definition.insertion_point.transform(transformation),
units
),
rotation: calculate_rotation(transformation.to_a),
application_id: component_instance.persistent_id.to_s
)
end
end
return speckle_state, block_instance
end
# rubocop:enable Metrics/MethodLength
@@ -228,6 +271,26 @@ module SpeckleConnector
instance_transform = instance.transformation
instance.transform!(instance_transform * transform.inverse * instance_transform.inverse)
end
def self.calculate_rotation(matrix)
# Ensure the matrix is a flat array with 16 elements
unless matrix.is_a?(Array) && matrix.size == 16
raise ArgumentError, 'Matrix must be an array with 16 elements'
end
# Extract the elements of the 2x2 rotation sub-matrix
cos_theta = matrix[0] # First column, first row
sin_theta = matrix[1] # Second column, first row
# Calculate the rotation angle in radians
theta = Math.atan2(sin_theta, cos_theta)
# Ensure the angle is between -π and π
theta -= 2 * Math::PI while theta > Math::PI
theta += 2 * Math::PI while theta < -Math::PI
theta
end
end
end
end
@@ -22,9 +22,15 @@ module SpeckleConnector
return format_naming_convention([family, type, category, element_id]) unless element_id.nil?
return "def::#{def_obj['applicationId']}" unless def_obj['applicationId'].nil?
name = def_obj['name']
return "#{name}::#{def_obj['applicationId']}" if !name.nil? && !def_obj['applicationId'].nil?
return "def::#{def_obj['id']}"
return "#{name}::#{def_obj['id']}" unless name.nil?
speckle_type = def_obj['speckle_type'].split('.').last
return "#{speckle_type}::#{def_obj['applicationId']}" unless def_obj['applicationId'].nil?
return "#{speckle_type}::#{def_obj['id']}"
end
def self.format_naming_convention(entries)
@@ -41,6 +47,13 @@ module SpeckleConnector
name
end
# Get instance name as speckle_type if it is structured as `speckle_type::application_id`
def self.get_instance_name(definition_name)
return definition_name unless definition_name.include?('::')
definition_name.split('::').first
end
# Creates a component definition and instance from a speckle object with a display value
# @param state [States::State] state of the application.
def self.to_native(state, obj, layer, entities, &convert_to_native)
@@ -49,11 +62,7 @@ module SpeckleConnector
obj['name'] = get_definition_name(obj)
state, _definitions = BlockDefinition.to_native(
state,
obj,
layer,
entities,
&convert_to_native
state, obj, layer, entities, &convert_to_native
)
definition = state.sketchup_state.sketchup_model.definitions[BlockDefinition.get_definition_name(obj)]
@@ -62,7 +71,7 @@ module SpeckleConnector
t_arr = obj['transform']
transform = t_arr.nil? ? Geom::Transformation.new : Other::Transform.to_native(t_arr, obj['units'])
instance = entities.add_instance(definition, transform)
instance.name = obj['name'] unless obj['name'].nil?
instance.name = get_instance_name(obj['name']) unless obj['name'].nil?
instance.layer = layer unless layer.nil?
# Align instance axes that created from display value. (without any transform)
# BlockInstance.align_instance_axes(instance)
@@ -74,13 +83,19 @@ module SpeckleConnector
elements = obj['elements'] || obj['@elements']
if !elements.nil? && elements.is_a?(Array)
elements.each do |element|
# Mullions is a special case here, they are extracted as base object with @displayValue from revit..
if element['@displayValue'].nil?
obj['geometry'].append(element)
else
obj['geometry'] += element['@displayValue']
# if only elements are there then assign only elements, there are some cases that RevitWalls can only
# have elements instead of display value
if obj['geometry'].nil? && !elements.nil?
obj['geometry'] = elements
else
if !elements.nil? && elements.is_a?(Array)
elements.each do |element|
# Mullions is a special case here, they are extracted as base object with @displayValue from revit..
if element['@displayValue'].nil?
obj['geometry'].append(element)
else
obj['geometry'] += element['@displayValue']
end
end
end
end
@@ -0,0 +1,25 @@
# frozen_string_literal: true
require_relative '../base'
module SpeckleConnector
module SpeckleObjects
module Other
# MappedBlockWrapper object definition for Speckle.
class MappedBlockWrapper < Base
SPECKLE_TYPE = 'Objects.Other.MappedBlockWrapper'
def initialize(category:, units:, instance:, application_id: nil)
super(
speckle_type: SPECKLE_TYPE,
total_children_count: 0,
application_id: application_id,
id: nil
)
self[:category] = category
self[:units] = units
self[:instance] = instance
end
end
end
end
end
@@ -15,8 +15,10 @@ module SpeckleConnector
family = def_obj['family']
type = def_obj['type']
category = def_obj['category']
element_id = def_obj['elementId']
id = def_obj['id']
return "#{family}-#{type}-#{category}-#{def_obj['elementId']}"
return "#{family}-#{type}-#{category}-#{element_id}-#{id}"
end
def self.to_native(state, definition, layer, entities, &convert_to_native)
@@ -0,0 +1,28 @@
# frozen_string_literal: true
require_relative 'collection'
module SpeckleConnector
module SpeckleObjects
module Speckle
module Core
module Models
# VectorLayerCollection object that collect GIS vector elements under it's elements.
class GisLayerCollection < Collection
# @param state [States::State] state of the Speckle application.
def self.to_native(state, vector_layer_collection, layer_or_folder, entities, &convert_to_native)
elements = vector_layer_collection['elements']
elements.each do |element|
new_state, _converted_entities = convert_to_native.call(state, element, layer_or_folder, entities)
state = new_state
end
return state, []
end
end
end
end
end
end
end
@@ -4,6 +4,7 @@ require_relative 'collection'
require_relative 'layer_collection'
require_relative '../../../built_elements/view3d'
require_relative '../../../built_elements/revit/direct_shape'
require_relative '../../../../mapper/mapper'
module SpeckleConnector
module SpeckleObjects
@@ -13,7 +14,9 @@ module SpeckleConnector
# ModelCollection object that collect other speckle objects under it's elements.
class ModelCollection < Collection
DIRECT_SHAPE = SpeckleObjects::BuiltElements::Revit::DirectShape
QUERY = SketchupModel::Query
VIEW3D = SpeckleObjects::BuiltElements::View3d
SPECKLE_SCHEMA_DICTIONARY_HANDLER = SketchupModel::Dictionary::SpeckleSchemaDictionaryHandler
def initialize(name:, active_layer:, elements: [], application_id: nil)
super(
name: name,
@@ -24,6 +27,27 @@ module SpeckleConnector
self[:active_layer] = active_layer
end
def self.to_native(state, model_collection, layer, entities, &convert_to_native)
elements = model_collection['elements']
views = model_collection['@Views']
if views
views.each do |view|
new_state, _converted_entities = convert_to_native.call(state, view, layer, entities)
state = new_state
end
end
elements.each do |element|
new_state, _converted_entities = convert_to_native.call(state, element, layer, entities)
state = new_state
end
active_layer = model_collection['active_layer']
state.sketchup_state.sketchup_model.active_layer = active_layer unless active_layer.nil?
return state, []
end
def self.from_sketchup_model(sketchup_model, speckle_state, units, preferences, &convert)
model_collection = ModelCollection.new(
name: 'Sketchup Model', active_layer: sketchup_model.active_layer.display_name,
@@ -31,7 +55,8 @@ module SpeckleConnector
)
# Direct shapes will pass directly to elements which are already flattened with all children
model_collection[:elements] += collect_direct_shapes(sketchup_model, units, preferences)
model_collection[:elements] += collect_mapped_entities(speckle_state, sketchup_model, units,
preferences, &convert)
# Views will pass directly to elements since they don't have any relation with layers and geometries.
model_collection[:elements] += VIEW3D.from_model(sketchup_model, units) if sketchup_model.pages.any?
@@ -52,28 +77,43 @@ module SpeckleConnector
return speckle_state, model_collection
end
def self.collect_direct_shapes(sketchup_model, units, preferences)
DIRECT_SHAPE.direct_shapes_on_selection(sketchup_model).collect do |entities|
entity = entities[0]
path = entities[1..-1]
direct_shape = DIRECT_SHAPE.from_entity(entity, path, units, preferences)
[direct_shape, [entity]]
# @param sketchup_model [Sketchup::Model] active model to retrieve and convert mapped entities.
def self.collect_mapped_entities(speckle_state, sketchup_model, units, preferences, &convert)
mapped_entities = Mapper.mapped_entities_on_selection(sketchup_model)
mapped_entities.collect do |entity_with_path|
convert_mapped_entity(speckle_state, entity_with_path, preferences, units, &convert)
end
end
def self.to_native(state, model_collection, layer, entities, &convert_to_native)
elements = model_collection['elements']
elements.each do |element|
new_state, _converted_entities = convert_to_native.call(state, element, layer, entities)
state = new_state
def self.convert_mapped_entity(speckle_state, entity_with_path, preferences, units, &convert)
entity = entity_with_path[0]
path = entity_with_path[1..-1]
method = SPECKLE_SCHEMA_DICTIONARY_HANDLER.get_attribute(entity, 'method')
if entity.is_a?(Sketchup::ComponentInstance) && method.nil?
method = SPECKLE_SCHEMA_DICTIONARY_HANDLER.get_attribute(entity.definition, 'method')
end
active_layer = model_collection['active_layer']
state.sketchup_state.sketchup_model.active_layer = active_layer unless active_layer.nil?
if !method.nil? && (method.include?('Floor') || method.include?('Wall')) && entity.is_a?(Sketchup::Face)
global_transformation = QUERY::Entity.global_transformation(entity, path)
floor = SpeckleObjects::Geometry::Mesh.from_face(speckle_state: speckle_state, face: entity,
units: units, model_preferences: preferences,
global_transform: global_transformation)
return [floor, [entity]]
end
return state, []
if method == 'Direct Shape'
direct_shape = DIRECT_SHAPE.from_entity(speckle_state, entity, path, units, preferences)
return [direct_shape, [entity]]
end
if ['New Revit Family', 'Family Instance'].include?(method)
_speckle_state, block_instance = SpeckleObjects::Other::BlockInstance.from_component_instance(
entity, units, preferences, speckle_state, path: path, &convert
)
return [block_instance, [entity]]
end
nil
end
end
end
@@ -0,0 +1,45 @@
# frozen_string_literal: true
require_relative '../immutable/immutable'
require_relative '../callbacks/callback_message'
require_relative '../speckle_entities/speckle_entity'
require_relative '../mapper/mapper_source'
module SpeckleConnector
module States
# State of the speckle on ruby.
class SpeckleMapperState
include Immutable::ImmutableUtils
# @return [ImmutableHash{Integer=>Sketchup::Entity}] persistent_id of the sketchup entity and itself
attr_reader :mapped_entities
# @return [Mapper::MapperSource] source of the mapper.
attr_reader :mapper_source
def initialize
@mapped_entities = Immutable::EmptyHash
@mapper_source = nil
end
def with_mapped_entity(entity)
new_mapped_entities = mapped_entities.put(entity.persistent_id, entity)
with_mapped_entities(new_mapped_entities)
end
def with_removed_mapped_entity(entity)
new_mapped_entities = mapped_entities.delete(entity.persistent_id)
with_mapped_entities(new_mapped_entities)
end
def with_mapped_entities(new_mapped_entities)
with(:@mapped_entities => new_mapped_entities)
end
def with_mapper_source(mapper_source)
# TODO: Check/Sync here parameters of the mapped entities.
with(:@mapper_source => mapper_source)
end
end
end
end
+32 -14
View File
@@ -1,5 +1,6 @@
# frozen_string_literal: true
require_relative 'speckle_mapper_state'
require_relative '../immutable/immutable'
require_relative '../callbacks/callback_message'
require_relative '../speckle_entities/speckle_entity'
@@ -10,13 +11,13 @@ module SpeckleConnector
class SpeckleState
include Immutable::ImmutableUtils
# @return [States::SpeckleMapperState] state of the mapper.
attr_reader :speckle_mapper_state
# @return [ImmutableHash{Integer=>SpeckleEntities::SpeckleEntity}] persistent_id of the sketchup entity and
# corresponding speckle entity
attr_reader :speckle_entities
# @return [ImmutableHash{Integer=>Sketchup::Entity}] persistent_id of the sketchup entity and itself
attr_reader :mapped_entities
# @return [Array] accounts on appdata.
attr_reader :accounts
@@ -47,10 +48,10 @@ module SpeckleConnector
@message_queue = queue
@stream_queue = stream_queue
@speckle_entities = Immutable::EmptyHash
@mapped_entities = Immutable::EmptyHash
@render_materials = Immutable::EmptyHash
@definitions = Immutable::EmptyHash
@relation = Relations::ManyToOneRelation.new
@speckle_mapper_state = SpeckleMapperState.new
end
# @param callback_name [String] name of the callback command
@@ -68,13 +69,19 @@ module SpeckleConnector
with(:@message_queue => new_queue)
end
def with_selection_queue(selection_parameters)
def with_mapper_selection_queue(selection_parameters)
new_queue = message_queue.merge({ "entitySelected":
"entitySelected(#{JSON.generate(selection_parameters)})" })
with(:@message_queue => new_queue)
end
def with_deselection_queue
def with_mapper_init_queue(init_parameters)
new_queue = message_queue.merge({ "mapperInitialized":
"mapperInitialized(#{JSON.generate(init_parameters)})" })
with(:@message_queue => new_queue)
end
def with_mapper_deselection_queue
new_queue = message_queue.merge({ "entitiesDeselected": 'entitiesDeselected()' })
with(:@message_queue => new_queue)
end
@@ -95,14 +102,29 @@ module SpeckleConnector
with(:@accounts => new_accounts)
end
def with_mapper_source(mapper_source)
new_speckle_mapper_state = speckle_mapper_state.with_mapper_source(mapper_source)
with(:@speckle_mapper_state => new_speckle_mapper_state)
end
def with_removed_mapper_source
new_speckle_mapper_state = speckle_mapper_state.with_mapper_source(nil)
with(:@speckle_mapper_state => new_speckle_mapper_state)
end
def with_mapped_entity(entity)
new_mapped_entities = mapped_entities.put(entity.persistent_id, entity)
with_mapped_entities(new_mapped_entities)
new_speckle_mapper_state = speckle_mapper_state.with_mapped_entity(entity)
with(:@speckle_mapper_state => new_speckle_mapper_state)
end
def with_removed_mapped_entity(entity)
new_mapped_entities = mapped_entities.delete(entity.persistent_id)
with_mapped_entities(new_mapped_entities)
new_speckle_mapper_state = speckle_mapper_state.with_removed_mapped_entity(entity)
with(:@speckle_mapper_state => new_speckle_mapper_state)
end
def with_mapped_entities(new_mapped_entities)
new_speckle_mapper_state = speckle_mapper_state.with_mapped_entities(new_mapped_entities)
with(:@speckle_mapper_state => new_speckle_mapper_state)
end
def with_speckle_entity(traversed_entity)
@@ -114,10 +136,6 @@ module SpeckleConnector
with(:@speckle_entities => new_speckle_entities)
end
def with_mapped_entities(new_mapped_entities)
with(:@mapped_entities => new_mapped_entities)
end
def with_relation(new_relation)
with(:@relation => new_relation)
end
+8 -3
View File
@@ -41,15 +41,20 @@ module SpeckleConnector
with(:@speckle_state => new_speckle_state)
end
def with_selection_queue(selection_parameters)
def with_mapper_selection_queue(selection_parameters)
new_speckle_state = if selection_parameters[:selection].any?
speckle_state.with_selection_queue(selection_parameters)
speckle_state.with_mapper_selection_queue(selection_parameters)
else
speckle_state.with_deselection_queue
speckle_state.with_mapper_deselection_queue
end
with(:@speckle_state => new_speckle_state)
end
def with_mapper_init_queue(init_parameters)
new_speckle_state = speckle_state.with_mapper_init_queue(init_parameters)
with(:@speckle_state => new_speckle_state)
end
def with_empty_stream_queue
new_speckle_state = speckle_state.with(:@stream_queue => {})
with(:@speckle_state => new_speckle_state)
+7 -1
View File
@@ -16,7 +16,9 @@ require_relative '../commands/model_preferences_updated'
require_relative '../commands/activate_diffing'
require_relative '../commands/apply_mappings'
require_relative '../commands/clear_mappings'
require_relative '../commands/mapper_source_updated'
require_relative '../actions/mapper_initialized'
require_relative '../actions/reload_accounts'
require_relative '../actions/load_saved_streams'
require_relative '../actions/init_local_accounts'
@@ -29,6 +31,7 @@ require_relative '../actions/isolate_mappings_from_table'
require_relative '../actions/hide_mappings_from_table'
require_relative '../actions/select_mappings_from_table'
require_relative '../actions/show_all_entities'
require_relative '../actions/clear_mapper_source'
module SpeckleConnector
module Ui
@@ -89,7 +92,10 @@ module SpeckleConnector
isolate_mappings_from_table: Commands::ActionCommand.new(@app, Actions::IsolateMappingsFromTable),
hide_mappings_from_table: Commands::ActionCommand.new(@app, Actions::HideMappingsFromTable),
select_mappings_from_table: Commands::ActionCommand.new(@app, Actions::SelectMappingsFromTable),
show_all_entities: Commands::ActionCommand.new(@app, Actions::ShowAllEntities)
show_all_entities: Commands::ActionCommand.new(@app, Actions::ShowAllEntities),
mapper_source_updated: Commands::MapperSourceUpdated.new(@app),
clear_mapper_source: Commands::ActionCommand.new(@app, Actions::ClearMapperSource),
mapper_initialized: Commands::ActionCommand.new(@app, Actions::MapperInitialized)
}.freeze
end
# rubocop:enable Metrics/MethodLength
+15 -7
View File
@@ -15,7 +15,7 @@
>
<v-tabs-slider class="mx-sm-1"></v-tabs-slider>
<v-tab href="#streams">
{{"Streams"}}
{{ streamsText }}
</v-tab>
<v-tab href="#mapper">
{{"Mapper"}}
@@ -88,7 +88,7 @@
<v-text-field
v-model="streamSearchQuery"
prepend-inner-icon="mdi-magnify"
label="Search streams"
:label="searchText"
background-color="background"
hide-details
clearable
@@ -101,6 +101,7 @@
</v-container>
<create-stream-dialog
v-if="accounts().length !== 0"
:is-f-e2="preferences && preferences.user && preferences.user.fe2"
:account-id="activeAccount().userInfo.id"
:server-url="activeAccount().serverInfo.url"
/>
@@ -115,7 +116,7 @@
</v-tab-item>
<v-tab-item :key="2" value="mapper">
<v-card flat>
<mapper></mapper>
<mapper :stream-text="streamText" :branch-text="branchText"></mapper>
</v-card>
</v-tab-item>
</v-tabs-items>
@@ -174,7 +175,7 @@ export default {
size: {
type: Number,
default: 42
},
}
},
data() {
return {
@@ -183,7 +184,11 @@ export default {
createStreamByIdDialog: false,
createStreamByIdText: "",
preferences: {},
tab: "streams"
tab: "streams",
searchText: '',
streamsText: 'Streams',
streamText: 'Stream',
branchText: 'Branch'
}
},
computed: {
@@ -209,8 +214,11 @@ export default {
})
bus.$on('update-preferences', async (preferences) => {
let prefs = JSON.parse(preferences)
this.preferences = prefs
this.preferences = JSON.parse(preferences)
this.searchText = this.preferences.user.fe2 ? 'Search projects' : 'Search streams'
this.streamsText = this.preferences.user.fe2 ? 'Projects' : 'Streams'
this.streamText = this.preferences.user.fe2 ? 'Project' : 'Stream'
this.branchText = this.preferences.user.fe2 ? 'Model' : 'Branch'
this.$vuetify.theme.dark = this.preferences.user.dark_theme
})
+8 -3
View File
@@ -232,22 +232,27 @@ export default {
// }
},
clearMappingsFromTableSelection(){
sketchup.exec({ name: "clear_mappings_from_table", data: this.elementSelection })
sketchup.exec({ name: "clear_mappings_from_table", data: this.elementSelection })
this.$mixpanel.track('MappingsAction', { name: 'Mappings Clear' })
},
isolateMappedElementsOnSketchup(){
if (this.isIsolated){
this.isIsolated = false
sketchup.exec({ name: "show_all_entities", data: {} })
this.$mixpanel.track('MappingsAction', { name: 'Mappings Un-Isolate' })
} else {
this.isIsolated = true
sketchup.exec({ name: "isolate_mappings_from_table", data: this.elementSelection })
this.$mixpanel.track('MappingsAction', { name: 'Mappings Isolate' })
}
},
hideMappedElementsOnSketchup(){
sketchup.exec({ name: "hide_mappings_from_table", data: this.elementSelection })
sketchup.exec({ name: "hide_mappings_from_table", data: this.elementSelection })
this.$mixpanel.track('MappingsAction', { name: 'Mappings Hide' })
},
selectMappedElementsOnSketchup(){
sketchup.exec({ name: "select_mappings_from_table", data: this.elementSelection })
sketchup.exec({ name: "select_mappings_from_table", data: this.elementSelection })
this.$mixpanel.track('MappingsAction', { name: 'Mappings Select Elements' })
},
// Update mapped elements table whenever mapped elements has changed.
getMappedElementsTableData(){
+399 -28
View File
@@ -58,6 +58,41 @@
</v-expansion-panel-content>
</v-expansion-panel>
<v-expansion-panel key="source">
<v-expansion-panel-header class="flex">
<v-container class="ma-0 pa-0">
<v-icon>
mdi-source-branch
</v-icon>
{{ `Source` }}
<v-tooltip right>
<template #activator="{ on, attrs }">
<v-btn
class="ma-0 ml-1"
height="20px"
width="20px"
icon
x-small
:color="getSourceStateIconColor()"
v-bind="attrs"
v-on="on"
@click="refreshSourceBranch"
>
<v-icon>
{{ getSourceStateIcon() }}
</v-icon>
</v-btn>
</template>
<span>{{ getSourceStateToolTip() }}</span>
</v-tooltip>
</v-container>
</v-expansion-panel-header>
<v-expansion-panel-content>
<mapper-source :stream-text="streamText" :branch-text="branchText" :source-state="this.sourceState"/>
</v-expansion-panel-content>
</v-expansion-panel>
<v-expansion-panel key="mapping">
<v-expansion-panel-header>
<div>
@@ -124,16 +159,56 @@
class="pt-0"
label="Mapper Method"
:disabled="!entitySelected"
:items="enabledMethods"
:items="availableMethods"
density="compact"
clearable
@change="onSelectedMethodChange"
></v-autocomplete>
<v-autocomplete
v-if="familySelectionActive"
v-model="selectedFamily"
class="pt-0"
label="Family"
:disabled="!entitySelected"
:items="inputFamilies"
density="compact"
clearable
@change="onSelectedFamilyChange"
></v-autocomplete>
<v-autocomplete
v-if="typeSelectionActive"
v-model="selectedFamilyType"
class="pt-0"
label="Type"
:disabled="!entitySelected"
item-value="type"
item-text="type"
:items="familyTypes"
density="compact"
clearable
></v-autocomplete>
<v-autocomplete
v-if="levelSelectionActive"
v-model="selectedLevel"
class="pt-0"
label="Base Level"
:disabled="!entitySelected"
:items="levels"
item-value="name"
item-text="name"
density="compact"
clearable
></v-autocomplete>
<v-autocomplete
v-if="categorySelectionActive"
v-model="selectedCategory"
class="pt-0"
label="Category"
:items="availableCategories"
:items="selectedMethod === 'New Revit Family' ? familyCategories : categories"
item-value="value"
item-text="key"
:disabled="!entitySelected"
@@ -142,6 +217,7 @@
></v-autocomplete>
<v-text-field
v-if="nameSelectionActive"
v-model="name"
class="pt-0"
label="Name"
@@ -153,6 +229,7 @@
<v-row justify="center" align="center">
<v-col cols="auto" class="pa-1 pb-2">
<v-btn
small
:disabled="!entitySelected"
@click="applyMapping"
>
@@ -163,6 +240,7 @@
</v-col>
<v-col cols="auto" class="pa-1 pb-2">
<v-btn
small
:disabled="!entitySelected"
@click="clearMapping"
>
@@ -173,10 +251,7 @@
</v-col>
</v-row>
</v-container>
</v-expansion-panel-content>
</v-expansion-panel>
@@ -204,11 +279,22 @@
/*global sketchup*/
import {bus} from "@/main";
import {groupBy} from "@/utils/groupBy";
import MappingSource from "@/components/MapperSource.vue";
import {sourceMap} from "@vue/cli-service/lib/config/terserOptions";
global.mapperSourceUpdated = function (streamId, levels, types, familyInstances) {
console.log(`Mapper source updated for ${streamId}.`)
bus.$emit('mapper-source-updated', JSON.stringify(levels), JSON.stringify(types), JSON.stringify(familyInstances))
}
global.entitySelected = function (selectionParameters) {
bus.$emit('entities-selected', JSON.stringify(selectionParameters))
}
global.mapperInitialized = function (initParameters) {
bus.$emit('mapper-initialized', JSON.stringify(initParameters))
}
global.entitiesDeselected = function () {
bus.$emit('entities-deselected')
}
@@ -219,12 +305,35 @@ global.mappedEntitiesUpdated = function (mappedEntities) {
export default {
name: "Mapper",
props: {
streamText: {
type: String,
default: ''
},
branchText: {
type: String,
default: ''
}
},
components: {
MapperSource: () => import('@/components/MapperSource.vue'),
GlobalToast: () => import('@/components/GlobalToast'),
MappedElements: () => import('@/components/MappedElements.vue')
},
data() {
return {
nativeFaceMethods: ['Floor', 'Wall'],
nativeEdgeMethods: ['Column', 'Beam', 'Brace', 'Pipe', 'Duct'],
nativeDefaultFaceMethods: ['Default Floor', 'Default Wall'],
nativeDefaultEdgeMethods: ['Default Column', 'Default Beam', 'Default Brace', 'Default Pipe', 'Default Duct'],
familySelectionActive: false,
typeSelectionActive: false,
levelSelectionActive: false,
categorySelectionActive: false,
nameSelectionActive: false,
sourceState: 'Not Set',
// Expanded indexes for selection table (Types)
selectionExpandedIndexes: [],
// Expanded indexes for mapped element table (Categories)
@@ -239,15 +348,33 @@ export default {
entitySelected: false,
selectedEntityCount: 0,
selectedEntities: [],
familyTypes: [],
lastSelectedEntity: null,
selectedMethod: null,
selectedCategory: null,
selectedFamily: null,
selectedFamilyType: null,
selectedLevel: null,
name: "",
enabledMethods: [],
availableCategories: [],
availableMethods: [],
categories: [],
familyCategories: [],
inputCategories: [],
inputFamilies: [],
inputFamilyTypes: {},
// comes from source
types: {},
levels: [],
familyInstanceTypes: {}, // comes from source as filtered
mappedEntityCount: 0,
mappedEntities: [],
panel: [1],
panel: [2],
selectionHeaders: [
{ text: 'Type', sortable: false, value: 'entityType', width: '60%' },
{ text: 'Count', sortable: false, align: 'center', value: 'count', width: '20%' },
@@ -354,9 +481,168 @@ export default {
}
},
methods:{
getSourceStateIcon(){
switch (this.sourceState){
case "Not Set":
return `mdi-cloud-off-outline`;
case "Set":
return `mdi-checkbox-marked-circle-outline`;
case "Outdated":
return `mdi-update`;
default:
break;
}
},
getSourceStateToolTip(){
switch (this.sourceState){
case "Not Set":
return 'Source disconnected.';
case "Set":
return 'Source connected.';
case "Outdated":
return 'Source branch is not up-to-date!';
default:
break;
}
},
getSourceStateIconColor(){
switch (this.sourceState){
case "Not Set":
return `grey`;
case "Set":
return `green`;
case "Outdated":
return `red`;
default:
break;
}
},
onSelectedMethodChange(){
this.hideOptionalMappingInputs()
this.updateMappingInputs()
this.getFamiliesFromSelectedMethod()
this.getTypesFromSelectedFamily()
this.$mixpanel.track('MappingsAction', { name: 'Mappings Set', schema: this.selectedMethod })
},
onSelectedFamilyChange(){
this.getTypesFromSelectedFamily();
},
updateMappingInputs(){
if (this.selectedMethod === null){
this.typeSelectionActive = false
this.familySelectionActive = false
this.levelSelectionActive = false
this.categorySelectionActive = false
this.nameSelectionActive = false
return
}
const nativeDefaultMethods = this.nativeDefaultEdgeMethods.concat(this.nativeDefaultFaceMethods)
const nativeMethods = this.nativeEdgeMethods.concat(this.nativeFaceMethods)
if (this.selectedMethod === 'Direct Shape'){
this.categorySelectionActive = true
this.nameSelectionActive = true
}
else if (this.selectedMethod === 'New Revit Family'){
this.categorySelectionActive = true
}
else if (this.selectedMethod === 'Family Instance'){
this.typeSelectionActive = true
this.familySelectionActive = true
this.levelSelectionActive = true
}
else if (nativeDefaultMethods.includes(this.selectedMethod)){
this.typeSelectionActive = false
this.familySelectionActive = false
this.levelSelectionActive = false
this.categorySelectionActive = false
this.nameSelectionActive = false
}
else if (nativeMethods.includes(this.selectedMethod)){
this.typeSelectionActive = true
this.familySelectionActive = true
this.levelSelectionActive = true
}
},
getTypesFromSelectedFamily(){
// There is no sense to set selected family type if method is not selected
if (this.selectedMethod){
if (this.sourceState !== 'Not Set'){
this.familyTypes = this.inputFamilyTypes[this.selectedFamily]
this.selectedFamilyType = this.familyTypes[0].type
if (this.selectedFamilyType === null || this.selectedFamilyType === undefined){
this.selectedFamilyType = this.familyTypes[0].type
}
}
if (this.selectedFamily === null || this.selectedFamily === undefined){
this.selectedFamily = this.inputFamilies[0]
}
if (this.familyTypes === null ||this.familyTypes === undefined){
this.familyTypes = this.inputFamilyTypes[this.selectedFamily]
}
}
},
getFamiliesFromSelectedMethod(){
switch (this.selectedMethod) {
case 'Floor':
this.inputFamilies = Object.keys(this.types['Floors']);
this.inputFamilyTypes = this.types['Floors']
break;
case 'Wall':
this.inputFamilies = Object.keys(this.types['Walls']);
this.inputFamilyTypes = this.types['Walls']
break;
case 'Column':
this.inputFamilies = Object.keys(this.types['Columns']);
this.inputFamilyTypes = this.types['Columns']
break;
case 'Beam':
this.inputFamilies = Object.keys(this.types['Beams']);
this.inputFamilyTypes = this.types['Beams']
break;
case 'Pipe':
this.inputFamilies = Object.keys(this.types['Piping System']);
this.inputFamilyTypes = this.types['Piping System']
break;
case 'Duct':
this.inputFamilies = Object.keys(this.types['Duct System']);
this.inputFamilyTypes = this.types['Duct System']
break;
case 'Family Instance':
this.inputFamilies = Object.keys(this.familyInstanceTypes);
this.inputFamilyTypes = this.familyInstanceTypes
break;
default:
break;
}
if (this.selectedMethod){
if (this.selectedFamily === null || this.selectedFamily === undefined){
if (this.inputFamilies[0]){
this.selectedFamily = this.inputFamilies[0]
}
}
if (this.selectedLevel === null || this.selectedLevel === undefined){
if (this.levels[0]){
this.selectedLevel = this.levels[0].name
}
}
}
},
hideOptionalMappingInputs(){
this.categorySelectionActive = false
this.nameSelectionActive = false
this.typeSelectionActive = false
this.familySelectionActive = false
this.levelSelectionActive = false
},
refreshSourceBranch(){
if (this.sourceState === 'Outdated'){
bus.$emit('refresh-source-branch')
this.$mixpanel.track('MappingsAction', { name: 'Mappings Source Update' })
}
},
clearInputs(){
this.enabledMethods = []
this.availableCategories = []
this.availableMethods = []
this.selectedEntities = []
this.selectionTableData = []
this.selectedEntityCount = 0
@@ -365,6 +651,9 @@ export default {
this.selectedCategory = null
this.entityMapped = false
this.definitionMapped = false
this.selectedLevel = null
this.selectedFamily = null
this.selectedFamilyType = null
},
getSelectionTableData(){
let groupByClass = groupBy('entityType')
@@ -403,12 +692,12 @@ export default {
return summary
},
setInputValuesFromSelection(){
// Clear all inputs if entity is not selected.
if (!this.entitySelected){
this.name = ""
this.selectedMethod = null
this.selectedCategory = null
this.clearMappingInputs()
return
}
// Check if definition card is selected and set definition mappings.
if (this.definitionSelected) {
if (!this.definitionMapped){
if (this.selectedEntityCount > 1){
@@ -416,7 +705,7 @@ export default {
}else{
this.name = this.lastSelectedEntity['definition']['entityName']
}
this.selectedMethod = 'Direct Shape'
// this.selectedMethod = 'Direct Shape'
this.selectedCategory = 49
} else {
if (this.selectedEntityCount > 1){
@@ -426,15 +715,26 @@ export default {
}
this.selectedMethod = this.lastSelectedEntity['definition']['schema']['method']
this.selectedCategory = this.lastSelectedEntity['definition']['schema']['category']
this.selectedFamily = this.lastSelectedEntity['definition']['schema']['family']
this.getFamiliesFromSelectedMethod()
this.getTypesFromSelectedFamily()
this.selectedFamilyType = this.lastSelectedEntity['definition']['schema']['family_type']
this.selectedLevel = this.lastSelectedEntity['definition']['schema']['level']
this.updateMappingInputs()
}
} else {
}
// Otherwise set entity mappings.
else
{
if (!this.entityMapped){
if (this.selectedEntityCount > 1){
this.name = '<Mixed>'
}else{
this.name = this.lastSelectedEntity['entityName']
}
this.selectedMethod = 'Direct Shape'
this.updateMappingInputs()
this.getFamiliesFromSelectedMethod()
this.getTypesFromSelectedFamily()
this.selectedCategory = 49
} else {
if (this.selectedEntityCount > 1){
@@ -443,7 +743,13 @@ export default {
this.name = this.lastSelectedEntity['schema']['name']
}
this.selectedMethod = this.lastSelectedEntity['schema']['method']
this.updateMappingInputs()
this.selectedFamily = this.lastSelectedEntity['schema']['family']
this.selectedCategory = this.lastSelectedEntity['schema']['category']
this.getFamiliesFromSelectedMethod()
this.getTypesFromSelectedFamily()
this.selectedFamilyType = this.lastSelectedEntity['schema']['family_type']
this.selectedLevel = this.lastSelectedEntity['schema']['level']
}
}
},
@@ -482,10 +788,29 @@ export default {
this.mappedElementsExpandedIndexes.push(slotData.item);
}
},
inputsReadyToApply(){
if (this.selectedMethod === null || this.selectedMethod === undefined){
return false;
}
const nativeMethods = this.nativeEdgeMethods.concat(this.nativeFaceMethods)
if (this.selectedMethod === 'Direct Shape'){
return this.selectedCategory !== null
}
else if (nativeMethods.includes(this.selectedMethod)){
return this.selectedFamily !== null &&
this.selectedFamilyType !== null &&
this.selectedLevel !== null
}
else {
return true;
}
},
applyMapping(){
if (this.selectedMethod === null || this.selectedCategory === null){
if (!this.inputsReadyToApply()){
this.$eventHub.$emit('error', {
text: 'Method and category are not set.\n'
text: 'Some inputs are not set to apply mapping.\n'
})
return
}
@@ -493,6 +818,9 @@ export default {
entitiesToMap: this.selectedEntities.map((entity) => entity['entityId']),
method: this.selectedMethod,
category: this.selectedCategory,
family: this.selectedFamily,
familyType: this.selectedFamilyType,
level: this.selectedLevel,
name: this.name,
isDefinition: this.definitionSelected
}
@@ -500,6 +828,8 @@ export default {
this.$eventHub.$emit('success', {
text: 'Mapping Applied.\n'
})
this.$mixpanel.track('MappingsAction', { name: 'Mappings Applied' })
},
clearMapping(){
const mapping = {
@@ -511,32 +841,73 @@ export default {
this.$eventHub.$emit('error', {
text: 'Mapping Cleared.\n'
})
}
},
mounted() {
sketchup.exec({name: "collect_mapped_entities", data: {}})
bus.$on('entities-selected', async (selectionParameters) => {
const selectionPars = JSON.parse(selectionParameters)
this.enabledMethods = selectionPars.mappingMethods
this.availableCategories = selectionPars.categories
this.selectedEntities = selectionPars.selection
this.$mixpanel.track('MappingsAction', { name: 'Mappings Clear' })
},
clearMappingInputs(){
this.selectedMethod = null
this.selectedCategory = null
this.name = ""
this.selectedFamily = null
this.selectedFamilyType = null
this.selectedLevel = null
this.availableMethods = null
},
getDataFromSelection(selectionParameters){
this.availableMethods = selectionParameters.mappingMethods
this.selectedEntities = selectionParameters.selection
this.selectedLevel = selectionParameters.selectedLevelName
},
updateStatesFromSelectionData(){
this.lastSelectedEntity = this.selectedEntities[this.selectedEntities.length - 1]
this.entityMapped = this.isEntitiesMapped(this.selectedEntities)
this.definitionMapped = this.isEntityDefinitionsMapped(this.selectedEntities)
this.definitionSelected = !this.entityMapped && this.definitionMapped
this.selectedEntityCount = this.selectedEntities.length
this.entitySelected = this.selectedEntityCount !== 0
}
},
mounted() {
sketchup.exec({name: "mapper_initialized", data: {}})
sketchup.exec({name: "collect_mapped_entities", data: {}})
bus.$on('mapper-initialized', async (initParameters) => {
const initPars = JSON.parse(initParameters)
this.categories = initPars.categories
this.familyCategories = initPars.familyCategories
})
bus.$on('mapper-source-updated', async (levels, types, familyInstances) => {
// Parse data to json object
this.familyInstanceTypes = JSON.parse(familyInstances)
this.levels = JSON.parse(levels)
this.types = JSON.parse(types)
})
bus.$on('entities-selected', async (selectionParameters) => {
// Parse data to json object
const selectionPars = JSON.parse(selectionParameters)
// Reset mapping inputs with nulls and empties.
this.clearMappingInputs()
// Get data from selection into objects and arrays. These data basically constructs the dropdowns.
this.getDataFromSelection(selectionPars)
// Update inner state of the mapper component according to selection data.
this.updateStatesFromSelectionData()
// Get selection table data.
this.getSelectionTableData()
// Set mapping input values from selection data.
this.setInputValuesFromSelection()
})
bus.$on('entities-deselected', async () => {
this.entitySelected = false
this.clearInputs()
this.hideOptionalMappingInputs()
})
bus.$on('mapped-entities-updated', async (mappedEntities) => {
this.mappedEntityCount = mappedEntities.length
})
bus.$on('set-source-up-to-date', (isUpToDate) => {
this.sourceState = isUpToDate
})
}
}
</script>
+222
View File
@@ -0,0 +1,222 @@
<template>
<v-container class="pa-0">
<v-autocomplete
v-model="sourceStreamId"
:label="streamText"
:items="allStreamsList"
item-text="name"
item-value="id"
density="compact"
></v-autocomplete>
<v-autocomplete
v-model="sourceBranchId"
class="pt-0 mb-n5"
:label="branchText"
:items="allBranchesList"
:disabled="sourceStreamId === null"
item-text="name"
item-value="id"
density="compact"
></v-autocomplete>
<v-container class="pa-0 mt-2">
<v-row justify="center" align="center">
<v-col cols="auto" class="pa-1 pb-2">
<v-btn
small
@click="applySource"
>
<v-icon dark left>
mdi-checkbox-marked-circle
</v-icon>Apply
</v-btn>
</v-col>
<v-col cols="auto" class="pa-1 pb-2">
<v-btn
small
:disabled="!sourceApplied"
@click="clearSource"
>
<v-icon dark left>
mdi-close-circle
</v-icon>Clear
</v-btn>
</v-col>
</v-row>
</v-container>
</v-container>
</template>
<script>
/*global sketchup*/
import gql from "graphql-tag";
import streamQuery from "@/graphql/stream.gql";
import {bus} from "@/main";
import ObjectLoader from "@speckle/objectloader";
const streamLimit = 20
export default {
name: "MappingSource",
props: {
streamSearchQuery: { type: String, default: null },
sourceState: { type: String, default: 'Not Set' },
streamText: {
type: String,
default: ''
},
branchText: {
type: String,
default: ''
}
},
data() {
return {
sourceApplied: false,
sourceStreamId: null,
sourceBranchId: null,
sourceStreamName: null,
sourceBranchName: null
}
},
apollo: {
streams: {
prefetch: true,
debounce: 300,
fetchPolicy: 'cache-and-network',
query: gql`
query Streams($query: String, $limit: Int, $cursor: String) {
streams(query: $query, limit: $limit, cursor: $cursor) {
totalCount
cursor
items {
id
name
}
}
}
`,
variables() {
return {
query: this.streamSearchQuery,
limit: streamLimit,
cursor: null
}
},
update(data) {
bus.$emit('streams-loaded')
this.showMoreEnabled = data.streams?.items.length < data.streams.totalCount
return data.streams
},
},
$subscribe: {
commitCreated: {
query: gql`
subscription ($streamId: String!) {
commitCreated(streamId: $streamId)
}
`,
variables() {
return {
streamId: this.sourceStreamId
}
},
result(data) {
if (data.data.commitCreated.sourceApplication.includes('Revit')){
if (data.data.commitCreated.branchName === this.selectedBranch.name){
this.afterCommitCreated()
this.$eventHub.$emit('notification', {
text: `A new commit was created on Revit!`,
})
this.$apollo.queries.stream.refetch()
}
}
},
skip() {
// Return true to skip the initial query execution
return this.sourceStreamId === null;
},
}
},
stream: {
query: streamQuery,
prefetch: true,
variables() {
return {
id: this.sourceStreamId
}
},
skip() {
// Return true to skip the initial query execution
return this.sourceStreamId === null;
},
}
},
computed: {
selectedBranch() {
if (!this.stream) return
return this.stream.branches.items.find((branch) => branch.id === this.sourceBranchId)
},
allStreamsList() {
if (this.$apollo.loading) return
return this.streams?.items
},
allBranchesList() {
if (this.$apollo.loading) return
return this.stream?.branches.items
},
},
mounted() {
bus.$on('refresh-source-branch', () => {
this.onSourceBranchChanged()
bus.$emit('set-source-up-to-date', 'Set')
})
},
methods: {
applySource(){
bus.$emit('set-source-up-to-date', 'Set')
this.onSourceBranchChanged()
this.$eventHub.$emit('success', {
text: 'Mapper source applied.\n'
})
this.$mixpanel.track('MappingsAction', { name: 'Mappings Source Apply' })
},
clearSource(){
sketchup.exec({name:"clear_mapper_source" , data: {}})
bus.$emit('set-source-up-to-date', 'Not Set')
this.sourceApplied = false
this.sourceBranchName = null
this.sourceStreamName = null
this.sourceBranchId = null
this.sourceStreamId = null
this.$eventHub.$emit('error', {
text: 'Mapper source cleared.\n'
})
},
afterCommitCreated(){
bus.$emit('set-source-up-to-date', 'Outdated')
},
async onSourceBranchChanged() {
const commitRefId = this.selectedBranch.commits.items[0]?.referencedObject
if (!commitRefId) { return }
const loader = new ObjectLoader({
serverUrl: localStorage.getItem('serverUrl'),
token: localStorage.getItem('SpeckleSketchup.AuthToken'),
streamId: this.sourceStreamId,
objectId: commitRefId
})
let rootObj = await loader.getAndConstructObject(this.updateLoadingStage)
sketchup.exec({name:"mapper_source_updated" , data: {
base: rootObj,
stream_id: this.sourceStreamId,
commit_id: commitRefId
}})
this.sourceApplied = true
}
}
}
</script>
+22 -4
View File
@@ -13,7 +13,7 @@
<v-toolbar-title class="ml-0" style="position: relative; left: -10px">
<!-- Uncomment when pinning is in place and add style="position: relative; left: -10px" to the element above :) -->
<v-btn
v-tooltip="'Pin this stream - it will be saved to this file.'"
v-tooltip="`Pin this ${streamText.toLowerCase()} - it will be saved to this file.`"
icon
x-small
@click="toggleSavedStream"
@@ -66,7 +66,11 @@
<template #activator="{ on, attrs }">
<v-slide-x-transition>
<div v-show="hover">
<create-branch-dialog :stream-name="stream.name" :stream-id="streamId"/>
<create-branch-dialog
:is-f-e2="preferences && preferences.user && preferences.user.fe2"
:stream-name="stream.name"
:stream-id="streamId"
/>
</div>
</v-slide-x-transition>
<v-chip v-if="stream.branches" small v-bind="attrs" class="mr-1" v-on="on">
@@ -133,7 +137,7 @@
hide-details
dense
flat
placeholder="Write your commit message here"
:placeholder="`Write your ${commitText.toLowerCase()} message here`"
/>
</div>
</v-slide-y-transition>
@@ -204,7 +208,11 @@ export default {
commitId: 'latest',
commitMessage: null,
invalid: false,
diffing: false
diffing: false,
streamText: '',
branchText: '',
commitText: '',
preferences: {}
}
},
apollo: {
@@ -286,6 +294,16 @@ export default {
}
},
mounted() {
bus.$on('update-preferences', async (preferences) => {
const pref = JSON.parse(preferences)
this.preferences = pref
this.streamText = pref.user.fe2 ? 'Project' : 'Stream'
this.branchText = pref.user.fe2 ? 'Model' : 'Branch'
this.commitText = pref.user.fe2 ? 'Version' : 'Commit'
})
// Collect preferences to render UI according to it
sketchup.exec({name: "collect_preferences", data: {}})
bus.$on(`deactivate-diffing-${this.streamId}`, () => {
this.diffing = false
})
@@ -3,7 +3,7 @@
<v-dialog v-model="showCreateBranch">
<template #activator="{ on: dialog, attrs }">
<v-btn
v-tooltip="'Create Branch'"
v-tooltip="`Create ${isFE2 ? 'Model' : 'Branch'}`"
icon x-small class="ml-0 mr-1"
v-bind="attrs"
v-on="{...dialog}"
@@ -15,10 +15,10 @@
</template>
<v-card>
<v-card-title class="text-h5 mb-1">
Create a New Branch
{{ `Create a New ${isFE2 ? 'Model' : 'Branch'}` }}
</v-card-title>
<v-card-subtitle class="py-0 my-0 font-italic">
under {{ streamName }} stream
{{ `under ${streamName} ${isFE2 ? 'project' : 'stream'}` }}
</v-card-subtitle>
<v-container class="px-6" pb-0>
<v-text-field
@@ -27,7 +27,7 @@
hide-details
dense
flat
placeholder="Branch Name"
:placeholder="`${isFE2 ? 'Model' : 'Branch'} Name`"
/>
<v-text-field
v-model="description"
@@ -75,6 +75,14 @@ export default {
streamName: {
type: String,
default: null
},
isFE2: {
type: Boolean,
default: false
},
branchTooltipName: {
type: String,
default: ''
}
},
data() {
@@ -16,13 +16,14 @@
left
>
mdi-plus-circle
</v-icon>Create New Stream
</v-icon>
{{ `Create New ${isFE2 ? 'Project': 'Stream'}` }}
</v-btn>
</template>
<v-card>
<v-card-title class="text-h5">
Create a New Stream
{{ `Create a New ${isFE2 ? 'Project' : 'Stream'}` }}
</v-card-title>
<v-container class="px-6" pb-0>
<!--
@@ -55,7 +56,7 @@
hide-details
dense
flat
placeholder="Stream Name (Optional)"
:placeholder="`${isFE2 ? 'Project' : 'Stream'} Name (Optional)`"
/>
<v-text-field
v-model="description"
@@ -67,7 +68,7 @@
/>
<v-switch
v-model="privateStream"
:label="'Private Stream'"
:label="`Private ${isFE2 ? 'Project' : 'Stream'}`"
></v-switch>
</v-container>
@@ -169,6 +170,10 @@ export default {
serverUrl: {
type: String,
default: null
},
isFE2: {
type: Boolean,
default: false
}
},
data() {
@@ -200,7 +205,7 @@ export default {
},
async getStream(){
try {
const streamWrapper = new StreamWrapper(this.createStreamByIdText, this.accountId, this.serverUrl)
const streamWrapper = new StreamWrapper(this.createStreamByIdText, this.accountId, this.serverUrl, this.isFE2)
let res = await this.$apollo.query({
query: gql`
query Stream($id: String!){
+23 -1
View File
@@ -16,14 +16,25 @@
Settings
</v-card-title>
<v-container class="px-6" pb-0>
<!-- Switch Theme -->
<!-- User preferences -->
<div class="sm1 mt-3">User Preferences</div>
<v-divider class="mb-2"/>
<!-- Switch Theme -->
<v-btn icon small class="mx-1" @click="switchTheme">
<v-icon>mdi-theme-light-dark</v-icon>
</v-btn>
<span>Color Mode</span>
<!-- FE2 -->
<v-switch
:input-value="fe2"
class="pt-3 mt-n2 mb-n7"
:label="'FE2'"
@change="fe2Handler"
/>
<!-- Register objects as Speckle Entity on send/receive -->
<v-switch
:input-value="registerSpeckleEntity"
@@ -133,6 +144,7 @@ export default {
includeComponentAttributes: this.preferences.model.include_component_entity_attributes,
mergeCoplanarFaces: this.preferences.model.merge_coplanar_faces,
diffing: this.preferences.user.diffing,
fe2: this.preferences.user.fe2,
registerSpeckleEntity: this.preferences.user.register_speckle_entity
}
},
@@ -147,6 +159,7 @@ export default {
this.includeComponentAttributes = newValue.model.include_component_entity_attributes
this.mergeCoplanarFaces = newValue.model.merge_coplanar_faces
this.diffing = newValue.user.diffing
this.fe2 = newValue.user.fe2
this.registerSpeckleEntity = newValue.user.register_speckle_entity
},
deep: true,
@@ -161,6 +174,15 @@ export default {
}
},
methods: {
fe2Handler(newValue){
this.fe2 = !!newValue
sketchup.exec({
name: "user_preferences_updated",
data: {preference_hash: "configSketchup", preference: "fe2", value: this.fe2}
})
this.$mixpanel.track('Connector Action', { name: 'Toggle FE2' })
sketchup.exec({name: "collect_preferences", data: {}})
},
diffingHandler(newValue){
this.diffing = !!newValue
sketchup.exec({
+9 -5
View File
@@ -1,7 +1,11 @@
require('url')
export class StreamWrapper {
constructor(streamIdOrUrl, accountId, serverUrl) {
constructor(streamIdOrUrl, accountId, serverUrl, isFE2) {
this.isFE2 = isFE2
this.streamsKey = this.isFE2 ? 'projects/': 'streams/'
this.branchesKey = this.isFE2 ? 'models/': 'branches/'
this.commitsKey = this.isFE2 ? 'versions/': 'commits/'
this.originalOutput = streamIdOrUrl
try {
this.streamWrapperFromUrl(streamIdOrUrl)
@@ -18,7 +22,7 @@ export class StreamWrapper {
this.segments = this.url.pathname.split('/').map((segment) => segment + '/')
this.serverUrl = this.url.origin
if (this.segments.length >= 4 && this.segments[3]?.toLowerCase() === "branches/"){
if (this.segments.length >= 4 && this.segments[3]?.toLowerCase() === this.branchesKey){
this.streamId = this.segments[2].replace("/", "")
if (this.segments.length > 5)
{
@@ -32,7 +36,7 @@ export class StreamWrapper {
} else {
switch (this.segments.length){
case 3: // ie http://speckle.server/streams/8fecc9aa6d
if (this.segments[1].toLowerCase() === "streams/")
if (this.segments[1].toLowerCase() === this.streamsKey)
this.streamId = this.segments[2].replace("/", "");
else
throw new Error(`Cannot parse ${this.originalOutput} into a stream wrapper class`);
@@ -48,7 +52,7 @@ export class StreamWrapper {
break;
case 5: // ie http://speckle.server/streams/8fecc9aa6d/commits/76a23d7179
switch (this.segments[3].toLowerCase()){
case "commits/":
case this.commitsKey:
this.streamId = this.segments[2].replace("/", "");
this.commitId = this.segments[4].replace("/", "");
break;
@@ -57,7 +61,7 @@ export class StreamWrapper {
this.branchName = this.segments[3].replace("/", "");
this.commitId = this.segments[4].replace("/", "");
break;
case "branches/":
case this.branchesKey:
this.streamId = this.segments[2].replace("/", "");
this.branchName = this.segments[4].replace("/", "");
break;
+11 -2
View File
@@ -27,7 +27,7 @@
elevation="0"
@click="showMore"
>
More Streams
{{ `More ${streamsText}` }}
</v-btn>
</div>
</div>
@@ -64,7 +64,8 @@ export default {
data() {
return {
showMoreEnabled: true,
savedStreams: []
savedStreams: [],
streamsText: ''
}
},
computed: {
@@ -80,6 +81,14 @@ export default {
}
},
mounted() {
bus.$on('update-preferences', async (preferences) => {
const pref = JSON.parse(preferences)
this.streamsText = pref.user.fe2 ? 'Projects' : 'Streams'
})
// Collect preferences to render UI according to it
sketchup.exec({name: "collect_preferences", data: {}})
bus.$on("deactivate-diffing-except", (exceptedStreamId) => {
this.savedStreams.forEach((streamId) => {
if (streamId !== exceptedStreamId){