Compare commits

...

44 Commits

Author SHA1 Message Date
oguzhankoral 18caf16621 WIP 2024-02-12 18:09:31 +03:00
oguzhankoral c781534950 Trigger sketchup only saved stream not exists 2024-02-12 11:52:18 +03:00
oguzhankoral 886ede61e1 Enrich selected account info and fix same user id issue 2024-02-12 11:31:02 +03:00
oguzhankoral 5a42ea39ce Disable reload 2024-02-12 11:30:39 +03:00
Oğuzhan Koral ab7397bf55 Feat (FE2): CNX-8702 fe2 urls (#320)
* Recreate ApolloProvider whenever account switches

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

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

* Filter family types for 'Family Instance'

* Adjust available mapping methods according to selection

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

* Implement family instance mapping

* Pass group selection info if only group selected

* Exclude definitions from conversion escape

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

* Add direct shape option to components back

* Calculate rotation of family instance

* Fix transformation matrix

* Use insertion point for instances

* Set inputs for mapped definition
2024-01-08 18:40:55 +03:00
Oğuzhan Koral 503fb4d246 Feat (GIS): support line element 2023-12-01 01:46:53 +03:00
oguzhankoral 2befefa752 Extract utils for line and polygon element 2023-11-30 15:07:31 +03:00
oguzhankoral 85e64c5076 Add line element support 2023-11-30 00:36:47 +03:00
Oğuzhan Koral 60523dc994 Fix (Collections): Support gis collections and none unit 2023-11-29 16:06:58 +03:00
oguzhankoral 6d780bf350 Rename GisLayerCollection as generic 2023-11-29 16:06:37 +03:00
oguzhankoral eec02a1f84 Support none unit 2023-11-29 15:58:26 +03:00
oguzhankoral ace2fe6fe3 WIP: Convert from 'meters' to 'm' 2023-11-29 13:26:53 +03:00
oguzhankoral 188794af8d Add support for vector layer 2023-11-29 13:26:53 +03:00
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
48 changed files with 1364 additions and 292 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\/.*/
@@ -0,0 +1,22 @@
# frozen_string_literal: true
require_relative 'action'
require_relative '../mapper/category/revit_category'
require_relative '../mapper/category/revit_family_category'
module SpeckleConnector
module Actions
# Collects mapper selection info.
class MapperInitialized < Action
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def self.update_state(state, _data)
init_parameters = {
categories: Mapper::Category::RevitCategory.to_a,
familyCategories: Mapper::Category::RevitFamilyCategory.to_a
}.freeze
state.with_mapper_init_queue(init_parameters)
end
end
end
end
@@ -2,6 +2,7 @@
require_relative 'action'
require_relative '../mapper/category/revit_category'
require_relative '../mapper/category/revit_family_category'
require_relative '../sketchup_model/reader/mapper_reader'
require_relative '../sketchup_model/reader/speckle_entities_reader'
require_relative '../sketchup_model/dictionary/speckle_entity_dictionary_handler'
@@ -35,6 +36,7 @@ module SpeckleConnector
end
def get_mapping_info(state, selection)
source_exist = !state.speckle_state.speckle_mapper_state.mapper_source.nil?
selection = filter_out_levels(selection)
grouped_by_type = group_by_type(selection)
@@ -45,10 +47,23 @@ module SpeckleConnector
# Return Direct Shape itself if multiple kinds of element are selected like Edge and Face.
# OR single type is equal to only direct shape supports.
return multiple_supported_selection_info(selection) if supported_entity_count > 1
# FIXME: Distinguish selection info according to selection elegantly!!!
if grouped_by_type.keys.first == Sketchup::ComponentInstance
return component_selection_info(selection, source_exist)
end
return group_selection_info(selection) if grouped_by_type.keys.first == Sketchup::Group
if supported_entity_count > 1 ||
(supported_entity_count == 1 &&
MAPPER_DIRECT_SHAPE_SUPPORTED_ENTITY_TYPES.include?(grouped_by_type.keys.first))
return direct_shape_selection_info(selection)
if source_exist
return direct_shape_selection_info_with_source(selection, [])
else
return direct_shape_selection_info(selection, source_exist)
end
end
# Only single type selections remained after this point.
@@ -73,29 +88,54 @@ module SpeckleConnector
EMPTY_SELECTION = {
selection: [],
mappingMethods: [],
categories: []
mappingMethods: []
}.freeze
def direct_shape_selection_info(selection)
def multiple_supported_selection_info(selection)
{
selection: SketchupModel::Reader::MapperReader.entities_schema_details(selection),
mappingMethods: ['Direct Shape'],
categories: Mapper::Category::RevitCategory.to_a
mappingMethods: ['Direct Shape']
}.freeze
end
def component_selection_info(selection, source_exist)
if source_exist
{
selection: SketchupModel::Reader::MapperReader.entities_schema_details(selection),
mappingMethods: ['Direct Shape', 'New Revit Family', 'Family Instance']
}.freeze
else
{
selection: SketchupModel::Reader::MapperReader.entities_schema_details(selection),
mappingMethods: ['Direct Shape', 'New Revit Family']
}.freeze
end
end
def group_selection_info(selection)
{
selection: SketchupModel::Reader::MapperReader.entities_schema_details(selection),
mappingMethods: ['Direct Shape']
}.freeze
end
def direct_shape_selection_info(selection, source_exist)
methods = ['Direct Shape', 'New Revit Family']
methods.append('Family Instance') if source_exist
{
selection: SketchupModel::Reader::MapperReader.entities_schema_details(selection),
mappingMethods: methods
}.freeze
end
def direct_shape_selection_info_with_default(selection, methods)
{
selection: SketchupModel::Reader::MapperReader.entities_schema_details(selection),
mappingMethods: ['Direct Shape'] + methods,
categories: Mapper::Category::RevitCategory.to_a
mappingMethods: ['Direct Shape'] + methods
}.freeze
end
def direct_shape_selection_info_with_source(state, filtered_selection, methods)
types = state.speckle_state.speckle_mapper_state.mapper_source.types
levels = state.speckle_state.speckle_mapper_state.mapper_source.levels
def direct_shape_selection_info_with_source(filtered_selection, methods)
instances = @selection.grep(Sketchup::ComponentInstance)
selected_level = instances.find do |i|
DICTIONARY::SpeckleEntityDictionaryHandler
@@ -109,8 +149,6 @@ module SpeckleConnector
selection: READER::MapperReader.entities_schema_details(filtered_selection),
mappingMethods: ['Direct Shape'] + methods,
categories: Mapper::Category::RevitCategory.to_a,
types: types,
levels: levels,
selectedLevelName: selected_level_name
}.freeze
end
@@ -119,13 +157,13 @@ module SpeckleConnector
def face_selection_info(state, faces)
source_exist = !state.speckle_state.speckle_mapper_state.mapper_source.nil?
grouped_by_verticality = faces.group_by { |face| face.normal.perpendicular?(VECTOR_Z) }
return direct_shape_selection_info(faces) if grouped_by_verticality.length == 2
return direct_shape_selection_info(faces, source_exist) if grouped_by_verticality.length == 2
if source_exist
if grouped_by_verticality.keys.first
direct_shape_selection_info_with_source(state, faces, ['Wall'])
direct_shape_selection_info_with_source(faces, ['Wall'])
else
direct_shape_selection_info_with_source(state, faces, ['Floor'])
direct_shape_selection_info_with_source(faces, ['Floor'])
end
else
if grouped_by_verticality.keys.first
@@ -141,7 +179,7 @@ module SpeckleConnector
if source_exist
methods = ['Column', 'Beam', 'Pipe', 'Duct']
direct_shape_selection_info_with_source(state, edges, methods)
direct_shape_selection_info_with_source(edges, methods)
else
default_methods = ['Default Column', 'Default Beam', 'Default Pipe', 'Default Duct']
direct_shape_selection_info_with_default(edges, default_methods)
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require_relative 'action'
require_relative '../constants/type_constants'
require_relative '../mapper/mapper_source'
require_relative '../speckle_objects/built_elements/revit/revit_element_type'
@@ -20,13 +21,15 @@ module SpeckleConnector
def update_state(state)
levels = convert_levels(state, @base['@Levels'])
types = convert_types(@base['@Types'])
family_instances = convert_family_instance_types(@base['@Types'])
mapper_source = Mapper::MapperSource.new(@stream_id, @commit_id, levels, types)
new_speckle_state = state.speckle_state.with_mapper_source(mapper_source)
state = state.with_speckle_state(new_speckle_state)
state.with_add_queue('mapperSourceUpdated', @stream_id, [
{ is_string: false, val: levels.to_json },
{ is_string: false, val: types.to_json }
{ is_string: false, val: types.to_json },
{ is_string: false, val: family_instances.to_json }
])
end
@@ -43,6 +46,27 @@ module SpeckleConnector
end.compact.to_h
end
def convert_family_instance_types(types)
family_instance_types = {}
types.each do |type, type_elements|
next if type_elements.nil? || !type_elements.is_a?(Array) || type == '__closure'
# skip type if there is no any revit symbol element type
symbol_element_types = type_elements.select do |t|
t['speckle_type'] == OBJECTS_BUILTELEMENTS_REVIT_REVITSYMBOLELEMENTTYPE &&
t['placementType'] == 'OneLevelBased'
end
next if symbol_element_types.empty?
elements = type_elements.map do |type_element|
SpeckleObjects::BuiltElements::Revit::RevitElementType.to_native(type_element)
end
elements = elements.group_by { |e| e[:family] }
family_instance_types.merge!(elements)
end
family_instance_types
end
def convert_levels(state, levels)
levels.collect do |level|
SpeckleObjects::BuiltElements::Level.to_native(state, level, @stream_id)
@@ -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}. ===="
@@ -0,0 +1,27 @@
# frozen_string_literal: true
require_relative 'command'
require_relative '../states/initial_state'
require_relative '../ui/vue_view'
require_relative '../actions/initialize_speckle'
require_relative '../observers/factory'
module SpeckleConnector
module Commands
# Command to reset Speckle UI window location onto center of SketchUp window.
class ResetWindowLocation < Command
private
def _run
app = self.app
vue_view = app.ui_controller.user_interfaces[Ui::SPECKLE_UI_ID]
if vue_view
vue_view.dialog.reset_dialog_location
else
puts "Speckle UI didn't initialized!"
end
end
end
end
end
@@ -3,6 +3,7 @@
require_relative 'menu_command_handler'
require_relative 'action_command'
require_relative 'initialize_speckle'
require_relative 'reset_window_location'
require_relative '../actions/one_click_send'
module SpeckleConnector
@@ -10,6 +11,7 @@ module SpeckleConnector
# Speckle menu commands that adds them to Sketchup menu and toolbar.
class SpeckleMenuCommands
CMD_INITIALIZE_SPECKLE = :initialize_speckle
CMD_RESET_WINDOW_LOCATION_SPECKLE = :reset_window_location_speckle
CMD_SEND_TO_SPECKLE = :send_to_speckle
CMD_RECEIVE_FROM_SPECKLE = :receive_from_speckle
@@ -26,6 +28,9 @@ module SpeckleConnector
commands.add_to_menu!(CMD_INITIALIZE_SPECKLE, speckle_menu)
commands.add_to_toolbar!(CMD_INITIALIZE_SPECKLE, speckle_toolbar)
commands[CMD_RESET_WINDOW_LOCATION_SPECKLE] = reset_window_location_command(app)
commands.add_to_menu!(CMD_RESET_WINDOW_LOCATION_SPECKLE, speckle_menu)
# commands[CMD_SEND_TO_SPECKLE] = send_command(app)
# commands.add_to_menu!(CMD_SEND_TO_SPECKLE, speckle_menu)
# commands.add_to_toolbar!(CMD_SEND_TO_SPECKLE, speckle_toolbar)
@@ -42,6 +47,17 @@ module SpeckleConnector
cmd
end
def self.reset_window_location_command(app)
cmd = MenuCommandHandler.sketchup_command(
ResetWindowLocation.new(app), 'Reset Window Location'
)
cmd.tooltip = 'Bring Speckle window onto center of SketchUp window'
cmd.status_bar_text = 'Bring Speckle window onto center of SketchUp window'
cmd.small_icon = '../../img/s2logo.png'
cmd.large_icon = '../../img/s2logo.png'
cmd
end
def self.send_command(app)
cmd = MenuCommandHandler.sketchup_command(
ActionCommand.new(app, Actions::OneClickSend), 'Send to Speckle'
@@ -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,6 +4,7 @@ module SpeckleConnector
BASE_OBJECT = 'Base'
OBJECTS_GIS_POLYGONELEMENT = 'Objects.GIS.PolygonElement'
OBJECTS_GIS_LINEELEMENT = 'Objects.GIS.LineElement'
OBJECTS_BUILTELEMENTS_VIEW3D = 'Objects.BuiltElements.View:Objects.BuiltElements.View3D'
OBJECTS_BUILTELEMENTS_NETWORK = 'Objects.BuiltElements.Network'
@@ -13,8 +14,10 @@ module SpeckleConnector
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'
@@ -33,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)
+19 -1
View File
@@ -4,6 +4,7 @@ require_relative 'converter'
require_relative '../constants/type_constants'
require_relative '../speckle_entities/speckle_entity'
require_relative '../speckle_objects/gis/polygon_element'
require_relative '../speckle_objects/gis/line_element'
require_relative '../speckle_objects/other/transform'
require_relative '../speckle_objects/other/render_material'
require_relative '../speckle_objects/other/block_definition'
@@ -19,6 +20,7 @@ require_relative '../speckle_objects/geometry/mesh'
require_relative '../speckle_objects/built_elements/view3d'
require_relative '../speckle_objects/built_elements/network'
require_relative '../speckle_objects/speckle/core/models/collection'
require_relative '../speckle_objects/speckle/core/models/gis_layer_collection'
require_relative '../sketchup_model/dictionary/speckle_entity_dictionary_handler'
module SpeckleConnector
@@ -32,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
@@ -56,11 +61,14 @@ module SpeckleConnector
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[
@@ -77,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
@@ -294,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
@@ -309,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)
@@ -64,19 +64,14 @@ module SpeckleConnector
def convert(entity, preferences, speckle_state, parent = :base)
convert = method(:convert)
unless SketchupModel::Reader::MapperReader.mapped_with_schema?(entity)
unless SketchupModel::Reader::MapperReader.mapped_with_schema?(entity) &&
!entity.is_a?(Sketchup::ComponentDefinition)
return from_native_to_speckle(entity, preferences, speckle_state, parent, &convert)
end
return speckle_state, nil
end
def from_mapped_to_speckle(entity, path, preferences)
direct_shape = SpeckleObjects::BuiltElements::Revit::DirectShape
.from_entity(speckle_state, entity, path, @units, preferences)
return [direct_shape, [entity]]
end
# rubocop:disable Metrics/MethodLength
def from_native_to_speckle(entity, preferences, speckle_state, parent, &convert)
if entity.is_a?(Sketchup::Edge)
Binary file not shown.
@@ -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
+2 -1
View File
@@ -4,6 +4,7 @@ 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'
@@ -31,7 +32,7 @@ module SpeckleConnector
mapped_selection
end
def self.to_speckle(speckle_state, entity, units, global_transformation: nil)
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?
@@ -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
@@ -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)
@@ -89,7 +89,7 @@ module SpeckleConnector
mapped_selection
end
def self.from_entity(speckle_state, 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(speckle_state, 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(speckle_state, 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]
@@ -126,7 +126,7 @@ module SpeckleConnector
else
mesh = Geometry::Mesh.from_face(
speckle_state: speckle_state,
face: face, units: units, model_preferences: preferences[:model],
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
@@ -36,6 +36,27 @@ module SpeckleConnector
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)
@@ -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
@@ -121,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
@@ -154,7 +154,7 @@ module SpeckleConnector
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 = Mapper.to_speckle(speckle_state, face, units, global_transformation: global_transform)
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,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
@@ -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
@@ -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
@@ -83,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
@@ -27,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,
@@ -60,29 +81,17 @@ module SpeckleConnector
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_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
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.convert_mapped_entity(speckle_state, entity_with_path, preferences, units)
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
if !method.nil? && (method.include?('Floor') || method.include?('Wall')) && entity.is_a?(Sketchup::Face)
global_transformation = QUERY::Entity.global_transformation(entity, path)
@@ -92,8 +101,19 @@ module SpeckleConnector
return [floor, [entity]]
end
direct_shape = DIRECT_SHAPE.from_entity(speckle_state, entity, path, units, preferences)
return [direct_shape, [entity]]
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
@@ -75,6 +75,12 @@ module SpeckleConnector
with(:@message_queue => new_queue)
end
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)
+5
View File
@@ -50,6 +50,11 @@ module SpeckleConnector
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)
+4
View File
@@ -57,6 +57,10 @@ module SpeckleConnector
html_dialog.execute_script(data)
end
def reset_dialog_location
html_dialog.center
end
private
# @return [UI::HtmlDialog] the Sketchup interface to dialog
+3 -1
View File
@@ -18,6 +18,7 @@ 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'
@@ -93,7 +94,8 @@ module SpeckleConnector
select_mappings_from_table: Commands::ActionCommand.new(@app, Actions::SelectMappingsFromTable),
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)
clear_mapper_source: Commands::ActionCommand.new(@app, Actions::ClearMapperSource),
mapper_initialized: Commands::ActionCommand.new(@app, Actions::MapperInitialized)
}.freeze
end
# rubocop:enable Metrics/MethodLength
+44 -14
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"}}
@@ -48,21 +48,25 @@
<v-img v-if="user.avatar" :src="user.avatar" />
<v-img v-else :src="`https://robohash.org/` + user.id + `.png?size=40x40`" />
</v-avatar>
<div>
<b>{{ user.name }}</b>
</div>
<div class="caption">
{{ user.company }}
<br />
{{ user.bio ? 'Bio: ' + user.bio : '' }}
</div>
<div>
<b>{{ user.name }}</b>
<br />
<b>{{ user.email }}</b>
</div>
<div class="caption">
<b>{{ serverInfo.canonicalUrl }}</b>
</div>
</v-card-text>
<v-card-text v-if="accounts()">
<v-divider class="my-3" />
<div v-for="account in accounts()" :key="account.id">
<v-btn
v-if="account.userInfo.id != user.id"
v-if="account.id != activeAccount().id"
rounded
large
class="my-1 elevation-0"
@@ -88,7 +92,7 @@
<v-text-field
v-model="streamSearchQuery"
prepend-inner-icon="mdi-magnify"
label="Search streams"
:label="searchText"
background-color="background"
hide-details
clearable
@@ -101,11 +105,12 @@
</v-container>
<create-stream-dialog
v-if="accounts().length !== 0"
:is-f-e2-terms="preferences && preferences.user && preferences.user.fe2"
:account-id="activeAccount().userInfo.id"
:server-url="activeAccount().serverInfo.url"
/>
<v-container v-if="accounts().length !== 0" fluid>
<router-view :stream-search-query="streamSearchQuery" />
<router-view :stream-search-query="streamSearchQuery"/>
</v-container>
<v-container v-else>
<login/>
@@ -115,7 +120,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>
@@ -127,6 +132,7 @@
/*global sketchup*/
import { bus } from './main'
import userQuery from './graphql/user.gql'
import serverInfoQuery from './graphql/serverInfo.gql'
import { onLogin } from './vue-apollo'
import Login from "@/views/Login";
@@ -146,7 +152,12 @@ global.loadAccounts = function (accounts) {
let uuid = localStorage.getItem('uuid')
if (accounts.length !== 0){
if (uuid) {
global.setSelectedAccount(accounts.find((acct) => acct['userInfo']['id'] === uuid))
var account = accounts.find((acct) => acct['userInfo']['id'] === uuid)
if (account){
global.setSelectedAccount(account)
}else{
global.setSelectedAccount(accounts.find((acct) => acct['isDefault']))
}
} else {
global.setSelectedAccount(accounts.find((acct) => acct['isDefault']))
}
@@ -158,6 +169,7 @@ global.setSelectedAccount = function (account) {
localStorage.setItem('serverUrl', account['serverInfo']['url'])
localStorage.setItem('SpeckleSketchup.AuthToken', account['token'])
localStorage.setItem('uuid', account['userInfo']['id'])
localStorage.setItem('frontend2', account['serverInfo']['frontend2'])
bus.$emit('selected-account-reloaded')
}
@@ -174,7 +186,7 @@ export default {
size: {
type: Number,
default: 42
},
}
},
data() {
return {
@@ -183,7 +195,11 @@ export default {
createStreamByIdDialog: false,
createStreamByIdText: "",
preferences: {},
tab: "streams"
tab: "streams",
searchText: '',
streamsText: 'Streams',
streamText: 'Stream',
branchText: 'Branch'
}
},
computed: {
@@ -194,6 +210,9 @@ export default {
apollo: {
user: {
query: userQuery
},
serverInfo: {
query: serverInfoQuery
}
},
mounted() {
@@ -209,8 +228,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
})
@@ -233,6 +255,14 @@ export default {
switchAccount(account) {
this.$mixpanel.track('Connector Action', { name: 'Account Select' })
global.setSelectedAccount(account)
// Force pushes to reload page to create ApolloClient from scratch
// setTimeout(() => {
// // timeout to wait a bit for potential sketchup.exec in the mean time calls
// location.reload()
// }, 200);
this.$apollo.queries.user.refetch()
this.$apollo.queries.serverInfo.refetch()
},
requestRefresh() {
sketchup.exec({name: 'reload_accounts', data: {}})
+96 -47
View File
@@ -89,7 +89,7 @@
</v-container>
</v-expansion-panel-header>
<v-expansion-panel-content>
<mapper-source :source-state="this.sourceState"/>
<mapper-source :stream-text="streamText" :branch-text="branchText" :source-state="this.sourceState"/>
</v-expansion-panel-content>
</v-expansion-panel>
@@ -171,7 +171,7 @@
class="pt-0"
label="Family"
:disabled="!entitySelected"
:items="families"
:items="inputFamilies"
density="compact"
clearable
@change="onSelectedFamilyChange"
@@ -208,7 +208,7 @@
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"
@@ -282,14 +282,19 @@ 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) {
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')
}
@@ -300,6 +305,16 @@ 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'),
@@ -333,7 +348,7 @@ export default {
entitySelected: false,
selectedEntityCount: 0,
selectedEntities: [],
allFamilyTypes: {},
familyTypes: [],
lastSelectedEntity: null,
@@ -345,10 +360,17 @@ export default {
name: "",
availableMethods: [],
availableCategories: [],
families: [],
allTypes: {},
categories: [],
familyCategories: [],
inputCategories: [],
inputFamilies: [],
inputFamilyTypes: {},
// comes from source
types: {},
levels: [],
familyInstanceTypes: {}, // comes from source as filtered
mappedEntityCount: 0,
mappedEntities: [],
@@ -521,6 +543,14 @@ export default {
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
@@ -535,52 +565,63 @@ export default {
}
},
getTypesFromSelectedFamily(){
this.familyTypes = this.allFamilyTypes[this.selectedFamily]
this.selectedFamilyType = this.familyTypes[0].type
if (this.selectedFamily === null || this.selectedFamily === undefined){
this.selectedFamily = this.families[0]
}
if (this.familyTypes === null ||this.familyTypes === undefined){
this.familyTypes = this.allFamilyTypes[this.selectedFamily]
}
if (this.selectedFamilyType === null || this.selectedFamilyType === undefined){
this.selectedFamilyType = this.familyTypes[0].type
// 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.families = Object.keys(this.allTypes['Floors']);
this.allFamilyTypes = this.allTypes['Floors']
this.inputFamilies = Object.keys(this.types['Floors']);
this.inputFamilyTypes = this.types['Floors']
break;
case 'Wall':
this.families = Object.keys(this.allTypes['Walls']);
this.allFamilyTypes = this.allTypes['Walls']
this.inputFamilies = Object.keys(this.types['Walls']);
this.inputFamilyTypes = this.types['Walls']
break;
case 'Column':
this.families = Object.keys(this.allTypes['Columns']);
this.allFamilyTypes = this.allTypes['Columns']
this.inputFamilies = Object.keys(this.types['Columns']);
this.inputFamilyTypes = this.types['Columns']
break;
case 'Beam':
this.families = Object.keys(this.allTypes['Beams']);
this.allFamilyTypes = this.allTypes['Beams']
this.inputFamilies = Object.keys(this.types['Beams']);
this.inputFamilyTypes = this.types['Beams']
break;
case 'Pipe':
this.families = Object.keys(this.allTypes['Piping System']);
this.allFamilyTypes = this.allTypes['Piping System']
this.inputFamilies = Object.keys(this.types['Piping System']);
this.inputFamilyTypes = this.types['Piping System']
break;
case 'Duct':
this.families = Object.keys(this.allTypes['Duct System']);
this.allFamilyTypes = this.allTypes['Duct System']
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.selectedFamily === null || this.selectedFamily === undefined){
this.selectedFamily = this.families[0]
}
if (this.selectedLevel === null || this.selectedLevel === undefined){
this.selectedLevel = this.levels[0].name
if (this.selectedMethod){
if (this.selectedFamily === null || this.selectedFamily === undefined){
this.selectedFamily = this.inputFamilies[0]
}
if (this.selectedLevel === null || this.selectedLevel === undefined){
this.selectedLevel = this.levels[0].name
}
}
},
hideOptionalMappingInputs(){
@@ -598,7 +639,6 @@ export default {
},
clearInputs(){
this.availableMethods = []
this.availableCategories = []
this.selectedEntities = []
this.selectionTableData = []
this.selectedEntityCount = 0
@@ -610,8 +650,6 @@ export default {
this.selectedLevel = null
this.selectedFamily = null
this.selectedFamilyType = null
this.allTypes = null
this.familyTypes = null
},
getSelectionTableData(){
let groupByClass = groupBy('entityType')
@@ -673,6 +711,12 @@ 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()
}
}
// Otherwise set entity mappings.
@@ -684,7 +728,6 @@ export default {
}else{
this.name = this.lastSelectedEntity['entityName']
}
console.log("entity not mapped")
this.updateMappingInputs()
this.getFamiliesFromSelectedMethod()
this.getTypesFromSelectedFamily()
@@ -696,7 +739,6 @@ export default {
this.name = this.lastSelectedEntity['schema']['name']
}
this.selectedMethod = this.lastSelectedEntity['schema']['method']
console.log("entity is mapped")
this.updateMappingInputs()
this.selectedFamily = this.lastSelectedEntity['schema']['family']
this.selectedCategory = this.lastSelectedEntity['schema']['category']
@@ -804,18 +846,11 @@ export default {
this.selectedFamily = null
this.selectedFamilyType = null
this.selectedLevel = null
this.familyTypes = null
this.levels = null
this.availableMethods = null
this.availableCategories = null
this.allTypes = null
},
getDataFromSelection(selectionParameters){
this.availableMethods = selectionParameters.mappingMethods
this.availableCategories = selectionParameters.categories
this.selectedEntities = selectionParameters.selection
this.allTypes = selectionParameters.types
this.levels = selectionParameters.levels
this.selectedLevel = selectionParameters.selectedLevelName
},
updateStatesFromSelectionData(){
@@ -828,8 +863,22 @@ export default {
}
},
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)
+10 -7
View File
@@ -2,7 +2,7 @@
<v-container class="pa-0">
<v-autocomplete
v-model="sourceStreamId"
label="Stream"
:label="streamText"
:items="allStreamsList"
item-text="name"
item-value="id"
@@ -12,7 +12,7 @@
<v-autocomplete
v-model="sourceBranchId"
class="pt-0 mb-n5"
label="Branch"
:label="branchText"
:items="allBranchesList"
:disabled="sourceStreamId === null"
item-text="name"
@@ -63,6 +63,14 @@ export default {
props: {
streamSearchQuery: { type: String, default: null },
sourceState: { type: String, default: 'Not Set' },
streamText: {
type: String,
default: ''
},
branchText: {
type: String,
default: ''
}
},
data() {
return {
@@ -211,9 +219,4 @@ export default {
}
}
}
</script>
<style scoped>
</style>
+110 -83
View File
@@ -1,10 +1,6 @@
<template>
<v-card
v-if="stream"
:class="`mb-3 rounded-lg grey ${$vuetify.theme.dark ? 'darken-4' : 'lighten-4'}`"
@mouseenter="hover = true"
@mouseleave="hover = false"
>
<v-card v-if="stream" :class="`mb-3 rounded-lg grey ${$vuetify.theme.dark ? 'darken-4' : 'lighten-4'}`"
@mouseenter="hover = true" @mouseleave="hover = false">
<v-toolbar flat height="70" :color="getColor(invalid)">
<v-btn v-tooptip="''" icon small outlined class="delta-btn" v-if="invalid" @click="activateDiffing">
<v-icon v-if="!diffing" class="toggleUpDown" :class='{ "rotate": diffing }' small>mdi-eye-off-outline</v-icon>
@@ -12,12 +8,8 @@
</v-btn>
<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.'"
icon
x-small
@click="toggleSavedStream"
>
<v-btn v-tooltip="`Pin this ${streamText.toLowerCase()} - it will be saved to this file.`" icon x-small
@click="toggleSavedStream">
<v-icon v-if="saved" x-small>mdi-pin</v-icon>
<v-icon v-else x-small>mdi-pin-outline</v-icon>
</v-btn>
@@ -29,24 +21,12 @@
<v-btn v-tooltip="'View online'" icon small class="mr-3" @click="openInWeb">
<v-icon small>mdi-open-in-new</v-icon>
</v-btn>
<v-btn
v-tooltip="'Send'"
icon
class="mr-3 elevation-2"
:loading="loadingSend"
@click="send"
>
<v-btn v-tooltip="'Send'" icon class="mr-3 elevation-2" :loading="loadingSend" @click="send">
<!-- <v-icon>mdi-upload</v-icon> -->
<v-img v-if="$vuetify.theme.dark" src="@/assets/SenderWhite.png" max-width="30" />
<v-img v-else src="@/assets/Sender.png" max-width="30" />
</v-btn>
<v-btn
v-tooltip="'Receive'"
icon
class="elevation-2"
:loading="loadingReceive"
@click="receive"
>
<v-btn v-tooltip="'Receive'" icon class="elevation-2" :loading="loadingReceive" @click="receive">
<!-- <v-icon>mdi-download</v-icon> -->
<v-img v-if="$vuetify.theme.dark" src="@/assets/ReceiverWhite.png" max-width="30" />
<v-img v-else src="@/assets/Receiver.png" max-width="30" />
@@ -66,7 +46,8 @@
<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">
@@ -76,42 +57,30 @@
</template>
<!-- Branch list -->
<v-list dense>
<v-list-item
v-for="(branch, index) in stream.branches.items"
:key="index"
link
@click="switchBranch(branch.name)"
>
<v-list-item v-for="(branch, index) in stream.branches.items" :key="index" link
@click="switchBranch(branch.name)">
<v-list-item-title class="text-caption font-weight-regular">
<v-icon v-if="branch.name === branchName" small class="mr-1 float-left">
mdi-check
</v-icon>
<v-icon v-else small class="mr-1 float-left">mdi-source-branch</v-icon>
{{ branch.name }} ({{ branch.commits.totalCount }})
{{ branch.name }} ({{ branch && branch.commits ? branch.commits.totalCount : 0 }})
</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
<v-menu offset-y>
<template #activator="{ on, attrs }">
<v-chip v-if="stream.commits" small v-bind="attrs" v-on="on">
<v-chip v-if="stream && stream.commits" small v-bind="attrs" v-on="on">
<v-icon small class="mr-1 float-left">mdi-source-commit</v-icon>
{{ selectedBranch.commits.items.length ? commitId : 'no commits' }}
</v-chip>
</template>
<v-list dense>
<v-list-item
v-for="(commit, index) in selectedBranch.commits.items"
:key="index"
link
@click="switchCommit(commit.id)"
>
<v-list v-if="selectedBranch && selectedBranch.commits" dense>
<v-list-item v-for="(commit, index) in selectedBranch.commits.items" :key="index" link
@click="switchCommit(commit.id)">
<v-list-item-title class="text-caption font-weight-regular">
<v-icon
v-if="(commitId == 'latest' && index == 0) || commit.id == commitId"
small
class="mr-1 float-left"
>
<v-icon v-if="(commitId == 'latest' && index == 0) || commit.id == commitId" small class="mr-1 float-left">
mdi-check
</v-icon>
<v-icon v-else small class="mr-1 float-left">mdi-source-commit</v-icon>
@@ -127,24 +96,14 @@
<div class="flex-grow-1 px-4">
<v-slide-y-transition>
<div v-show="hover">
<v-text-field
v-model="commitMessage"
xxxclass="small-text-field"
hide-details
dense
flat
placeholder="Write your commit message here"
/>
<v-text-field v-model="commitMessage" xxxclass="small-text-field" hide-details dense flat
:placeholder="`Write your ${commitText.toLowerCase()} message here`" />
</div>
</v-slide-y-transition>
</div>
</v-card-text>
<v-progress-linear
v-if="(loadingSend || loadingReceive) && loadingStage"
key="progress-bar"
height="14"
indeterminate
>
<v-progress-linear v-if="(loadingSend || loadingReceive) && loadingStage" key="progress-bar" height="14"
indeterminate>
<div class="text-caption">
{{ loadingStage }}
</div>
@@ -160,8 +119,9 @@
import gql from 'graphql-tag'
import { bus } from '../main'
import streamQuery from '../graphql/stream.gql'
import projectQuery from '../graphql/project.gql'
import ObjectLoader from '@speckle/objectloader'
import {HostApplications} from '@/utils/hostApplications'
import { HostApplications } from '@/utils/hostApplications'
global.convertedFromSketchup = function (streamId, batches, commitId, totalChildrenCount) {
bus.$emit(`sketchup-objects-${streamId}`, batches, commitId, totalChildrenCount)
@@ -204,7 +164,12 @@ export default {
commitId: 'latest',
commitMessage: null,
invalid: false,
diffing: false
diffing: false,
streamText: '',
branchText: '',
commitText: '',
preferences: {},
filterBranchIds: []
}
},
apollo: {
@@ -217,6 +182,18 @@ export default {
}
}
},
project: {
prefetch: true,
query: projectQuery,
variables() {
return {
projectId: this.streamId,
filter: {
ids: this.filterBranchIds
}
}
}
},
$subscribe: {
commitCreated: {
query: gql`
@@ -286,6 +263,48 @@ 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(`set-stream-branch-commit-${this.streamId}`, async (branchId, commitId) => {
console.log(branchId, "branchId");
let res = await this.$apollo.query({
query: gql`
query Project($projectId: String!, $filter: ProjectModelsFilter) {
project(id: $projectId) {
id
name
models (filter: $filter) {
items {
id
name
versions {
items {
id
}
}
}
}
}
}
`,
variables: {
projectId: this.streamId,
filter: {
ids: [branchId]
}
}
})
console.log(res.data);
})
bus.$on(`deactivate-diffing-${this.streamId}`, () => {
this.diffing = false
})
@@ -329,11 +348,11 @@ export default {
this.$mixpanel.track('Send', { method: 'OneClick' })
})
if (this.saved) sketchup.exec({name: "notify_connected", data: {stream_id: this.streamId}})
if (this.saved) sketchup.exec({ name: "notify_connected", data: { stream_id: this.streamId } })
},
methods: {
getColor(invalid){
if(invalid){
getColor(invalid) {
if (invalid) {
return "#ffdfdf"
} else {
return ""
@@ -343,7 +362,9 @@ export default {
return new Promise((resolve) => setTimeout(resolve, ms))
},
openInWeb() {
window.open(`${localStorage.getItem('serverUrl')}/streams/${this.streamId}`)
var url = localStorage.getItem('frontend2') === "true" ? `${localStorage.getItem('serverUrl')}/projects/${this.streamId}` : `${localStorage.getItem('serverUrl')}/streams/${this.streamId}`
window.open(url)
this.$mixpanel.track('Connector Action', { name: 'Open In Web' })
},
switchBranch(branchName) {
@@ -355,22 +376,22 @@ export default {
this.$mixpanel.track('Connector Action', { name: 'Commit Switch' })
this.commitId = commitId
},
activateDiffing(){
if (this.diffing){
activateDiffing() {
if (this.diffing) {
this.diffing = false
sketchup.exec({name: "deactivate_diffing", data: {}})
sketchup.exec({ name: "deactivate_diffing", data: {} })
return
}
this.diffing = true
bus.$emit("deactivate-diffing-except", (this.streamId))
sketchup.exec({name: "activate_diffing", data: {stream_id: this.streamId}})
sketchup.exec({ name: "activate_diffing", data: { stream_id: this.streamId } })
},
toggleSavedStream() {
if (this.saved) {
sketchup.exec({name: "remove_stream", data: {stream_id: this.streamId}})
sketchup.exec({ name: "remove_stream", data: { stream_id: this.streamId } })
this.$mixpanel.track('Connector Action', { name: 'Stream Remove' })
} else {
sketchup.exec({name: "save_stream", data: {stream_id: this.streamId}})
sketchup.exec({ name: "save_stream", data: { stream_id: this.streamId } })
this.$mixpanel.track('Connector Action', { name: 'Stream Save' })
}
},
@@ -381,7 +402,7 @@ export default {
const isMultiplayer = this.selectedCommit.authorId !== selectedAccount['userInfo']['id']
const sourceApp = this.selectedCommit.sourceApplication
const sourceAppSlug = HostApplications.GetHostAppFromString(sourceApp).slug
this.$mixpanel.track('Receive', { isMultiplayer: isMultiplayer, sourceHostApp: sourceAppSlug, sourceHostAppVersion: sourceApp})
this.$mixpanel.track('Receive', { isMultiplayer: isMultiplayer, sourceHostApp: sourceAppSlug, sourceHostAppVersion: sourceApp })
const refId = this.selectedCommit?.referencedObject
if (!refId) {
this.loadingReceive = false
@@ -398,14 +419,16 @@ export default {
let rootObj = await loader.getAndConstructObject(this.updateLoadingStage)
sketchup.exec({name:"receive_objects" , data: {
sketchup.exec({
name: "receive_objects", data: {
base: rootObj,
stream_name: this.stream.name,
stream_id: this.streamId,
branch_name: this.selectedCommit.branchName,
branch_id: this.selectedCommit.id,
source_app: this.selectedCommit.sourceApplication
}})
}
})
await this.$apollo.mutate({
mutation: gql`
@@ -431,7 +454,7 @@ export default {
this.loadingStage = 'converting'
this.loadingSend = true
this.$mixpanel.track('Send')
sketchup.exec({name:"send_selection" , data: {stream_id: this.streamId}})
sketchup.exec({ name: "send_selection", data: { stream_id: this.streamId } })
console.log('>>> SpeckleSketchUp: Objects requested from SketchUp')
await this.sleep(2000)
},
@@ -461,7 +484,7 @@ export default {
}
const t1 = Date.now()
const elapsedTime = (t1-t0) / 1000
const elapsedTime = (t1 - t0) / 1000
console.log(`Upload time: ${elapsedTime} second`)
let commit = {
@@ -483,13 +506,12 @@ export default {
}
})
console.log('>>> SpeckleSketchUp: Sent to stream: ' + this.streamId, commit)
const url = localStorage.getItem('frontend2') === 'true' ? `${localStorage.getItem('serverUrl')}/projects/${this.streamId}/models/${this.selectedBranch.id}@${res.data.commitCreate}` : `${localStorage.getItem('serverUrl')}/streams/${this.streamId}/commits/${res.data.commitCreate}`
this.$eventHub.$emit('notification', {
text: 'Model selection sent!\n',
action: {
name: 'View in Web',
url: `${localStorage.getItem('serverUrl')}/streams/${this.streamId}/commits/${
res.data.commitCreate
}`
url: url
}
})
this.$apollo.queries.stream.refetch()
@@ -537,6 +559,7 @@ export default {
.fade-leave-active {
transition: opacity 0.2s ease-in;
}
.fade-enter,
.fade-leave-to {
opacity: 0;
@@ -547,27 +570,31 @@ export default {
max-height: 1200px;
overflow: hidden;
}
.expand-leave-active {
transition: all 0.3s ease;
max-height: 1200px;
overflow: hidden;
}
.expand-enter,
.expand-leave-to {
max-height: 0;
opacity: 0;
}
.v-text-field >>> input {
.v-text-field>>>input {
font-size: 0.9em;
}
.v-text-field >>> label {
.v-text-field>>>label {
font-size: 0.9em;
}
.btn-fix:focus::before {
opacity: 0 !important;
}
.btn-fix:hover::before {
opacity: 0.08 !important;
}
@@ -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 ${isFE2Terms ? 'Project': 'Stream'}` }}
</v-btn>
</template>
<v-card>
<v-card-title class="text-h5">
Create a New Stream
{{ `Create a New ${isFE2Terms ? '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="`${isFE2Terms ? 'Project' : 'Stream'} Name (Optional)`"
/>
<v-text-field
v-model="description"
@@ -67,7 +68,7 @@
/>
<v-switch
v-model="privateStream"
:label="'Private Stream'"
:label="`Private ${isFE2Terms ? 'Project' : 'Stream'}`"
></v-switch>
</v-container>
@@ -169,6 +170,10 @@ export default {
serverUrl: {
type: String,
default: null
},
isFE2Terms: {
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, localStorage.getItem('frontend2') === 'true')
let res = await this.$apollo.query({
query: gql`
query Stream($id: String!){
@@ -245,7 +250,9 @@ export default {
this.$eventHub.$emit('notification', {
text: 'Stream Added by URL!\n',
})
bus.$emit('stream-added-by-id-or-url', stream.id)
console.log(streamWrapper);
console.log(streamWrapper.branchName.slice(0, -1), "stream-added-by-id-or-url");
await bus.$emit('stream-added-by-id-or-url', stream.id, streamWrapper.branchName.slice(0, -1), streamWrapper.commitId)
this.$mixpanel.track('Connector Action', { name: 'Stream Add From URL' })
}
catch (e){
+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({
+17
View File
@@ -0,0 +1,17 @@
query Project($projectId: String!, $filter: ProjectModelsFilter) {
project(id: $projectId) {
id
name
models (filter: $filter) {
items {
id
name
versions {
items {
id
}
}
}
}
}
}
+6
View File
@@ -0,0 +1,6 @@
query{
serverInfo {
name
canonicalUrl
}
}
+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;
+21 -6
View File
@@ -17,7 +17,7 @@
</div>
<div v-if="allStreamsList" class="mt-5">
<div v-for="stream in allStreamsList" :key="stream.id">
<stream-card :stream-id="stream.id" />
<stream-card :stream-id="stream.id"/>
</div>
<div class="actions text-center">
<v-btn
@@ -27,7 +27,7 @@
elevation="0"
@click="showMore"
>
More Streams
{{ `More ${streamsText}` }}
</v-btn>
</div>
</div>
@@ -59,12 +59,16 @@ export default {
StreamCard: () => import('@/components/StreamCard')
},
props: {
streamSearchQuery: { type: String, default: null }
streamSearchQuery: {
type: String,
default: null
}
},
data() {
return {
showMoreEnabled: true,
savedStreams: []
savedStreams: [],
streamsText: ''
}
},
computed: {
@@ -80,6 +84,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){
@@ -108,16 +120,19 @@ export default {
bus.$on('set-saved-streams', (streamIds) => {
this.savedStreams = streamIds
})
bus.$on('stream-added-by-id-or-url', (streamId) => {
bus.$on('stream-added-by-id-or-url', async(streamId, branchId, commitId) => {
if (!this.savedStreams){
this.savedStreams = []
this.savedStreams.push(streamId)
sketchup.exec({name: "save_stream", data: {stream_id: streamId}})
} else {
if (!this.savedStreams.includes(streamId)){
this.savedStreams.push(streamId)
sketchup.exec({name: "save_stream", data: {stream_id: streamId}})
}
}
sketchup.exec({name: "save_stream", data: {stream_id: streamId}})
await bus.$emit(`set-stream-branch-commit-${streamId}`, branchId, commitId)
})
sketchup.exec({name: "load_saved_streams", data: {}})
console.log('LAUNCHED')