diff --git a/.editorconfig b/.editorconfig index 46f01e9e1..1648a5be2 100644 --- a/.editorconfig +++ b/.editorconfig @@ -254,6 +254,9 @@ dotnet_diagnostic.ca1508.severity = warning # Avoid dead conditional code dotnet_diagnostic.ca1509.severity = warning # Invalid entry in code metrics configuration file dotnet_diagnostic.ca1861.severity = none # Prefer 'static readonly' fields over constant array arguments if the called method is called repeatedly and is not mutating the passed array (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1861) +# CA2007: Consider calling ConfigureAwait on the awaited task (this is not needed for application code, in fact we don't want to call anything but ConfigureAwait(true) which is the default) +dotnet_diagnostic.CA2007.severity = none + dotnet_diagnostic.cs8618.severity = suggestion # nullable problem diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 5fc4560da..2a239e563 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -4,33 +4,36 @@ # * @specklesystems/connectors -# Core -# Not needed, falls back on team approval - -# Objects -# Converters require product owner approval, anything else falls back to team approval - -Objects/ConverterAutocadCivil/* @clairekuang @connorivy -Objects/ConverterCSI/* @connorivy -Objects/ConverterDynamo/* @teocomi @alanrynne -Objects/ConverterMicrostation/* @connorivy -Objects/ConverterRevit/* @connorivy @teocomi -Objects/ConverterRhinoGh/* @alanrynne @clairekuang -Objects/ConverterTeklaStructures/* @connorivy -Objects/StructuralUtilities/PolygonMesher/* @connorivy - # Connectors -ConnectorAutocadCivil/* @clairekuang -ConnectorArchicad/* @jozseflkiss -ConnectorCSI/* @connorivy -ConnectorDynamo/* @teocomi @alanrynne -ConnectorGrasshopper/* @alanrynne @clairekuang -ConnectorMicrostation/* @clairekuang -ConnectorRevit/* @teocomi @connorivy -ConnectorRhino/* @clairekuang @alanrynne -ConnectorTeklaStructures/* @connorivy +/Connectors/ArcGIS/* @KatKatKateryna +/Connectors/Autocad/* @clairekuang @oguzhankoral @didimitrie +/Connectors/Civil3d/* @clairekuang @oguzhankoral @didimitrie +/Connectors/CSi/* @bjoernsteinhagen @dogukankaratas +/Connectors/Navisworks/* @jsdbroughton +/Connectors/Revit/* @clairekuang @oguzhankoral @didimitrie +/Connectors/Rhino/* @clairekuang @oguzhankoral @didimitrie +/Connectors/Tekla/* @bjoernsteinhagen @dogukankaratas -# DesktopUI +# Converters +/Convertors/ArcGIS/* @KatKatKateryna +/Convertors/Autocad/* @clairekuang @oguzhankoral @didimitrie +/Convertors/Civil3d/* @clairekuang @oguzhankoral @didimitrie +/Convertors/CSi/* @bjoernsteinhagen @dogukankaratas +/Convertors/Navisworks/* @jsdbroughton +/Convertors/Revit/* @clairekuang @oguzhankoral @didimitrie +/Convertors/Rhino/* @clairekuang @oguzhankoral @didimitrie +/Convertors/Tekla/* @bjoernsteinhagen @dogukankaratas -DesktopUI2/* @teocomi @clairekuang +# DUI + +/DUI3/* @clairekuang @oguzhankoral @didimitrie + +# Importers +/Importers/* @JR-Morgan @didimitrie @oguzhankoral @adamhathcock + +# SDK +/SDK/* @JR-Morgan @clairekuang @didimitrie @oguzhankoral @adamhathcock + +# Build +/Build/* @JR-Morgan @oguzhankoral @adamhathcock diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 1efeba6a8..49d7d2d32 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,30 +1,46 @@ -## Description & motivation +## Description +## User Value + + + + ## Changes: - -- [ ] My pull request follows the guidelines in the [Contributing guide](https://github.com/specklesystems/speckle-server/blob/main/CONTRIBUTING.md)? -- [ ] My pull request does not duplicate any other open [Pull Requests](../../pulls) for the same update/change? - [ ] My commits are related to the pull request and do not amend unrelated code or documentation. -- [ ] My code follows a similar style to existing code. - [ ] I have added appropriate tests. - [ ] I have updated or added relevant documentation. -## References - - diff --git a/.github/workflows/ci.yml b/.github/workflows/pr.yml similarity index 93% rename from .github/workflows/ci.yml rename to .github/workflows/pr.yml index 8d2512ca3..a1419ad40 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/pr.yml @@ -13,8 +13,6 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 - with: - fetch-depth: 0 - name: Setup .NET uses: actions/setup-dotnet@v4 @@ -46,7 +44,10 @@ jobs: path: ~/.nuget/packages key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }} - - name: ⚒️ Run build + - name: ⚒️ Run Build on Linux + run: ./build.sh build-linux + + - name: ⚒️ Run tests run: ./build.sh test-only - name: Upload coverage reports to Codecov with GitHub Action diff --git a/.github/workflows/main.yml b/.github/workflows/release.yml similarity index 75% rename from .github/workflows/main.yml rename to .github/workflows/release.yml index 478f552d6..d852356bf 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/release.yml @@ -6,7 +6,7 @@ on: tags: ["v3.*"] # Manual delivery on every 3.x tag jobs: - build: + build-windows: runs-on: windows-latest outputs: version: ${{ steps.set-version.outputs.version }} @@ -27,10 +27,10 @@ jobs: path: ~/.nuget/packages key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }} - - name: ⚒️ Run GitVersion + - name: ⚒️ Run GitVersion on Windows run: ./build.ps1 build-server-version - - name: ⚒️ Run build + - name: ⚒️ Run build on Windows run: ./build.ps1 - name: ⬆️ Upload artifacts @@ -38,6 +38,8 @@ jobs: with: name: output-${{ env.GitVersion_FullSemVer }} path: output/*.* + if-no-files-found: error + retention-days: 1 compression-level: 0 # no compression - id: set-version @@ -46,19 +48,18 @@ jobs: deploy-installers: runs-on: ubuntu-latest - needs: build + needs: build-windows env: IS_TAG_BUILD: ${{ github.ref_type == 'tag' }} IS_RELEASE_BRANCH: ${{ startsWith(github.ref_name, 'release/') || github.ref_name == 'main'}} steps: - name: 🔫 Trigger Build Installers uses: ALEEF02/workflow-dispatch@v3.0.0 - continue-on-error: true with: workflow: Build Installers repo: specklesystems/connector-installers token: ${{ secrets.CONNECTORS_GH_TOKEN }} - inputs: '{ "run_id": "${{ github.run_id }}", "version": "${{ needs.build.outputs.version }}", "public_release": ${{ env.IS_TAG_BUILD }}, "store_artifacts": ${{ env.IS_RELEASE_BRANCH }} }' + inputs: '{ "run_id": "${{ github.run_id }}", "version": "${{ needs.build-windows.outputs.version }}", "public_release": ${{ env.IS_TAG_BUILD }}, "store_artifacts": ${{ env.IS_RELEASE_BRANCH }} }' ref: main wait-for-completion: true wait-for-completion-interval: 10s @@ -70,11 +71,13 @@ jobs: with: name: output-* - test: + build-linux: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 + with: + fetch-depth: 0 - name: Setup .NET uses: actions/setup-dotnet@v4 @@ -87,11 +90,21 @@ jobs: path: ~/.nuget/packages key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }} - - name: ⚒️ Run build + - name: ⚒️ Run GitVersion on Linux + run: ./build.sh build-server-version + + - name: ⚒️ Run tests on Linux run: ./build.sh test-only + - name: ⚒️ Run Build and Pack on Linux + run: ./build.sh build-linux + - name: Upload coverage reports to Codecov with GitHub Action uses: codecov/codecov-action@v5 with: - file: Converters/**/coverage.xml + files: Converters/**/coverage.xml token: ${{ secrets.CODECOV_TOKEN }} + + - name: Push to nuget.org + if: ${{ github.ref_type == 'tag' }} + run: dotnet nuget push output/*.nupkg --source "https://api.nuget.org/v3/index.json" --api-key ${{secrets.CONNECTORS_NUGET_TOKEN }} --skip-duplicate diff --git a/Build/Consts.cs b/Build/Consts.cs index 93fb96031..8b27b9eb7 100644 --- a/Build/Consts.cs +++ b/Build/Consts.cs @@ -42,11 +42,29 @@ public static class Consts ] ), new( - "tekla-structures", + "navisworks", + [ + new("Connectors/Navisworks/Speckle.Connectors.Navisworks2020", "net48"), + new("Connectors/Navisworks/Speckle.Connectors.Navisworks2021", "net48"), + new("Connectors/Navisworks/Speckle.Connectors.Navisworks2022", "net48"), + new("Connectors/Navisworks/Speckle.Connectors.Navisworks2023", "net48"), + new("Connectors/Navisworks/Speckle.Connectors.Navisworks2024", "net48"), + new("Connectors/Navisworks/Speckle.Connectors.Navisworks2025", "net48") + ] + ), + new( + "teklastructures", [ new("Connectors/Tekla/Speckle.Connector.Tekla2023", "net48"), new("Connectors/Tekla/Speckle.Connector.Tekla2024", "net48") ] + ), + new( + "etabs", + [ + new("Connectors/CSi/Speckle.Connectors.ETABS21", "net48"), + new("Connectors/CSi/Speckle.Connectors.ETABS22", "net8.0-windows"), + ] ) }; } diff --git a/Build/Github.cs b/Build/Github.cs index b62ddeb39..9c9f335a1 100644 --- a/Build/Github.cs +++ b/Build/Github.cs @@ -28,11 +28,11 @@ public static class Github Content = content }; request.Headers.Add("X-GitHub-Api-Version", "2022-11-28"); - var response = await client.SendAsync(request).ConfigureAwait(false); + var response = await client.SendAsync(request); if (!response.IsSuccessStatusCode) { throw new InvalidOperationException( - $"{response.StatusCode} {response.ReasonPhrase} {await response.Content.ReadAsStringAsync().ConfigureAwait(false)}" + $"{response.StatusCode} {response.ReasonPhrase} {await response.Content.ReadAsStringAsync()}" ); } } diff --git a/Build/Program.cs b/Build/Program.cs index 8604c198c..5da0c0b8f 100644 --- a/Build/Program.cs +++ b/Build/Program.cs @@ -7,6 +7,7 @@ using static SimpleExec.Command; const string CLEAN = "clean"; const string RESTORE = "restore"; const string BUILD = "build"; +const string BUILD_LINUX = "build-linux"; const string TEST = "test"; const string TEST_ONLY = "test-only"; const string FORMAT = "format"; @@ -17,6 +18,7 @@ const string BUILD_SERVER_VERSION = "build-server-version"; const string CLEAN_LOCKS = "clean-locks"; const string CHECK_SOLUTIONS = "check-solutions"; const string DEEP_CLEAN = "deep-clean"; +const string DEEP_CLEAN_LOCAL = "deep-clean-local"; //need to pass arguments /*var arguments = new List(); @@ -26,18 +28,59 @@ if (args.Length > 1) args = new[] { arguments.First() }; //arguments = arguments.Skip(1).ToList(); }*/ +void Build(string solution, string configuration) +{ + Console.WriteLine(); + Console.WriteLine(); + Console.WriteLine($"Building solution '{solution}' as '{configuration}'"); + Console.WriteLine(); + Run("dotnet", $"build .\\{solution} --configuration {configuration} --no-restore"); +} +void Restore(string solution) +{ + Console.WriteLine(); + Console.WriteLine($"Restoring solution '{solution}'"); + Console.WriteLine(); + Run("dotnet", $"restore .\\{solution} --no-cache"); +} +void DeleteFiles(string pattern) +{ + foreach (var f in Glob.Files(".", pattern)) + { + Console.WriteLine("Found and will delete: " + f); + File.Delete(f); + } +} +void DeleteDirectories(string pattern) +{ + foreach (var f in Glob.Directories(".", pattern)) + { + if (f.StartsWith("Build")) + { + continue; + } + Console.WriteLine("Found and will delete: " + f); + Directory.Delete(f, true); + } +} + +void CleanSolution(string solution, string configuration) +{ + Console.WriteLine("Cleaning solution: " + solution); + + DeleteDirectories("**/bin"); + DeleteDirectories("**/obj"); + DeleteFiles("**/*.lock.json"); + Restore(solution); + Build(solution, configuration); +} Target( CLEAN_LOCKS, () => { - foreach (var f in Glob.Files(".", "**/*.lock.json")) - { - Console.WriteLine("Found and will delete: " + f); - File.Delete(f); - } - Console.WriteLine("Running restore now."); - Run("dotnet", "restore .\\Speckle.Connectors.sln --no-cache"); + DeleteFiles("**/*.lock.json"); + Restore("Speckle.Connectors.sln"); } ); @@ -45,26 +88,14 @@ Target( DEEP_CLEAN, () => { - foreach (var f in Glob.Directories(".", "**/bin")) - { - if (f.StartsWith("Build")) - { - continue; - } - Console.WriteLine("Found and will delete: " + f); - Directory.Delete(f, true); - } - foreach (var f in Glob.Directories(".", "**/obj")) - { - if (f.StartsWith("Build")) - { - continue; - } - Console.WriteLine("Found and will delete: " + f); - Directory.Delete(f, true); - } - Console.WriteLine("Running restore now."); - Run("dotnet", "restore .\\Speckle.Connectors.sln --no-cache"); + CleanSolution("Speckle.Connectors.sln", "debug"); + } +); +Target( + DEEP_CLEAN_LOCAL, + () => + { + CleanSolution("Local.sln", "local"); } ); @@ -98,7 +129,7 @@ Target( VERSION, async () => { - var (output, _) = await ReadAsync("dotnet", "minver -v w").ConfigureAwait(false); + var (output, _) = await ReadAsync("dotnet", "minver -v w"); output = output.Trim(); Console.WriteLine($"Version: {output}"); Run("echo", $"\"version={output}\" >> $GITHUB_OUTPUT"); @@ -175,10 +206,32 @@ Target( Glob.Files(".", "**/*.Tests.csproj"), file => { - Run("dotnet", $"restore {file} --locked-mode"); + Run("dotnet", $"build {file} -c Release --no-incremental"); Run( "dotnet", - $"test {file} -c Release --no-restore --verbosity=minimal /p:AltCover=true /p:AltCoverAttributeFilter=ExcludeFromCodeCoverage /p:AltCoverVerbosity=Warning" + $"test {file} -c Release --no-build --verbosity=minimal /p:AltCover=true /p:AltCoverAttributeFilter=ExcludeFromCodeCoverage /p:AltCoverVerbosity=Warning" + ); + } +); + +Target( + BUILD_LINUX, + DependsOn(FORMAT), + Glob.Files(".", "**/Speckle.Importers.Ifc.csproj"), + file => + { + Run("dotnet", $"restore {file} --locked-mode"); + var version = Environment.GetEnvironmentVariable("GitVersion_FullSemVer") ?? "3.0.0-localBuild"; + var fileVersion = Environment.GetEnvironmentVariable("GitVersion_AssemblySemFileVer") ?? "3.0.0.0"; + Console.WriteLine($"Version: {version} & {fileVersion}"); + Run( + "dotnet", + $"build {file} -c Release --no-restore -warnaserror -p:Version={version} -p:FileVersion={fileVersion} -v:m" + ); + + RunAsync( + "dotnet", + $"pack {file} -c Release -o output --no-build -p:Version={version} -p:FileVersion={fileVersion} -v:m" ); } ); diff --git a/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Bindings/ArcGISReceiveBinding.cs b/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Bindings/ArcGISReceiveBinding.cs index ef35bc039..0924287ad 100644 --- a/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Bindings/ArcGISReceiveBinding.cs +++ b/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Bindings/ArcGISReceiveBinding.cs @@ -19,7 +19,7 @@ namespace Speckle.Connectors.ArcGIS.Bindings; public sealed class ArcGISReceiveBinding : IReceiveBinding { public string Name { get; } = "receiveBinding"; - private readonly CancellationManager _cancellationManager; + private readonly ICancellationManager _cancellationManager; private readonly DocumentModelStore _store; private readonly IServiceProvider _serviceProvider; private readonly IOperationProgressManager _operationProgressManager; @@ -32,7 +32,7 @@ public sealed class ArcGISReceiveBinding : IReceiveBinding public ArcGISReceiveBinding( DocumentModelStore store, IBrowserBridge parent, - CancellationManager cancellationManager, + ICancellationManager cancellationManager, IServiceProvider serviceProvider, IOperationProgressManager operationProgressManager, ILogger logger, @@ -60,7 +60,7 @@ public sealed class ArcGISReceiveBinding : IReceiveBinding throw new InvalidOperationException("No download model card was found."); } - CancellationToken cancellationToken = _cancellationManager.InitCancellationTokenSource(modelCardId); + using var cancellationItem = _cancellationManager.GetCancellationItem(modelCardId); using var scope = _serviceProvider.CreateScope(); scope .ServiceProvider.GetRequiredService>() @@ -76,19 +76,16 @@ public sealed class ArcGISReceiveBinding : IReceiveBinding .ServiceProvider.GetRequiredService() .Execute( modelCard.GetReceiveInfo("ArcGIS"), // POC: get host app name from settings? same for GetSendInfo - _operationProgressManager.CreateOperationProgressEventHandler(Parent, modelCardId, cancellationToken), - cancellationToken - ) - .ConfigureAwait(false); + _operationProgressManager.CreateOperationProgressEventHandler(Parent, modelCardId, cancellationItem.Token), + cancellationItem.Token + ); modelCard.BakedObjectIds = receiveOperationResults.BakedObjectIds.ToList(); - await Commands - .SetModelReceiveResult( - modelCardId, - receiveOperationResults.BakedObjectIds, - receiveOperationResults.ConversionResults - ) - .ConfigureAwait(false); + await Commands.SetModelReceiveResult( + modelCardId, + receiveOperationResults.BakedObjectIds, + receiveOperationResults.ConversionResults + ); } catch (OperationCanceledException) { @@ -100,7 +97,7 @@ public sealed class ArcGISReceiveBinding : IReceiveBinding catch (Exception ex) when (!ex.IsFatal()) // UX reasons - we will report operation exceptions as model card error. We may change this later when we have more exception documentation { _logger.LogModelCardHandledError(ex); - await Commands.SetModelError(modelCardId, ex).ConfigureAwait(false); + await Commands.SetModelError(modelCardId, ex); } } diff --git a/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Bindings/ArcGISSelectionBinding.cs b/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Bindings/ArcGISSelectionBinding.cs index 6ef531c23..180156939 100644 --- a/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Bindings/ArcGISSelectionBinding.cs +++ b/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Bindings/ArcGISSelectionBinding.cs @@ -12,15 +12,18 @@ public class ArcGISSelectionBinding : ISelectionBinding public string Name => "selectionBinding"; public IBrowserBridge Parent { get; } - public ArcGISSelectionBinding(IBrowserBridge parent, MapMembersUtils mapMemberUtils) + public ArcGISSelectionBinding( + IBrowserBridge parent, + MapMembersUtils mapMemberUtils, + ITopLevelExceptionHandler topLevelExceptionHandler + ) { _mapMemberUtils = mapMemberUtils; Parent = parent; - var topLevelHandler = parent.TopLevelExceptionHandler; // example: https://github.com/Esri/arcgis-pro-sdk-community-samples/blob/master/Map-Authoring/QueryBuilderControl/DefinitionQueryDockPaneViewModel.cs // MapViewEventArgs args = new(MapView.Active); - TOCSelectionChangedEvent.Subscribe(_ => topLevelHandler.CatchUnhandled(OnSelectionChanged), true); + TOCSelectionChangedEvent.Subscribe(_ => topLevelExceptionHandler.CatchUnhandled(OnSelectionChanged), true); } private void OnSelectionChanged() @@ -53,11 +56,8 @@ public class ArcGISSelectionBinding : ISelectionBinding selectedMembers.AddRange(mapView.GetSelectedStandaloneTables()); List allNestedMembers = new(); - foreach (MapMember member in selectedMembers) - { - var layerMapMembers = _mapMemberUtils.UnpackMapLayers(selectedMembers); - allNestedMembers.AddRange(layerMapMembers); - } + var layerMapMembers = _mapMemberUtils.UnpackMapLayers(selectedMembers); + allNestedMembers.AddRange(layerMapMembers); List objectTypes = allNestedMembers .Select(o => o.GetType().ToString().Split(".").Last()) diff --git a/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Bindings/ArcGISSendBinding.cs b/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Bindings/ArcGISSendBinding.cs index 1b3dcb613..c86eeda40 100644 --- a/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Bindings/ArcGISSendBinding.cs +++ b/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Bindings/ArcGISSendBinding.cs @@ -13,6 +13,7 @@ using Speckle.Connectors.ArcGIS.Utils; using Speckle.Connectors.Common.Caching; using Speckle.Connectors.Common.Cancellation; using Speckle.Connectors.Common.Operations; +using Speckle.Connectors.Common.Threading; using Speckle.Connectors.DUI.Bindings; using Speckle.Connectors.DUI.Bridge; using Speckle.Connectors.DUI.Exceptions; @@ -38,12 +39,14 @@ public sealed class ArcGISSendBinding : ISendBinding private readonly DocumentModelStore _store; private readonly IServiceProvider _serviceProvider; private readonly List _sendFilters; - private readonly CancellationManager _cancellationManager; + private readonly ICancellationManager _cancellationManager; private readonly ISendConversionCache _sendConversionCache; private readonly IOperationProgressManager _operationProgressManager; private readonly ILogger _logger; private readonly ITopLevelExceptionHandler _topLevelExceptionHandler; private readonly IArcGISConversionSettingsFactory _arcGISConversionSettingsFactory; + private readonly IThreadContext _threadContext; + private readonly ISpeckleApplication _speckleApplication; /// /// Used internally to aggregate the changed objects' id. Note we're using a concurrent dictionary here as the expiry check method is not thread safe, and this was causing problems. See: @@ -62,12 +65,15 @@ public sealed class ArcGISSendBinding : ISendBinding IBrowserBridge parent, IEnumerable sendFilters, IServiceProvider serviceProvider, - CancellationManager cancellationManager, + ICancellationManager cancellationManager, ISendConversionCache sendConversionCache, IOperationProgressManager operationProgressManager, ILogger logger, IArcGISConversionSettingsFactory arcGisConversionSettingsFactory, - MapMembersUtils mapMemberUtils + MapMembersUtils mapMemberUtils, + IThreadContext threadContext, + ISpeckleApplication speckleApplication, + ITopLevelExceptionHandler topLevelExceptionHandler ) { _store = store; @@ -77,9 +83,11 @@ public sealed class ArcGISSendBinding : ISendBinding _sendConversionCache = sendConversionCache; _operationProgressManager = operationProgressManager; _logger = logger; - _topLevelExceptionHandler = parent.TopLevelExceptionHandler; + _topLevelExceptionHandler = topLevelExceptionHandler; _arcGISConversionSettingsFactory = arcGisConversionSettingsFactory; _mapMemberUtils = mapMemberUtils; + _threadContext = threadContext; + _speckleApplication = speckleApplication; Parent = parent; Commands = new SendBindingUICommands(parent); @@ -90,18 +98,22 @@ public sealed class ArcGISSendBinding : ISendBinding }; } + private void OnDocumentStoreChangedEvent(object _) => _sendConversionCache.ClearCache(); + private void SubscribeToArcGISEvents() { LayersRemovedEvent.Subscribe( a => - _topLevelExceptionHandler.FireAndForget(async () => await GetIdsForLayersRemovedEvent(a).ConfigureAwait(false)), + _topLevelExceptionHandler.FireAndForget( + async () => await QueuedTask.Run(async () => await GetIdsForLayersRemovedEvent(a)) + ), true ); StandaloneTablesRemovedEvent.Subscribe( a => _topLevelExceptionHandler.FireAndForget( - async () => await GetIdsForStandaloneTablesRemovedEvent(a).ConfigureAwait(false) + async () => await QueuedTask.Run(async () => await GetIdsForStandaloneTablesRemovedEvent(a)) ), true ); @@ -109,7 +121,7 @@ public sealed class ArcGISSendBinding : ISendBinding MapPropertyChangedEvent.Subscribe( a => _topLevelExceptionHandler.FireAndForget( - async () => await GetIdsForMapPropertyChangedEvent(a).ConfigureAwait(false) + async () => await QueuedTask.Run(async () => await GetIdsForMapPropertyChangedEvent(a)) ), true ); // Map units, CRS etc. @@ -117,13 +129,17 @@ public sealed class ArcGISSendBinding : ISendBinding MapMemberPropertiesChangedEvent.Subscribe( a => _topLevelExceptionHandler.FireAndForget( - async () => await GetIdsForMapMemberPropertiesChangedEvent(a).ConfigureAwait(false) + async () => await QueuedTask.Run(async () => await GetIdsForMapMemberPropertiesChangedEvent(a)) ), true ); // e.g. Layer name ActiveMapViewChangedEvent.Subscribe( - _ => _topLevelExceptionHandler.CatchUnhandled(SubscribeToMapMembersDataSourceChange), + _ => + _topLevelExceptionHandler.FireAndForget(async () => + { + await QueuedTask.Run(SubscribeToMapMembersDataSourceChange); + }), true ); @@ -139,28 +155,24 @@ public sealed class ArcGISSendBinding : ISendBinding private void SubscribeToMapMembersDataSourceChange() { - var task = QueuedTask.Run(() => + if (MapView.Active == null) { - if (MapView.Active == null) - { - return; - } + return; + } - // subscribe to layers - foreach (Layer layer in MapView.Active.Map.Layers) + // subscribe to layers + foreach (Layer layer in MapView.Active.Map.Layers) + { + if (layer is FeatureLayer featureLayer) { - if (layer is FeatureLayer featureLayer) - { - SubscribeToFeatureLayerDataSourceChange(featureLayer); - } + SubscribeToFeatureLayerDataSourceChange(featureLayer); } - // subscribe to tables - foreach (StandaloneTable table in MapView.Active.Map.StandaloneTables) - { - SubscribeToTableDataSourceChange(table); - } - }); - task.Wait(); + } + // subscribe to tables + foreach (StandaloneTable table in MapView.Active.Map.StandaloneTables) + { + SubscribeToTableDataSourceChange(table); + } } private void SubscribeToFeatureLayerDataSourceChange(FeatureLayer layer) @@ -195,25 +207,25 @@ public sealed class ArcGISSendBinding : ISendBinding { RowCreatedEvent.Subscribe( (args) => - Parent.TopLevelExceptionHandler.FireAndForget(async () => + _topLevelExceptionHandler.FireAndForget(async () => { - await OnRowChanged(args).ConfigureAwait(false); + await OnRowChanged(args); }), layerTable ); RowChangedEvent.Subscribe( (args) => - Parent.TopLevelExceptionHandler.FireAndForget(async () => + _topLevelExceptionHandler.FireAndForget(async () => { - await OnRowChanged(args).ConfigureAwait(false); + await OnRowChanged(args); }), layerTable ); RowDeletedEvent.Subscribe( (args) => - Parent.TopLevelExceptionHandler.FireAndForget(async () => + _topLevelExceptionHandler.FireAndForget(async () => { - await OnRowChanged(args).ConfigureAwait(false); + await OnRowChanged(args); }), layerTable ); @@ -258,7 +270,7 @@ public sealed class ArcGISSendBinding : ISendBinding } } - await RunExpirationChecks(false).ConfigureAwait(false); + await RunExpirationChecks(false); } private async Task GetIdsForLayersRemovedEvent(LayerEventsArgs args) @@ -267,7 +279,7 @@ public sealed class ArcGISSendBinding : ISendBinding { ChangedObjectIds[layer.URI] = 1; } - await RunExpirationChecks(true).ConfigureAwait(false); + await RunExpirationChecks(true); } private async Task GetIdsForStandaloneTablesRemovedEvent(StandaloneTableEventArgs args) @@ -276,7 +288,7 @@ public sealed class ArcGISSendBinding : ISendBinding { ChangedObjectIds[table.URI] = 1; } - await RunExpirationChecks(true).ConfigureAwait(false); + await RunExpirationChecks(true); } private async Task GetIdsForMapPropertyChangedEvent(MapPropertyChangedEventArgs args) @@ -289,7 +301,7 @@ public sealed class ArcGISSendBinding : ISendBinding ChangedObjectIds[member.URI] = 1; } } - await RunExpirationChecks(false).ConfigureAwait(false); + await RunExpirationChecks(false); } private void GetIdsForLayersAddedEvent(LayerEventsArgs args) @@ -339,7 +351,7 @@ public sealed class ArcGISSendBinding : ISendBinding { ChangedObjectIds[member.URI] = 1; } - await RunExpirationChecks(false).ConfigureAwait(false); + await RunExpirationChecks(false); } } @@ -364,66 +376,61 @@ public sealed class ArcGISSendBinding : ISendBinding throw new InvalidOperationException("No publish model card was found."); } - CancellationToken cancellationToken = _cancellationManager.InitCancellationTokenSource(modelCardId); + using var cancellationItem = _cancellationManager.GetCancellationItem(modelCardId); - var sendResult = await QueuedTask - .Run(async () => - { - using var scope = _serviceProvider.CreateScope(); - scope - .ServiceProvider.GetRequiredService>() - .Initialize( - _arcGISConversionSettingsFactory.Create( - Project.Current, - MapView.Active.Map, - new CRSoffsetRotation(MapView.Active.Map) - ) - ); - List mapMembers = modelCard - .SendFilter.NotNull() - .RefreshObjectIds() - .Select(id => (MapMember)MapView.Active.Map.FindLayer(id) ?? MapView.Active.Map.FindStandaloneTable(id)) - .Where(obj => obj != null) - .ToList(); - - if (mapMembers.Count == 0) - { - // Handle as CARD ERROR in this function - throw new SpeckleSendFilterException( - "No objects were found to convert. Please update your publish filter!" - ); - } - - // subscribe to the selected layer events - foreach (MapMember mapMember in mapMembers) - { - if (mapMember is FeatureLayer featureLayer) - { - SubscribeToFeatureLayerDataSourceChange(featureLayer); - } - else if (mapMember is StandaloneTable table) - { - SubscribeToTableDataSourceChange(table); - } - } - - var result = await scope - .ServiceProvider.GetRequiredService>() - .Execute( - mapMembers, - modelCard.GetSendInfo("ArcGIS"), // POC: get host app name from settings? same for GetReceiveInfo - _operationProgressManager.CreateOperationProgressEventHandler(Parent, modelCardId, cancellationToken), - cancellationToken + using var scope = _serviceProvider.CreateScope(); + List mapMembers = await QueuedTask.Run(() => + { + scope + .ServiceProvider.GetRequiredService>() + .Initialize( + _arcGISConversionSettingsFactory.Create( + Project.Current, + MapView.Active.Map, + new CRSoffsetRotation(MapView.Active.Map) ) - .ConfigureAwait(false); + ); - return result; - }) - .ConfigureAwait(false); + return modelCard + .SendFilter.NotNull() + .RefreshObjectIds() + .Select(id => (MapMember)MapView.Active.Map.FindLayer(id) ?? MapView.Active.Map.FindStandaloneTable(id)) + .Where(obj => obj != null) + .ToList(); + }); - await Commands - .SetModelSendResult(modelCardId, sendResult.RootObjId, sendResult.ConversionResults) - .ConfigureAwait(false); + if (mapMembers.Count == 0) + { + // Handle as CARD ERROR in this function + throw new SpeckleSendFilterException("No objects were found to convert. Please update your publish filter!"); + } + + await QueuedTask.Run(() => + { + // subscribe to the selected layer events + foreach (MapMember mapMember in mapMembers) + { + if (mapMember is FeatureLayer featureLayer) + { + SubscribeToFeatureLayerDataSourceChange(featureLayer); + } + else if (mapMember is StandaloneTable table) + { + SubscribeToTableDataSourceChange(table); + } + } + }); + + var sendResult = await scope + .ServiceProvider.GetRequiredService>() + .Execute( + mapMembers, + modelCard.GetSendInfo(_speckleApplication.ApplicationAndVersion), + _operationProgressManager.CreateOperationProgressEventHandler(Parent, modelCardId, cancellationItem.Token), + cancellationItem.Token + ); + + await Commands.SetModelSendResult(modelCardId, sendResult.RootObjId, sendResult.ConversionResults); } catch (OperationCanceledException) { @@ -435,7 +442,7 @@ public sealed class ArcGISSendBinding : ISendBinding catch (Exception ex) when (!ex.IsFatal()) // UX reasons - we will report operation exceptions as model card error. We may change this later when we have more exception documentation { _logger.LogModelCardHandledError(ex); - await Commands.SetModelError(modelCardId, ex).ConfigureAwait(false); + await Commands.SetModelError(modelCardId, ex); } } @@ -470,7 +477,7 @@ public sealed class ArcGISSendBinding : ISendBinding } } - await Commands.SetModelsExpired(expiredSenderIds).ConfigureAwait(false); + await Commands.SetModelsExpired(expiredSenderIds); ChangedObjectIds = new(); } } diff --git a/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Bindings/BasicConnectorBinding.cs b/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Bindings/BasicConnectorBinding.cs index 6b3e68ccf..f00d7fe7b 100644 --- a/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Bindings/BasicConnectorBinding.cs +++ b/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Bindings/BasicConnectorBinding.cs @@ -21,18 +21,25 @@ public class BasicConnectorBinding : IBasicConnectorBinding public BasicConnectorBindingCommands Commands { get; } private readonly DocumentModelStore _store; private readonly ISpeckleApplication _speckleApplication; + private readonly ITopLevelExceptionHandler _topLevelExceptionHandler; - public BasicConnectorBinding(DocumentModelStore store, IBrowserBridge parent, ISpeckleApplication speckleApplication) + public BasicConnectorBinding( + DocumentModelStore store, + IBrowserBridge parent, + ISpeckleApplication speckleApplication, + ITopLevelExceptionHandler topLevelExceptionHandler + ) { _store = store; _speckleApplication = speckleApplication; + _topLevelExceptionHandler = topLevelExceptionHandler; Parent = parent; Commands = new BasicConnectorBindingCommands(parent); _store.DocumentChanged += (_, _) => - parent.TopLevelExceptionHandler.FireAndForget(async () => + _topLevelExceptionHandler.FireAndForget(async () => { - await Commands.NotifyDocumentChanged().ConfigureAwait(false); + await Commands.NotifyDocumentChanged(); }); } @@ -54,14 +61,16 @@ public class BasicConnectorBinding : IBasicConnectorBinding public DocumentModelStore GetDocumentState() => _store; - public void AddModel(ModelCard model) => _store.Models.Add(model); + public void AddModel(ModelCard model) => _store.AddModel(model); public void UpdateModel(ModelCard model) => _store.UpdateModel(model); public void RemoveModel(ModelCard model) => _store.RemoveModel(model); - public async Task HighlightObjects(IReadOnlyList objectIds) => - await HighlightObjectsOnView(objectIds.Select(x => new ObjectID(x)).ToList()).ConfigureAwait(false); + public async Task HighlightObjects(IReadOnlyList objectIds) + { + await HighlightObjectsOnView(objectIds.Select(x => new ObjectID(x)).ToList()); + } public async Task HighlightModel(string modelCardId) { @@ -88,24 +97,22 @@ public class BasicConnectorBinding : IBasicConnectorBinding { return; } - await HighlightObjectsOnView(objectIds).ConfigureAwait(false); + await HighlightObjectsOnView(objectIds); } private async Task HighlightObjectsOnView(IReadOnlyList objectIds) { - MapView mapView = MapView.Active; + await QueuedTask.Run(() => + { + MapView mapView = MapView.Active; - await QueuedTask - .Run(async () => - { - List mapMembersFeatures = GetMapMembers(objectIds, mapView); - ClearSelectionInTOC(); - ClearSelection(); - await SelectMapMembersInTOC(mapMembersFeatures).ConfigureAwait(false); - SelectMapMembersAndFeatures(mapMembersFeatures); - mapView.ZoomToSelected(); - }) - .ConfigureAwait(false); + List mapMembersFeatures = GetMapMembers(objectIds, mapView); + ClearSelectionInTOC(); + ClearSelection(); + SelectMapMembersInTOC(mapMembersFeatures); + SelectMapMembersAndFeatures(mapMembersFeatures); + mapView.ZoomToSelected(); + }); } private List GetMapMembers(IReadOnlyList objectIds, MapView mapView) @@ -171,7 +178,7 @@ public class BasicConnectorBinding : IBasicConnectorBinding } } - private async Task SelectMapMembersInTOC(IReadOnlyList mapMembersFeatures) + private void SelectMapMembersInTOC(IReadOnlyList mapMembersFeatures) { List layers = new(); List tables = new(); @@ -187,7 +194,7 @@ public class BasicConnectorBinding : IBasicConnectorBinding } else { - await QueuedTask.Run(() => layer.SetExpanded(true)).ConfigureAwait(false); + layer.SetExpanded(true); } } else if (member is StandaloneTable table) diff --git a/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Config.daml b/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Config.daml index acb8e4c5c..6c1b6c751 100644 --- a/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Config.daml +++ b/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Config.daml @@ -20,7 +20,7 @@ Speckle - Speckle connector for ArcGIS + Next Gen Speckle Connector (Beta) for ArcGIS Images\AddinDesktop32.png Speckle Systems Speckle Systems @@ -33,14 +33,14 @@ - + - + an empty group. change appearsOnAddinTab to "True" if control is to be in the addin tab--> + /// /// - public async Task ParseMaterials( - List materialProxies, - IProgress onOperationProgressed - ) + public void ParseMaterials(List materialProxies, IProgress onOperationProgressed) { // injected as Singleton, so we need to clean existing proxies first ObjectMaterialsIdMap = new(); @@ -89,7 +54,6 @@ public class ArcGISColorManager foreach (RenderMaterialProxy colorProxy in materialProxies) { onOperationProgressed.Report(new("Converting materials", (double)++count / materialProxies.Count)); - await Task.Yield(); foreach (string objectId in colorProxy.objects) { Color convertedColor = Color.FromArgb(colorProxy.value.diffuse); @@ -98,6 +62,14 @@ public class ArcGISColorManager } } + public int CIMColorToInt(CIMColor color) + { + return (255 << 24) + | ((int)Math.Round(color.Values[0]) << 16) + | ((int)Math.Round(color.Values[1]) << 8) + | (int)Math.Round(color.Values[2]); + } + /// /// Create a new CIMUniqueValueClass for UniqueRenderer per each object ID /// @@ -111,6 +83,7 @@ public class ArcGISColorManager { // declare default white color Color color = Color.FromArgb(255, 255, 255, 255); + bool colorFound = false; // get color moving upwards from the object foreach (var parent in tc.GetAscendants()) @@ -120,16 +93,43 @@ public class ArcGISColorManager if (ObjectMaterialsIdMap.TryGetValue(appId, out Color objColorMaterial)) { color = objColorMaterial; + colorFound = true; break; } if (ObjectColorsIdMap.TryGetValue(appId, out Color objColor)) { color = objColor; + colorFound = true; break; } } } + // handling Revit case, where child objects have separate colors/materials + if (!colorFound && tc.Current is IDataObject) + { + var displayable = tc.Current.TryGetDisplayValue(); + if (displayable != null) + { + foreach (var childObj in displayable) + { + if (childObj.applicationId is string appId) + { + if (ObjectMaterialsIdMap.TryGetValue(appId, out Color objColorMaterial)) + { + color = objColorMaterial; + break; + } + if (ObjectColorsIdMap.TryGetValue(appId, out Color objColor)) + { + color = objColor; + break; + } + } + } + } + } + CIMSymbolReference symbol = CreateSymbol(speckleGeometryType, color); // First create a "CIMUniqueValueClass" @@ -196,7 +196,7 @@ public class ArcGISColorManager } // declare default grey color, create default symbol for the given layer geometry type - var color = Color.FromArgb(ColorFactory.Instance.GreyRGB.CIMColorToInt()); + var color = Color.FromArgb(CIMColorToInt(ColorFactory.Instance.GreyRGB)); CIMSymbolReference defaultSymbol = CreateSymbol(fLayer.ShapeType, color); // get existing renderer classes @@ -227,7 +227,10 @@ public class ArcGISColorManager foreach (var tContext in traversalContexts) { // get unique label - string uniqueLabel = tContext.Current.id; + string? uniqueLabel = tContext.Current?.id; + + // remove any GIS-specific classes for now + /* if (tContext.Current is IGisFeature gisFeat) { var existingLabel = gisFeat.attributes["Speckle_ID"]; @@ -235,9 +238,9 @@ public class ArcGISColorManager { uniqueLabel = stringLabel; } - } + }*/ - if (!listUniqueValueClasses.Select(x => x.Label).Contains(uniqueLabel)) + if (uniqueLabel is not null && !listUniqueValueClasses.Select(x => x.Label).Contains(uniqueLabel)) { CIMUniqueValueClass newUniqueValueClass = CreateColorCategory(tContext, fLayer.ShapeType, uniqueLabel); listUniqueValueClasses.Add(newUniqueValueClass); @@ -259,376 +262,4 @@ public class ArcGISColorManager }; return uvr; } - - private string GetColorApplicationId(int argb, double order) => $"{argb}_{order}"; - - // Adds the element id to the color proxy based on colorId if it exists in ColorProxies, - // otherwise creates a new Color Proxy with the element id in the objects property - private void AddElementIdToColorProxy(string elementAppId, int colorValue, string colorId, int displayPriority) - { - if (ColorProxies.TryGetValue(colorId, out ColorProxy? colorProxy)) - { - colorProxy.objects.Add(elementAppId); - } - else - { - ColorProxy newProxy = - new() - { - value = colorValue, - applicationId = colorId, - objects = new() { elementAppId }, - name = colorId - }; - - newProxy["displayOrder"] = displayPriority; // 0 - top layer (top display priority), 1,2,3.. decreasing priority - ColorProxies.Add(colorId, newProxy); - } - } - - private void ProcessRasterLayerColors(RasterLayer rasterLayer, int displayPriority) - { - string elementAppId = $"{rasterLayer.URI}_0"; // POC: explain why count = 0 here - int argb = -1; - string colorId = GetColorApplicationId(argb, displayPriority); // We are using a default color of -1 for all raster layers - AddElementIdToColorProxy(elementAppId, argb, colorId, displayPriority); - } - - /// - /// Record colors from every feature of the layer into ColorProxies - /// - /// - /// - private void ProcessFeatureLayerColors(FeatureLayer layer, int displayPriority) - { - // first get a list of layer fields - // field names are unique, but often their alias is used instead by renderer headings - // so we are storing both names and alieas in this dictionary for fast lookup - // POC: adding aliases are not optimal, because they do not need to be unique && they can be the same as the name of another field - Dictionary layerFieldDictionary = new(); - foreach (FieldDescription field in layer.GetFieldDescriptions()) - { - layerFieldDictionary.TryAdd(field.Name, field); - layerFieldDictionary.TryAdd(field.Alias, field); - } - - CIMRenderer layerRenderer = layer.GetRenderer(); - int count = 1; - using (RowCursor rowCursor = layer.Search()) - { - // if layer doesn't have a valid data source (and the conversion likely failed), don't create a colorProxy - if (rowCursor is null) - { - return; - } - while (rowCursor.MoveNext()) - { - string elementAppId = $"{layer.URI}_{count}"; - using (Row row = rowCursor.Current) - { - // get row color - int argb = GetLayerColorByRendererAndRow(layerRenderer, row, layerFieldDictionary); - string colorId = GetColorApplicationId(argb, displayPriority); - AddElementIdToColorProxy(elementAppId, argb, colorId, displayPriority); - } - - count++; - } - } - } - - // Attempts to retrieve the color from a CIMSymbol - private bool TryGetSymbolColor(CIMSymbol symbol, out int symbolColor) - { - symbolColor = -1; - if (symbol.GetColor() is CIMColor cimColor) - { - switch (cimColor) - { - case CIMRGBColor rgbColor: - symbolColor = rgbColor.CIMColorToInt(); - return true; - case CIMHSVColor hsvColor: - symbolColor = RgbFromHsv(hsvColor); - return true; - case CIMCMYKColor cmykColor: - symbolColor = RgbFromCmyk(cmykColor); - return true; - default: - return false; - } - } - else - { - return false; - } - } - - private int RbgToInt(int a, int r, int g, int b) - { - return (a << 24) | (r << 16) | (g << 8) | b; - } - - private int RgbFromCmyk(CIMCMYKColor cmykColor) - { - float c = cmykColor.C; - float m = cmykColor.M; - float y = cmykColor.Y; - float k = cmykColor.K; - - int r = Convert.ToInt32(255 * (1 - c) * (1 - k)); - int g = Convert.ToInt32(255 * (1 - m) * (1 - k)); - int b = Convert.ToInt32(255 * (1 - y) * (1 - k)); - return RbgToInt(255, r, g, b); - } - - private int RgbFromHsv(CIMHSVColor hsvColor) - { - // Translates HSV color to RGB color - // H: 0.0 - 360.0, S: 0.0 - 100.0, V: 0.0 - 100.0 - // R, G, B: 0.0 - 1.0 - - float hue = hsvColor.H; - float saturation = hsvColor.S; - float value = hsvColor.V; - - float c = (value / 100) * (saturation / 100); - float x = c * (1 - Math.Abs(((hue / 60) % 2) - 1)); - float m = (value / 100) - c; - - float r = 0; - float g = 0; - float b = 0; - - if (hue >= 0 && hue < 60) - { - r = c; - g = x; - b = 0; - } - else if (hue >= 60 && hue < 120) - { - r = x; - g = c; - b = 0; - } - else if (hue >= 120 && hue < 180) - { - r = 0; - g = c; - b = x; - } - else if (hue >= 180 && hue < 240) - { - r = 0; - g = x; - b = c; - } - else if (hue >= 240 && hue < 300) - { - r = x; - g = 0; - b = c; - } - else if (hue >= 300 && hue < 360) - { - r = c; - g = 0; - b = x; - } - - r += m; - g += m; - b += m; - - // convert rgb 0.0-1.0 float to int - int red = (int)Math.Round(r * 255); - int green = (int)Math.Round(g * 255); - int blue = (int)Math.Round(b * 255); - - return RbgToInt(255, red, green, blue); - } - - private bool TryGetUniqueRendererColor( - CIMUniqueValueRenderer uniqueRenderer, - Row row, - Dictionary fields, - out int color - ) - { - if (uniqueRenderer.DefaultSymbol is null) - { - color = RbgToInt(255, 255, 255, 255); - return false; - } - if (!TryGetSymbolColor(uniqueRenderer.DefaultSymbol.Symbol, out color)) // get default color - { - return false; - } - - // note: usually there is only 1 group - foreach (CIMUniqueValueGroup group in uniqueRenderer.Groups) - { - string[] fieldNames = uniqueRenderer.Fields; - List usedFields = new(); - foreach (string fieldName in fieldNames) - { - if (fields.TryGetValue(fieldName, out FieldDescription? headingField)) - { - usedFields.Add(headingField.Name); - } - } - - // loop through all values in groups to see if any have met conditions that result in a different color - foreach (CIMUniqueValueClass groupClass in group.Classes) - { - bool groupConditionsMet = true; - foreach (CIMUniqueValue value in groupClass.Values) - { - // all field values have to match the row values - for (int i = 0; i < usedFields.Count; i++) - { - string groupValue = value.FieldValues[i].Replace("", ""); - object? rowValue = row[usedFields[i]]; - - (string newRowValue, string newGroupValue) = MakeValuesComparable(rowValue, groupValue); - if (newGroupValue != newRowValue) - { - groupConditionsMet = false; - break; - } - } - } - - // set the group color to class symbol color if conditions are met - if (groupConditionsMet) - { - if (groupClass.Symbol is null) - { - color = RbgToInt(255, 255, 255, 255); - return false; - } - if (!TryGetSymbolColor(groupClass.Symbol.Symbol, out color)) - { - return false; - } - } - } - } - - return true; - } - - /// - /// Make comparable the Label string of a UniqueValueRenderer (groupValue), and a Feature Attribute value (rowValue) - /// - /// - /// - private (string, string) MakeValuesComparable(object? rowValue, string groupValue) - { - string newGroupValue = groupValue; - string newRowValue = Convert.ToString(rowValue) ?? ""; - - // int, doubles are tricky to compare with strings, trimming both to 5 digits - if (rowValue is int or short or long) - { - newRowValue = newRowValue.Split(".")[0]; - newGroupValue = newGroupValue.Split(".")[0]; - } - else if (rowValue is double || rowValue is float) - { - newRowValue = string.Concat( - newRowValue.Split(".")[0], - ".", - newRowValue.Split(".")[^1].AsSpan(0, Math.Min(5, newRowValue.Split(".")[^1].Length)) - ); - newGroupValue = string.Concat( - newGroupValue.Split(".")[0], - ".", - newGroupValue.Split(".")[^1].AsSpan(0, Math.Min(5, newGroupValue.Split(".")[^1].Length)) - ); - } - - return (newRowValue, newGroupValue); - } - - private bool TryGetGraduatedRendererColor( - CIMClassBreaksRenderer graduatedRenderer, - Row row, - Dictionary fields, - out int color - ) - { - if (graduatedRenderer.DefaultSymbol is null) - { - color = RbgToInt(255, 255, 255, 255); - return false; - } - if (!TryGetSymbolColor(graduatedRenderer.DefaultSymbol.Symbol, out color)) // get default color - { - return false; - } - - string? usedField = null; - if (fields.TryGetValue(graduatedRenderer.Field, out FieldDescription? field)) - { - usedField = field.Name; - } - - List reversedBreaks = new(graduatedRenderer.Breaks); - reversedBreaks.Reverse(); - foreach (var rBreak in reversedBreaks) - { - // keep looping until the last matching condition - if (Convert.ToDouble(row[usedField]) <= rBreak.UpperBound) - { - if (!TryGetSymbolColor(rBreak.Symbol.Symbol, out color)) // get default color - { - return false; - } - } - } - - return true; - } - - // Tries to retrieve the feature layer color by renderer and row, or a default color of -1 - private int GetLayerColorByRendererAndRow(CIMRenderer renderer, Row row, Dictionary fields) - { - // default color to white. this will be used if the renderer is not supported. - int color = -1; - - // get color depending on renderer type - switch (renderer) - { - case CIMSimpleRenderer simpleRenderer: - if (!TryGetSymbolColor(simpleRenderer.Symbol.Symbol, out color)) - { - // POC: report CONVERTED WITH WARNING when implemented - } - break; - - // unique renderers have groups of conditions that may affect the color of a feature - // resulting in a different color than the default renderer symbol color - case CIMUniqueValueRenderer uniqueRenderer: - if (!TryGetUniqueRendererColor(uniqueRenderer, row, fields, out color)) // get default color - { - // POC: report CONVERTED WITH WARNING when implemented - } - break; - - case CIMClassBreaksRenderer graduatedRenderer: - if (!TryGetGraduatedRendererColor(graduatedRenderer, row, fields, out color)) // get default color - { - // POC: report CONVERTED WITH WARNING when implemented - } - break; - - default: - // POC: report CONVERTED WITH WARNING when implemented, unsupported renderer e.g. CIMProportionalRenderer - break; - } - - return color; - } } diff --git a/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/HostApp/ArcGISColorUnpacker.cs b/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/HostApp/ArcGISColorUnpacker.cs new file mode 100644 index 000000000..c8c6dd838 --- /dev/null +++ b/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/HostApp/ArcGISColorUnpacker.cs @@ -0,0 +1,461 @@ +using ArcGIS.Desktop.Mapping; +using Speckle.Sdk.Models.Proxies; + +namespace Speckle.Connectors.ArcGIS.HostApp; + +public class ArcGISColorUnpacker +{ + /// + /// Cache of all color proxies for converted features. Key is the Color proxy argb value. + /// + public Dictionary ColorProxyCache { get; } = new(); + + /// + /// Stores the current renderer (determined by mapMember) + /// + private AC.CIM.CIMRenderer? StoredRenderer { get; set; } + + /// + /// Stores the current renderer (determined by tin mapmember) + /// + private AC.CIM.CIMTinRenderer? StoredTinRenderer { get; set; } + + /// + /// Stores the used renderer fields from the layer + /// + private List StoredRendererFields { get; set; } + + /// + /// Stores an already processed color for current mapMember, to dbe used by all mapMember objects. Only applies to simple type renderers + /// + private int? StoredColor { get; set; } + + /// + /// Stores a feature layer renderer to be used by in , any fields used by the renderer from the layer, and resets the and + /// + /// + /// Must be called on MCT. + public void StoreRendererAndFields(ADM.FeatureLayer featureLayer) + { + // field names are unique, but often their alias is used instead by renderer headings + // so we are storing both names and alias in this dictionary for fast lookup + // POC: adding aliases are not optimal, because they do not need to be unique && they can be the same as the name of another field + Dictionary layerFieldDictionary = new(); + foreach (ADM.FieldDescription field in featureLayer.GetFieldDescriptions()) + { + layerFieldDictionary.TryAdd(field.Name, field.Name); + layerFieldDictionary.TryAdd(field.Alias, field.Name); + } + + // clear stored values + StoredRendererFields = new(); + StoredColor = null; + StoredRenderer = null; + + AC.CIM.CIMRenderer layerRenderer = featureLayer.GetRenderer(); + List fields = new(); + bool isSupported = false; + switch (layerRenderer) + { + case AC.CIM.CIMSimpleRenderer: + isSupported = true; + break; + case AC.CIM.CIMUniqueValueRenderer uniqueValueRenderer: + isSupported = true; + fields = uniqueValueRenderer.Fields.ToList(); + break; + case AC.CIM.CIMClassBreaksRenderer classBreaksRenderer: + isSupported = true; + fields.Add(classBreaksRenderer.Field); + break; + default: + // TODO: log error here that a renderer is unsupported + break; + } + + if (isSupported) + { + StoredRenderer = layerRenderer; + foreach (string field in fields) + { + if (layerFieldDictionary.TryGetValue(field, out string? fieldName)) + { + StoredRendererFields.Add(fieldName); + } + } + } + } + + /// + /// Stores a las layer renderer to be used by in + /// + /// + /// Must be called on MCT. + public void StoreRenderer(ADM.LasDatasetLayer lasLayer) + { + // clear stored values + StoredTinRenderer = null; + + // POC: not sure why we are only using the first renderer here + AC.CIM.CIMTinRenderer layerRenderer = lasLayer.GetRenderers()[0]; + bool isSupported = false; + switch (layerRenderer) + { + case AC.CIM.CIMTinUniqueValueRenderer: + isSupported = true; + break; + default: + // TODO: log error here that a renderer is unsupported + break; + } + + if (isSupported) + { + StoredTinRenderer = layerRenderer; + } + } + + /// + /// Processes a las layer's point color by the stored , and stores the point's id and color proxy to the . + /// POC: logic probably can be combined with ProcessFeatureLayerColor. + /// + /// + public void ProcessLasLayerColor(ACD.Analyst3D.LasPoint point, string pointApplicationId) + { + // get the color from the renderer and point + AC.CIM.CIMColor? color; + switch (StoredTinRenderer) + { + case AC.CIM.CIMTinUniqueValueRenderer uniqueValueRenderer: + color = GetPointColorByUniqueValueRenderer(uniqueValueRenderer, point); + break; + + default: + return; + } + + // get or create the color proxy for the point + int argb = CIMColorToInt(color ?? point.RGBColor); + AddObjectIdToColorProxyCache(pointApplicationId, argb); + } + + // Retrieves the las point color from a unique value renderer + // unique renderers have groups of conditions that may affect the color of a feature + // resulting in a different color than the default renderer symbol color + private AC.CIM.CIMColor? GetPointColorByUniqueValueRenderer( + AC.CIM.CIMTinUniqueValueRenderer renderer, + ACD.Analyst3D.LasPoint point + ) + { + foreach (AC.CIM.CIMUniqueValueGroup group in renderer.Groups) + { + foreach (AC.CIM.CIMUniqueValueClass groupClass in group.Classes) + { + foreach (AC.CIM.CIMUniqueValue value in groupClass.Values) + { + // all field values have to match the row values + for (int i = 0; i < value.FieldValues.Length; i++) + { + string groupValue = value.FieldValues[i].Replace("", ""); + object? pointValue = point.ClassCode; + + if (ValuesAreEqual(groupValue, pointValue)) + { + return groupClass.Symbol.Symbol.GetColor(); + } + } + } + } + } + + return null; + } + + /// + /// Processes a feature layer's row color by the stored , and stores the row's id and color proxy to the . + /// + /// + /// + /// + public void ProcessFeatureLayerColor(ACD.Row row, string rowApplicationId) + { + // if stored color is not null, this means the renderer was a simple renderer that applies to the entire layer, and was already created. + // just add the row application id to the color proxy. + if (StoredColor is int existingColorProxyId) + { + AddObjectIdToColorProxyCache(rowApplicationId, existingColorProxyId); + return; + } + + // get the color from the renderer and row + AC.CIM.CIMColor? color = null; + switch (StoredRenderer) + { + // simple renderers do not rely on fields, so the color can be retrieved from the renderer directly + case AC.CIM.CIMSimpleRenderer simpleRenderer: + color = simpleRenderer.Symbol.Symbol.GetColor(); + break; + + case AC.CIM.CIMUniqueValueRenderer uniqueValueRenderer: + color = GetRowColorByUniqueValueRenderer(uniqueValueRenderer, row); + break; + + case AC.CIM.CIMClassBreaksRenderer classBreaksRenderer: + color = GetRowColorByClassBreaksRenderer(classBreaksRenderer, row); + break; + } + + if (color is null) + { + // TODO: log error or throw exception that color could not be retrieved + return; + } + + // get or create the color proxy for the row + int argb = CIMColorToInt(color); + AddObjectIdToColorProxyCache(rowApplicationId, argb); + + // store color if from simple renderer + if (StoredRenderer is AC.CIM.CIMSimpleRenderer) + { + StoredColor = argb; + } + } + + // Retrieves the row color from a class breaks renderer + // unique renderers have groups of conditions that may affect the color of a feature + // resulting in a different color than the default renderer symbol color + private AC.CIM.CIMColor? GetRowColorByClassBreaksRenderer(AC.CIM.CIMClassBreaksRenderer renderer, ACD.Row row) + { + AC.CIM.CIMColor? color = null; + + // get the default symbol color + if (renderer.DefaultSymbol?.Symbol.GetColor() is AC.CIM.CIMColor defaultColor) + { + color = defaultColor; + } + + // get the first stored field, since this renderer should only have 1 field + double storedFieldValue = Convert.ToDouble(row[StoredRendererFields.First()]); + + List reversedBreaks = new(renderer.Breaks); + reversedBreaks.Reverse(); + foreach (var rBreak in reversedBreaks) + { + // keep looping until the last matching condition + if (storedFieldValue <= rBreak.UpperBound) + { + if (rBreak.Symbol.Symbol.GetColor() is AC.CIM.CIMColor breakColor) + { + color = breakColor; + } + else + { + // TODO: log error here, could not retrieve break color from symbol + } + } + } + + return color; + } + + // Retrieves the row color from a unique value renderer + // unique renderers have groups of conditions that may affect the color of a feature + // resulting in a different color than the default renderer symbol color + private AC.CIM.CIMColor? GetRowColorByUniqueValueRenderer(AC.CIM.CIMUniqueValueRenderer renderer, ACD.Row row) + { + AC.CIM.CIMColor? color = null; + + // get the default symbol color + if (renderer.DefaultSymbol?.Symbol.GetColor() is AC.CIM.CIMColor defaultColor) + { + color = defaultColor; + } + + // note: usually there is only 1 group + foreach (AC.CIM.CIMUniqueValueGroup group in renderer.Groups) + { + // loop through all values in groups to see if any have met conditions that result in a different color + foreach (AC.CIM.CIMUniqueValueClass groupClass in group.Classes) + { + bool groupConditionsMet = true; + foreach (AC.CIM.CIMUniqueValue value in groupClass.Values) + { + // all field values have to match the row values + for (int i = 0; i < StoredRendererFields.Count; i++) + { + string groupValue = value.FieldValues[i]; + object? rowValue = row[StoredRendererFields[i]]; + + if (!ValuesAreEqual(groupValue, rowValue)) + { + groupConditionsMet = false; + break; + } + } + } + + // set the group color to class symbol color if conditions are met + if (groupConditionsMet) + { + if (groupClass.Symbol.Symbol.GetColor() is AC.CIM.CIMColor groupColor) + { + color = groupColor; + } + else + { + // TODO: log error here, could not retrieve group color from symbol + } + } + } + } + + return color; + } + + /// + /// Compares the label string of a UniqueValueRenderer (groupValue), and an object value (row, las point), to determine if they are equal + /// + /// + /// + private bool ValuesAreEqual(string groupValue, object? objectValue) + { + switch (objectValue) + { + case int: + case short: + case long: + case byte: + string objectValueString = Convert.ToString(objectValue) ?? ""; + return groupValue.Equals(objectValueString); + + case string: + return groupValue.Equals(objectValue); + + // POC: these are tricky to compare with the label strings accurately, so will trim both values to 5 decimal places. + case double d: + return double.TryParse(groupValue, out double groupDouble) && groupDouble - d < 0.000001; + case float f: + return float.TryParse(groupValue, out float groupFloat) && groupFloat - f < 0.000001; + + default: + return false; + } + } + + private void AddObjectIdToColorProxyCache(string objectId, int argb) + { + if (ColorProxyCache.TryGetValue(argb, out ColorProxy? colorProxy)) + { + colorProxy.objects.Add(objectId); + } + else + { + ColorProxy newColorProxy = + new() + { + name = argb.ToString(), + objects = new() { objectId }, + value = argb, + applicationId = argb.ToString() + }; + + ColorProxyCache.Add(argb, newColorProxy); + } + } + + private int ArgbToInt(int a, int r, int g, int b) + { + return (a << 24) | (r << 16) | (g << 8) | b; + } + + // Gets the argb int from a CIMColor + // Defaults to assuming CIMColor.Values represent the red, green, and blue channels. + private int CIMColorToInt(AC.CIM.CIMColor color) + { + switch (color) + { + case AC.CIM.CIMHSVColor hsv: + (float hsvR, float hsvG, float hsvB) = RgbFromHsv(hsv.H, hsv.S, hsv.V); + return ArgbToInt( + (int)Math.Round(hsv.Alpha), + (int)Math.Round(hsvR * 255), + (int)Math.Round(hsvG * 255), + (int)Math.Round(hsvB * 255) + ); + + case AC.CIM.CIMCMYKColor cmyk: + float k = cmyk.K; + int cmykR = Convert.ToInt32(255 * (1 - cmyk.C) * (1 - k)); + int cmykG = Convert.ToInt32(255 * (1 - cmyk.M) * (1 - k)); + int cmykB = Convert.ToInt32(255 * (1 - cmyk.Y) * (1 - k)); + return ArgbToInt((int)Math.Round(cmyk.Alpha), cmykR, cmykG, cmykB); + + default: + return ArgbToInt( + (int)Math.Round(color.Alpha), + (int)Math.Round(color.Values[0]), + (int)Math.Round(color.Values[1]), + (int)Math.Round(color.Values[2]) + ); + } + } + + private (float, float, float) RgbFromHsv(float hue, float saturation, float value) + { + // Translates HSV color to RGB color + // H: 0.0 - 360.0, S: 0.0 - 100.0, V: 0.0 - 100.0 + // R, G, B: 0.0 - 1.0 + + float c = (value / 100) * (saturation / 100); + float x = c * (1 - Math.Abs(((hue / 60) % 2) - 1)); + float m = (value / 100) - c; + + float r = 0; + float g = 0; + float b = 0; + + if (hue >= 0 && hue < 60) + { + r = c; + g = x; + b = 0; + } + else if (hue >= 60 && hue < 120) + { + r = x; + g = c; + b = 0; + } + else if (hue >= 120 && hue < 180) + { + r = 0; + g = c; + b = x; + } + else if (hue >= 180 && hue < 240) + { + r = 0; + g = x; + b = c; + } + else if (hue >= 240 && hue < 300) + { + r = x; + g = 0; + b = c; + } + else if (hue >= 300 && hue < 360) + { + r = c; + g = 0; + b = x; + } + + r += m; + g += m; + b += m; + + return (r, g, b); + } +} diff --git a/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/HostApp/ArcGISLayerUnpacker.cs b/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/HostApp/ArcGISLayerUnpacker.cs new file mode 100644 index 000000000..3db6653e0 --- /dev/null +++ b/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/HostApp/ArcGISLayerUnpacker.cs @@ -0,0 +1,97 @@ +using Speckle.Connectors.ArcGIS.HostApp.Extensions; +using Speckle.Sdk.Models.Collections; + +namespace Speckle.Connectors.ArcGIS.HostApp; + +public class ArcGISLayerUnpacker +{ + /// + /// Cache of all collections created by unpacked Layer MapMembers. Key is the Speckle applicationId (Layer URI). + /// + public Dictionary CollectionCache { get; } = new(); + + /// + /// Mapmembers can be layers containing objects, or LayerContainers containing other layers. + /// Unpacks selected mapMembers and creates their corresponding collection on the root collection. + /// + /// + /// + /// List of layers containing objects. + /// Thrown when this method is *not* called on the MCT, because this method accesses mapmember fields + public List UnpackSelection( + IEnumerable mapMembers, + Collection parentCollection, + List? objects = null + ) + { + if (objects is null) + { + objects = new(); + } + + foreach (ADM.MapMember mapMember in mapMembers) + { + switch (mapMember) + { + case ADM.ILayerContainer container: + Collection containerCollection = CreateAndCacheMapMemberCollection(mapMember, true); + parentCollection.elements.Add(containerCollection); + + UnpackSelection(container.Layers, containerCollection, objects); + break; + + default: + if (!(objects.Contains(mapMember))) + { + Collection collection = CreateAndCacheMapMemberCollection(mapMember); + parentCollection.elements.Add(collection); + objects.Add(mapMember); + } + break; + } + } + + return objects; + } + + private Collection CreateAndCacheMapMemberCollection(ADM.MapMember mapMember, bool isLayerContainer = false) + { + string mapMemberApplicationId = mapMember.GetSpeckleApplicationId(); + Collection collection = + new() + { + name = mapMember.Name, + applicationId = mapMemberApplicationId, + ["type"] = mapMember.GetType().Name + }; + + switch (mapMember) + { + case ADM.IDisplayTable displayTable: // get fields from layers that implement IDisplayTable, eg FeatureLayer or StandaloneTable + Dictionary? fields = displayTable + .GetFieldDescriptions() + .ToDictionary(field => field.Name, field => field.Type.ToString()); + collection["fields"] = fields; + if (mapMember is ADM.BasicFeatureLayer basicFeatureLayer) + { + collection["shapeType"] = basicFeatureLayer.ShapeType.ToString(); + } + break; + + case ADM.Layer layer: + collection["mapLayerType"] = layer.MapLayerType.ToString(); + break; + + case ADM.ILayerContainer: + default: + break; + } + + if (!isLayerContainer) // do not cache layer containers, since these won't contain any objects + { + CollectionCache.Add(mapMemberApplicationId, collection); + } + + return collection; + } +} diff --git a/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/HostApp/Extensions/SpeckleApplicationIdExtensions.cs b/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/HostApp/Extensions/SpeckleApplicationIdExtensions.cs new file mode 100644 index 000000000..8a796234e --- /dev/null +++ b/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/HostApp/Extensions/SpeckleApplicationIdExtensions.cs @@ -0,0 +1,31 @@ +using ArcGIS.Core.Data.Raster; + +namespace Speckle.Connectors.ArcGIS.HostApp.Extensions; + +public static class SpeckleApplicationIdExtensions +{ + /// + /// Retrieves the Speckle application id for map members + /// + public static string GetSpeckleApplicationId(this ADM.MapMember mapMember) => mapMember.URI; + + /// + /// Constructs the Speckle application id for Features as a concatenation of the layer URI (applicationId) + /// and the row OID (index of row in layer). + /// + /// Throws when this is *not* called on MCT. Use QueuedTask.Run. + public static string GetSpeckleApplicationId(this ACD.Row row, string layerApplicationId) => + $"{layerApplicationId}_{row.GetObjectID()}"; + + /// + /// Constructs the Speckle application id for Raster as a concatenation of the layer URI (applicationId) and 0-index + /// + public static string GetSpeckleApplicationId(this Raster _, string layerApplicationId) => $"{layerApplicationId}_0"; + + /// + /// Constructs the Speckle application id for LasDatasets as a concatenation of the layer URI (applicationId) + /// and point OID. + /// + public static string GetSpeckleApplicationId(this ACD.Analyst3D.LasPoint point, string layerApplicationId) => + $"{layerApplicationId}_{point.PointID}"; +} diff --git a/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Operations/Receive/ArcGISHostObjectBuilder.cs b/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Operations/Receive/ArcGISHostObjectBuilder.cs index 2b98a8300..b99dc225a 100644 --- a/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Operations/Receive/ArcGISHostObjectBuilder.cs +++ b/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Operations/Receive/ArcGISHostObjectBuilder.cs @@ -12,7 +12,7 @@ using Speckle.Connectors.Common.Operations; using Speckle.Converters.ArcGIS3; using Speckle.Converters.ArcGIS3.Utils; using Speckle.Converters.Common; -using Speckle.Objects.GIS; +using Speckle.Objects.Data; using Speckle.Objects.Other; using Speckle.Sdk; using Speckle.Sdk.Models; @@ -20,7 +20,6 @@ using Speckle.Sdk.Models.Collections; using Speckle.Sdk.Models.GraphTraversal; using Speckle.Sdk.Models.Instances; using Speckle.Sdk.Models.Proxies; -using RasterLayer = Speckle.Objects.GIS.RasterLayer; namespace Speckle.Connectors.ArcGIS.Operations.Receive; @@ -30,7 +29,6 @@ public class ArcGISHostObjectBuilder : IHostObjectBuilder private readonly IFeatureClassUtils _featureClassUtils; private readonly ILocalToGlobalUnpacker _localToGlobalUnpacker; private readonly LocalToGlobalConverterUtils _localToGlobalConverterUtils; - private readonly ICrsUtils _crsUtils; // POC: figure out the correct scope to only initialize on Receive private readonly IConverterSettingsStore _settingsStore; @@ -43,7 +41,6 @@ public class ArcGISHostObjectBuilder : IHostObjectBuilder IFeatureClassUtils featureClassUtils, ILocalToGlobalUnpacker localToGlobalUnpacker, LocalToGlobalConverterUtils localToGlobalConverterUtils, - ICrsUtils crsUtils, GraphTraversal traverseFunction, ArcGISColorManager colorManager ) @@ -55,10 +52,22 @@ public class ArcGISHostObjectBuilder : IHostObjectBuilder _localToGlobalConverterUtils = localToGlobalConverterUtils; _traverseFunction = traverseFunction; _colorManager = colorManager; - _crsUtils = crsUtils; } - public async Task Build( + public Task Build( + Base rootObject, + string projectName, + string modelName, + IProgress onOperationProgressed, + CancellationToken cancellationToken + ) + { + return QueuedTask.Run( + () => BuildInternal(rootObject, projectName, modelName, onOperationProgressed, cancellationToken) + ); + } + + private HostObjectBuilderResult BuildInternal( Base rootObject, string projectName, string modelName, @@ -78,18 +87,18 @@ public class ArcGISHostObjectBuilder : IHostObjectBuilder .ToList(); if (materials != null) { - await _colorManager.ParseMaterials(materials, onOperationProgressed).ConfigureAwait(false); + _colorManager.ParseMaterials(materials, onOperationProgressed); } // get colors List? colors = (rootObject[ProxyKeys.COLOR] as List)?.Cast().ToList(); if (colors != null) { - await _colorManager.ParseColors(colors, onOperationProgressed).ConfigureAwait(false); + _colorManager.ParseColors(colors, onOperationProgressed); } int count = 0; - List objectsToConvert = GetObjectsToConvert(rootObject); + IReadOnlyCollection objectsToConvert = GetObjectsToConvert(rootObject); Dictionary conversionTracker = new(); // 1. convert everything @@ -104,13 +113,15 @@ public class ArcGISHostObjectBuilder : IHostObjectBuilder try { obj = _localToGlobalConverterUtils.TransformObjects(objectToConvert.AtomicObject, objectToConvert.Matrix); - object? conversionResult = - obj is GisNonGeometricFeature - ? null - : await QueuedTask.Run(() => _converter.Convert(obj)).ConfigureAwait(false); + object conversionResult = _converter.Convert(obj); string nestedLayerPath = $"{string.Join("\\", path)}"; - if (objectToConvert.TraversalContext.Parent?.Current is not VectorLayer) + + if (obj is ArcgisObject gisObj) + { + nestedLayerPath += $"\\{gisObj.name}"; + } + else { nestedLayerPath += $"\\{obj.speckle_type.Split(".")[^1]}"; // add sub-layer by speckleType, for non-GIS objects } @@ -130,29 +141,20 @@ public class ArcGISHostObjectBuilder : IHostObjectBuilder // 2.1. Group conversionTrackers (to write into datasets) onOperationProgressed.Report(new("Grouping features into layers", null)); - Dictionary> convertedGroups = await QueuedTask - .Run(async () => - { - return await _featureClassUtils - .GroupConversionTrackers(conversionTracker, (s, progres) => onOperationProgressed.Report(new(s, progres))) - .ConfigureAwait(false); - }) - .ConfigureAwait(false); + Dictionary> convertedGroups = + _featureClassUtils.GroupConversionTrackers( + conversionTracker, + (s, progres) => onOperationProgressed.Report(new(s, progres)) + ); // 2.2. Write groups of objects to Datasets onOperationProgressed.Report(new("Writing to Database", null)); - await QueuedTask - .Run(async () => - { - await _featureClassUtils - .CreateDatasets( - conversionTracker, - convertedGroups, - (s, progres) => onOperationProgressed.Report(new(s, progres)) - ) - .ConfigureAwait(false); - }) - .ConfigureAwait(false); + + _featureClassUtils.CreateDatasets( + conversionTracker, + convertedGroups, + (s, progres) => onOperationProgressed.Report(new(s, progres)) + ); // 3. add layer and tables to the Map and Table Of Content @@ -204,8 +206,7 @@ public class ArcGISHostObjectBuilder : IHostObjectBuilder else { // no layer yet, create and add layer to Map - MapMember mapMember = await AddDatasetsToMap(trackerItem, createdLayerGroups, projectName, modelName) - .ConfigureAwait(false); + MapMember mapMember = AddDatasetsToMap(trackerItem, createdLayerGroups, projectName, modelName); // add layer and layer URI to tracker trackerItem.AddConvertedMapMember(mapMember); @@ -233,24 +234,20 @@ public class ArcGISHostObjectBuilder : IHostObjectBuilder if (bakedMember.Value.Item1 is FeatureLayer fLayer) { // Set the feature layer's renderer. - await QueuedTask.Run(() => fLayer.SetRenderer(bakedMember.Value.Item2)).ConfigureAwait(false); + fLayer.SetRenderer(bakedMember.Value.Item2); } } bakedObjectIds.AddRange(createdLayerGroups.Values.Select(x => x.URI)); // TODO: validated a correct set regarding bakedobject ids - return new(bakedObjectIds, results); + return new HostObjectBuilderResult(bakedObjectIds, results); } - private List GetObjectsToConvert(Base rootObject) + private IReadOnlyCollection GetObjectsToConvert(Base rootObject) { // keep GISlayers in the list, because they are still needed to extract CRS of the commit (code below) List objectsToConvertTc = _traverseFunction.Traverse(rootObject).ToList(); - // get CRS from any present VectorLayer - Base? vLayer = objectsToConvertTc.FirstOrDefault(x => x.Current is VectorLayer)?.Current; - using var crs = _crsUtils.FindSetCrsDataOnReceive(vLayer); // TODO help - // now filter the objects objectsToConvertTc = objectsToConvertTc.Where(ctx => ctx.Current is not Collection).ToList(); @@ -304,80 +301,72 @@ public class ArcGISHostObjectBuilder : IHostObjectBuilder } } - private async Task AddDatasetsToMap( + private MapMember AddDatasetsToMap( ObjectConversionTracker trackerItem, Dictionary createdLayerGroups, string projectName, string modelName ) { - return await QueuedTask - .Run(() => + // get layer details + string? datasetId = trackerItem.DatasetId; // should not be null here + Uri uri = new($"{_settingsStore.Current.SpeckleDatabasePath.AbsolutePath.Replace('/', '\\')}\\{datasetId}"); + string nestedLayerName = trackerItem.NestedLayerName; + + // add group for the current layer + string shortName = nestedLayerName.Split("\\")[^1]; + string nestedLayerPath = string.Join("\\", nestedLayerName.Split("\\").SkipLast(1)); + + // if no general group layer found + if (createdLayerGroups.Count == 0) + { + Map map = _settingsStore.Current.Map; + GroupLayer mainGroupLayer = LayerFactory.Instance.CreateGroupLayer(map, 0, $"{projectName}: {modelName}"); + mainGroupLayer.SetExpanded(true); + createdLayerGroups["Basic Speckle Group"] = mainGroupLayer; // key doesn't really matter here + } + + var groupLayer = CreateNestedGroupLayer(nestedLayerPath, createdLayerGroups); + + // Most of the Speckle-written datasets will be containing geometry and added as Layers + // although, some datasets might be just tables (e.g. native GIS Tables, in the future maybe Revit schedules etc. + // We can create a connection to the dataset in advance and determine its type, but this will be more + // expensive, than assuming by default that it's a layer with geometry (which in most cases it's expected to be) + try + { + var layer = LayerFactory.Instance.CreateLayer(uri, groupLayer, layerName: shortName); + if (layer == null) { - // get layer details - string? datasetId = trackerItem.DatasetId; // should not be null here - Uri uri = new($"{_settingsStore.Current.SpeckleDatabasePath.AbsolutePath.Replace('/', '\\')}\\{datasetId}"); - string nestedLayerName = trackerItem.NestedLayerName; + throw new SpeckleException($"Layer '{shortName}' was not created"); + } + layer.SetExpanded(false); - // add group for the current layer - string shortName = nestedLayerName.Split("\\")[^1]; - string nestedLayerPath = string.Join("\\", nestedLayerName.Split("\\").SkipLast(1)); + // if Scene + // https://community.esri.com/t5/arcgis-pro-sdk-questions/sdk-equivalent-to-changing-layer-s-elevation/td-p/1346139 + if (_settingsStore.Current.Map.IsScene) + { + var groundSurfaceLayer = _settingsStore.Current.Map.GetGroundElevationSurfaceLayer(); + var layerElevationSurface = new CIMLayerElevationSurface { ElevationSurfaceLayerURI = groundSurfaceLayer.URI, }; - // if no general group layer found - if (createdLayerGroups.Count == 0) + // for Feature Layers + if (layer.GetDefinition() is CIMFeatureLayer cimLyr) { - Map map = _settingsStore.Current.Map; - GroupLayer mainGroupLayer = LayerFactory.Instance.CreateGroupLayer(map, 0, $"{projectName}: {modelName}"); - mainGroupLayer.SetExpanded(true); - createdLayerGroups["Basic Speckle Group"] = mainGroupLayer; // key doesn't really matter here + cimLyr.LayerElevation = layerElevationSurface; + layer.SetDefinition(cimLyr); } + } - var groupLayer = CreateNestedGroupLayer(nestedLayerPath, createdLayerGroups); - - // Most of the Speckle-written datasets will be containing geometry and added as Layers - // although, some datasets might be just tables (e.g. native GIS Tables, in the future maybe Revit schedules etc. - // We can create a connection to the dataset in advance and determine its type, but this will be more - // expensive, than assuming by default that it's a layer with geometry (which in most cases it's expected to be) - try - { - var layer = LayerFactory.Instance.CreateLayer(uri, groupLayer, layerName: shortName); - if (layer == null) - { - throw new SpeckleException($"Layer '{shortName}' was not created"); - } - layer.SetExpanded(false); - - // if Scene - // https://community.esri.com/t5/arcgis-pro-sdk-questions/sdk-equivalent-to-changing-layer-s-elevation/td-p/1346139 - if (_settingsStore.Current.Map.IsScene) - { - var groundSurfaceLayer = _settingsStore.Current.Map.GetGroundElevationSurfaceLayer(); - var layerElevationSurface = new CIMLayerElevationSurface - { - ElevationSurfaceLayerURI = groundSurfaceLayer.URI, - }; - - // for Feature Layers - if (layer.GetDefinition() is CIMFeatureLayer cimLyr) - { - cimLyr.LayerElevation = layerElevationSurface; - layer.SetDefinition(cimLyr); - } - } - - return (MapMember)layer; - } - catch (ArgumentException) - { - StandaloneTable table = StandaloneTableFactory.Instance.CreateStandaloneTable( - uri, - groupLayer, - tableName: shortName - ); - return table; - } - }) - .ConfigureAwait(false); + return layer; + } + catch (ArgumentException) + { + StandaloneTable table = StandaloneTableFactory.Instance.CreateStandaloneTable( + uri, + groupLayer, + tableName: shortName + ); + return table; + } } private GroupLayer CreateNestedGroupLayer(string nestedLayerPath, Dictionary createdLayerGroups) @@ -419,17 +408,4 @@ public class ArcGISHostObjectBuilder : IHostObjectBuilder var originalPath = reverseOrderPath.Reverse().ToArray(); return originalPath.Where(x => !string.IsNullOrEmpty(x)).ToArray(); } - - [Pure] - private static bool HasGISParent(TraversalContext context) - { - List gisLayers = context.GetAscendants().Where(IsGISType).Where(obj => obj != context.Current).ToList(); - return gisLayers.Count > 0; - } - - [Pure] - private static bool IsGISType(Base obj) - { - return obj is RasterLayer or VectorLayer; - } } diff --git a/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Operations/Send/ArcGISRootObjectBuilder.cs b/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Operations/Send/ArcGISRootObjectBuilder.cs index 08f3e2d0e..7f8ff6cfe 100644 --- a/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Operations/Send/ArcGISRootObjectBuilder.cs +++ b/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Operations/Send/ArcGISRootObjectBuilder.cs @@ -1,189 +1,207 @@ -using System.Diagnostics; +using ArcGIS.Core.Data.Raster; +using ArcGIS.Core.Geometry; using ArcGIS.Desktop.Framework.Threading.Tasks; -using ArcGIS.Desktop.Mapping; using Microsoft.Extensions.Logging; using Speckle.Connectors.ArcGIS.HostApp; +using Speckle.Connectors.ArcGIS.HostApp.Extensions; using Speckle.Connectors.ArcGIS.Utils; using Speckle.Connectors.Common.Builders; -using Speckle.Connectors.Common.Caching; using Speckle.Connectors.Common.Conversion; using Speckle.Connectors.Common.Extensions; using Speckle.Connectors.Common.Operations; using Speckle.Converters.ArcGIS3; using Speckle.Converters.Common; -using Speckle.Objects.GIS; using Speckle.Sdk; using Speckle.Sdk.Logging; using Speckle.Sdk.Models; using Speckle.Sdk.Models.Collections; -using Speckle.Sdk.Models.Proxies; namespace Speckle.Connectors.ArcGis.Operations.Send; /// /// Stateless builder object to turn an ISendFilter into a object /// -public class ArcGISRootObjectBuilder : IRootObjectBuilder +public class ArcGISRootObjectBuilder : IRootObjectBuilder { private readonly IRootToSpeckleConverter _rootToSpeckleConverter; - private readonly ISendConversionCache _sendConversionCache; - private readonly ArcGISColorManager _colorManager; + private readonly ArcGISLayerUnpacker _layerUnpacker; + private readonly ArcGISColorUnpacker _colorUnpacker; private readonly IConverterSettingsStore _converterSettings; - private readonly MapMembersUtils _mapMemberUtils; private readonly ILogger _logger; private readonly ISdkActivityFactory _activityFactory; + private readonly MapMembersUtils _mapMemberUtils; public ArcGISRootObjectBuilder( - ISendConversionCache sendConversionCache, - ArcGISColorManager colorManager, + ArcGISLayerUnpacker layerUnpacker, + ArcGISColorUnpacker colorUnpacker, IConverterSettingsStore converterSettings, IRootToSpeckleConverter rootToSpeckleConverter, - MapMembersUtils mapMemberUtils, ILogger logger, - ISdkActivityFactory activityFactory + ISdkActivityFactory activityFactory, + MapMembersUtils mapMemberUtils ) { - _sendConversionCache = sendConversionCache; - _colorManager = colorManager; + _layerUnpacker = layerUnpacker; + _colorUnpacker = colorUnpacker; _converterSettings = converterSettings; _rootToSpeckleConverter = rootToSpeckleConverter; - _mapMemberUtils = mapMemberUtils; _logger = logger; _activityFactory = activityFactory; + _mapMemberUtils = mapMemberUtils; } -#pragma warning disable CA1506 - public async Task Build( -#pragma warning restore CA1506 - IReadOnlyList objects, - SendInfo sendInfo, + public Task Build( + IReadOnlyList layers, + SendInfo __, IProgress onOperationProgressed, - CancellationToken ct = default + CancellationToken cancellationToken + ) => QueuedTask.Run(() => BuildInternal(layers, __, onOperationProgressed, cancellationToken)); + + private async Task BuildInternal( + IReadOnlyList layers, + SendInfo __, + IProgress onOperationProgressed, + CancellationToken cancellationToken ) { // TODO: add a warning if Geographic CRS is set // "Data has been sent in the units 'degrees'. It is advisable to set the project CRS to Projected type (e.g. EPSG:32631) to be able to receive geometry correctly in CAD/BIM software" - int count = 0; - Collection rootObjectCollection = new() { name = MapView.Active.Map.Name }; //TODO: Collections - rootObjectCollection["units"] = _converterSettings.Current.SpeckleUnits; - - List results = new(objects.Count); - var cacheHitCount = 0; - List<(ILayerContainer, Collection)> nestedGroups = new(); - - // reorder selected layers by Table of Content (TOC) order - List<(MapMember, int)> layersWithDisplayPriority = _mapMemberUtils.GetLayerDisplayPriority( - MapView.Active.Map, - objects - ); - - onOperationProgressed.Report(new("Converting", null)); - using (var __ = _activityFactory.Start("Converting objects")) - { - foreach ((MapMember mapMember, _) in layersWithDisplayPriority) + // 0 - Create Root collection and attach CRS properties + // CRS properties are useful for data based workflows coming out of gis applications + SpatialReference sr = _converterSettings.Current.ActiveCRSoffsetRotation.SpatialReference; + Dictionary spatialReference = + new() { - ct.ThrowIfCancellationRequested(); + ["name"] = sr.Name, + ["unit"] = sr.Unit.Name, + ["wkid"] = sr.Wkid, + ["wkt"] = sr.Wkt, + }; - using (var convertingActivity = _activityFactory.Start("Converting object")) + Dictionary crs = + new() + { + ["trueNorthRadians"] = _converterSettings.Current.ActiveCRSoffsetRotation.TrueNorthRadians, + ["latOffset"] = _converterSettings.Current.ActiveCRSoffsetRotation.LatOffset, + ["lonOffset"] = _converterSettings.Current.ActiveCRSoffsetRotation.LonOffset, + ["spatialReference"] = spatialReference + }; + + Collection rootCollection = + new() + { + name = ADM.MapView.Active.Map.Name, + ["units"] = _converterSettings.Current.SpeckleUnits, + ["crs"] = crs + }; + + // 1 - Unpack the selected mapmembers + // In Arcgis, mapmembers are collections of other mapmember or objects. + // We need to unpack the selected mapmembers into all leaf-level mapmembers (containing just objects) and build the root collection structure during unpacking. + // Mapmember dynamically attached properties are also added at this step. + List unpackedLayers; + Dictionary layersWithFeatureCount; + long allFeaturesCount; + ADM.Map map = ADM.MapView.Active.Map; + IEnumerable layersOrdered = _mapMemberUtils.GetMapMembersInOrder(map, layers); + using (var _ = _activityFactory.Start("Unpacking selection")) + { + unpackedLayers = _layerUnpacker.UnpackSelection(layersOrdered, rootCollection); + + // count number of features to convert. Raster layers are counter as 1 feature for now (not ideal) + layersWithFeatureCount = CountAllFeaturesInLayers(unpackedLayers); + allFeaturesCount = layersWithFeatureCount.Values.Sum(); + } + + List results = new(unpackedLayers.Count); + onOperationProgressed.Report(new("Converting", null)); + using (var convertingActivity = _activityFactory.Start("Converting objects")) + { + long count = 0; + + foreach (var (layer, layerFeatureCount) in layersWithFeatureCount) + { + cancellationToken.ThrowIfCancellationRequested(); + string layerApplicationId = layer.GetSpeckleApplicationId(); + + try { - var collectionHost = rootObjectCollection; - string applicationId = mapMember.URI; - string sourceType = mapMember.GetType().Name; - - Base converted; - try + // get the corresponding collection for this layer - we'll add all converted objects to the collection + if (_layerUnpacker.CollectionCache.TryGetValue(layerApplicationId, out Collection? layerCollection)) { - int groupCount = nestedGroups.Count; // bake here, because count will change in the loop - // if the layer is not a part of the group, reset groups - for (int i = 0; i < groupCount; i++) + var status = Status.SUCCESS; + var sdkStatus = SdkActivityStatusCode.Ok; + + // TODO: check cache first to see if this layer was previously converted + /* + if (_sendConversionCache.TryGetValue( + sendInfo.ProjectId, + layerApplicationId, + out ObjectReference? value + )) { - if (nestedGroups.Count > 0 && !nestedGroups[0].Item1.Layers.Select(x => x.URI).Contains(applicationId)) - { - nestedGroups.RemoveAt(0); - } - else - { - // break at the first group, which contains current layer + + } + */ + + switch (layer) + { + case ADM.FeatureLayer featureLayer: + List convertedFeatureLayerObjects = ConvertFeatureLayerObjects( + featureLayer, + count, + allFeaturesCount, + onOperationProgressed, + cancellationToken + ); + layerCollection.elements.AddRange(convertedFeatureLayerObjects); + break; + case ADM.RasterLayer rasterLayer: + List convertedRasterLayerObjects = ConvertRasterLayerObjects( + rasterLayer, + count, + allFeaturesCount, + onOperationProgressed, + cancellationToken + ); + layerCollection.elements.AddRange(convertedRasterLayerObjects); + break; + case ADM.LasDatasetLayer lasDatasetLayer: + List convertedLasDatasetObjects = ConvertLasDatasetLayerObjects( + lasDatasetLayer, + count, + allFeaturesCount, + onOperationProgressed, + cancellationToken + ); + layerCollection.elements.AddRange(convertedLasDatasetObjects); + break; + default: + status = Status.ERROR; + sdkStatus = SdkActivityStatusCode.Error; break; - } } - // don't use cache for group layers - if ( - mapMember is not ILayerContainer - && _sendConversionCache.TryGetValue(sendInfo.ProjectId, applicationId, out ObjectReference? value) - ) - { - converted = value; - cacheHitCount++; - } - else - { - if (mapMember is ILayerContainer group) - { - // group layer will always come before it's contained layers - // keep active group last in the list - converted = new Collection(); - nestedGroups.Insert(0, (group, (Collection)converted)); - } - else - { - converted = await QueuedTask - .Run(() => (Collection)_rootToSpeckleConverter.Convert(mapMember)) - .ConfigureAwait(false); - - // get units & Active CRS (for writing geometry coords) - converted["units"] = _converterSettings.Current.SpeckleUnits; - - var spatialRef = _converterSettings.Current.ActiveCRSoffsetRotation.SpatialReference; - converted["crs"] = new CRS - { - wkt = spatialRef.Wkt, - name = spatialRef.Name, - offset_y = Convert.ToSingle(_converterSettings.Current.ActiveCRSoffsetRotation.LatOffset), - offset_x = Convert.ToSingle(_converterSettings.Current.ActiveCRSoffsetRotation.LonOffset), - rotation = Convert.ToSingle(_converterSettings.Current.ActiveCRSoffsetRotation.TrueNorthRadians), - units_native = _converterSettings.Current.SpeckleUnits - }; - } - - // other common properties for layers and groups - converted["name"] = mapMember.Name; - converted.applicationId = applicationId; - } - - if ( - nestedGroups.Count == 0 - || nestedGroups.Count == 1 && nestedGroups[0].Item2.applicationId == applicationId - ) - { - // add to host if no groups, or current root group - collectionHost.elements.Add(converted); - } - else - { - // if we are adding a layer inside the group - var parentCollection = nestedGroups.FirstOrDefault(x => - x.Item1.Layers.Select(y => y.URI).Contains(applicationId) - ); - parentCollection.Item2.elements.Add(converted); - } - - results.Add(new(Status.SUCCESS, applicationId, sourceType, converted)); - convertingActivity?.SetStatus(SdkActivityStatusCode.Ok); + count += layerFeatureCount; + results.Add(new(status, layerApplicationId, layer.GetType().Name, layerCollection)); + convertingActivity?.SetStatus(sdkStatus); } - catch (Exception ex) when (!ex.IsFatal()) + else { - _logger.LogSendConversionError(ex, sourceType); - results.Add(new(Status.ERROR, applicationId, sourceType, null, ex)); - convertingActivity?.SetStatus(SdkActivityStatusCode.Error); - convertingActivity?.RecordException(ex); + throw new SpeckleException($"No converted Collection found for layer {layerApplicationId}."); } } + catch (Exception ex) when (!ex.IsFatal()) + { + _logger.LogSendConversionError(ex, layer.GetType().Name); + results.Add(new(Status.ERROR, layerApplicationId, layer.GetType().Name, null, ex)); + convertingActivity?.SetStatus(SdkActivityStatusCode.Error); + convertingActivity?.RecordException(ex); + } - onOperationProgressed.Report(new("Converting", (double)++count / objects.Count)); + await Task.Yield(); } } @@ -192,15 +210,153 @@ public class ArcGISRootObjectBuilder : IRootObjectBuilder throw new SpeckleException("Failed to convert all objects."); // fail fast instead creating empty commit! It will appear as model card error with red color. } - // POC: Add Color Proxies - List colorProxies = _colorManager.UnpackColors(layersWithDisplayPriority); - rootObjectCollection[ProxyKeys.COLOR] = colorProxies; + // 3 - Add Color Proxies + rootCollection[ProxyKeys.COLOR] = _colorUnpacker.ColorProxyCache.Values.ToList(); - // POC: Log would be nice, or can be removed. - Debug.WriteLine( - $"Cache hit count {cacheHitCount} out of {objects.Count} ({(double)cacheHitCount / objects.Count})" - ); + return new RootObjectBuilderResult(rootCollection, results); + } - return new RootObjectBuilderResult(rootObjectCollection, results); + private Dictionary CountAllFeaturesInLayers(List unpackedLayers) + { + Dictionary layersFeatureCount = new(); + + foreach (ADM.MapMember layer in unpackedLayers) + { + switch (layer) + { + case ADM.FeatureLayer featureLayer: + layersFeatureCount.Add(featureLayer, featureLayer.GetFeatureClass().GetCount()); + break; + case ADM.RasterLayer rasterLayer: + // count Raster layer as 1 feature: not optimal but this is the approach for now + layersFeatureCount.Add(rasterLayer, 1); + break; + case ADM.LasDatasetLayer lasDatasetLayer: + var dataset = lasDatasetLayer.GetLasDataset(); + // simple dataset.GetPointCount() keeps returning null, so switched to EstimatePointCount + layersFeatureCount.Add( + lasDatasetLayer, + (long)dataset.EstimatePointCount(dataset.GetDefinition().GetExtent()) + ); + break; + } + } + return layersFeatureCount; + } + + private List ConvertFeatureLayerObjects( + ADM.FeatureLayer featureLayer, + long count, + long allFeaturesCount, + IProgress onOperationProgressed, + CancellationToken cancellationToken + ) + { + string layerApplicationId = featureLayer.GetSpeckleApplicationId(); + List convertedObjects = new(); + // store the layer renderer for color unpacking + _colorUnpacker.StoreRendererAndFields(featureLayer); + + // search the rows of the layer, where each row is treated like an object + // RowCursor is IDisposable but is not being correctly picked up by IDE warnings. + // This means we need to be carefully adding using statements based on the API documentation coming from each method/class + using (ACD.RowCursor rowCursor = featureLayer.Search()) + { + while (rowCursor.MoveNext()) + { + // allow cancellation before every feature + cancellationToken.ThrowIfCancellationRequested(); + + // Same IDisposable issue appears to happen on Row class too. Docs say it should always be disposed of manually by the caller. + using (ACD.Row row = rowCursor.Current) + { + // get application id. test for subtypes before defaulting to base type. + Base converted = _rootToSpeckleConverter.Convert(row); + string applicationId = row.GetSpeckleApplicationId(layerApplicationId); + converted.applicationId = applicationId; + + convertedObjects.Add(converted); + + // process the object color + _colorUnpacker.ProcessFeatureLayerColor(row, applicationId); + } + // update report + onOperationProgressed.Report(new("Converting", (double)++count / allFeaturesCount)); + } + } + + return convertedObjects; + } + + // POC: raster colors are stored as mesh vertex colors in RasterToSpeckleConverter. Should probably move to color unpacker. + private List ConvertRasterLayerObjects( + ADM.RasterLayer rasterLayer, + long count, + long allFeaturesCount, + IProgress onOperationProgressed, + CancellationToken cancellationToken + ) + { + string layerApplicationId = rasterLayer.GetSpeckleApplicationId(); + List convertedObjects = new(); + Raster raster = rasterLayer.GetRaster(); + + // check cancellation token before conversion + cancellationToken.ThrowIfCancellationRequested(); + Base converted = _rootToSpeckleConverter.Convert(raster); + string applicationId = raster.GetSpeckleApplicationId(layerApplicationId); + converted.applicationId = applicationId; + convertedObjects.Add(converted); + + // update report + onOperationProgressed.Report(new("Converting", (double)++count / allFeaturesCount)); + + return convertedObjects; + } + + private List ConvertLasDatasetLayerObjects( + ADM.LasDatasetLayer lasDatasetLayer, + long count, + long allFeaturesCount, + IProgress onOperationProgressed, + CancellationToken cancellationToken + ) + { + string layerApplicationId = lasDatasetLayer.GetSpeckleApplicationId(); + List convertedObjects = new(); + + try + { + // store the layer renderer for color unpacking + _colorUnpacker.StoreRenderer(lasDatasetLayer); + + using (ACD.Analyst3D.LasPointCursor ptCursor = lasDatasetLayer.SearchPoints(new ACD.Analyst3D.LasPointFilter())) + { + while (ptCursor.MoveNext()) + { + // allow cancellation before every point + cancellationToken.ThrowIfCancellationRequested(); + + using (ACD.Analyst3D.LasPoint pt = ptCursor.Current) + { + Base converted = _rootToSpeckleConverter.Convert(pt); + string applicationId = pt.GetSpeckleApplicationId(layerApplicationId); + converted.applicationId = applicationId; + convertedObjects.Add(converted); + + // process the object color + _colorUnpacker.ProcessLasLayerColor(pt, applicationId); + } + // update report + onOperationProgressed.Report(new("Converting", (double)++count / allFeaturesCount)); + } + } + } + catch (ACD.Exceptions.TinException ex) + { + throw new SpeckleException("3D analyst extension is not enabled for .las layer operations", ex); + } + + return convertedObjects; } } diff --git a/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/SpeckleDUI3ViewModel.cs b/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/SpeckleDUI3ViewModel.cs index 9d74daac4..2406f8398 100644 --- a/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/SpeckleDUI3ViewModel.cs +++ b/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/SpeckleDUI3ViewModel.cs @@ -18,7 +18,7 @@ internal sealed class SpeckleDUI3ViewModel : DockPane /// protected override async Task InitializeAsync() { - await base.InitializeAsync().ConfigureAwait(false); + await base.InitializeAsync(); } /// @@ -26,7 +26,7 @@ internal sealed class SpeckleDUI3ViewModel : DockPane /// protected override async Task UninitializeAsync() { - await base.UninitializeAsync().ConfigureAwait(false); + await base.UninitializeAsync(); } } diff --git a/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Utils/ArcGisDocumentStore.cs b/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Utils/ArcGisDocumentStore.cs index 7911f3ac4..199bd3d13 100644 --- a/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Utils/ArcGisDocumentStore.cs +++ b/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Utils/ArcGisDocumentStore.cs @@ -3,18 +3,25 @@ using ArcGIS.Desktop.Core.Events; using ArcGIS.Desktop.Framework.Threading.Tasks; using ArcGIS.Desktop.Mapping; using ArcGIS.Desktop.Mapping.Events; +using Speckle.Connectors.Common.Threading; using Speckle.Connectors.DUI.Bridge; using Speckle.Connectors.DUI.Models; using Speckle.Connectors.DUI.Utils; -using Speckle.Sdk.Common; namespace Speckle.Connectors.ArcGIS.Utils; public class ArcGISDocumentStore : DocumentModelStore { - public ArcGISDocumentStore(IJsonSerializer jsonSerializer, ITopLevelExceptionHandler topLevelExceptionHandler) - : base(jsonSerializer, true) + private readonly IThreadContext _threadContext; + + public ArcGISDocumentStore( + IJsonSerializer jsonSerializer, + ITopLevelExceptionHandler topLevelExceptionHandler, + IThreadContext threadContext + ) + : base(jsonSerializer) { + _threadContext = threadContext; ActiveMapViewChangedEvent.Subscribe(a => topLevelExceptionHandler.CatchUnhandled(() => OnMapViewChanged(a)), true); ProjectSavingEvent.Subscribe( _ => @@ -37,7 +44,7 @@ public class ArcGISDocumentStore : DocumentModelStore if (!IsDocumentInit && MapView.Active != null) { IsDocumentInit = true; - ReadFromFile(); + LoadState(); OnDocumentChanged(); } } @@ -49,14 +56,14 @@ public class ArcGISDocumentStore : DocumentModelStore return; } - WriteToFile(); + SaveState(); } private void OnProjectSaving() { if (MapView.Active is not null) { - WriteToFile(); + SaveState(); } } @@ -71,58 +78,56 @@ public class ArcGISDocumentStore : DocumentModelStore } IsDocumentInit = true; - ReadFromFile(); + LoadState(); OnDocumentChanged(); } - public override void WriteToFile() - { - Map map = MapView.Active.Map; - QueuedTask.Run(() => - { - // Read existing metadata - To prevent messing existing metadata. 🤞 Hope other add-in developers will do same :D - var existingMetadata = map.GetMetadata(); - - // Parse existing metadata - XDocument existingXmlDocument = !string.IsNullOrEmpty(existingMetadata) - ? XDocument.Parse(existingMetadata) - : new XDocument(new XElement("metadata")); - - string serializedModels = Serialize(); - - XElement xmlModelCards = new("SpeckleModelCards", serializedModels); - - // Check if SpeckleModelCards element already exists at root and update it - var speckleModelCardsElement = existingXmlDocument.Root?.Element("SpeckleModelCards"); - if (speckleModelCardsElement != null) + protected override void HostAppSaveState(string modelCardState) => + QueuedTask + .Run(() => { - speckleModelCardsElement.ReplaceWith(xmlModelCards); - } - else + Map map = MapView.Active.Map; + // Read existing metadata - To prevent messing existing metadata. 🤞 Hope other add-in developers will do same :D + var existingMetadata = map.GetMetadata(); + + // Parse existing metadata + XDocument existingXmlDocument = !string.IsNullOrEmpty(existingMetadata) + ? XDocument.Parse(existingMetadata) + : new XDocument(new XElement("metadata")); + + XElement xmlModelCards = new("SpeckleModelCards", modelCardState); + + // Check if SpeckleModelCards element already exists at root and update it + var speckleModelCardsElement = existingXmlDocument.Root?.Element("SpeckleModelCards"); + if (speckleModelCardsElement != null) + { + speckleModelCardsElement.ReplaceWith(xmlModelCards); + } + else + { + existingXmlDocument.Root?.Add(xmlModelCards); + } + + map.SetMetadata(existingXmlDocument.ToString()); + }) + .FireAndForget(); + + protected override void LoadState() => + QueuedTask + .Run(() => { - existingXmlDocument.Root?.Add(xmlModelCards); - } + Map map = MapView.Active.Map; + var metadata = map.GetMetadata(); + var root = XDocument.Parse(metadata).Root; + var element = root?.Element("SpeckleModelCards"); + if (element is null) + { + ClearAndSave(); + return; + } - map.SetMetadata(existingXmlDocument.ToString()); - }); - } - - public override void ReadFromFile() - { - Map map = MapView.Active.Map; - QueuedTask.Run(() => - { - var metadata = map.GetMetadata(); - var root = XDocument.Parse(metadata).Root; - var element = root?.Element("SpeckleModelCards"); - if (element is null) - { - Models = new(); - return; - } - - string modelsString = element.Value; - Models = Deserialize(modelsString).NotNull(); - }); - } + string modelsString = element.Value; + LoadFromString(modelsString); + }) + .FireAndForget(); } diff --git a/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Utils/MapMembersUtils.cs b/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Utils/MapMembersUtils.cs index f03092c5d..345660ea6 100644 --- a/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Utils/MapMembersUtils.cs +++ b/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Utils/MapMembersUtils.cs @@ -40,24 +40,22 @@ public class MapMembersUtils return mapMembers; } - // Gets the layer display priority for selected layers - public List<(MapMember, int)> GetLayerDisplayPriority(Map map, IReadOnlyList selectedMapMembers) + /// + /// Sorts the selected mapmembers into the same order as they appear in the Table of Contents (TOC) bar in the file. + /// This is a required step before unpacking layers, because depending on the user selection order, some children layers may appear before their container layer if both the container and children layers are selected. + /// + public IEnumerable GetMapMembersInOrder(Map map, IReadOnlyList selectedMapMembers) { // first get all map layers List allMapMembers = GetAllMapMembers(map); // recalculate selected layer priority from all map layers - List<(MapMember, int)> selectedLayers = new(); - int newCount = 0; foreach (MapMember mapMember in allMapMembers) { if (selectedMapMembers.Contains(mapMember)) { - selectedLayers.Add((mapMember, newCount)); - newCount++; + yield return mapMember; } } - - return selectedLayers; } } diff --git a/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/packages.lock.json b/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/packages.lock.json index 15e916c65..af8acb964 100644 --- a/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/packages.lock.json +++ b/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/packages.lock.json @@ -161,11 +161,6 @@ "resolved": "8.0.0", "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" }, - "Speckle.DoubleNumerics": { - "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w==" - }, "Speckle.Newtonsoft.Json": { "type": "Transitive", "resolved": "13.0.2", @@ -226,9 +221,9 @@ "dependencies": { "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", "Speckle.Connectors.Logging": "[1.0.0, )", - "Speckle.Objects": "[3.1.0-dev.191, )", - "Speckle.Sdk": "[3.1.0-dev.191, )", - "Speckle.Sdk.Dependencies": "[3.1.0-dev.191, )" + "Speckle.Objects": "[3.1.1, )", + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" } }, "speckle.connectors.dui": { @@ -236,9 +231,8 @@ "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", "Speckle.Connectors.Common": "[1.0.0, )", - "Speckle.Sdk": "[3.1.0-dev.191, )", - "Speckle.Sdk.Dependencies": "[3.1.0-dev.191, )", - "System.Threading.Tasks.Dataflow": "[6.0.0, )" + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" } }, "speckle.connectors.dui.webview": { @@ -262,7 +256,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.191, )" + "Speckle.Objects": "[3.1.1, )" } }, "Microsoft.Extensions.DependencyInjection": { @@ -298,20 +292,26 @@ "resolved": "1.0.1938.49", "contentHash": "z8KnFnaTYzhA/ZnyRX0qGfS1NU5ZBJeClAH64F0fVDvdDJTvME7xl6zTJ0Jlfe1BtL3C0NH9xTy64shg2baKdw==" }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "+m7jRFm0ABbkcSz2UphdxAsislY10IpQ1u79c8a3aVvegLjnsVQZ1sXfRIRO1aFdulkhjYKXYpB3N9M8Z+epgQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.191" + "Speckle.Sdk": "3.1.1" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "VVT3LJiYlhqnggxxdeTt1QLrqfxDb044x0yX6kxS9b5MRzeDvK2Vz86pLDfuHs+SXvDimRVfYx1M42IW/aPcTQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.Bcl.AsyncInterfaces": "5.0.0", @@ -319,22 +319,16 @@ "Microsoft.Data.Sqlite": "7.0.5", "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", "Microsoft.Extensions.Logging": "2.2.0", - "Speckle.DoubleNumerics": "4.0.1", + "Speckle.DoubleNumerics": "4.1.0", "Speckle.Newtonsoft.Json": "13.0.2", - "Speckle.Sdk.Dependencies": "3.1.0-dev.191" + "Speckle.Sdk.Dependencies": "3.1.1" } }, "Speckle.Sdk.Dependencies": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "EmEOyjsGsNi56Z/ZoBOn8WirTmIT2yqWvlUeUh0BSPX2TDMZXHTKOM/kHmP6HSd10KVFn2Zo/ItY7/K9iRtL1Q==" - }, - "System.Threading.Tasks.Dataflow": { - "type": "CentralTransitive", - "requested": "[6.0.0, )", - "resolved": "6.0.0", - "contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA==" + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" } }, "net6.0-windows7.0/win-x64": { diff --git a/Connectors/Autocad/Directory.Build.targets b/Connectors/Autocad/Directory.Build.targets new file mode 100644 index 000000000..5e08b67eb --- /dev/null +++ b/Connectors/Autocad/Directory.Build.targets @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Connectors/Autocad/Speckle.Connectors.Autocad2022/packages.lock.json b/Connectors/Autocad/Speckle.Connectors.Autocad2022/packages.lock.json index 89b2990ad..1350a8c92 100644 --- a/Connectors/Autocad/Speckle.Connectors.Autocad2022/packages.lock.json +++ b/Connectors/Autocad/Speckle.Connectors.Autocad2022/packages.lock.json @@ -159,11 +159,6 @@ "resolved": "8.0.0", "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" }, - "Speckle.DoubleNumerics": { - "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w==" - }, "Speckle.Newtonsoft.Json": { "type": "Transitive", "resolved": "13.0.2", @@ -259,9 +254,9 @@ "dependencies": { "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", "Speckle.Connectors.Logging": "[1.0.0, )", - "Speckle.Objects": "[3.1.0-dev.191, )", - "Speckle.Sdk": "[3.1.0-dev.191, )", - "Speckle.Sdk.Dependencies": "[3.1.0-dev.191, )" + "Speckle.Objects": "[3.1.1, )", + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" } }, "speckle.connectors.dui": { @@ -269,9 +264,8 @@ "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", "Speckle.Connectors.Common": "[1.0.0, )", - "Speckle.Sdk": "[3.1.0-dev.191, )", - "Speckle.Sdk.Dependencies": "[3.1.0-dev.191, )", - "System.Threading.Tasks.Dataflow": "[6.0.0, )" + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" } }, "speckle.connectors.dui.webview": { @@ -295,7 +289,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.191, )" + "Speckle.Objects": "[3.1.1, )" } }, "Microsoft.Extensions.DependencyInjection": { @@ -337,20 +331,26 @@ "resolved": "1.0.1938.49", "contentHash": "z8KnFnaTYzhA/ZnyRX0qGfS1NU5ZBJeClAH64F0fVDvdDJTvME7xl6zTJ0Jlfe1BtL3C0NH9xTy64shg2baKdw==" }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "+m7jRFm0ABbkcSz2UphdxAsislY10IpQ1u79c8a3aVvegLjnsVQZ1sXfRIRO1aFdulkhjYKXYpB3N9M8Z+epgQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.191" + "Speckle.Sdk": "3.1.1" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "VVT3LJiYlhqnggxxdeTt1QLrqfxDb044x0yX6kxS9b5MRzeDvK2Vz86pLDfuHs+SXvDimRVfYx1M42IW/aPcTQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.Bcl.AsyncInterfaces": "5.0.0", @@ -358,22 +358,16 @@ "Microsoft.Data.Sqlite": "7.0.5", "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", "Microsoft.Extensions.Logging": "2.2.0", - "Speckle.DoubleNumerics": "4.0.1", + "Speckle.DoubleNumerics": "4.1.0", "Speckle.Newtonsoft.Json": "13.0.2", - "Speckle.Sdk.Dependencies": "3.1.0-dev.191" + "Speckle.Sdk.Dependencies": "3.1.1" } }, "Speckle.Sdk.Dependencies": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "EmEOyjsGsNi56Z/ZoBOn8WirTmIT2yqWvlUeUh0BSPX2TDMZXHTKOM/kHmP6HSd10KVFn2Zo/ItY7/K9iRtL1Q==" - }, - "System.Threading.Tasks.Dataflow": { - "type": "CentralTransitive", - "requested": "[6.0.0, )", - "resolved": "6.0.0", - "contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA==" + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" } } } diff --git a/Connectors/Autocad/Speckle.Connectors.Autocad2023/packages.lock.json b/Connectors/Autocad/Speckle.Connectors.Autocad2023/packages.lock.json index 0a65e71c6..c54f83c68 100644 --- a/Connectors/Autocad/Speckle.Connectors.Autocad2023/packages.lock.json +++ b/Connectors/Autocad/Speckle.Connectors.Autocad2023/packages.lock.json @@ -159,11 +159,6 @@ "resolved": "8.0.0", "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" }, - "Speckle.DoubleNumerics": { - "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w==" - }, "Speckle.Newtonsoft.Json": { "type": "Transitive", "resolved": "13.0.2", @@ -259,9 +254,9 @@ "dependencies": { "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", "Speckle.Connectors.Logging": "[1.0.0, )", - "Speckle.Objects": "[3.1.0-dev.191, )", - "Speckle.Sdk": "[3.1.0-dev.191, )", - "Speckle.Sdk.Dependencies": "[3.1.0-dev.191, )" + "Speckle.Objects": "[3.1.1, )", + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" } }, "speckle.connectors.dui": { @@ -269,9 +264,8 @@ "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", "Speckle.Connectors.Common": "[1.0.0, )", - "Speckle.Sdk": "[3.1.0-dev.191, )", - "Speckle.Sdk.Dependencies": "[3.1.0-dev.191, )", - "System.Threading.Tasks.Dataflow": "[6.0.0, )" + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" } }, "speckle.connectors.dui.webview": { @@ -295,7 +289,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.191, )" + "Speckle.Objects": "[3.1.1, )" } }, "Microsoft.Extensions.DependencyInjection": { @@ -337,20 +331,26 @@ "resolved": "1.0.1938.49", "contentHash": "z8KnFnaTYzhA/ZnyRX0qGfS1NU5ZBJeClAH64F0fVDvdDJTvME7xl6zTJ0Jlfe1BtL3C0NH9xTy64shg2baKdw==" }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "+m7jRFm0ABbkcSz2UphdxAsislY10IpQ1u79c8a3aVvegLjnsVQZ1sXfRIRO1aFdulkhjYKXYpB3N9M8Z+epgQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.191" + "Speckle.Sdk": "3.1.1" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "VVT3LJiYlhqnggxxdeTt1QLrqfxDb044x0yX6kxS9b5MRzeDvK2Vz86pLDfuHs+SXvDimRVfYx1M42IW/aPcTQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.Bcl.AsyncInterfaces": "5.0.0", @@ -358,22 +358,16 @@ "Microsoft.Data.Sqlite": "7.0.5", "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", "Microsoft.Extensions.Logging": "2.2.0", - "Speckle.DoubleNumerics": "4.0.1", + "Speckle.DoubleNumerics": "4.1.0", "Speckle.Newtonsoft.Json": "13.0.2", - "Speckle.Sdk.Dependencies": "3.1.0-dev.191" + "Speckle.Sdk.Dependencies": "3.1.1" } }, "Speckle.Sdk.Dependencies": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "EmEOyjsGsNi56Z/ZoBOn8WirTmIT2yqWvlUeUh0BSPX2TDMZXHTKOM/kHmP6HSd10KVFn2Zo/ItY7/K9iRtL1Q==" - }, - "System.Threading.Tasks.Dataflow": { - "type": "CentralTransitive", - "requested": "[6.0.0, )", - "resolved": "6.0.0", - "contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA==" + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" } } } diff --git a/Connectors/Autocad/Speckle.Connectors.Autocad2024/packages.lock.json b/Connectors/Autocad/Speckle.Connectors.Autocad2024/packages.lock.json index c49ad8c3f..304087c23 100644 --- a/Connectors/Autocad/Speckle.Connectors.Autocad2024/packages.lock.json +++ b/Connectors/Autocad/Speckle.Connectors.Autocad2024/packages.lock.json @@ -159,11 +159,6 @@ "resolved": "8.0.0", "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" }, - "Speckle.DoubleNumerics": { - "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w==" - }, "Speckle.Newtonsoft.Json": { "type": "Transitive", "resolved": "13.0.2", @@ -259,9 +254,9 @@ "dependencies": { "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", "Speckle.Connectors.Logging": "[1.0.0, )", - "Speckle.Objects": "[3.1.0-dev.191, )", - "Speckle.Sdk": "[3.1.0-dev.191, )", - "Speckle.Sdk.Dependencies": "[3.1.0-dev.191, )" + "Speckle.Objects": "[3.1.1, )", + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" } }, "speckle.connectors.dui": { @@ -269,9 +264,8 @@ "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", "Speckle.Connectors.Common": "[1.0.0, )", - "Speckle.Sdk": "[3.1.0-dev.191, )", - "Speckle.Sdk.Dependencies": "[3.1.0-dev.191, )", - "System.Threading.Tasks.Dataflow": "[6.0.0, )" + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" } }, "speckle.connectors.dui.webview": { @@ -296,7 +290,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.191, )" + "Speckle.Objects": "[3.1.1, )" } }, "Microsoft.Extensions.DependencyInjection": { @@ -338,20 +332,26 @@ "resolved": "1.0.1938.49", "contentHash": "z8KnFnaTYzhA/ZnyRX0qGfS1NU5ZBJeClAH64F0fVDvdDJTvME7xl6zTJ0Jlfe1BtL3C0NH9xTy64shg2baKdw==" }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "+m7jRFm0ABbkcSz2UphdxAsislY10IpQ1u79c8a3aVvegLjnsVQZ1sXfRIRO1aFdulkhjYKXYpB3N9M8Z+epgQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.191" + "Speckle.Sdk": "3.1.1" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "VVT3LJiYlhqnggxxdeTt1QLrqfxDb044x0yX6kxS9b5MRzeDvK2Vz86pLDfuHs+SXvDimRVfYx1M42IW/aPcTQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.Bcl.AsyncInterfaces": "5.0.0", @@ -359,22 +359,16 @@ "Microsoft.Data.Sqlite": "7.0.5", "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", "Microsoft.Extensions.Logging": "2.2.0", - "Speckle.DoubleNumerics": "4.0.1", + "Speckle.DoubleNumerics": "4.1.0", "Speckle.Newtonsoft.Json": "13.0.2", - "Speckle.Sdk.Dependencies": "3.1.0-dev.191" + "Speckle.Sdk.Dependencies": "3.1.1" } }, "Speckle.Sdk.Dependencies": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "EmEOyjsGsNi56Z/ZoBOn8WirTmIT2yqWvlUeUh0BSPX2TDMZXHTKOM/kHmP6HSd10KVFn2Zo/ItY7/K9iRtL1Q==" - }, - "System.Threading.Tasks.Dataflow": { - "type": "CentralTransitive", - "requested": "[6.0.0, )", - "resolved": "6.0.0", - "contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA==" + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" } } } diff --git a/Connectors/Autocad/Speckle.Connectors.Autocad2025/packages.lock.json b/Connectors/Autocad/Speckle.Connectors.Autocad2025/packages.lock.json index 0bd34afe8..ac5d24237 100644 --- a/Connectors/Autocad/Speckle.Connectors.Autocad2025/packages.lock.json +++ b/Connectors/Autocad/Speckle.Connectors.Autocad2025/packages.lock.json @@ -150,11 +150,6 @@ "resolved": "8.0.0", "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" }, - "Speckle.DoubleNumerics": { - "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w==" - }, "Speckle.Newtonsoft.Json": { "type": "Transitive", "resolved": "13.0.2", @@ -215,9 +210,9 @@ "dependencies": { "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", "Speckle.Connectors.Logging": "[1.0.0, )", - "Speckle.Objects": "[3.1.0-dev.191, )", - "Speckle.Sdk": "[3.1.0-dev.191, )", - "Speckle.Sdk.Dependencies": "[3.1.0-dev.191, )" + "Speckle.Objects": "[3.1.1, )", + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" } }, "speckle.connectors.dui": { @@ -225,9 +220,8 @@ "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", "Speckle.Connectors.Common": "[1.0.0, )", - "Speckle.Sdk": "[3.1.0-dev.191, )", - "Speckle.Sdk.Dependencies": "[3.1.0-dev.191, )", - "System.Threading.Tasks.Dataflow": "[6.0.0, )" + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" } }, "speckle.connectors.dui.webview": { @@ -252,7 +246,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.191, )" + "Speckle.Objects": "[3.1.1, )" } }, "Microsoft.Extensions.DependencyInjection": { @@ -294,42 +288,42 @@ "resolved": "1.0.1938.49", "contentHash": "z8KnFnaTYzhA/ZnyRX0qGfS1NU5ZBJeClAH64F0fVDvdDJTvME7xl6zTJ0Jlfe1BtL3C0NH9xTy64shg2baKdw==" }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "+m7jRFm0ABbkcSz2UphdxAsislY10IpQ1u79c8a3aVvegLjnsVQZ1sXfRIRO1aFdulkhjYKXYpB3N9M8Z+epgQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.191" + "Speckle.Sdk": "3.1.1" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "VVT3LJiYlhqnggxxdeTt1QLrqfxDb044x0yX6kxS9b5MRzeDvK2Vz86pLDfuHs+SXvDimRVfYx1M42IW/aPcTQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.CSharp": "4.7.0", "Microsoft.Data.Sqlite": "7.0.5", "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", "Microsoft.Extensions.Logging": "2.2.0", - "Speckle.DoubleNumerics": "4.0.1", + "Speckle.DoubleNumerics": "4.1.0", "Speckle.Newtonsoft.Json": "13.0.2", - "Speckle.Sdk.Dependencies": "3.1.0-dev.191" + "Speckle.Sdk.Dependencies": "3.1.1" } }, "Speckle.Sdk.Dependencies": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "EmEOyjsGsNi56Z/ZoBOn8WirTmIT2yqWvlUeUh0BSPX2TDMZXHTKOM/kHmP6HSd10KVFn2Zo/ItY7/K9iRtL1Q==" - }, - "System.Threading.Tasks.Dataflow": { - "type": "CentralTransitive", - "requested": "[6.0.0, )", - "resolved": "6.0.0", - "contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA==" + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" } }, "net8.0-windows7.0/win-x64": { diff --git a/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadBasicConnectorBinding.cs b/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadBasicConnectorBinding.cs index dbc34f1b6..25f583332 100644 --- a/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadBasicConnectorBinding.cs +++ b/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadBasicConnectorBinding.cs @@ -1,6 +1,7 @@ using Autodesk.AutoCAD.DatabaseServices; using Microsoft.Extensions.Logging; using Speckle.Connectors.Autocad.HostApp.Extensions; +using Speckle.Connectors.Common.Threading; using Speckle.Connectors.DUI.Bindings; using Speckle.Connectors.DUI.Bridge; using Speckle.Connectors.DUI.Models; @@ -19,6 +20,8 @@ public class AutocadBasicConnectorBinding : IBasicConnectorBinding private readonly DocumentModelStore _store; private readonly ISpeckleApplication _speckleApplication; + private readonly IThreadContext _threadContext; + private readonly ITopLevelExceptionHandler _topLevelExceptionHandler; private readonly ILogger _logger; public BasicConnectorBindingCommands Commands { get; } @@ -28,7 +31,9 @@ public class AutocadBasicConnectorBinding : IBasicConnectorBinding IBrowserBridge parent, IAccountManager accountManager, ISpeckleApplication speckleApplication, - ILogger logger + ILogger logger, + IThreadContext threadContext, + ITopLevelExceptionHandler topLevelExceptionHandler ) { _store = store; @@ -36,12 +41,14 @@ public class AutocadBasicConnectorBinding : IBasicConnectorBinding _accountManager = accountManager; _speckleApplication = speckleApplication; Commands = new BasicConnectorBindingCommands(parent); - _store.DocumentChanged += (_, _) => - parent.TopLevelExceptionHandler.FireAndForget(async () => - { - await Commands.NotifyDocumentChanged().ConfigureAwait(false); - }); _logger = logger; + _threadContext = threadContext; + _topLevelExceptionHandler = topLevelExceptionHandler; + _store.DocumentChanged += (_, _) => + _topLevelExceptionHandler.FireAndForget(async () => + { + await Commands.NotifyDocumentChanged(); + }); } public string GetConnectorVersion() => _speckleApplication.SpeckleVersion; @@ -66,7 +73,7 @@ public class AutocadBasicConnectorBinding : IBasicConnectorBinding public DocumentModelStore GetDocumentState() => _store; - public void AddModel(ModelCard model) => _store.Models.Add(model); + public void AddModel(ModelCard model) => _store.AddModel(model); public void UpdateModel(ModelCard model) => _store.UpdateModel(model); @@ -79,7 +86,7 @@ public class AutocadBasicConnectorBinding : IBasicConnectorBinding var dbObjects = doc.GetObjects(objectIds); var acadObjectIds = dbObjects.Select(tuple => tuple.Root.Id).ToArray(); - await HighlightObjectsOnView(acadObjectIds).ConfigureAwait(false); + await HighlightObjectsOnView(acadObjectIds); } public async Task HighlightModel(string modelCardId) @@ -116,78 +123,73 @@ public class AutocadBasicConnectorBinding : IBasicConnectorBinding if (objectIds.Length == 0) { - await Commands - .SetModelError(modelCardId, new OperationCanceledException("No objects found to highlight.")) - .ConfigureAwait(false); + await Commands.SetModelError(modelCardId, new OperationCanceledException("No objects found to highlight.")); return; } - await HighlightObjectsOnView(objectIds, modelCardId).ConfigureAwait(false); + await HighlightObjectsOnView(objectIds, modelCardId); } private async Task HighlightObjectsOnView(ObjectId[] objectIds, string? modelCardId = null) { var doc = Application.DocumentManager.MdiActiveDocument; - await Parent - .RunOnMainThreadAsync(async () => + await _threadContext.RunOnMainAsync(async () => + { + try { + doc.Editor.SetImpliedSelection([]); // Deselects try { - doc.Editor.SetImpliedSelection(Array.Empty()); // Deselects + doc.Editor.SetImpliedSelection(objectIds); + } + catch (Exception e) when (!e.IsFatal()) + { + // SWALLOW REASON: + // If the objects under the blocks, it won't be able to select them. + // If we try, API will throw the invalid input error, because we request something from API that Autocad doesn't + // handle it on its current canvas. Block elements only selectable when in its scope. + } + doc.Editor.UpdateScreen(); + + Extents3d selectedExtents = new(); + + var tr = doc.TransactionManager.StartTransaction(); + foreach (ObjectId objectId in objectIds) + { try { - doc.Editor.SetImpliedSelection(objectIds); + var entity = (Entity?)tr.GetObject(objectId, OpenMode.ForRead); + if (entity?.GeometricExtents != null) + { + selectedExtents.AddExtents(entity.GeometricExtents); + } } catch (Exception e) when (!e.IsFatal()) { - // SWALLOW REASON: - // If the objects under the blocks, it won't be able to select them. - // If we try, API will throw the invalid input error, because we request something from API that Autocad doesn't - // handle it on its current canvas. Block elements only selectable when in its scope. + // Note: we're swallowing exeptions here because of a weird case when receiving blocks, we would have + // acad api throw an error on accessing entity.GeometricExtents. + // may also throw Autodesk.AutoCAD.Runtime.Exception for invalid extents on objects like rays and xlines } - doc.Editor.UpdateScreen(); - - Extents3d selectedExtents = new(); - - var tr = doc.TransactionManager.StartTransaction(); - foreach (ObjectId objectId in objectIds) - { - try - { - var entity = (Entity?)tr.GetObject(objectId, OpenMode.ForRead); - if (entity?.GeometricExtents != null) - { - selectedExtents.AddExtents(entity.GeometricExtents); - } - } - catch (Exception e) when (!e.IsFatal()) - { - // Note: we're swallowing exeptions here because of a weird case when receiving blocks, we would have - // acad api throw an error on accessing entity.GeometricExtents. - } - } - - doc.Editor.Zoom(selectedExtents); - tr.Commit(); - Autodesk.AutoCAD.Internal.Utils.FlushGraphics(); } - catch (Exception ex) when (!ex.IsFatal()) + + doc.Editor.Zoom(selectedExtents); + tr.Commit(); + Autodesk.AutoCAD.Internal.Utils.FlushGraphics(); + } + catch (Exception ex) when (!ex.IsFatal()) + { + if (modelCardId != null) { - if (modelCardId != null) - { - await Commands - .SetModelError(modelCardId, new OperationCanceledException("Failed to highlight objects.")) - .ConfigureAwait(false); - } - else - { - // This will happen, in some cases, where we highlight individual objects. Should be caught by the top level handler and not - // crash the host app. - throw; - } + await Commands.SetModelError(modelCardId, new OperationCanceledException("Failed to highlight objects.")); } - }) - .ConfigureAwait(false); + else + { + // This will happen, in some cases, where we highlight individual objects. Should be caught by the top level handler and not + // crash the host app. + throw; + } + } + }); } } diff --git a/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadReceiveBaseBinding.cs b/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadReceiveBaseBinding.cs new file mode 100644 index 000000000..eb58a6783 --- /dev/null +++ b/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadReceiveBaseBinding.cs @@ -0,0 +1,119 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Speckle.Connectors.Common.Cancellation; +using Speckle.Connectors.Common.Operations; +using Speckle.Connectors.Common.Threading; +using Speckle.Connectors.DUI.Bindings; +using Speckle.Connectors.DUI.Bridge; +using Speckle.Connectors.DUI.Logging; +using Speckle.Connectors.DUI.Models; +using Speckle.Connectors.DUI.Models.Card; +using Speckle.Sdk; + +namespace Speckle.Connectors.Autocad.Bindings; + +public abstract class AutocadReceiveBaseBinding : IReceiveBinding +{ + public string Name => "receiveBinding"; + public IBrowserBridge Parent { get; } + + private readonly DocumentModelStore _store; + private readonly ICancellationManager _cancellationManager; + private readonly IServiceProvider _serviceProvider; + private readonly IOperationProgressManager _operationProgressManager; + private readonly ILogger _logger; + private readonly ISpeckleApplication _speckleApplication; + private readonly IThreadContext _threadContext; + + private ReceiveBindingUICommands Commands { get; } + + protected AutocadReceiveBaseBinding( + DocumentModelStore store, + IBrowserBridge parent, + ICancellationManager cancellationManager, + IServiceProvider serviceProvider, + IOperationProgressManager operationProgressManager, + ILogger logger, + ISpeckleApplication speckleApplication, + IThreadContext threadContext + ) + { + _store = store; + _cancellationManager = cancellationManager; + _serviceProvider = serviceProvider; + _operationProgressManager = operationProgressManager; + _logger = logger; + _speckleApplication = speckleApplication; + _threadContext = threadContext; + Parent = parent; + Commands = new ReceiveBindingUICommands(parent); + } + + protected abstract void InitializeSettings(IServiceProvider serviceProvider); + + public void CancelReceive(string modelCardId) => _cancellationManager.CancelOperation(modelCardId); + + public async Task Receive(string modelCardId) => + await _threadContext.RunOnMainAsync(async () => await ReceiveInternal(modelCardId)); + + private async Task ReceiveInternal(string modelCardId) + { + using var scope = _serviceProvider.CreateScope(); + InitializeSettings(scope.ServiceProvider); + + try + { + // Get receiver card + if (_store.GetModelById(modelCardId) is not ReceiverModelCard modelCard) + { + // Handle as GLOBAL ERROR at BrowserBridge + throw new InvalidOperationException("No download model card was found."); + } + + using var cancellationItem = _cancellationManager.GetCancellationItem(modelCardId); + + // Disable document activation (document creation and document switch) + // Not disabling results in DUI model card being out of sync with the active document + // The DocumentActivated event isn't usable probably because it is pushed to back of main thread queue + Application.DocumentManager.DocumentActivationEnabled = false; + + // Receive host objects + var operationResults = await scope + .ServiceProvider.GetRequiredService() + .Execute( + modelCard.GetReceiveInfo(_speckleApplication.Slug), + _operationProgressManager.CreateOperationProgressEventHandler(Parent, modelCardId, cancellationItem.Token), + cancellationItem.Token + ); + + await Commands.SetModelReceiveResult( + modelCardId, + operationResults.BakedObjectIds, + operationResults.ConversionResults + ); + } + catch (OperationCanceledException) + { + // SWALLOW -> UI handles it immediately, so we do not need to handle anything for now! + // Idea for later -> when cancel called, create promise from UI to solve it later with this catch block. + // So have 3 state on UI -> Cancellation clicked -> Cancelling -> Cancelled + return; + } + catch (Exception ex) when (!ex.IsFatal()) // UX reasons - we will report operation exceptions as model card error. We may change this later when we have more exception documentation + { + _logger.LogModelCardHandledError(ex); + await Commands.SetModelError(modelCardId, ex); + } + finally + { + // reenable document activation + Application.DocumentManager.DocumentActivationEnabled = true; + + // regenerate doc to flush graphics, sometimes some objects (ellipses, nurbs curves) do not appear fully visible after receive. + // Adding a regen (must be run on main thread) here, but it doesn't seem to work: + // it's run on main thread, tried sending the "regen" string to execute, also tried regen after every object bake, but still can't fix. + // the objects should appear visible if you manually call the "regen" command after the operation finishes, or click on a view on the view cube which also calls regen. + Application.DocumentManager.CurrentDocument.Editor.Regen(); + } + } +} diff --git a/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadReceiveBinding.cs b/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadReceiveBinding.cs index d2d68a7fd..fb2295040 100644 --- a/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadReceiveBinding.cs +++ b/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadReceiveBinding.cs @@ -1,109 +1,49 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Speckle.Connectors.Common.Cancellation; -using Speckle.Connectors.Common.Operations; +using Speckle.Connectors.Common.Threading; using Speckle.Connectors.DUI.Bindings; using Speckle.Connectors.DUI.Bridge; -using Speckle.Connectors.DUI.Logging; using Speckle.Connectors.DUI.Models; -using Speckle.Connectors.DUI.Models.Card; using Speckle.Converters.Autocad; using Speckle.Converters.Common; using Speckle.Sdk; namespace Speckle.Connectors.Autocad.Bindings; -public sealed class AutocadReceiveBinding : IReceiveBinding +public sealed class AutocadReceiveBinding : AutocadReceiveBaseBinding { - public string Name => "receiveBinding"; - public IBrowserBridge Parent { get; } - - private readonly DocumentModelStore _store; - private readonly CancellationManager _cancellationManager; - private readonly IServiceProvider _serviceProvider; - private readonly IOperationProgressManager _operationProgressManager; - private readonly ILogger _logger; private readonly IAutocadConversionSettingsFactory _autocadConversionSettingsFactory; - private readonly ISpeckleApplication _speckleApplication; - - private ReceiveBindingUICommands Commands { get; } public AutocadReceiveBinding( DocumentModelStore store, IBrowserBridge parent, - CancellationManager cancellationManager, + ICancellationManager cancellationManager, IServiceProvider serviceProvider, IOperationProgressManager operationProgressManager, ILogger logger, IAutocadConversionSettingsFactory autocadConversionSettingsFactory, - ISpeckleApplication speckleApplication + ISpeckleApplication speckleApplication, + IThreadContext threadContext ) + : base( + store, + parent, + cancellationManager, + serviceProvider, + operationProgressManager, + logger, + speckleApplication, + threadContext + ) { - _store = store; - _cancellationManager = cancellationManager; - _serviceProvider = serviceProvider; - _operationProgressManager = operationProgressManager; - _logger = logger; _autocadConversionSettingsFactory = autocadConversionSettingsFactory; - _speckleApplication = speckleApplication; - Parent = parent; - Commands = new ReceiveBindingUICommands(parent); } - public void CancelReceive(string modelCardId) => _cancellationManager.CancelOperation(modelCardId); - - public async Task Receive(string modelCardId) + protected override void InitializeSettings(IServiceProvider serviceProvider) { - using var scope = _serviceProvider.CreateScope(); - scope - .ServiceProvider.GetRequiredService>() + serviceProvider + .GetRequiredService>() .Initialize(_autocadConversionSettingsFactory.Create(Application.DocumentManager.CurrentDocument)); - try - { - // Get receiver card - if (_store.GetModelById(modelCardId) is not ReceiverModelCard modelCard) - { - // Handle as GLOBAL ERROR at BrowserBridge - throw new InvalidOperationException("No download model card was found."); - } - - CancellationToken cancellationToken = _cancellationManager.InitCancellationTokenSource(modelCardId); - - // Disable document activation (document creation and document switch) - // Not disabling results in DUI model card being out of sync with the active document - // The DocumentActivated event isn't usable probably because it is pushed to back of main thread queue - Application.DocumentManager.DocumentActivationEnabled = false; - - // Receive host objects - var operationResults = await scope - .ServiceProvider.GetRequiredService() - .Execute( - modelCard.GetReceiveInfo(_speckleApplication.Slug), - _operationProgressManager.CreateOperationProgressEventHandler(Parent, modelCardId, cancellationToken), - cancellationToken - ) - .ConfigureAwait(false); - - await Commands - .SetModelReceiveResult(modelCardId, operationResults.BakedObjectIds, operationResults.ConversionResults) - .ConfigureAwait(false); - } - catch (OperationCanceledException) - { - // SWALLOW -> UI handles it immediately, so we do not need to handle anything for now! - // Idea for later -> when cancel called, create promise from UI to solve it later with this catch block. - // So have 3 state on UI -> Cancellation clicked -> Cancelling -> Cancelled - return; - } - catch (Exception ex) when (!ex.IsFatal()) // UX reasons - we will report operation exceptions as model card error. We may change this later when we have more exception documentation - { - _logger.LogModelCardHandledError(ex); - await Commands.SetModelError(modelCardId, ex).ConfigureAwait(false); - } - finally - { - // reenable document activation - Application.DocumentManager.DocumentActivationEnabled = true; - } } } diff --git a/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadSelectionBinding.cs b/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadSelectionBinding.cs index 0be4d5611..e4d60e2b4 100644 --- a/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadSelectionBinding.cs +++ b/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadSelectionBinding.cs @@ -1,6 +1,7 @@ using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.EditorInput; using Speckle.Connectors.Autocad.HostApp.Extensions; +using Speckle.Connectors.Common.Threading; using Speckle.Connectors.DUI.Bindings; using Speckle.Connectors.DUI.Bridge; @@ -8,17 +9,23 @@ namespace Speckle.Connectors.Autocad.Bindings; public class AutocadSelectionBinding : ISelectionBinding { - private const string SELECTION_EVENT = "setSelection"; private readonly ITopLevelExceptionHandler _topLevelExceptionHandler; + private readonly IThreadContext _threadContext; + private const string SELECTION_EVENT = "setSelection"; private readonly HashSet _visitedDocuments = new(); public string Name => "selectionBinding"; public IBrowserBridge Parent { get; } - public AutocadSelectionBinding(IBrowserBridge parent) + public AutocadSelectionBinding( + IBrowserBridge parent, + ITopLevelExceptionHandler topLevelExceptionHandler, + IThreadContext threadContext + ) { - _topLevelExceptionHandler = parent.TopLevelExceptionHandler; + _topLevelExceptionHandler = topLevelExceptionHandler; + _threadContext = threadContext; Parent = parent; // POC: Use here Context for doc. In converters it's OK but we are still lacking to use context into bindings. @@ -41,9 +48,7 @@ public class AutocadSelectionBinding : ISelectionBinding if (!_visitedDocuments.Contains(document)) { document.ImpliedSelectionChanged += (_, _) => - _topLevelExceptionHandler.FireAndForget( - async () => await Parent.RunOnMainThreadAsync(OnSelectionChanged).ConfigureAwait(false) - ); + _topLevelExceptionHandler.FireAndForget(async () => await _threadContext.RunOnMainAsync(OnSelectionChanged)); _visitedDocuments.Add(document); } @@ -57,7 +62,7 @@ public class AutocadSelectionBinding : ISelectionBinding private async Task OnSelectionChanged() { _selectionInfo = GetSelectionInternal(); - await Parent.Send(SELECTION_EVENT, _selectionInfo).ConfigureAwait(false); + await Parent.Send(SELECTION_EVENT, _selectionInfo); } public SelectionInfo GetSelection() => _selectionInfo; diff --git a/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadSendBaseBinding.cs b/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadSendBaseBinding.cs index 11f840f92..7cc3b7043 100644 --- a/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadSendBaseBinding.cs +++ b/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadSendBaseBinding.cs @@ -1,13 +1,14 @@ -using System.Collections.Concurrent; +using System.Collections.Concurrent; +using System.Diagnostics.CodeAnalysis; using Autodesk.AutoCAD.DatabaseServices; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -using Speckle.Connectors.Autocad.HostApp; using Speckle.Connectors.Autocad.HostApp.Extensions; using Speckle.Connectors.Autocad.Operations.Send; using Speckle.Connectors.Common.Caching; using Speckle.Connectors.Common.Cancellation; using Speckle.Connectors.Common.Operations; +using Speckle.Connectors.Common.Threading; using Speckle.Connectors.DUI.Bindings; using Speckle.Connectors.DUI.Bridge; using Speckle.Connectors.DUI.Exceptions; @@ -21,6 +22,7 @@ using Speckle.Sdk.Common; namespace Speckle.Connectors.Autocad.Bindings; +[SuppressMessage("ReSharper", "AsyncVoidMethod")] public abstract class AutocadSendBaseBinding : ISendBinding { public string Name => "sendBinding"; @@ -29,15 +31,16 @@ public abstract class AutocadSendBaseBinding : ISendBinding public IBrowserBridge Parent { get; } private readonly DocumentModelStore _store; - private readonly IAutocadIdleManager _idleManager; private readonly List _sendFilters; - private readonly CancellationManager _cancellationManager; + private readonly ICancellationManager _cancellationManager; private readonly IServiceProvider _serviceProvider; private readonly ISendConversionCache _sendConversionCache; private readonly IOperationProgressManager _operationProgressManager; private readonly ILogger _logger; - private readonly ITopLevelExceptionHandler _topLevelExceptionHandler; private readonly ISpeckleApplication _speckleApplication; + private readonly IThreadContext _threadContext; + private readonly ITopLevelExceptionHandler _topLevelExceptionHandler; + private readonly IAppIdleManager _idleManager; /// /// Used internally to aggregate the changed objects' id. Note we're using a concurrent dictionary here as the expiry check method is not thread safe, and this was causing problems. See: @@ -45,23 +48,24 @@ public abstract class AutocadSendBaseBinding : ISendBinding /// As to why a concurrent dictionary, it's because it's the cheapest/easiest way to do so. /// https://stackoverflow.com/questions/18922985/concurrent-hashsett-in-net-framework /// - private ConcurrentDictionary ChangedObjectIds { get; set; } = new(); + private ConcurrentBag ChangedObjectIds { get; set; } = new(); protected AutocadSendBaseBinding( DocumentModelStore store, - IAutocadIdleManager idleManager, IBrowserBridge parent, IEnumerable sendFilters, - CancellationManager cancellationManager, + ICancellationManager cancellationManager, IServiceProvider serviceProvider, ISendConversionCache sendConversionCache, IOperationProgressManager operationProgressManager, ILogger logger, - ISpeckleApplication speckleApplication + ISpeckleApplication speckleApplication, + IThreadContext threadContext, + ITopLevelExceptionHandler topLevelExceptionHandler, + IAppIdleManager idleManager ) { _store = store; - _idleManager = idleManager; _serviceProvider = serviceProvider; _cancellationManager = cancellationManager; _sendFilters = sendFilters.ToList(); @@ -69,7 +73,9 @@ public abstract class AutocadSendBaseBinding : ISendBinding _operationProgressManager = operationProgressManager; _logger = logger; _speckleApplication = speckleApplication; - _topLevelExceptionHandler = parent.TopLevelExceptionHandler; + _threadContext = threadContext; + _topLevelExceptionHandler = topLevelExceptionHandler; + _idleManager = idleManager; Parent = parent; Commands = new SendBindingUICommands(parent); @@ -103,31 +109,25 @@ public abstract class AutocadSendBaseBinding : ISendBinding doc.Database.ObjectModified += (_, e) => OnObjectChanged(e.DBObject); } - private void OnObjectChanged(DBObject dbObject) - { + private void OnObjectChanged(DBObject dbObject) => _topLevelExceptionHandler.CatchUnhandled(() => OnChangeChangedObjectIds(dbObject)); - } private void OnChangeChangedObjectIds(DBObject dBObject) { - ChangedObjectIds[dBObject.GetSpeckleApplicationId()] = 1; - _idleManager.SubscribeToIdle( - nameof(AutocadSendBinding), - async () => await RunExpirationChecks().ConfigureAwait(false) - ); + ChangedObjectIds.Add(dBObject.GetSpeckleApplicationId()); + _idleManager.SubscribeToIdle(nameof(RunExpirationChecks), async () => await RunExpirationChecks()); } private async Task RunExpirationChecks() { var senders = _store.GetSenders(); - string[] objectIdsList = ChangedObjectIds.Keys.ToArray(); List expiredSenderIds = new(); - _sendConversionCache.EvictObjects(objectIdsList); + _sendConversionCache.EvictObjects(ChangedObjectIds); foreach (SenderModelCard modelCard in senders) { - var intersection = modelCard.SendFilter.NotNull().RefreshObjectIds().Intersect(objectIdsList).ToList(); + var intersection = modelCard.SendFilter.NotNull().RefreshObjectIds().Intersect(ChangedObjectIds).ToList(); bool isExpired = intersection.Count != 0; if (isExpired) { @@ -135,7 +135,7 @@ public abstract class AutocadSendBaseBinding : ISendBinding } } - await Commands.SetModelsExpired(expiredSenderIds).ConfigureAwait(false); + await Commands.SetModelsExpired(expiredSenderIds); ChangedObjectIds = new(); } @@ -144,9 +144,7 @@ public abstract class AutocadSendBaseBinding : ISendBinding public List GetSendSettings() => []; public async Task Send(string modelCardId) => - await Parent - .RunOnMainThreadAsync(async () => await SendInternal(modelCardId).ConfigureAwait(false)) - .ConfigureAwait(false); + await _threadContext.RunOnMainAsync(async () => await SendInternal(modelCardId)); protected abstract void InitializeSettings(IServiceProvider serviceProvider); @@ -163,7 +161,7 @@ public abstract class AutocadSendBaseBinding : ISendBinding using var scope = _serviceProvider.CreateScope(); InitializeSettings(scope.ServiceProvider); - CancellationToken cancellationToken = _cancellationManager.InitCancellationTokenSource(modelCardId); + using var cancellationItem = _cancellationManager.GetCancellationItem(modelCardId); // Disable document activation (document creation and document switch) // Not disabling results in DUI model card being out of sync with the active document @@ -185,15 +183,12 @@ public abstract class AutocadSendBaseBinding : ISendBinding .ServiceProvider.GetRequiredService>() .Execute( autocadObjects, - modelCard.GetSendInfo(_speckleApplication.Slug), - _operationProgressManager.CreateOperationProgressEventHandler(Parent, modelCardId, cancellationToken), - cancellationToken - ) - .ConfigureAwait(false); + modelCard.GetSendInfo(_speckleApplication.ApplicationAndVersion), + _operationProgressManager.CreateOperationProgressEventHandler(Parent, modelCardId, cancellationItem.Token), + cancellationItem.Token + ); - await Commands - .SetModelSendResult(modelCardId, sendResult.RootObjId, sendResult.ConversionResults) - .ConfigureAwait(false); + await Commands.SetModelSendResult(modelCardId, sendResult.RootObjId, sendResult.ConversionResults); } catch (OperationCanceledException) { @@ -205,7 +200,7 @@ public abstract class AutocadSendBaseBinding : ISendBinding catch (Exception ex) when (!ex.IsFatal()) // UX reasons - we will report operation exceptions as model card error. We may change this later when we have more exception documentation { _logger.LogModelCardHandledError(ex); - await Commands.SetModelError(modelCardId, ex).ConfigureAwait(false); + await Commands.SetModelError(modelCardId, ex); } finally { diff --git a/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadSendBinding.cs b/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadSendBinding.cs index bc2a28a96..5968bcdca 100644 --- a/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadSendBinding.cs +++ b/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadSendBinding.cs @@ -1,8 +1,8 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -using Speckle.Connectors.Autocad.HostApp; using Speckle.Connectors.Common.Caching; using Speckle.Connectors.Common.Cancellation; +using Speckle.Connectors.Common.Threading; using Speckle.Connectors.DUI.Bindings; using Speckle.Connectors.DUI.Bridge; using Speckle.Connectors.DUI.Models; @@ -19,20 +19,21 @@ public sealed class AutocadSendBinding : AutocadSendBaseBinding public AutocadSendBinding( DocumentModelStore store, - IAutocadIdleManager idleManager, IBrowserBridge parent, IEnumerable sendFilters, - CancellationManager cancellationManager, + ICancellationManager cancellationManager, IServiceProvider serviceProvider, ISendConversionCache sendConversionCache, IOperationProgressManager operationProgressManager, ILogger logger, IAutocadConversionSettingsFactory autocadConversionSettingsFactory, - ISpeckleApplication speckleApplication + ISpeckleApplication speckleApplication, + IThreadContext threadContext, + ITopLevelExceptionHandler topLevelExceptionHandler, + IAppIdleManager appIdleManager ) : base( store, - idleManager, parent, sendFilters, cancellationManager, @@ -40,7 +41,10 @@ public sealed class AutocadSendBinding : AutocadSendBaseBinding sendConversionCache, operationProgressManager, logger, - speckleApplication + speckleApplication, + threadContext, + topLevelExceptionHandler, + appIdleManager ) { _autocadConversionSettingsFactory = autocadConversionSettingsFactory; diff --git a/Connectors/Autocad/Speckle.Connectors.AutocadShared/DependencyInjection/SharedRegistration.cs b/Connectors/Autocad/Speckle.Connectors.AutocadShared/DependencyInjection/SharedRegistration.cs index 865963b20..6e2db8da2 100644 --- a/Connectors/Autocad/Speckle.Connectors.AutocadShared/DependencyInjection/SharedRegistration.cs +++ b/Connectors/Autocad/Speckle.Connectors.AutocadShared/DependencyInjection/SharedRegistration.cs @@ -10,10 +10,10 @@ using Speckle.Connectors.Common.Builders; using Speckle.Connectors.Common.Caching; using Speckle.Connectors.Common.Instances; using Speckle.Connectors.Common.Operations; +using Speckle.Connectors.Common.Threading; using Speckle.Connectors.DUI; using Speckle.Connectors.DUI.Bindings; using Speckle.Connectors.DUI.Bridge; -using Speckle.Connectors.DUI.Models; using Speckle.Connectors.DUI.Models.Card.SendFilter; using Speckle.Connectors.DUI.WebView; using Speckle.Sdk.Models.GraphTraversal; @@ -25,13 +25,12 @@ public static class SharedRegistration public static void AddAutocadBase(this IServiceCollection serviceCollection) { serviceCollection.AddConnectorUtils(); - serviceCollection.AddDUI(); + serviceCollection.AddDUI(); serviceCollection.AddDUIView(); // Register other connector specific types serviceCollection.AddTransient(); serviceCollection.AddSingleton(new AutocadDocumentManager()); // TODO: Dependent to TransactionContext, can be moved to AutocadContext - serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); // Unpackers and builders @@ -45,10 +44,10 @@ public static class SharedRegistration serviceCollection.AddScoped(); serviceCollection.AddScoped(); - serviceCollection.AddScoped(); + serviceCollection.AddScoped(); serviceCollection.AddScoped(); - serviceCollection.AddScoped(); + serviceCollection.AddScoped(); serviceCollection.AddSingleton(); @@ -62,8 +61,6 @@ public static class SharedRegistration serviceCollection.AddSingleton(sp => sp.GetRequiredService()); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); - - serviceCollection.RegisterTopLevelExceptionHandler(); } public static void LoadSend(this IServiceCollection serviceCollection) diff --git a/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadColorBaker.cs b/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadColorBaker.cs index 531ded14e..c02e1d839 100644 --- a/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadColorBaker.cs +++ b/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadColorBaker.cs @@ -1,6 +1,7 @@ using Autodesk.AutoCAD.Colors; using Microsoft.Extensions.Logging; using Speckle.Connectors.Common.Operations; +using Speckle.InterfaceGenerator; using Speckle.Sdk; using Speckle.Sdk.Models.Proxies; using AutocadColor = Autodesk.AutoCAD.Colors.Color; @@ -10,15 +11,9 @@ namespace Speckle.Connectors.Autocad.HostApp; /// /// Expects to be a scoped dependency for a given operation and helps with layer creation and cleanup. /// -public class AutocadColorBaker +[GenerateAutoInterface] +public class AutocadColorBaker(ILogger logger) : IAutocadColorBaker { - private readonly ILogger _logger; - - public AutocadColorBaker(ILogger logger) - { - _logger = logger; - } - /// /// For receive operations /// @@ -29,10 +24,7 @@ public class AutocadColorBaker /// /// /// - public async Task ParseColors( - IReadOnlyCollection colorProxies, - IProgress onOperationProgressed - ) + public void ParseColors(IReadOnlyCollection colorProxies, IProgress onOperationProgressed) { var count = 0; foreach (ColorProxy colorProxy in colorProxies) @@ -62,10 +54,8 @@ public class AutocadColorBaker } catch (Exception ex) when (!ex.IsFatal()) { - _logger.LogError(ex, "Failed parsing color proxy"); + logger.LogError(ex, "Failed parsing color proxy"); } - - await Task.Yield(); } } diff --git a/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadDocumentModelStore.cs b/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadDocumentModelStore.cs index ae8061b88..895aacd7b 100644 --- a/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadDocumentModelStore.cs +++ b/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadDocumentModelStore.cs @@ -1,25 +1,26 @@ using Speckle.Connectors.DUI.Bridge; using Speckle.Connectors.DUI.Models; using Speckle.Connectors.DUI.Utils; -using Speckle.Sdk.Common; namespace Speckle.Connectors.Autocad.HostApp; public class AutocadDocumentStore : DocumentModelStore { - private readonly string _nullDocumentName = "Null Doc"; + private const string NULL_DOCUMENT_NAME = "Null Doc"; private string _previousDocName; private readonly AutocadDocumentManager _autocadDocumentManager; + private readonly ITopLevelExceptionHandler _topLevelExceptionHandler; public AutocadDocumentStore( IJsonSerializer jsonSerializer, AutocadDocumentManager autocadDocumentManager, ITopLevelExceptionHandler topLevelExceptionHandler ) - : base(jsonSerializer, true) + : base(jsonSerializer) { _autocadDocumentManager = autocadDocumentManager; - _previousDocName = _nullDocumentName; + _topLevelExceptionHandler = topLevelExceptionHandler; + _previousDocName = NULL_DOCUMENT_NAME; // POC: Will be addressed to move it into AutocadContext! if (Application.DocumentManager.MdiActiveDocument != null) @@ -41,39 +42,38 @@ public class AutocadDocumentStore : DocumentModelStore private void OnDocChangeInternal(Document? doc) { - var currentDocName = doc != null ? doc.Name : _nullDocumentName; + var currentDocName = doc != null ? doc.Name : NULL_DOCUMENT_NAME; if (_previousDocName == currentDocName) { return; } _previousDocName = currentDocName; - ReadFromFile(); + LoadState(); OnDocumentChanged(); } - public override void ReadFromFile() + protected override void LoadState() { - Models = new(); - // POC: Will be addressed to move it into AutocadContext! Document? doc = Application.DocumentManager.MdiActiveDocument; if (doc == null) { + ClearAndSave(); return; } string? serializedModelCards = _autocadDocumentManager.ReadModelCards(doc); if (serializedModelCards == null) { + ClearAndSave(); return; } - - Models = Deserialize(serializedModelCards).NotNull(); + LoadFromString(serializedModelCards); } - public override void WriteToFile() + protected override void HostAppSaveState(string modelCardState) { // POC: Will be addressed to move it into AutocadContext! Document doc = Application.DocumentManager.MdiActiveDocument; @@ -83,7 +83,6 @@ public class AutocadDocumentStore : DocumentModelStore return; } - string modelCardsString = Serialize(); - _autocadDocumentManager.WriteModelCards(doc, modelCardsString); + _autocadDocumentManager.WriteModelCards(doc, modelCardState); } } diff --git a/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadGroupBaker.cs b/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadGroupBaker.cs index 9a40b5040..a9c5e801a 100644 --- a/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadGroupBaker.cs +++ b/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadGroupBaker.cs @@ -2,6 +2,7 @@ using Autodesk.AutoCAD.DatabaseServices; using Microsoft.Extensions.Logging; using Speckle.Connectors.Common.Conversion; using Speckle.Sdk; +using Speckle.Sdk.Dependencies; using Speckle.Sdk.Models.Proxies; namespace Speckle.Connectors.Autocad.HostApp; @@ -29,12 +30,12 @@ public class AutocadGroupBaker /// /// // TODO: Oguzhan! Do not report here too! But this is TBD that we don't know the shape of the report yet. - public List CreateGroups( + public IReadOnlyCollection CreateGroups( IEnumerable groupProxies, - Dictionary> applicationIdMap + Dictionary> applicationIdMap ) { - List results = new(); + HashSet results = new(); using var groupCreationTransaction = Application.DocumentManager.CurrentDocument.Database.TransactionManager.StartTransaction(); @@ -75,6 +76,6 @@ public class AutocadGroupBaker groupCreationTransaction.Commit(); - return results; + return results.Freeze(); } } diff --git a/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadIdleManager.cs b/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadIdleManager.cs index 10ebddb6f..50cfb8675 100644 --- a/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadIdleManager.cs +++ b/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadIdleManager.cs @@ -12,10 +12,7 @@ public sealed class AutocadIdleManager(IIdleCallManager idleCallManager) { private readonly IIdleCallManager _idleCallManager = idleCallManager; - protected override void AddEvent() - { - Application.Idle += AutocadAppOnIdle; - } + protected override void AddEvent() => Application.Idle += AutocadAppOnIdle; private void AutocadAppOnIdle(object? sender, EventArgs e) => _idleCallManager.AppOnIdle(() => Application.Idle -= AutocadAppOnIdle); diff --git a/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadInstanceBaker.cs b/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadInstanceBaker.cs index 35d44abe0..e1373f54f 100644 --- a/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadInstanceBaker.cs +++ b/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadInstanceBaker.cs @@ -12,6 +12,7 @@ using Speckle.DoubleNumerics; using Speckle.Sdk; using Speckle.Sdk.Common; using Speckle.Sdk.Common.Exceptions; +using Speckle.Sdk.Dependencies; using Speckle.Sdk.Models; using Speckle.Sdk.Models.Collections; using Speckle.Sdk.Models.Instances; @@ -22,19 +23,19 @@ namespace Speckle.Connectors.Autocad.HostApp; /// /// Expects to be a scoped dependency receive operation. /// -public class AutocadInstanceBaker : IInstanceBaker> +public class AutocadInstanceBaker : IInstanceBaker> { private readonly AutocadLayerBaker _layerBaker; - private readonly AutocadColorBaker _colorBaker; - private readonly AutocadMaterialBaker _materialBaker; + private readonly IAutocadColorBaker _colorBaker; + private readonly IAutocadMaterialBaker _materialBaker; private readonly AutocadContext _autocadContext; private readonly ILogger _logger; private readonly IConverterSettingsStore _converterSettings; public AutocadInstanceBaker( AutocadLayerBaker layerBaker, - AutocadColorBaker colorBaker, - AutocadMaterialBaker materialBaker, + IAutocadColorBaker colorBaker, + IAutocadMaterialBaker materialBaker, AutocadContext autocadContext, ILogger logger, IConverterSettingsStore converterSettings @@ -49,9 +50,9 @@ public class AutocadInstanceBaker : IInstanceBaker> } [SuppressMessage("Maintainability", "CA1506:Avoid excessive class coupling")] - public async Task BakeInstances( - IReadOnlyCollection<(Collection[] collectionPath, IInstanceComponent obj)> instanceComponents, - Dictionary> applicationIdMap, + public BakeResult BakeInstances( + ICollection<(Collection[] collectionPath, IInstanceComponent obj)> instanceComponents, + Dictionary> applicationIdMap, string baseLayerName, IProgress onOperationProgressed ) @@ -64,9 +65,9 @@ public class AutocadInstanceBaker : IInstanceBaker> var definitionIdAndApplicationIdMap = new Dictionary(); using var transaction = Application.DocumentManager.CurrentDocument.Database.TransactionManager.StartTransaction(); - var conversionResults = new List(); - var createdObjectIds = new List(); - var consumedObjectIds = new List(); + var conversionResults = new HashSet(); + var createdObjectIds = new HashSet(); + var consumedObjectIds = new HashSet(); var count = 0; foreach (var (collectionPath, instanceOrDefinition) in sortedInstanceComponents) @@ -79,7 +80,9 @@ public class AutocadInstanceBaker : IInstanceBaker> { // TODO: create definition (block table record) var constituentEntities = definitionProxy - .objects.Select(id => applicationIdMap.TryGetValue(id, out List? value) ? value : null) + .objects.Select(id => + applicationIdMap.TryGetValue(id, out IReadOnlyCollection? value) ? value : null + ) .Where(x => x is not null) .SelectMany(ent => ent!) .ToList(); @@ -109,8 +112,8 @@ public class AutocadInstanceBaker : IInstanceBaker> definitionIdAndApplicationIdMap[definitionProxy.applicationId] = id; transaction.AddNewlyCreatedDBObject(record, true); var consumedEntitiesHandleValues = constituentEntities.Select(ent => ent.GetSpeckleApplicationId()).ToArray(); - consumedObjectIds.AddRange(consumedEntitiesHandleValues); - createdObjectIds.RemoveAll(newId => consumedEntitiesHandleValues.Contains(newId)); + consumedObjectIds.UnionWith(consumedEntitiesHandleValues); + createdObjectIds.RemoveWhere(newId => consumedEntitiesHandleValues.Contains(newId)); } else if ( instanceOrDefinition is InstanceProxy instanceProxy @@ -128,7 +131,7 @@ public class AutocadInstanceBaker : IInstanceBaker> string layerName = _layerBaker.CreateLayerForReceive(collectionPath, baseLayerName); // get color and material if any - string instanceId = instanceProxy.applicationId ?? instanceProxy.id; + string instanceId = instanceProxy.applicationId ?? instanceProxy.id.NotNull(); AutocadColor? objColor = _colorBaker.ObjectColorsIdMap.TryGetValue(instanceId, out AutocadColor? color) ? color : null; @@ -167,8 +170,7 @@ public class AutocadInstanceBaker : IInstanceBaker> } transaction.Commit(); - await Task.Yield(); - return new(createdObjectIds, consumedObjectIds, conversionResults); + return new(createdObjectIds.Freeze(), consumedObjectIds.Freeze(), conversionResults.Freeze()); } /// diff --git a/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadLayerBaker.cs b/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadLayerBaker.cs index a7beefdb5..ebd4737eb 100644 --- a/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadLayerBaker.cs +++ b/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadLayerBaker.cs @@ -2,6 +2,7 @@ using Autodesk.AutoCAD.EditorInput; using Autodesk.AutoCAD.LayerManager; using Speckle.Connectors.Common.Operations.Receive; +using Speckle.Sdk.Common; using Speckle.Sdk.Models.Collections; using AutocadColor = Autodesk.AutoCAD.Colors.Color; @@ -11,15 +12,15 @@ public class AutocadLayerBaker : TraversalContextUnpacker { private readonly string _layerFilterName = "Speckle"; private readonly AutocadContext _autocadContext; - private readonly AutocadMaterialBaker _materialBaker; - private readonly AutocadColorBaker _colorBaker; + private readonly IAutocadMaterialBaker _materialBaker; + private readonly IAutocadColorBaker _colorBaker; private Document Doc => Application.DocumentManager.MdiActiveDocument; private readonly HashSet _uniqueLayerNames = new(); public AutocadLayerBaker( AutocadContext autocadContext, - AutocadMaterialBaker materialBaker, - AutocadColorBaker colorBaker + IAutocadMaterialBaker materialBaker, + IAutocadColorBaker colorBaker ) { _autocadContext = autocadContext; @@ -52,7 +53,7 @@ public class AutocadLayerBaker : TraversalContextUnpacker // Goes up the tree to find any potential parent layer that has a material/color for (int j = layerPath.Length - 1; j >= 0; j--) { - string layerId = layerPath[j].applicationId ?? layerPath[j].id; + string layerId = layerPath[j].applicationId ?? layerPath[j].id.NotNull(); if (!foundColor) { diff --git a/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadMaterialBaker.cs b/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadMaterialBaker.cs index 4aea349fb..1e09a7f2b 100644 --- a/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadMaterialBaker.cs +++ b/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadMaterialBaker.cs @@ -4,8 +4,10 @@ using Autodesk.AutoCAD.GraphicsInterface; using Microsoft.Extensions.Logging; using Speckle.Connectors.Common.Conversion; using Speckle.Connectors.Common.Operations; +using Speckle.InterfaceGenerator; using Speckle.Objects.Other; using Speckle.Sdk; +using Speckle.Sdk.Common; using Speckle.Sdk.Models; using Material = Autodesk.AutoCAD.DatabaseServices.Material; using RenderMaterial = Speckle.Objects.Other.RenderMaterial; @@ -15,7 +17,8 @@ namespace Speckle.Connectors.Autocad.HostApp; /// /// Expects to be a scoped dependency for a given operation and helps with layer creation and cleanup. /// -public class AutocadMaterialBaker +[GenerateAutoInterface] +public class AutocadMaterialBaker : IAutocadMaterialBaker { private readonly ILogger _logger; private readonly AutocadContext _autocadContext; @@ -42,7 +45,7 @@ public class AutocadMaterialBaker public bool TryGetMaterialId(Base originalObject, Base? parentObject, out ObjectId materialId) { materialId = ObjectId.Null; - var originalObjectId = originalObject.applicationId ?? originalObject.id; + var originalObjectId = originalObject.applicationId ?? originalObject.id.NotNull(); if (ObjectMaterialsIdMap.TryGetValue(originalObjectId, out ObjectId originalObjectMaterialId)) { materialId = originalObjectMaterialId; @@ -54,7 +57,7 @@ public class AutocadMaterialBaker return false; } - var subObjectId = parentObject.applicationId ?? parentObject.id; + var subObjectId = parentObject.applicationId ?? parentObject.id.NotNull(); if (ObjectMaterialsIdMap.TryGetValue(subObjectId, out ObjectId subObjectMaterialId)) { materialId = subObjectMaterialId; @@ -91,8 +94,8 @@ public class AutocadMaterialBaker transaction.Commit(); } - public async Task ParseAndBakeRenderMaterials( - List materialProxies, + public void ParseAndBakeRenderMaterials( + IReadOnlyCollection materialProxies, string baseLayerPrefix, IProgress onOperationProgressed ) @@ -114,7 +117,7 @@ public class AutocadMaterialBaker // bake render material RenderMaterial renderMaterial = materialProxy.value; - string renderMaterialId = renderMaterial.applicationId ?? renderMaterial.id; + string renderMaterialId = renderMaterial.applicationId ?? renderMaterial.id.NotNull(); ObjectId materialId = ObjectId.Null; if (!ObjectMaterialsIdMap.TryGetValue(renderMaterialId, out materialId)) @@ -140,7 +143,6 @@ public class AutocadMaterialBaker } transaction.Commit(); - await Task.Yield(); } private (ObjectId, ReceiveConversionResult) BakeMaterial( @@ -156,7 +158,7 @@ public class AutocadMaterialBaker { // POC: Currently we're relying on the render material name for identification if it's coming from speckle and from which model; could we do something else? // POC: we should assume render materials all have application ids? - string renderMaterialId = renderMaterial.applicationId ?? renderMaterial.id; + string renderMaterialId = renderMaterial.applicationId ?? renderMaterial.id.NotNull(); string matName = _autocadContext.RemoveInvalidChars( $"{renderMaterial.name}-({renderMaterialId})-{baseLayerPrefix}" ); diff --git a/Connectors/Autocad/Speckle.Connectors.AutocadShared/Operations/Receive/AutocadHostObjectBuilder.cs b/Connectors/Autocad/Speckle.Connectors.AutocadShared/Operations/Receive/AutocadHostObjectBuilder.cs index c9bdc4ee9..6cba21874 100644 --- a/Connectors/Autocad/Speckle.Connectors.AutocadShared/Operations/Receive/AutocadHostObjectBuilder.cs +++ b/Connectors/Autocad/Speckle.Connectors.AutocadShared/Operations/Receive/AutocadHostObjectBuilder.cs @@ -3,10 +3,13 @@ using Speckle.Connectors.Autocad.HostApp; using Speckle.Connectors.Autocad.HostApp.Extensions; using Speckle.Connectors.Common.Builders; using Speckle.Connectors.Common.Conversion; +using Speckle.Connectors.Common.Extensions; using Speckle.Connectors.Common.Operations; using Speckle.Connectors.Common.Operations.Receive; using Speckle.Converters.Common; using Speckle.Sdk; +using Speckle.Sdk.Common; +using Speckle.Sdk.Dependencies; using Speckle.Sdk.Models; using Speckle.Sdk.Models.Collections; using Speckle.Sdk.Models.Instances; @@ -17,84 +20,44 @@ namespace Speckle.Connectors.Autocad.Operations.Receive; /// /// Expects to be a scoped dependency per receive operation. /// -public class AutocadHostObjectBuilder : IHostObjectBuilder +public class AutocadHostObjectBuilder( + IRootToHostConverter converter, + AutocadLayerBaker layerBaker, + AutocadGroupBaker groupBaker, + AutocadInstanceBaker instanceBaker, + IAutocadMaterialBaker materialBaker, + IAutocadColorBaker colorBaker, + AutocadContext autocadContext, + RootObjectUnpacker rootObjectUnpacker +) : IHostObjectBuilder { - private readonly AutocadLayerBaker _layerBaker; - private readonly IRootToHostConverter _converter; - private readonly ISyncToThread _syncToThread; - private readonly AutocadGroupBaker _groupBaker; - private readonly AutocadMaterialBaker _materialBaker; - private readonly AutocadColorBaker _colorBaker; - private readonly AutocadInstanceBaker _instanceBaker; - private readonly AutocadContext _autocadContext; - private readonly RootObjectUnpacker _rootObjectUnpacker; - - public AutocadHostObjectBuilder( - IRootToHostConverter converter, - AutocadLayerBaker layerBaker, - AutocadGroupBaker groupBaker, - AutocadInstanceBaker instanceBaker, - AutocadMaterialBaker materialBaker, - AutocadColorBaker colorBaker, - ISyncToThread syncToThread, - AutocadContext autocadContext, - RootObjectUnpacker rootObjectUnpacker - ) - { - _converter = converter; - _layerBaker = layerBaker; - _groupBaker = groupBaker; - _instanceBaker = instanceBaker; - _materialBaker = materialBaker; - _colorBaker = colorBaker; - _syncToThread = syncToThread; - _autocadContext = autocadContext; - _rootObjectUnpacker = rootObjectUnpacker; - } - - public async Task Build( + public Task Build( Base rootObject, string projectName, string modelName, IProgress onOperationProgressed, - CancellationToken _ - ) - { - // NOTE: This is the only place we apply ISyncToThread across connectors. We need to sync up with main thread here - // after GetObject and Deserialization. It is anti-pattern now. Happiness level 3/10 but works. - return await _syncToThread - .RunOnThread( - async () => await BuildImpl(rootObject, projectName, modelName, onOperationProgressed).ConfigureAwait(false) - ) - .ConfigureAwait(false); - } - - private async Task BuildImpl( - Base rootObject, - string projectName, - string modelName, - IProgress onOperationProgressed + CancellationToken cancellationToken ) { // Prompt the UI conversion started. Progress bar will swoosh. onOperationProgressed.Report(new("Converting", null)); // Layer filter for received commit with project and model name - _layerBaker.CreateLayerFilter(projectName, modelName); + layerBaker.CreateLayerFilter(projectName, modelName); // 0 - Clean then Rock n Roll! - string baseLayerPrefix = _autocadContext.RemoveInvalidChars($"SPK-{projectName}-{modelName}-"); + string baseLayerPrefix = autocadContext.RemoveInvalidChars($"SPK-{projectName}-{modelName}-"); PreReceiveDeepClean(baseLayerPrefix); // 1 - Unpack objects and proxies from root commit object - var unpackedRoot = _rootObjectUnpacker.Unpack(rootObject); + var unpackedRoot = rootObjectUnpacker.Unpack(rootObject); // 2 - Split atomic objects and instance components with their path - var (atomicObjects, instanceComponents) = _rootObjectUnpacker.SplitAtomicObjectsAndInstances( + var (atomicObjects, instanceComponents) = rootObjectUnpacker.SplitAtomicObjectsAndInstances( unpackedRoot.ObjectsToConvert ); - var atomicObjectsWithPath = _layerBaker.GetAtomicObjectsWithPath(atomicObjects); - var instanceComponentsWithPath = _layerBaker.GetInstanceComponentsWithPath(instanceComponents); + var atomicObjectsWithPath = layerBaker.GetAtomicObjectsWithPath(atomicObjects); + var instanceComponentsWithPath = layerBaker.GetInstanceComponentsWithPath(instanceComponents); // POC: these are not captured by traversal, so we need to re-add them here if (unpackedRoot.DefinitionProxies != null && unpackedRoot.DefinitionProxies.Count > 0) @@ -108,33 +71,35 @@ public class AutocadHostObjectBuilder : IHostObjectBuilder // 3 - Bake materials and colors, as they are used later down the line by layers and objects if (unpackedRoot.RenderMaterialProxies != null) { - await _materialBaker - .ParseAndBakeRenderMaterials(unpackedRoot.RenderMaterialProxies, baseLayerPrefix, onOperationProgressed) - .ConfigureAwait(true); + materialBaker.ParseAndBakeRenderMaterials( + unpackedRoot.RenderMaterialProxies, + baseLayerPrefix, + onOperationProgressed + ); } if (unpackedRoot.ColorProxies != null) { - await _colorBaker.ParseColors(unpackedRoot.ColorProxies, onOperationProgressed).ConfigureAwait(true); + colorBaker.ParseColors(unpackedRoot.ColorProxies, onOperationProgressed); } - // 5 - Convert atomic objects - List results = new(); - List bakedObjectIds = new(); - Dictionary> applicationIdMap = new(); + // 4 - Convert atomic objects + HashSet results = new(); + HashSet bakedObjectIds = new(); + Dictionary> applicationIdMap = new(); var count = 0; foreach (var (layerPath, atomicObject) in atomicObjectsWithPath) { - string objectId = atomicObject.applicationId ?? atomicObject.id; + string objectId = atomicObject.applicationId ?? atomicObject.id.NotNull(); onOperationProgressed.Report(new("Converting objects", (double)++count / atomicObjects.Count)); + cancellationToken.ThrowIfCancellationRequested(); try { - List convertedObjects = await ConvertObject(atomicObject, layerPath, baseLayerPrefix) - .ConfigureAwait(true); + IReadOnlyCollection convertedObjects = ConvertObject(atomicObject, layerPath, baseLayerPrefix); applicationIdMap[objectId] = convertedObjects; - results.AddRange( + results.UnionWith( convertedObjects.Select(e => new ReceiveConversionResult( Status.SUCCESS, atomicObject, @@ -143,7 +108,7 @@ public class AutocadHostObjectBuilder : IHostObjectBuilder )) ); - bakedObjectIds.AddRange(convertedObjects.Select(e => e.GetSpeckleApplicationId())); + bakedObjectIds.UnionWith(convertedObjects.Select(e => e.GetSpeckleApplicationId())); } catch (Exception ex) when (!ex.IsFatal()) { @@ -151,72 +116,80 @@ public class AutocadHostObjectBuilder : IHostObjectBuilder } } - // 6 - Convert instances - var (createdInstanceIds, consumedObjectIds, instanceConversionResults) = await _instanceBaker - .BakeInstances(instanceComponentsWithPath, applicationIdMap, baseLayerPrefix, onOperationProgressed) - .ConfigureAwait(true); + // 5 - Convert instances + var (createdInstanceIds, consumedObjectIds, instanceConversionResults) = instanceBaker.BakeInstances( + instanceComponentsWithPath, + applicationIdMap, + baseLayerPrefix, + onOperationProgressed + ); - bakedObjectIds.RemoveAll(id => consumedObjectIds.Contains(id)); - bakedObjectIds.AddRange(createdInstanceIds); - results.RemoveAll(result => result.ResultId != null && consumedObjectIds.Contains(result.ResultId)); - results.AddRange(instanceConversionResults); + bakedObjectIds.RemoveWhere(id => consumedObjectIds.Contains(id)); + bakedObjectIds.UnionWith(createdInstanceIds); + results.RemoveWhere(result => result.ResultId != null && consumedObjectIds.Contains(result.ResultId)); + results.UnionWith(instanceConversionResults); - // 7 - Create groups + // 6 - Create groups if (unpackedRoot.GroupProxies != null) { - List groupResults = _groupBaker.CreateGroups( + IReadOnlyCollection groupResults = groupBaker.CreateGroups( unpackedRoot.GroupProxies, applicationIdMap ); - results.AddRange(groupResults); + results.UnionWith(groupResults); } - return new HostObjectBuilderResult(bakedObjectIds, results); + return Task.FromResult(new HostObjectBuilderResult(bakedObjectIds, results)); } private void PreReceiveDeepClean(string baseLayerPrefix) { - _layerBaker.DeleteAllLayersByPrefix(baseLayerPrefix); - _instanceBaker.PurgeInstances(baseLayerPrefix); - _materialBaker.PurgeMaterials(baseLayerPrefix); + layerBaker.DeleteAllLayersByPrefix(baseLayerPrefix); + instanceBaker.PurgeInstances(baseLayerPrefix); + materialBaker.PurgeMaterials(baseLayerPrefix); } - private async Task> ConvertObject(Base obj, Collection[] layerPath, string baseLayerNamePrefix) + private IReadOnlyCollection ConvertObject(Base obj, Collection[] layerPath, string baseLayerNamePrefix) { - string layerName = _layerBaker.CreateLayerForReceive(layerPath, baseLayerNamePrefix); - var convertedEntities = new List(); + string layerName = layerBaker.CreateLayerForReceive(layerPath, baseLayerNamePrefix); + var convertedEntities = new HashSet(); using var tr = Application.DocumentManager.CurrentDocument.Database.TransactionManager.StartTransaction(); // 1: convert - var converted = _converter.Convert(obj); + var converted = converter.Convert(obj); // 2: handle result - if (converted is Entity entity) + switch (converted) { - var bakedEntity = BakeObject(entity, obj, layerName); - convertedEntities.Add(bakedEntity); - } - else if (converted is IEnumerable<(object, Base)> fallbackConversionResult) - { - var bakedFallbackEntities = BakeObjectsAsGroup(fallbackConversionResult, obj, layerName, baseLayerNamePrefix); - convertedEntities.AddRange(bakedFallbackEntities); + case Entity entity: + var bakedEntity = BakeObject(entity, obj, layerName); + convertedEntities.Add(bakedEntity); + break; + + case List<(Entity, Base)> listConversionResult: // this is from fallback conversion for brep/brepx/subdx/extrusionx/polycurve + var bakedFallbackEntities = BakeObjectsAsGroup(listConversionResult, obj, layerName, baseLayerNamePrefix); + convertedEntities.UnionWith(bakedFallbackEntities); + break; + + default: + // TODO: capture defualt case with report object here? Same as in Rhino + break; } tr.Commit(); - await Task.Delay(10).ConfigureAwait(true); - return convertedEntities; + return convertedEntities.Freeze(); } private Entity BakeObject(Entity entity, Base originalObject, string layerName, Base? parentObject = null) { - var objId = originalObject.applicationId ?? originalObject.id; - if (_colorBaker.ObjectColorsIdMap.TryGetValue(objId, out AutocadColor? color)) + var objId = originalObject.applicationId ?? originalObject.id.NotNull(); + if (colorBaker.ObjectColorsIdMap.TryGetValue(objId, out AutocadColor? color)) { entity.Color = color; } - if (_materialBaker.TryGetMaterialId(originalObject, parentObject, out ObjectId matId)) + if (materialBaker.TryGetMaterialId(originalObject, parentObject, out ObjectId matId)) { entity.MaterialId = matId; } @@ -226,7 +199,7 @@ public class AutocadHostObjectBuilder : IHostObjectBuilder } private List BakeObjectsAsGroup( - IEnumerable<(object, Base)> fallbackConversionResult, + List<(Entity, Base)> fallbackConversionResult, Base parentObject, string layerName, string baseLayerName @@ -236,22 +209,21 @@ public class AutocadHostObjectBuilder : IHostObjectBuilder var entities = new List(); foreach (var (conversionResult, originalObject) in fallbackConversionResult) { - if (conversionResult is not Entity entity) - { - // TODO: throw? - continue; - } + BakeObject(conversionResult, originalObject, layerName, parentObject); + ids.Add(conversionResult.ObjectId); + entities.Add(conversionResult); + } - BakeObject(entity, originalObject, layerName, parentObject); - ids.Add(entity.ObjectId); - entities.Add(entity); + if (entities.Count <= 1) // return if empty list or only one, because we don't want to create empty or single item groups. + { + return entities; } var tr = Application.DocumentManager.CurrentDocument.Database.TransactionManager.TopTransaction; var groupDictionary = (DBDictionary) tr.GetObject(Application.DocumentManager.CurrentDocument.Database.GroupDictionaryId, OpenMode.ForWrite); - var groupName = _autocadContext.RemoveInvalidChars( + var groupName = autocadContext.RemoveInvalidChars( $@"{parentObject.speckle_type.Split('.').Last()} - {parentObject.applicationId ?? parentObject.id} ({baseLayerName})" ); diff --git a/Connectors/Autocad/Speckle.Connectors.AutocadShared/Operations/Send/AutocadRootObjectBaseBuilder.cs b/Connectors/Autocad/Speckle.Connectors.AutocadShared/Operations/Send/AutocadRootObjectBaseBuilder.cs index 9c8c77e15..1afe1c683 100644 --- a/Connectors/Autocad/Speckle.Connectors.AutocadShared/Operations/Send/AutocadRootObjectBaseBuilder.cs +++ b/Connectors/Autocad/Speckle.Connectors.AutocadShared/Operations/Send/AutocadRootObjectBaseBuilder.cs @@ -49,13 +49,6 @@ public abstract class AutocadRootObjectBaseBuilder : IRootObjectBuilder Build( - IReadOnlyList objects, - SendInfo sendInfo, - IProgress onOperationProgressed, - CancellationToken ct = default - ) => Task.FromResult(BuildSync(objects, sendInfo, onOperationProgressed, ct)); - [SuppressMessage( "Maintainability", "CA1506:Avoid excessive class coupling", @@ -65,11 +58,11 @@ public abstract class AutocadRootObjectBaseBuilder : IRootObjectBuilder Build( IReadOnlyList objects, SendInfo sendInfo, IProgress onOperationProgressed, - CancellationToken ct = default + CancellationToken cancellationToken ) { // 0 - Init the root @@ -101,7 +94,7 @@ public abstract class AutocadRootObjectBaseBuilder : IRootObjectBuilder(); - PaletteSet.AddVisual($"Speckle (Beta) for {AppUtils.App.Name} WebView", panelWebView); + PaletteSet.AddVisual("Speckle (Beta)", panelWebView); FocusPalette(); } diff --git a/Connectors/Autocad/Speckle.Connectors.AutocadShared/Plugin/AutocadRibbon.cs b/Connectors/Autocad/Speckle.Connectors.AutocadShared/Plugin/AutocadRibbon.cs index 7d6dc8b30..fef1accfd 100644 --- a/Connectors/Autocad/Speckle.Connectors.AutocadShared/Plugin/AutocadRibbon.cs +++ b/Connectors/Autocad/Speckle.Connectors.AutocadShared/Plugin/AutocadRibbon.cs @@ -46,20 +46,20 @@ public class AutocadRibbon private void Create() { - RibbonTab tab = FindOrMakeTab("Add-ins"); - RibbonPanelSource source = new() { Title = "Speckle 2 (New Beta)" }; + RibbonTab tab = FindOrMakeTab("Speckle"); + RibbonPanelSource source = new() { Title = "Speckle (Beta)" }; RibbonPanel panel = new() { Source = source }; tab.Panels.Add(panel); RibbonToolTip speckleToolTip = new() { - Title = "Speckle 2 (New Beta)", - Content = "Speckle Connector for " + AppUtils.App.Name, + Title = "Speckle (Beta)", + Content = $"Next Gen Speckle Connector (Beta) for {AppUtils.App.Name}", IsHelpEnabled = true // Without this "Press F1 for help" does not appear in the tooltip }; - _ = CreateSpeckleButton("Connector " + AppUtils.App.Name + " (New)", source, null, speckleToolTip, "logo"); + _ = CreateSpeckleButton("Speckle (Beta)", source, null, speckleToolTip, "logo"); } private void ComponentManager_ItemInitialized(object? sender, RibbonItemEventArgs e) diff --git a/Connectors/Autocad/Speckle.Connectors.AutocadShared/Speckle.Connectors.AutocadShared.projitems b/Connectors/Autocad/Speckle.Connectors.AutocadShared/Speckle.Connectors.AutocadShared.projitems index 682bf35e2..579b8cadb 100644 --- a/Connectors/Autocad/Speckle.Connectors.AutocadShared/Speckle.Connectors.AutocadShared.projitems +++ b/Connectors/Autocad/Speckle.Connectors.AutocadShared/Speckle.Connectors.AutocadShared.projitems @@ -9,6 +9,7 @@ Speckle.Connectors.AutocadShared + @@ -21,6 +22,7 @@ + @@ -29,7 +31,6 @@ - diff --git a/Connectors/Autocad/Speckle.Connectors.Civil3d2022/packages.lock.json b/Connectors/Autocad/Speckle.Connectors.Civil3d2022/packages.lock.json index 053764093..e934606bf 100644 --- a/Connectors/Autocad/Speckle.Connectors.Civil3d2022/packages.lock.json +++ b/Connectors/Autocad/Speckle.Connectors.Civil3d2022/packages.lock.json @@ -168,11 +168,6 @@ "resolved": "8.0.0", "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" }, - "Speckle.DoubleNumerics": { - "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w==" - }, "Speckle.Newtonsoft.Json": { "type": "Transitive", "resolved": "13.0.2", @@ -268,9 +263,9 @@ "dependencies": { "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", "Speckle.Connectors.Logging": "[1.0.0, )", - "Speckle.Objects": "[3.1.0-dev.191, )", - "Speckle.Sdk": "[3.1.0-dev.191, )", - "Speckle.Sdk.Dependencies": "[3.1.0-dev.191, )" + "Speckle.Objects": "[3.1.1, )", + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" } }, "speckle.connectors.dui": { @@ -278,9 +273,8 @@ "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", "Speckle.Connectors.Common": "[1.0.0, )", - "Speckle.Sdk": "[3.1.0-dev.191, )", - "Speckle.Sdk.Dependencies": "[3.1.0-dev.191, )", - "System.Threading.Tasks.Dataflow": "[6.0.0, )" + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" } }, "speckle.connectors.dui.webview": { @@ -305,7 +299,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.191, )" + "Speckle.Objects": "[3.1.1, )" } }, "Microsoft.Extensions.DependencyInjection": { @@ -347,20 +341,26 @@ "resolved": "1.0.1938.49", "contentHash": "z8KnFnaTYzhA/ZnyRX0qGfS1NU5ZBJeClAH64F0fVDvdDJTvME7xl6zTJ0Jlfe1BtL3C0NH9xTy64shg2baKdw==" }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "+m7jRFm0ABbkcSz2UphdxAsislY10IpQ1u79c8a3aVvegLjnsVQZ1sXfRIRO1aFdulkhjYKXYpB3N9M8Z+epgQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.191" + "Speckle.Sdk": "3.1.1" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "VVT3LJiYlhqnggxxdeTt1QLrqfxDb044x0yX6kxS9b5MRzeDvK2Vz86pLDfuHs+SXvDimRVfYx1M42IW/aPcTQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.Bcl.AsyncInterfaces": "5.0.0", @@ -368,22 +368,16 @@ "Microsoft.Data.Sqlite": "7.0.5", "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", "Microsoft.Extensions.Logging": "2.2.0", - "Speckle.DoubleNumerics": "4.0.1", + "Speckle.DoubleNumerics": "4.1.0", "Speckle.Newtonsoft.Json": "13.0.2", - "Speckle.Sdk.Dependencies": "3.1.0-dev.191" + "Speckle.Sdk.Dependencies": "3.1.1" } }, "Speckle.Sdk.Dependencies": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "EmEOyjsGsNi56Z/ZoBOn8WirTmIT2yqWvlUeUh0BSPX2TDMZXHTKOM/kHmP6HSd10KVFn2Zo/ItY7/K9iRtL1Q==" - }, - "System.Threading.Tasks.Dataflow": { - "type": "CentralTransitive", - "requested": "[6.0.0, )", - "resolved": "6.0.0", - "contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA==" + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" } } } diff --git a/Connectors/Autocad/Speckle.Connectors.Civil3d2023/packages.lock.json b/Connectors/Autocad/Speckle.Connectors.Civil3d2023/packages.lock.json index 79c866130..da637e759 100644 --- a/Connectors/Autocad/Speckle.Connectors.Civil3d2023/packages.lock.json +++ b/Connectors/Autocad/Speckle.Connectors.Civil3d2023/packages.lock.json @@ -168,11 +168,6 @@ "resolved": "8.0.0", "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" }, - "Speckle.DoubleNumerics": { - "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w==" - }, "Speckle.Newtonsoft.Json": { "type": "Transitive", "resolved": "13.0.2", @@ -268,9 +263,9 @@ "dependencies": { "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", "Speckle.Connectors.Logging": "[1.0.0, )", - "Speckle.Objects": "[3.1.0-dev.191, )", - "Speckle.Sdk": "[3.1.0-dev.191, )", - "Speckle.Sdk.Dependencies": "[3.1.0-dev.191, )" + "Speckle.Objects": "[3.1.1, )", + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" } }, "speckle.connectors.dui": { @@ -278,9 +273,8 @@ "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", "Speckle.Connectors.Common": "[1.0.0, )", - "Speckle.Sdk": "[3.1.0-dev.191, )", - "Speckle.Sdk.Dependencies": "[3.1.0-dev.191, )", - "System.Threading.Tasks.Dataflow": "[6.0.0, )" + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" } }, "speckle.connectors.dui.webview": { @@ -305,7 +299,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.191, )" + "Speckle.Objects": "[3.1.1, )" } }, "Microsoft.Extensions.DependencyInjection": { @@ -347,20 +341,26 @@ "resolved": "1.0.1938.49", "contentHash": "z8KnFnaTYzhA/ZnyRX0qGfS1NU5ZBJeClAH64F0fVDvdDJTvME7xl6zTJ0Jlfe1BtL3C0NH9xTy64shg2baKdw==" }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "+m7jRFm0ABbkcSz2UphdxAsislY10IpQ1u79c8a3aVvegLjnsVQZ1sXfRIRO1aFdulkhjYKXYpB3N9M8Z+epgQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.191" + "Speckle.Sdk": "3.1.1" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "VVT3LJiYlhqnggxxdeTt1QLrqfxDb044x0yX6kxS9b5MRzeDvK2Vz86pLDfuHs+SXvDimRVfYx1M42IW/aPcTQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.Bcl.AsyncInterfaces": "5.0.0", @@ -368,22 +368,16 @@ "Microsoft.Data.Sqlite": "7.0.5", "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", "Microsoft.Extensions.Logging": "2.2.0", - "Speckle.DoubleNumerics": "4.0.1", + "Speckle.DoubleNumerics": "4.1.0", "Speckle.Newtonsoft.Json": "13.0.2", - "Speckle.Sdk.Dependencies": "3.1.0-dev.191" + "Speckle.Sdk.Dependencies": "3.1.1" } }, "Speckle.Sdk.Dependencies": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "EmEOyjsGsNi56Z/ZoBOn8WirTmIT2yqWvlUeUh0BSPX2TDMZXHTKOM/kHmP6HSd10KVFn2Zo/ItY7/K9iRtL1Q==" - }, - "System.Threading.Tasks.Dataflow": { - "type": "CentralTransitive", - "requested": "[6.0.0, )", - "resolved": "6.0.0", - "contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA==" + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" } } } diff --git a/Connectors/Autocad/Speckle.Connectors.Civil3d2024/packages.lock.json b/Connectors/Autocad/Speckle.Connectors.Civil3d2024/packages.lock.json index 33982214f..c63932e0d 100644 --- a/Connectors/Autocad/Speckle.Connectors.Civil3d2024/packages.lock.json +++ b/Connectors/Autocad/Speckle.Connectors.Civil3d2024/packages.lock.json @@ -168,11 +168,6 @@ "resolved": "8.0.0", "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" }, - "Speckle.DoubleNumerics": { - "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w==" - }, "Speckle.Newtonsoft.Json": { "type": "Transitive", "resolved": "13.0.2", @@ -268,9 +263,9 @@ "dependencies": { "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", "Speckle.Connectors.Logging": "[1.0.0, )", - "Speckle.Objects": "[3.1.0-dev.191, )", - "Speckle.Sdk": "[3.1.0-dev.191, )", - "Speckle.Sdk.Dependencies": "[3.1.0-dev.191, )" + "Speckle.Objects": "[3.1.1, )", + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" } }, "speckle.connectors.dui": { @@ -278,9 +273,8 @@ "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", "Speckle.Connectors.Common": "[1.0.0, )", - "Speckle.Sdk": "[3.1.0-dev.191, )", - "Speckle.Sdk.Dependencies": "[3.1.0-dev.191, )", - "System.Threading.Tasks.Dataflow": "[6.0.0, )" + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" } }, "speckle.connectors.dui.webview": { @@ -305,7 +299,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.191, )" + "Speckle.Objects": "[3.1.1, )" } }, "Microsoft.Extensions.DependencyInjection": { @@ -347,20 +341,26 @@ "resolved": "1.0.1938.49", "contentHash": "z8KnFnaTYzhA/ZnyRX0qGfS1NU5ZBJeClAH64F0fVDvdDJTvME7xl6zTJ0Jlfe1BtL3C0NH9xTy64shg2baKdw==" }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "+m7jRFm0ABbkcSz2UphdxAsislY10IpQ1u79c8a3aVvegLjnsVQZ1sXfRIRO1aFdulkhjYKXYpB3N9M8Z+epgQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.191" + "Speckle.Sdk": "3.1.1" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "VVT3LJiYlhqnggxxdeTt1QLrqfxDb044x0yX6kxS9b5MRzeDvK2Vz86pLDfuHs+SXvDimRVfYx1M42IW/aPcTQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.Bcl.AsyncInterfaces": "5.0.0", @@ -368,22 +368,16 @@ "Microsoft.Data.Sqlite": "7.0.5", "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", "Microsoft.Extensions.Logging": "2.2.0", - "Speckle.DoubleNumerics": "4.0.1", + "Speckle.DoubleNumerics": "4.1.0", "Speckle.Newtonsoft.Json": "13.0.2", - "Speckle.Sdk.Dependencies": "3.1.0-dev.191" + "Speckle.Sdk.Dependencies": "3.1.1" } }, "Speckle.Sdk.Dependencies": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "EmEOyjsGsNi56Z/ZoBOn8WirTmIT2yqWvlUeUh0BSPX2TDMZXHTKOM/kHmP6HSd10KVFn2Zo/ItY7/K9iRtL1Q==" - }, - "System.Threading.Tasks.Dataflow": { - "type": "CentralTransitive", - "requested": "[6.0.0, )", - "resolved": "6.0.0", - "contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA==" + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" } } } diff --git a/Connectors/Autocad/Speckle.Connectors.Civil3d2025/packages.lock.json b/Connectors/Autocad/Speckle.Connectors.Civil3d2025/packages.lock.json index 53bc1ed56..230fa0c80 100644 --- a/Connectors/Autocad/Speckle.Connectors.Civil3d2025/packages.lock.json +++ b/Connectors/Autocad/Speckle.Connectors.Civil3d2025/packages.lock.json @@ -159,11 +159,6 @@ "resolved": "8.0.0", "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" }, - "Speckle.DoubleNumerics": { - "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w==" - }, "Speckle.Newtonsoft.Json": { "type": "Transitive", "resolved": "13.0.2", @@ -224,9 +219,9 @@ "dependencies": { "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", "Speckle.Connectors.Logging": "[1.0.0, )", - "Speckle.Objects": "[3.1.0-dev.191, )", - "Speckle.Sdk": "[3.1.0-dev.191, )", - "Speckle.Sdk.Dependencies": "[3.1.0-dev.191, )" + "Speckle.Objects": "[3.1.1, )", + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" } }, "speckle.connectors.dui": { @@ -234,9 +229,8 @@ "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", "Speckle.Connectors.Common": "[1.0.0, )", - "Speckle.Sdk": "[3.1.0-dev.191, )", - "Speckle.Sdk.Dependencies": "[3.1.0-dev.191, )", - "System.Threading.Tasks.Dataflow": "[6.0.0, )" + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" } }, "speckle.connectors.dui.webview": { @@ -262,7 +256,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.191, )" + "Speckle.Objects": "[3.1.1, )" } }, "Microsoft.Extensions.DependencyInjection": { @@ -304,42 +298,42 @@ "resolved": "1.0.1938.49", "contentHash": "z8KnFnaTYzhA/ZnyRX0qGfS1NU5ZBJeClAH64F0fVDvdDJTvME7xl6zTJ0Jlfe1BtL3C0NH9xTy64shg2baKdw==" }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "+m7jRFm0ABbkcSz2UphdxAsislY10IpQ1u79c8a3aVvegLjnsVQZ1sXfRIRO1aFdulkhjYKXYpB3N9M8Z+epgQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.191" + "Speckle.Sdk": "3.1.1" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "VVT3LJiYlhqnggxxdeTt1QLrqfxDb044x0yX6kxS9b5MRzeDvK2Vz86pLDfuHs+SXvDimRVfYx1M42IW/aPcTQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.CSharp": "4.7.0", "Microsoft.Data.Sqlite": "7.0.5", "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", "Microsoft.Extensions.Logging": "2.2.0", - "Speckle.DoubleNumerics": "4.0.1", + "Speckle.DoubleNumerics": "4.1.0", "Speckle.Newtonsoft.Json": "13.0.2", - "Speckle.Sdk.Dependencies": "3.1.0-dev.191" + "Speckle.Sdk.Dependencies": "3.1.1" } }, "Speckle.Sdk.Dependencies": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "EmEOyjsGsNi56Z/ZoBOn8WirTmIT2yqWvlUeUh0BSPX2TDMZXHTKOM/kHmP6HSd10KVFn2Zo/ItY7/K9iRtL1Q==" - }, - "System.Threading.Tasks.Dataflow": { - "type": "CentralTransitive", - "requested": "[6.0.0, )", - "resolved": "6.0.0", - "contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA==" + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" } }, "net8.0-windows7.0/win-x64": { diff --git a/Connectors/Autocad/Speckle.Connectors.Civil3dShared/Bindings/Civil3dReceiveBinding.cs b/Connectors/Autocad/Speckle.Connectors.Civil3dShared/Bindings/Civil3dReceiveBinding.cs new file mode 100644 index 000000000..a986aea62 --- /dev/null +++ b/Connectors/Autocad/Speckle.Connectors.Civil3dShared/Bindings/Civil3dReceiveBinding.cs @@ -0,0 +1,60 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Speckle.Connectors.Autocad.Bindings; +using Speckle.Connectors.Common.Cancellation; +using Speckle.Connectors.Common.Threading; +using Speckle.Connectors.DUI.Bindings; +using Speckle.Connectors.DUI.Bridge; +using Speckle.Connectors.DUI.Models; +using Speckle.Converters.Autocad; +using Speckle.Converters.Civil3dShared; +using Speckle.Converters.Common; +using Speckle.Sdk; + +namespace Speckle.Connectors.Civil3dShared.Bindings; + +public sealed class Civil3dReceiveBinding : AutocadReceiveBaseBinding +{ + private readonly ICivil3dConversionSettingsFactory _civil3dConversionSettingsFactory; + private readonly IAutocadConversionSettingsFactory _autocadConversionSettingsFactory; + + public Civil3dReceiveBinding( + DocumentModelStore store, + IBrowserBridge parent, + ICancellationManager cancellationManager, + IServiceProvider serviceProvider, + IOperationProgressManager operationProgressManager, + ILogger logger, + ICivil3dConversionSettingsFactory civil3dConversionSettingsFactory, + IAutocadConversionSettingsFactory autocadConversionSettingsFactory, + ISpeckleApplication speckleApplication, + IThreadContext threadContext + ) + : base( + store, + parent, + cancellationManager, + serviceProvider, + operationProgressManager, + logger, + speckleApplication, + threadContext + ) + { + _civil3dConversionSettingsFactory = civil3dConversionSettingsFactory; + _autocadConversionSettingsFactory = autocadConversionSettingsFactory; + } + + // POC: we're registering the conversion settings for autocad here because we need the autocad conversion settings to be able to use the autocad typed converters. + // POC: We need a separate receive binding for civil3d due to using a different unit converter (needed for conversion settings construction) + protected override void InitializeSettings(IServiceProvider serviceProvider) + { + serviceProvider + .GetRequiredService>() + .Initialize(_civil3dConversionSettingsFactory.Create(Application.DocumentManager.CurrentDocument)); + + serviceProvider + .GetRequiredService>() + .Initialize(_autocadConversionSettingsFactory.Create(Application.DocumentManager.CurrentDocument)); + } +} diff --git a/Connectors/Autocad/Speckle.Connectors.Civil3dShared/Bindings/Civil3dSendBinding.cs b/Connectors/Autocad/Speckle.Connectors.Civil3dShared/Bindings/Civil3dSendBinding.cs index a1aa571b0..d497027e6 100644 --- a/Connectors/Autocad/Speckle.Connectors.Civil3dShared/Bindings/Civil3dSendBinding.cs +++ b/Connectors/Autocad/Speckle.Connectors.Civil3dShared/Bindings/Civil3dSendBinding.cs @@ -1,9 +1,9 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Speckle.Connectors.Autocad.Bindings; -using Speckle.Connectors.Autocad.HostApp; using Speckle.Connectors.Common.Caching; using Speckle.Connectors.Common.Cancellation; +using Speckle.Connectors.Common.Threading; using Speckle.Connectors.DUI.Bindings; using Speckle.Connectors.DUI.Bridge; using Speckle.Connectors.DUI.Models; @@ -22,21 +22,22 @@ public sealed class Civil3dSendBinding : AutocadSendBaseBinding public Civil3dSendBinding( DocumentModelStore store, - IAutocadIdleManager idleManager, IBrowserBridge parent, IEnumerable sendFilters, - CancellationManager cancellationManager, + ICancellationManager cancellationManager, IServiceProvider serviceProvider, ISendConversionCache sendConversionCache, IOperationProgressManager operationProgressManager, ILogger logger, ICivil3dConversionSettingsFactory civil3dConversionSettingsFactory, IAutocadConversionSettingsFactory autocadConversionSettingsFactory, - ISpeckleApplication speckleApplication + ISpeckleApplication speckleApplication, + IThreadContext threadContext, + ITopLevelExceptionHandler topLevelExceptionHandler, + IAppIdleManager appIdleManager ) : base( store, - idleManager, parent, sendFilters, cancellationManager, @@ -44,7 +45,10 @@ public sealed class Civil3dSendBinding : AutocadSendBaseBinding sendConversionCache, operationProgressManager, logger, - speckleApplication + speckleApplication, + threadContext, + topLevelExceptionHandler, + appIdleManager ) { _civil3dConversionSettingsFactory = civil3dConversionSettingsFactory; diff --git a/Connectors/Autocad/Speckle.Connectors.Civil3dShared/DependencyInjection/Civil3dConnectorModule.cs b/Connectors/Autocad/Speckle.Connectors.Civil3dShared/DependencyInjection/Civil3dConnectorModule.cs index 7dcb841d2..f11b60c85 100644 --- a/Connectors/Autocad/Speckle.Connectors.Civil3dShared/DependencyInjection/Civil3dConnectorModule.cs +++ b/Connectors/Autocad/Speckle.Connectors.Civil3dShared/DependencyInjection/Civil3dConnectorModule.cs @@ -6,7 +6,6 @@ using Speckle.Connectors.Civil3dShared.Bindings; using Speckle.Connectors.Civil3dShared.Operations.Send; using Speckle.Connectors.Common.Builders; using Speckle.Connectors.DUI.Bindings; -using Speckle.Converters.Civil3dShared.Helpers; using Speckle.Converters.Civil3dShared.ToSpeckle; using Speckle.Sdk; @@ -17,18 +16,20 @@ public static class Civil3dConnectorModule public static void AddCivil3d(this IServiceCollection serviceCollection) { serviceCollection.AddAutocadBase(); - serviceCollection.LoadSend(); - // register civil specific send classes + // add send + serviceCollection.LoadSend(); serviceCollection.AddScoped, Civil3dRootObjectBuilder>(); serviceCollection.AddSingleton(); - // automatically detects the Class:IClass interface pattern to register all generated interfaces - serviceCollection.AddMatchingInterfacesAsTransient(Assembly.GetExecutingAssembly()); + // add receive + serviceCollection.LoadReceive(); + serviceCollection.AddSingleton(); // additional classes serviceCollection.AddScoped(); - serviceCollection.AddScoped(); - serviceCollection.AddScoped(); + + // automatically detects the Class:IClass interface pattern to register all generated interfaces + serviceCollection.AddMatchingInterfacesAsTransient(Assembly.GetExecutingAssembly()); } } diff --git a/Connectors/Autocad/Speckle.Connectors.Civil3dShared/Operations/Send/Civil3dRootObjectBuilder.cs b/Connectors/Autocad/Speckle.Connectors.Civil3dShared/Operations/Send/Civil3dRootObjectBuilder.cs index db9850f07..9f17bd7e0 100644 --- a/Connectors/Autocad/Speckle.Connectors.Civil3dShared/Operations/Send/Civil3dRootObjectBuilder.cs +++ b/Connectors/Autocad/Speckle.Connectors.Civil3dShared/Operations/Send/Civil3dRootObjectBuilder.cs @@ -4,7 +4,6 @@ using Speckle.Connectors.Autocad.HostApp; using Speckle.Connectors.Autocad.Operations.Send; using Speckle.Connectors.Common.Caching; using Speckle.Connectors.Common.Operations; -using Speckle.Converters.Civil3dShared.Helpers; using Speckle.Converters.Civil3dShared.ToSpeckle; using Speckle.Converters.Common; using Speckle.Sdk.Logging; @@ -16,14 +15,10 @@ public sealed class Civil3dRootObjectBuilder : AutocadRootObjectBaseBuilder { private readonly AutocadLayerUnpacker _layerUnpacker; private readonly PropertySetDefinitionHandler _propertySetDefinitionHandler; - private readonly CatchmentGroupHandler _catchmentGroupHandler; - private readonly PipeNetworkHandler _pipeNetworkHandler; public Civil3dRootObjectBuilder( AutocadLayerUnpacker layerUnpacker, PropertySetDefinitionHandler propertySetDefinitionHandler, - CatchmentGroupHandler catchmentGroupHandler, - PipeNetworkHandler pipeNetworkHandler, IRootToSpeckleConverter converter, ISendConversionCache sendConversionCache, AutocadInstanceUnpacker instanceObjectManager, @@ -46,8 +41,6 @@ public sealed class Civil3dRootObjectBuilder : AutocadRootObjectBaseBuilder { _layerUnpacker = layerUnpacker; _propertySetDefinitionHandler = propertySetDefinitionHandler; - _catchmentGroupHandler = catchmentGroupHandler; - _pipeNetworkHandler = pipeNetworkHandler; } public override (Collection, LayerTableRecord?) CreateObjectCollection(Entity entity, Transaction tr) @@ -57,11 +50,8 @@ public sealed class Civil3dRootObjectBuilder : AutocadRootObjectBaseBuilder return (layer, autocadLayer); } - // POC: probably will need to add Network proxies as well public override void AddAdditionalProxiesToRoot(Collection rootObject) { rootObject[ProxyKeys.PROPERTYSET_DEFINITIONS] = _propertySetDefinitionHandler.Definitions; - rootObject["catchmentGroupProxies"] = _catchmentGroupHandler.CatchmentGroupProxiesCache.Values.ToList(); - rootObject["pipeNetworkProxies"] = _pipeNetworkHandler.PipeNetworkProxiesCache.Values.ToList(); } } diff --git a/Connectors/Autocad/Speckle.Connectors.Civil3dShared/Speckle.Connectors.Civil3dShared.projitems b/Connectors/Autocad/Speckle.Connectors.Civil3dShared/Speckle.Connectors.Civil3dShared.projitems index 391ba05b1..69bca876a 100644 --- a/Connectors/Autocad/Speckle.Connectors.Civil3dShared/Speckle.Connectors.Civil3dShared.projitems +++ b/Connectors/Autocad/Speckle.Connectors.Civil3dShared/Speckle.Connectors.Civil3dShared.projitems @@ -9,13 +9,13 @@ Speckle.Connectors.Civil3dShared + - \ No newline at end of file diff --git a/Connectors/CSi/Speckle.Connectors.CSiShared/Bindings/CsiSharedBasicConnectorBinding.cs b/Connectors/CSi/Speckle.Connectors.CSiShared/Bindings/CsiSharedBasicConnectorBinding.cs new file mode 100644 index 000000000..2923d690c --- /dev/null +++ b/Connectors/CSi/Speckle.Connectors.CSiShared/Bindings/CsiSharedBasicConnectorBinding.cs @@ -0,0 +1,71 @@ +using Speckle.Connectors.Common.Threading; +using Speckle.Connectors.DUI.Bindings; +using Speckle.Connectors.DUI.Bridge; +using Speckle.Connectors.DUI.Models; +using Speckle.Connectors.DUI.Models.Card; +using Speckle.Sdk; + +namespace Speckle.Connectors.CSiShared.Bindings; + +public class CsiSharedBasicConnectorBinding : IBasicConnectorBinding +{ + private readonly ISpeckleApplication _speckleApplication; + private readonly DocumentModelStore _store; + private readonly ITopLevelExceptionHandler _topLevelExceptionHandler; + private readonly IThreadContext _threadContext; + public string Name => "baseBinding"; + public IBrowserBridge Parent { get; } + public BasicConnectorBindingCommands Commands { get; } + + public CsiSharedBasicConnectorBinding( + IBrowserBridge parent, + ISpeckleApplication speckleApplication, + DocumentModelStore store, + ITopLevelExceptionHandler topLevelExceptionHandler, + IThreadContext threadContext + ) + { + _threadContext = threadContext; + Parent = parent; + _speckleApplication = speckleApplication; + _store = store; + _topLevelExceptionHandler = topLevelExceptionHandler; + Commands = new BasicConnectorBindingCommands(Parent); + + _store.DocumentChanged += (_, _) => + _topLevelExceptionHandler.FireAndForget(async () => + { + // enforce main thread + await _threadContext.RunOnMainAsync(async () => + { + await Commands.NotifyDocumentChanged(); + }); + }); + } + + public string GetConnectorVersion() => _speckleApplication.SpeckleVersion; + + public string GetSourceApplicationName() => _speckleApplication.Slug; + + public string GetSourceApplicationVersion() => _speckleApplication.HostApplicationVersion; + + public DocumentInfo? GetDocumentInfo() => new("ETABS Model", "ETABS Model", "1"); + + public DocumentModelStore GetDocumentState() => _store; + + /// Operations must run on the main thread for ETABS and SAP 2000 + public void AddModel(ModelCard model) => + _topLevelExceptionHandler.CatchUnhandled(() => _threadContext.RunOnThread(() => _store.AddModel(model), true)); + + /// Operations must run on the main thread for ETABS and SAP 2000 + public void UpdateModel(ModelCard model) => + _topLevelExceptionHandler.CatchUnhandled(() => _threadContext.RunOnThread(() => _store.UpdateModel(model), true)); + + /// Operations must run on the main thread for ETABS and SAP 2000 + public void RemoveModel(ModelCard model) => + _topLevelExceptionHandler.CatchUnhandled(() => _threadContext.RunOnThread(() => _store.RemoveModel(model), true)); + + public Task HighlightModel(string modelCardId) => Task.CompletedTask; + + public Task HighlightObjects(IReadOnlyList objectIds) => Task.CompletedTask; +} diff --git a/Connectors/CSi/Speckle.Connectors.CSiShared/Bindings/CsiSharedSelectionBinding.cs b/Connectors/CSi/Speckle.Connectors.CSiShared/Bindings/CsiSharedSelectionBinding.cs new file mode 100644 index 000000000..bb3592e44 --- /dev/null +++ b/Connectors/CSi/Speckle.Connectors.CSiShared/Bindings/CsiSharedSelectionBinding.cs @@ -0,0 +1,108 @@ +using Speckle.Connectors.Common.Threading; +using Speckle.Connectors.CSiShared.HostApp; +using Speckle.Connectors.CSiShared.Utils; +using Speckle.Connectors.DUI.Bindings; +using Speckle.Connectors.DUI.Bridge; +using Speckle.Converters.CSiShared.Utils; +using Timer = System.Timers.Timer; + +namespace Speckle.Connectors.CSiShared.Bindings; + +public class CsiSharedSelectionBinding : ISelectionBinding, IDisposable +{ + private bool _disposed; + private readonly Timer _selectionTimer; + private readonly ICsiApplicationService _csiApplicationService; + private readonly IThreadContext _threadContext; + private HashSet _lastSelection = new(); + private readonly ITopLevelExceptionHandler _topLevelExceptionHandler; + + public IBrowserBridge Parent { get; } + public string Name => "selectionBinding"; + + public CsiSharedSelectionBinding( + IBrowserBridge parent, + ICsiApplicationService csiApplicationService, + ITopLevelExceptionHandler topLevelExceptionHandler, + IThreadContext threadContext + ) + { + _threadContext = threadContext; + Parent = parent; + _csiApplicationService = csiApplicationService; + _topLevelExceptionHandler = topLevelExceptionHandler; + + _selectionTimer = new Timer(1000); + _selectionTimer.Elapsed += (_, _) => + _topLevelExceptionHandler.CatchUnhandled(() => _threadContext.RunOnMain(CheckSelectionChanged)); + _selectionTimer.Start(); + } + + private void CheckSelectionChanged() + { + // timer callbacks are on a background thread, but CSI API calls must be on main thread + var currentSelection = GetSelection(); + var currentIds = new HashSet(currentSelection.SelectedObjectIds); + + if (!_lastSelection.SetEquals(currentIds)) + { + _lastSelection = currentIds; + // ensure UI updates also run on main thread + _threadContext.RunOnMain( + () => + _topLevelExceptionHandler.CatchUnhandled( + () => Parent.Send(SelectionBindingEvents.SET_SELECTION, currentSelection) + ) + ); + } + } + + protected virtual void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing) + { + _selectionTimer?.Dispose(); + } + _disposed = true; + } + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Gets the selection and creates an encoded ID (objectType and objectName). + /// + /// + /// Refer to ObjectIdentifier.cs for more info. + /// + public SelectionInfo GetSelection() + { + int numberItems = 0; + int[] objectType = []; + string[] objectName = []; + + _csiApplicationService.SapModel.SelectObj.GetSelected(ref numberItems, ref objectType, ref objectName); + + var encodedIds = new List(numberItems); + var typeCounts = new Dictionary(); + for (int i = 0; i < numberItems; i++) + { + var typeKey = (ModelObjectType)objectType[i]; + var typeName = typeKey.ToString(); + encodedIds.Add(ObjectIdentifier.Encode(objectType[i], objectName[i])); + typeCounts[typeName] = (typeCounts.TryGetValue(typeName, out var count) ? count : 0) + 1; // NOTE: Cross-framework compatibility (net 48 and net8) + } + var summary = + encodedIds.Count == 0 + ? "No objects selected." + : $"{encodedIds.Count} objects ({string.Join(", ", + typeCounts.Select(kv => $"{kv.Value} {kv.Key}"))})"; + return new SelectionInfo(encodedIds, summary); + } +} diff --git a/Connectors/CSi/Speckle.Connectors.CSiShared/Bindings/CsiSharedSendBinding.cs b/Connectors/CSi/Speckle.Connectors.CSiShared/Bindings/CsiSharedSendBinding.cs new file mode 100644 index 000000000..b3cfb41ab --- /dev/null +++ b/Connectors/CSi/Speckle.Connectors.CSiShared/Bindings/CsiSharedSendBinding.cs @@ -0,0 +1,132 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Speckle.Connectors.Common.Cancellation; +using Speckle.Connectors.Common.Operations; +using Speckle.Connectors.CSiShared.HostApp; +using Speckle.Connectors.CSiShared.Utils; +using Speckle.Connectors.DUI.Bindings; +using Speckle.Connectors.DUI.Bridge; +using Speckle.Connectors.DUI.Exceptions; +using Speckle.Connectors.DUI.Logging; +using Speckle.Connectors.DUI.Models; +using Speckle.Connectors.DUI.Models.Card; +using Speckle.Connectors.DUI.Models.Card.SendFilter; +using Speckle.Connectors.DUI.Settings; +using Speckle.Converters.Common; +using Speckle.Converters.CSiShared; +using Speckle.Sdk; +using Speckle.Sdk.Common; +using Speckle.Sdk.Logging; + +namespace Speckle.Connectors.CSiShared.Bindings; + +public sealed class CsiSharedSendBinding : ISendBinding +{ + public string Name => "sendBinding"; + public SendBindingUICommands Commands { get; } + public IBrowserBridge Parent { get; } + + private readonly DocumentModelStore _store; + private readonly IServiceProvider _serviceProvider; + private readonly List _sendFilters; + private readonly ICancellationManager _cancellationManager; + private readonly IOperationProgressManager _operationProgressManager; + private readonly ILogger _logger; + private readonly ICsiApplicationService _csiApplicationService; + private readonly ICsiConversionSettingsFactory _csiConversionSettingsFactory; + private readonly ISpeckleApplication _speckleApplication; + private readonly ISdkActivityFactory _activityFactory; + + public CsiSharedSendBinding( + DocumentModelStore store, + IBrowserBridge parent, + IEnumerable sendFilters, + IServiceProvider serviceProvider, + ICancellationManager cancellationManager, + IOperationProgressManager operationProgressManager, + ILogger logger, + ICsiConversionSettingsFactory csiConversionSettingsFactory, + ISpeckleApplication speckleApplication, + ISdkActivityFactory activityFactory, + ICsiApplicationService csiApplicationService + ) + { + _store = store; + _serviceProvider = serviceProvider; + _sendFilters = sendFilters.ToList(); + _cancellationManager = cancellationManager; + _operationProgressManager = operationProgressManager; + _logger = logger; + Parent = parent; + Commands = new SendBindingUICommands(parent); + _csiConversionSettingsFactory = csiConversionSettingsFactory; + _speckleApplication = speckleApplication; + _activityFactory = activityFactory; + _csiApplicationService = csiApplicationService; + } + + public List GetSendFilters() => _sendFilters; + + public List GetSendSettings() => []; + + public async Task Send(string modelCardId) + { + using var activity = _activityFactory.Start(); + + try + { + if (_store.GetModelById(modelCardId) is not SenderModelCard modelCard) + { + throw new InvalidOperationException("No publish model card was found."); + } + using var scope = _serviceProvider.CreateScope(); + scope + .ServiceProvider.GetRequiredService>() + .Initialize(_csiConversionSettingsFactory.Create(_csiApplicationService.SapModel)); + + using var cancellationItem = _cancellationManager.GetCancellationItem(modelCardId); + + List wrappers = modelCard + .SendFilter.NotNull() + .RefreshObjectIds() + .Select(DecodeObjectIdentifier) + .ToList(); + + if (wrappers.Count == 0) + { + throw new SpeckleSendFilterException("No objects were found to convert. Please update your publish filter!"); + } + + var sendResult = await scope + .ServiceProvider.GetRequiredService>() + .Execute( + wrappers, + modelCard.GetSendInfo(_speckleApplication.ApplicationAndVersion), + _operationProgressManager.CreateOperationProgressEventHandler(Parent, modelCardId, cancellationItem.Token), + cancellationItem.Token + ); + + await Commands.SetModelSendResult(modelCardId, sendResult.RootObjId, sendResult.ConversionResults); + } + catch (OperationCanceledException) + { + return; + } + catch (Exception ex) when (!ex.IsFatal()) + { + _logger.LogModelCardHandledError(ex); + await Commands.SetModelError(modelCardId, ex); + } + } + + private ICsiWrapper DecodeObjectIdentifier(string encodedId) + { + var (type, name) = ObjectIdentifier.Decode(encodedId); + return CsiWrapperFactory.Create(type, name); + } + + public void CancelSend(string modelCardId) + { + _cancellationManager.CancelOperation(modelCardId); + } +} diff --git a/Connectors/CSi/Speckle.Connectors.CSiShared/Filters/CsiSharedSelectionFilter.cs b/Connectors/CSi/Speckle.Connectors.CSiShared/Filters/CsiSharedSelectionFilter.cs new file mode 100644 index 000000000..2d26785ef --- /dev/null +++ b/Connectors/CSi/Speckle.Connectors.CSiShared/Filters/CsiSharedSelectionFilter.cs @@ -0,0 +1,13 @@ +using Speckle.Connectors.DUI.Models.Card.SendFilter; + +namespace Speckle.Connectors.CSiShared.Filters; + +public class CsiSharedSelectionFilter : DirectSelectionSendFilter +{ + public CsiSharedSelectionFilter() + { + IsDefault = true; + } + + public override List RefreshObjectIds() => SelectedObjectIds; +} diff --git a/Connectors/CSi/Speckle.Connectors.CSiShared/GlobalUsing.cs b/Connectors/CSi/Speckle.Connectors.CSiShared/GlobalUsing.cs new file mode 100644 index 000000000..2209f341b --- /dev/null +++ b/Connectors/CSi/Speckle.Connectors.CSiShared/GlobalUsing.cs @@ -0,0 +1 @@ +global using CSiAPIv1; diff --git a/Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/CsiApplicationService.cs b/Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/CsiApplicationService.cs new file mode 100644 index 000000000..fe2fba816 --- /dev/null +++ b/Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/CsiApplicationService.cs @@ -0,0 +1,16 @@ +namespace Speckle.Connectors.CSiShared.HostApp; + +/// +/// Create a centralized access point for ETABS and SAP APIs across the entire program. +/// +/// +/// All API methods are based on the objectType and objectName, not the GUID. +/// CSi is already giving us the "sapModel" reference through the plugin interface. No need to attach to running instance. +/// Since objectType is a single int (1, 2 ... 7) we know first index will always be the objectType. +/// Prevent having to pass the "sapModel" around between classes and this ensures consistent access. +/// Name "sapModel" is misleading since it doesn't only apply to SAP2000, but this is the convention in the API, so we keep it. +/// +public interface ICsiApplicationService +{ + cSapModel SapModel { get; } +} diff --git a/Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/CsiDocumentModelStore.cs b/Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/CsiDocumentModelStore.cs new file mode 100644 index 000000000..d17d80a75 --- /dev/null +++ b/Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/CsiDocumentModelStore.cs @@ -0,0 +1,156 @@ +using System.IO; +using Microsoft.Extensions.Logging; +using Speckle.Connectors.Common.Threading; +using Speckle.Connectors.DUI.Bridge; +using Speckle.Connectors.DUI.Models; +using Speckle.Connectors.DUI.Utils; +using Speckle.Sdk; +using Speckle.Sdk.Helpers; +using Speckle.Sdk.Logging; +using Timer = System.Timers.Timer; + +namespace Speckle.Connectors.CSiShared.HostApp; + +public class CsiDocumentModelStore : DocumentModelStore, IDisposable +{ + private readonly ISpeckleApplication _speckleApplication; + private readonly ILogger _logger; + private readonly ICsiApplicationService _csiApplicationService; + private readonly ITopLevelExceptionHandler _topLevelExceptionHandler; + private readonly IThreadContext _threadContext; + private readonly Timer _modelCheckTimer; + private string _lastModelFilename = string.Empty; + private bool _disposed; + private string HostAppUserDataPath { get; set; } + private string DocumentStateFile { get; set; } + private string ModelPathHash { get; set; } + + public CsiDocumentModelStore( + IJsonSerializer jsonSerializer, + ISpeckleApplication speckleApplication, + ILogger logger, + ICsiApplicationService csiApplicationService, + ITopLevelExceptionHandler topLevelExceptionHandler, + IThreadContext threadContext + ) + : base(jsonSerializer) + { + _threadContext = threadContext; + _speckleApplication = speckleApplication; + _logger = logger; + _csiApplicationService = csiApplicationService; + _topLevelExceptionHandler = topLevelExceptionHandler; + + // initialize timer to check for model changes + _modelCheckTimer = new Timer(1000); + + // timer runs on background thread but model checks must be on main thread + _modelCheckTimer.Elapsed += (_, _) => + _topLevelExceptionHandler.CatchUnhandled(() => _threadContext.RunOnMain(CheckModelChanges)); + _modelCheckTimer.Start(); + } + + private void CheckModelChanges() + { + string currentFilename = _csiApplicationService.SapModel.GetModelFilename(); + + if (string.IsNullOrEmpty(currentFilename) || currentFilename == _lastModelFilename) + { + return; + } + + _lastModelFilename = currentFilename; + SetPaths(); + LoadState(); + OnDocumentChanged(); + } + + public override Task OnDocumentStoreInitialized() + { + var currentFilename = _csiApplicationService.SapModel.GetModelFilename(); + if (!string.IsNullOrEmpty(currentFilename)) + { + _lastModelFilename = currentFilename; + SetPaths(); + LoadState(); + } + return Task.CompletedTask; + } + + private void SetPaths() + { + try + { + ModelPathHash = Crypt.Md5(_csiApplicationService.SapModel.GetModelFilename(), length: 32); + HostAppUserDataPath = Path.Combine( + SpecklePathProvider.UserSpeckleFolderPath, + "ConnectorsFileData", + _speckleApplication.Slug + ); + DocumentStateFile = Path.Combine(HostAppUserDataPath, $"{ModelPathHash}.json"); + _logger.LogDebug($"Paths set - Hash: {ModelPathHash}, File: {DocumentStateFile}"); + } + catch (Exception ex) when (!ex.IsFatal()) + { + _logger.LogError(ex, "Error in setting paths for CsiDocumentModelStore"); + } + } + + protected override void HostAppSaveState(string modelCardState) + { + try + { + if (!Directory.Exists(HostAppUserDataPath)) + { + Directory.CreateDirectory(HostAppUserDataPath); + } + + File.WriteAllText(DocumentStateFile, modelCardState); + } + catch (Exception ex) when (!ex.IsFatal()) + { + _logger.LogError(ex, "Failed to save state"); + } + } + + protected override void LoadState() + { + try + { + if (!File.Exists(DocumentStateFile)) + { + ClearAndSave(); + return; + } + + string serializedState = File.ReadAllText(DocumentStateFile); + LoadFromString(serializedState); + } + catch (Exception ex) when (!ex.IsFatal()) + { + _logger.LogError(ex, "Failed to load state, initializing empty state"); + ClearAndSave(); + } + } + + protected virtual void Dispose(bool disposing) + { + if (_disposed) + { + return; + } + + if (disposing) + { + _modelCheckTimer.Dispose(); + } + + _disposed = true; + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } +} diff --git a/Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/CsiSendCollectionManager.cs b/Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/CsiSendCollectionManager.cs new file mode 100644 index 000000000..cb25ca67c --- /dev/null +++ b/Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/CsiSendCollectionManager.cs @@ -0,0 +1,43 @@ +using Speckle.Converters.Common; +using Speckle.Converters.CSiShared; +using Speckle.Sdk.Models; +using Speckle.Sdk.Models.Collections; + +namespace Speckle.Connectors.CSiShared.HostApp; + +/// +/// We can use the CSiWrappers to create our collection structure. +/// +/// +/// This class manages the collections. If the key (from the path) already exists, this collection is returned. +/// If it doesn't exist, a new collection is created and added to the rootObject. +/// +public class CsiSendCollectionManager +{ + protected IConverterSettingsStore ConverterSettings { get; } + protected Dictionary CollectionCache { get; } = new(); + + public CsiSendCollectionManager(IConverterSettingsStore converterSettings) + { + ConverterSettings = converterSettings; + } + + public virtual Collection AddObjectCollectionToRoot(Base convertedObject, Collection rootObject) + { + var path = GetCollectionPath(convertedObject); + + if (CollectionCache.TryGetValue(path, out Collection? collection)) + { + return collection; + } + + Collection childCollection = CreateCollection(convertedObject); + rootObject.elements.Add(childCollection); + CollectionCache[path] = childCollection; + return childCollection; + } + + protected virtual string GetCollectionPath(Base convertedObject) => convertedObject["type"]?.ToString() ?? "Unknown"; + + protected virtual Collection CreateCollection(Base convertedObject) => new(GetCollectionPath(convertedObject)); +} diff --git a/Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/Helpers/CsiFrameSectionPropertyExtractor.cs b/Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/Helpers/CsiFrameSectionPropertyExtractor.cs new file mode 100644 index 000000000..0750f116b --- /dev/null +++ b/Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/Helpers/CsiFrameSectionPropertyExtractor.cs @@ -0,0 +1,115 @@ +using Speckle.Converters.Common; +using Speckle.Converters.CSiShared; +using Speckle.Converters.CSiShared.Utils; + +namespace Speckle.Connectors.CSiShared.HostApp.Helpers; + +/// +/// Base frame section property extractor for CSi products. +/// +/// +/// Handles common Csi API calls for frame section properties +/// Provides foundation for application-specific extractors. +/// +public class CsiFrameSectionPropertyExtractor : IFrameSectionPropertyExtractor +{ + private readonly IConverterSettingsStore _settingsStore; + + public CsiFrameSectionPropertyExtractor(IConverterSettingsStore settingsStore) + { + _settingsStore = settingsStore; + } + + public void ExtractProperties(string sectionName, Dictionary properties) + { + GetMaterialName(sectionName, properties); + GetSectionProperties(sectionName, properties); + GetPropertyModifiers(sectionName, properties); + } + + private void GetMaterialName(string sectionName, Dictionary properties) + { + // get material name + string materialName = string.Empty; + _settingsStore.Current.SapModel.PropFrame.GetMaterial(sectionName, ref materialName); + + // append to General Data of properties dictionary + Dictionary generalData = properties.EnsureNested(SectionPropertyCategory.GENERAL_DATA); + generalData["Material"] = materialName; + } + + private void GetSectionProperties(string sectionName, Dictionary properties) + { + double crossSectionalArea = 0, + shearAreaInMajorAxisDirection = 0, + shearAreaInMinorAxisDirection = 0, + torsionalConstant = 0, + momentOfInertiaAboutMajorAxis = 0, + momentOfInertiaAboutMinorAxis = 0, + sectionModulusAboutMajorAxis = 0, + sectionModulusAboutMinorAxis = 0, + plasticModulusAboutMajorAxis = 0, + plasticModulusAboutMinorAxis = 0, + radiusOfGyrationAboutMajorAxis = 0, + radiusOfGyrationAboutMinorAxis = 0; + + _settingsStore.Current.SapModel.PropFrame.GetSectProps( + sectionName, + ref crossSectionalArea, + ref shearAreaInMajorAxisDirection, + ref shearAreaInMinorAxisDirection, + ref torsionalConstant, + ref momentOfInertiaAboutMajorAxis, + ref momentOfInertiaAboutMinorAxis, + ref sectionModulusAboutMajorAxis, + ref sectionModulusAboutMinorAxis, + ref plasticModulusAboutMajorAxis, + ref plasticModulusAboutMinorAxis, + ref radiusOfGyrationAboutMajorAxis, + ref radiusOfGyrationAboutMinorAxis + ); + + string distanceUnit = _settingsStore.Current.SpeckleUnits; + string areaUnit = $"{distanceUnit}²"; // // TODO: Formalize this better + string modulusUnit = $"{distanceUnit}³"; // // TODO: Formalize this better + string inertiaUnit = $"{distanceUnit}\u2074"; // TODO: Formalize this better + + Dictionary mechanicalProperties = properties.EnsureNested( + SectionPropertyCategory.SECTION_PROPERTIES + ); + mechanicalProperties.AddWithUnits("Area", crossSectionalArea, areaUnit); + mechanicalProperties.AddWithUnits("As2", shearAreaInMajorAxisDirection, areaUnit); + mechanicalProperties.AddWithUnits("As3", shearAreaInMinorAxisDirection, areaUnit); + mechanicalProperties.AddWithUnits("J", torsionalConstant, inertiaUnit); + mechanicalProperties.AddWithUnits("I22", momentOfInertiaAboutMajorAxis, inertiaUnit); + mechanicalProperties.AddWithUnits("I33", momentOfInertiaAboutMinorAxis, inertiaUnit); + mechanicalProperties.AddWithUnits("S22", sectionModulusAboutMajorAxis, modulusUnit); + mechanicalProperties.AddWithUnits("S33", sectionModulusAboutMinorAxis, modulusUnit); + mechanicalProperties.AddWithUnits("Z22", plasticModulusAboutMajorAxis, modulusUnit); + mechanicalProperties.AddWithUnits("Z33", plasticModulusAboutMinorAxis, modulusUnit); + mechanicalProperties.AddWithUnits("R22", radiusOfGyrationAboutMajorAxis, distanceUnit); + mechanicalProperties.AddWithUnits("R33", radiusOfGyrationAboutMinorAxis, distanceUnit); + } + + private void GetPropertyModifiers(string sectionName, Dictionary properties) + { + double[] stiffnessModifiersArray = []; + _settingsStore.Current.SapModel.PropFrame.GetModifiers(sectionName, ref stiffnessModifiersArray); + + Dictionary modifiers = + new() + { + ["Cross-section (Axial) Area"] = stiffnessModifiersArray[0], + ["Shear Area in 2 Direction"] = stiffnessModifiersArray[1], + ["Shear Area in 3 Direction"] = stiffnessModifiersArray[2], + ["Torsional Constant"] = stiffnessModifiersArray[3], + ["Moment of Inertia about 2 Axis"] = stiffnessModifiersArray[4], + ["Moment of Inertia about 3 Axis"] = stiffnessModifiersArray[5], + ["Mass"] = stiffnessModifiersArray[6], + ["Weight"] = stiffnessModifiersArray[7], + }; + + Dictionary generalData = properties.EnsureNested(SectionPropertyCategory.GENERAL_DATA); + generalData["Modifiers"] = modifiers; + } +} diff --git a/Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/Helpers/CsiMaterialPropertyExtractor.cs b/Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/Helpers/CsiMaterialPropertyExtractor.cs new file mode 100644 index 000000000..279ba1b17 --- /dev/null +++ b/Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/Helpers/CsiMaterialPropertyExtractor.cs @@ -0,0 +1,212 @@ +using Speckle.Converters.Common; +using Speckle.Converters.CSiShared; +using Speckle.Converters.CSiShared.Utils; + +namespace Speckle.Connectors.CSiShared.HostApp.Helpers; + +/// +/// Base material property extractor for CSi products. +/// +/// +/// Currently, all material property extraction can happen on a CsiShared level which simplifies things a lot. +/// Properties depend on the directional symmetry of the material, hence the switch statements. +/// +public class CsiMaterialPropertyExtractor +{ + /// + /// Property strings for all mechanical properties, used by numerous methods. + /// + private static class MechanicalPropertyNames + { + public const string MODULUS_OF_ELASTICITY = "Modulus of Elasticity, E"; + public const string MODULUS_OF_ELASTICITY_ARRAY = "Modulus of Elasticity Array, E"; + public const string POISSON_RATIO = "Poisson's Ratio, U"; + public const string POISSON_RATIO_ARRAY = "Poisson's Ratio Array, U"; + public const string THERMAL_COEFFICIENT = "Coefficient of Thermal Expansion, A"; + public const string THERMAL_COEFFICIENT_ARRAY = "Coefficient of Thermal Expansion Array, A"; + public const string SHEAR_MODULUS = "Shear Modulus, G"; + public const string SHEAR_MODULUS_ARRAY = "Shear Modulus Array, G"; + } + + private readonly IConverterSettingsStore _settingsStore; + + public CsiMaterialPropertyExtractor(IConverterSettingsStore settingsStore) + { + _settingsStore = settingsStore; + } + + public void ExtractProperties(string materialName, Dictionary properties) + { + GetGeneralProperties(materialName, properties); + GetWeightAndMassProperties(materialName, properties); // TODO: Add units + GetMechanicalProperties(materialName, properties); // TODO: Add units + } + + private void GetGeneralProperties(string materialName, Dictionary properties) + { + { + eMatType materialType = default; + int materialColor = 0; + string materialNotes = string.Empty; + string materialGuid = string.Empty; + + _settingsStore.Current.SapModel.PropMaterial.GetMaterial( + materialName, + ref materialType, + ref materialColor, + ref materialNotes, + ref materialGuid + ); + + var generalData = properties.EnsureNested(SectionPropertyCategory.GENERAL_DATA); + generalData["Name"] = materialName; + generalData["Type"] = materialType.ToString(); + generalData["Notes"] = materialNotes; + } + } + + private void GetWeightAndMassProperties(string materialName, Dictionary properties) + { + double weightPerUnitVolume = double.NaN; + double massPerUnitVolume = double.NaN; + + _settingsStore.Current.SapModel.PropMaterial.GetWeightAndMass( + materialName, + ref weightPerUnitVolume, + ref massPerUnitVolume + ); + + var weightAndMass = properties.EnsureNested("Weight and Mass"); + weightAndMass["Weight per Unit Volume"] = weightPerUnitVolume; + weightAndMass["Mass per Unit Volume"] = massPerUnitVolume; + } + + private void GetMechanicalProperties(string materialName, Dictionary properties) + { + int materialDirectionalSymmetryKey = 0; + eMatType materialType = default; + + _settingsStore.Current.SapModel.PropMaterial.GetTypeOAPI( + materialName, + ref materialType, + ref materialDirectionalSymmetryKey + ); + + var materialDirectionalSymmetryValue = materialDirectionalSymmetryKey switch + { + 0 => DirectionalSymmetryType.ISOTROPIC, + 1 => DirectionalSymmetryType.ORTHOTROPIC, + 2 => DirectionalSymmetryType.ANISOTROPIC, + 3 => DirectionalSymmetryType.UNIAXIAL, + _ => throw new ArgumentException($"Unknown symmetry type: {materialDirectionalSymmetryKey}") + }; + + var mechanicalProperties = properties.EnsureNested("Mechanical Properties"); + mechanicalProperties["Directional Symmetry Type"] = materialDirectionalSymmetryValue.ToString(); + + GetMechanicalPropertiesByType(materialName, materialDirectionalSymmetryValue, mechanicalProperties); + } + + private void GetMechanicalPropertiesByType( + string materialName, + DirectionalSymmetryType directionalSymmetryType, + Dictionary mechanicalProperties + ) + { + switch (directionalSymmetryType) + { + case DirectionalSymmetryType.ISOTROPIC: + ExtractIsotropicProperties(materialName, mechanicalProperties); + break; + case DirectionalSymmetryType.ORTHOTROPIC: + ExtractOrthotropicProperties(materialName, mechanicalProperties); + break; + case DirectionalSymmetryType.ANISOTROPIC: + ExtractAnisotropicProperties(materialName, mechanicalProperties); + break; + case DirectionalSymmetryType.UNIAXIAL: + ExtractUniaxialProperties(materialName, mechanicalProperties); + break; + default: + throw new ArgumentException($"Unknown directional symmetry type: {directionalSymmetryType}"); + } + } + + private void ExtractIsotropicProperties(string materialName, Dictionary mechanicalProperties) + { + double modulusOfElasticity = double.NaN; + double poissonRatio = double.NaN; + double thermalCoefficient = double.NaN; + double shearModulus = double.NaN; + + _settingsStore.Current.SapModel.PropMaterial.GetMPIsotropic( + materialName, + ref modulusOfElasticity, + ref poissonRatio, + ref thermalCoefficient, + ref shearModulus + ); + + mechanicalProperties[MechanicalPropertyNames.MODULUS_OF_ELASTICITY] = modulusOfElasticity; + mechanicalProperties[MechanicalPropertyNames.POISSON_RATIO] = poissonRatio; + mechanicalProperties[MechanicalPropertyNames.THERMAL_COEFFICIENT] = thermalCoefficient; + mechanicalProperties[MechanicalPropertyNames.SHEAR_MODULUS] = shearModulus; + } + + private void ExtractOrthotropicProperties(string materialName, Dictionary mechanicalProperties) + { + double[] modulusOfElasticityArray = []; + double[] poissonRatioArray = []; + double[] thermalCoefficientArray = []; + double[] shearModulusArray = []; + + _settingsStore.Current.SapModel.PropMaterial.GetMPOrthotropic( + materialName, + ref modulusOfElasticityArray, + ref poissonRatioArray, + ref thermalCoefficientArray, + ref shearModulusArray + ); + + mechanicalProperties[MechanicalPropertyNames.MODULUS_OF_ELASTICITY_ARRAY] = modulusOfElasticityArray; + mechanicalProperties[MechanicalPropertyNames.POISSON_RATIO_ARRAY] = poissonRatioArray; + mechanicalProperties[MechanicalPropertyNames.THERMAL_COEFFICIENT_ARRAY] = thermalCoefficientArray; + mechanicalProperties[MechanicalPropertyNames.SHEAR_MODULUS_ARRAY] = shearModulusArray; + } + + private void ExtractAnisotropicProperties(string materialName, Dictionary mechanicalProperties) + { + double[] modulusOfElasticityArray = []; + double[] poissonRatioArray = []; + double[] thermalCoefficientArray = []; + double[] shearModulusArray = []; + + _settingsStore.Current.SapModel.PropMaterial.GetMPAnisotropic( + materialName, + ref modulusOfElasticityArray, + ref poissonRatioArray, + ref thermalCoefficientArray, + ref shearModulusArray + ); + + mechanicalProperties[MechanicalPropertyNames.MODULUS_OF_ELASTICITY_ARRAY] = modulusOfElasticityArray; + mechanicalProperties[MechanicalPropertyNames.POISSON_RATIO_ARRAY] = poissonRatioArray; + mechanicalProperties[MechanicalPropertyNames.THERMAL_COEFFICIENT_ARRAY] = thermalCoefficientArray; + mechanicalProperties[MechanicalPropertyNames.SHEAR_MODULUS_ARRAY] = shearModulusArray; + } + + private void ExtractUniaxialProperties(string materialName, Dictionary mechanicalProperties) + { + double modulusOfElasticity = double.NaN; + double thermalCoefficient = double.NaN; + + _settingsStore.Current.SapModel.PropMaterial.GetMPUniaxial( + materialName, + ref modulusOfElasticity, + ref thermalCoefficient + ); + + mechanicalProperties[MechanicalPropertyNames.MODULUS_OF_ELASTICITY] = modulusOfElasticity; + mechanicalProperties[MechanicalPropertyNames.THERMAL_COEFFICIENT] = thermalCoefficient; + } +} diff --git a/Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/Helpers/CsiShellSectionPropertyExtractor.cs b/Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/Helpers/CsiShellSectionPropertyExtractor.cs new file mode 100644 index 000000000..6d2d82bee --- /dev/null +++ b/Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/Helpers/CsiShellSectionPropertyExtractor.cs @@ -0,0 +1,68 @@ +using Speckle.Converters.Common; +using Speckle.Converters.CSiShared; +using Speckle.Converters.CSiShared.Utils; + +namespace Speckle.Connectors.CSiShared.HostApp.Helpers; + +/// +/// Base shell section property extractor for CSi products. +/// +/// +/// Handles common Csi API calls for shell section properties. +/// Provides foundation for application-specific extractors. +/// +public class CsiShellSectionPropertyExtractor : IShellSectionPropertyExtractor +{ + private readonly IConverterSettingsStore _settingsStore; + + public CsiShellSectionPropertyExtractor(IConverterSettingsStore settingsStore) + { + _settingsStore = settingsStore; + } + + public void ExtractProperties(string sectionName, Dictionary properties) + { + GetPropertyType(sectionName, properties); + GetPropertyModifiers(sectionName, properties); + } + + private void GetPropertyType(string sectionName, Dictionary properties) + { + int propertyTypeKey = 1; + _settingsStore.Current.SapModel.PropArea.GetTypeOAPI(sectionName, ref propertyTypeKey); + var propertyTypeValue = propertyTypeKey switch + { + 1 => AreaPropertyType.SHELL, + 2 => AreaPropertyType.PLANE, + 3 => AreaPropertyType.ASOLID, + _ => throw new ArgumentException($"Unknown property type: {propertyTypeKey}"), + }; + + var generalData = properties.EnsureNested(SectionPropertyCategory.GENERAL_DATA); + generalData["Property Type"] = propertyTypeValue.ToString(); + } + + private void GetPropertyModifiers(string sectionName, Dictionary properties) + { + double[] stiffnessModifiersArray = []; + _settingsStore.Current.SapModel.PropArea.GetModifiers(sectionName, ref stiffnessModifiersArray); + + Dictionary modifiers = + new() + { + ["Membrane f11 Direction"] = stiffnessModifiersArray[0], + ["Membrane f22 Direction"] = stiffnessModifiersArray[1], + ["Membrane f12 Direction"] = stiffnessModifiersArray[2], + ["Bending m11 Direction"] = stiffnessModifiersArray[3], + ["Bending m22 Direction"] = stiffnessModifiersArray[3], + ["Bending m12 Direction"] = stiffnessModifiersArray[4], + ["Shear v13 Direction"] = stiffnessModifiersArray[5], + ["Shear v23 Direction"] = stiffnessModifiersArray[6], + ["Mass"] = stiffnessModifiersArray[7], + ["Weight"] = stiffnessModifiersArray[8] + }; + + var generalData = properties.EnsureNested(SectionPropertyCategory.GENERAL_DATA); + generalData["Modifiers"] = modifiers; + } +} diff --git a/Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/Helpers/IApplicationSectionPropertyExtractor.cs b/Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/Helpers/IApplicationSectionPropertyExtractor.cs new file mode 100644 index 000000000..e16d3b3ea --- /dev/null +++ b/Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/Helpers/IApplicationSectionPropertyExtractor.cs @@ -0,0 +1,18 @@ +namespace Speckle.Connectors.CSiShared.HostApp.Helpers; + +/// +/// Contract for host application specific section property extraction. +/// +/// +/// Mirrors property extraction system pattern by composing with base extractor. +/// Enables both shared and application-specific property extraction in one call. +/// +public interface IApplicationSectionPropertyExtractor +{ + void ExtractProperties(string sectionName, Dictionary properties); +} + +// NOTE: Seemingly silly, but allows us to register the correct extractor for the correct type. +public interface IApplicationFrameSectionPropertyExtractor : IApplicationSectionPropertyExtractor { } + +public interface IApplicationShellSectionPropertyExtractor : IApplicationSectionPropertyExtractor { } diff --git a/Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/Helpers/ISectionPropertyExtractor.cs b/Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/Helpers/ISectionPropertyExtractor.cs new file mode 100644 index 000000000..5f401cdd1 --- /dev/null +++ b/Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/Helpers/ISectionPropertyExtractor.cs @@ -0,0 +1,14 @@ +namespace Speckle.Connectors.CSiShared.HostApp.Helpers; + +/// +/// Core contract for section property extraction common across CSi products. +/// +public interface ISectionPropertyExtractor +{ + void ExtractProperties(string sectionName, Dictionary properties); +} + +// NOTE: Seemingly silly, but allows us to register the correct extractor for the correct type. +public interface IFrameSectionPropertyExtractor : ISectionPropertyExtractor { } + +public interface IShellSectionPropertyExtractor : ISectionPropertyExtractor { } diff --git a/Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/Helpers/ISectionUnpacker.cs b/Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/Helpers/ISectionUnpacker.cs new file mode 100644 index 000000000..cee5215b5 --- /dev/null +++ b/Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/Helpers/ISectionUnpacker.cs @@ -0,0 +1,10 @@ +using Speckle.Sdk.Models.Proxies; + +namespace Speckle.Connectors.CSiShared.HostApp.Helpers; + +// NOTE: Interface because Etabs and Sap2000 section unpacking and extraction is different. +// At ServiceRegistration, we inject the correct implementation of the ISectionUnpacker +public interface ISectionUnpacker +{ + IEnumerable UnpackSections(); +} diff --git a/Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/MaterialUnpacker.cs b/Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/MaterialUnpacker.cs new file mode 100644 index 000000000..1de645a85 --- /dev/null +++ b/Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/MaterialUnpacker.cs @@ -0,0 +1,51 @@ +using Speckle.Connectors.CSiShared.HostApp.Helpers; +using Speckle.Converters.CSiShared.ToSpeckle.Helpers; +using Speckle.Sdk.Models.Proxies; + +namespace Speckle.Connectors.CSiShared.HostApp; + +/// +/// Creates material proxies based on stored entries from the materials cache +/// +public class MaterialUnpacker +{ + private readonly CsiMaterialPropertyExtractor _propertyExtractor; + private readonly CsiToSpeckleCacheSingleton _csiToSpeckleCacheSingleton; + + public MaterialUnpacker( + CsiMaterialPropertyExtractor propertyExtractor, + CsiToSpeckleCacheSingleton csiToSpeckleCacheSingleton + ) + { + _propertyExtractor = propertyExtractor; + _csiToSpeckleCacheSingleton = csiToSpeckleCacheSingleton; + } + + // Creates a list of material proxies from the csi materials cache + public IEnumerable UnpackMaterials() + { + foreach (var kvp in _csiToSpeckleCacheSingleton.MaterialCache) + { + // get the cached entry + string materialName = kvp.Key; + List sectionIds = kvp.Value; + + // get the properties of the material + Dictionary properties = new(); // create empty dictionary + _propertyExtractor.ExtractProperties(materialName, properties); // dictionary mutated with respective properties + + // create the material proxy + GroupProxy materialProxy = + new() + { + id = materialName, + name = materialName, + applicationId = materialName, + objects = sectionIds, + ["properties"] = properties + }; + + yield return materialProxy; + } + } +} diff --git a/Connectors/CSi/Speckle.Connectors.CSiShared/Operations/Send/CsiRootObjectBuilder.cs b/Connectors/CSi/Speckle.Connectors.CSiShared/Operations/Send/CsiRootObjectBuilder.cs new file mode 100644 index 000000000..c27845fb6 --- /dev/null +++ b/Connectors/CSi/Speckle.Connectors.CSiShared/Operations/Send/CsiRootObjectBuilder.cs @@ -0,0 +1,163 @@ +using Microsoft.Extensions.Logging; +using Speckle.Connectors.Common.Builders; +using Speckle.Connectors.Common.Conversion; +using Speckle.Connectors.Common.Operations; +using Speckle.Connectors.CSiShared.HostApp; +using Speckle.Connectors.CSiShared.HostApp.Helpers; +using Speckle.Converters.Common; +using Speckle.Converters.CSiShared; +using Speckle.Converters.CSiShared.Extensions; +using Speckle.Sdk; +using Speckle.Sdk.Logging; +using Speckle.Sdk.Models; +using Speckle.Sdk.Models.Collections; + +namespace Speckle.Connectors.CSiShared.Builders; + +/// +/// Manages the conversion of CSi model objects and establishes proxy-based relationships. +/// +/// +/// Core responsibilities: +/// - Converts ICsiWrappers to Speckle objects through caching-aware conversion +/// - Creates proxy objects for materials and sections from model data +/// - Establishes relationships between objects and their dependencies +/// +/// The builder follows a two-phase process: +/// 1. Conversion Phase: ICsiWrappers → Speckle objects with cached results handling +/// 2. Relationship Phase: Material/section proxy creation and relationship mapping +/// +public class CsiRootObjectBuilder : IRootObjectBuilder +{ + private readonly IRootToSpeckleConverter _rootToSpeckleConverter; + private readonly IConverterSettingsStore _converterSettings; + private readonly CsiSendCollectionManager _sendCollectionManager; + private readonly MaterialUnpacker _materialUnpacker; + private readonly ISectionUnpacker _sectionUnpacker; + private readonly ILogger _logger; + private readonly ISdkActivityFactory _activityFactory; + private readonly ICsiApplicationService _csiApplicationService; + + public CsiRootObjectBuilder( + IRootToSpeckleConverter rootToSpeckleConverter, + IConverterSettingsStore converterSettings, + CsiSendCollectionManager sendCollectionManager, + MaterialUnpacker materialUnpacker, + ISectionUnpacker sectionUnpacker, + ILogger logger, + ISdkActivityFactory activityFactory, + ICsiApplicationService csiApplicationService + ) + { + _converterSettings = converterSettings; + _sendCollectionManager = sendCollectionManager; + _materialUnpacker = materialUnpacker; + _sectionUnpacker = sectionUnpacker; + _rootToSpeckleConverter = rootToSpeckleConverter; + _logger = logger; + _activityFactory = activityFactory; + _csiApplicationService = csiApplicationService; + } + + /// + /// Converts Csi objects into a Speckle-compatible object hierarchy with established relationships. + /// + /// + /// Operation sequence: + /// 1. Creates root collection with model metadata + /// 2. Converts each object with caching and progress tracking + /// 3. Creates proxies for materials and sections + /// + public async Task Build( + IReadOnlyList csiObjects, + SendInfo sendInfo, + IProgress onOperationProgressed, + CancellationToken cancellationToken + ) + { + using var activity = _activityFactory.Start("Build"); + + string modelFileName = _csiApplicationService.SapModel.GetModelFilename(false) ?? "Unnamed model"; + Collection rootObjectCollection = + new() { name = modelFileName, ["units"] = _converterSettings.Current.SpeckleUnits }; + + List results = new(csiObjects.Count); + int count = 0; + + using (var _ = _activityFactory.Start("Convert all")) + { + foreach (ICsiWrapper csiObject in csiObjects) + { + cancellationToken.ThrowIfCancellationRequested(); + using var _2 = _activityFactory.Start("Convert"); + + var result = ConvertCsiObject(csiObject, rootObjectCollection); + results.Add(result); + + count++; + onOperationProgressed.Report(new("Converting", (double)count / csiObjects.Count)); + await Task.Yield(); + } + } + + if (results.All(x => x.Status == Status.ERROR)) + { + throw new SpeckleException("Failed to convert all objects."); + } + + using (var _ = _activityFactory.Start("Process Proxies")) + { + // Create and add material proxies + rootObjectCollection[ProxyKeys.MATERIAL] = _materialUnpacker.UnpackMaterials().ToList(); + + // Create and all section proxies (frame and shell) + rootObjectCollection[ProxyKeys.SECTION] = _sectionUnpacker.UnpackSections().ToList(); + } + + return new RootObjectBuilderResult(rootObjectCollection, results); + } + + /// + /// Converts a single Csi wrapper "object" to a data object with appropriate collection management. + /// + private SendConversionResult ConvertCsiObject(ICsiWrapper csiObject, Collection typeCollection) + { + string sourceType = csiObject.ObjectName; + string applicationId = csiObject switch + { + CsiJointWrapper jointWrapper => jointWrapper.GetSpeckleApplicationId(_csiApplicationService.SapModel), + CsiFrameWrapper frameWrapper => frameWrapper.GetSpeckleApplicationId(_csiApplicationService.SapModel), + CsiCableWrapper cableWrapper => cableWrapper.GetSpeckleApplicationId(_csiApplicationService.SapModel), + CsiTendonWrapper tendonWrapper => tendonWrapper.ObjectName, // No GetGUID method in the Csi API available + CsiShellWrapper shellWrapper => shellWrapper.GetSpeckleApplicationId(_csiApplicationService.SapModel), + CsiSolidWrapper solidWrapper => solidWrapper.GetSpeckleApplicationId(_csiApplicationService.SapModel), + CsiLinkWrapper linkWrapper => linkWrapper.GetSpeckleApplicationId(_csiApplicationService.SapModel), + _ => throw new ArgumentException($"Unsupported wrapper type: {csiObject.GetType()}", nameof(csiObject)) + }; + + try + { + Base converted = _rootToSpeckleConverter.Convert(csiObject); + + var collection = _sendCollectionManager.AddObjectCollectionToRoot(converted, typeCollection); + collection.elements.Add(converted); + + return new(Status.SUCCESS, applicationId, sourceType, converted); + } + // Expected not implemented: + // TODO: SAP 2000: CsiCableWrapper, CsiSolidWrapper + // TODO: ETABS: CsiLinkWrapper, CsiTendonWrapper + // NOTE: CsiLinkWrapper - not important to data extraction workflow + // NOTE: CsiTendonWrapper - not typically modelled in ETABS, rather SAFE + catch (NotImplementedException ex) + { + _logger.LogError(ex, sourceType); + return new(Status.WARNING, applicationId, sourceType, null, ex); + } + catch (Exception ex) when (!ex.IsFatal()) + { + _logger.LogError(ex, sourceType); + return new(Status.ERROR, applicationId, sourceType, null, ex); + } + } +} diff --git a/Connectors/CSi/Speckle.Connectors.CSiShared/Plugin/CsiPluginBase.cs b/Connectors/CSi/Speckle.Connectors.CSiShared/Plugin/CsiPluginBase.cs new file mode 100644 index 000000000..eb7c6df1e --- /dev/null +++ b/Connectors/CSi/Speckle.Connectors.CSiShared/Plugin/CsiPluginBase.cs @@ -0,0 +1,57 @@ +namespace Speckle.Connectors.CSiShared; + +[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "")] +public abstract class CSiPluginBase : cPluginContract, IDisposable +{ + private const string s_modality = "Non-Modal"; + private SpeckleFormBase? _panel; + private bool _disposed; + + public void Main(ref cSapModel sapModel, ref cPluginCallback pluginCallback) + { + _panel = CreateForm(); + _panel.Initialize(ref sapModel, ref pluginCallback); + _panel.FormClosed += (_, _) => Dispose(); + + if (string.Equals(s_modality, "Non-Modal", StringComparison.OrdinalIgnoreCase)) + { + _panel.Show(); + } + else + { + _panel.ShowDialog(); + } + } + + protected abstract SpeckleFormBase CreateForm(); + + public virtual int Info(ref string text) + { + text = "Hey Speckler! This is our next-gen CSi Connector."; + return 0; + } + + protected virtual void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing) + { + _panel?.Dispose(); + _panel = null; + } + _disposed = true; + } + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + ~CSiPluginBase() + { + Dispose(false); + } +} diff --git a/Connectors/CSi/Speckle.Connectors.CSiShared/Plugin/SpeckleFormBase.cs b/Connectors/CSi/Speckle.Connectors.CSiShared/Plugin/SpeckleFormBase.cs new file mode 100644 index 000000000..f3b01b9c6 --- /dev/null +++ b/Connectors/CSi/Speckle.Connectors.CSiShared/Plugin/SpeckleFormBase.cs @@ -0,0 +1,100 @@ +using System.ComponentModel; +using System.Reflection; +using System.Windows.Forms.Integration; +using Microsoft.Extensions.DependencyInjection; +using Speckle.Connectors.Common; +using Speckle.Connectors.CSiShared.HostApp; +using Speckle.Connectors.DUI; +using Speckle.Connectors.DUI.WebView; +using Speckle.Converters.CSiShared; +using Speckle.Sdk.Host; + +namespace Speckle.Connectors.CSiShared; + +[DesignerCategory("")] +public abstract class SpeckleFormBase : Form, ICsiApplicationService +{ + private ElementHost Host { get; set; } + private cPluginCallback _pluginCallback; + private bool _disposed; +#pragma warning disable CA2213 + private ServiceProvider _container; +#pragma warning restore CA2213 + + protected SpeckleFormBase() + { + Text = "Speckle (Beta)"; + Size = new System.Drawing.Size(400, 600); + } + + public cSapModel SapModel { get; private set; } + + protected virtual void ConfigureServices(IServiceCollection services) + { + services.Initialize(GetHostApplication(), GetVersion()); + services.AddCsi(); + services.AddCsiConverters(); + } + + protected abstract HostApplication GetHostApplication(); + + protected abstract HostAppVersion GetVersion(); + + public void Initialize(ref cSapModel sapModel, ref cPluginCallback pluginCallback) + { + // store app-specific model and callback references (callback if at all possible?) + SapModel = sapModel; + _pluginCallback = pluginCallback; + + string assemblyName = + Assembly.GetExecutingAssembly().GetName().Name + ?? throw new InvalidOperationException("Could not determine executing assembly name"); + string resourcePath = $"{assemblyName}.Resources.et_element_Speckle.bmp"; + + // load and set the speckle icon from embedded resources + using (var stream = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream(resourcePath)) + { + if (stream == null) + { + throw new InvalidOperationException($"Could not find resource: {resourcePath}"); + } + + using var bmp = new Bitmap(stream); + Icon = Icon.FromHandle(bmp.GetHicon()); + } + + // configure dependency injection services + var services = new ServiceCollection(); + services.AddSingleton(this); + ConfigureServices(services); + + // build service container and initialize ui framework + _container = services.BuildServiceProvider(); + _container.UseDUI(); + + // setup webview control and form properties + var webview = _container.GetRequiredService(); + Host = new() { Child = webview, Dock = DockStyle.Fill }; + Controls.Add(Host); + FormBorderStyle = FormBorderStyle.Sizable; + // this.TopLevel = true; + // TODO: Get IntrPtr for Csi window + FormClosing += Form1Closing; + } + + private void Form1Closing(object? sender, FormClosingEventArgs e) => _pluginCallback.Finish(0); + + protected override void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing) + { + _container.Dispose(); + Host.Dispose(); + base.Dispose(disposing); + } + _disposed = true; + } + } +} diff --git a/Connectors/CSi/Speckle.Connectors.CSiShared/Resources/et_element_Speckle.bmp b/Connectors/CSi/Speckle.Connectors.CSiShared/Resources/et_element_Speckle.bmp new file mode 100644 index 000000000..36085a0d9 Binary files /dev/null and b/Connectors/CSi/Speckle.Connectors.CSiShared/Resources/et_element_Speckle.bmp differ diff --git a/Connectors/CSi/Speckle.Connectors.CSiShared/ServiceRegistration.cs b/Connectors/CSi/Speckle.Connectors.CSiShared/ServiceRegistration.cs new file mode 100644 index 000000000..41b60f2a2 --- /dev/null +++ b/Connectors/CSi/Speckle.Connectors.CSiShared/ServiceRegistration.cs @@ -0,0 +1,56 @@ +using Microsoft.Extensions.DependencyInjection; +using Speckle.Connectors.Common; +using Speckle.Connectors.Common.Builders; +using Speckle.Connectors.Common.Operations; +using Speckle.Connectors.Common.Threading; +using Speckle.Connectors.CSiShared.Bindings; +using Speckle.Connectors.CSiShared.Builders; +using Speckle.Connectors.CSiShared.Filters; +using Speckle.Connectors.CSiShared.HostApp; +using Speckle.Connectors.CSiShared.HostApp.Helpers; +using Speckle.Connectors.DUI; +using Speckle.Connectors.DUI.Bindings; +using Speckle.Connectors.DUI.Bridge; +using Speckle.Connectors.DUI.Models.Card.SendFilter; +using Speckle.Connectors.DUI.WebView; +using Speckle.Converters.CSiShared; +using Speckle.Converters.CSiShared.ToSpeckle.Helpers; + +namespace Speckle.Connectors.CSiShared; + +public static class ServiceRegistration +{ + public static IServiceCollection AddCsi(this IServiceCollection services) + { + services.AddSingleton(); + + services.AddConnectorUtils(); + services.AddDUI(); + services.AddDUIView(); + + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + + services.AddSingleton(sp => sp.GetRequiredService()); + services.AddSingleton(); + + services.AddSingleton(); + services.AddSingleton(); + + services.AddScoped(); + services.AddScoped(); + services.AddScoped, CsiRootObjectBuilder>(); + services.AddScoped>(); + + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + + // add converter caches + services.AddScoped(); + + return services; + } +} diff --git a/Connectors/CSi/Speckle.Connectors.CSiShared/Speckle.Connectors.CSiShared.projitems b/Connectors/CSi/Speckle.Connectors.CSiShared/Speckle.Connectors.CSiShared.projitems new file mode 100644 index 000000000..ebb05f5e1 --- /dev/null +++ b/Connectors/CSi/Speckle.Connectors.CSiShared/Speckle.Connectors.CSiShared.projitems @@ -0,0 +1,41 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + a8e949b8-aa55-4909-99f0-8b551791a1f8 + + + Speckle.Connectors.CSiShared + + + + Always + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Connectors/CSi/Speckle.Connectors.CSiShared/Speckle.Connectors.CSiShared.shproj b/Connectors/CSi/Speckle.Connectors.CSiShared/Speckle.Connectors.CSiShared.shproj new file mode 100644 index 000000000..b8a544863 --- /dev/null +++ b/Connectors/CSi/Speckle.Connectors.CSiShared/Speckle.Connectors.CSiShared.shproj @@ -0,0 +1,14 @@ + + + + a8e949b8-aa55-4909-99f0-8b551791a1f8 + 14.0 + + + + + + + + + diff --git a/Connectors/CSi/Speckle.Connectors.CSiShared/Utils/ObjectIdentifiers.cs b/Connectors/CSi/Speckle.Connectors.CSiShared/Utils/ObjectIdentifiers.cs new file mode 100644 index 000000000..63122f594 --- /dev/null +++ b/Connectors/CSi/Speckle.Connectors.CSiShared/Utils/ObjectIdentifiers.cs @@ -0,0 +1,35 @@ +namespace Speckle.Connectors.CSiShared.Utils; + +/// +/// ObjectIdentifier based on concatenating the objectType and objectName. CSi is annoying, we can't use GUIDs. +/// +/// +/// All API methods are based on the objectType and objectName, not the GUID. +/// We will obviously manage the GUIDs but for all method calls we need a concatenated version of the objectType and objectName. +/// Since objectType is a single int (1, 2 ... 7) we know first index will always be the objectType. +/// This int gets used by the CsiWrapperFactory to create the CSiWrappers. +/// +public static class ObjectIdentifier +{ + public static string Encode(int objectType, string objectName) + { + if (objectType < 1 || objectType > 7) // Both ETABS and SAP2000 APIs have the same returns for objectType + { + throw new ArgumentException($"Invalid object type: {objectType}. Must be between 1 and 7."); + } + return $"{objectType}{objectName}"; + } + + public static (int type, string name) Decode(string encodedId) + { + if (string.IsNullOrEmpty(encodedId) || encodedId.Length < 2) // Superfluous. But rather safe than sorry + { + throw new ArgumentException($"Invalid encoded ID: {encodedId}"); + } + + int objectType = int.Parse(encodedId[0].ToString()); + string objectName = encodedId[1..]; + + return (objectType, objectName); + } +} diff --git a/Connectors/CSi/Speckle.Connectors.ETABS21/Plugin/SpeckleForm.cs b/Connectors/CSi/Speckle.Connectors.ETABS21/Plugin/SpeckleForm.cs new file mode 100644 index 000000000..349144d0c --- /dev/null +++ b/Connectors/CSi/Speckle.Connectors.ETABS21/Plugin/SpeckleForm.cs @@ -0,0 +1,12 @@ +using Speckle.Connectors.ETABSShared; +using Speckle.Sdk.Host; + +// NOTE: Plugin entry point must match the assembly name, otherwise ETABS hits you with a "Not found" error when loading plugin +// Disabling error below to prioritize DUI3 project structure. Name of cPlugin class cannot be changed +#pragma warning disable IDE0130 +namespace Speckle.Connectors.ETABS21; + +public class SpeckleForm : EtabsSpeckleFormBase +{ + protected override HostAppVersion GetVersion() => HostAppVersion.v21; +} diff --git a/Connectors/CSi/Speckle.Connectors.ETABS21/Plugin/cPlugin.cs b/Connectors/CSi/Speckle.Connectors.ETABS21/Plugin/cPlugin.cs new file mode 100644 index 000000000..ec042fead --- /dev/null +++ b/Connectors/CSi/Speckle.Connectors.ETABS21/Plugin/cPlugin.cs @@ -0,0 +1,12 @@ +using Speckle.Connectors.ETABSShared; + +// NOTE: Plugin entry point must match the assembly name, otherwise ETABS hits you with a "Not found" error when loading plugin +// Disabling error below to prioritize DUI3 project structure. Name of cPlugin class cannot be changed +#pragma warning disable IDE0130 +namespace Speckle.Connectors.ETABS21; + +[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "")] +public class cPlugin : EtabsPluginBase +{ + protected override EtabsSpeckleFormBase CreateEtabsForm() => new SpeckleForm(); +} diff --git a/Connectors/CSi/Speckle.Connectors.ETABS21/Properties/launchSettings.json b/Connectors/CSi/Speckle.Connectors.ETABS21/Properties/launchSettings.json new file mode 100644 index 000000000..9bb0d7513 --- /dev/null +++ b/Connectors/CSi/Speckle.Connectors.ETABS21/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "ETABS 21": { + "commandName": "Executable", + "executablePath": "C:\\Program Files\\Computers and Structures\\ETABS 21\\ETABS.exe" + } + } +} diff --git a/Connectors/CSi/Speckle.Connectors.ETABS21/Speckle.Connectors.ETABS21.csproj b/Connectors/CSi/Speckle.Connectors.ETABS21/Speckle.Connectors.ETABS21.csproj new file mode 100644 index 000000000..4cbe5c3f2 --- /dev/null +++ b/Connectors/CSi/Speckle.Connectors.ETABS21/Speckle.Connectors.ETABS21.csproj @@ -0,0 +1,33 @@ + + + + net48 + AnyCPU + true + true + 21 + $(DefineConstants);ETABS21 + true + Debug;Release;Local + + + + + + + + + + + + + + + + + + + + + + diff --git a/Connectors/CSi/Speckle.Connectors.ETABS21/packages.lock.json b/Connectors/CSi/Speckle.Connectors.ETABS21/packages.lock.json new file mode 100644 index 000000000..a4fce9244 --- /dev/null +++ b/Connectors/CSi/Speckle.Connectors.ETABS21/packages.lock.json @@ -0,0 +1,372 @@ +{ + "version": 2, + "dependencies": { + ".NETFramework,Version=v4.8": { + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net48": "1.0.3" + } + }, + "Microsoft.SourceLink.GitHub": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "dependencies": { + "Microsoft.Build.Tasks.Git": "8.0.0", + "Microsoft.SourceLink.Common": "8.0.0" + } + }, + "PolySharp": { + "type": "Direct", + "requested": "[1.14.1, )", + "resolved": "1.14.1", + "contentHash": "mOOmFYwad3MIOL14VCjj02LljyF1GNw1wP0YVlxtcPvqdxjGGMNdNJJxHptlry3MOd8b40Flm8RPOM8JOlN2sQ==" + }, + "Speckle.CSI.API": { + "type": "Direct", + "requested": "[1.30.0, )", + "resolved": "1.30.0", + "contentHash": "4S5Udr+YDU43YgB+TXgnPtGioRj1hDnucHlr42ikr72h1yQwzmkC2HwWJibjZD+sOrAke67q1N8geIqJj9Ss4Q==" + }, + "Speckle.InterfaceGenerator": { + "type": "Direct", + "requested": "[0.9.6, )", + "resolved": "0.9.6", + "contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w==" + }, + "GraphQL.Client": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "8yPNBbuVBpTptivyAlak4GZvbwbUcjeQTL4vN1HKHRuOykZ4r7l5fcLS6vpyPyLn0x8FsL31xbOIKyxbmR9rbA==", + "dependencies": { + "GraphQL.Client.Abstractions": "6.0.0", + "GraphQL.Client.Abstractions.Websocket": "6.0.0", + "System.Net.WebSockets.Client.Managed": "1.0.22", + "System.Reactive": "5.0.0" + } + }, + "GraphQL.Client.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "h7uzWFORHZ+CCjwr/ThAyXMr0DPpzEANDa4Uo54wqCQ+j7qUKwqYTgOrb1W40sqbvNaZm9v/X7It31SUw0maHA==", + "dependencies": { + "GraphQL.Primitives": "6.0.0" + } + }, + "GraphQL.Client.Abstractions.Websocket": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "Nr9bPf8gIOvLuXpqEpqr9z9jslYFJOvd0feHth3/kPqeR3uMbjF5pjiwh4jxyMcxHdr8Pb6QiXkV3hsSyt0v7A==", + "dependencies": { + "GraphQL.Client.Abstractions": "6.0.0" + } + }, + "GraphQL.Primitives": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "yg72rrYDapfsIUrul7aF6wwNnTJBOFvuA9VdDTQpPa8AlAriHbufeXYLBcodKjfUdkCnaiggX1U/nEP08Zb5GA==" + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "W8DPQjkMScOMTtJbPwmPyj9c3zYSFGawDW3jwlBOOsnY+EzZFLgNQ/UMkK35JmkNOVPdCyPr2Tw7Vv9N+KA3ZQ==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.CSharp": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==" + }, + "Microsoft.Data.Sqlite": { + "type": "Transitive", + "resolved": "7.0.5", + "contentHash": "KGxbPeWsQMnmQy43DSBxAFtHz3l2JX8EWBSGUCvT3CuZ8KsuzbkqMIJMDOxWtG8eZSoCDI04aiVQjWuuV8HmSw==", + "dependencies": { + "Microsoft.Data.Sqlite.Core": "7.0.5", + "SQLitePCLRaw.bundle_e_sqlite3": "2.1.4" + } + }, + "Microsoft.Data.Sqlite.Core": { + "type": "Transitive", + "resolved": "7.0.5", + "contentHash": "FTerRmQPqHrCrnoUzhBu+E+1DNGwyrAMLqHkAqOOOu5pGfyMOj8qQUBxI/gDtWtG11p49UxSfWmBzRNlwZqfUg==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "nOP8R1mVb/6mZtm2qgAJXn/LFm/2kMjHDAg/QJLFG6CuWYJtaD3p1BwQhufBVvRzL9ceJ/xF0SQ0qsI2GkDQAA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "65MrmXCziWaQFrI0UHkQbesrX5wTwf9XPjY5yFm/VkgJKFJ5gqvXRoXjIZcf2wLi5ZlwGz/oMYfyURVCWbM5iw==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "vJ9xvOZCnUAIHcGC3SU35r3HKmHTVIeHzo6u/qzlHAqD8m6xv92MLin4oJntTvkpKxVX3vI1GFFkIQtU3AdlsQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "2.2.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw==" + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "UpZLNLBpIZ0GTebShui7xXYh6DmBHjWM8NxGxZbdQh/bPZ5e6YswqI+bru6BnEL5eWiOdodsXtEz3FROcgi/qg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Primitives": "2.2.0", + "System.ComponentModel.Annotations": "4.5.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "azyQtqbm4fSaDzZHD/J+V6oWMFaf2tWP4WEGIYePLCMw3+b2RQdj9ybgbQyjCshcitQKQ4lEDOZjmSlTTrHxUg==", + "dependencies": { + "System.Memory": "4.5.1", + "System.Runtime.CompilerServices.Unsafe": "4.5.1" + } + }, + "Microsoft.NETFramework.ReferenceAssemblies.net48": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "zMk4D+9zyiEWByyQ7oPImPN/Jhpj166Ky0Nlla4eXlNL8hI/BtSJsgR8Inldd4NNpIAH3oh8yym0W2DrhXdSLQ==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "Speckle.Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.2", + "contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA==" + }, + "SQLitePCLRaw.bundle_e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "EWI1olKDjFEBMJu0+3wuxwziIAdWDVMYLhuZ3Qs84rrz+DHwD00RzWPZCa+bLnHCf3oJwuFZIRsHT5p236QXww==", + "dependencies": { + "SQLitePCLRaw.lib.e_sqlite3": "2.1.4", + "SQLitePCLRaw.provider.dynamic_cdecl": "2.1.4" + } + }, + "SQLitePCLRaw.core": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "inBjvSHo9UDKneGNzfUfDjK08JzlcIhn1+SP5Y3m6cgXpCxXKCJDy6Mka7LpgSV+UZmKSnC8rTwB0SQ0xKu5pA==", + "dependencies": { + "System.Memory": "4.5.3" + } + }, + "SQLitePCLRaw.lib.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "2C9Q9eX7CPLveJA0rIhf9RXAvu+7nWZu1A2MdG6SD/NOu26TakGgL1nsbc0JAspGijFOo3HoN79xrx8a368fBg==" + }, + "SQLitePCLRaw.provider.dynamic_cdecl": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "ZsaKKhgYF9B1fvcnOGKl3EycNAwd9CRWX7v0rEfuPWhQQ5Jjpvf2VEHahiLIGHio3hxi3EIKFJw9KvyowWOUAw==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "System.Buffers": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "AwarXzzoDwX6BgrhjoJsk6tUezZEozOT5Y9QKF94Gl4JK91I4PIIBkBco9068Y9/Dra8Dkbie99kXB8+1BaYKw==" + }, + "System.ComponentModel.Annotations": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "UxYQ3FGUOtzJ7LfSdnYSFd7+oEv6M8NgUatatIN2HxNtDdlcvFAf+VIq4Of9cDMJEJC0aSRv/x898RYhB4Yppg==" + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.3", + "contentHash": "3oDzvc/zzetpTKWMShs1AADwZjQ/36HnsufHRPcOjyRAAMLDlu2iD33MBI2opxnezcVUtXyqDXXjoFMOU9c7SA==", + "dependencies": { + "System.Buffers": "4.4.0", + "System.Numerics.Vectors": "4.4.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.2" + } + }, + "System.Net.WebSockets.Client.Managed": { + "type": "Transitive", + "resolved": "1.0.22", + "contentHash": "WqEOxPlXjuZrIjUtXNE9NxEfU/n5E35iV2PtoZdJSUC4tlrqwHnTee+wvMIM4OUaJWmwrymeqcgYrE0IkGAgLA==", + "dependencies": { + "System.Buffers": "4.4.0", + "System.Numerics.Vectors": "4.4.0" + } + }, + "System.Numerics.Vectors": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ==" + }, + "System.Reactive": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "4.5.3", + "contentHash": "3TIsJhD1EiiT0w2CcDMN/iSSwnNnsrnbzeVHSKkaEgV85txMprmuO+Yq2AdSbeVGcg28pdNDTPK87tJhX7VFHw==" + }, + "System.Threading.Tasks.Extensions": { + "type": "Transitive", + "resolved": "4.5.4", + "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + }, + "speckle.connectors.common": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", + "Speckle.Connectors.Logging": "[1.0.0, )", + "Speckle.Objects": "[3.1.1, )", + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" + } + }, + "speckle.connectors.dui": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", + "Speckle.Connectors.Common": "[1.0.0, )", + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" + } + }, + "speckle.connectors.dui.webview": { + "type": "Project", + "dependencies": { + "Microsoft.Web.WebView2": "[1.0.1938.49, )", + "Speckle.Connectors.DUI": "[1.0.0, )" + } + }, + "speckle.connectors.logging": { + "type": "Project" + }, + "speckle.converters.common": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", + "Speckle.Objects": "[3.1.1, )" + } + }, + "speckle.converters.etabs21": { + "type": "Project", + "dependencies": { + "Speckle.Converters.Common": "[1.0.0, )" + } + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "MZtBIwfDFork5vfjpJdG5g8wuJFt7d/y3LOSVVtDK/76wlbtz6cjltfKHqLx2TKVqTj5/c41t77m1+h20zqtPA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0" + } + }, + "Microsoft.Extensions.Logging": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "Nxqhadc9FCmFHzU+fz3oc8sFlE6IadViYg8dfUdGzJZ2JUxnCsRghBhhOWdM4B2zSZqEc+0BjliBh/oNdRZuig==", + "dependencies": { + "Microsoft.Extensions.Configuration.Binder": "2.2.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging.Abstractions": "2.2.0", + "Microsoft.Extensions.Options": "2.2.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" + }, + "Microsoft.Web.WebView2": { + "type": "CentralTransitive", + "requested": "[1.0.1938.49, )", + "resolved": "1.0.1938.49", + "contentHash": "z8KnFnaTYzhA/ZnyRX0qGfS1NU5ZBJeClAH64F0fVDvdDJTvME7xl6zTJ0Jlfe1BtL3C0NH9xTy64shg2baKdw==" + }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, + "Speckle.Objects": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", + "dependencies": { + "Speckle.Sdk": "3.1.1" + } + }, + "Speckle.Sdk": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", + "dependencies": { + "GraphQL.Client": "6.0.0", + "Microsoft.Bcl.AsyncInterfaces": "5.0.0", + "Microsoft.CSharp": "4.7.0", + "Microsoft.Data.Sqlite": "7.0.5", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging": "2.2.0", + "Speckle.DoubleNumerics": "4.1.0", + "Speckle.Newtonsoft.Json": "13.0.2", + "Speckle.Sdk.Dependencies": "3.1.1" + } + }, + "Speckle.Sdk.Dependencies": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" + } + } + } +} \ No newline at end of file diff --git a/Connectors/CSi/Speckle.Connectors.ETABS22/Plugin/SpeckleForm.cs b/Connectors/CSi/Speckle.Connectors.ETABS22/Plugin/SpeckleForm.cs new file mode 100644 index 000000000..c25e9b5ca --- /dev/null +++ b/Connectors/CSi/Speckle.Connectors.ETABS22/Plugin/SpeckleForm.cs @@ -0,0 +1,12 @@ +using Speckle.Connectors.ETABSShared; +using Speckle.Sdk.Host; + +// NOTE: Plugin entry point must match the assembly name, otherwise ETABS hits you with a "Not found" error when loading plugin +// Disabling error below to prioritize DUI3 project structure. Name of cPlugin class cannot be changed +#pragma warning disable IDE0130 +namespace Speckle.Connectors.ETABS22; + +public class SpeckleForm : EtabsSpeckleFormBase +{ + protected override HostAppVersion GetVersion() => HostAppVersion.v22; +} diff --git a/Connectors/CSi/Speckle.Connectors.ETABS22/Plugin/cPlugin.cs b/Connectors/CSi/Speckle.Connectors.ETABS22/Plugin/cPlugin.cs new file mode 100644 index 000000000..acc99c4ad --- /dev/null +++ b/Connectors/CSi/Speckle.Connectors.ETABS22/Plugin/cPlugin.cs @@ -0,0 +1,12 @@ +using Speckle.Connectors.ETABSShared; + +// NOTE: Plugin entry point must match the assembly name, otherwise ETABS hits you with a "Not found" error when loading plugin +// Disabling error below to prioritize DUI3 project structure. Name of cPlugin class cannot be changed +#pragma warning disable IDE0130 +namespace Speckle.Connectors.ETABS22; + +[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "")] +public class cPlugin : EtabsPluginBase +{ + protected override EtabsSpeckleFormBase CreateEtabsForm() => new SpeckleForm(); +} diff --git a/Connectors/CSi/Speckle.Connectors.ETABS22/Properties/launchSettings.json b/Connectors/CSi/Speckle.Connectors.ETABS22/Properties/launchSettings.json new file mode 100644 index 000000000..1fd348981 --- /dev/null +++ b/Connectors/CSi/Speckle.Connectors.ETABS22/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "ETABS 22": { + "commandName": "Executable", + "executablePath": "C:\\Program Files\\Computers and Structures\\ETABS 22\\ETABS.exe" + } + } +} diff --git a/Connectors/CSi/Speckle.Connectors.ETABS22/Speckle.Connectors.ETABS22.csproj b/Connectors/CSi/Speckle.Connectors.ETABS22/Speckle.Connectors.ETABS22.csproj new file mode 100644 index 000000000..03d9b4077 --- /dev/null +++ b/Connectors/CSi/Speckle.Connectors.ETABS22/Speckle.Connectors.ETABS22.csproj @@ -0,0 +1,32 @@ + + + + net8.0-windows + AnyCPU + true + true + 22 + $(DefineConstants);ETABS22;ETABS22_OR_GREATER + true + Debug;Release;Local + + + + + + + + + + + + + + + + + + + + + diff --git a/Connectors/CSi/Speckle.Connectors.ETABS22/packages.lock.json b/Connectors/CSi/Speckle.Connectors.ETABS22/packages.lock.json new file mode 100644 index 000000000..6ced4a78f --- /dev/null +++ b/Connectors/CSi/Speckle.Connectors.ETABS22/packages.lock.json @@ -0,0 +1,327 @@ +{ + "version": 2, + "dependencies": { + "net8.0-windows7.0": { + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net461": "1.0.3" + } + }, + "Microsoft.SourceLink.GitHub": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "dependencies": { + "Microsoft.Build.Tasks.Git": "8.0.0", + "Microsoft.SourceLink.Common": "8.0.0" + } + }, + "PolySharp": { + "type": "Direct", + "requested": "[1.14.1, )", + "resolved": "1.14.1", + "contentHash": "mOOmFYwad3MIOL14VCjj02LljyF1GNw1wP0YVlxtcPvqdxjGGMNdNJJxHptlry3MOd8b40Flm8RPOM8JOlN2sQ==" + }, + "Speckle.CSI.API": { + "type": "Direct", + "requested": "[2.4.0, )", + "resolved": "2.4.0", + "contentHash": "/n3qIBeamiYlWm77/2+dDPYExm/MoDEtnu5IPB2G9Dei06wMgkdBefaSDKWnh3u4iuyha6TvrBZgVGosUylRDg==" + }, + "Speckle.InterfaceGenerator": { + "type": "Direct", + "requested": "[0.9.6, )", + "resolved": "0.9.6", + "contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w==" + }, + "GraphQL.Client": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "8yPNBbuVBpTptivyAlak4GZvbwbUcjeQTL4vN1HKHRuOykZ4r7l5fcLS6vpyPyLn0x8FsL31xbOIKyxbmR9rbA==", + "dependencies": { + "GraphQL.Client.Abstractions": "6.0.0", + "GraphQL.Client.Abstractions.Websocket": "6.0.0", + "System.Reactive": "5.0.0" + } + }, + "GraphQL.Client.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "h7uzWFORHZ+CCjwr/ThAyXMr0DPpzEANDa4Uo54wqCQ+j7qUKwqYTgOrb1W40sqbvNaZm9v/X7It31SUw0maHA==", + "dependencies": { + "GraphQL.Primitives": "6.0.0" + } + }, + "GraphQL.Client.Abstractions.Websocket": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "Nr9bPf8gIOvLuXpqEpqr9z9jslYFJOvd0feHth3/kPqeR3uMbjF5pjiwh4jxyMcxHdr8Pb6QiXkV3hsSyt0v7A==", + "dependencies": { + "GraphQL.Client.Abstractions": "6.0.0" + } + }, + "GraphQL.Primitives": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "yg72rrYDapfsIUrul7aF6wwNnTJBOFvuA9VdDTQpPa8AlAriHbufeXYLBcodKjfUdkCnaiggX1U/nEP08Zb5GA==" + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.CSharp": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==" + }, + "Microsoft.Data.Sqlite": { + "type": "Transitive", + "resolved": "7.0.5", + "contentHash": "KGxbPeWsQMnmQy43DSBxAFtHz3l2JX8EWBSGUCvT3CuZ8KsuzbkqMIJMDOxWtG8eZSoCDI04aiVQjWuuV8HmSw==", + "dependencies": { + "Microsoft.Data.Sqlite.Core": "7.0.5", + "SQLitePCLRaw.bundle_e_sqlite3": "2.1.4" + } + }, + "Microsoft.Data.Sqlite.Core": { + "type": "Transitive", + "resolved": "7.0.5", + "contentHash": "FTerRmQPqHrCrnoUzhBu+E+1DNGwyrAMLqHkAqOOOu5pGfyMOj8qQUBxI/gDtWtG11p49UxSfWmBzRNlwZqfUg==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "nOP8R1mVb/6mZtm2qgAJXn/LFm/2kMjHDAg/QJLFG6CuWYJtaD3p1BwQhufBVvRzL9ceJ/xF0SQ0qsI2GkDQAA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "65MrmXCziWaQFrI0UHkQbesrX5wTwf9XPjY5yFm/VkgJKFJ5gqvXRoXjIZcf2wLi5ZlwGz/oMYfyURVCWbM5iw==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "vJ9xvOZCnUAIHcGC3SU35r3HKmHTVIeHzo6u/qzlHAqD8m6xv92MLin4oJntTvkpKxVX3vI1GFFkIQtU3AdlsQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "2.2.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw==" + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "UpZLNLBpIZ0GTebShui7xXYh6DmBHjWM8NxGxZbdQh/bPZ5e6YswqI+bru6BnEL5eWiOdodsXtEz3FROcgi/qg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Primitives": "2.2.0", + "System.ComponentModel.Annotations": "4.5.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "azyQtqbm4fSaDzZHD/J+V6oWMFaf2tWP4WEGIYePLCMw3+b2RQdj9ybgbQyjCshcitQKQ4lEDOZjmSlTTrHxUg==", + "dependencies": { + "System.Memory": "4.5.1", + "System.Runtime.CompilerServices.Unsafe": "4.5.1" + } + }, + "Microsoft.NETFramework.ReferenceAssemblies.net461": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "AmOJZwCqnOCNp6PPcf9joyogScWLtwy0M1WkqfEQ0M9nYwyDD7EX9ZjscKS5iYnyvteX7kzSKFCKt9I9dXA6mA==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "Speckle.Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.2", + "contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA==" + }, + "SQLitePCLRaw.bundle_e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "EWI1olKDjFEBMJu0+3wuxwziIAdWDVMYLhuZ3Qs84rrz+DHwD00RzWPZCa+bLnHCf3oJwuFZIRsHT5p236QXww==", + "dependencies": { + "SQLitePCLRaw.lib.e_sqlite3": "2.1.4", + "SQLitePCLRaw.provider.e_sqlite3": "2.1.4" + } + }, + "SQLitePCLRaw.core": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "inBjvSHo9UDKneGNzfUfDjK08JzlcIhn1+SP5Y3m6cgXpCxXKCJDy6Mka7LpgSV+UZmKSnC8rTwB0SQ0xKu5pA==", + "dependencies": { + "System.Memory": "4.5.3" + } + }, + "SQLitePCLRaw.lib.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "2C9Q9eX7CPLveJA0rIhf9RXAvu+7nWZu1A2MdG6SD/NOu26TakGgL1nsbc0JAspGijFOo3HoN79xrx8a368fBg==" + }, + "SQLitePCLRaw.provider.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "CSlb5dUp1FMIkez9Iv5EXzpeq7rHryVNqwJMWnpq87j9zWZexaEMdisDktMsnnrzKM6ahNrsTkjqNodTBPBxtQ==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "System.ComponentModel.Annotations": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "UxYQ3FGUOtzJ7LfSdnYSFd7+oEv6M8NgUatatIN2HxNtDdlcvFAf+VIq4Of9cDMJEJC0aSRv/x898RYhB4Yppg==" + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.3", + "contentHash": "3oDzvc/zzetpTKWMShs1AADwZjQ/36HnsufHRPcOjyRAAMLDlu2iD33MBI2opxnezcVUtXyqDXXjoFMOU9c7SA==" + }, + "System.Reactive": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==" + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Zh8t8oqolRaFa9vmOZfdQm/qKejdqz0J9kr7o2Fu0vPeoH3BL1EOXipKWwkWtLT1JPzjByrF19fGuFlNbmPpiw==" + }, + "speckle.connectors.common": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", + "Speckle.Connectors.Logging": "[1.0.0, )", + "Speckle.Objects": "[3.1.1, )", + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" + } + }, + "speckle.connectors.dui": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", + "Speckle.Connectors.Common": "[1.0.0, )", + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" + } + }, + "speckle.connectors.dui.webview": { + "type": "Project", + "dependencies": { + "Microsoft.Web.WebView2": "[1.0.1938.49, )", + "Speckle.Connectors.DUI": "[1.0.0, )" + } + }, + "speckle.connectors.logging": { + "type": "Project" + }, + "speckle.converters.common": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", + "Speckle.Objects": "[3.1.1, )" + } + }, + "speckle.converters.etabs22": { + "type": "Project", + "dependencies": { + "Speckle.Converters.Common": "[1.0.0, )" + } + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "MZtBIwfDFork5vfjpJdG5g8wuJFt7d/y3LOSVVtDK/76wlbtz6cjltfKHqLx2TKVqTj5/c41t77m1+h20zqtPA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0" + } + }, + "Microsoft.Extensions.Logging": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "Nxqhadc9FCmFHzU+fz3oc8sFlE6IadViYg8dfUdGzJZ2JUxnCsRghBhhOWdM4B2zSZqEc+0BjliBh/oNdRZuig==", + "dependencies": { + "Microsoft.Extensions.Configuration.Binder": "2.2.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging.Abstractions": "2.2.0", + "Microsoft.Extensions.Options": "2.2.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" + }, + "Microsoft.Web.WebView2": { + "type": "CentralTransitive", + "requested": "[1.0.1938.49, )", + "resolved": "1.0.1938.49", + "contentHash": "z8KnFnaTYzhA/ZnyRX0qGfS1NU5ZBJeClAH64F0fVDvdDJTvME7xl6zTJ0Jlfe1BtL3C0NH9xTy64shg2baKdw==" + }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, + "Speckle.Objects": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", + "dependencies": { + "Speckle.Sdk": "3.1.1" + } + }, + "Speckle.Sdk": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", + "dependencies": { + "GraphQL.Client": "6.0.0", + "Microsoft.CSharp": "4.7.0", + "Microsoft.Data.Sqlite": "7.0.5", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging": "2.2.0", + "Speckle.DoubleNumerics": "4.1.0", + "Speckle.Newtonsoft.Json": "13.0.2", + "Speckle.Sdk.Dependencies": "3.1.1" + } + }, + "Speckle.Sdk.Dependencies": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" + } + } + } +} \ No newline at end of file diff --git a/Connectors/CSi/Speckle.Connectors.ETABSShared/HostApp/EtabsSectionUnpacker.cs b/Connectors/CSi/Speckle.Connectors.ETABSShared/HostApp/EtabsSectionUnpacker.cs new file mode 100644 index 000000000..528974450 --- /dev/null +++ b/Connectors/CSi/Speckle.Connectors.ETABSShared/HostApp/EtabsSectionUnpacker.cs @@ -0,0 +1,110 @@ +using Speckle.Connectors.CSiShared.HostApp.Helpers; +using Speckle.Connectors.ETABSShared.HostApp.Helpers; +using Speckle.Converters.CSiShared.ToSpeckle.Helpers; +using Speckle.Sdk.Models.Proxies; + +namespace Speckle.Connectors.ETABSShared.HostApp; + +/// +/// Unpacks and creates proxies for frame and shell sections from the model. +/// +/// +/// Provides a unified approach to section extraction across different section types. +/// Leverages specialized extractors to handle complex property retrieval. Centralizes +/// section proxy creation with robust error handling and logging mechanisms. +/// +public class EtabsSectionUnpacker : ISectionUnpacker +{ + private readonly EtabsSectionPropertyExtractor _propertyExtractor; + private readonly CsiToSpeckleCacheSingleton _csiToSpeckleCacheSingleton; + + public EtabsSectionUnpacker( + EtabsSectionPropertyExtractor propertyExtractor, + CsiToSpeckleCacheSingleton csiToSpeckleCacheSingleton + ) + { + _propertyExtractor = propertyExtractor; + _csiToSpeckleCacheSingleton = csiToSpeckleCacheSingleton; + } + + public IEnumerable UnpackSections() + { + foreach (GroupProxy frameSectionProxy in UnpackFrameSections()) + { + yield return frameSectionProxy; + } + + foreach (GroupProxy shellSectionProxy in UnpackShellSections()) + { + yield return shellSectionProxy; + } + } + + private IEnumerable UnpackFrameSections() + { + foreach (var entry in _csiToSpeckleCacheSingleton.FrameSectionCache) + { + string sectionName = entry.Key; + List frameIds = entry.Value; + + // Initialize properties outside the if statement + Dictionary properties = new Dictionary(); + + // get the properties of the section + // openings will have objects assigned to them, but won't have properties + // sectionName is initialized with string.Empty, but api ref returns string "None" + if (sectionName != "None") + { + properties = _propertyExtractor.ExtractFrameSectionProperties(sectionName); + } + + // create the section proxy + GroupProxy sectionProxy = + new() + { + id = sectionName, + name = sectionName, + applicationId = sectionName, + objects = frameIds, + ["type"] = "Frame Section", // since sectionProxies are a flat list, need some way to distinguish from shell + ["properties"] = properties // openings will just have an empty dict here + }; + + yield return sectionProxy; + } + } + + private IEnumerable UnpackShellSections() + { + foreach (var entry in _csiToSpeckleCacheSingleton.ShellSectionCache) + { + string sectionName = entry.Key; + List frameIds = entry.Value; + + // Initialize properties outside the if statement + Dictionary properties = new Dictionary(); + + // get the properties of the section + // openings will have objects assigned to them, but won't have properties + // sectionName is initialized with string.Empty, but api ref returns string "None" + if (sectionName != "None") + { + properties = _propertyExtractor.ExtractShellSectionProperties(sectionName); + } + + // create the section proxy + GroupProxy sectionProxy = + new() + { + id = sectionName, + name = sectionName, + applicationId = sectionName, + objects = frameIds, + ["type"] = "Shell Section", // since sectionProxies are a flat list, need some way to distinguish from frame + ["properties"] = properties // openings will just have an empty dict here + }; + + yield return sectionProxy; + } + } +} diff --git a/Connectors/CSi/Speckle.Connectors.ETABSShared/HostApp/EtabsSendCollectionManager.cs b/Connectors/CSi/Speckle.Connectors.ETABSShared/HostApp/EtabsSendCollectionManager.cs new file mode 100644 index 000000000..8726d1cc5 --- /dev/null +++ b/Connectors/CSi/Speckle.Connectors.ETABSShared/HostApp/EtabsSendCollectionManager.cs @@ -0,0 +1,153 @@ +using Speckle.Connectors.CSiShared.HostApp; +using Speckle.Converters.Common; +using Speckle.Converters.CSiShared; +using Speckle.Converters.CSiShared.Utils; +using Speckle.Sdk.Models; +using Speckle.Sdk.Models.Collections; + +namespace Speckle.Connectors.ETABSShared.HostApp; + +/// +/// ETABS-specific collection manager that organizes structural elements by level and type. +/// Creates a hierarchical structure that mirrors ETABS' native organization. +/// +public class EtabsSendCollectionManager : CsiSendCollectionManager +{ + private const string DEFAULT_LEVEL = "Unassigned"; + + private readonly Dictionary _categoryNames = + new() + { + { ElementCategory.COLUMN, "Columns" }, + { ElementCategory.BEAM, "Beams" }, + { ElementCategory.BRACE, "Braces" }, + { ElementCategory.WALL, "Walls" }, + { ElementCategory.FLOOR, "Floors" }, + { ElementCategory.RAMP, "Ramps" }, + { ElementCategory.JOINT, "Joints" }, + { ElementCategory.OTHER, "Other" } + }; + + public EtabsSendCollectionManager(IConverterSettingsStore converterSettings) + : base(converterSettings) { } + + public override Collection AddObjectCollectionToRoot(Base convertedObject, Collection rootObject) + { + var level = GetObjectLevelFromObject(convertedObject); + var category = GetElementCategoryFromObject(convertedObject); + + return GetOrCreateCollectionHierarchy(level, category, rootObject); + } + + private string GetObjectLevelFromObject(Base obj) + { + // Properties from converter are stored in "Object ID" dictionary + // NOTE: Introduce enums for these object keys? I don't like string indexing. + if (obj["properties"] is not Dictionary properties) + { + return DEFAULT_LEVEL; + } + + if ( + properties.TryGetValue(ObjectPropertyCategory.OBJECT_ID, out var objectId) + && objectId is Dictionary parameters + ) + { + return parameters.TryGetValue(CommonObjectProperty.LEVEL, out var level) + ? level?.ToString() ?? DEFAULT_LEVEL + : DEFAULT_LEVEL; + } + + return DEFAULT_LEVEL; + } + + private ElementCategory GetElementCategoryFromObject(Base obj) + { + var type = obj["type"]?.ToString(); + + // Handle non-structural elements + if (string.IsNullOrEmpty(type)) + { + return ElementCategory.OTHER; + } + + // For frames and shells, get design orientation from Object ID + if ( + (type == ModelObjectType.FRAME.ToString() || type == ModelObjectType.SHELL.ToString()) + && obj["properties"] is Dictionary properties + ) + { + if ( + properties.TryGetValue(ObjectPropertyCategory.OBJECT_ID, out var objectId) + && objectId is Dictionary parameters + ) + { + if (parameters.TryGetValue(CommonObjectProperty.DESIGN_ORIENTATION, out var orientation)) + { + return GetCategoryFromDesignOrientation(orientation?.ToString(), type); + } + } + } + + // For joints, simply categorize as joints + return type == ModelObjectType.JOINT.ToString() ? ElementCategory.JOINT : ElementCategory.OTHER; + } + + private ElementCategory GetCategoryFromDesignOrientation(string? orientation, string type) + { + if (string.IsNullOrEmpty(orientation)) + { + return ElementCategory.OTHER; + } + + return (orientation, type) switch + { + ("Column", nameof(ModelObjectType.FRAME)) => ElementCategory.COLUMN, + ("Beam", nameof(ModelObjectType.FRAME)) => ElementCategory.BEAM, + ("Brace", nameof(ModelObjectType.FRAME)) => ElementCategory.BRACE, + ("Wall", nameof(ModelObjectType.SHELL)) => ElementCategory.WALL, + ("Floor", nameof(ModelObjectType.SHELL)) => ElementCategory.FLOOR, + ("Ramp", nameof(ModelObjectType.SHELL)) => ElementCategory.RAMP, + _ => ElementCategory.OTHER + }; + } + + private Collection GetOrCreateCollectionHierarchy(string level, ElementCategory category, Collection root) + { + var hierarchyKey = $"{level}_{category}"; + + if (CollectionCache.TryGetValue(hierarchyKey, out var existingCollection)) + { + return existingCollection; + } + + var levelCollection = GetOrCreateLevelCollection(level, root); + var categoryCollection = CreateCategoryCollection(category, levelCollection); + + CollectionCache[hierarchyKey] = categoryCollection; + return categoryCollection; + } + + private Collection GetOrCreateLevelCollection(string level, Collection root) + { + var levelKey = $"Level_{level}"; + + if (CollectionCache.TryGetValue(levelKey, out var existingCollection)) + { + return existingCollection; + } + + var levelCollection = new Collection(level); + root.elements.Add(levelCollection); + CollectionCache[levelKey] = levelCollection; + + return levelCollection; + } + + private Collection CreateCategoryCollection(ElementCategory category, Collection levelCollection) + { + var categoryCollection = new Collection(_categoryNames[category]); + levelCollection.elements.Add(categoryCollection); + return categoryCollection; + } +} diff --git a/Connectors/CSi/Speckle.Connectors.ETABSShared/HostApp/Helpers/EtabsFrameSectionPropertyExtractor.cs b/Connectors/CSi/Speckle.Connectors.ETABSShared/HostApp/Helpers/EtabsFrameSectionPropertyExtractor.cs new file mode 100644 index 000000000..97b9c9a91 --- /dev/null +++ b/Connectors/CSi/Speckle.Connectors.ETABSShared/HostApp/Helpers/EtabsFrameSectionPropertyExtractor.cs @@ -0,0 +1,76 @@ +using Speckle.Connectors.CSiShared.HostApp.Helpers; +using Speckle.Converters.Common; +using Speckle.Converters.CSiShared; +using Speckle.Converters.CSiShared.Utils; + +namespace Speckle.Connectors.ETABSShared.HostApp.Helpers; + +/// +/// Extracts ETABS-specific frame section properties. +/// +public class EtabsFrameSectionPropertyExtractor : IApplicationFrameSectionPropertyExtractor +{ + private readonly IConverterSettingsStore _settingsStore; + + public EtabsFrameSectionPropertyExtractor(IConverterSettingsStore settingsStore) + { + _settingsStore = settingsStore; + } + + /// + /// Gets generalised frame section properties + /// + /// + /// Sap2000 doesn't support this method, unfortunately + /// Alternative is to account for extraction according to section type - we're talking over 40 section types! + /// This way, we get basic information with minimal computational costs. + /// + public void ExtractProperties(string sectionName, Dictionary properties) + { + // Get all frame properties + int numberOfNames = 0; + string[] names = []; + eFramePropType[] propTypes = []; + double[] t3 = [], + t2 = [], + tf = [], + tw = [], + t2b = [], + tfb = [], + area = []; + + _settingsStore.Current.SapModel.PropFrame.GetAllFrameProperties_2( + ref numberOfNames, + ref names, + ref propTypes, + ref t3, + ref t2, + ref tf, + ref tw, + ref t2b, + ref tfb, + ref area + ); + + // Find the index of the current section + int sectionIndex = Array.IndexOf(names, sectionName); + + if (sectionIndex != -1) + { + // General Data + var generalData = properties.EnsureNested(SectionPropertyCategory.GENERAL_DATA); + generalData["Section Shape"] = propTypes[sectionIndex].ToString(); + + // Section Dimensions + string unit = _settingsStore.Current.SpeckleUnits; + var sectionDimensions = properties.EnsureNested(SectionPropertyCategory.SECTION_DIMENSIONS); + sectionDimensions.AddWithUnits("t3", t3[sectionIndex], unit); + sectionDimensions.AddWithUnits("t2", t2[sectionIndex], unit); + sectionDimensions.AddWithUnits("tf", tf[sectionIndex], unit); + sectionDimensions.AddWithUnits("tw", tw[sectionIndex], unit); + sectionDimensions.AddWithUnits("t2b", t2b[sectionIndex], unit); + sectionDimensions.AddWithUnits("tfb", tfb[sectionIndex], unit); + sectionDimensions.AddWithUnits("Area", area[sectionIndex], $"{unit}²"); + } + } +} diff --git a/Connectors/CSi/Speckle.Connectors.ETABSShared/HostApp/Helpers/EtabsSectionPropertyExtractor.cs b/Connectors/CSi/Speckle.Connectors.ETABSShared/HostApp/Helpers/EtabsSectionPropertyExtractor.cs new file mode 100644 index 000000000..93e0f3d30 --- /dev/null +++ b/Connectors/CSi/Speckle.Connectors.ETABSShared/HostApp/Helpers/EtabsSectionPropertyExtractor.cs @@ -0,0 +1,53 @@ +using Speckle.Connectors.CSiShared.HostApp.Helpers; + +namespace Speckle.Connectors.ETABSShared.HostApp.Helpers; + +/// +/// Coordinates property extraction combining base CSi and ETABS-specific properties. +/// +/// +/// Mirrors property extraction system pattern used in EtabsPropertiesExtractor. +/// Composition handled at coordinator level rather than individual extractors. +/// +public class EtabsSectionPropertyExtractor +{ + private readonly IFrameSectionPropertyExtractor _csiFrameExtractor; + private readonly IShellSectionPropertyExtractor _csiShellExtractor; + private readonly IApplicationFrameSectionPropertyExtractor _etabsFrameExtractor; + private readonly IApplicationShellSectionPropertyExtractor _etabsShellExtractor; + + public EtabsSectionPropertyExtractor( + IFrameSectionPropertyExtractor csiFrameExtractor, + IShellSectionPropertyExtractor csiShellExtractor, + IApplicationFrameSectionPropertyExtractor etabsFrameExtractor, + IApplicationShellSectionPropertyExtractor etabsShellExtractor + ) + { + _csiFrameExtractor = csiFrameExtractor; + _csiShellExtractor = csiShellExtractor; + _etabsFrameExtractor = etabsFrameExtractor; + _etabsShellExtractor = etabsShellExtractor; + } + + /// + /// Extract the frame section properties on both a Csi and app-specific level + /// + public Dictionary ExtractFrameSectionProperties(string sectionName) + { + Dictionary properties = new(); + _csiFrameExtractor.ExtractProperties(sectionName, properties); + _etabsFrameExtractor.ExtractProperties(sectionName, properties); + return properties; + } + + /// + /// Extract the shell section properties on both a Csi and app-specific level + /// + public Dictionary ExtractShellSectionProperties(string sectionName) + { + Dictionary properties = new(); + _csiShellExtractor.ExtractProperties(sectionName, properties); + _etabsShellExtractor.ExtractProperties(sectionName, properties); + return properties; + } +} diff --git a/Connectors/CSi/Speckle.Connectors.ETABSShared/HostApp/Helpers/EtabsShellSectionPropertyExtractor.cs b/Connectors/CSi/Speckle.Connectors.ETABSShared/HostApp/Helpers/EtabsShellSectionPropertyExtractor.cs new file mode 100644 index 000000000..4e137a72d --- /dev/null +++ b/Connectors/CSi/Speckle.Connectors.ETABSShared/HostApp/Helpers/EtabsShellSectionPropertyExtractor.cs @@ -0,0 +1,64 @@ +using Microsoft.Extensions.Logging; +using Speckle.Connectors.CSiShared.HostApp.Helpers; +using Speckle.Converters.Common; +using Speckle.Converters.CSiShared; +using Speckle.Converters.CSiShared.Utils; + +namespace Speckle.Connectors.ETABSShared.HostApp.Helpers; + +/// +/// Extracts ETABS-specific shell section properties. +/// +public class EtabsShellSectionPropertyExtractor : IApplicationShellSectionPropertyExtractor +{ + private readonly IConverterSettingsStore _settingsStore; + private readonly ILogger _logger; + private readonly EtabsShellSectionResolver _etabsShellSectionResolver; + + public EtabsShellSectionPropertyExtractor( + IConverterSettingsStore settingsStore, + ILogger logger, + EtabsShellSectionResolver etabsShellSectionResolver + ) + { + _settingsStore = settingsStore; + _logger = logger; + _etabsShellSectionResolver = etabsShellSectionResolver; + } + + /// + /// Extract shell section properties + /// + /// + /// sectionName is unique across all types (Wall, Slab and Deck) + /// There is no general query such as PropArea.GetShell() - rather we have to be specific on the type, for example + /// PropArea.GetWall() or PropArea.GetDeck() BUT we can't get the building type given a SectionName. + /// Hence the introduction of ResolveSection. + /// + public void ExtractProperties(string sectionName, Dictionary properties) + { + // Step 01: Finding the appropriate api query for the unknown section type (wall, deck or slab) + Dictionary resolvedProperties = _etabsShellSectionResolver.ResolveSection(sectionName); + + // Step 02: Mutate properties dictionary with resolved properties + foreach (var nestedDictionary in resolvedProperties) + { + if (nestedDictionary.Value is not Dictionary nestedValues) + { + _logger.LogWarning( + "Unexpected value type for key {Key} in section {SectionName}. Expected Dictionary, got {ActualType}", + nestedDictionary.Key, + sectionName, + nestedDictionary.Value?.GetType().Name ?? "null" + ); + continue; + } + + var nestedProperties = properties.EnsureNested(nestedDictionary.Key); + foreach (var kvp in nestedValues) + { + nestedProperties[kvp.Key] = kvp.Value; + } + } + } +} diff --git a/Connectors/CSi/Speckle.Connectors.ETABSShared/HostApp/Helpers/EtabsShellSectionResolver.cs b/Connectors/CSi/Speckle.Connectors.ETABSShared/HostApp/Helpers/EtabsShellSectionResolver.cs new file mode 100644 index 000000000..1d48f7c43 --- /dev/null +++ b/Connectors/CSi/Speckle.Connectors.ETABSShared/HostApp/Helpers/EtabsShellSectionResolver.cs @@ -0,0 +1,182 @@ +using Speckle.Converters.Common; +using Speckle.Converters.CSiShared; +using Speckle.Converters.CSiShared.Utils; + +namespace Speckle.Connectors.ETABSShared.HostApp.Helpers; + +/// +/// Attempts to resolve the section type and retrieve its properties by trying different section resolvers. +/// +/// +/// This service focuses solely on determining the correct section type and returning its properties. +/// Since section names are unique across different types (Wall, Slab, Deck), it uses a try-and-fail approach +/// rather than attempting to predetermine the type. The first successful resolution is returned. +/// +public record AreaSectionResult +{ + public bool Success { get; init; } + public Dictionary Properties { get; init; } +} + +public interface IAreaSectionResolver +{ + AreaSectionResult TryResolveSection(string sectionName); +} + +public class EtabsShellSectionResolver +{ + private readonly IConverterSettingsStore _settingsStore; + private readonly IEnumerable _resolvers; + + public EtabsShellSectionResolver(IConverterSettingsStore settingsStore) + { + _settingsStore = settingsStore; + _resolvers = + [ + new WallSectionResolver(_settingsStore), + new SlabSectionResolver(_settingsStore), + new DeckSectionResolver(_settingsStore) + ]; + } + + public Dictionary ResolveSection(string sectionName) + { + foreach (var resolver in _resolvers) + { + var result = resolver.TryResolveSection(sectionName); + if (result.Success) + { + return result.Properties; + } + } + + throw new InvalidOperationException($"Section '{sectionName}' could not be resolved to any known type."); + } +} + +public class WallSectionResolver(IConverterSettingsStore settingsStore) : IAreaSectionResolver +{ + public AreaSectionResult TryResolveSection(string sectionName) + { + eWallPropType wallPropType = default; + eShellType shellType = default; + string matProp = string.Empty; + double thickness = 0.0; + int color = 0; + string notes = string.Empty; + string guid = string.Empty; + + var result = settingsStore.Current.SapModel.PropArea.GetWall( + sectionName, + ref wallPropType, + ref shellType, + ref matProp, + ref thickness, + ref color, + ref notes, + ref guid + ); + + Dictionary generalData = []; + generalData["Property Name"] = sectionName; + generalData["Property Type"] = wallPropType.ToString(); + generalData["Material"] = matProp; + generalData["Modeling Type"] = shellType.ToString(); + generalData["Display Color"] = color; + generalData["Notes"] = notes; + + Dictionary propertyData = []; + propertyData["Type"] = "Wall"; + propertyData.AddWithUnits("Thickness", thickness, settingsStore.Current.SpeckleUnits); + + Dictionary properties = []; + properties[SectionPropertyCategory.GENERAL_DATA] = generalData; + properties[SectionPropertyCategory.PROPERTY_DATA] = propertyData; + + return new AreaSectionResult { Success = result == 0, Properties = properties }; + } +} + +public class SlabSectionResolver(IConverterSettingsStore settingsStore) : IAreaSectionResolver +{ + public AreaSectionResult TryResolveSection(string sectionName) + { + eSlabType slabType = default; + eShellType shellType = default; + string matProp = string.Empty; + double thickness = 0.0; + int color = 0; + string notes = string.Empty; + string guid = string.Empty; + + var result = settingsStore.Current.SapModel.PropArea.GetSlab( + sectionName, + ref slabType, + ref shellType, + ref matProp, + ref thickness, + ref color, + ref notes, + ref guid + ); + + Dictionary generalData = []; + generalData["Property Name"] = sectionName; + generalData["Material"] = matProp; + generalData["Modeling Type"] = shellType.ToString(); + generalData["Display Color"] = color; + generalData["Notes"] = notes; + + Dictionary propertyData = []; + propertyData["Type"] = slabType.ToString(); + propertyData.AddWithUnits("Thickness", thickness, settingsStore.Current.SpeckleUnits); + + Dictionary properties = []; + properties[SectionPropertyCategory.GENERAL_DATA] = generalData; + properties[SectionPropertyCategory.PROPERTY_DATA] = propertyData; + + return new AreaSectionResult { Success = result == 0, Properties = properties }; + } +} + +public class DeckSectionResolver(IConverterSettingsStore settingsStore) : IAreaSectionResolver +{ + public AreaSectionResult TryResolveSection(string sectionName) + { + eDeckType deckType = default; + eShellType shellType = default; + string deckMatProp = string.Empty; + double thickness = 0.0; + int color = 0; + string notes = string.Empty; + string guid = string.Empty; + + var result = settingsStore.Current.SapModel.PropArea.GetDeck( + sectionName, + ref deckType, + ref shellType, + ref deckMatProp, + ref thickness, + ref color, + ref notes, + ref guid + ); + + Dictionary generalData = []; + generalData["Property Name"] = sectionName; + generalData["Property Type"] = deckType.ToString(); + generalData["Material"] = deckMatProp; + generalData["Modeling Type"] = shellType.ToString(); + generalData["Display Color"] = color; + generalData["Notes"] = notes; + + Dictionary propertyData = []; + propertyData.AddWithUnits("Thickness", thickness, settingsStore.Current.SpeckleUnits); + + Dictionary properties = []; + properties[SectionPropertyCategory.GENERAL_DATA] = generalData; + properties[SectionPropertyCategory.PROPERTY_DATA] = propertyData; + + return new AreaSectionResult { Success = result == 0, Properties = properties }; + } +} diff --git a/Connectors/CSi/Speckle.Connectors.ETABSShared/Plugin/EtabsPluginBase.cs b/Connectors/CSi/Speckle.Connectors.ETABSShared/Plugin/EtabsPluginBase.cs new file mode 100644 index 000000000..342d6f47c --- /dev/null +++ b/Connectors/CSi/Speckle.Connectors.ETABSShared/Plugin/EtabsPluginBase.cs @@ -0,0 +1,17 @@ +using Speckle.Connectors.CSiShared; + +namespace Speckle.Connectors.ETABSShared; + +[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "")] +public abstract class EtabsPluginBase : CSiPluginBase +{ + public override int Info(ref string text) + { + text = "Next Gen Speckle Connector for ETABS"; + return 0; + } + + protected override SpeckleFormBase CreateForm() => CreateEtabsForm(); + + protected abstract EtabsSpeckleFormBase CreateEtabsForm(); +} diff --git a/Connectors/CSi/Speckle.Connectors.ETABSShared/Plugin/EtabsSpeckleFormBase.cs b/Connectors/CSi/Speckle.Connectors.ETABSShared/Plugin/EtabsSpeckleFormBase.cs new file mode 100644 index 000000000..0556f35ad --- /dev/null +++ b/Connectors/CSi/Speckle.Connectors.ETABSShared/Plugin/EtabsSpeckleFormBase.cs @@ -0,0 +1,16 @@ +using Microsoft.Extensions.DependencyInjection; +using Speckle.Connectors.CSiShared; +using Speckle.Sdk.Host; + +namespace Speckle.Connectors.ETABSShared; + +public abstract class EtabsSpeckleFormBase : SpeckleFormBase +{ + protected override HostApplication GetHostApplication() => HostApplications.ETABS; + + protected override void ConfigureServices(IServiceCollection services) + { + base.ConfigureServices(services); + services.AddEtabs(); + } +} diff --git a/Connectors/CSi/Speckle.Connectors.ETABSShared/ServiceRegistration.cs b/Connectors/CSi/Speckle.Connectors.ETABSShared/ServiceRegistration.cs new file mode 100644 index 000000000..871a32cd6 --- /dev/null +++ b/Connectors/CSi/Speckle.Connectors.ETABSShared/ServiceRegistration.cs @@ -0,0 +1,24 @@ +using Microsoft.Extensions.DependencyInjection; +using Speckle.Connectors.CSiShared.HostApp; +using Speckle.Connectors.CSiShared.HostApp.Helpers; +using Speckle.Connectors.ETABSShared.HostApp; +using Speckle.Connectors.ETABSShared.HostApp.Helpers; +using Speckle.Converters.ETABSShared; + +namespace Speckle.Connectors.ETABSShared; + +public static class ServiceRegistration +{ + public static IServiceCollection AddEtabs(this IServiceCollection services) + { + services.AddEtabsConverters(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + + return services; + } +} diff --git a/Connectors/CSi/Speckle.Connectors.ETABSShared/Speckle.Connectors.ETABSShared.projitems b/Connectors/CSi/Speckle.Connectors.ETABSShared/Speckle.Connectors.ETABSShared.projitems new file mode 100644 index 000000000..d6f11f0f8 --- /dev/null +++ b/Connectors/CSi/Speckle.Connectors.ETABSShared/Speckle.Connectors.ETABSShared.projitems @@ -0,0 +1,22 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + 5d1e0b0d-56a7-4e13-b9a9-8633e02b8f17 + + + Speckle.Connectors.ETABSShared + + + + + + + + + + + + + \ No newline at end of file diff --git a/Connectors/CSi/Speckle.Connectors.ETABSShared/Speckle.Connectors.ETABSShared.shproj b/Connectors/CSi/Speckle.Connectors.ETABSShared/Speckle.Connectors.ETABSShared.shproj new file mode 100644 index 000000000..64ebd540a --- /dev/null +++ b/Connectors/CSi/Speckle.Connectors.ETABSShared/Speckle.Connectors.ETABSShared.shproj @@ -0,0 +1,13 @@ + + + + 5d1e0b0d-56a7-4e13-b9a9-8633e02b8f17 + 14.0 + + + + + + + + diff --git a/Connectors/Navisworks/Directory.Build.targets b/Connectors/Navisworks/Directory.Build.targets new file mode 100644 index 000000000..ec391895f --- /dev/null +++ b/Connectors/Navisworks/Directory.Build.targets @@ -0,0 +1,34 @@ + + + + true + + NextGen Speckle Connector for Autodesk Navisworks Manage + $(Authors) jonathon@speckle.systems + $(PackageTags) connector nwd nwc nwf navisworks manage + + $(AppData)\Autodesk\ApplicationPlugins\Speckle.Connectors.Navisworks.bundle + $(AppData)\Autodesk\ApplicationPlugins\Speckle.Connectors.Navisworks.bundle\Contents\$(NavisworksVersion) + Speckle.Connector.Navisworks + + + + + + + + + + + + + + + + + + + + + + diff --git a/Connectors/Navisworks/Speckle.Connectors.Navisworks2020/Properties/launchSettings.json b/Connectors/Navisworks/Speckle.Connectors.Navisworks2020/Properties/launchSettings.json new file mode 100644 index 000000000..618204c00 --- /dev/null +++ b/Connectors/Navisworks/Speckle.Connectors.Navisworks2020/Properties/launchSettings.json @@ -0,0 +1,9 @@ +{ + "profiles": { + "Speckle.Connector.Navisworks2020": { + "commandName": "Executable", + "executablePath": "C:\\Program Files\\Autodesk\\Navisworks Manage 2020\\Roamer.exe", + "commandLineArgs": " -licensing AdLM" + } + } +} diff --git a/Connectors/Navisworks/Speckle.Connectors.Navisworks2020/Speckle.Connectors.Navisworks2020.csproj b/Connectors/Navisworks/Speckle.Connectors.Navisworks2020/Speckle.Connectors.Navisworks2020.csproj new file mode 100644 index 000000000..ff44acc18 --- /dev/null +++ b/Connectors/Navisworks/Speckle.Connectors.Navisworks2020/Speckle.Connectors.Navisworks2020.csproj @@ -0,0 +1,27 @@ + + + net48 + x64 + win-x64 + 512 + + v17 + 2020 + + $(DefineConstants);TRACE;NAVIS2020;NAVIS + Debug;Release;Local + + + + + + + + + + + + + + + diff --git a/Connectors/Navisworks/Speckle.Connectors.Navisworks2020/packages.lock.json b/Connectors/Navisworks/Speckle.Connectors.Navisworks2020/packages.lock.json new file mode 100644 index 000000000..f561c800f --- /dev/null +++ b/Connectors/Navisworks/Speckle.Connectors.Navisworks2020/packages.lock.json @@ -0,0 +1,387 @@ +{ + "version": 2, + "dependencies": { + ".NETFramework,Version=v4.8": { + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net48": "1.0.3" + } + }, + "Microsoft.SourceLink.GitHub": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "dependencies": { + "Microsoft.Build.Tasks.Git": "8.0.0", + "Microsoft.SourceLink.Common": "8.0.0" + } + }, + "PolySharp": { + "type": "Direct", + "requested": "[1.14.1, )", + "resolved": "1.14.1", + "contentHash": "mOOmFYwad3MIOL14VCjj02LljyF1GNw1wP0YVlxtcPvqdxjGGMNdNJJxHptlry3MOd8b40Flm8RPOM8JOlN2sQ==" + }, + "Speckle.InterfaceGenerator": { + "type": "Direct", + "requested": "[0.9.6, )", + "resolved": "0.9.6", + "contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w==" + }, + "Speckle.Navisworks.API": { + "type": "Direct", + "requested": "[2020.0.0, )", + "resolved": "2020.0.0", + "contentHash": "biB2RR0HNxrbHD7zBZoJUhwzPwVE5IFg9l4/747bHOLRJC3FM5UtzdjGwvRZwfOlFyM4P26NYARSiCaxSNIBpg==" + }, + "GraphQL.Client": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "8yPNBbuVBpTptivyAlak4GZvbwbUcjeQTL4vN1HKHRuOykZ4r7l5fcLS6vpyPyLn0x8FsL31xbOIKyxbmR9rbA==", + "dependencies": { + "GraphQL.Client.Abstractions": "6.0.0", + "GraphQL.Client.Abstractions.Websocket": "6.0.0", + "System.Net.WebSockets.Client.Managed": "1.0.22", + "System.Reactive": "5.0.0" + } + }, + "GraphQL.Client.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "h7uzWFORHZ+CCjwr/ThAyXMr0DPpzEANDa4Uo54wqCQ+j7qUKwqYTgOrb1W40sqbvNaZm9v/X7It31SUw0maHA==", + "dependencies": { + "GraphQL.Primitives": "6.0.0" + } + }, + "GraphQL.Client.Abstractions.Websocket": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "Nr9bPf8gIOvLuXpqEpqr9z9jslYFJOvd0feHth3/kPqeR3uMbjF5pjiwh4jxyMcxHdr8Pb6QiXkV3hsSyt0v7A==", + "dependencies": { + "GraphQL.Client.Abstractions": "6.0.0" + } + }, + "GraphQL.Primitives": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "yg72rrYDapfsIUrul7aF6wwNnTJBOFvuA9VdDTQpPa8AlAriHbufeXYLBcodKjfUdkCnaiggX1U/nEP08Zb5GA==" + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "W8DPQjkMScOMTtJbPwmPyj9c3zYSFGawDW3jwlBOOsnY+EzZFLgNQ/UMkK35JmkNOVPdCyPr2Tw7Vv9N+KA3ZQ==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.CSharp": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==" + }, + "Microsoft.Data.Sqlite": { + "type": "Transitive", + "resolved": "7.0.5", + "contentHash": "KGxbPeWsQMnmQy43DSBxAFtHz3l2JX8EWBSGUCvT3CuZ8KsuzbkqMIJMDOxWtG8eZSoCDI04aiVQjWuuV8HmSw==", + "dependencies": { + "Microsoft.Data.Sqlite.Core": "7.0.5", + "SQLitePCLRaw.bundle_e_sqlite3": "2.1.4" + } + }, + "Microsoft.Data.Sqlite.Core": { + "type": "Transitive", + "resolved": "7.0.5", + "contentHash": "FTerRmQPqHrCrnoUzhBu+E+1DNGwyrAMLqHkAqOOOu5pGfyMOj8qQUBxI/gDtWtG11p49UxSfWmBzRNlwZqfUg==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "nOP8R1mVb/6mZtm2qgAJXn/LFm/2kMjHDAg/QJLFG6CuWYJtaD3p1BwQhufBVvRzL9ceJ/xF0SQ0qsI2GkDQAA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "65MrmXCziWaQFrI0UHkQbesrX5wTwf9XPjY5yFm/VkgJKFJ5gqvXRoXjIZcf2wLi5ZlwGz/oMYfyURVCWbM5iw==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "vJ9xvOZCnUAIHcGC3SU35r3HKmHTVIeHzo6u/qzlHAqD8m6xv92MLin4oJntTvkpKxVX3vI1GFFkIQtU3AdlsQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "2.2.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw==" + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "UpZLNLBpIZ0GTebShui7xXYh6DmBHjWM8NxGxZbdQh/bPZ5e6YswqI+bru6BnEL5eWiOdodsXtEz3FROcgi/qg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Primitives": "2.2.0", + "System.ComponentModel.Annotations": "4.5.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "azyQtqbm4fSaDzZHD/J+V6oWMFaf2tWP4WEGIYePLCMw3+b2RQdj9ybgbQyjCshcitQKQ4lEDOZjmSlTTrHxUg==", + "dependencies": { + "System.Memory": "4.5.1", + "System.Runtime.CompilerServices.Unsafe": "4.5.1" + } + }, + "Microsoft.NETFramework.ReferenceAssemblies.net48": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "zMk4D+9zyiEWByyQ7oPImPN/Jhpj166Ky0Nlla4eXlNL8hI/BtSJsgR8Inldd4NNpIAH3oh8yym0W2DrhXdSLQ==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "Speckle.Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.2", + "contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA==" + }, + "SQLitePCLRaw.bundle_e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "EWI1olKDjFEBMJu0+3wuxwziIAdWDVMYLhuZ3Qs84rrz+DHwD00RzWPZCa+bLnHCf3oJwuFZIRsHT5p236QXww==", + "dependencies": { + "SQLitePCLRaw.lib.e_sqlite3": "2.1.4", + "SQLitePCLRaw.provider.dynamic_cdecl": "2.1.4" + } + }, + "SQLitePCLRaw.core": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "inBjvSHo9UDKneGNzfUfDjK08JzlcIhn1+SP5Y3m6cgXpCxXKCJDy6Mka7LpgSV+UZmKSnC8rTwB0SQ0xKu5pA==", + "dependencies": { + "System.Memory": "4.5.3" + } + }, + "SQLitePCLRaw.lib.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "2C9Q9eX7CPLveJA0rIhf9RXAvu+7nWZu1A2MdG6SD/NOu26TakGgL1nsbc0JAspGijFOo3HoN79xrx8a368fBg==" + }, + "SQLitePCLRaw.provider.dynamic_cdecl": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "ZsaKKhgYF9B1fvcnOGKl3EycNAwd9CRWX7v0rEfuPWhQQ5Jjpvf2VEHahiLIGHio3hxi3EIKFJw9KvyowWOUAw==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "System.Buffers": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "AwarXzzoDwX6BgrhjoJsk6tUezZEozOT5Y9QKF94Gl4JK91I4PIIBkBco9068Y9/Dra8Dkbie99kXB8+1BaYKw==" + }, + "System.ComponentModel.Annotations": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "UxYQ3FGUOtzJ7LfSdnYSFd7+oEv6M8NgUatatIN2HxNtDdlcvFAf+VIq4Of9cDMJEJC0aSRv/x898RYhB4Yppg==" + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.3", + "contentHash": "3oDzvc/zzetpTKWMShs1AADwZjQ/36HnsufHRPcOjyRAAMLDlu2iD33MBI2opxnezcVUtXyqDXXjoFMOU9c7SA==", + "dependencies": { + "System.Buffers": "4.4.0", + "System.Numerics.Vectors": "4.4.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.2" + } + }, + "System.Net.WebSockets.Client.Managed": { + "type": "Transitive", + "resolved": "1.0.22", + "contentHash": "WqEOxPlXjuZrIjUtXNE9NxEfU/n5E35iV2PtoZdJSUC4tlrqwHnTee+wvMIM4OUaJWmwrymeqcgYrE0IkGAgLA==", + "dependencies": { + "System.Buffers": "4.4.0", + "System.Numerics.Vectors": "4.4.0" + } + }, + "System.Numerics.Vectors": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ==" + }, + "System.Reactive": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "4.5.3", + "contentHash": "3TIsJhD1EiiT0w2CcDMN/iSSwnNnsrnbzeVHSKkaEgV85txMprmuO+Yq2AdSbeVGcg28pdNDTPK87tJhX7VFHw==" + }, + "System.Threading.Tasks.Extensions": { + "type": "Transitive", + "resolved": "4.5.4", + "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + }, + "speckle.connectors.common": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", + "Speckle.Connectors.Logging": "[1.0.0, )", + "Speckle.Objects": "[3.1.1, )", + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" + } + }, + "speckle.connectors.dui": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", + "Speckle.Connectors.Common": "[1.0.0, )", + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" + } + }, + "speckle.connectors.dui.webview": { + "type": "Project", + "dependencies": { + "Microsoft.Web.WebView2": "[1.0.1938.49, )", + "Speckle.Connectors.DUI": "[1.0.0, )" + } + }, + "speckle.connectors.logging": { + "type": "Project" + }, + "speckle.converters.common": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", + "Speckle.Objects": "[3.1.1, )" + } + }, + "speckle.converters.navisworks2020": { + "type": "Project", + "dependencies": { + "Speckle.Connectors.DUI": "[1.0.0, )", + "Speckle.Converters.Common": "[1.0.0, )", + "Speckle.Navisworks.API": "[2020.0.0, )" + } + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "MZtBIwfDFork5vfjpJdG5g8wuJFt7d/y3LOSVVtDK/76wlbtz6cjltfKHqLx2TKVqTj5/c41t77m1+h20zqtPA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0" + } + }, + "Microsoft.Extensions.Logging": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "Nxqhadc9FCmFHzU+fz3oc8sFlE6IadViYg8dfUdGzJZ2JUxnCsRghBhhOWdM4B2zSZqEc+0BjliBh/oNdRZuig==", + "dependencies": { + "Microsoft.Extensions.Configuration.Binder": "2.2.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging.Abstractions": "2.2.0", + "Microsoft.Extensions.Options": "2.2.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" + }, + "Microsoft.Web.WebView2": { + "type": "CentralTransitive", + "requested": "[1.0.1938.49, )", + "resolved": "1.0.1938.49", + "contentHash": "z8KnFnaTYzhA/ZnyRX0qGfS1NU5ZBJeClAH64F0fVDvdDJTvME7xl6zTJ0Jlfe1BtL3C0NH9xTy64shg2baKdw==" + }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, + "Speckle.Objects": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", + "dependencies": { + "Speckle.Sdk": "3.1.1" + } + }, + "Speckle.Sdk": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", + "dependencies": { + "GraphQL.Client": "6.0.0", + "Microsoft.Bcl.AsyncInterfaces": "5.0.0", + "Microsoft.CSharp": "4.7.0", + "Microsoft.Data.Sqlite": "7.0.5", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging": "2.2.0", + "Speckle.DoubleNumerics": "4.1.0", + "Speckle.Newtonsoft.Json": "13.0.2", + "Speckle.Sdk.Dependencies": "3.1.1" + } + }, + "Speckle.Sdk.Dependencies": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" + } + }, + ".NETFramework,Version=v4.8/win-x64": { + "SQLitePCLRaw.lib.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "2C9Q9eX7CPLveJA0rIhf9RXAvu+7nWZu1A2MdG6SD/NOu26TakGgL1nsbc0JAspGijFOo3HoN79xrx8a368fBg==" + }, + "Microsoft.Web.WebView2": { + "type": "CentralTransitive", + "requested": "[1.0.1938.49, )", + "resolved": "1.0.1938.49", + "contentHash": "z8KnFnaTYzhA/ZnyRX0qGfS1NU5ZBJeClAH64F0fVDvdDJTvME7xl6zTJ0Jlfe1BtL3C0NH9xTy64shg2baKdw==" + } + } + } +} \ No newline at end of file diff --git a/Connectors/Navisworks/Speckle.Connectors.Navisworks2021/Properties/launchSettings.json b/Connectors/Navisworks/Speckle.Connectors.Navisworks2021/Properties/launchSettings.json new file mode 100644 index 000000000..4c3311c03 --- /dev/null +++ b/Connectors/Navisworks/Speckle.Connectors.Navisworks2021/Properties/launchSettings.json @@ -0,0 +1,9 @@ +{ + "profiles": { + "Speckle.Connector.Navisworks2021": { + "commandName": "Executable", + "executablePath": "C:\\Program Files\\Autodesk\\Navisworks Manage 2021\\Roamer.exe", + "commandLineArgs": " -licensing AdLM" + } + } +} diff --git a/Connectors/Navisworks/Speckle.Connectors.Navisworks2021/Speckle.Connectors.Navisworks2021.csproj b/Connectors/Navisworks/Speckle.Connectors.Navisworks2021/Speckle.Connectors.Navisworks2021.csproj new file mode 100644 index 000000000..b1cfe19b9 --- /dev/null +++ b/Connectors/Navisworks/Speckle.Connectors.Navisworks2021/Speckle.Connectors.Navisworks2021.csproj @@ -0,0 +1,27 @@ + + + net48 + x64 + win-x64 + 512 + + v18 + 2021 + + $(DefineConstants);TRACE;NAVIS2021;NAVIS + Debug;Release;Local + + + + + + + + + + + + + + + diff --git a/Connectors/Navisworks/Speckle.Connectors.Navisworks2021/packages.lock.json b/Connectors/Navisworks/Speckle.Connectors.Navisworks2021/packages.lock.json new file mode 100644 index 000000000..57232af5e --- /dev/null +++ b/Connectors/Navisworks/Speckle.Connectors.Navisworks2021/packages.lock.json @@ -0,0 +1,387 @@ +{ + "version": 2, + "dependencies": { + ".NETFramework,Version=v4.8": { + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net48": "1.0.3" + } + }, + "Microsoft.SourceLink.GitHub": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "dependencies": { + "Microsoft.Build.Tasks.Git": "8.0.0", + "Microsoft.SourceLink.Common": "8.0.0" + } + }, + "PolySharp": { + "type": "Direct", + "requested": "[1.14.1, )", + "resolved": "1.14.1", + "contentHash": "mOOmFYwad3MIOL14VCjj02LljyF1GNw1wP0YVlxtcPvqdxjGGMNdNJJxHptlry3MOd8b40Flm8RPOM8JOlN2sQ==" + }, + "Speckle.InterfaceGenerator": { + "type": "Direct", + "requested": "[0.9.6, )", + "resolved": "0.9.6", + "contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w==" + }, + "Speckle.Navisworks.API": { + "type": "Direct", + "requested": "[2021.0.0, )", + "resolved": "2021.0.0", + "contentHash": "cY7sU8dxISfTQLinUIOki/azS+bIX28uEZQO4ijrq0eOUhJlKcKWS273kHPoL0+T0Xrkd+1OWj2YFa2PbHGgwQ==" + }, + "GraphQL.Client": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "8yPNBbuVBpTptivyAlak4GZvbwbUcjeQTL4vN1HKHRuOykZ4r7l5fcLS6vpyPyLn0x8FsL31xbOIKyxbmR9rbA==", + "dependencies": { + "GraphQL.Client.Abstractions": "6.0.0", + "GraphQL.Client.Abstractions.Websocket": "6.0.0", + "System.Net.WebSockets.Client.Managed": "1.0.22", + "System.Reactive": "5.0.0" + } + }, + "GraphQL.Client.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "h7uzWFORHZ+CCjwr/ThAyXMr0DPpzEANDa4Uo54wqCQ+j7qUKwqYTgOrb1W40sqbvNaZm9v/X7It31SUw0maHA==", + "dependencies": { + "GraphQL.Primitives": "6.0.0" + } + }, + "GraphQL.Client.Abstractions.Websocket": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "Nr9bPf8gIOvLuXpqEpqr9z9jslYFJOvd0feHth3/kPqeR3uMbjF5pjiwh4jxyMcxHdr8Pb6QiXkV3hsSyt0v7A==", + "dependencies": { + "GraphQL.Client.Abstractions": "6.0.0" + } + }, + "GraphQL.Primitives": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "yg72rrYDapfsIUrul7aF6wwNnTJBOFvuA9VdDTQpPa8AlAriHbufeXYLBcodKjfUdkCnaiggX1U/nEP08Zb5GA==" + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "W8DPQjkMScOMTtJbPwmPyj9c3zYSFGawDW3jwlBOOsnY+EzZFLgNQ/UMkK35JmkNOVPdCyPr2Tw7Vv9N+KA3ZQ==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.CSharp": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==" + }, + "Microsoft.Data.Sqlite": { + "type": "Transitive", + "resolved": "7.0.5", + "contentHash": "KGxbPeWsQMnmQy43DSBxAFtHz3l2JX8EWBSGUCvT3CuZ8KsuzbkqMIJMDOxWtG8eZSoCDI04aiVQjWuuV8HmSw==", + "dependencies": { + "Microsoft.Data.Sqlite.Core": "7.0.5", + "SQLitePCLRaw.bundle_e_sqlite3": "2.1.4" + } + }, + "Microsoft.Data.Sqlite.Core": { + "type": "Transitive", + "resolved": "7.0.5", + "contentHash": "FTerRmQPqHrCrnoUzhBu+E+1DNGwyrAMLqHkAqOOOu5pGfyMOj8qQUBxI/gDtWtG11p49UxSfWmBzRNlwZqfUg==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "nOP8R1mVb/6mZtm2qgAJXn/LFm/2kMjHDAg/QJLFG6CuWYJtaD3p1BwQhufBVvRzL9ceJ/xF0SQ0qsI2GkDQAA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "65MrmXCziWaQFrI0UHkQbesrX5wTwf9XPjY5yFm/VkgJKFJ5gqvXRoXjIZcf2wLi5ZlwGz/oMYfyURVCWbM5iw==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "vJ9xvOZCnUAIHcGC3SU35r3HKmHTVIeHzo6u/qzlHAqD8m6xv92MLin4oJntTvkpKxVX3vI1GFFkIQtU3AdlsQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "2.2.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw==" + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "UpZLNLBpIZ0GTebShui7xXYh6DmBHjWM8NxGxZbdQh/bPZ5e6YswqI+bru6BnEL5eWiOdodsXtEz3FROcgi/qg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Primitives": "2.2.0", + "System.ComponentModel.Annotations": "4.5.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "azyQtqbm4fSaDzZHD/J+V6oWMFaf2tWP4WEGIYePLCMw3+b2RQdj9ybgbQyjCshcitQKQ4lEDOZjmSlTTrHxUg==", + "dependencies": { + "System.Memory": "4.5.1", + "System.Runtime.CompilerServices.Unsafe": "4.5.1" + } + }, + "Microsoft.NETFramework.ReferenceAssemblies.net48": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "zMk4D+9zyiEWByyQ7oPImPN/Jhpj166Ky0Nlla4eXlNL8hI/BtSJsgR8Inldd4NNpIAH3oh8yym0W2DrhXdSLQ==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "Speckle.Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.2", + "contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA==" + }, + "SQLitePCLRaw.bundle_e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "EWI1olKDjFEBMJu0+3wuxwziIAdWDVMYLhuZ3Qs84rrz+DHwD00RzWPZCa+bLnHCf3oJwuFZIRsHT5p236QXww==", + "dependencies": { + "SQLitePCLRaw.lib.e_sqlite3": "2.1.4", + "SQLitePCLRaw.provider.dynamic_cdecl": "2.1.4" + } + }, + "SQLitePCLRaw.core": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "inBjvSHo9UDKneGNzfUfDjK08JzlcIhn1+SP5Y3m6cgXpCxXKCJDy6Mka7LpgSV+UZmKSnC8rTwB0SQ0xKu5pA==", + "dependencies": { + "System.Memory": "4.5.3" + } + }, + "SQLitePCLRaw.lib.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "2C9Q9eX7CPLveJA0rIhf9RXAvu+7nWZu1A2MdG6SD/NOu26TakGgL1nsbc0JAspGijFOo3HoN79xrx8a368fBg==" + }, + "SQLitePCLRaw.provider.dynamic_cdecl": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "ZsaKKhgYF9B1fvcnOGKl3EycNAwd9CRWX7v0rEfuPWhQQ5Jjpvf2VEHahiLIGHio3hxi3EIKFJw9KvyowWOUAw==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "System.Buffers": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "AwarXzzoDwX6BgrhjoJsk6tUezZEozOT5Y9QKF94Gl4JK91I4PIIBkBco9068Y9/Dra8Dkbie99kXB8+1BaYKw==" + }, + "System.ComponentModel.Annotations": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "UxYQ3FGUOtzJ7LfSdnYSFd7+oEv6M8NgUatatIN2HxNtDdlcvFAf+VIq4Of9cDMJEJC0aSRv/x898RYhB4Yppg==" + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.3", + "contentHash": "3oDzvc/zzetpTKWMShs1AADwZjQ/36HnsufHRPcOjyRAAMLDlu2iD33MBI2opxnezcVUtXyqDXXjoFMOU9c7SA==", + "dependencies": { + "System.Buffers": "4.4.0", + "System.Numerics.Vectors": "4.4.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.2" + } + }, + "System.Net.WebSockets.Client.Managed": { + "type": "Transitive", + "resolved": "1.0.22", + "contentHash": "WqEOxPlXjuZrIjUtXNE9NxEfU/n5E35iV2PtoZdJSUC4tlrqwHnTee+wvMIM4OUaJWmwrymeqcgYrE0IkGAgLA==", + "dependencies": { + "System.Buffers": "4.4.0", + "System.Numerics.Vectors": "4.4.0" + } + }, + "System.Numerics.Vectors": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ==" + }, + "System.Reactive": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "4.5.3", + "contentHash": "3TIsJhD1EiiT0w2CcDMN/iSSwnNnsrnbzeVHSKkaEgV85txMprmuO+Yq2AdSbeVGcg28pdNDTPK87tJhX7VFHw==" + }, + "System.Threading.Tasks.Extensions": { + "type": "Transitive", + "resolved": "4.5.4", + "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + }, + "speckle.connectors.common": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", + "Speckle.Connectors.Logging": "[1.0.0, )", + "Speckle.Objects": "[3.1.1, )", + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" + } + }, + "speckle.connectors.dui": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", + "Speckle.Connectors.Common": "[1.0.0, )", + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" + } + }, + "speckle.connectors.dui.webview": { + "type": "Project", + "dependencies": { + "Microsoft.Web.WebView2": "[1.0.1938.49, )", + "Speckle.Connectors.DUI": "[1.0.0, )" + } + }, + "speckle.connectors.logging": { + "type": "Project" + }, + "speckle.converters.common": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", + "Speckle.Objects": "[3.1.1, )" + } + }, + "speckle.converters.navisworks2021": { + "type": "Project", + "dependencies": { + "Speckle.Connectors.DUI": "[1.0.0, )", + "Speckle.Converters.Common": "[1.0.0, )", + "Speckle.Navisworks.API": "[2021.0.0, )" + } + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "MZtBIwfDFork5vfjpJdG5g8wuJFt7d/y3LOSVVtDK/76wlbtz6cjltfKHqLx2TKVqTj5/c41t77m1+h20zqtPA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0" + } + }, + "Microsoft.Extensions.Logging": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "Nxqhadc9FCmFHzU+fz3oc8sFlE6IadViYg8dfUdGzJZ2JUxnCsRghBhhOWdM4B2zSZqEc+0BjliBh/oNdRZuig==", + "dependencies": { + "Microsoft.Extensions.Configuration.Binder": "2.2.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging.Abstractions": "2.2.0", + "Microsoft.Extensions.Options": "2.2.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" + }, + "Microsoft.Web.WebView2": { + "type": "CentralTransitive", + "requested": "[1.0.1938.49, )", + "resolved": "1.0.1938.49", + "contentHash": "z8KnFnaTYzhA/ZnyRX0qGfS1NU5ZBJeClAH64F0fVDvdDJTvME7xl6zTJ0Jlfe1BtL3C0NH9xTy64shg2baKdw==" + }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, + "Speckle.Objects": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", + "dependencies": { + "Speckle.Sdk": "3.1.1" + } + }, + "Speckle.Sdk": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", + "dependencies": { + "GraphQL.Client": "6.0.0", + "Microsoft.Bcl.AsyncInterfaces": "5.0.0", + "Microsoft.CSharp": "4.7.0", + "Microsoft.Data.Sqlite": "7.0.5", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging": "2.2.0", + "Speckle.DoubleNumerics": "4.1.0", + "Speckle.Newtonsoft.Json": "13.0.2", + "Speckle.Sdk.Dependencies": "3.1.1" + } + }, + "Speckle.Sdk.Dependencies": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" + } + }, + ".NETFramework,Version=v4.8/win-x64": { + "SQLitePCLRaw.lib.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "2C9Q9eX7CPLveJA0rIhf9RXAvu+7nWZu1A2MdG6SD/NOu26TakGgL1nsbc0JAspGijFOo3HoN79xrx8a368fBg==" + }, + "Microsoft.Web.WebView2": { + "type": "CentralTransitive", + "requested": "[1.0.1938.49, )", + "resolved": "1.0.1938.49", + "contentHash": "z8KnFnaTYzhA/ZnyRX0qGfS1NU5ZBJeClAH64F0fVDvdDJTvME7xl6zTJ0Jlfe1BtL3C0NH9xTy64shg2baKdw==" + } + } + } +} \ No newline at end of file diff --git a/Connectors/Navisworks/Speckle.Connectors.Navisworks2022/Properties/launchSettings.json b/Connectors/Navisworks/Speckle.Connectors.Navisworks2022/Properties/launchSettings.json new file mode 100644 index 000000000..c8dd67e7d --- /dev/null +++ b/Connectors/Navisworks/Speckle.Connectors.Navisworks2022/Properties/launchSettings.json @@ -0,0 +1,9 @@ +{ + "profiles": { + "Speckle.Connector.Navisworks2022": { + "commandName": "Executable", + "executablePath": "C:\\Program Files\\Autodesk\\Navisworks Manage 2022\\Roamer.exe", + "commandLineArgs": " -licensing AdLM" + } + } +} diff --git a/Connectors/Navisworks/Speckle.Connectors.Navisworks2022/Speckle.Connectors.Navisworks2022.csproj b/Connectors/Navisworks/Speckle.Connectors.Navisworks2022/Speckle.Connectors.Navisworks2022.csproj new file mode 100644 index 000000000..2857479fa --- /dev/null +++ b/Connectors/Navisworks/Speckle.Connectors.Navisworks2022/Speckle.Connectors.Navisworks2022.csproj @@ -0,0 +1,27 @@ + + + net48 + x64 + win-x64 + 512 + + v19 + 2022 + + $(DefineConstants);TRACE;NAVIS2022;NAVIS + Debug;Release;Local + + + + + + + + + + + + + + + diff --git a/Connectors/Navisworks/Speckle.Connectors.Navisworks2022/packages.lock.json b/Connectors/Navisworks/Speckle.Connectors.Navisworks2022/packages.lock.json new file mode 100644 index 000000000..04777ce9c --- /dev/null +++ b/Connectors/Navisworks/Speckle.Connectors.Navisworks2022/packages.lock.json @@ -0,0 +1,387 @@ +{ + "version": 2, + "dependencies": { + ".NETFramework,Version=v4.8": { + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net48": "1.0.3" + } + }, + "Microsoft.SourceLink.GitHub": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "dependencies": { + "Microsoft.Build.Tasks.Git": "8.0.0", + "Microsoft.SourceLink.Common": "8.0.0" + } + }, + "PolySharp": { + "type": "Direct", + "requested": "[1.14.1, )", + "resolved": "1.14.1", + "contentHash": "mOOmFYwad3MIOL14VCjj02LljyF1GNw1wP0YVlxtcPvqdxjGGMNdNJJxHptlry3MOd8b40Flm8RPOM8JOlN2sQ==" + }, + "Speckle.InterfaceGenerator": { + "type": "Direct", + "requested": "[0.9.6, )", + "resolved": "0.9.6", + "contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w==" + }, + "Speckle.Navisworks.API": { + "type": "Direct", + "requested": "[2022.0.0, )", + "resolved": "2022.0.0", + "contentHash": "x0RW4Iqw8YHVK4ZiLEyLLfI5ffuRBR0KhEvmy9ZpT8SLNeDL/c6jn+7JWBVFUJPu+ObVnU+KqJjLdHmphN0lMQ==" + }, + "GraphQL.Client": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "8yPNBbuVBpTptivyAlak4GZvbwbUcjeQTL4vN1HKHRuOykZ4r7l5fcLS6vpyPyLn0x8FsL31xbOIKyxbmR9rbA==", + "dependencies": { + "GraphQL.Client.Abstractions": "6.0.0", + "GraphQL.Client.Abstractions.Websocket": "6.0.0", + "System.Net.WebSockets.Client.Managed": "1.0.22", + "System.Reactive": "5.0.0" + } + }, + "GraphQL.Client.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "h7uzWFORHZ+CCjwr/ThAyXMr0DPpzEANDa4Uo54wqCQ+j7qUKwqYTgOrb1W40sqbvNaZm9v/X7It31SUw0maHA==", + "dependencies": { + "GraphQL.Primitives": "6.0.0" + } + }, + "GraphQL.Client.Abstractions.Websocket": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "Nr9bPf8gIOvLuXpqEpqr9z9jslYFJOvd0feHth3/kPqeR3uMbjF5pjiwh4jxyMcxHdr8Pb6QiXkV3hsSyt0v7A==", + "dependencies": { + "GraphQL.Client.Abstractions": "6.0.0" + } + }, + "GraphQL.Primitives": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "yg72rrYDapfsIUrul7aF6wwNnTJBOFvuA9VdDTQpPa8AlAriHbufeXYLBcodKjfUdkCnaiggX1U/nEP08Zb5GA==" + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "W8DPQjkMScOMTtJbPwmPyj9c3zYSFGawDW3jwlBOOsnY+EzZFLgNQ/UMkK35JmkNOVPdCyPr2Tw7Vv9N+KA3ZQ==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.CSharp": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==" + }, + "Microsoft.Data.Sqlite": { + "type": "Transitive", + "resolved": "7.0.5", + "contentHash": "KGxbPeWsQMnmQy43DSBxAFtHz3l2JX8EWBSGUCvT3CuZ8KsuzbkqMIJMDOxWtG8eZSoCDI04aiVQjWuuV8HmSw==", + "dependencies": { + "Microsoft.Data.Sqlite.Core": "7.0.5", + "SQLitePCLRaw.bundle_e_sqlite3": "2.1.4" + } + }, + "Microsoft.Data.Sqlite.Core": { + "type": "Transitive", + "resolved": "7.0.5", + "contentHash": "FTerRmQPqHrCrnoUzhBu+E+1DNGwyrAMLqHkAqOOOu5pGfyMOj8qQUBxI/gDtWtG11p49UxSfWmBzRNlwZqfUg==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "nOP8R1mVb/6mZtm2qgAJXn/LFm/2kMjHDAg/QJLFG6CuWYJtaD3p1BwQhufBVvRzL9ceJ/xF0SQ0qsI2GkDQAA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "65MrmXCziWaQFrI0UHkQbesrX5wTwf9XPjY5yFm/VkgJKFJ5gqvXRoXjIZcf2wLi5ZlwGz/oMYfyURVCWbM5iw==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "vJ9xvOZCnUAIHcGC3SU35r3HKmHTVIeHzo6u/qzlHAqD8m6xv92MLin4oJntTvkpKxVX3vI1GFFkIQtU3AdlsQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "2.2.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw==" + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "UpZLNLBpIZ0GTebShui7xXYh6DmBHjWM8NxGxZbdQh/bPZ5e6YswqI+bru6BnEL5eWiOdodsXtEz3FROcgi/qg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Primitives": "2.2.0", + "System.ComponentModel.Annotations": "4.5.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "azyQtqbm4fSaDzZHD/J+V6oWMFaf2tWP4WEGIYePLCMw3+b2RQdj9ybgbQyjCshcitQKQ4lEDOZjmSlTTrHxUg==", + "dependencies": { + "System.Memory": "4.5.1", + "System.Runtime.CompilerServices.Unsafe": "4.5.1" + } + }, + "Microsoft.NETFramework.ReferenceAssemblies.net48": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "zMk4D+9zyiEWByyQ7oPImPN/Jhpj166Ky0Nlla4eXlNL8hI/BtSJsgR8Inldd4NNpIAH3oh8yym0W2DrhXdSLQ==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "Speckle.Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.2", + "contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA==" + }, + "SQLitePCLRaw.bundle_e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "EWI1olKDjFEBMJu0+3wuxwziIAdWDVMYLhuZ3Qs84rrz+DHwD00RzWPZCa+bLnHCf3oJwuFZIRsHT5p236QXww==", + "dependencies": { + "SQLitePCLRaw.lib.e_sqlite3": "2.1.4", + "SQLitePCLRaw.provider.dynamic_cdecl": "2.1.4" + } + }, + "SQLitePCLRaw.core": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "inBjvSHo9UDKneGNzfUfDjK08JzlcIhn1+SP5Y3m6cgXpCxXKCJDy6Mka7LpgSV+UZmKSnC8rTwB0SQ0xKu5pA==", + "dependencies": { + "System.Memory": "4.5.3" + } + }, + "SQLitePCLRaw.lib.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "2C9Q9eX7CPLveJA0rIhf9RXAvu+7nWZu1A2MdG6SD/NOu26TakGgL1nsbc0JAspGijFOo3HoN79xrx8a368fBg==" + }, + "SQLitePCLRaw.provider.dynamic_cdecl": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "ZsaKKhgYF9B1fvcnOGKl3EycNAwd9CRWX7v0rEfuPWhQQ5Jjpvf2VEHahiLIGHio3hxi3EIKFJw9KvyowWOUAw==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "System.Buffers": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "AwarXzzoDwX6BgrhjoJsk6tUezZEozOT5Y9QKF94Gl4JK91I4PIIBkBco9068Y9/Dra8Dkbie99kXB8+1BaYKw==" + }, + "System.ComponentModel.Annotations": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "UxYQ3FGUOtzJ7LfSdnYSFd7+oEv6M8NgUatatIN2HxNtDdlcvFAf+VIq4Of9cDMJEJC0aSRv/x898RYhB4Yppg==" + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.3", + "contentHash": "3oDzvc/zzetpTKWMShs1AADwZjQ/36HnsufHRPcOjyRAAMLDlu2iD33MBI2opxnezcVUtXyqDXXjoFMOU9c7SA==", + "dependencies": { + "System.Buffers": "4.4.0", + "System.Numerics.Vectors": "4.4.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.2" + } + }, + "System.Net.WebSockets.Client.Managed": { + "type": "Transitive", + "resolved": "1.0.22", + "contentHash": "WqEOxPlXjuZrIjUtXNE9NxEfU/n5E35iV2PtoZdJSUC4tlrqwHnTee+wvMIM4OUaJWmwrymeqcgYrE0IkGAgLA==", + "dependencies": { + "System.Buffers": "4.4.0", + "System.Numerics.Vectors": "4.4.0" + } + }, + "System.Numerics.Vectors": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ==" + }, + "System.Reactive": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "4.5.3", + "contentHash": "3TIsJhD1EiiT0w2CcDMN/iSSwnNnsrnbzeVHSKkaEgV85txMprmuO+Yq2AdSbeVGcg28pdNDTPK87tJhX7VFHw==" + }, + "System.Threading.Tasks.Extensions": { + "type": "Transitive", + "resolved": "4.5.4", + "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + }, + "speckle.connectors.common": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", + "Speckle.Connectors.Logging": "[1.0.0, )", + "Speckle.Objects": "[3.1.1, )", + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" + } + }, + "speckle.connectors.dui": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", + "Speckle.Connectors.Common": "[1.0.0, )", + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" + } + }, + "speckle.connectors.dui.webview": { + "type": "Project", + "dependencies": { + "Microsoft.Web.WebView2": "[1.0.1938.49, )", + "Speckle.Connectors.DUI": "[1.0.0, )" + } + }, + "speckle.connectors.logging": { + "type": "Project" + }, + "speckle.converters.common": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", + "Speckle.Objects": "[3.1.1, )" + } + }, + "speckle.converters.navisworks2022": { + "type": "Project", + "dependencies": { + "Speckle.Connectors.DUI": "[1.0.0, )", + "Speckle.Converters.Common": "[1.0.0, )", + "Speckle.Navisworks.API": "[2022.0.0, )" + } + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "MZtBIwfDFork5vfjpJdG5g8wuJFt7d/y3LOSVVtDK/76wlbtz6cjltfKHqLx2TKVqTj5/c41t77m1+h20zqtPA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0" + } + }, + "Microsoft.Extensions.Logging": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "Nxqhadc9FCmFHzU+fz3oc8sFlE6IadViYg8dfUdGzJZ2JUxnCsRghBhhOWdM4B2zSZqEc+0BjliBh/oNdRZuig==", + "dependencies": { + "Microsoft.Extensions.Configuration.Binder": "2.2.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging.Abstractions": "2.2.0", + "Microsoft.Extensions.Options": "2.2.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" + }, + "Microsoft.Web.WebView2": { + "type": "CentralTransitive", + "requested": "[1.0.1938.49, )", + "resolved": "1.0.1938.49", + "contentHash": "z8KnFnaTYzhA/ZnyRX0qGfS1NU5ZBJeClAH64F0fVDvdDJTvME7xl6zTJ0Jlfe1BtL3C0NH9xTy64shg2baKdw==" + }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, + "Speckle.Objects": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", + "dependencies": { + "Speckle.Sdk": "3.1.1" + } + }, + "Speckle.Sdk": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", + "dependencies": { + "GraphQL.Client": "6.0.0", + "Microsoft.Bcl.AsyncInterfaces": "5.0.0", + "Microsoft.CSharp": "4.7.0", + "Microsoft.Data.Sqlite": "7.0.5", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging": "2.2.0", + "Speckle.DoubleNumerics": "4.1.0", + "Speckle.Newtonsoft.Json": "13.0.2", + "Speckle.Sdk.Dependencies": "3.1.1" + } + }, + "Speckle.Sdk.Dependencies": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" + } + }, + ".NETFramework,Version=v4.8/win-x64": { + "SQLitePCLRaw.lib.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "2C9Q9eX7CPLveJA0rIhf9RXAvu+7nWZu1A2MdG6SD/NOu26TakGgL1nsbc0JAspGijFOo3HoN79xrx8a368fBg==" + }, + "Microsoft.Web.WebView2": { + "type": "CentralTransitive", + "requested": "[1.0.1938.49, )", + "resolved": "1.0.1938.49", + "contentHash": "z8KnFnaTYzhA/ZnyRX0qGfS1NU5ZBJeClAH64F0fVDvdDJTvME7xl6zTJ0Jlfe1BtL3C0NH9xTy64shg2baKdw==" + } + } + } +} \ No newline at end of file diff --git a/Connectors/Navisworks/Speckle.Connectors.Navisworks2023/Properties/launchSettings.json b/Connectors/Navisworks/Speckle.Connectors.Navisworks2023/Properties/launchSettings.json new file mode 100644 index 000000000..2c8b4363f --- /dev/null +++ b/Connectors/Navisworks/Speckle.Connectors.Navisworks2023/Properties/launchSettings.json @@ -0,0 +1,9 @@ +{ + "profiles": { + "Speckle.Connector.Navisworks2023": { + "commandName": "Executable", + "executablePath": "C:\\Program Files\\Autodesk\\Navisworks Manage 2023\\Roamer.exe", + "commandLineArgs": " -licensing AdLM" + } + } +} diff --git a/Connectors/Navisworks/Speckle.Connectors.Navisworks2023/Speckle.Connectors.Navisworks2023.csproj b/Connectors/Navisworks/Speckle.Connectors.Navisworks2023/Speckle.Connectors.Navisworks2023.csproj new file mode 100644 index 000000000..2a5bd13c9 --- /dev/null +++ b/Connectors/Navisworks/Speckle.Connectors.Navisworks2023/Speckle.Connectors.Navisworks2023.csproj @@ -0,0 +1,27 @@ + + + net48 + x64 + win-x64 + 512 + + v20 + 2023 + + $(DefineConstants);TRACE;NAVIS2023;NAVIS + Debug;Release;Local + + + + + + + + + + + + + + + diff --git a/Connectors/Navisworks/Speckle.Connectors.Navisworks2023/packages.lock.json b/Connectors/Navisworks/Speckle.Connectors.Navisworks2023/packages.lock.json new file mode 100644 index 000000000..ff6bbf26a --- /dev/null +++ b/Connectors/Navisworks/Speckle.Connectors.Navisworks2023/packages.lock.json @@ -0,0 +1,387 @@ +{ + "version": 2, + "dependencies": { + ".NETFramework,Version=v4.8": { + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net48": "1.0.3" + } + }, + "Microsoft.SourceLink.GitHub": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "dependencies": { + "Microsoft.Build.Tasks.Git": "8.0.0", + "Microsoft.SourceLink.Common": "8.0.0" + } + }, + "PolySharp": { + "type": "Direct", + "requested": "[1.14.1, )", + "resolved": "1.14.1", + "contentHash": "mOOmFYwad3MIOL14VCjj02LljyF1GNw1wP0YVlxtcPvqdxjGGMNdNJJxHptlry3MOd8b40Flm8RPOM8JOlN2sQ==" + }, + "Speckle.InterfaceGenerator": { + "type": "Direct", + "requested": "[0.9.6, )", + "resolved": "0.9.6", + "contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w==" + }, + "Speckle.Navisworks.API": { + "type": "Direct", + "requested": "[2023.0.0, )", + "resolved": "2023.0.0", + "contentHash": "+qRmcyLD3DpuSSwX2IbEwp0gJllbiKgv313PZfEfh8I2uvxf+5YNqDzY0OGOxWIdPKqaQmdUZ1ELzoDtucCWzA==" + }, + "GraphQL.Client": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "8yPNBbuVBpTptivyAlak4GZvbwbUcjeQTL4vN1HKHRuOykZ4r7l5fcLS6vpyPyLn0x8FsL31xbOIKyxbmR9rbA==", + "dependencies": { + "GraphQL.Client.Abstractions": "6.0.0", + "GraphQL.Client.Abstractions.Websocket": "6.0.0", + "System.Net.WebSockets.Client.Managed": "1.0.22", + "System.Reactive": "5.0.0" + } + }, + "GraphQL.Client.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "h7uzWFORHZ+CCjwr/ThAyXMr0DPpzEANDa4Uo54wqCQ+j7qUKwqYTgOrb1W40sqbvNaZm9v/X7It31SUw0maHA==", + "dependencies": { + "GraphQL.Primitives": "6.0.0" + } + }, + "GraphQL.Client.Abstractions.Websocket": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "Nr9bPf8gIOvLuXpqEpqr9z9jslYFJOvd0feHth3/kPqeR3uMbjF5pjiwh4jxyMcxHdr8Pb6QiXkV3hsSyt0v7A==", + "dependencies": { + "GraphQL.Client.Abstractions": "6.0.0" + } + }, + "GraphQL.Primitives": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "yg72rrYDapfsIUrul7aF6wwNnTJBOFvuA9VdDTQpPa8AlAriHbufeXYLBcodKjfUdkCnaiggX1U/nEP08Zb5GA==" + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "W8DPQjkMScOMTtJbPwmPyj9c3zYSFGawDW3jwlBOOsnY+EzZFLgNQ/UMkK35JmkNOVPdCyPr2Tw7Vv9N+KA3ZQ==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.CSharp": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==" + }, + "Microsoft.Data.Sqlite": { + "type": "Transitive", + "resolved": "7.0.5", + "contentHash": "KGxbPeWsQMnmQy43DSBxAFtHz3l2JX8EWBSGUCvT3CuZ8KsuzbkqMIJMDOxWtG8eZSoCDI04aiVQjWuuV8HmSw==", + "dependencies": { + "Microsoft.Data.Sqlite.Core": "7.0.5", + "SQLitePCLRaw.bundle_e_sqlite3": "2.1.4" + } + }, + "Microsoft.Data.Sqlite.Core": { + "type": "Transitive", + "resolved": "7.0.5", + "contentHash": "FTerRmQPqHrCrnoUzhBu+E+1DNGwyrAMLqHkAqOOOu5pGfyMOj8qQUBxI/gDtWtG11p49UxSfWmBzRNlwZqfUg==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "nOP8R1mVb/6mZtm2qgAJXn/LFm/2kMjHDAg/QJLFG6CuWYJtaD3p1BwQhufBVvRzL9ceJ/xF0SQ0qsI2GkDQAA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "65MrmXCziWaQFrI0UHkQbesrX5wTwf9XPjY5yFm/VkgJKFJ5gqvXRoXjIZcf2wLi5ZlwGz/oMYfyURVCWbM5iw==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "vJ9xvOZCnUAIHcGC3SU35r3HKmHTVIeHzo6u/qzlHAqD8m6xv92MLin4oJntTvkpKxVX3vI1GFFkIQtU3AdlsQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "2.2.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw==" + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "UpZLNLBpIZ0GTebShui7xXYh6DmBHjWM8NxGxZbdQh/bPZ5e6YswqI+bru6BnEL5eWiOdodsXtEz3FROcgi/qg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Primitives": "2.2.0", + "System.ComponentModel.Annotations": "4.5.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "azyQtqbm4fSaDzZHD/J+V6oWMFaf2tWP4WEGIYePLCMw3+b2RQdj9ybgbQyjCshcitQKQ4lEDOZjmSlTTrHxUg==", + "dependencies": { + "System.Memory": "4.5.1", + "System.Runtime.CompilerServices.Unsafe": "4.5.1" + } + }, + "Microsoft.NETFramework.ReferenceAssemblies.net48": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "zMk4D+9zyiEWByyQ7oPImPN/Jhpj166Ky0Nlla4eXlNL8hI/BtSJsgR8Inldd4NNpIAH3oh8yym0W2DrhXdSLQ==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "Speckle.Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.2", + "contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA==" + }, + "SQLitePCLRaw.bundle_e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "EWI1olKDjFEBMJu0+3wuxwziIAdWDVMYLhuZ3Qs84rrz+DHwD00RzWPZCa+bLnHCf3oJwuFZIRsHT5p236QXww==", + "dependencies": { + "SQLitePCLRaw.lib.e_sqlite3": "2.1.4", + "SQLitePCLRaw.provider.dynamic_cdecl": "2.1.4" + } + }, + "SQLitePCLRaw.core": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "inBjvSHo9UDKneGNzfUfDjK08JzlcIhn1+SP5Y3m6cgXpCxXKCJDy6Mka7LpgSV+UZmKSnC8rTwB0SQ0xKu5pA==", + "dependencies": { + "System.Memory": "4.5.3" + } + }, + "SQLitePCLRaw.lib.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "2C9Q9eX7CPLveJA0rIhf9RXAvu+7nWZu1A2MdG6SD/NOu26TakGgL1nsbc0JAspGijFOo3HoN79xrx8a368fBg==" + }, + "SQLitePCLRaw.provider.dynamic_cdecl": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "ZsaKKhgYF9B1fvcnOGKl3EycNAwd9CRWX7v0rEfuPWhQQ5Jjpvf2VEHahiLIGHio3hxi3EIKFJw9KvyowWOUAw==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "System.Buffers": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "AwarXzzoDwX6BgrhjoJsk6tUezZEozOT5Y9QKF94Gl4JK91I4PIIBkBco9068Y9/Dra8Dkbie99kXB8+1BaYKw==" + }, + "System.ComponentModel.Annotations": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "UxYQ3FGUOtzJ7LfSdnYSFd7+oEv6M8NgUatatIN2HxNtDdlcvFAf+VIq4Of9cDMJEJC0aSRv/x898RYhB4Yppg==" + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.3", + "contentHash": "3oDzvc/zzetpTKWMShs1AADwZjQ/36HnsufHRPcOjyRAAMLDlu2iD33MBI2opxnezcVUtXyqDXXjoFMOU9c7SA==", + "dependencies": { + "System.Buffers": "4.4.0", + "System.Numerics.Vectors": "4.4.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.2" + } + }, + "System.Net.WebSockets.Client.Managed": { + "type": "Transitive", + "resolved": "1.0.22", + "contentHash": "WqEOxPlXjuZrIjUtXNE9NxEfU/n5E35iV2PtoZdJSUC4tlrqwHnTee+wvMIM4OUaJWmwrymeqcgYrE0IkGAgLA==", + "dependencies": { + "System.Buffers": "4.4.0", + "System.Numerics.Vectors": "4.4.0" + } + }, + "System.Numerics.Vectors": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ==" + }, + "System.Reactive": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "4.5.3", + "contentHash": "3TIsJhD1EiiT0w2CcDMN/iSSwnNnsrnbzeVHSKkaEgV85txMprmuO+Yq2AdSbeVGcg28pdNDTPK87tJhX7VFHw==" + }, + "System.Threading.Tasks.Extensions": { + "type": "Transitive", + "resolved": "4.5.4", + "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + }, + "speckle.connectors.common": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", + "Speckle.Connectors.Logging": "[1.0.0, )", + "Speckle.Objects": "[3.1.1, )", + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" + } + }, + "speckle.connectors.dui": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", + "Speckle.Connectors.Common": "[1.0.0, )", + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" + } + }, + "speckle.connectors.dui.webview": { + "type": "Project", + "dependencies": { + "Microsoft.Web.WebView2": "[1.0.1938.49, )", + "Speckle.Connectors.DUI": "[1.0.0, )" + } + }, + "speckle.connectors.logging": { + "type": "Project" + }, + "speckle.converters.common": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", + "Speckle.Objects": "[3.1.1, )" + } + }, + "speckle.converters.navisworks2023": { + "type": "Project", + "dependencies": { + "Speckle.Connectors.DUI": "[1.0.0, )", + "Speckle.Converters.Common": "[1.0.0, )", + "Speckle.Navisworks.API": "[2023.0.0, )" + } + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "MZtBIwfDFork5vfjpJdG5g8wuJFt7d/y3LOSVVtDK/76wlbtz6cjltfKHqLx2TKVqTj5/c41t77m1+h20zqtPA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0" + } + }, + "Microsoft.Extensions.Logging": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "Nxqhadc9FCmFHzU+fz3oc8sFlE6IadViYg8dfUdGzJZ2JUxnCsRghBhhOWdM4B2zSZqEc+0BjliBh/oNdRZuig==", + "dependencies": { + "Microsoft.Extensions.Configuration.Binder": "2.2.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging.Abstractions": "2.2.0", + "Microsoft.Extensions.Options": "2.2.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" + }, + "Microsoft.Web.WebView2": { + "type": "CentralTransitive", + "requested": "[1.0.1938.49, )", + "resolved": "1.0.1938.49", + "contentHash": "z8KnFnaTYzhA/ZnyRX0qGfS1NU5ZBJeClAH64F0fVDvdDJTvME7xl6zTJ0Jlfe1BtL3C0NH9xTy64shg2baKdw==" + }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, + "Speckle.Objects": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", + "dependencies": { + "Speckle.Sdk": "3.1.1" + } + }, + "Speckle.Sdk": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", + "dependencies": { + "GraphQL.Client": "6.0.0", + "Microsoft.Bcl.AsyncInterfaces": "5.0.0", + "Microsoft.CSharp": "4.7.0", + "Microsoft.Data.Sqlite": "7.0.5", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging": "2.2.0", + "Speckle.DoubleNumerics": "4.1.0", + "Speckle.Newtonsoft.Json": "13.0.2", + "Speckle.Sdk.Dependencies": "3.1.1" + } + }, + "Speckle.Sdk.Dependencies": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" + } + }, + ".NETFramework,Version=v4.8/win-x64": { + "SQLitePCLRaw.lib.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "2C9Q9eX7CPLveJA0rIhf9RXAvu+7nWZu1A2MdG6SD/NOu26TakGgL1nsbc0JAspGijFOo3HoN79xrx8a368fBg==" + }, + "Microsoft.Web.WebView2": { + "type": "CentralTransitive", + "requested": "[1.0.1938.49, )", + "resolved": "1.0.1938.49", + "contentHash": "z8KnFnaTYzhA/ZnyRX0qGfS1NU5ZBJeClAH64F0fVDvdDJTvME7xl6zTJ0Jlfe1BtL3C0NH9xTy64shg2baKdw==" + } + } + } +} \ No newline at end of file diff --git a/Connectors/Navisworks/Speckle.Connectors.Navisworks2024/Properties/launchSettings.json b/Connectors/Navisworks/Speckle.Connectors.Navisworks2024/Properties/launchSettings.json new file mode 100644 index 000000000..32e5dc126 --- /dev/null +++ b/Connectors/Navisworks/Speckle.Connectors.Navisworks2024/Properties/launchSettings.json @@ -0,0 +1,9 @@ +{ + "profiles": { + "Speckle.Connector.Navisworks2024": { + "commandName": "Executable", + "executablePath": "C:\\Program Files\\Autodesk\\Navisworks Manage 2024\\Roamer.exe", + "commandLineArgs": " -licensing AdLM" + } + } +} diff --git a/Connectors/Navisworks/Speckle.Connectors.Navisworks2024/Speckle.Connectors.Navisworks2024.csproj b/Connectors/Navisworks/Speckle.Connectors.Navisworks2024/Speckle.Connectors.Navisworks2024.csproj new file mode 100644 index 000000000..5f59b5173 --- /dev/null +++ b/Connectors/Navisworks/Speckle.Connectors.Navisworks2024/Speckle.Connectors.Navisworks2024.csproj @@ -0,0 +1,27 @@ + + + net48 + x64 + win-x64 + 512 + + v21 + 2024 + + $(DefineConstants);TRACE;NAVIS2024;NAVIS + Debug;Release;Local + + + + + + + + + + + + + + + diff --git a/Connectors/Navisworks/Speckle.Connectors.Navisworks2024/packages.lock.json b/Connectors/Navisworks/Speckle.Connectors.Navisworks2024/packages.lock.json new file mode 100644 index 000000000..55284d535 --- /dev/null +++ b/Connectors/Navisworks/Speckle.Connectors.Navisworks2024/packages.lock.json @@ -0,0 +1,387 @@ +{ + "version": 2, + "dependencies": { + ".NETFramework,Version=v4.8": { + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net48": "1.0.3" + } + }, + "Microsoft.SourceLink.GitHub": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "dependencies": { + "Microsoft.Build.Tasks.Git": "8.0.0", + "Microsoft.SourceLink.Common": "8.0.0" + } + }, + "PolySharp": { + "type": "Direct", + "requested": "[1.14.1, )", + "resolved": "1.14.1", + "contentHash": "mOOmFYwad3MIOL14VCjj02LljyF1GNw1wP0YVlxtcPvqdxjGGMNdNJJxHptlry3MOd8b40Flm8RPOM8JOlN2sQ==" + }, + "Speckle.InterfaceGenerator": { + "type": "Direct", + "requested": "[0.9.6, )", + "resolved": "0.9.6", + "contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w==" + }, + "Speckle.Navisworks.API": { + "type": "Direct", + "requested": "[2024.0.0, )", + "resolved": "2024.0.0", + "contentHash": "SnkvhcENMy3YLWbzy4lCweMuWdAbNzAtwvffFH2xVHmnm/2INnMGucYGazAHN496d6wgl9YRGa4qftgVsg7T7A==" + }, + "GraphQL.Client": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "8yPNBbuVBpTptivyAlak4GZvbwbUcjeQTL4vN1HKHRuOykZ4r7l5fcLS6vpyPyLn0x8FsL31xbOIKyxbmR9rbA==", + "dependencies": { + "GraphQL.Client.Abstractions": "6.0.0", + "GraphQL.Client.Abstractions.Websocket": "6.0.0", + "System.Net.WebSockets.Client.Managed": "1.0.22", + "System.Reactive": "5.0.0" + } + }, + "GraphQL.Client.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "h7uzWFORHZ+CCjwr/ThAyXMr0DPpzEANDa4Uo54wqCQ+j7qUKwqYTgOrb1W40sqbvNaZm9v/X7It31SUw0maHA==", + "dependencies": { + "GraphQL.Primitives": "6.0.0" + } + }, + "GraphQL.Client.Abstractions.Websocket": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "Nr9bPf8gIOvLuXpqEpqr9z9jslYFJOvd0feHth3/kPqeR3uMbjF5pjiwh4jxyMcxHdr8Pb6QiXkV3hsSyt0v7A==", + "dependencies": { + "GraphQL.Client.Abstractions": "6.0.0" + } + }, + "GraphQL.Primitives": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "yg72rrYDapfsIUrul7aF6wwNnTJBOFvuA9VdDTQpPa8AlAriHbufeXYLBcodKjfUdkCnaiggX1U/nEP08Zb5GA==" + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "W8DPQjkMScOMTtJbPwmPyj9c3zYSFGawDW3jwlBOOsnY+EzZFLgNQ/UMkK35JmkNOVPdCyPr2Tw7Vv9N+KA3ZQ==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.CSharp": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==" + }, + "Microsoft.Data.Sqlite": { + "type": "Transitive", + "resolved": "7.0.5", + "contentHash": "KGxbPeWsQMnmQy43DSBxAFtHz3l2JX8EWBSGUCvT3CuZ8KsuzbkqMIJMDOxWtG8eZSoCDI04aiVQjWuuV8HmSw==", + "dependencies": { + "Microsoft.Data.Sqlite.Core": "7.0.5", + "SQLitePCLRaw.bundle_e_sqlite3": "2.1.4" + } + }, + "Microsoft.Data.Sqlite.Core": { + "type": "Transitive", + "resolved": "7.0.5", + "contentHash": "FTerRmQPqHrCrnoUzhBu+E+1DNGwyrAMLqHkAqOOOu5pGfyMOj8qQUBxI/gDtWtG11p49UxSfWmBzRNlwZqfUg==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "nOP8R1mVb/6mZtm2qgAJXn/LFm/2kMjHDAg/QJLFG6CuWYJtaD3p1BwQhufBVvRzL9ceJ/xF0SQ0qsI2GkDQAA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "65MrmXCziWaQFrI0UHkQbesrX5wTwf9XPjY5yFm/VkgJKFJ5gqvXRoXjIZcf2wLi5ZlwGz/oMYfyURVCWbM5iw==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "vJ9xvOZCnUAIHcGC3SU35r3HKmHTVIeHzo6u/qzlHAqD8m6xv92MLin4oJntTvkpKxVX3vI1GFFkIQtU3AdlsQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "2.2.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw==" + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "UpZLNLBpIZ0GTebShui7xXYh6DmBHjWM8NxGxZbdQh/bPZ5e6YswqI+bru6BnEL5eWiOdodsXtEz3FROcgi/qg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Primitives": "2.2.0", + "System.ComponentModel.Annotations": "4.5.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "azyQtqbm4fSaDzZHD/J+V6oWMFaf2tWP4WEGIYePLCMw3+b2RQdj9ybgbQyjCshcitQKQ4lEDOZjmSlTTrHxUg==", + "dependencies": { + "System.Memory": "4.5.1", + "System.Runtime.CompilerServices.Unsafe": "4.5.1" + } + }, + "Microsoft.NETFramework.ReferenceAssemblies.net48": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "zMk4D+9zyiEWByyQ7oPImPN/Jhpj166Ky0Nlla4eXlNL8hI/BtSJsgR8Inldd4NNpIAH3oh8yym0W2DrhXdSLQ==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "Speckle.Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.2", + "contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA==" + }, + "SQLitePCLRaw.bundle_e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "EWI1olKDjFEBMJu0+3wuxwziIAdWDVMYLhuZ3Qs84rrz+DHwD00RzWPZCa+bLnHCf3oJwuFZIRsHT5p236QXww==", + "dependencies": { + "SQLitePCLRaw.lib.e_sqlite3": "2.1.4", + "SQLitePCLRaw.provider.dynamic_cdecl": "2.1.4" + } + }, + "SQLitePCLRaw.core": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "inBjvSHo9UDKneGNzfUfDjK08JzlcIhn1+SP5Y3m6cgXpCxXKCJDy6Mka7LpgSV+UZmKSnC8rTwB0SQ0xKu5pA==", + "dependencies": { + "System.Memory": "4.5.3" + } + }, + "SQLitePCLRaw.lib.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "2C9Q9eX7CPLveJA0rIhf9RXAvu+7nWZu1A2MdG6SD/NOu26TakGgL1nsbc0JAspGijFOo3HoN79xrx8a368fBg==" + }, + "SQLitePCLRaw.provider.dynamic_cdecl": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "ZsaKKhgYF9B1fvcnOGKl3EycNAwd9CRWX7v0rEfuPWhQQ5Jjpvf2VEHahiLIGHio3hxi3EIKFJw9KvyowWOUAw==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "System.Buffers": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "AwarXzzoDwX6BgrhjoJsk6tUezZEozOT5Y9QKF94Gl4JK91I4PIIBkBco9068Y9/Dra8Dkbie99kXB8+1BaYKw==" + }, + "System.ComponentModel.Annotations": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "UxYQ3FGUOtzJ7LfSdnYSFd7+oEv6M8NgUatatIN2HxNtDdlcvFAf+VIq4Of9cDMJEJC0aSRv/x898RYhB4Yppg==" + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.3", + "contentHash": "3oDzvc/zzetpTKWMShs1AADwZjQ/36HnsufHRPcOjyRAAMLDlu2iD33MBI2opxnezcVUtXyqDXXjoFMOU9c7SA==", + "dependencies": { + "System.Buffers": "4.4.0", + "System.Numerics.Vectors": "4.4.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.2" + } + }, + "System.Net.WebSockets.Client.Managed": { + "type": "Transitive", + "resolved": "1.0.22", + "contentHash": "WqEOxPlXjuZrIjUtXNE9NxEfU/n5E35iV2PtoZdJSUC4tlrqwHnTee+wvMIM4OUaJWmwrymeqcgYrE0IkGAgLA==", + "dependencies": { + "System.Buffers": "4.4.0", + "System.Numerics.Vectors": "4.4.0" + } + }, + "System.Numerics.Vectors": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ==" + }, + "System.Reactive": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "4.5.3", + "contentHash": "3TIsJhD1EiiT0w2CcDMN/iSSwnNnsrnbzeVHSKkaEgV85txMprmuO+Yq2AdSbeVGcg28pdNDTPK87tJhX7VFHw==" + }, + "System.Threading.Tasks.Extensions": { + "type": "Transitive", + "resolved": "4.5.4", + "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + }, + "speckle.connectors.common": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", + "Speckle.Connectors.Logging": "[1.0.0, )", + "Speckle.Objects": "[3.1.1, )", + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" + } + }, + "speckle.connectors.dui": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", + "Speckle.Connectors.Common": "[1.0.0, )", + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" + } + }, + "speckle.connectors.dui.webview": { + "type": "Project", + "dependencies": { + "Microsoft.Web.WebView2": "[1.0.1938.49, )", + "Speckle.Connectors.DUI": "[1.0.0, )" + } + }, + "speckle.connectors.logging": { + "type": "Project" + }, + "speckle.converters.common": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", + "Speckle.Objects": "[3.1.1, )" + } + }, + "speckle.converters.navisworks2024": { + "type": "Project", + "dependencies": { + "Speckle.Connectors.DUI": "[1.0.0, )", + "Speckle.Converters.Common": "[1.0.0, )", + "Speckle.Navisworks.API": "[2024.0.0, )" + } + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "MZtBIwfDFork5vfjpJdG5g8wuJFt7d/y3LOSVVtDK/76wlbtz6cjltfKHqLx2TKVqTj5/c41t77m1+h20zqtPA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0" + } + }, + "Microsoft.Extensions.Logging": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "Nxqhadc9FCmFHzU+fz3oc8sFlE6IadViYg8dfUdGzJZ2JUxnCsRghBhhOWdM4B2zSZqEc+0BjliBh/oNdRZuig==", + "dependencies": { + "Microsoft.Extensions.Configuration.Binder": "2.2.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging.Abstractions": "2.2.0", + "Microsoft.Extensions.Options": "2.2.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" + }, + "Microsoft.Web.WebView2": { + "type": "CentralTransitive", + "requested": "[1.0.1938.49, )", + "resolved": "1.0.1938.49", + "contentHash": "z8KnFnaTYzhA/ZnyRX0qGfS1NU5ZBJeClAH64F0fVDvdDJTvME7xl6zTJ0Jlfe1BtL3C0NH9xTy64shg2baKdw==" + }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, + "Speckle.Objects": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", + "dependencies": { + "Speckle.Sdk": "3.1.1" + } + }, + "Speckle.Sdk": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", + "dependencies": { + "GraphQL.Client": "6.0.0", + "Microsoft.Bcl.AsyncInterfaces": "5.0.0", + "Microsoft.CSharp": "4.7.0", + "Microsoft.Data.Sqlite": "7.0.5", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging": "2.2.0", + "Speckle.DoubleNumerics": "4.1.0", + "Speckle.Newtonsoft.Json": "13.0.2", + "Speckle.Sdk.Dependencies": "3.1.1" + } + }, + "Speckle.Sdk.Dependencies": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" + } + }, + ".NETFramework,Version=v4.8/win-x64": { + "SQLitePCLRaw.lib.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "2C9Q9eX7CPLveJA0rIhf9RXAvu+7nWZu1A2MdG6SD/NOu26TakGgL1nsbc0JAspGijFOo3HoN79xrx8a368fBg==" + }, + "Microsoft.Web.WebView2": { + "type": "CentralTransitive", + "requested": "[1.0.1938.49, )", + "resolved": "1.0.1938.49", + "contentHash": "z8KnFnaTYzhA/ZnyRX0qGfS1NU5ZBJeClAH64F0fVDvdDJTvME7xl6zTJ0Jlfe1BtL3C0NH9xTy64shg2baKdw==" + } + } + } +} \ No newline at end of file diff --git a/Connectors/Navisworks/Speckle.Connectors.Navisworks2025/Properties/launchSettings.json b/Connectors/Navisworks/Speckle.Connectors.Navisworks2025/Properties/launchSettings.json new file mode 100644 index 000000000..3b8100b12 --- /dev/null +++ b/Connectors/Navisworks/Speckle.Connectors.Navisworks2025/Properties/launchSettings.json @@ -0,0 +1,9 @@ +{ + "profiles": { + "Speckle.Connector.Navisworks2025": { + "commandName": "Executable", + "executablePath": "C:\\Program Files\\Autodesk\\Navisworks Manage 2025\\Roamer.exe", + "commandLineArgs": " -licensing AdLM" + } + } +} diff --git a/Connectors/Navisworks/Speckle.Connectors.Navisworks2025/Speckle.Connectors.Navisworks2025.csproj b/Connectors/Navisworks/Speckle.Connectors.Navisworks2025/Speckle.Connectors.Navisworks2025.csproj new file mode 100644 index 000000000..292982b18 --- /dev/null +++ b/Connectors/Navisworks/Speckle.Connectors.Navisworks2025/Speckle.Connectors.Navisworks2025.csproj @@ -0,0 +1,28 @@ + + + net48 + x64 + win-x64 + 512 + + v22 + 2025 + + $(DefineConstants);TRACE;NAVIS2025;NAVIS + Debug;Release;Local + + + + + + + + + + + + + + + + diff --git a/Connectors/Navisworks/Speckle.Connectors.Navisworks2025/packages.lock.json b/Connectors/Navisworks/Speckle.Connectors.Navisworks2025/packages.lock.json new file mode 100644 index 000000000..4089c841d --- /dev/null +++ b/Connectors/Navisworks/Speckle.Connectors.Navisworks2025/packages.lock.json @@ -0,0 +1,387 @@ +{ + "version": 2, + "dependencies": { + ".NETFramework,Version=v4.8": { + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net48": "1.0.3" + } + }, + "Microsoft.SourceLink.GitHub": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "dependencies": { + "Microsoft.Build.Tasks.Git": "8.0.0", + "Microsoft.SourceLink.Common": "8.0.0" + } + }, + "Microsoft.Web.WebView2": { + "type": "Direct", + "requested": "[1.0.2045.28, )", + "resolved": "1.0.2045.28", + "contentHash": "QDsR/HCNjzqYZrIuNLxyfmOIqVfMq1iGyoBR3OVVlFl4NZ12T8jRC0MNpOReKfgwtO5zh8g+eBYR6RAXFltcVA==" + }, + "PolySharp": { + "type": "Direct", + "requested": "[1.14.1, )", + "resolved": "1.14.1", + "contentHash": "mOOmFYwad3MIOL14VCjj02LljyF1GNw1wP0YVlxtcPvqdxjGGMNdNJJxHptlry3MOd8b40Flm8RPOM8JOlN2sQ==" + }, + "Speckle.InterfaceGenerator": { + "type": "Direct", + "requested": "[0.9.6, )", + "resolved": "0.9.6", + "contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w==" + }, + "Speckle.Navisworks.API": { + "type": "Direct", + "requested": "[2025.0.0, )", + "resolved": "2025.0.0", + "contentHash": "+q2IObnUGqtC1O/ddy2p0HKm1eXRo7Yi80oD9VIWClidvGb3rVsXKZWBHiv4HwSn5JcOMSEt1cdSlRQLm8Ehjg==" + }, + "GraphQL.Client": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "8yPNBbuVBpTptivyAlak4GZvbwbUcjeQTL4vN1HKHRuOykZ4r7l5fcLS6vpyPyLn0x8FsL31xbOIKyxbmR9rbA==", + "dependencies": { + "GraphQL.Client.Abstractions": "6.0.0", + "GraphQL.Client.Abstractions.Websocket": "6.0.0", + "System.Net.WebSockets.Client.Managed": "1.0.22", + "System.Reactive": "5.0.0" + } + }, + "GraphQL.Client.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "h7uzWFORHZ+CCjwr/ThAyXMr0DPpzEANDa4Uo54wqCQ+j7qUKwqYTgOrb1W40sqbvNaZm9v/X7It31SUw0maHA==", + "dependencies": { + "GraphQL.Primitives": "6.0.0" + } + }, + "GraphQL.Client.Abstractions.Websocket": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "Nr9bPf8gIOvLuXpqEpqr9z9jslYFJOvd0feHth3/kPqeR3uMbjF5pjiwh4jxyMcxHdr8Pb6QiXkV3hsSyt0v7A==", + "dependencies": { + "GraphQL.Client.Abstractions": "6.0.0" + } + }, + "GraphQL.Primitives": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "yg72rrYDapfsIUrul7aF6wwNnTJBOFvuA9VdDTQpPa8AlAriHbufeXYLBcodKjfUdkCnaiggX1U/nEP08Zb5GA==" + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "W8DPQjkMScOMTtJbPwmPyj9c3zYSFGawDW3jwlBOOsnY+EzZFLgNQ/UMkK35JmkNOVPdCyPr2Tw7Vv9N+KA3ZQ==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.CSharp": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==" + }, + "Microsoft.Data.Sqlite": { + "type": "Transitive", + "resolved": "7.0.5", + "contentHash": "KGxbPeWsQMnmQy43DSBxAFtHz3l2JX8EWBSGUCvT3CuZ8KsuzbkqMIJMDOxWtG8eZSoCDI04aiVQjWuuV8HmSw==", + "dependencies": { + "Microsoft.Data.Sqlite.Core": "7.0.5", + "SQLitePCLRaw.bundle_e_sqlite3": "2.1.4" + } + }, + "Microsoft.Data.Sqlite.Core": { + "type": "Transitive", + "resolved": "7.0.5", + "contentHash": "FTerRmQPqHrCrnoUzhBu+E+1DNGwyrAMLqHkAqOOOu5pGfyMOj8qQUBxI/gDtWtG11p49UxSfWmBzRNlwZqfUg==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "nOP8R1mVb/6mZtm2qgAJXn/LFm/2kMjHDAg/QJLFG6CuWYJtaD3p1BwQhufBVvRzL9ceJ/xF0SQ0qsI2GkDQAA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "65MrmXCziWaQFrI0UHkQbesrX5wTwf9XPjY5yFm/VkgJKFJ5gqvXRoXjIZcf2wLi5ZlwGz/oMYfyURVCWbM5iw==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "vJ9xvOZCnUAIHcGC3SU35r3HKmHTVIeHzo6u/qzlHAqD8m6xv92MLin4oJntTvkpKxVX3vI1GFFkIQtU3AdlsQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "2.2.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw==" + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "UpZLNLBpIZ0GTebShui7xXYh6DmBHjWM8NxGxZbdQh/bPZ5e6YswqI+bru6BnEL5eWiOdodsXtEz3FROcgi/qg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Primitives": "2.2.0", + "System.ComponentModel.Annotations": "4.5.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "azyQtqbm4fSaDzZHD/J+V6oWMFaf2tWP4WEGIYePLCMw3+b2RQdj9ybgbQyjCshcitQKQ4lEDOZjmSlTTrHxUg==", + "dependencies": { + "System.Memory": "4.5.1", + "System.Runtime.CompilerServices.Unsafe": "4.5.1" + } + }, + "Microsoft.NETFramework.ReferenceAssemblies.net48": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "zMk4D+9zyiEWByyQ7oPImPN/Jhpj166Ky0Nlla4eXlNL8hI/BtSJsgR8Inldd4NNpIAH3oh8yym0W2DrhXdSLQ==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "Speckle.Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.2", + "contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA==" + }, + "SQLitePCLRaw.bundle_e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "EWI1olKDjFEBMJu0+3wuxwziIAdWDVMYLhuZ3Qs84rrz+DHwD00RzWPZCa+bLnHCf3oJwuFZIRsHT5p236QXww==", + "dependencies": { + "SQLitePCLRaw.lib.e_sqlite3": "2.1.4", + "SQLitePCLRaw.provider.dynamic_cdecl": "2.1.4" + } + }, + "SQLitePCLRaw.core": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "inBjvSHo9UDKneGNzfUfDjK08JzlcIhn1+SP5Y3m6cgXpCxXKCJDy6Mka7LpgSV+UZmKSnC8rTwB0SQ0xKu5pA==", + "dependencies": { + "System.Memory": "4.5.3" + } + }, + "SQLitePCLRaw.lib.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "2C9Q9eX7CPLveJA0rIhf9RXAvu+7nWZu1A2MdG6SD/NOu26TakGgL1nsbc0JAspGijFOo3HoN79xrx8a368fBg==" + }, + "SQLitePCLRaw.provider.dynamic_cdecl": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "ZsaKKhgYF9B1fvcnOGKl3EycNAwd9CRWX7v0rEfuPWhQQ5Jjpvf2VEHahiLIGHio3hxi3EIKFJw9KvyowWOUAw==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "System.Buffers": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "AwarXzzoDwX6BgrhjoJsk6tUezZEozOT5Y9QKF94Gl4JK91I4PIIBkBco9068Y9/Dra8Dkbie99kXB8+1BaYKw==" + }, + "System.ComponentModel.Annotations": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "UxYQ3FGUOtzJ7LfSdnYSFd7+oEv6M8NgUatatIN2HxNtDdlcvFAf+VIq4Of9cDMJEJC0aSRv/x898RYhB4Yppg==" + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.3", + "contentHash": "3oDzvc/zzetpTKWMShs1AADwZjQ/36HnsufHRPcOjyRAAMLDlu2iD33MBI2opxnezcVUtXyqDXXjoFMOU9c7SA==", + "dependencies": { + "System.Buffers": "4.4.0", + "System.Numerics.Vectors": "4.4.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.2" + } + }, + "System.Net.WebSockets.Client.Managed": { + "type": "Transitive", + "resolved": "1.0.22", + "contentHash": "WqEOxPlXjuZrIjUtXNE9NxEfU/n5E35iV2PtoZdJSUC4tlrqwHnTee+wvMIM4OUaJWmwrymeqcgYrE0IkGAgLA==", + "dependencies": { + "System.Buffers": "4.4.0", + "System.Numerics.Vectors": "4.4.0" + } + }, + "System.Numerics.Vectors": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ==" + }, + "System.Reactive": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "4.5.3", + "contentHash": "3TIsJhD1EiiT0w2CcDMN/iSSwnNnsrnbzeVHSKkaEgV85txMprmuO+Yq2AdSbeVGcg28pdNDTPK87tJhX7VFHw==" + }, + "System.Threading.Tasks.Extensions": { + "type": "Transitive", + "resolved": "4.5.4", + "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + }, + "speckle.connectors.common": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", + "Speckle.Connectors.Logging": "[1.0.0, )", + "Speckle.Objects": "[3.1.1, )", + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" + } + }, + "speckle.connectors.dui": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", + "Speckle.Connectors.Common": "[1.0.0, )", + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" + } + }, + "speckle.connectors.dui.webview": { + "type": "Project", + "dependencies": { + "Microsoft.Web.WebView2": "[1.0.1938.49, )", + "Speckle.Connectors.DUI": "[1.0.0, )" + } + }, + "speckle.connectors.logging": { + "type": "Project" + }, + "speckle.converters.common": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", + "Speckle.Objects": "[3.1.1, )" + } + }, + "speckle.converters.navisworks2025": { + "type": "Project", + "dependencies": { + "Speckle.Connectors.DUI": "[1.0.0, )", + "Speckle.Converters.Common": "[1.0.0, )", + "Speckle.Navisworks.API": "[2025.0.0, )" + } + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "MZtBIwfDFork5vfjpJdG5g8wuJFt7d/y3LOSVVtDK/76wlbtz6cjltfKHqLx2TKVqTj5/c41t77m1+h20zqtPA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0" + } + }, + "Microsoft.Extensions.Logging": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "Nxqhadc9FCmFHzU+fz3oc8sFlE6IadViYg8dfUdGzJZ2JUxnCsRghBhhOWdM4B2zSZqEc+0BjliBh/oNdRZuig==", + "dependencies": { + "Microsoft.Extensions.Configuration.Binder": "2.2.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging.Abstractions": "2.2.0", + "Microsoft.Extensions.Options": "2.2.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" + }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, + "Speckle.Objects": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", + "dependencies": { + "Speckle.Sdk": "3.1.1" + } + }, + "Speckle.Sdk": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", + "dependencies": { + "GraphQL.Client": "6.0.0", + "Microsoft.Bcl.AsyncInterfaces": "5.0.0", + "Microsoft.CSharp": "4.7.0", + "Microsoft.Data.Sqlite": "7.0.5", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging": "2.2.0", + "Speckle.DoubleNumerics": "4.1.0", + "Speckle.Newtonsoft.Json": "13.0.2", + "Speckle.Sdk.Dependencies": "3.1.1" + } + }, + "Speckle.Sdk.Dependencies": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" + } + }, + ".NETFramework,Version=v4.8/win-x64": { + "Microsoft.Web.WebView2": { + "type": "Direct", + "requested": "[1.0.2045.28, )", + "resolved": "1.0.2045.28", + "contentHash": "QDsR/HCNjzqYZrIuNLxyfmOIqVfMq1iGyoBR3OVVlFl4NZ12T8jRC0MNpOReKfgwtO5zh8g+eBYR6RAXFltcVA==" + }, + "SQLitePCLRaw.lib.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "2C9Q9eX7CPLveJA0rIhf9RXAvu+7nWZu1A2MdG6SD/NOu26TakGgL1nsbc0JAspGijFOo3HoN79xrx8a368fBg==" + } + } + } +} \ No newline at end of file diff --git a/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Bindings/NavisworksBasicConnectorBinding.cs b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Bindings/NavisworksBasicConnectorBinding.cs new file mode 100644 index 000000000..9aa3f531d --- /dev/null +++ b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Bindings/NavisworksBasicConnectorBinding.cs @@ -0,0 +1,58 @@ +using Speckle.Connectors.DUI.Bindings; +using Speckle.Connectors.DUI.Bridge; +using Speckle.Connectors.DUI.Models; +using Speckle.Connectors.DUI.Models.Card; +using Speckle.Sdk; + +namespace Speckle.Connector.Navisworks.Bindings; + +public class NavisworksBasicConnectorBinding : IBasicConnectorBinding +{ + public string Name => "baseBinding"; + public IBrowserBridge Parent { get; } + public BasicConnectorBindingCommands Commands { get; } + + private readonly DocumentModelStore _store; + private readonly ISpeckleApplication _speckleApplication; + + public NavisworksBasicConnectorBinding( + IBrowserBridge parent, + DocumentModelStore store, + ISpeckleApplication speckleApplication + ) + { + Parent = parent; + _store = store; + _speckleApplication = speckleApplication; + Commands = new BasicConnectorBindingCommands(parent); + } + + public string GetSourceApplicationName() => _speckleApplication.Slug; + + public string GetSourceApplicationVersion() => _speckleApplication.HostApplicationVersion; + + public string GetConnectorVersion() => _speckleApplication.SpeckleVersion; + + public DocumentInfo? GetDocumentInfo() => + NavisworksApp.ActiveDocument is null || NavisworksApp.ActiveDocument.Models.Count == 0 + ? null + : new DocumentInfo( + NavisworksApp.ActiveDocument.CurrentFileName, + NavisworksApp.ActiveDocument.Title, + NavisworksApp.ActiveDocument.GetHashCode().ToString() + ); + + public DocumentModelStore GetDocumentState() => _store; + + public void AddModel(ModelCard model) => _store.AddModel(model); + + public void UpdateModel(ModelCard model) => _store.UpdateModel(model); + + public void RemoveModel(ModelCard model) => _store.RemoveModel(model); + + public Task HighlightModel(string modelCardId) => Task.CompletedTask; + + public async Task HighlightObjects(IReadOnlyList objectIds) => + // TODO: Implement highlighting logic on main thread + await Task.CompletedTask; +} diff --git a/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Bindings/NavisworksSelectionBinding.cs b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Bindings/NavisworksSelectionBinding.cs new file mode 100644 index 000000000..8665f6851 --- /dev/null +++ b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Bindings/NavisworksSelectionBinding.cs @@ -0,0 +1,59 @@ +using Speckle.Connector.Navisworks.Services; +using Speckle.Connectors.DUI.Bindings; +using Speckle.Connectors.DUI.Bridge; + +namespace Speckle.Connector.Navisworks.Bindings; + +public class NavisworksSelectionBinding : ISelectionBinding +{ + private readonly IAppIdleManager _idleManager; + private readonly IElementSelectionService _selectionService; + private const string SELECTION_EVENT = "setSelection"; + public string Name { get; } = "selectionBinding"; + public IBrowserBridge Parent { get; } + + public NavisworksSelectionBinding( + IAppIdleManager idleManager, + IBrowserBridge parent, + IElementSelectionService selectionService + ) + { + _idleManager = idleManager; + _selectionService = selectionService; + Parent = parent; + + NavisworksApp.ActiveDocument.CurrentSelection.Changed += OnSelectionChange; + } + + private void OnSelectionChange(object? o, EventArgs eventArgs) => + _idleManager.SubscribeToIdle(nameof(UpdateSelectionAsync), async () => await UpdateSelectionAsync()); + + private async Task UpdateSelectionAsync() + { + var selInfo = GetSelection(); + await Parent.Send(SELECTION_EVENT, selInfo); + } + + public SelectionInfo GetSelection() + { + // Ensure there is an active document and a valid selection + var activeDocument = NavisworksApp.ActiveDocument; + if (activeDocument == null || activeDocument.CurrentSelection.SelectedItems.IsEmpty) + { + // Return an empty list if no valid selection exists + return new SelectionInfo([], "No selection available"); + } + + // Ensure only visible elements are processed by filtering using IsElementVisible + var selectedObjectsIds = new HashSet( + activeDocument + .CurrentSelection.SelectedItems.Where(_selectionService.IsVisible) // Exclude hidden elements + .Select(_selectionService.GetModelItemPath) // Resolve to index paths + ); + + return new SelectionInfo( + selectedObjectsIds, + $"{selectedObjectsIds.Count} object{(selectedObjectsIds.Count != 1 ? "s" : "")}" + ); + } +} diff --git a/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Bindings/NavisworksSendBinding.cs b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Bindings/NavisworksSendBinding.cs new file mode 100644 index 000000000..db3458050 --- /dev/null +++ b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Bindings/NavisworksSendBinding.cs @@ -0,0 +1,199 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Speckle.Connector.Navisworks.Operations.Send.Filters; +using Speckle.Connector.Navisworks.Operations.Send.Settings; +using Speckle.Connector.Navisworks.Services; +using Speckle.Connectors.Common.Cancellation; +using Speckle.Connectors.Common.Operations; +using Speckle.Connectors.Common.Threading; +using Speckle.Connectors.DUI.Bindings; +using Speckle.Connectors.DUI.Bridge; +using Speckle.Connectors.DUI.Exceptions; +using Speckle.Connectors.DUI.Logging; +using Speckle.Connectors.DUI.Models; +using Speckle.Connectors.DUI.Models.Card; +using Speckle.Connectors.DUI.Models.Card.SendFilter; +using Speckle.Connectors.DUI.Settings; +using Speckle.Converter.Navisworks.Settings; +using Speckle.Converters.Common; +using Speckle.Sdk; +using Speckle.Sdk.Common; +using Speckle.Sdk.Logging; + +namespace Speckle.Connector.Navisworks.Bindings; + +public class NavisworksSendBinding : ISendBinding +{ + public string Name => "sendBinding"; + public IBrowserBridge Parent { get; } + + public SendBindingUICommands Commands { get; } + + private readonly DocumentModelStore _store; + private readonly IServiceProvider _serviceProvider; + private readonly ICancellationManager _cancellationManager; + private readonly IOperationProgressManager _operationProgressManager; + private readonly ILogger _logger; + private readonly ISpeckleApplication _speckleApplication; + private readonly ISdkActivityFactory _activityFactory; + private readonly INavisworksConversionSettingsFactory _conversionSettingsFactory; + private readonly ToSpeckleSettingsManagerNavisworks _toSpeckleSettingsManagerNavisworks; + private readonly IElementSelectionService _selectionService; + private readonly IThreadContext _threadContext; + + public NavisworksSendBinding( + DocumentModelStore store, + IBrowserBridge parent, + IServiceProvider serviceProvider, + ICancellationManager cancellationManager, + IOperationProgressManager operationProgressManager, + ILogger logger, + ISpeckleApplication speckleApplication, + ISdkActivityFactory activityFactory, + INavisworksConversionSettingsFactory conversionSettingsFactory, + ToSpeckleSettingsManagerNavisworks toSpeckleSettingsManagerNavisworks, + IElementSelectionService selectionService, + IThreadContext threadContext + ) + { + Parent = parent; + Commands = new SendBindingUICommands(parent); + _store = store; + _serviceProvider = serviceProvider; + _cancellationManager = cancellationManager; + _operationProgressManager = operationProgressManager; + _logger = logger; + _speckleApplication = speckleApplication; + _activityFactory = activityFactory; + _conversionSettingsFactory = conversionSettingsFactory; + _toSpeckleSettingsManagerNavisworks = toSpeckleSettingsManagerNavisworks; + _selectionService = selectionService; + _threadContext = threadContext; + SubscribeToNavisworksEvents(); + } + + private static void SubscribeToNavisworksEvents() { } + + // Do not change the behavior/scope of this class on send binding unless make sure the behavior is same. Otherwise, we might not be able to update list of saved sets. + public List GetSendFilters() => + [ + new NavisworksSelectionFilter() { IsDefault = true }, + new NavisworksSavedSetsFilter(new ElementSelectionService()) + ]; + + public List GetSendSettings() => + [ + new VisualRepresentationSetting(RepresentationMode.Active), + new OriginModeSetting(OriginMode.ModelOrigin), + new IncludeInternalPropertiesSetting(false), + new ConvertHiddenElementsSetting(false), + new PreserveModelHierarchySetting(false), + ]; + + public async Task Send(string modelCardId) => + await _threadContext.RunOnMainAsync(async () => await SendInternal(modelCardId)); + + private async Task SendInternal(string modelCardId) + { + using var activity = _activityFactory.Start(); + try + { + var modelCard = GetModelCard(modelCardId); + + using var scope = _serviceProvider.CreateScope(); + + InitializeConverterSettings(scope, modelCard); + + using var cancellationItem = _cancellationManager.GetCancellationItem(modelCardId); + + var navisworksModelItems = GetNavisworksModelItems(modelCard); + + var sendResult = await ExecuteSendOperation(scope, modelCard, navisworksModelItems, cancellationItem.Token); + + await Commands.SetModelSendResult(modelCardId, sendResult.RootObjId, sendResult.ConversionResults); + } + catch (OperationCanceledException) + { + // SWALLOW -> UI handles it immediately, so we do not need to handle anything for now! + // Idea for later -> when cancel called, create promise from UI to solve it later with this catch block. + // So have 3 state on UI -> Cancellation clicked -> Cancelling -> Cancelled + } + catch (Exception ex) when (!ex.IsFatal()) // UX reasons - we will report operation exceptions as model card error. We may change this later when we have more exception documentation + { + _logger.LogModelCardHandledError(ex); + await Commands.SetModelError(modelCardId, ex); + } + } + + private SenderModelCard GetModelCard(string modelCardId) => + _store.GetModelById(modelCardId) is not SenderModelCard modelCard + ? throw new InvalidOperationException("No publish model card was found.") + : modelCard; + + private void InitializeConverterSettings(IServiceScope scope, SenderModelCard modelCard) => + scope + .ServiceProvider.GetRequiredService>() + .Initialize( + _conversionSettingsFactory.Create( + originMode: _toSpeckleSettingsManagerNavisworks.GetOriginMode(modelCard), + visualRepresentationMode: _toSpeckleSettingsManagerNavisworks.GetVisualRepresentationMode(modelCard), + convertHiddenElements: _toSpeckleSettingsManagerNavisworks.GetConvertHiddenElements(modelCard), + includeInternalProperties: _toSpeckleSettingsManagerNavisworks.GetIncludeInternalProperties(modelCard), + preserveModelHierarchy: _toSpeckleSettingsManagerNavisworks.GetPreserveModelHierarchy(modelCard) + ) + ); + + private List GetNavisworksModelItems(SenderModelCard modelCard) + { + var selectedPaths = modelCard.SendFilter.NotNull().RefreshObjectIds(); + var convertHiddenElementsSetting = + modelCard.Settings!.FirstOrDefault(s => s.Id == "convertHiddenElements")?.Value as bool? ?? false; + var message = convertHiddenElementsSetting + ? "No visible objects were found to convert. Please update your publish filter!" + : "No objects were found to convert. Please update your publish filter, or check items are visible!"; + + if (selectedPaths.Count == 0) + { + throw new SpeckleSendFilterException(message); + } + + var modelItems = selectedPaths + .Select(_selectionService.GetModelItemFromPath) + .SelectMany(_selectionService.GetGeometryNodes) + .Where(_selectionService.IsVisible) + .ToList(); + + return modelItems.Count == 0 ? throw new SpeckleSendFilterException(message) : modelItems; + } + + private async Task ExecuteSendOperation( + IServiceScope scope, + SenderModelCard modelCard, + List navisworksModelItems, + CancellationToken token + ) => + await scope + .ServiceProvider.GetRequiredService>() + .Execute( + navisworksModelItems, + modelCard.GetSendInfo(_speckleApplication.ApplicationAndVersion), + _operationProgressManager.CreateOperationProgressEventHandler(Parent, modelCard.ModelCardId.NotNull(), token), + token + ); + + public void CancelSend(string modelCardId) => _cancellationManager.CancelOperation(modelCardId); + + /// + /// Cancels all outstanding send operations for the current document. + /// This method is called when the active document changes, to ensure + /// that any in-progress send operations are properly canceled before + /// the new document is loaded. + /// + public void CancelAllSendOperations() + { + foreach (var modelCardId in _store.GetSenders().Select(m => m.ModelCardId)) + { + CancelSend(modelCardId ?? string.Empty); + } + } +} diff --git a/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/DependencyInjection/NavisworksConnectorServiceRegistration.cs b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/DependencyInjection/NavisworksConnectorServiceRegistration.cs new file mode 100644 index 000000000..2d593318b --- /dev/null +++ b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/DependencyInjection/NavisworksConnectorServiceRegistration.cs @@ -0,0 +1,73 @@ +using Microsoft.Extensions.DependencyInjection; +using Speckle.Connector.Navisworks.Bindings; +using Speckle.Connector.Navisworks.HostApp; +using Speckle.Connector.Navisworks.Operations.Send; +using Speckle.Connector.Navisworks.Operations.Send.Filters; +using Speckle.Connector.Navisworks.Operations.Send.Settings; +using Speckle.Connector.Navisworks.Services; +using Speckle.Connectors.Common; +using Speckle.Connectors.Common.Builders; +using Speckle.Connectors.Common.Operations; +using Speckle.Connectors.Common.Threading; +using Speckle.Connectors.DUI; +using Speckle.Connectors.DUI.Bindings; +using Speckle.Connectors.DUI.Bridge; +using Speckle.Connectors.DUI.Models; +using Speckle.Connectors.DUI.Models.Card.SendFilter; +using Speckle.Connectors.DUI.WebView; +using Speckle.Converter.Navisworks.Settings; +using Speckle.Converters.Common; +using Speckle.Sdk.Models.GraphTraversal; + +namespace Speckle.Connector.Navisworks.DependencyInjection; + +public static class NavisworksConnectorServiceRegistration +{ + public static void AddNavisworks(this IServiceCollection serviceCollection) + { + // Register Core functionality + serviceCollection.AddConnectorUtils(); + serviceCollection.AddDUI(); + serviceCollection.AddDUIView(); + + // Register bindings + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + + // Register Navisworks specific binding + serviceCollection.AddSingleton(sp => sp.GetRequiredService()); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + + // Conversion settings + serviceCollection.AddSingleton(); + serviceCollection.AddScoped< + IConverterSettingsStore, + ConverterSettingsStore + >(); + + serviceCollection.AddScoped(); + serviceCollection.AddScoped(); + + serviceCollection.AddSingleton(); + + // Sending operations + serviceCollection.AddScoped, NavisworksRootObjectBuilder>(); + serviceCollection.AddScoped>(); + serviceCollection.AddSingleton(DefaultTraversal.CreateTraversalFunc()); + serviceCollection.AddSingleton(); + + // Register Intercom/interop + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(sp => sp.GetRequiredService()); + serviceCollection.AddSingleton(); + + // register filters + serviceCollection.AddScoped(); + serviceCollection.AddScoped(); + serviceCollection.AddScoped(); + } +} diff --git a/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Extensions/ElementSelectionService.cs b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Extensions/ElementSelectionService.cs new file mode 100644 index 000000000..4399b947a --- /dev/null +++ b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Extensions/ElementSelectionService.cs @@ -0,0 +1,23 @@ +using static Speckle.Converter.Navisworks.Helpers.ElementSelectionHelper; + +namespace Speckle.Connector.Navisworks.Services; + +public interface IElementSelectionService +{ + string GetModelItemPath(NAV.ModelItem modelItem); + NAV.ModelItem GetModelItemFromPath(string path); + bool IsVisible(NAV.ModelItem modelItem); + IReadOnlyCollection GetGeometryNodes(NAV.ModelItem modelItem); +} + +public class ElementSelectionService : IElementSelectionService +{ + public string GetModelItemPath(NAV.ModelItem modelItem) => ResolveModelItemToIndexPath(modelItem); + + public NAV.ModelItem GetModelItemFromPath(string path) => ResolveIndexPathToModelItem(path); + + public bool IsVisible(NAV.ModelItem modelItem) => IsElementVisible(modelItem); + + public IReadOnlyCollection GetGeometryNodes(NAV.ModelItem modelItem) => + ResolveGeometryLeafNodes(modelItem); +} diff --git a/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/GlobalUsing.cs b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/GlobalUsing.cs new file mode 100644 index 000000000..8b9816d3e --- /dev/null +++ b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/GlobalUsing.cs @@ -0,0 +1,2 @@ +global using NAV = Autodesk.Navisworks.Api; +global using NavisworksApp = Autodesk.Navisworks.Api.Application; diff --git a/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/HostApp/NavisworksColorUnpacker.cs b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/HostApp/NavisworksColorUnpacker.cs new file mode 100644 index 000000000..50d6c2ee7 --- /dev/null +++ b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/HostApp/NavisworksColorUnpacker.cs @@ -0,0 +1,170 @@ +using Microsoft.Extensions.Logging; +using Speckle.Connector.Navisworks.Services; +using Speckle.Converter.Navisworks.Helpers; +using Speckle.Converter.Navisworks.Settings; +using Speckle.Converters.Common; +using Speckle.Sdk; +using Speckle.Sdk.Models.Proxies; +using ComApi = Autodesk.Navisworks.Api.Interop.ComApi; +using ComBridge = Autodesk.Navisworks.Api.ComApi.ComApiBridge; + +namespace Speckle.Connector.Navisworks.HostApp; + +public class NavisworksColorUnpacker( + ILogger logger, + IConverterSettingsStore converterSettings, + IElementSelectionService selectionService +) +{ + private static T Select(RepresentationMode mode, T active, T permanent, T original, T defaultValue) => + mode switch + { + RepresentationMode.Active => active, + RepresentationMode.Permanent => permanent, + RepresentationMode.Original => original, + _ => defaultValue, + }; + + internal List UnpackColor( + IReadOnlyList navisworksObjects, + Dictionary> groupedNodes + ) + { + if (navisworksObjects == null) + { + throw new ArgumentNullException(nameof(navisworksObjects)); + } + + if (groupedNodes == null) + { + throw new ArgumentNullException(nameof(groupedNodes)); + } + + Dictionary colorProxies = []; + Dictionary mergedIds = []; + + foreach (var group in groupedNodes) + { + string groupKey = group.Key; + + foreach (var nodePath in group.Value.Select(selectionService.GetModelItemPath)) + { + mergedIds[nodePath] = groupKey; + } + } + + foreach (NAV.ModelItem navisworksObject in navisworksObjects) + { + try + { + if (!Is2DElement(navisworksObject)) + { + continue; + } + + var navisworksObjectId = selectionService.GetModelItemPath(navisworksObject); + + var finalId = mergedIds.TryGetValue(navisworksObjectId, out var mergedId) ? mergedId : navisworksObjectId; + + var geometry = navisworksObject.Geometry; + var mode = converterSettings.Current.User.VisualRepresentationMode; + + using var defaultColor = new NAV.Color(1.0, 1.0, 1.0); + + var representationColor = Select( + mode, + geometry.ActiveColor, + geometry.PermanentColor, + geometry.OriginalColor, + defaultColor + ); + var colorId = Select( + mode, + $"{geometry.ActiveColor.GetHashCode()}_{geometry.ActiveTransparency}".GetHashCode(), + $"{geometry.PermanentColor.GetHashCode()}_{geometry.PermanentTransparency}".GetHashCode(), + $"{geometry.OriginalColor.GetHashCode()}_{geometry.OriginalTransparency}".GetHashCode(), + 0 + ); + + var colorName = ColorConverter.NavisworksColorToColor(representationColor).Name; + + if (colorProxies.TryGetValue(colorId.ToString(), out ColorProxy? colorProxy)) + { + colorProxy.objects.Add(finalId); + } + else + { + colorProxies[colorId.ToString()] = new ColorProxy + { + value = ColorConverter.NavisworksColorToColor(representationColor).ToArgb(), + name = colorName, + applicationId = colorId.ToString(), + objects = [finalId] + }; + } + } + catch (Exception ex) when (!ex.IsFatal()) + { + logger.LogError(ex, "Failed to unpack color for Navisworks object"); + } + } + + return colorProxies.Values.ToList(); + } + + private static bool Is2DElement(NAV.ModelItem modelItem) + { + if (!modelItem.HasGeometry) + { + return false; + } + + var primitiveChecker = new PrimitiveChecker(); + + var comSelection = ComBridge.ToInwOpSelection([modelItem]); + try + { + foreach (ComApi.InwOaPath path in comSelection.Paths()) + { + GC.KeepAlive(path); + + foreach (ComApi.InwOaFragment3 fragment in path.Fragments()) + { + GC.KeepAlive(fragment); + + fragment.GenerateSimplePrimitives(ComApi.nwEVertexProperty.eNORMAL, primitiveChecker); + + // Exit early if triangles are found + if (primitiveChecker.HasTriangles) + { + return false; + } + } + } + + // Return true if any 2D primitives are found + return primitiveChecker.HasLines || primitiveChecker.HasPoints || primitiveChecker.HasSnapPoints; + } + finally + { + System.Runtime.InteropServices.Marshal.ReleaseComObject(comSelection); + } + } +} + +public class PrimitiveChecker : ComApi.InwSimplePrimitivesCB +{ + public bool HasTriangles { get; private set; } + public bool HasLines { get; private set; } + public bool HasPoints { get; private set; } + public bool HasSnapPoints { get; private set; } + + public void Line(ComApi.InwSimpleVertex v1, ComApi.InwSimpleVertex v2) => HasLines = true; + + public void Point(ComApi.InwSimpleVertex v1) => HasPoints = true; + + public void SnapPoint(ComApi.InwSimpleVertex v1) => HasSnapPoints = true; + + public void Triangle(ComApi.InwSimpleVertex v1, ComApi.InwSimpleVertex v2, ComApi.InwSimpleVertex v3) => + HasTriangles = true; +} diff --git a/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/HostApp/NavisworksDocumentEvents.cs b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/HostApp/NavisworksDocumentEvents.cs new file mode 100644 index 000000000..04255e6f1 --- /dev/null +++ b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/HostApp/NavisworksDocumentEvents.cs @@ -0,0 +1,145 @@ +using Microsoft.Extensions.DependencyInjection; +using Speckle.Connector.Navisworks.Bindings; +using Speckle.Connectors.DUI.Bindings; +using Speckle.Connectors.DUI.Bridge; + +namespace Speckle.Connector.Navisworks.HostApp; + +/// +/// Manages document and model state change notifications for the Navisworks connector. +/// Coalesces various document events into batched updates to be processed during idle time. +/// +public sealed class NavisworksDocumentEvents +{ + private readonly IServiceProvider _serviceProvider; + private readonly ITopLevelExceptionHandler _topLevelExceptionHandler; + private readonly IAppIdleManager _idleManager; + private readonly object _subscriptionLock = new(); + + private bool _isSubscribed; + private bool _isProcessing; + + private int _priorModelCount; + private int _finalModelCount; + + /// + /// Initializes a new instance of the class and subscribes to document events. + /// + /// The service provider for dependency injection. + public NavisworksDocumentEvents( + IServiceProvider serviceProvider, + ITopLevelExceptionHandler topLevelExceptionHandler, + IAppIdleManager idleManager + ) + { + _serviceProvider = serviceProvider; + _topLevelExceptionHandler = topLevelExceptionHandler; + _idleManager = idleManager; + + SubscribeToDocumentModelEvents(); + } + + /// + /// Subscribes to document-level events to monitor model state changes. + /// + private void SubscribeToDocumentModelEvents() + { + lock (_subscriptionLock) + { + if (_isSubscribed) + { + return; + } + + var activeDocument = NavisworksApp.ActiveDocument; + if (activeDocument != null) + { + activeDocument.Models.CollectionChanged += HandleDocumentModelCountChanged; + activeDocument.Models.CollectionChanging += HandleDocumentModelCountChanging; + } + + _isSubscribed = true; + } + } + + /// + /// Tracks the current model count before changes occur. + /// + private void HandleDocumentModelCountChanging(object sender, EventArgs e) => + _priorModelCount = ((NAV.Document)sender).Models.Count; + + /// + /// Schedules processing of model count changes during idle time. + /// + private void HandleDocumentModelCountChanged(object sender, EventArgs e) + { + _finalModelCount = ((NAV.Document)sender).Models.Count; + + _topLevelExceptionHandler.CatchUnhandled( + () => + _idleManager.SubscribeToIdle( + nameof(ProcessModelStateChangeAsync), + async () => await ProcessModelStateChangeAsync() + ) + ); + } + + private async Task ProcessModelStateChangeAsync() + { + if (_isProcessing) + { + return; + } + + _isProcessing = true; + + try + { + var store = _serviceProvider.GetRequiredService(); + var basicBinding = _serviceProvider.GetRequiredService(); + var commands = (basicBinding as NavisworksBasicConnectorBinding)?.Commands; + + switch (_finalModelCount) + { + case 0 when _priorModelCount > 0: + store.ClearAndSave(); + break; + case > 0 when _priorModelCount == 0: + store.ReloadState(); + break; + } + + if (commands != null) + { + await commands.NotifyDocumentChanged(); + } + } + finally + { + _isProcessing = false; + } + } + + private void UnsubscribeFromDocumentModelEvents(object _) + { + var activeDocument = NavisworksApp.ActiveDocument; + if (activeDocument != null) + { + UnsubscribeFromModelEvents(activeDocument); + } + + _isSubscribed = false; + } + + private void UnsubscribeFromModelEvents(NAV.Document document) + { + document.Models.CollectionChanged -= HandleDocumentModelCountChanged; + document.Models.CollectionChanging -= HandleDocumentModelCountChanging; + + var sendBinding = _serviceProvider + .GetRequiredService>() + .OfType() + .First(); + sendBinding.CancelAllSendOperations(); + } +} diff --git a/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/HostApp/NavisworksDocumentModelStore.cs b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/HostApp/NavisworksDocumentModelStore.cs new file mode 100644 index 000000000..5763ac5d6 --- /dev/null +++ b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/HostApp/NavisworksDocumentModelStore.cs @@ -0,0 +1,188 @@ +using System.Data; +using Speckle.Connectors.DUI.Bridge; +using Speckle.Connectors.DUI.Models; +using Speckle.Connectors.DUI.Utils; +using Database = Autodesk.Navisworks.Api.DocumentParts.DocumentDatabase; + +namespace Speckle.Connector.Navisworks.HostApp; + +/// +/// Manages persistence of Speckle model states within Navisworks' embedded SQLite database. +/// Provides mechanisms for reliable read/write operations with retry handling and validation. +/// +public sealed class NavisworksDocumentModelStore : DocumentModelStore +{ + private const string TABLE_NAME = "speckle"; + private const string KEY_NAME = "Speckle_DUI3"; + + private readonly ITopLevelExceptionHandler _topLevelExceptionHandler; + private string _lastSavedState = string.Empty; + + public NavisworksDocumentModelStore( + IJsonSerializer jsonSerializer, + ITopLevelExceptionHandler topLevelExceptionHandler + ) + : base(jsonSerializer) + { + _topLevelExceptionHandler = topLevelExceptionHandler; + LoadState(); + } + + protected override void HostAppSaveState(string modelCardState) + { + if (!IsActiveDocumentValid()) + { + return; + } + + // Compare current state with last saved state + if (modelCardState == _lastSavedState) + { + return; // Skip save if states match + } + + try + { + SaveStateToDatabase(modelCardState); + _lastSavedState = modelCardState; // Update last saved state after successful save + } + catch (NAV.Data.DatabaseException ex) + { + _topLevelExceptionHandler.CatchUnhandled( + () => throw new InvalidOperationException("Failed to write Speckle state to database", ex) + ); + } + } + + /// + /// Public method to reload the state from storage. + /// + public void ReloadState() => LoadState(); + + protected override void LoadState() + { + if (!IsActiveDocumentValid()) + { + ClearAndSaveThisState(); + return; + } + + try + { + string serializedState = RetrieveStateFromDatabase(); + LoadFromString(serializedState); + _lastSavedState = serializedState; // Store initial state after loading + } + catch (NAV.Data.DatabaseException ex) + { + ClearAndSaveThisState(); // Clear models on failure to avoid stale data + _topLevelExceptionHandler.CatchUnhandled( + () => throw new InvalidOperationException("Failed to read Speckle state from database", ex) + ); + } + } + + private void ClearAndSaveThisState() + { + ClearAndSave(); + _lastSavedState = string.Empty; // Reset last saved state when clearing + } + + private static bool IsActiveDocumentValid() + { + try + { + var activeDoc = NavisworksApp.ActiveDocument; + return activeDoc?.Database != null && activeDoc.Models.Count > 0 && activeDoc.ActiveSheet != null; + } + catch (ArgumentException) + { + return false; // Handle invalid document access + } + catch (ObjectDisposedException) + { + return false; // Handle disposed document state + } + } + + private static void SaveStateToDatabase(string modelCardState) + { + var activeDoc = NavisworksApp.ActiveDocument; + if (activeDoc?.Database == null) + { + return; + } + + var database = activeDoc.Database; + + if (!DoesTableExist(database)) + { + CreateTable(database); + } + + using var transaction = database.BeginTransaction(NAV.Data.DatabaseChangedAction.Edited); + try + { + ReplaceStateInDatabase(transaction, modelCardState); + transaction.Commit(); + } + catch + { + transaction.Rollback(); // Roll back transaction on failure + throw; + } + } + + private static void ReplaceStateInDatabase(NAV.Data.NavisworksTransaction transaction, string modelCardState) + { + var command = transaction.Connection.CreateCommand(); + command.CommandText = $"REPLACE INTO {TABLE_NAME}(key, value) VALUES(@key, @value)"; + command.Parameters.AddWithValue("@key", KEY_NAME); + command.Parameters.AddWithValue("@value", modelCardState); + command.ExecuteNonQuery(); + } + + private static bool DoesTableExist(Database database) + { + var checkCommand = database.Value.CreateCommand(); + checkCommand.CommandText = $"SELECT name FROM sqlite_master WHERE type='table' AND name='{TABLE_NAME}'"; + return checkCommand.ExecuteScalar() != null; + } + + private static void CreateTable(Database database) + { + using var transaction = database.BeginTransaction(NAV.Data.DatabaseChangedAction.Edited); + try + { + var command = transaction.Connection.CreateCommand(); + command.CommandText = $"CREATE TABLE {TABLE_NAME}(key TEXT PRIMARY KEY, value TEXT)"; + command.ExecuteNonQuery(); + transaction.Commit(); + } + catch + { + transaction.Rollback(); + throw; + } + } + + private static string RetrieveStateFromDatabase() + { + var database = NavisworksApp.ActiveDocument!.Database; + using var table = new DataTable(); + + if (!DoesTableExist(database)) + { + return string.Empty; + } + + using var dataAdapter = new NAV.Data.NavisworksDataAdapter( + $"SELECT value FROM {TABLE_NAME} WHERE key = @key", + database.Value + ); + dataAdapter.SelectCommand.Parameters.AddWithValue("@key", KEY_NAME); + dataAdapter.Fill(table); + + return table.Rows.Count <= 0 ? string.Empty : table.Rows[0]["value"] as string ?? string.Empty; + } +} diff --git a/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/HostApp/NavisworksIdleManager.cs b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/HostApp/NavisworksIdleManager.cs new file mode 100644 index 000000000..2a9087774 --- /dev/null +++ b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/HostApp/NavisworksIdleManager.cs @@ -0,0 +1,30 @@ +using Speckle.Connectors.DUI.Bridge; + +namespace Speckle.Connector.Navisworks.HostApp; + +/// +/// Manages the scheduling of deferred operations during Navisworks idle periods. +/// Ensures UI updates and operations are batched efficiently to prevent UI freezing. +/// +public sealed class NavisworksIdleManager : AppIdleManager +{ + private readonly IIdleCallManager _idleCallManager; + + /// + /// Initializes a new instance of the NavisworksIdleManager. + /// + /// The manager responsible for queuing and executing deferred operations. + public NavisworksIdleManager(IIdleCallManager idleCallManager) + : base(idleCallManager) + { + _idleCallManager = idleCallManager; + } + + /// + /// Subscribes to Navisworks idle events when operations are queued. + /// + protected override void AddEvent() => NavisworksApp.Idle += NavisworksAppOnIdle; + + private void NavisworksAppOnIdle(object? sender, EventArgs e) => + _idleCallManager.AppOnIdle(() => NavisworksApp.Idle -= NavisworksAppOnIdle); +} diff --git a/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/HostApp/NavisworksMaterialUnpacker.cs b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/HostApp/NavisworksMaterialUnpacker.cs new file mode 100644 index 000000000..450d195c5 --- /dev/null +++ b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/HostApp/NavisworksMaterialUnpacker.cs @@ -0,0 +1,170 @@ +using Microsoft.Extensions.Logging; +using Speckle.Connector.Navisworks.Services; +using Speckle.Converter.Navisworks.Helpers; +using Speckle.Converter.Navisworks.Settings; +using Speckle.Converters.Common; +using Speckle.Objects.Other; +using Speckle.Sdk; + +namespace Speckle.Connector.Navisworks.HostApp; + +public class NavisworksMaterialUnpacker( + ILogger logger, + IConverterSettingsStore converterSettings, + IElementSelectionService selectionService +) +{ + // Helper function to select a property based on the representation mode + // Selector method for individual properties + private static T Select(RepresentationMode mode, T active, T permanent, T original, T defaultValue) => + mode switch + { + RepresentationMode.Active => active, + RepresentationMode.Permanent => permanent, + RepresentationMode.Original => original, + _ => defaultValue, + }; + + internal List UnpackRenderMaterial( + IReadOnlyList navisworksObjects, + Dictionary> groupedNodes + ) + { + if (navisworksObjects == null) + { + throw new ArgumentNullException(nameof(navisworksObjects)); + } + + if (groupedNodes == null) + { + throw new ArgumentNullException(nameof(groupedNodes)); + } + + Dictionary renderMaterialProxies = []; + Dictionary mergedIds = []; + + foreach (var group in groupedNodes) + { + string groupKey = group.Key; + + foreach (var nodePath in group.Value.Select(selectionService.GetModelItemPath)) + { + mergedIds[nodePath] = groupKey; + } + } + + foreach (NAV.ModelItem navisworksObject in navisworksObjects) + { + try + { + if (!navisworksObject.HasGeometry) + { + continue; + } + + var navisworksObjectId = selectionService.GetModelItemPath(navisworksObject); + var finalId = mergedIds.TryGetValue(navisworksObjectId, out var mergedId) ? mergedId : navisworksObjectId; + var geometry = navisworksObject.Geometry; + var mode = converterSettings.Current.User.VisualRepresentationMode; + + using var defaultColor = new NAV.Color(1.0, 1.0, 1.0); + + var renderColor = Select( + mode, + geometry.ActiveColor, + geometry.PermanentColor, + geometry.OriginalColor, + defaultColor + ); + var renderTransparency = Select( + mode, + geometry.ActiveTransparency, + geometry.PermanentTransparency, + geometry.OriginalTransparency, + 0.0 + ); + var renderMaterialId = Select( + mode, + $"{geometry.ActiveColor.GetHashCode()}_{geometry.ActiveTransparency}".GetHashCode(), + $"{geometry.PermanentColor.GetHashCode()}_{geometry.PermanentTransparency}".GetHashCode(), + $"{geometry.OriginalColor.GetHashCode()}_{geometry.OriginalTransparency}".GetHashCode(), + 0 + ); + + var materialName = + $"NavisworksMaterial_{Math.Abs(ColorConverter.NavisworksColorToColor(renderColor).ToArgb())}"; + + // Check Item category for material name + var itemCategory = navisworksObject.PropertyCategories.FindCategoryByDisplayName("Item"); + if (itemCategory != null) + { + var itemProperties = itemCategory.Properties; + var itemMaterial = itemProperties.FindPropertyByDisplayName("Material"); + if (itemMaterial != null && !string.IsNullOrEmpty(itemMaterial.DisplayName)) + { + materialName = itemMaterial.Value.ToDisplayString(); + } + } + + // Check Material category for material name + var materialPropertyCategory = navisworksObject.PropertyCategories.FindCategoryByDisplayName("Material"); + if (materialPropertyCategory != null) + { + var material = materialPropertyCategory.Properties; + var name = material.FindPropertyByDisplayName("Name"); + if (name != null && !string.IsNullOrEmpty(name.DisplayName)) + { + materialName = name.Value.ToDisplayString(); + } + } + + if (renderMaterialProxies.TryGetValue(renderMaterialId.ToString(), out RenderMaterialProxy? value)) + { + value.objects.Add(finalId); + } + else + { + renderMaterialProxies[renderMaterialId.ToString()] = new RenderMaterialProxy() + { + value = ConvertRenderColorAndTransparencyToSpeckle( + materialName, + renderTransparency, + renderColor, + renderMaterialId + ), + objects = [finalId] + }; + } + } + catch (Exception ex) when (!ex.IsFatal()) + { + logger.LogError(ex, "Failed to unpack render material from Navisworks object"); + } + } + + return renderMaterialProxies.Values.ToList(); + } + + private static RenderMaterial ConvertRenderColorAndTransparencyToSpeckle( + string name, + double transparency, + NAV.Color navisworksColor, + int applicationId + ) + { + var color = ColorConverter.NavisworksColorToColor(navisworksColor); + + var speckleRenderMaterial = new RenderMaterial() + { + name = !string.IsNullOrEmpty(name) ? name : $"NavisworksMaterial_{Math.Abs(color.ToArgb())}", + opacity = 1 - transparency, + metalness = 0, + roughness = 1, + diffuse = color.ToArgb(), + emissive = 0, + applicationId = applicationId.ToString() + }; + + return speckleRenderMaterial; + } +} diff --git a/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Operations/Send/Filters/NavisworksSavedSetsFilter.cs b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Operations/Send/Filters/NavisworksSavedSetsFilter.cs new file mode 100644 index 000000000..a34bc0c9b --- /dev/null +++ b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Operations/Send/Filters/NavisworksSavedSetsFilter.cs @@ -0,0 +1,131 @@ +using Speckle.Connector.Navisworks.Services; +using Speckle.Connectors.DUI.Exceptions; +using Speckle.Connectors.DUI.Models.Card.SendFilter; +using Speckle.Connectors.DUI.Utils; +using Speckle.Converter.Navisworks.Constants; + +namespace Speckle.Connector.Navisworks.Operations.Send.Filters; + +public class NavisworksSavedSetsFilter : DiscriminatedObject, ISendFilterSelect +{ + private readonly IElementSelectionService _selectionService; + + public NavisworksSavedSetsFilter(IElementSelectionService selectionService) + { + _selectionService = selectionService; + + Items = []; + SelectedItems = []; + + GetSavedSets(); + } + + public string Id { get; set; } = "navisworksSavedSets"; + public string Name { get; set; } = "Saved Sets"; + public string? Summary { get; set; } + public bool IsDefault { get; set; } + public List SelectedObjectIds { get; set; } = []; + public Dictionary? IdMap { get; set; } + + public bool IsMultiSelectable { get; set; } = true; + public List SelectedItems { get; set; } + public List Items { get; set; } + + public List RefreshObjectIds() + { + List objectIds = []; + + if (SelectedItems.Count == 0) + { + return objectIds; + } + + var selectionSets = NavisworksApp.ActiveDocument.SelectionSets; + + foreach (var selectedSetGuid in SelectedItems) + { + var guid = new Guid(selectedSetGuid.Id); + var selectionSetItem = + selectionSets.ResolveGuid(guid) + ?? throw new SpeckleSendFilterException($"Selection set with GUID {guid} not found."); + var selectionSet = (NAV.SelectionSet)selectionSetItem; + if (selectionSet.HasSearch) + { + objectIds.AddRange(ResolveSearchSet(selectionSet.Search)); + } + + if (selectionSet.HasExplicitModelItems) + { + objectIds.AddRange(ResolveSelectionSet(selectionSet.ExplicitModelItems)); + } + } + + return objectIds; + } + + private IEnumerable ResolveSelectionSet(NAV.ModelItemCollection selectionSetExplicitModelItems) => + selectionSetExplicitModelItems + .Where(_selectionService.IsVisible) // Exclude hidden elements + .Select(_selectionService.GetModelItemPath) // Resolve to index paths + .ToList(); + + private IEnumerable ResolveSearchSet(NAV.Search selectionSetSearch) => + selectionSetSearch + .FindAll(NavisworksApp.ActiveDocument, false) + .Where(_selectionService.IsVisible) + .Select(_selectionService.GetModelItemPath) + .ToList(); + + /// + /// Since it is called from constructor, it is re-called whenever UI calls SendBinding.GetSendFilters() on SendFilter dialog. + /// Do not change the behavior/scope of this class on send binding unless make sure the behavior is same. Otherwise, we might not be able to update list of saved sets. + /// + private void GetSavedSets() + { + List savedSetRecords = []; + + CollectSavedSets(NavisworksApp.ActiveDocument.SelectionSets.RootItem, savedSetRecords); + + Items = savedSetRecords + .Select(setRecord => + { + string hierarchicalName = BuildHierarchicalName(setRecord); + return new SendFilterSelectItem(setRecord.Guid.ToString(), hierarchicalName); + }) + .ToList(); + } + + private static void CollectSavedSets(NAV.SavedItem parentItem, List collectedSets) + { + if (!parentItem.IsGroup) + { + return; + } + + foreach (NAV.SavedItem item in ((NAV.FolderItem)parentItem).Children) + { + if (item.IsGroup) + { + CollectSavedSets(item, collectedSets); + } + else + { + collectedSets.Add((NAV.SelectionSet)item); + } + } + } + + private static string BuildHierarchicalName(NAV.SavedItem item) + { + var pathParts = new List { item.DisplayName }; + + var current = item.Parent; + while (current != null && current != NavisworksApp.ActiveDocument.SelectionSets.RootItem) + { + pathParts.Insert(0, current.DisplayName); + current = current.Parent; + } + + return string.Join(PathConstants.SET_SEPARATOR, pathParts); + } +} diff --git a/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Operations/Send/Filters/NavisworksSelectionFilter.cs b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Operations/Send/Filters/NavisworksSelectionFilter.cs new file mode 100644 index 000000000..dab2b224d --- /dev/null +++ b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Operations/Send/Filters/NavisworksSelectionFilter.cs @@ -0,0 +1,13 @@ +using Speckle.Connectors.DUI.Models.Card.SendFilter; + +namespace Speckle.Connector.Navisworks.Operations.Send.Filters; + +public class NavisworksSelectionFilter : DirectSelectionSendFilter +{ + public NavisworksSelectionFilter() + { + IsDefault = true; + } + + public override List RefreshObjectIds() => SelectedObjectIds; +} diff --git a/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Operations/Send/GeometryNodeMerger.cs b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Operations/Send/GeometryNodeMerger.cs new file mode 100644 index 000000000..c7747bee3 --- /dev/null +++ b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Operations/Send/GeometryNodeMerger.cs @@ -0,0 +1,165 @@ +using Speckle.Connector.Navisworks.Services; +using Speckle.Converter.Navisworks.Constants; + +namespace Speckle.Connector.Navisworks.Operations.Send; + +/// +/// Groups geometry nodes by their parent paths for merging displayValues +/// +public static class GeometryNodeMerger +{ + /// + /// Groups sibling geometry nodes based on material properties for merging. + /// Only merges nodes that share the same parent and have identical material properties. + /// + /// The collection of ModelItems to process + /// Dictionary mapping parent paths (with material signature suffix) to their mergeable child nodes + public static Dictionary> GroupSiblingGeometryNodes(IReadOnlyList nodes) + { + var selectionService = new ElementSelectionService(); + + // Group nameless geometry nodes by parent path and material signature + var mergeableGroups = nodes + .Where(node => node.HasGeometry && string.IsNullOrEmpty(node.DisplayName)) // Only anonymous geometry nodes + .GroupBy(node => + { + // Get parent path + var path = selectionService.GetModelItemPath(node); + var lastSeparatorIndex = path.LastIndexOf(PathConstants.SEPARATOR); + var parentPath = lastSeparatorIndex == -1 ? path : path[..lastSeparatorIndex]; + + // Generate material signature + string signature = GenerateSignature(node); + + // Combine parent path with signature + return $"{parentPath}{PathConstants.MATERIAL_SEPARATOR}{signature}"; + }) + .Where(group => group.Count() > 1) // Only include groups with multiple children + .ToDictionary(group => group.Key, group => group.ToList()); + + return mergeableGroups; + } + + /// + /// Generates a signature for a node based on material properties + /// + private static string GenerateSignature(NAV.ModelItem node) + { + var signatureProperties = new Dictionary(); + + // We can as many signature defining methods as we want here + AddMaterialProperties(node, signatureProperties); + + // When we are done adding properties, we can generate the signature + return GetSignature(signatureProperties); + } + + /// + /// Adds material-related properties to the properties dictionary + /// + private static void AddMaterialProperties(NAV.ModelItem node, Dictionary properties) + { + if (!node.HasGeometry || node.Geometry == null) + { + return; + } + + var geometry = node.Geometry; + if (geometry.ActiveColor != null) + { + properties["ActiveColor"] = (geometry.ActiveColor.R, geometry.ActiveColor.G, geometry.ActiveColor.B); + properties["ActiveTransparency"] = geometry.ActiveTransparency; + } + + // Add material name if available + var materialName = GetMaterialName(node); + if (!string.IsNullOrEmpty(materialName)) + { + properties["MaterialName"] = materialName; + } + } + + /// + /// Creates a hash-based signature from a dictionary of properties. + /// + /// Dictionary containing property name/value pairs to include in the signature + /// Length of the returned hash string (default: 8 characters) + /// A hash string representing the combined properties + private static string GetSignature(Dictionary properties, int hashLength = 8) + { + if (properties.Count == 0) + { + return "empty"; + } + + // Build a consistent string representation of all properties + var hashInput = new System.Text.StringBuilder(); + + // Sort keys to ensure consistent order + var sortedKeys = properties.Keys.OrderBy(k => k).ToList(); + + foreach (var key in sortedKeys) + { + var value = properties[key]; + switch (value) + { + case null: + continue; + // Format numbers with fixed precision to avoid floating point inconsistencies + case double doubleValue: + hashInput.Append($"{key}:{Math.Round(doubleValue, 6)}_"); + break; + case float floatValue: + hashInput.Append($"{key}:{Math.Round(floatValue, 6)}_"); + break; + default: + hashInput.Append($"{key}:{value.GetHashCode()}_"); + break; + } + } + + if (hashInput.Length == 0) + { + return "empty"; + } + + // Use MD5 hash with warning suppression +#pragma warning disable CA5351 // Do Not Use Broken Cryptographic Algorithms + using var md5 = System.Security.Cryptography.MD5.Create(); + var inputBytes = System.Text.Encoding.UTF8.GetBytes(hashInput.ToString()); + var hashBytes = md5.ComputeHash(inputBytes); +#pragma warning restore CA5351 + + var fullHashString = BitConverter.ToString(hashBytes).Replace("-", ""); + return fullHashString[..Math.Min(hashLength, fullHashString.Length)]; + } + + /// + /// Extracts material name from a node if available. + /// + private static string GetMaterialName(NAV.ModelItem node) + { + // Check Item category for material name + var itemCategory = node.PropertyCategories.FindCategoryByDisplayName("Item"); + if (itemCategory != null) + { + var itemProperties = itemCategory.Properties; + var itemMaterial = itemProperties.FindPropertyByDisplayName("Material"); + if (itemMaterial != null && !string.IsNullOrEmpty(itemMaterial.DisplayName)) + { + return itemMaterial.Value.ToDisplayString(); + } + } + + // Check Material category for material name + var materialPropertyCategory = node.PropertyCategories.FindCategoryByDisplayName("Material"); + if (materialPropertyCategory == null) + { + return string.Empty; + } + + var material = materialPropertyCategory.Properties; + var name = material.FindPropertyByDisplayName("Name"); + return name != null && !string.IsNullOrEmpty(name.DisplayName) ? name.Value.ToDisplayString() : string.Empty; + } +} diff --git a/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Operations/Send/NavisworksHierarchyBuilder.cs b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Operations/Send/NavisworksHierarchyBuilder.cs new file mode 100644 index 000000000..68749a1a9 --- /dev/null +++ b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Operations/Send/NavisworksHierarchyBuilder.cs @@ -0,0 +1,146 @@ +using Speckle.Connector.Navisworks.Services; +using Speckle.Converter.Navisworks.Constants; +using Speckle.Converters.Common; +using Speckle.Sdk.Models; +using Speckle.Sdk.Models.Collections; + +namespace Speckle.Connector.Navisworks.Operations.Send; + +/// +/// Rebuilds the Navisworks document hierarchy from converted geometry leaves while preserving +/// the parent-child relationships between elements in the original model structure. +/// +public class NavisworksHierarchyBuilder +{ + private readonly Dictionary _geometryLeaves; + private readonly IRootToSpeckleConverter _converter; + private readonly IElementSelectionService _selectionService; + private readonly Dictionary _allNodes; + + /// + /// Initializes a new instance of the NavisworksHierarchyBuilder. + /// + /// Dictionary of path-indexed converted geometry elements + /// Converter to transform Navisworks elements to Speckle objects + /// Service for resolving Navisworks element paths + public NavisworksHierarchyBuilder( + Dictionary geometryLeaves, + IRootToSpeckleConverter converter, + IElementSelectionService selectionService + ) + { + _geometryLeaves = geometryLeaves; + _converter = converter; + _selectionService = selectionService; + _allNodes = new Dictionary(); + } + + /// + /// Constructs a hierarchical tree of Speckle objects that mirrors the Navisworks document structure. + /// + /// List of root-level Speckle Base objects containing the full hierarchy + public List BuildHierarchy() + { + foreach (var kvp in _geometryLeaves) + { + if (kvp.Value != null) + { + _allNodes[kvp.Key] = kvp.Value; + } + } + + // For each leaf path, traverse up the document structure converting any missing ancestors + foreach (var nodePath in _allNodes.ToList().Select(kvp => kvp.Key)) + { + ClimbUpToRoot(nodePath); + } + + var allPaths = _allNodes.Keys.ToList(); + allPaths.Sort( + (a, b) => + { + var depthA = a.Count(c => c == PathConstants.SEPARATOR); + var depthB = b.Count(c => c == PathConstants.SEPARATOR); + return depthB.CompareTo(depthA); // <- Sort in ascending order of path length + } + ); + + // Link nodes to parents and identify root nodes that have no recognized parent + var rootCandidates = new Dictionary(_allNodes); + + foreach (var nodePath in allPaths) + { + if (nodePath == "0") + { + continue; + } + + var nodeBase = _allNodes[nodePath]; + var parentPath = GetParentPath(nodePath); + + if (string.IsNullOrEmpty(parentPath)) + { + continue; + } + + // Navisworks API: Add child elements to parent collections + if (!_allNodes.TryGetValue(parentPath, out var parentBase) || parentBase is not Collection parentCollection) + { + continue; + } + + parentCollection.elements.Add(nodeBase); + rootCandidates.Remove(nodePath); + } + + var rootNodes = rootCandidates.Values.ToList(); + PruneEmptyCollections(rootNodes); + + return rootNodes; + } + + private void ClimbUpToRoot(string currentPath) + { + while (!string.IsNullOrEmpty(currentPath) && currentPath != "0") + { + var parentPath = GetParentPath(currentPath); + + if (string.IsNullOrEmpty(parentPath)) + { + return; + } + + if (_allNodes.ContainsKey(parentPath)) + { + currentPath = parentPath; + continue; + } + + var parentModelItem = _selectionService.GetModelItemFromPath(parentPath); + var parentConverted = _converter.Convert(parentModelItem); + _allNodes[parentPath] = parentConverted; + + currentPath = parentPath; + } + } + + private static string GetParentPath(string path) + { + var idx = path.LastIndexOf(PathConstants.SEPARATOR); + return idx == -1 ? string.Empty : path[..idx]; + } + + private static void PruneEmptyCollections(List nodes) + { + foreach (var node in nodes.ToList()) + { + if (node is not Collection collection) + { + continue; + } + + PruneEmptyCollections(collection.elements); + collection.elements.RemoveAll(child => child is Collection { elements.Count: 0 }); + } + } +} diff --git a/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Operations/Send/NavisworksRootObjectBuilder.cs b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Operations/Send/NavisworksRootObjectBuilder.cs new file mode 100644 index 000000000..2fc4254a7 --- /dev/null +++ b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Operations/Send/NavisworksRootObjectBuilder.cs @@ -0,0 +1,335 @@ +using Microsoft.Extensions.Logging; +using Speckle.Connector.Navisworks.HostApp; +using Speckle.Connector.Navisworks.Services; +using Speckle.Connectors.Common.Builders; +using Speckle.Connectors.Common.Caching; +using Speckle.Connectors.Common.Conversion; +using Speckle.Connectors.Common.Operations; +using Speckle.Converter.Navisworks.Helpers; +using Speckle.Converter.Navisworks.Settings; +using Speckle.Converters.Common; +using Speckle.Objects.Data; +using Speckle.Sdk; +using Speckle.Sdk.Logging; +using Speckle.Sdk.Models; +using Speckle.Sdk.Models.Collections; +using static Speckle.Connector.Navisworks.Operations.Send.GeometryNodeMerger; + +namespace Speckle.Connector.Navisworks.Operations.Send; + +public class NavisworksRootObjectBuilder( + IRootToSpeckleConverter rootToSpeckleConverter, + ISendConversionCache sendConversionCache, + IConverterSettingsStore converterSettings, + ILogger logger, + ISdkActivityFactory activityFactory, + NavisworksMaterialUnpacker materialUnpacker, + NavisworksColorUnpacker colorUnpacker, + IElementSelectionService elementSelectionService +) : IRootObjectBuilder +{ + private bool SkipNodeMerging { get; set; } + + internal NavisworksConversionSettings GetCurrentSettings() => converterSettings.Current; + + /// + /// Asynchronously builds a Speckle object hierarchy from Navisworks model items. + /// + /// The list of Navisworks items to convert. + /// Information about the send operation. + /// Progress reporting callback. + /// Token to cancel the operation. + /// A result containing the root collection and conversion results. + /// Thrown when no objects can be converted. + public async Task Build( + IReadOnlyList navisworksModelItems, + SendInfo sendInfo, + IProgress onOperationProgressed, + CancellationToken cancellationToken + ) + { +#if DEBUG + // This is a temporary workaround to disable node merging for debugging purposes - false is default, true is for debugging + SkipNodeMerging = false; +#endif + using var activity = activityFactory.Start("Build"); + + ValidateInputs(navisworksModelItems, sendInfo, onOperationProgressed); + + // 2. Initialize root collection + var rootCollection = InitializeRootCollection(); + + // 3. Convert all model items and store results + var (convertedElements, conversionResults) = await ConvertModelItemsAsync( + navisworksModelItems, + sendInfo, + onOperationProgressed, + cancellationToken + ); + + ValidateConversionResults(conversionResults); + + var groupedNodes = SkipNodeMerging ? [] : GroupSiblingGeometryNodes(navisworksModelItems); + var finalElements = BuildFinalElements(convertedElements, groupedNodes); + + await AddProxiesToCollection(rootCollection, navisworksModelItems, groupedNodes); + + rootCollection.elements = finalElements; + return new RootObjectBuilderResult(rootCollection, conversionResults); + } + + private static void ValidateInputs( + IReadOnlyList navisworksModelItems, + SendInfo sendInfo, + IProgress onOperationProgressed + ) + { + if (!navisworksModelItems.Any()) + { + throw new SpeckleException("No objects to convert"); + } + + if (navisworksModelItems == null) + { + throw new ArgumentNullException(nameof(navisworksModelItems)); + } + + if (onOperationProgressed == null || sendInfo == null) + { + throw new ArgumentNullException(onOperationProgressed == null ? nameof(onOperationProgressed) : nameof(sendInfo)); + } + } + + private Collection InitializeRootCollection() => + new() + { + name = NavisworksApp.ActiveDocument.Title ?? "Unnamed model", + ["units"] = converterSettings.Current.Derived.SpeckleUnits + }; + + private Task<(Dictionary converted, List results)> ConvertModelItemsAsync( + IReadOnlyList navisworksModelItems, + SendInfo sendInfo, + IProgress onOperationProgressed, + CancellationToken cancellationToken + ) + { + var results = new List(navisworksModelItems.Count); + var convertedBases = new Dictionary(); + int processedCount = 0; + int totalCount = navisworksModelItems.Count; + + foreach (var item in navisworksModelItems) + { + cancellationToken.ThrowIfCancellationRequested(); + var converted = ConvertNavisworksItem(item, convertedBases, sendInfo); + results.Add(converted); + processedCount++; + onOperationProgressed.Report(new CardProgress("Converting", (double)processedCount / totalCount)); + } + + return Task.FromResult((convertedBases, results)); + } + + private static void ValidateConversionResults(List results) + { + if (results.All(x => x.Status == Status.ERROR)) + { + throw new SpeckleException("Failed to convert all objects."); + } + } + + private List BuildFinalElements( + Dictionary convertedBases, + Dictionary> groupedNodes + ) + { + // First build the grouped nodes as before + var finalElements = new List(); + var processedPaths = new HashSet(); + AddGroupedElements(finalElements, convertedBases, groupedNodes, processedPaths); + + // If hierarchy mode is enabled, reorganize into proper nested structure + if (converterSettings.Current.User.PreserveModelHierarchy) + { + var hierarchyBuilder = new NavisworksHierarchyBuilder( + convertedBases, + rootToSpeckleConverter, + elementSelectionService + ); + + var hierarchy = hierarchyBuilder.BuildHierarchy(); + + return hierarchy; + } + + // Otherwise continue with flat mode + AddRemainingElements(finalElements, convertedBases, processedPaths); + return finalElements; + } + + private void AddGroupedElements( + List finalElements, + Dictionary convertedBases, + Dictionary> groupedNodes, + HashSet processedPaths + ) + { + foreach (var group in groupedNodes) + { + var siblingBases = new List(); + foreach (var itemPath in group.Value.Select(elementSelectionService.GetModelItemPath)) + { + processedPaths.Add(itemPath); + if (convertedBases.TryGetValue(itemPath, out var convertedBase) && convertedBase != null) + { + siblingBases.Add(convertedBase); + } + } + + if (siblingBases.Count > 0) + { + finalElements.Add(CreateNavisworksObject(group.Key, siblingBases)); + } + } + } + + private void AddRemainingElements( + List finalElements, + Dictionary convertedBases, + HashSet processedPaths + ) + { + foreach (var kvp in convertedBases.Where(kvp => !processedPaths.Contains(kvp.Key))) + { + switch (kvp.Value) + { + case null: + continue; + case Collection collection: + finalElements.Add(collection); + break; + default: + if (CreateNavisworksObject(kvp.Value) is { } navisworksObject) + { + finalElements.Add(navisworksObject); + } + + break; + } + } + } + + private (string name, string path) GetContext(string applicationId) + { + var modelItem = elementSelectionService.GetModelItemFromPath(applicationId); + var context = HierarchyHelper.ExtractContext(modelItem); + return (context.Name, context.Path); + } + + /// + /// Processes and adds any remaining non-grouped elements. + /// + /// + /// Handles both Collection and Base type elements differently. + /// Only processes elements that weren't handled in grouped processing. + /// + private NavisworksObject CreateNavisworksObject(string groupKey, List siblingBases) + { + string cleanParentPath = ElementSelectionHelper.GetCleanPath(groupKey); + (string name, string path) = GetContext(cleanParentPath); + + return new NavisworksObject + { + name = name, + displayValue = siblingBases.SelectMany(b => b["displayValue"] as List ?? []).ToList(), + properties = siblingBases.First()["properties"] as Dictionary ?? [], + units = converterSettings.Current.Derived.SpeckleUnits, + applicationId = groupKey, // Use the full composite key as applicationId to preserve uniqueness + ["path"] = path + }; + } + + /// + /// Creates a NavisworksObject from a single converted base. + /// + /// The converted Speckle Base object. + /// A new NavisworksObject containing the converted data. + private NavisworksObject? CreateNavisworksObject(Base convertedBase) + { + if (convertedBase.applicationId == null) + { + return null; + } + + (string name, string path) = GetContext(convertedBase.applicationId); + + return new NavisworksObject + { + name = name, + displayValue = convertedBase["displayValue"] as List ?? [], + properties = convertedBase["properties"] as Dictionary ?? [], + units = converterSettings.Current.Derived.SpeckleUnits, + applicationId = convertedBase.applicationId, + ["path"] = path + }; + } + + private Task AddProxiesToCollection( + Collection rootCollection, + IReadOnlyList navisworksModelItems, + Dictionary> groupedNodes + ) + { + using var _ = activityFactory.Start("UnpackProxies"); + + var renderMaterials = materialUnpacker.UnpackRenderMaterial(navisworksModelItems, groupedNodes); + if (renderMaterials.Count > 0) + { + rootCollection[ProxyKeys.RENDER_MATERIAL] = renderMaterials; + } + + var colors = colorUnpacker.UnpackColor(navisworksModelItems, groupedNodes); + if (colors.Count > 0) + { + rootCollection[ProxyKeys.COLOR] = colors; + } + + return Task.CompletedTask; + } + + /// + /// Converts a single Navisworks item to a Speckle object. + /// + /// + /// Attempts to retrieve from cache first. + /// Falls back to fresh conversion if not cached. + /// Logs errors but doesn't throw exceptions. + /// + /// A SendConversionResult indicating success or failure. + private SendConversionResult ConvertNavisworksItem( + NAV.ModelItem navisworksItem, + Dictionary convertedBases, + SendInfo sendInfo + ) + { + string applicationId = elementSelectionService.GetModelItemPath(navisworksItem); + string sourceType = navisworksItem.GetType().Name; + + try + { + Base converted = sendConversionCache.TryGetValue(applicationId, sendInfo.ProjectId, out ObjectReference? cached) + ? cached + : rootToSpeckleConverter.Convert(navisworksItem); + + convertedBases[applicationId] = converted; + + return new SendConversionResult(Status.SUCCESS, applicationId, sourceType, converted); + } + catch (Exception ex) when (!ex.IsFatal()) + { + logger.LogError(ex, "Failed to convert model item {id}", applicationId); + return new SendConversionResult(Status.ERROR, applicationId, "ModelItem", null, ex); + } + } +} diff --git a/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Operations/Send/Settings/ConvertHiddenEleementsSetting.cs b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Operations/Send/Settings/ConvertHiddenEleementsSetting.cs new file mode 100644 index 000000000..fe2966800 --- /dev/null +++ b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Operations/Send/Settings/ConvertHiddenEleementsSetting.cs @@ -0,0 +1,12 @@ +using Speckle.Connectors.DUI.Settings; + +namespace Speckle.Connector.Navisworks.Operations.Send.Settings; + +public class ConvertHiddenElementsSetting(bool value) : ICardSetting +{ + public string? Id { get; set; } = "convertHiddenElements"; + public string? Title { get; set; } = "Convert Hidden Elements"; + public string? Type { get; set; } = "boolean"; + public List? Enum { get; set; } + public object? Value { get; set; } = value; +} diff --git a/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Operations/Send/Settings/IncludeInternalPropertiesSetting.cs b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Operations/Send/Settings/IncludeInternalPropertiesSetting.cs new file mode 100644 index 000000000..594cf59bc --- /dev/null +++ b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Operations/Send/Settings/IncludeInternalPropertiesSetting.cs @@ -0,0 +1,12 @@ +using Speckle.Connectors.DUI.Settings; + +namespace Speckle.Connector.Navisworks.Operations.Send.Settings; + +public class IncludeInternalPropertiesSetting(bool value) : ICardSetting +{ + public string? Id { get; set; } = "includeInternalProperties"; + public string? Title { get; set; } = "Include Internal Properties"; + public string? Type { get; set; } = "boolean"; + public List? Enum { get; set; } + public object? Value { get; set; } = value; +} diff --git a/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Operations/Send/Settings/OriginModeSetting.cs b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Operations/Send/Settings/OriginModeSetting.cs new file mode 100644 index 000000000..cbc398d64 --- /dev/null +++ b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Operations/Send/Settings/OriginModeSetting.cs @@ -0,0 +1,18 @@ +using Speckle.Connectors.DUI.Settings; +using Speckle.Converter.Navisworks.Settings; + +namespace Speckle.Connector.Navisworks.Operations.Send.Settings; + +public class OriginModeSetting(OriginMode value) : ICardSetting +{ + public string? Id { get; set; } = "originMode"; + public string? Title { get; set; } = "Origin Mode"; + public string? Type { get; set; } = "string"; + public List? Enum { get; set; } = System.Enum.GetNames(typeof(OriginMode)).ToList(); + public object? Value { get; set; } = value.ToString(); + + public static readonly Dictionary OriginModeMap = System + .Enum.GetValues(typeof(OriginMode)) + .Cast() + .ToDictionary(v => v.ToString(), v => v); +} diff --git a/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Operations/Send/Settings/PreserveModelHierarchySetting.cs b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Operations/Send/Settings/PreserveModelHierarchySetting.cs new file mode 100644 index 000000000..b8e16672b --- /dev/null +++ b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Operations/Send/Settings/PreserveModelHierarchySetting.cs @@ -0,0 +1,12 @@ +using Speckle.Connectors.DUI.Settings; + +namespace Speckle.Connector.Navisworks.Operations.Send.Settings; + +public class PreserveModelHierarchySetting(bool value) : ICardSetting +{ + public string? Id { get; set; } = "preserveModelHierarchy"; + public string? Title { get; set; } = "Preserve Model Hierarchy"; + public string? Type { get; set; } = "boolean"; + public List? Enum { get; set; } + public object? Value { get; set; } = value; +} diff --git a/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Operations/Send/Settings/ToSpeckleSettingsManagerNavisworks.cs b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Operations/Send/Settings/ToSpeckleSettingsManagerNavisworks.cs new file mode 100644 index 000000000..522b73f13 --- /dev/null +++ b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Operations/Send/Settings/ToSpeckleSettingsManagerNavisworks.cs @@ -0,0 +1,146 @@ +using System.Diagnostics.CodeAnalysis; +using Speckle.Connectors.Common.Caching; +using Speckle.Connectors.DUI.Models.Card; +using Speckle.Converter.Navisworks.Settings; +using Speckle.InterfaceGenerator; +using Speckle.Sdk.Common; + +namespace Speckle.Connector.Navisworks.Operations.Send.Settings; + +[GenerateAutoInterface] +public class ToSpeckleSettingsManagerNavisworks : IToSpeckleSettingsManagerNavisworks +{ + private readonly ISendConversionCache _sendConversionCache; + + // cache invalidation process run with ModelCardId since the settings are model specific + private readonly Dictionary _visualRepresentationCache = []; + private readonly Dictionary _originModeCache = []; + private readonly Dictionary _convertHiddenElementsCache = []; + private readonly Dictionary _includeInternalPropertiesCache = []; + private readonly Dictionary _preserveModelHierarchyCache = []; + + public ToSpeckleSettingsManagerNavisworks(ISendConversionCache sendConversionCache) + { + _sendConversionCache = sendConversionCache; + } + + public RepresentationMode GetVisualRepresentationMode(SenderModelCard modelCard) + { + if (modelCard == null) + { + throw new ArgumentNullException(nameof(modelCard)); + } + + var representationString = modelCard.Settings?.First(s => s.Id == "visualRepresentation").Value as string; + + if ( + representationString is not null + && VisualRepresentationSetting.VisualRepresentationMap.TryGetValue( + representationString, + out RepresentationMode representation + ) + ) + { + if (_visualRepresentationCache.TryGetValue(modelCard.ModelCardId.NotNull(), out RepresentationMode previousType)) + { + if (previousType != representation) + { + EvictCacheForModelCard(modelCard); + } + } + + _visualRepresentationCache[modelCard.ModelCardId.NotNull()] = representation; + return representation; + } + + throw new ArgumentException($"Invalid visual representation value: {representationString}"); + } + + public OriginMode GetOriginMode(SenderModelCard modelCard) + { + if (modelCard == null) + { + throw new ArgumentNullException(nameof(modelCard)); + } + + var originString = modelCard.Settings?.First(s => s.Id == "originMode").Value as string; + + if (originString is not null && OriginModeSetting.OriginModeMap.TryGetValue(originString, out OriginMode origin)) + { + if (_originModeCache.TryGetValue(modelCard.ModelCardId.NotNull(), out OriginMode previousType)) + { + if (previousType != origin) + { + EvictCacheForModelCard(modelCard); + } + } + + _originModeCache[modelCard.ModelCardId.NotNull()] = origin; + return origin; + } + + throw new ArgumentException($"Invalid origin mode value: {originString}"); + } + + public bool GetConvertHiddenElements(SenderModelCard modelCard) + { + if (modelCard == null) + { + throw new ArgumentNullException(nameof(modelCard)); + } + + var value = modelCard.Settings?.FirstOrDefault(s => s.Id == "convertHiddenElements")?.Value as bool?; + + var returnValue = value != null && value.NotNull(); + if (_convertHiddenElementsCache.TryGetValue(modelCard.ModelCardId.NotNull(), out var previousValue)) + { + if (previousValue != returnValue) + { + EvictCacheForModelCard(modelCard); + } + } + + _convertHiddenElementsCache[modelCard.ModelCardId] = returnValue; + return returnValue; + } + + public bool GetIncludeInternalProperties([NotNull] SenderModelCard modelCard) + { + var value = modelCard.Settings?.FirstOrDefault(s => s.Id == "includeInternalProperties")?.Value as bool?; + + var returnValue = value != null && value.NotNull(); + if (_includeInternalPropertiesCache.TryGetValue(modelCard.ModelCardId.NotNull(), out var previousValue)) + { + if (previousValue != returnValue) + { + EvictCacheForModelCard(modelCard); + } + } + + _includeInternalPropertiesCache[modelCard.ModelCardId] = returnValue; + return returnValue; + } + + public bool GetPreserveModelHierarchy([NotNull] SenderModelCard modelCard) + { + var value = modelCard.Settings?.FirstOrDefault(s => s.Id == "preserveModelHierarchy")?.Value as bool?; + + var returnValue = value != null && value.NotNull(); + if (_preserveModelHierarchyCache.TryGetValue(modelCard.ModelCardId.NotNull(), out var previousValue)) + { + if (previousValue != returnValue) + { + EvictCacheForModelCard(modelCard); + } + } + + _preserveModelHierarchyCache[modelCard.ModelCardId] = returnValue; + return returnValue; + } + + private void EvictCacheForModelCard(SenderModelCard modelCard) + { + var objectIds = modelCard.SendFilter != null ? modelCard.SendFilter.NotNull().SelectedObjectIds : []; + _sendConversionCache.EvictObjects(objectIds); + } +} diff --git a/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Operations/Send/Settings/VisualRepresentationSetting.cs b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Operations/Send/Settings/VisualRepresentationSetting.cs new file mode 100644 index 000000000..0c58f809f --- /dev/null +++ b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Operations/Send/Settings/VisualRepresentationSetting.cs @@ -0,0 +1,18 @@ +using Speckle.Connectors.DUI.Settings; +using Speckle.Converter.Navisworks.Settings; + +namespace Speckle.Connector.Navisworks.Operations.Send.Settings; + +public class VisualRepresentationSetting(RepresentationMode value) : ICardSetting +{ + public string? Id { get; set; } = "visualRepresentation"; + public string? Title { get; set; } = "Visual Representation"; + public string? Type { get; set; } = "string"; + public List? Enum { get; set; } = System.Enum.GetNames(typeof(RepresentationMode)).ToList(); + public object? Value { get; set; } = value.ToString(); + + public static readonly Dictionary VisualRepresentationMap = System + .Enum.GetValues(typeof(RepresentationMode)) + .Cast() + .ToDictionary(v => v.ToString(), v => v); +} diff --git a/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Plugin/BrowserPane.xaml b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Plugin/BrowserPane.xaml new file mode 100644 index 000000000..ab3a0c963 --- /dev/null +++ b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Plugin/BrowserPane.xaml @@ -0,0 +1,17 @@ + + + + + diff --git a/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Plugin/BrowserPane.xaml.cs b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Plugin/BrowserPane.xaml.cs new file mode 100644 index 000000000..2605eece7 --- /dev/null +++ b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Plugin/BrowserPane.xaml.cs @@ -0,0 +1,8 @@ +using System.Windows.Controls; + +namespace Speckle.Connectors.Navisworks; + +public class BrowserPane : UserControl +{ + public BrowserPane() { } +} diff --git a/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Plugin/DockableConnectorPane.cs b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Plugin/DockableConnectorPane.cs new file mode 100644 index 000000000..ea9601f7b --- /dev/null +++ b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Plugin/DockableConnectorPane.cs @@ -0,0 +1,67 @@ +using System.Diagnostics.CodeAnalysis; +using System.Windows.Forms; +using System.Windows.Forms.Integration; +using Microsoft.Extensions.DependencyInjection; +using Speckle.Connector.Navisworks.DependencyInjection; +using Speckle.Connector.Navisworks.HostApp; +using Speckle.Connector.Navisworks.Plugin.Tools; +using Speckle.Connectors.Common; +using Speckle.Connectors.DUI; +using Speckle.Connectors.DUI.WebView; +using Speckle.Converter.Navisworks.DependencyInjection; +using Speckle.Sdk.Host; + +namespace Speckle.Connector.Navisworks.Plugin; + +[ + NAV.Plugins.DockPanePlugin(450, 750, FixedSize = false, AutoScroll = true, MinimumHeight = 410, MinimumWidth = 250), + NAV.Plugins.Plugin( + SpeckleV3Tool.PLUGIN, + SpeckleV3Tool.DEVELOPER_ID, + DisplayName = SpeckleV3Tool.DISPLAY_NAME, + Options = NAV.Plugins.PluginOptions.None, + ToolTip = "Speckle Connector for Navisworks", + ExtendedToolTip = "Next Gen Speckle Connector (Beta) for Navisworks" + ) +] +[SuppressMessage( + "design", + "CA1812:Avoid uninstantiated internal classes", + Justification = "Instantiated by Navisworks" +)] +internal sealed class Connector : NAV.Plugins.DockPanePlugin +{ + private ServiceProvider? Container { get; set; } + + public override Control CreateControlPane() + { + AppDomain.CurrentDomain.AssemblyResolve += AssemblyResolver.OnAssemblyResolve; + + var services = new ServiceCollection(); + + services.Initialize(HostApplications.Navisworks, SpeckleV3Tool.Version); + + services.AddNavisworks(); + services.AddNavisworksConverter(); + + Container = services.BuildServiceProvider(); + Container.UseDUI(); + Container.GetRequiredService(); + + var u = Container.GetRequiredService(); + + var speckleHost = new ElementHost { AutoSize = true, Child = u }; + + speckleHost.CreateControl(); + + return speckleHost; + } + + public override void DestroyControlPane(Control pane) + { + if (pane is UserControl control) + { + control.Dispose(); + } + } +} diff --git a/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Plugin/NavisworksRibbon.name b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Plugin/NavisworksRibbon.name new file mode 100644 index 000000000..88c2545d3 --- /dev/null +++ b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Plugin/NavisworksRibbon.name @@ -0,0 +1,10 @@ +# This file uses UTF8 encoding. Lines starting with # or $, or ending with = +# do NOT need translating. +$utf8 + +DisplayName=Speckle +Speckle.DisplayName=Speckle +SpeckleV3.Title=Speckle (Beta) +SpeckleV2.Title=Speckle +Speckle_Launch.DisplayName=Speckle (Beta) +Speckle_Launch_V2.DisplayName=Speckle diff --git a/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Plugin/NavisworksRibbon.xaml b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Plugin/NavisworksRibbon.xaml new file mode 100644 index 000000000..a4e2eb5b1 --- /dev/null +++ b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Plugin/NavisworksRibbon.xaml @@ -0,0 +1,29 @@ + + + + + + + + + + + + diff --git a/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Plugin/NavisworksRibbon.xaml.cs b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Plugin/NavisworksRibbon.xaml.cs new file mode 100644 index 000000000..a3018a8a2 --- /dev/null +++ b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Plugin/NavisworksRibbon.xaml.cs @@ -0,0 +1,160 @@ +using System.Windows.Forms; +using Speckle.Connector.Navisworks.Plugin.Tools; + +namespace Speckle.Connector.Navisworks.Plugin; + +/// +/// Handles plugin state and ribbon management for the Speckle V3 and V2 connectors. +/// +[ + NAV.Plugins.Plugin(SpeckleV3Tool.PLUGIN_ID, SpeckleV3Tool.DEVELOPER_ID, DisplayName = SpeckleV3Tool.DISPLAY_NAME), + NAV.Plugins.Strings(SpeckleV3Tool.RIBBON_STRINGS), + NAV.Plugins.RibbonLayout("NavisworksRibbon.xaml"), + NAV.Plugins.RibbonTab( + SpeckleV3Tool.RIBBON_TAB_ID, + DisplayName = SpeckleV3Tool.RIBBON_TAB_DISPLAY_NAME, + LoadForCanExecute = true + ), + NAV.Plugins.Command( + SpeckleV3Tool.COMMAND, + LoadForCanExecute = true, + Icon = "Resources/v3_logo16.png", + LargeIcon = "Resources/v3_logo32.png", + ToolTip = "Speckle Connector for Navisworks", + DisplayName = "$Speckle_Launch.DisplayName" + ), + NAV.Plugins.Command( + SpeckleV2Tool.COMMAND, + LoadForCanExecute = true, + Icon = "Resources/v2_logo16.png", + LargeIcon = "Resources/v2_logo32.png", + ToolTip = "Legacy Speckle v2 Connector", + DisplayName = "$Speckle_Launch_V2.DisplayName" + ) +] +internal sealed class RibbonHandler : NAV.Plugins.CommandHandlerPlugin +{ + private static bool? s_isV2PluginAvailable; // Nullable to indicate uncached state. + private static bool s_isV2RibbonHidden; // Tracks if the ribbon tab is already hidden. + + static RibbonHandler() + { + // Subscribe to the static PluginRecordsChanged event + NAV.ApplicationParts.ApplicationPlugins.PluginRecordsChanged += OnPluginRecordsChanged; + } + + private static void OnPluginRecordsChanged(object sender, EventArgs e) => s_isV2PluginAvailable = null; + + /// + /// Determines whether a command can be executed and manages V2 plugin visibility. + /// + /// The command identifier to check. + /// A CommandState indicating whether the command can be executed. + public override NAV.Plugins.CommandState CanExecuteCommand(string commandId) + { + switch (commandId) + { + case SpeckleV3Tool.COMMAND: + return new NAV.Plugins.CommandState(true); + case SpeckleV2Tool.COMMAND: + { + // Find the v2 plugin + NAV.Plugins.PluginRecord? v2Plugin = PluginUtilities.FindV2Plugin(); + s_isV2PluginAvailable = v2Plugin != null; + + // Pass the plugin to the method for managing ribbon visibility + HideV2RibbonTab(); + + return new NAV.Plugins.CommandState((bool)s_isV2PluginAvailable); + } + default: + return new NAV.Plugins.CommandState(false); + } + } + + private static void HideV2RibbonTab() + { + if (s_isV2RibbonHidden) + { + return; // Skip if already hidden. + } + + var v2RibbonTab = Autodesk.Windows.ComponentManager.Ribbon.Tabs.FirstOrDefault(tab => + tab.Id == SpeckleV2Tool.RIBBON_TAB_ID + SpeckleV2Tool.PLUGIN_SUFFIX + ); + + if (v2RibbonTab == null) + { + return; + } + + Autodesk.Windows.ComponentManager.Ribbon.Tabs.Remove(v2RibbonTab); + s_isV2RibbonHidden = true; // Mark as hidden to avoid redundant calls. + } + + /// + /// Executes the specified command after validating the Navisworks version. + /// + /// The command to execute. + /// Additional command parameters. + /// 0 if successful, non-zero otherwise. + public override int ExecuteCommand(string commandId, params string[] parameters) + { + if (!IsValidVersion()) + { + return 0; + } + + switch (commandId) + { + case SpeckleV3Tool.COMMAND: + HandleCommand(SpeckleV3Tool.PLUGIN, commandId); + break; + + case SpeckleV2Tool.COMMAND: + HandleCommand(SpeckleV2Tool.PLUGIN, $"{SpeckleV2Tool.PLUGIN}.{SpeckleV2Tool.DEVELOPER_ID}"); + break; + default: + { + MessageBox.Show($"You have clicked on an unexpected command with ID = '{commandId}'"); + break; + } + } + + return 0; + } + + private static void HandleCommand(string pluginId, string commandId) + { + if (PluginUtilities.ShouldSkipLoad(pluginId, commandId, true)) + { + return; + } + + var pluginRecord = NavisworksApp.Plugins.FindPlugin(pluginId + SpeckleV3Tool.PLUGIN_SUFFIX); + if (pluginRecord == null) + { + return; + } + + _ = pluginRecord.LoadedPlugin ?? pluginRecord.LoadPlugin(); + PluginUtilities.ActivatePluginPane(pluginRecord); + } + + private static bool IsValidVersion() + { + if (NavisworksApp.Version.RuntimeProductName.Contains(SpeckleV3Tool.Version.ToString().Replace("v", ""))) + { + return true; + } + + MessageBox.Show( + $"This Add-In was built for Navisworks {SpeckleV3Tool.Version}, " + + $"please contact support@speckle.systems for assistance...", + "Cannot Continue!", + MessageBoxButtons.OK, + MessageBoxIcon.Error + ); + return false; + } +} diff --git a/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Plugin/PackageContents.xml b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Plugin/PackageContents.xml new file mode 100644 index 000000000..b2d9acd59 --- /dev/null +++ b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Plugin/PackageContents.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Plugin/Tools/SpeckleV2Tool.cs b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Plugin/Tools/SpeckleV2Tool.cs new file mode 100644 index 000000000..bb6f04a9b --- /dev/null +++ b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Plugin/Tools/SpeckleV2Tool.cs @@ -0,0 +1,10 @@ +namespace Speckle.Connector.Navisworks.Plugin.Tools; + +public static class SpeckleV2Tool +{ + public const string COMMAND = "Speckle_Launch_V2"; + public const string PLUGIN = "SpeckleUI"; + public const string RIBBON_TAB_ID = "SpeckleNavisworks.Speckle"; + public const string PLUGIN_SUFFIX = ".Speckle"; + public const string DEVELOPER_ID = "Speckle"; +} diff --git a/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Plugin/Tools/SpeckleV3Tool.cs b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Plugin/Tools/SpeckleV3Tool.cs new file mode 100644 index 000000000..50bf27676 --- /dev/null +++ b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Plugin/Tools/SpeckleV3Tool.cs @@ -0,0 +1,41 @@ +using Speckle.Sdk.Host; + +namespace Speckle.Connector.Navisworks.Plugin.Tools; + +public static class SpeckleV3Tool +{ + public const string DEVELOPER_ID = "Speckle"; + public const string COMMAND = "Speckle_Launch"; + public const string PLUGIN = "SpeckleUI3"; + public const string PLUGIN_ID = "SpeckleNavisworksNextGen"; + public const string DISPLAY_NAME = "Speckle (Beta)"; + public const string RIBBON_TAB_ID = "Speckle"; + public const string RIBBON_TAB_DISPLAY_NAME = "Speckle"; + public const string RIBBON_STRINGS = "NavisworksRibbon.name"; + public const string PLUGIN_SUFFIX = ".Speckle"; + + public static HostApplication App => +#if NAVIS + HostApplications.Navisworks; +#else + throw new NotSupportedException(); +#endif + public static HostAppVersion Version => +#if NAVIS2020 + HostAppVersion.v2020; +#elif NAVIS2021 + HostAppVersion.v2021; +#elif NAVIS2022 + HostAppVersion.v2022; +#elif NAVIS2023 + HostAppVersion.v2023; +#elif NAVIS2024 + HostAppVersion.v2024; +#elif NAVIS2025 + HostAppVersion.v2025; +#elif NAVIS2026 + HostAppVersion.v2026; +#else + throw new NotSupportedException("This version is not supported"); +#endif +} diff --git a/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Plugin/Utilities.cs b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Plugin/Utilities.cs new file mode 100644 index 000000000..f9c786910 --- /dev/null +++ b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Plugin/Utilities.cs @@ -0,0 +1,31 @@ +using Autodesk.Navisworks.Api.Plugins; +using Speckle.Connector.Navisworks.Plugin.Tools; + +namespace Speckle.Connector.Navisworks.Plugin; + +internal static class PluginUtilities +{ + public static bool ShouldSkipLoad(string plugin, string command, bool notAutomatedCheck) => + notAutomatedCheck && NavisworksApp.IsAutomated || string.IsNullOrEmpty(plugin) || string.IsNullOrEmpty(command); + + internal static PluginRecord? FindV2Plugin() + { + var pluginRecords = NavisworksApp.Plugins.PluginRecords; + var v2Plugin = pluginRecords.FirstOrDefault(p => p.Id == SpeckleV2Tool.RIBBON_TAB_ID); + + return v2Plugin ?? null; + } + + public static void ActivatePluginPane(PluginRecord? pluginRecord) + { + if (pluginRecord is null || !pluginRecord.IsEnabled || !pluginRecord.IsLoaded) + { + return; + } + + if (pluginRecord.LoadedPlugin is DockPanePlugin dockPanePlugin) + { + dockPanePlugin.ActivatePane(); + } + } +} diff --git a/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Resources/v2_logo16.png b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Resources/v2_logo16.png new file mode 100644 index 000000000..5520837af Binary files /dev/null and b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Resources/v2_logo16.png differ diff --git a/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Resources/v2_logo32.png b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Resources/v2_logo32.png new file mode 100644 index 000000000..a96460dad Binary files /dev/null and b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Resources/v2_logo32.png differ diff --git a/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Resources/v3_logo16.png b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Resources/v3_logo16.png new file mode 100644 index 000000000..61872f0d0 Binary files /dev/null and b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Resources/v3_logo16.png differ diff --git a/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Resources/v3_logo32.png b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Resources/v3_logo32.png new file mode 100644 index 000000000..2ad0c346e Binary files /dev/null and b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Resources/v3_logo32.png differ diff --git a/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Speckle.Connectors.NavisworksShared.projitems b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Speckle.Connectors.NavisworksShared.projitems new file mode 100644 index 000000000..0937aeec6 --- /dev/null +++ b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Speckle.Connectors.NavisworksShared.projitems @@ -0,0 +1,67 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + D1D9FBB0-5928-4AD0-9AD3-EC61B7BD96CA + + + Speckle.Connectors.NavisworksShared + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + + + Always + + + diff --git a/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Speckle.Connectors.NavisworksShared.shproj b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Speckle.Connectors.NavisworksShared.shproj new file mode 100644 index 000000000..b41ae7048 --- /dev/null +++ b/Connectors/Navisworks/Speckle.Connectors.NavisworksShared/Speckle.Connectors.NavisworksShared.shproj @@ -0,0 +1,12 @@ + + + + {62813838-52F7-43CB-9062-BB2611C00C79} + + + + + + + diff --git a/Connectors/Revit/Directory.Build.targets b/Connectors/Revit/Directory.Build.targets new file mode 100644 index 000000000..9b8142c9b --- /dev/null +++ b/Connectors/Revit/Directory.Build.targets @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/Connectors/Revit/Speckle.Connectors.Revit2022/packages.lock.json b/Connectors/Revit/Speckle.Connectors.Revit2022/packages.lock.json index 1b575fa06..2a69921f7 100644 --- a/Connectors/Revit/Speckle.Connectors.Revit2022/packages.lock.json +++ b/Connectors/Revit/Speckle.Connectors.Revit2022/packages.lock.json @@ -187,11 +187,6 @@ "resolved": "8.0.0", "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" }, - "Speckle.DoubleNumerics": { - "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w==" - }, "Speckle.Newtonsoft.Json": { "type": "Transitive", "resolved": "13.0.2", @@ -287,9 +282,9 @@ "dependencies": { "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", "Speckle.Connectors.Logging": "[1.0.0, )", - "Speckle.Objects": "[3.1.0-dev.191, )", - "Speckle.Sdk": "[3.1.0-dev.191, )", - "Speckle.Sdk.Dependencies": "[3.1.0-dev.191, )" + "Speckle.Objects": "[3.1.1, )", + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" } }, "speckle.connectors.dui": { @@ -297,9 +292,8 @@ "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", "Speckle.Connectors.Common": "[1.0.0, )", - "Speckle.Sdk": "[3.1.0-dev.191, )", - "Speckle.Sdk.Dependencies": "[3.1.0-dev.191, )", - "System.Threading.Tasks.Dataflow": "[6.0.0, )" + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" } }, "speckle.connectors.logging": { @@ -309,7 +303,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.191, )" + "Speckle.Objects": "[3.1.1, )" } }, "speckle.converters.revit2022": { @@ -352,13 +346,19 @@ "resolved": "2.2.0", "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "+m7jRFm0ABbkcSz2UphdxAsislY10IpQ1u79c8a3aVvegLjnsVQZ1sXfRIRO1aFdulkhjYKXYpB3N9M8Z+epgQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.191" + "Speckle.Sdk": "3.1.1" } }, "Speckle.Revit.API": { @@ -369,9 +369,9 @@ }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "VVT3LJiYlhqnggxxdeTt1QLrqfxDb044x0yX6kxS9b5MRzeDvK2Vz86pLDfuHs+SXvDimRVfYx1M42IW/aPcTQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.Bcl.AsyncInterfaces": "5.0.0", @@ -379,22 +379,16 @@ "Microsoft.Data.Sqlite": "7.0.5", "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", "Microsoft.Extensions.Logging": "2.2.0", - "Speckle.DoubleNumerics": "4.0.1", + "Speckle.DoubleNumerics": "4.1.0", "Speckle.Newtonsoft.Json": "13.0.2", - "Speckle.Sdk.Dependencies": "3.1.0-dev.191" + "Speckle.Sdk.Dependencies": "3.1.1" } }, "Speckle.Sdk.Dependencies": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "EmEOyjsGsNi56Z/ZoBOn8WirTmIT2yqWvlUeUh0BSPX2TDMZXHTKOM/kHmP6HSd10KVFn2Zo/ItY7/K9iRtL1Q==" - }, - "System.Threading.Tasks.Dataflow": { - "type": "CentralTransitive", - "requested": "[6.0.0, )", - "resolved": "6.0.0", - "contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA==" + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" } } } diff --git a/Connectors/Revit/Speckle.Connectors.Revit2023/packages.lock.json b/Connectors/Revit/Speckle.Connectors.Revit2023/packages.lock.json index 9091cd3bc..d57768ba2 100644 --- a/Connectors/Revit/Speckle.Connectors.Revit2023/packages.lock.json +++ b/Connectors/Revit/Speckle.Connectors.Revit2023/packages.lock.json @@ -187,11 +187,6 @@ "resolved": "8.0.0", "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" }, - "Speckle.DoubleNumerics": { - "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w==" - }, "Speckle.Newtonsoft.Json": { "type": "Transitive", "resolved": "13.0.2", @@ -287,9 +282,9 @@ "dependencies": { "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", "Speckle.Connectors.Logging": "[1.0.0, )", - "Speckle.Objects": "[3.1.0-dev.191, )", - "Speckle.Sdk": "[3.1.0-dev.191, )", - "Speckle.Sdk.Dependencies": "[3.1.0-dev.191, )" + "Speckle.Objects": "[3.1.1, )", + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" } }, "speckle.connectors.dui": { @@ -297,9 +292,8 @@ "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", "Speckle.Connectors.Common": "[1.0.0, )", - "Speckle.Sdk": "[3.1.0-dev.191, )", - "Speckle.Sdk.Dependencies": "[3.1.0-dev.191, )", - "System.Threading.Tasks.Dataflow": "[6.0.0, )" + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" } }, "speckle.connectors.logging": { @@ -309,7 +303,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.191, )" + "Speckle.Objects": "[3.1.1, )" } }, "speckle.converters.revit2023": { @@ -352,13 +346,19 @@ "resolved": "2.2.0", "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "+m7jRFm0ABbkcSz2UphdxAsislY10IpQ1u79c8a3aVvegLjnsVQZ1sXfRIRO1aFdulkhjYKXYpB3N9M8Z+epgQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.191" + "Speckle.Sdk": "3.1.1" } }, "Speckle.Revit.API": { @@ -369,9 +369,9 @@ }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "VVT3LJiYlhqnggxxdeTt1QLrqfxDb044x0yX6kxS9b5MRzeDvK2Vz86pLDfuHs+SXvDimRVfYx1M42IW/aPcTQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.Bcl.AsyncInterfaces": "5.0.0", @@ -379,22 +379,16 @@ "Microsoft.Data.Sqlite": "7.0.5", "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", "Microsoft.Extensions.Logging": "2.2.0", - "Speckle.DoubleNumerics": "4.0.1", + "Speckle.DoubleNumerics": "4.1.0", "Speckle.Newtonsoft.Json": "13.0.2", - "Speckle.Sdk.Dependencies": "3.1.0-dev.191" + "Speckle.Sdk.Dependencies": "3.1.1" } }, "Speckle.Sdk.Dependencies": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "EmEOyjsGsNi56Z/ZoBOn8WirTmIT2yqWvlUeUh0BSPX2TDMZXHTKOM/kHmP6HSd10KVFn2Zo/ItY7/K9iRtL1Q==" - }, - "System.Threading.Tasks.Dataflow": { - "type": "CentralTransitive", - "requested": "[6.0.0, )", - "resolved": "6.0.0", - "contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA==" + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" } } } diff --git a/Connectors/Revit/Speckle.Connectors.Revit2024/packages.lock.json b/Connectors/Revit/Speckle.Connectors.Revit2024/packages.lock.json index f7f994215..b17d84681 100644 --- a/Connectors/Revit/Speckle.Connectors.Revit2024/packages.lock.json +++ b/Connectors/Revit/Speckle.Connectors.Revit2024/packages.lock.json @@ -187,11 +187,6 @@ "resolved": "8.0.0", "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" }, - "Speckle.DoubleNumerics": { - "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w==" - }, "Speckle.Newtonsoft.Json": { "type": "Transitive", "resolved": "13.0.2", @@ -287,9 +282,9 @@ "dependencies": { "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", "Speckle.Connectors.Logging": "[1.0.0, )", - "Speckle.Objects": "[3.1.0-dev.191, )", - "Speckle.Sdk": "[3.1.0-dev.191, )", - "Speckle.Sdk.Dependencies": "[3.1.0-dev.191, )" + "Speckle.Objects": "[3.1.1, )", + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" } }, "speckle.connectors.dui": { @@ -297,9 +292,8 @@ "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", "Speckle.Connectors.Common": "[1.0.0, )", - "Speckle.Sdk": "[3.1.0-dev.191, )", - "Speckle.Sdk.Dependencies": "[3.1.0-dev.191, )", - "System.Threading.Tasks.Dataflow": "[6.0.0, )" + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" } }, "speckle.connectors.logging": { @@ -309,7 +303,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.191, )" + "Speckle.Objects": "[3.1.1, )" } }, "speckle.converters.revit2024": { @@ -352,13 +346,19 @@ "resolved": "2.2.0", "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "+m7jRFm0ABbkcSz2UphdxAsislY10IpQ1u79c8a3aVvegLjnsVQZ1sXfRIRO1aFdulkhjYKXYpB3N9M8Z+epgQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.191" + "Speckle.Sdk": "3.1.1" } }, "Speckle.Revit.API": { @@ -369,9 +369,9 @@ }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "VVT3LJiYlhqnggxxdeTt1QLrqfxDb044x0yX6kxS9b5MRzeDvK2Vz86pLDfuHs+SXvDimRVfYx1M42IW/aPcTQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.Bcl.AsyncInterfaces": "5.0.0", @@ -379,22 +379,16 @@ "Microsoft.Data.Sqlite": "7.0.5", "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", "Microsoft.Extensions.Logging": "2.2.0", - "Speckle.DoubleNumerics": "4.0.1", + "Speckle.DoubleNumerics": "4.1.0", "Speckle.Newtonsoft.Json": "13.0.2", - "Speckle.Sdk.Dependencies": "3.1.0-dev.191" + "Speckle.Sdk.Dependencies": "3.1.1" } }, "Speckle.Sdk.Dependencies": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "EmEOyjsGsNi56Z/ZoBOn8WirTmIT2yqWvlUeUh0BSPX2TDMZXHTKOM/kHmP6HSd10KVFn2Zo/ItY7/K9iRtL1Q==" - }, - "System.Threading.Tasks.Dataflow": { - "type": "CentralTransitive", - "requested": "[6.0.0, )", - "resolved": "6.0.0", - "contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA==" + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" } } } diff --git a/Connectors/Revit/Speckle.Connectors.Revit2025/Speckle.Connectors.Revit2025.csproj b/Connectors/Revit/Speckle.Connectors.Revit2025/Speckle.Connectors.Revit2025.csproj index bb46333a0..d07c52a8c 100644 --- a/Connectors/Revit/Speckle.Connectors.Revit2025/Speckle.Connectors.Revit2025.csproj +++ b/Connectors/Revit/Speckle.Connectors.Revit2025/Speckle.Connectors.Revit2025.csproj @@ -1,7 +1,7 @@  net8.0-windows - x64 + x64 win-x64 true 2025 @@ -18,7 +18,7 @@ - + @@ -28,7 +28,6 @@ - Always diff --git a/Connectors/Revit/Speckle.Connectors.Revit2025/packages.lock.json b/Connectors/Revit/Speckle.Connectors.Revit2025/packages.lock.json index ae1f267a0..d3c894b8b 100644 --- a/Connectors/Revit/Speckle.Connectors.Revit2025/packages.lock.json +++ b/Connectors/Revit/Speckle.Connectors.Revit2025/packages.lock.json @@ -172,11 +172,6 @@ "resolved": "8.0.0", "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" }, - "Speckle.DoubleNumerics": { - "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w==" - }, "Speckle.Newtonsoft.Json": { "type": "Transitive", "resolved": "13.0.2", @@ -237,9 +232,9 @@ "dependencies": { "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", "Speckle.Connectors.Logging": "[1.0.0, )", - "Speckle.Objects": "[3.1.0-dev.191, )", - "Speckle.Sdk": "[3.1.0-dev.191, )", - "Speckle.Sdk.Dependencies": "[3.1.0-dev.191, )" + "Speckle.Objects": "[3.1.1, )", + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" } }, "speckle.connectors.dui": { @@ -247,16 +242,8 @@ "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", "Speckle.Connectors.Common": "[1.0.0, )", - "Speckle.Sdk": "[3.1.0-dev.191, )", - "Speckle.Sdk.Dependencies": "[3.1.0-dev.191, )", - "System.Threading.Tasks.Dataflow": "[6.0.0, )" - } - }, - "speckle.connectors.dui.webview": { - "type": "Project", - "dependencies": { - "Microsoft.Web.WebView2": "[1.0.1938.49, )", - "Speckle.Connectors.DUI": "[1.0.0, )" + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" } }, "speckle.connectors.logging": { @@ -266,7 +253,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.191, )" + "Speckle.Objects": "[3.1.1, )" } }, "speckle.converters.revit2025": { @@ -309,19 +296,19 @@ "resolved": "2.2.0", "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" }, - "Microsoft.Web.WebView2": { + "Speckle.DoubleNumerics": { "type": "CentralTransitive", - "requested": "[1.0.1938.49, )", - "resolved": "1.0.1938.49", - "contentHash": "z8KnFnaTYzhA/ZnyRX0qGfS1NU5ZBJeClAH64F0fVDvdDJTvME7xl6zTJ0Jlfe1BtL3C0NH9xTy64shg2baKdw==" + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "+m7jRFm0ABbkcSz2UphdxAsislY10IpQ1u79c8a3aVvegLjnsVQZ1sXfRIRO1aFdulkhjYKXYpB3N9M8Z+epgQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.191" + "Speckle.Sdk": "3.1.1" } }, "Speckle.Revit.API": { @@ -332,31 +319,25 @@ }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "VVT3LJiYlhqnggxxdeTt1QLrqfxDb044x0yX6kxS9b5MRzeDvK2Vz86pLDfuHs+SXvDimRVfYx1M42IW/aPcTQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.CSharp": "4.7.0", "Microsoft.Data.Sqlite": "7.0.5", "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", "Microsoft.Extensions.Logging": "2.2.0", - "Speckle.DoubleNumerics": "4.0.1", + "Speckle.DoubleNumerics": "4.1.0", "Speckle.Newtonsoft.Json": "13.0.2", - "Speckle.Sdk.Dependencies": "3.1.0-dev.191" + "Speckle.Sdk.Dependencies": "3.1.1" } }, "Speckle.Sdk.Dependencies": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "EmEOyjsGsNi56Z/ZoBOn8WirTmIT2yqWvlUeUh0BSPX2TDMZXHTKOM/kHmP6HSd10KVFn2Zo/ItY7/K9iRtL1Q==" - }, - "System.Threading.Tasks.Dataflow": { - "type": "CentralTransitive", - "requested": "[6.0.0, )", - "resolved": "6.0.0", - "contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA==" + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" } }, "net8.0-windows7.0/win-x64": { @@ -385,12 +366,6 @@ "type": "Transitive", "resolved": "2.1.4", "contentHash": "2C9Q9eX7CPLveJA0rIhf9RXAvu+7nWZu1A2MdG6SD/NOu26TakGgL1nsbc0JAspGijFOo3HoN79xrx8a368fBg==" - }, - "Microsoft.Web.WebView2": { - "type": "CentralTransitive", - "requested": "[1.0.1938.49, )", - "resolved": "1.0.1938.49", - "contentHash": "z8KnFnaTYzhA/ZnyRX0qGfS1NU5ZBJeClAH64F0fVDvdDJTvME7xl6zTJ0Jlfe1BtL3C0NH9xTy64shg2baKdw==" } } } diff --git a/Connectors/Revit/Speckle.Connectors.RevitShared.Cef/CefSharpPanel.xaml.cs b/Connectors/Revit/Speckle.Connectors.RevitShared.Cef/CefSharpPanel.xaml.cs index ae78681ed..98e98c88f 100644 --- a/Connectors/Revit/Speckle.Connectors.RevitShared.Cef/CefSharpPanel.xaml.cs +++ b/Connectors/Revit/Speckle.Connectors.RevitShared.Cef/CefSharpPanel.xaml.cs @@ -13,15 +13,9 @@ public partial class CefSharpPanel : Page, Autodesk.Revit.UI.IDockablePaneProvid InitializeComponent(); } - public Task ExecuteScriptAsyncMethod(string script, CancellationToken cancellationToken) + public void ExecuteScript(string script) { - Browser.Dispatcher.Invoke( - () => Browser.ExecuteScriptAsync(script), - DispatcherPriority.Background, - cancellationToken - ); - - return Task.CompletedTask; + Browser.Dispatcher.Invoke(() => Browser.ExecuteScriptAsync(script), DispatcherPriority.Background); } public bool IsBrowserInitialized => Browser.IsBrowserInitialized; diff --git a/Connectors/Revit/Speckle.Connectors.RevitShared/Bindings/BasicConnectorBindingRevit.cs b/Connectors/Revit/Speckle.Connectors.RevitShared/Bindings/BasicConnectorBindingRevit.cs index d118b513c..24e8e1f28 100644 --- a/Connectors/Revit/Speckle.Connectors.RevitShared/Bindings/BasicConnectorBindingRevit.cs +++ b/Connectors/Revit/Speckle.Connectors.RevitShared/Bindings/BasicConnectorBindingRevit.cs @@ -1,10 +1,8 @@ using Autodesk.Revit.DB; -using Microsoft.Extensions.Logging; using Revit.Async; using Speckle.Connectors.DUI.Bridge; using Speckle.Connectors.DUI.Models; using Speckle.Connectors.DUI.Models.Card; -using Speckle.Connectors.Revit.HostApp; using Speckle.Connectors.RevitShared; using Speckle.Connectors.RevitShared.Operations.Send.Filters; using Speckle.Converters.RevitShared.Helpers; @@ -21,35 +19,31 @@ internal sealed class BasicConnectorBindingRevit : IBasicConnectorBinding public BasicConnectorBindingCommands Commands { get; } - private readonly APIContext _apiContext; private readonly DocumentModelStore _store; private readonly RevitContext _revitContext; private readonly ISpeckleApplication _speckleApplication; - private readonly ILogger _logger; + private readonly ITopLevelExceptionHandler _topLevelExceptionHandler; public BasicConnectorBindingRevit( - APIContext apiContext, DocumentModelStore store, IBrowserBridge parent, RevitContext revitContext, ISpeckleApplication speckleApplication, - ILogger logger + ITopLevelExceptionHandler topLevelExceptionHandler ) { Name = "baseBinding"; Parent = parent; - _apiContext = apiContext; _store = store; _revitContext = revitContext; _speckleApplication = speckleApplication; - _logger = logger; + _topLevelExceptionHandler = topLevelExceptionHandler; Commands = new BasicConnectorBindingCommands(parent); - // POC: event binding? _store.DocumentChanged += (_, _) => - parent.TopLevelExceptionHandler.FireAndForget(async () => + _topLevelExceptionHandler.FireAndForget(async () => { - await Commands.NotifyDocumentChanged().ConfigureAwait(false); + await Commands.NotifyDocumentChanged(); }); } @@ -82,7 +76,7 @@ internal sealed class BasicConnectorBindingRevit : IBasicConnectorBinding public DocumentModelStore GetDocumentState() => _store; - public void AddModel(ModelCard model) => _store.Models.Add(model); + public void AddModel(ModelCard model) => _store.AddModel(model); public void UpdateModel(ModelCard model) => _store.UpdateModel(model); @@ -91,13 +85,6 @@ internal sealed class BasicConnectorBindingRevit : IBasicConnectorBinding public async Task HighlightModel(string modelCardId) { var model = _store.GetModelById(modelCardId); - - if (model is null) - { - _logger.LogError("Model was null when highlighting received model"); - return; - } - var activeUIDoc = _revitContext.UIApplication?.ActiveUIDocument ?? throw new SpeckleException("Unable to retrieve active UI document"); @@ -108,21 +95,21 @@ internal sealed class BasicConnectorBindingRevit : IBasicConnectorBinding { if (senderModelCard.SendFilter is IRevitSendFilter revitFilter) { - revitFilter.SetContext(_revitContext, _apiContext); + revitFilter.SetContext(_revitContext); } if (senderModelCard.SendFilter is RevitViewsFilter revitViewsFilter) { - await _apiContext - .Run(() => - { - var view = revitViewsFilter.GetView(); - if (view is not null) + var view = revitViewsFilter.GetView(); + if (view is not null) + { + await RevitTask + .RunAsync(() => { _revitContext.UIApplication.ActiveUIDocument.ActiveView = view; - } - }) - .ConfigureAwait(false); + }) + .ConfigureAwait(false); + } return; } @@ -147,13 +134,11 @@ internal sealed class BasicConnectorBindingRevit : IBasicConnectorBinding if (elementIds.Count == 0) { - await Commands - .SetModelError(modelCardId, new InvalidOperationException("No objects found to highlight.")) - .ConfigureAwait(false); + await Commands.SetModelError(modelCardId, new InvalidOperationException("No objects found to highlight.")); return; } - await HighlightObjectsOnView(elementIds).ConfigureAwait(false); + await HighlightObjectsOnView(elementIds); } /// @@ -174,7 +159,6 @@ internal sealed class BasicConnectorBindingRevit : IBasicConnectorBinding .ToList() ) .ConfigureAwait(false); - ; } private async Task HighlightObjectsOnView(List objectIds) @@ -184,7 +168,6 @@ internal sealed class BasicConnectorBindingRevit : IBasicConnectorBinding _revitContext.UIApplication?.ActiveUIDocument ?? throw new SpeckleException("Unable to retrieve active UI document"); - // UiDocument operations should be wrapped into RevitTask, otherwise doesn't work on other tasks. await RevitTask .RunAsync(() => { @@ -192,6 +175,9 @@ internal sealed class BasicConnectorBindingRevit : IBasicConnectorBinding activeUIDoc.ShowElements(objectIds); }) .ConfigureAwait(false); - ; + + // activeUIDoc.Selection.SetElementIds(objectIds); + // activeUIDoc.ShowElements(objectIds); + // ; } } diff --git a/Connectors/Revit/Speckle.Connectors.RevitShared/Bindings/RevitBaseBinding.cs b/Connectors/Revit/Speckle.Connectors.RevitShared/Bindings/RevitBaseBinding.cs index 04c363a18..6f71371f0 100644 --- a/Connectors/Revit/Speckle.Connectors.RevitShared/Bindings/RevitBaseBinding.cs +++ b/Connectors/Revit/Speckle.Connectors.RevitShared/Bindings/RevitBaseBinding.cs @@ -1,7 +1,5 @@ using Speckle.Connectors.DUI.Bindings; using Speckle.Connectors.DUI.Bridge; -using Speckle.Connectors.DUI.Models; -using Speckle.Converters.RevitShared.Helpers; namespace Speckle.Connectors.Revit.Bindings; @@ -11,14 +9,9 @@ internal abstract class RevitBaseBinding : IBinding public string Name { get; } public IBrowserBridge Parent { get; } - protected readonly DocumentModelStore Store; - protected readonly RevitContext RevitContext; - - protected RevitBaseBinding(string name, DocumentModelStore store, IBrowserBridge parent, RevitContext revitContext) + protected RevitBaseBinding(string name, IBrowserBridge parent) { Name = name; Parent = parent; - Store = store; - RevitContext = revitContext; } } diff --git a/Connectors/Revit/Speckle.Connectors.RevitShared/Bindings/RevitReceiveBinding.cs b/Connectors/Revit/Speckle.Connectors.RevitShared/Bindings/RevitReceiveBinding.cs index 27bb4868a..d2a14f79c 100644 --- a/Connectors/Revit/Speckle.Connectors.RevitShared/Bindings/RevitReceiveBinding.cs +++ b/Connectors/Revit/Speckle.Connectors.RevitShared/Bindings/RevitReceiveBinding.cs @@ -8,6 +8,7 @@ using Speckle.Connectors.DUI.Bridge; using Speckle.Connectors.DUI.Logging; using Speckle.Connectors.DUI.Models; using Speckle.Connectors.DUI.Models.Card; +using Speckle.Connectors.Revit.Plugin; using Speckle.Converters.Common; using Speckle.Converters.RevitShared.Settings; using Speckle.Sdk; @@ -21,7 +22,7 @@ internal sealed class RevitReceiveBinding : IReceiveBinding private readonly IOperationProgressManager _operationProgressManager; private readonly ILogger _logger; - private readonly CancellationManager _cancellationManager; + private readonly ICancellationManager _cancellationManager; private readonly DocumentModelStore _store; private readonly IServiceProvider _serviceProvider; private readonly IRevitConversionSettingsFactory _revitConversionSettingsFactory; @@ -30,7 +31,7 @@ internal sealed class RevitReceiveBinding : IReceiveBinding public RevitReceiveBinding( DocumentModelStore store, - CancellationManager cancellationManager, + ICancellationManager cancellationManager, IBrowserBridge parent, IServiceProvider serviceProvider, IOperationProgressManager operationProgressManager, @@ -64,7 +65,7 @@ internal sealed class RevitReceiveBinding : IReceiveBinding throw new InvalidOperationException("No download model card was found."); } - CancellationToken cancellationToken = _cancellationManager.InitCancellationTokenSource(modelCardId); + using var cancellationItem = _cancellationManager.GetCancellationItem(modelCardId); using var scope = _serviceProvider.CreateScope(); scope @@ -81,15 +82,16 @@ internal sealed class RevitReceiveBinding : IReceiveBinding .ServiceProvider.GetRequiredService() .Execute( modelCard.GetReceiveInfo(_speckleApplication.Slug), - _operationProgressManager.CreateOperationProgressEventHandler(Parent, modelCardId, cancellationToken), - cancellationToken - ) - .ConfigureAwait(false); + _operationProgressManager.CreateOperationProgressEventHandler(Parent, modelCardId, cancellationItem.Token), + cancellationItem.Token + ); modelCard.BakedObjectIds = conversionResults.BakedObjectIds.ToList(); - await Commands - .SetModelReceiveResult(modelCardId, conversionResults.BakedObjectIds, conversionResults.ConversionResults) - .ConfigureAwait(false); + await Commands.SetModelReceiveResult( + modelCardId, + conversionResults.BakedObjectIds, + conversionResults.ConversionResults + ); } catch (OperationCanceledException) { @@ -97,10 +99,19 @@ internal sealed class RevitReceiveBinding : IReceiveBinding // Idea for later -> when cancel called, create promise from UI to solve it later with this catch block. // So have 3 state on UI -> Cancellation clicked -> Cancelling -> Cancelled } + catch (SpeckleRevitTaskException ex) + { + await SpeckleRevitTaskException.ProcessException(modelCardId, ex, _logger, Commands); + } catch (Exception ex) when (!ex.IsFatal()) // UX reasons - we will report operation exceptions as model card error. We may change this later when we have more exception documentation { _logger.LogModelCardHandledError(ex); - await Commands.SetModelError(modelCardId, ex).ConfigureAwait(false); + await Commands.SetModelError(modelCardId, ex); + } + finally + { + // otherwise the id of the operation persists on the cancellation manager and triggers 'Operations cancelled because of document swap!' message to UI. + _cancellationManager.CancelOperation(modelCardId); } } } diff --git a/Connectors/Revit/Speckle.Connectors.RevitShared/Bindings/RevitSendBinding.cs b/Connectors/Revit/Speckle.Connectors.RevitShared/Bindings/RevitSendBinding.cs index f4c724515..ede591e1f 100644 --- a/Connectors/Revit/Speckle.Connectors.RevitShared/Bindings/RevitSendBinding.cs +++ b/Connectors/Revit/Speckle.Connectors.RevitShared/Bindings/RevitSendBinding.cs @@ -28,9 +28,10 @@ namespace Speckle.Connectors.Revit.Bindings; internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding { - private readonly IRevitIdleManager _idleManager; - private readonly APIContext _apiContext; - private readonly CancellationManager _cancellationManager; + private readonly IAppIdleManager _idleManager; + private readonly RevitContext _revitContext; + private readonly DocumentModelStore _store; + private readonly ICancellationManager _cancellationManager; private readonly IServiceProvider _serviceProvider; private readonly ISendConversionCache _sendConversionCache; private readonly IOperationProgressManager _operationProgressManager; @@ -39,6 +40,7 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding private readonly ElementUnpacker _elementUnpacker; private readonly IRevitConversionSettingsFactory _revitConversionSettingsFactory; private readonly ISpeckleApplication _speckleApplication; + private readonly ITopLevelExceptionHandler _topLevelExceptionHandler; /// /// Used internally to aggregate the changed objects' id. Note we're using a concurrent dictionary here as the expiry check method is not thread safe, and this was causing problems. See: @@ -49,11 +51,10 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding private ConcurrentDictionary ChangedObjectIds { get; set; } = new(); public RevitSendBinding( - IRevitIdleManager idleManager, + IAppIdleManager idleManager, RevitContext revitContext, - APIContext apiContext, DocumentModelStore store, - CancellationManager cancellationManager, + ICancellationManager cancellationManager, IBrowserBridge bridge, IServiceProvider serviceProvider, ISendConversionCache sendConversionCache, @@ -62,12 +63,14 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding ILogger logger, ElementUnpacker elementUnpacker, IRevitConversionSettingsFactory revitConversionSettingsFactory, - ISpeckleApplication speckleApplication + ISpeckleApplication speckleApplication, + ITopLevelExceptionHandler topLevelExceptionHandler ) - : base("sendBinding", store, bridge, revitContext) + : base("sendBinding", bridge) { _idleManager = idleManager; - _apiContext = apiContext; + _revitContext = revitContext; + _store = store; _cancellationManager = cancellationManager; _serviceProvider = serviceProvider; _sendConversionCache = sendConversionCache; @@ -77,25 +80,24 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding _elementUnpacker = elementUnpacker; _revitConversionSettingsFactory = revitConversionSettingsFactory; _speckleApplication = speckleApplication; - var topLevelExceptionHandler = Parent.TopLevelExceptionHandler; + _topLevelExceptionHandler = topLevelExceptionHandler; Commands = new SendBindingUICommands(bridge); // TODO expiry events // TODO filters need refresh events - _idleManager.RunAsync(() => - { - revitContext.UIApplication.NotNull().Application.DocumentChanged += (_, e) => - topLevelExceptionHandler.CatchUnhandled(() => DocChangeHandler(e)); - }); - Store.DocumentChanged += (_, _) => - topLevelExceptionHandler.FireAndForget(async () => await OnDocumentChanged().ConfigureAwait(false)); + + revitContext.UIApplication.NotNull().Application.DocumentChanged += (_, e) => + _topLevelExceptionHandler.CatchUnhandled(() => DocChangeHandler(e)); + _store.DocumentChanged += (_, _) => topLevelExceptionHandler.FireAndForget(async () => await OnDocumentChanged()); } + private async Task OnDocumentStoreChangedEvent(object _) => await Commands.NotifyDocumentChanged(); + public List GetSendFilters() => [ new RevitSelectionFilter() { IsDefault = true }, - new RevitViewsFilter(RevitContext, _apiContext), - new RevitCategoriesFilter(RevitContext, _apiContext) + new RevitViewsFilter(_revitContext), + new RevitCategoriesFilter(_revitContext) ]; public List GetSendSettings() => @@ -114,13 +116,13 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding // Note: removed top level handling thing as it was confusing me try { - if (Store.GetModelById(modelCardId) is not SenderModelCard modelCard) + if (_store.GetModelById(modelCardId) is not SenderModelCard modelCard) { // Handle as GLOBAL ERROR at BrowserBridge throw new InvalidOperationException("No publish model card was found."); } - CancellationToken cancellationToken = _cancellationManager.InitCancellationTokenSource(modelCardId); + using var cancellationItem = _cancellationManager.GetCancellationItem(modelCardId); using var scope = _serviceProvider.CreateScope(); scope @@ -133,7 +135,7 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding ) ); - List elements = await RefreshElementsOnSender(modelCard.NotNull()).ConfigureAwait(false); + List elements = await RefreshElementsOnSender(modelCard.NotNull()); List elementIds = elements.Select(el => el.Id).ToList(); if (elementIds.Count == 0) @@ -146,15 +148,12 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding .ServiceProvider.GetRequiredService>() .Execute( elementIds, - modelCard.GetSendInfo(_speckleApplication.Slug), - _operationProgressManager.CreateOperationProgressEventHandler(Parent, modelCardId, cancellationToken), - cancellationToken - ) - .ConfigureAwait(false); + modelCard.GetSendInfo(_speckleApplication.ApplicationAndVersion), + _operationProgressManager.CreateOperationProgressEventHandler(Parent, modelCardId, cancellationItem.Token), + cancellationItem.Token + ); - await Commands - .SetModelSendResult(modelCardId, sendResult.RootObjId, sendResult.ConversionResults) - .ConfigureAwait(false); + await Commands.SetModelSendResult(modelCardId, sendResult.RootObjId, sendResult.ConversionResults); } catch (OperationCanceledException) { @@ -162,27 +161,34 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding // Idea for later -> when cancel called, create promise from UI to solve it later with this catch block. // So have 3 state on UI -> Cancellation clicked -> Cancelling -> Cancelled } + catch (SpeckleRevitTaskException ex) + { + await SpeckleRevitTaskException.ProcessException(modelCardId, ex, _logger, Commands); + } catch (Exception ex) when (!ex.IsFatal()) // UX reasons - we will report operation exceptions as model card error. We may change this later when we have more exception documentation { _logger.LogModelCardHandledError(ex); - await Commands.SetModelError(modelCardId, ex).ConfigureAwait(false); + await Commands.SetModelError(modelCardId, ex); + } + finally + { + // otherwise the id of the operation persists on the cancellation manager and triggers 'Operations cancelled because of document swap!' message to UI. + _cancellationManager.CancelOperation(modelCardId); } } private async Task> RefreshElementsOnSender(SenderModelCard modelCard) { var activeUIDoc = - RevitContext.UIApplication?.ActiveUIDocument + _revitContext.UIApplication.NotNull().ActiveUIDocument ?? throw new SpeckleException("Unable to retrieve active UI document"); if (modelCard.SendFilter is IRevitSendFilter viewFilter) { - viewFilter.SetContext(RevitContext, _apiContext); + viewFilter.SetContext(_revitContext); } - var selectedObjects = await _apiContext - .Run(_ => modelCard.SendFilter.NotNull().RefreshObjectIds()) - .ConfigureAwait(false); + var selectedObjects = modelCard.SendFilter.NotNull().RefreshObjectIds(); List elements = selectedObjects .Select(uid => activeUIDoc.Document.GetElement(uid)) @@ -199,9 +205,11 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding } // We update the state on the UI SenderModelCard to prevent potential inconsistencies between hostApp IdMap in sendfilters. - await Commands - .SetFilterObjectIds(modelCard.ModelCardId.NotNull(), modelCard.SendFilter.IdMap, newSelectedObjectIds) - .ConfigureAwait(false); + await Commands.SetFilterObjectIds( + modelCard.ModelCardId.NotNull(), + modelCard.SendFilter.IdMap, + newSelectedObjectIds + ); } return elements; @@ -254,7 +262,7 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding if (HaveUnitsChanged(e.GetDocument())) { var objectIds = new List(); - foreach (var sender in Store.GetSenders().ToList()) + foreach (var sender in _store.GetSenders().ToList()) { if (sender.SendFilter is null) { @@ -308,9 +316,9 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding private async Task PostSetObjectIds() { - foreach (var sender in Store.GetSenders().ToList()) + foreach (var sender in _store.GetSenders().ToList()) { - await RefreshElementsOnSender(sender).ConfigureAwait(false); + await RefreshElementsOnSender(sender); } } @@ -325,20 +333,24 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding // var intersection = ChangedObjectIds.Keys.Intersect(views).ToList(); // if (intersection.Count != 0) // { - // await Commands.RefreshSendFilters().ConfigureAwait(false); + // await Commands.RefreshSendFilters(); // } - if (ChangedObjectIds.Keys.Any(e => RevitContext.UIApplication?.ActiveUIDocument.Document.GetElement(e) is View)) + if ( + ChangedObjectIds.Keys.Any(e => + _revitContext.UIApplication.NotNull().ActiveUIDocument.Document.GetElement(e) is View + ) + ) { - await Commands.RefreshSendFilters().ConfigureAwait(false); + await Commands.RefreshSendFilters(); } } private async Task RunExpirationChecks() { - var senders = Store.GetSenders().ToList(); + var senders = _store.GetSenders().ToList(); // string[] objectIdsList = ChangedObjectIds.Keys.ToArray(); - var doc = RevitContext.UIApplication?.ActiveUIDocument.Document; + var doc = _revitContext.UIApplication.NotNull().ActiveUIDocument.Document; if (doc == null) { @@ -346,15 +358,32 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding } var objUniqueIds = new List(); + var changedIds = ChangedObjectIds.Keys.ToList(); + + // Handling type changes: if an element's type is changed, we need to mark as changed all objects that have that type. + // Step 1: get any changed types + var elementTypeIdsList = changedIds + .Select(e => doc.GetElement(e)) + .OfType() + .Select(el => el.Id) + .ToArray(); + + // Step 2: Find all elements of the changed types, and add them to the changed ids list. + if (elementTypeIdsList.Length != 0) + { + using var collector = new FilteredElementCollector(doc); + var collectorElements = collector + .WhereElementIsNotElementType() + .Where(e => elementTypeIdsList.Contains(e.GetTypeId())); + foreach (var elm in collectorElements) + { + changedIds.Add(elm.Id); + } + } foreach (var sender in senders) { - // if (sender.SendFilter is null) // NOTE: RunExpirationChecks sometimes triggered unnecessarily before send and, we didn't set up yet IdMap, if so we do not need to deal with it - // { - // continue; - // } - - foreach (var changedElementId in ChangedObjectIds.Keys) + foreach (var changedElementId in changedIds) { if (sender.SendFilter?.IdMap?.TryGetValue(changedElementId.ToString(), out var id) ?? false) { @@ -363,21 +392,6 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding } } - // foreach (var changedElementId in ChangedObjectIds.Keys.ToArray()) - // { - // foreach (var sender in senders) - // { - // if (sender.SendFilter.NotNull().IdMap is null) - // { - // continue; - // } - // if (sender.SendFilter.NotNull().IdMap.NotNull().ContainsKey(changedElementId.ToString())) - // { - // objUniqueIds.Add(sender.SendFilter.NotNull().IdMap.NotNull()[changedElementId.ToString()]); - // } - // } - // } - var unpackedObjectIds = _elementUnpacker.GetUnpackedElementIds(objUniqueIds); _sendConversionCache.EvictObjects(unpackedObjectIds); @@ -387,7 +401,12 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding { if (modelCard.SendFilter is IRevitSendFilter viewFilter) { - viewFilter.SetContext(RevitContext, _apiContext); + viewFilter.SetContext(_revitContext); + } + + if (modelCard.SendFilter is null || modelCard.SendFilter.IdMap is null) + { + continue; } var selectedObjects = modelCard.SendFilter.NotNull().IdMap.NotNull().Values; @@ -399,7 +418,7 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding } } - await Commands.SetModelsExpired(expiredSenderIds).ConfigureAwait(false); + await Commands.SetModelsExpired(expiredSenderIds); ChangedObjectIds = new(); } @@ -412,13 +431,11 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding if (_cancellationManager.NumberOfOperations > 0) { _cancellationManager.CancelAllOperations(); - await Commands - .SetGlobalNotification( - ToastNotificationType.INFO, - "Document Switch", - "Operations cancelled because of document swap!" - ) - .ConfigureAwait(false); + await Commands.SetGlobalNotification( + ToastNotificationType.INFO, + "Document Switch", + "Operations cancelled because of document swap!" + ); } } } diff --git a/Connectors/Revit/Speckle.Connectors.RevitShared/Bindings/SelectionBinding.cs b/Connectors/Revit/Speckle.Connectors.RevitShared/Bindings/SelectionBinding.cs index 174131ee2..953cfe56c 100644 --- a/Connectors/Revit/Speckle.Connectors.RevitShared/Bindings/SelectionBinding.cs +++ b/Connectors/Revit/Speckle.Connectors.RevitShared/Bindings/SelectionBinding.cs @@ -1,7 +1,5 @@ using Speckle.Connectors.DUI.Bindings; using Speckle.Connectors.DUI.Bridge; -using Speckle.Connectors.DUI.Models; -using Speckle.Connectors.Revit.Plugin; using Speckle.Converters.RevitShared.Helpers; using Speckle.Sdk.Common; @@ -13,32 +11,37 @@ internal sealed class SelectionBinding : RevitBaseBinding, ISelectionBinding, ID #if REVIT2022 private readonly System.Timers.Timer _selectionTimer; #endif + private readonly RevitContext _revitContext; + private readonly IAppIdleManager _idleManager; + private readonly ITopLevelExceptionHandler _topLevelExceptionHandler; public SelectionBinding( RevitContext revitContext, - DocumentModelStore store, - IRevitIdleManager revitIdleManager, - IBrowserBridge parent + IBrowserBridge parent, + IAppIdleManager idleManager, + ITopLevelExceptionHandler topLevelExceptionHandler ) - : base("selectionBinding", store, parent, revitContext) + : base("selectionBinding", parent) { + _revitContext = revitContext; + _idleManager = idleManager; + _topLevelExceptionHandler = topLevelExceptionHandler; + #if REVIT2022 // NOTE: getting the selection data should be a fast function all, even for '000s of elements - and having a timer hitting it every 1s is ok. _selectionTimer = new System.Timers.Timer(1000); - _selectionTimer.Elapsed += (_, _) => parent.TopLevelExceptionHandler.CatchUnhandled(OnSelectionChanged); + _selectionTimer.Elapsed += (_, _) => _topLevelExceptionHandler.CatchUnhandled(OnSelectionChanged); _selectionTimer.Start(); #else - revitIdleManager.RunAsync(() => - { - RevitContext.UIApplication.NotNull().SelectionChanged += (_, _) => - revitIdleManager.SubscribeToIdle(nameof(SelectionBinding), OnSelectionChanged); - }); + + _revitContext.UIApplication.NotNull().SelectionChanged += (_, _) => + _idleManager.SubscribeToIdle(nameof(OnSelectionChanged), OnSelectionChanged); #endif } private void OnSelectionChanged() { - if (RevitContext.UIApplication == null || RevitContext.UIApplication.ActiveUIDocument == null) + if (_revitContext.UIApplication.NotNull().ActiveUIDocument == null) { return; } @@ -47,12 +50,12 @@ internal sealed class SelectionBinding : RevitBaseBinding, ISelectionBinding, ID public SelectionInfo GetSelection() { - if (RevitContext.UIApplication == null || RevitContext.UIApplication.ActiveUIDocument == null) + if (_revitContext.UIApplication.NotNull().ActiveUIDocument == null) { return new SelectionInfo(Array.Empty(), "No objects selected."); } - var activeUIDoc = RevitContext.UIApplication.ActiveUIDocument.NotNull(); + var activeUIDoc = _revitContext.UIApplication.ActiveUIDocument.NotNull(); // POC: this was also being called on shutdown // probably the bridge needs to be able to know if the plugin has been terminated diff --git a/Connectors/Revit/Speckle.Connectors.RevitShared/DependencyInjection/RevitConnectorModule.cs b/Connectors/Revit/Speckle.Connectors.RevitShared/DependencyInjection/RevitConnectorModule.cs index f6f532d35..6f6f6f5b0 100644 --- a/Connectors/Revit/Speckle.Connectors.RevitShared/DependencyInjection/RevitConnectorModule.cs +++ b/Connectors/Revit/Speckle.Connectors.RevitShared/DependencyInjection/RevitConnectorModule.cs @@ -8,7 +8,6 @@ using Speckle.Connectors.Common.Operations; using Speckle.Connectors.DUI; using Speckle.Connectors.DUI.Bindings; using Speckle.Connectors.DUI.Bridge; -using Speckle.Connectors.DUI.Models; using Speckle.Connectors.Revit.Bindings; using Speckle.Connectors.Revit.HostApp; using Speckle.Connectors.Revit.Operations.Receive; @@ -26,12 +25,9 @@ public static class ServiceRegistration public static void AddRevit(this IServiceCollection serviceCollection) { serviceCollection.AddConnectorUtils(); - serviceCollection.AddDUI(); + serviceCollection.AddDUI(); RegisterUiDependencies(serviceCollection); - // register - serviceCollection.AddSingleton(); - // Storage Schema serviceCollection.AddScoped(); serviceCollection.AddScoped(); @@ -45,13 +41,12 @@ public static class ServiceRegistration serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(); - - serviceCollection.RegisterTopLevelExceptionHandler(); serviceCollection.AddSingleton(sp => sp.GetRequiredService()); serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + // send operation and dependencies serviceCollection.AddScoped>(); serviceCollection.AddScoped(); @@ -73,9 +68,6 @@ public static class ServiceRegistration // operation progress manager serviceCollection.AddSingleton(); - - // API context helps us to run functions on Revit UI Thread (main) - serviceCollection.AddSingleton(); } public static void RegisterUiDependencies(IServiceCollection serviceCollection) diff --git a/Connectors/Revit/Speckle.Connectors.RevitShared/HostApp/APIContext.cs b/Connectors/Revit/Speckle.Connectors.RevitShared/HostApp/APIContext.cs deleted file mode 100644 index 5260d0933..000000000 --- a/Connectors/Revit/Speckle.Connectors.RevitShared/HostApp/APIContext.cs +++ /dev/null @@ -1,130 +0,0 @@ -using Autodesk.Revit.UI; - -namespace Speckle.Connectors.Revit.HostApp; - -/// -/// This class gives access to the Revit API context from anywhere in your codebase. This is essentially a -/// lite version of the Revit.Async package from Kennan Chan. Most of the functionality was taken from that code. -/// The main difference is that this class does not subscribe to the applicationIdling event from revit -/// which the docs say will impact the performance of Revit -/// -public sealed class APIContext : IDisposable -{ - private readonly SemaphoreSlim _semaphore = new(1, 1); - private readonly UIControlledApplication _uiApplication; - private readonly ExternalEventHandler _factoryExternalEventHandler; -#pragma warning disable CA2213 - private readonly ExternalEvent _factoryExternalEvent; -#pragma warning restore CA2213 - - public APIContext(UIControlledApplication application) - { - _uiApplication = application; - _factoryExternalEventHandler = new(ExternalEvent.Create); - _factoryExternalEvent = ExternalEvent.Create(_factoryExternalEventHandler); - } - - public async Task Run(Func func) - { - await _semaphore.WaitAsync().ConfigureAwait(false); - try - { - var handler = new ExternalEventHandler(func); - using var externalEvent = await Run(_factoryExternalEventHandler, handler, _factoryExternalEvent) - .ConfigureAwait(false); - - return await Run(handler, _uiApplication, externalEvent).ConfigureAwait(false); - } - finally - { - _semaphore.Release(); - } - } - - public async Task Run(Action action) => - await Run(app => - { - action(app); - return null!; - }) - .ConfigureAwait(false); - - public async Task Run(Action action) => - await Run(_ => - { - action(); - return null!; - }) - .ConfigureAwait(false); - - private async Task Run( - ExternalEventHandler handler, - TParameter parameter, - ExternalEvent externalEvent - ) - { - var task = handler.GetTask(parameter); - externalEvent.Raise(); - - return await task.ConfigureAwait(false); - } - - public void Dispose() - { - _factoryExternalEvent.Dispose(); - _semaphore.Dispose(); - } -} - -public enum HandlerStatus -{ - NotStarted, - Started, - IsCompleted, - IsFaulted, -} - -internal sealed class ExternalEventHandler : IExternalEventHandler -{ - private TaskCompletionSource Result { get; set; } - - public Task GetTask(TParameter parameter) - { - Parameter = parameter; - Result = new TaskCompletionSource(); - return Result.Task; - } - - private readonly Func _func; - - public ExternalEventHandler(Func func) - { - this._func = func; - } - - public HandlerStatus Status { get; private set; } = HandlerStatus.NotStarted; - private TParameter Parameter { get; set; } - - [System.Diagnostics.CodeAnalysis.SuppressMessage( - "Design", - "CA1031:Do not catch general exception types", - Justification = "This is a very generic utility method for running things in a Revit context. If the result of the Run method is awaited, then the exception caught here will be raised there." - )] - public void Execute(UIApplication app) - { - Status = HandlerStatus.Started; - try - { - var r = _func(Parameter); - Result.SetResult(r); - Status = HandlerStatus.IsCompleted; - } - catch (Exception ex) - { - Status = HandlerStatus.IsFaulted; - Result.SetException(ex); - } - } - - public string GetName() => "SpeckleRevitContextEventHandler"; -} diff --git a/Connectors/Revit/Speckle.Connectors.RevitShared/HostApp/ElementUnpacker.cs b/Connectors/Revit/Speckle.Connectors.RevitShared/HostApp/ElementUnpacker.cs index bc3304998..c6eb4107a 100644 --- a/Connectors/Revit/Speckle.Connectors.RevitShared/HostApp/ElementUnpacker.cs +++ b/Connectors/Revit/Speckle.Connectors.RevitShared/HostApp/ElementUnpacker.cs @@ -31,7 +31,8 @@ public class ElementUnpacker // Step 2: pack curtain wall elements, once we know the full extent of our flattened item list. // The behaviour we're looking for: // If parent wall is part of selection, does not select individual elements out. Otherwise, selects individual elements (Panels, Mullions) as atomic objects. - return PackCurtainWallElements(atomicObjects); + // NOTE: this also conditionally "packs" stacked wall elements if their parent is present. See detailed note inside the function. + return PackCurtainWallElementsAndStackedWalls(atomicObjects); } /// @@ -63,6 +64,13 @@ public class ElementUnpacker var groupElements = g.GetMemberIds().Select(doc.GetElement); unpackedElements.AddRange(UnpackElements(groupElements)); } + else if (element is BaseArray baseArray) + { + var arrayElements = baseArray.GetCopiedMemberIds().Select(doc.GetElement); + var originalElements = baseArray.GetOriginalMemberIds().Select(doc.GetElement); + unpackedElements.AddRange(UnpackElements(arrayElements)); + unpackedElements.AddRange(UnpackElements(originalElements)); + } // UNPACK: Family instances (as they potentially have nested families inside) else if (element is FamilyInstance familyInstance) { @@ -90,18 +98,24 @@ public class ElementUnpacker return unpackedElements.GroupBy(el => el.Id).Select(g => g.First()).ToList(); // no disinctBy in here sadly. } - private List PackCurtainWallElements(List elements) + private List PackCurtainWallElementsAndStackedWalls(List elements) { var ids = elements.Select(el => el.Id).ToArray(); var doc = _revitContext.UIApplication?.ActiveUIDocument.Document!; elements.RemoveAll(element => - (element is Mullion m && ids.Contains(m.Host.Id)) - || (element is Panel p && ids.Contains(p.Host.Id)) + (element is Mullion { Host: not null } m && ids.Contains(m.Host.Id)) + || (element is Panel { Host: not null } p && ids.Contains(p.Host.Id)) || ( element is FamilyInstance { Host: not null } f && doc.GetElement(f.Host.Id) is Wall { CurtainGrid: not null } && ids.Contains(f.Host.Id) ) + // NOTE: It is required to explicitly skip stacked wall members because, when getting objects from a view, + // the api will return the wall parent and its stacked children walls separately. This does not happen + // via selection. Via category ("Walls") we do not get any parent wall, but just the components of the stacked wall separately. + // If you wonder why revit is driving people to insanity, this is one of those moments. + // See [CNX-851: Stacked Wall Duplicate Geometry or Materials not applied](https://linear.app/speckle/issue/CNX-851/stacked-wall-duplicate-geometry-or-materials-not-applied) + || (element is Wall { IsStackedWallMember: true } wall && ids.Contains(wall.StackedWallOwnerId)) ); return elements; } diff --git a/Connectors/Revit/Speckle.Connectors.RevitShared/HostApp/RevitDocumentStore.cs b/Connectors/Revit/Speckle.Connectors.RevitShared/HostApp/RevitDocumentStore.cs index c14e993de..8faa53b64 100644 --- a/Connectors/Revit/Speckle.Connectors.RevitShared/HostApp/RevitDocumentStore.cs +++ b/Connectors/Revit/Speckle.Connectors.RevitShared/HostApp/RevitDocumentStore.cs @@ -1,15 +1,12 @@ -using System.Diagnostics; using Autodesk.Revit.DB; using Autodesk.Revit.DB.ExtensibleStorage; using Autodesk.Revit.UI; using Autodesk.Revit.UI.Events; -using Revit.Async; +using Speckle.Connectors.Common.Threading; using Speckle.Connectors.DUI.Bridge; using Speckle.Connectors.DUI.Models; using Speckle.Connectors.DUI.Utils; -using Speckle.Connectors.Revit.Plugin; using Speckle.Converters.RevitShared.Helpers; -using Speckle.Sdk; using Speckle.Sdk.Common; namespace Speckle.Connectors.Revit.HostApp; @@ -20,44 +17,44 @@ internal sealed class RevitDocumentStore : DocumentModelStore // POC: move to somewhere central? private static readonly Guid s_revitDocumentStoreId = new("D35B3695-EDC9-4E15-B62A-D3FC2CB83FA3"); + private readonly IAppIdleManager _idleManager; private readonly RevitContext _revitContext; - private readonly IRevitIdleManager _idleManager; private readonly DocumentModelStorageSchema _documentModelStorageSchema; private readonly IdStorageSchema _idStorageSchema; + private readonly ITopLevelExceptionHandler _topLevelExceptionHandler; + private readonly IThreadContext _threadContext; public RevitDocumentStore( - IRevitIdleManager idleManager, + IAppIdleManager idleManager, RevitContext revitContext, IJsonSerializer jsonSerializer, DocumentModelStorageSchema documentModelStorageSchema, IdStorageSchema idStorageSchema, - ITopLevelExceptionHandler topLevelExceptionHandler + ITopLevelExceptionHandler topLevelExceptionHandler, + IThreadContext threadContext ) - : base(jsonSerializer, true) + : base(jsonSerializer) { _idleManager = idleManager; _revitContext = revitContext; _documentModelStorageSchema = documentModelStorageSchema; _idStorageSchema = idStorageSchema; + _topLevelExceptionHandler = topLevelExceptionHandler; + _threadContext = threadContext; - _idleManager.RunAsync(() => - { - UIApplication uiApplication = _revitContext.UIApplication.NotNull(); + UIApplication uiApplication = _revitContext.UIApplication.NotNull(); - uiApplication.ViewActivated += (s, e) => topLevelExceptionHandler.CatchUnhandled(() => OnViewActivated(s, e)); + uiApplication.ViewActivated += (s, e) => _topLevelExceptionHandler.CatchUnhandled(() => OnViewActivated(s, e)); - uiApplication.Application.DocumentOpening += (_, _) => - topLevelExceptionHandler.CatchUnhandled(() => IsDocumentInit = false); + uiApplication.Application.DocumentOpening += (_, _) => + _topLevelExceptionHandler.CatchUnhandled(() => IsDocumentInit = false); - uiApplication.Application.DocumentOpened += (_, _) => - topLevelExceptionHandler.CatchUnhandled(() => IsDocumentInit = false); - }); - - Models.CollectionChanged += (_, _) => topLevelExceptionHandler.CatchUnhandled(WriteToFile); + uiApplication.Application.DocumentOpened += (_, _) => + _topLevelExceptionHandler.CatchUnhandled(() => IsDocumentInit = false); // There is no event that we can hook here for double-click file open... // It is kind of harmless since we create this object as "SingleInstance". - ReadFromFile(); + LoadState(); OnDocumentChanged(); } @@ -79,62 +76,56 @@ internal sealed class RevitDocumentStore : DocumentModelStore IsDocumentInit = true; _idleManager.SubscribeToIdle( - nameof(RevitDocumentStore), + nameof(LoadState) + nameof(OnDocumentChanged), () => { - ReadFromFile(); + LoadState(); OnDocumentChanged(); } ); } - public override void WriteToFile() + protected override void HostAppSaveState(string modelCardState) { - var doc = _revitContext.UIApplication?.ActiveUIDocument.Document; - // POC: this can happen? A: Not really, imho (dim) + var doc = _revitContext.UIApplication?.ActiveUIDocument?.Document; + // POC: this can happen? A: Not really, imho (dim) (Adam seyz yes it can if loading also triggers a save) if (doc == null) { return; } - RevitTask.RunAsync(() => - { - using Transaction t = new(doc, "Speckle Write State"); - t.Start(); - using DataStorage ds = GetSettingsDataStorage(doc) ?? DataStorage.Create(doc); + _threadContext + .RunOnMain(() => + { + using Transaction t = new(doc, "Speckle Write State"); + t.Start(); + using DataStorage ds = GetSettingsDataStorage(doc) ?? DataStorage.Create(doc); - using Entity stateEntity = new(_documentModelStorageSchema.GetSchema()); - string serializedModels = Serialize(); - stateEntity.Set("contents", serializedModels); + using Entity stateEntity = new(_documentModelStorageSchema.GetSchema()); + string serializedModels = Serialize(); + stateEntity.Set("contents", serializedModels); - using Entity idEntity = new(_idStorageSchema.GetSchema()); - idEntity.Set("Id", s_revitDocumentStoreId); + using Entity idEntity = new(_idStorageSchema.GetSchema()); + idEntity.Set("Id", s_revitDocumentStoreId); - ds.SetEntity(idEntity); - ds.SetEntity(stateEntity); - t.Commit(); - }); + ds.SetEntity(idEntity); + ds.SetEntity(stateEntity); + t.Commit(); + }) + .FireAndForget(); } - public override void ReadFromFile() + protected override void LoadState() { - try + var stateEntity = GetSpeckleEntity(_revitContext.UIApplication?.ActiveUIDocument?.Document); + if (stateEntity == null || !stateEntity.IsValid()) { - var stateEntity = GetSpeckleEntity(_revitContext.UIApplication?.ActiveUIDocument?.Document); - if (stateEntity == null || !stateEntity.IsValid()) - { - Models = new(); - return; - } + ClearAndSave(); + return; + } - string modelsString = stateEntity.Get("contents"); - Models = Deserialize(modelsString).NotNull(); - } - catch (Exception ex) when (!ex.IsFatal()) - { - Models = new(); - Debug.WriteLine(ex.Message); // POC: Log here error and notify UI that cards not read succesfully - } + string modelsString = stateEntity.Get("contents"); + LoadFromString(modelsString); } private DataStorage? GetSettingsDataStorage(Document doc) diff --git a/Connectors/Revit/Speckle.Connectors.RevitShared/HostApp/RevitMaterialBaker.cs b/Connectors/Revit/Speckle.Connectors.RevitShared/HostApp/RevitMaterialBaker.cs index d883c6c9c..e09e106c1 100644 --- a/Connectors/Revit/Speckle.Connectors.RevitShared/HostApp/RevitMaterialBaker.cs +++ b/Connectors/Revit/Speckle.Connectors.RevitShared/HostApp/RevitMaterialBaker.cs @@ -5,6 +5,7 @@ using Speckle.Converters.Common; using Speckle.Converters.RevitShared.Settings; using Speckle.Objects.Other; using Speckle.Sdk; +using Speckle.Sdk.Common; using Speckle.Sdk.Models.Collections; using Speckle.Sdk.Models.Extensions; using Speckle.Sdk.Models.GraphTraversal; @@ -108,7 +109,7 @@ public class RevitMaterialBaker /// /// public Dictionary BakeMaterials( - List speckleRenderMaterialProxies, + IReadOnlyCollection speckleRenderMaterialProxies, string baseLayerName ) { @@ -122,7 +123,7 @@ public class RevitMaterialBaker var diffuse = System.Drawing.Color.FromArgb(speckleRenderMaterial.diffuse); double transparency = 1 - speckleRenderMaterial.opacity; double smoothness = 1 - speckleRenderMaterial.roughness; - string materialId = speckleRenderMaterial.applicationId ?? speckleRenderMaterial.id; + string materialId = speckleRenderMaterial.applicationId ?? speckleRenderMaterial.id.NotNull(); string matName = _revitUtils.RemoveInvalidChars($"{speckleRenderMaterial.name}-({materialId})-{baseLayerName}"); var newMaterialId = Autodesk.Revit.DB.Material.Create(_converterSettings.Current.Document, matName); diff --git a/Connectors/Revit/Speckle.Connectors.RevitShared/HostApp/SendCollectionManager.cs b/Connectors/Revit/Speckle.Connectors.RevitShared/HostApp/SendCollectionManager.cs index cd34cd8d3..9321f51de 100644 --- a/Connectors/Revit/Speckle.Connectors.RevitShared/HostApp/SendCollectionManager.cs +++ b/Connectors/Revit/Speckle.Connectors.RevitShared/HostApp/SendCollectionManager.cs @@ -12,6 +12,7 @@ public class SendCollectionManager { private readonly IConverterSettingsStore _converterSettings; private readonly Dictionary _collectionCache = new(); + private readonly Dictionary props)> _levelCache = new(); // stores level id and its properties public SendCollectionManager(IConverterSettingsStore converterSettings) { @@ -30,9 +31,29 @@ public class SendCollectionManager var doc = _converterSettings.Current.Document; var path = new List(); + // Step 0: get the level and its properties + string levelName = "No Level"; + Dictionary levelProperties = new(); + if (element.LevelId != ElementId.InvalidElementId) + { + if (_levelCache.TryGetValue(element.LevelId, out var cachedLevel)) + { + levelName = cachedLevel.name; + levelProperties = cachedLevel.props; + } + else + { + var level = (Level)doc.GetElement(element.LevelId); + levelName = level.Name; + levelProperties.Add("elevation", level.Elevation); + levelProperties.Add("units", _converterSettings.Current.SpeckleUnits); + _levelCache.Add(element.LevelId, (levelName, levelProperties)); + } + } + // Step 1: create path components. Currently, this is // level > category > type - path.Add(doc.GetElement(element.LevelId) is not Level level ? "No level" : level.Name); + path.Add(levelName); path.Add(element.Category?.Name ?? "No category"); var typeId = element.GetTypeId(); if (typeId != ElementId.InvalidElementId) @@ -57,8 +78,9 @@ public class SendCollectionManager string flatPathName = ""; Collection previousCollection = rootObject; - foreach (var pathItem in path) + for (int i = 0; i < path.Count; i++) { + var pathItem = path[i]; flatPathName += pathItem; Collection childCollection; if (_collectionCache.TryGetValue(flatPathName, out Collection? collection)) @@ -68,6 +90,13 @@ public class SendCollectionManager else { childCollection = new Collection(pathItem); + // add props if it's the 1st path item, representing level + // if the structure ever changes from level > category > type, this needs to be changed + if (i == 0 && levelProperties.Count > 0) + { + childCollection["properties"] = levelProperties; + } + previousCollection.elements.Add(childCollection); _collectionCache[flatPathName] = childCollection; } diff --git a/Connectors/Revit/Speckle.Connectors.RevitShared/HostApp/SupportedCategoriesUtils.cs b/Connectors/Revit/Speckle.Connectors.RevitShared/HostApp/SupportedCategoriesUtils.cs new file mode 100644 index 000000000..1f9632609 --- /dev/null +++ b/Connectors/Revit/Speckle.Connectors.RevitShared/HostApp/SupportedCategoriesUtils.cs @@ -0,0 +1,44 @@ +using Autodesk.Revit.DB; + +namespace Speckle.Connectors.Revit.HostApp; + +public static class SupportedCategoriesUtils +{ + /// + /// Filters out all categories besides Model categories, and Grids in Annotation. This utility should be used + /// to clean any elements we might want to send pre-conversion as well as in what categories + /// to display in our category filter. + /// + /// + /// + public static bool IsSupportedCategory(Category? category) + { + if (category is null || !category.IsVisibleInUI) + { + return false; + } + + switch (category.CategoryType) + { + case CategoryType.Annotation: + return +#if REVIT2023_OR_GREATER + category.BuiltInCategory == BuiltInCategory.OST_Grids; +#else + category.Name == "OST_Grids"; +#endif + + case CategoryType.Model: + return +#if REVIT2023_OR_GREATER + category.BuiltInCategory != BuiltInCategory.OST_AreaSchemes + && category.BuiltInCategory != BuiltInCategory.OST_AreaSchemeLines; +#else + category.Name != "OST_AreaSchemeLines" && category.Name != "OST_AreaSchemes"; +#endif + + default: + return false; + } + } +} diff --git a/Connectors/Revit/Speckle.Connectors.RevitShared/Operations/Receive/RevitHostObjectBuilder.cs b/Connectors/Revit/Speckle.Connectors.RevitShared/Operations/Receive/RevitHostObjectBuilder.cs index 6734d1219..ae7e839c5 100644 --- a/Connectors/Revit/Speckle.Connectors.RevitShared/Operations/Receive/RevitHostObjectBuilder.cs +++ b/Connectors/Revit/Speckle.Connectors.RevitShared/Operations/Receive/RevitHostObjectBuilder.cs @@ -1,11 +1,11 @@ using Autodesk.Revit.DB; using Microsoft.Extensions.Logging; -using Revit.Async; using Speckle.Connectors.Common.Builders; using Speckle.Connectors.Common.Conversion; using Speckle.Connectors.Common.Instances; using Speckle.Connectors.Common.Operations; using Speckle.Connectors.Common.Operations.Receive; +using Speckle.Connectors.Common.Threading; using Speckle.Connectors.Revit.HostApp; using Speckle.Converters.Common; using Speckle.Converters.Common.Objects; @@ -16,6 +16,7 @@ using Speckle.DoubleNumerics; using Speckle.Objects; using Speckle.Objects.Geometry; using Speckle.Sdk; +using Speckle.Sdk.Common; using Speckle.Sdk.Common.Exceptions; using Speckle.Sdk.Logging; using Speckle.Sdk.Models; @@ -23,51 +24,24 @@ using Transform = Speckle.Objects.Other.Transform; namespace Speckle.Connectors.Revit.Operations.Receive; -internal sealed class RevitHostObjectBuilder : IHostObjectBuilder, IDisposable -{ - private readonly IRootToHostConverter _converter; - private readonly IConverterSettingsStore _converterSettings; - private readonly RevitToHostCacheSingleton _revitToHostCacheSingleton; - private readonly ITransactionManager _transactionManager; - private readonly ILocalToGlobalUnpacker _localToGlobalUnpacker; - private readonly RevitGroupBaker _groupBaker; - private readonly RevitMaterialBaker _materialBaker; - private readonly ILogger _logger; - private readonly ITypedConverter< - (Base atomicObject, List matrix), +public sealed class RevitHostObjectBuilder( + IRootToHostConverter converter, + IConverterSettingsStore converterSettings, + ITransactionManager transactionManager, + ISdkActivityFactory activityFactory, + ILocalToGlobalUnpacker localToGlobalUnpacker, + RevitGroupBaker groupManager, + RevitMaterialBaker materialBaker, + RootObjectUnpacker rootObjectUnpacker, + ILogger logger, + IThreadContext threadContext, + RevitToHostCacheSingleton revitToHostCacheSingleton, + ITypedConverter< + (Base atomicObject, IReadOnlyCollection matrix), DirectShape - > _localToGlobalDirectShapeConverter; - - private readonly RootObjectUnpacker _rootObjectUnpacker; - private readonly ISdkActivityFactory _activityFactory; - - public RevitHostObjectBuilder( - IRootToHostConverter converter, - IConverterSettingsStore converterSettings, - ITransactionManager transactionManager, - ISdkActivityFactory activityFactory, - ILocalToGlobalUnpacker localToGlobalUnpacker, - RevitGroupBaker groupManager, - RevitMaterialBaker materialBaker, - RootObjectUnpacker rootObjectUnpacker, - ILogger logger, - RevitToHostCacheSingleton revitToHostCacheSingleton, - ITypedConverter<(Base atomicObject, List matrix), DirectShape> localToGlobalDirectShapeConverter - ) - { - _converter = converter; - _converterSettings = converterSettings; - _transactionManager = transactionManager; - _localToGlobalUnpacker = localToGlobalUnpacker; - _groupBaker = groupManager; - _materialBaker = materialBaker; - _rootObjectUnpacker = rootObjectUnpacker; - _logger = logger; - _revitToHostCacheSingleton = revitToHostCacheSingleton; - _localToGlobalDirectShapeConverter = localToGlobalDirectShapeConverter; - _activityFactory = activityFactory; - } - + > localToGlobalDirectShapeConverter +) : IHostObjectBuilder, IDisposable +{ public Task Build( Base rootObject, string projectName, @@ -75,7 +49,9 @@ internal sealed class RevitHostObjectBuilder : IHostObjectBuilder, IDisposable IProgress onOperationProgressed, CancellationToken cancellationToken ) => - RevitTask.RunAsync(() => BuildSync(rootObject, projectName, modelName, onOperationProgressed, cancellationToken)); + threadContext.RunOnMainAsync( + () => Task.FromResult(BuildSync(rootObject, projectName, modelName, onOperationProgressed, cancellationToken)) + ); private HostObjectBuilderResult BuildSync( Base rootObject, @@ -88,27 +64,27 @@ internal sealed class RevitHostObjectBuilder : IHostObjectBuilder, IDisposable var baseGroupName = $"Project {projectName}: Model {modelName}"; // TODO: unify this across connectors! onOperationProgressed.Report(new("Converting", null)); - using var activity = _activityFactory.Start("Build"); + using var activity = activityFactory.Start("Build"); // 0 - Clean then Rock n Roll! 🎸 { - _activityFactory.Start("Pre receive clean"); - _transactionManager.StartTransaction(true, "Pre receive clean"); + activityFactory.Start("Pre receive clean"); + transactionManager.StartTransaction(true, "Pre receive clean"); try { PreReceiveDeepClean(baseGroupName); } catch (Exception ex) when (!ex.IsFatal()) { - _logger.LogError(ex, "Failed to clean up before receive in Revit"); + logger.LogError(ex, "Failed to clean up before receive in Revit"); } - _transactionManager.CommitTransaction(); + transactionManager.CommitTransaction(); } // 1 - Unpack objects and proxies from root commit object - var unpackedRoot = _rootObjectUnpacker.Unpack(rootObject); - var localToGlobalMaps = _localToGlobalUnpacker.Unpack( + var unpackedRoot = rootObjectUnpacker.Unpack(rootObject); + var localToGlobalMaps = localToGlobalUnpacker.Unpack( unpackedRoot.DefinitionProxies, unpackedRoot.ObjectsToConvert.ToList() ); @@ -116,14 +92,14 @@ internal sealed class RevitHostObjectBuilder : IHostObjectBuilder, IDisposable // 2 - Bake materials if (unpackedRoot.RenderMaterialProxies != null) { - _transactionManager.StartTransaction(true, "Baking materials"); - _materialBaker.MapLayersRenderMaterials(unpackedRoot); - var map = _materialBaker.BakeMaterials(unpackedRoot.RenderMaterialProxies, baseGroupName); + transactionManager.StartTransaction(true, "Baking materials"); + materialBaker.MapLayersRenderMaterials(unpackedRoot); + var map = materialBaker.BakeMaterials(unpackedRoot.RenderMaterialProxies, baseGroupName); foreach (var kvp in map) { - _revitToHostCacheSingleton.MaterialsByObjectId.Add(kvp.Key, kvp.Value); + revitToHostCacheSingleton.MaterialsByObjectId.Add(kvp.Key, kvp.Value); } - _transactionManager.CommitTransaction(); + transactionManager.CommitTransaction(); } // 3 - Bake objects @@ -132,26 +108,26 @@ internal sealed class RevitHostObjectBuilder : IHostObjectBuilder, IDisposable List<(DirectShape res, string applicationId)> postBakePaintTargets ) conversionResults; { - using var _ = _activityFactory.Start("Baking objects"); - _transactionManager.StartTransaction(true, "Baking objects"); + using var _ = activityFactory.Start("Baking objects"); + transactionManager.StartTransaction(true, "Baking objects"); conversionResults = BakeObjects(localToGlobalMaps, onOperationProgressed, cancellationToken); - _transactionManager.CommitTransaction(); + transactionManager.CommitTransaction(); } // 4 - Paint solids { - using var _ = _activityFactory.Start("Painting solids"); - _transactionManager.StartTransaction(true, "Painting solids"); + using var _ = activityFactory.Start("Painting solids"); + transactionManager.StartTransaction(true, "Painting solids"); PostBakePaint(conversionResults.postBakePaintTargets); - _transactionManager.CommitTransaction(); + transactionManager.CommitTransaction(); } // 5 - Create group { - using var _ = _activityFactory.Start("Grouping"); - _transactionManager.StartTransaction(true, "Grouping"); - _groupBaker.BakeGroupForTopLevel(baseGroupName); - _transactionManager.CommitTransaction(); + using var _ = activityFactory.Start("Grouping"); + transactionManager.StartTransaction(true, "Grouping"); + groupManager.BakeGroupForTopLevel(baseGroupName); + transactionManager.CommitTransaction(); } return conversionResults.builderResult; @@ -161,12 +137,12 @@ internal sealed class RevitHostObjectBuilder : IHostObjectBuilder, IDisposable HostObjectBuilderResult builderResult, List<(DirectShape res, string applicationId)> postBakePaintTargets ) BakeObjects( - List localToGlobalMaps, + IReadOnlyCollection localToGlobalMaps, IProgress onOperationProgressed, CancellationToken cancellationToken ) { - using var _ = _activityFactory.Start("BakeObjects"); + using var _ = activityFactory.Start("BakeObjects"); var conversionResults = new List(); var bakedObjectIds = new List(); int count = 0; @@ -178,7 +154,7 @@ internal sealed class RevitHostObjectBuilder : IHostObjectBuilder, IDisposable cancellationToken.ThrowIfCancellationRequested(); try { - using var activity = _activityFactory.Start("BakeObject"); + using var activity = activityFactory.Start("BakeObject"); // POC hack of the ages: try to pre transform curves, points and meshes before baking // we need to bypass the local to global converter as there we don't have access to what we want. that service will/should stop existing. @@ -188,32 +164,37 @@ internal sealed class RevitHostObjectBuilder : IHostObjectBuilder, IDisposable && localToGlobalMap.AtomicObject["units"] is string units ) { + //TODO TransformTo will be deprecated as it's dangerous and requires ID transposing which is wrong! + //ID needs to be copied to the new instance + var id = localToGlobalMap.AtomicObject.id; ITransformable? newTransformable = null; foreach (var mat in localToGlobalMap.Matrix) { transformable.TransformTo(new Transform() { matrix = mat, units = units }, out newTransformable); + transformable = newTransformable; // we need to keep the reference to the new object, as we're going to use it in the cache } localToGlobalMap.AtomicObject = (newTransformable as Base)!; - localToGlobalMap.Matrix = new(); // flush out the list, as we've applied the transforms already + localToGlobalMap.AtomicObject.id = id; // restore the id, as it's used in the cache + localToGlobalMap.Matrix = new HashSet(); // flush out the list, as we've applied the transforms already } // actual conversion happens here! - var result = _converter.Convert(localToGlobalMap.AtomicObject); + var result = converter.Convert(localToGlobalMap.AtomicObject); onOperationProgressed.Report(new("Converting", (double)++count / localToGlobalMaps.Count)); if (result is DirectShapeDefinitionWrapper) { // direct shape creation happens here - DirectShape directShapes = _localToGlobalDirectShapeConverter.Convert( + DirectShape directShapes = localToGlobalDirectShapeConverter.Convert( (localToGlobalMap.AtomicObject, localToGlobalMap.Matrix) ); bakedObjectIds.Add(directShapes.UniqueId); - _groupBaker.AddToTopLevelGroup(directShapes); + groupManager.AddToTopLevelGroup(directShapes); if (localToGlobalMap.AtomicObject is IRawEncodedObject and Base myBase) { - postBakePaintTargets.Add((directShapes, myBase.applicationId ?? myBase.id)); + postBakePaintTargets.Add((directShapes, myBase.applicationId ?? myBase.id.NotNull())); } conversionResults.Add( @@ -228,7 +209,7 @@ internal sealed class RevitHostObjectBuilder : IHostObjectBuilder, IDisposable catch (Exception ex) when (!ex.IsFatal()) { conversionResults.Add(new(Status.ERROR, localToGlobalMap.AtomicObject, null, null, ex)); - _logger.LogError(ex, $"Failed to convert object of type {localToGlobalMap.AtomicObject.speckle_type}"); + logger.LogError(ex, $"Failed to convert object of type {localToGlobalMap.AtomicObject.speckle_type}"); } } return (new(bakedObjectIds, conversionResults), postBakePaintTargets); @@ -244,7 +225,7 @@ internal sealed class RevitHostObjectBuilder : IHostObjectBuilder, IDisposable { var elGeometry = res.get_Geometry(new Options() { DetailLevel = ViewDetailLevel.Undefined }); var materialId = ElementId.InvalidElementId; - if (_revitToHostCacheSingleton.MaterialsByObjectId.TryGetValue(applicationId, out var mappedElementId)) + if (revitToHostCacheSingleton.MaterialsByObjectId.TryGetValue(applicationId, out var mappedElementId)) { materialId = mappedElementId; } @@ -261,7 +242,7 @@ internal sealed class RevitHostObjectBuilder : IHostObjectBuilder, IDisposable { foreach (Face face in s.Faces) { - _converterSettings.Current.Document.Paint(res.Id, face, materialId); + converterSettings.Current.Document.Paint(res.Id, face, materialId); } } } @@ -270,12 +251,12 @@ internal sealed class RevitHostObjectBuilder : IHostObjectBuilder, IDisposable private void PreReceiveDeepClean(string baseGroupName) { - DirectShapeLibrary.GetDirectShapeLibrary(_converterSettings.Current.Document).Reset(); // Note: this needs to be cleared, as it is being used in the converter + DirectShapeLibrary.GetDirectShapeLibrary(converterSettings.Current.Document).Reset(); // Note: this needs to be cleared, as it is being used in the converter - _revitToHostCacheSingleton.MaterialsByObjectId.Clear(); // Massive hack! - _groupBaker.PurgeGroups(baseGroupName); - _materialBaker.PurgeMaterials(baseGroupName); + revitToHostCacheSingleton.MaterialsByObjectId.Clear(); // Massive hack! + groupManager.PurgeGroups(baseGroupName); + materialBaker.PurgeMaterials(baseGroupName); } - public void Dispose() => _transactionManager?.Dispose(); + public void Dispose() => transactionManager?.Dispose(); } diff --git a/Connectors/Revit/Speckle.Connectors.RevitShared/Operations/Send/Filters/IRevitSendFilter.cs b/Connectors/Revit/Speckle.Connectors.RevitShared/Operations/Send/Filters/IRevitSendFilter.cs index 8895b357b..fe576b8b3 100644 --- a/Connectors/Revit/Speckle.Connectors.RevitShared/Operations/Send/Filters/IRevitSendFilter.cs +++ b/Connectors/Revit/Speckle.Connectors.RevitShared/Operations/Send/Filters/IRevitSendFilter.cs @@ -1,9 +1,8 @@ -using Speckle.Connectors.Revit.HostApp; -using Speckle.Converters.RevitShared.Helpers; +using Speckle.Converters.RevitShared.Helpers; namespace Speckle.Connectors.RevitShared.Operations.Send.Filters; public interface IRevitSendFilter { - public void SetContext(RevitContext revitContext, APIContext apiContext); + public void SetContext(RevitContext revitContext); } diff --git a/Connectors/Revit/Speckle.Connectors.RevitShared/Operations/Send/Filters/RevitCategoriesFilter.cs b/Connectors/Revit/Speckle.Connectors.RevitShared/Operations/Send/Filters/RevitCategoriesFilter.cs index b1cd74db7..295bee35d 100644 --- a/Connectors/Revit/Speckle.Connectors.RevitShared/Operations/Send/Filters/RevitCategoriesFilter.cs +++ b/Connectors/Revit/Speckle.Connectors.RevitShared/Operations/Send/Filters/RevitCategoriesFilter.cs @@ -4,6 +4,7 @@ using Speckle.Connectors.DUI.Models.Card.SendFilter; using Speckle.Connectors.DUI.Utils; using Speckle.Connectors.Revit.HostApp; using Speckle.Converters.RevitShared.Helpers; +using Speckle.Sdk.Common; namespace Speckle.Connectors.RevitShared.Operations.Send.Filters; @@ -12,7 +13,6 @@ public record CategoryData(string Name, string Id); public class RevitCategoriesFilter : DiscriminatedObject, ISendFilter, IRevitSendFilter { private RevitContext _revitContext; - private APIContext _apiContext; private Document? _doc; public string Id { get; set; } = "revitCategories"; public string Name { get; set; } = "Categories"; @@ -25,11 +25,10 @@ public class RevitCategoriesFilter : DiscriminatedObject, ISendFilter, IRevitSen public RevitCategoriesFilter() { } - public RevitCategoriesFilter(RevitContext revitContext, APIContext apiContext) + public RevitCategoriesFilter(RevitContext revitContext) { _revitContext = revitContext; - _apiContext = apiContext; - _doc = _revitContext.UIApplication?.ActiveUIDocument.Document; + _doc = _revitContext.UIApplication.NotNull().ActiveUIDocument.Document; GetCategories(); } @@ -72,7 +71,10 @@ public class RevitCategoriesFilter : DiscriminatedObject, ISendFilter, IRevitSen foreach (Category category in _doc.Settings.Categories) { - categories.Add(new CategoryData(category.Name, category.Id.ToString())); + if (SupportedCategoriesUtils.IsSupportedCategory(category)) + { + categories.Add(new CategoryData(category.Name, category.Id.ToString())); + } } AvailableCategories = categories; @@ -82,10 +84,9 @@ public class RevitCategoriesFilter : DiscriminatedObject, ISendFilter, IRevitSen /// NOTE: this is needed since we need doc on `GetObjectIds()` function after it deserialized. /// DI doesn't help here to pass RevitContext from constructor. /// - public void SetContext(RevitContext revitContext, APIContext apiContext) + public void SetContext(RevitContext revitContext) { _revitContext = revitContext; - _apiContext = apiContext; _doc = _revitContext.UIApplication?.ActiveUIDocument.Document; } } diff --git a/Connectors/Revit/Speckle.Connectors.RevitShared/Operations/Send/Filters/RevitViewsFilter.cs b/Connectors/Revit/Speckle.Connectors.RevitShared/Operations/Send/Filters/RevitViewsFilter.cs index d98f46b80..ac54ab0c6 100644 --- a/Connectors/Revit/Speckle.Connectors.RevitShared/Operations/Send/Filters/RevitViewsFilter.cs +++ b/Connectors/Revit/Speckle.Connectors.RevitShared/Operations/Send/Filters/RevitViewsFilter.cs @@ -2,7 +2,6 @@ using Speckle.Connectors.DUI.Exceptions; using Speckle.Connectors.DUI.Models.Card.SendFilter; using Speckle.Connectors.DUI.Utils; -using Speckle.Connectors.Revit.HostApp; using Speckle.Converters.RevitShared.Helpers; namespace Speckle.Connectors.RevitShared.Operations.Send.Filters; @@ -10,7 +9,6 @@ namespace Speckle.Connectors.RevitShared.Operations.Send.Filters; public class RevitViewsFilter : DiscriminatedObject, ISendFilter, IRevitSendFilter { private RevitContext _revitContext; - private APIContext _apiContext; private Document? _doc; public string Id { get; set; } = "revitViews"; public string Name { get; set; } = "Views"; @@ -23,10 +21,9 @@ public class RevitViewsFilter : DiscriminatedObject, ISendFilter, IRevitSendFilt public RevitViewsFilter() { } - public RevitViewsFilter(RevitContext revitContext, APIContext apiContext) + public RevitViewsFilter(RevitContext revitContext) { _revitContext = revitContext; - _apiContext = apiContext; _doc = _revitContext.UIApplication?.ActiveUIDocument.Document; GetViews(); @@ -91,6 +88,18 @@ public class RevitViewsFilter : DiscriminatedObject, ISendFilter, IRevitSendFilt .OfClass(typeof(View)) .Cast() .Where(v => !v.IsTemplate) + .Where(v => !v.IsAssemblyView) + .Where(v => + v.ViewType + is ViewType.FloorPlan + or ViewType.Elevation + or ViewType.Rendering + or ViewType.Section + or ViewType.ThreeD + or ViewType.Detail + or ViewType.CeilingPlan + or ViewType.AreaPlan + ) .Select(v => v.ViewType.ToString() + " - " + v.Name.ToString()) .ToList(); AvailableViews = views; @@ -100,10 +109,9 @@ public class RevitViewsFilter : DiscriminatedObject, ISendFilter, IRevitSendFilt /// NOTE: this is needed since we need doc on `GetObjectIds()` function after it deserialized. /// DI doesn't help here to pass RevitContext from constructor. /// - public void SetContext(RevitContext revitContext, APIContext apiContext) + public void SetContext(RevitContext revitContext) { _revitContext = revitContext; - _apiContext = apiContext; _doc = _revitContext.UIApplication?.ActiveUIDocument.Document; } } diff --git a/Connectors/Revit/Speckle.Connectors.RevitShared/Operations/Send/RevitRootObjectBuilder.cs b/Connectors/Revit/Speckle.Connectors.RevitShared/Operations/Send/RevitRootObjectBuilder.cs index 2b374c37d..5dc1ea395 100644 --- a/Connectors/Revit/Speckle.Connectors.RevitShared/Operations/Send/RevitRootObjectBuilder.cs +++ b/Connectors/Revit/Speckle.Connectors.RevitShared/Operations/Send/RevitRootObjectBuilder.cs @@ -1,71 +1,50 @@ using Autodesk.Revit.DB; using Microsoft.Extensions.Logging; -using Revit.Async; using Speckle.Connectors.Common.Builders; using Speckle.Connectors.Common.Caching; using Speckle.Connectors.Common.Conversion; using Speckle.Connectors.Common.Extensions; using Speckle.Connectors.Common.Operations; +using Speckle.Connectors.Common.Threading; using Speckle.Connectors.DUI.Exceptions; using Speckle.Connectors.Revit.HostApp; using Speckle.Converters.Common; using Speckle.Converters.RevitShared.Helpers; using Speckle.Converters.RevitShared.Settings; -using Speckle.Converters.RevitShared.ToSpeckle; using Speckle.Sdk; using Speckle.Sdk.Models; using Speckle.Sdk.Models.Collections; namespace Speckle.Connectors.Revit.Operations.Send; -public class RevitRootObjectBuilder : IRootObjectBuilder +public class RevitRootObjectBuilder( + IRootToSpeckleConverter converter, + IConverterSettingsStore converterSettings, + ISendConversionCache sendConversionCache, + ElementUnpacker elementUnpacker, + IThreadContext threadContext, + SendCollectionManager sendCollectionManager, + ILogger logger, + RevitToSpeckleCacheSingleton revitToSpeckleCacheSingleton +) : IRootObjectBuilder { // POC: SendSelection and RevitConversionContextStack should be interfaces, former needs interfaces - private readonly IRootToSpeckleConverter _converter; - private readonly IConverterSettingsStore _converterSettings; - private readonly ISendConversionCache _sendConversionCache; - private readonly ElementUnpacker _elementUnpacker; - private readonly SendCollectionManager _sendCollectionManager; - private readonly RevitToSpeckleCacheSingleton _revitToSpeckleCacheSingleton; - private readonly ILogger _logger; - private readonly ParameterDefinitionHandler _parameterDefinitionHandler; - public RevitRootObjectBuilder( - IRootToSpeckleConverter converter, - IConverterSettingsStore converterSettings, - ISendConversionCache sendConversionCache, - ElementUnpacker elementUnpacker, - SendCollectionManager sendCollectionManager, - ILogger logger, - ParameterDefinitionHandler parameterDefinitionHandler, - RevitToSpeckleCacheSingleton revitToSpeckleCacheSingleton - ) - { - _converter = converter; - _converterSettings = converterSettings; - _sendConversionCache = sendConversionCache; - _elementUnpacker = elementUnpacker; - _sendCollectionManager = sendCollectionManager; - _revitToSpeckleCacheSingleton = revitToSpeckleCacheSingleton; - _logger = logger; - _parameterDefinitionHandler = parameterDefinitionHandler; - } - - public async Task Build( + public Task Build( IReadOnlyList objects, SendInfo sendInfo, IProgress onOperationProgressed, CancellationToken ct = default - ) => await RevitTask.RunAsync(() => BuildSync(objects, sendInfo, onOperationProgressed, ct)).ConfigureAwait(false); + ) => threadContext.RunOnMainAsync(() => Task.FromResult(BuildSync(objects, sendInfo, onOperationProgressed, ct))); private RootObjectBuilderResult BuildSync( IReadOnlyList objects, SendInfo sendInfo, IProgress onOperationProgressed, - CancellationToken ct = default + CancellationToken cancellationToken ) { - var doc = _converterSettings.Current.Document; + var doc = converterSettings.Current.Document; if (doc.IsFamilyDocument) { @@ -74,75 +53,96 @@ public class RevitRootObjectBuilder : IRootObjectBuilder // 0 - Init the root Collection rootObject = - new() { name = _converterSettings.Current.Document.PathName.Split('\\').Last().Split('.').First() }; - rootObject["units"] = _converterSettings.Current.SpeckleUnits; + new() { name = converterSettings.Current.Document.PathName.Split('\\').Last().Split('.').First() }; + rootObject["units"] = converterSettings.Current.SpeckleUnits; var revitElements = new List(); - + List results = new(revitElements.Count); // Convert ids to actual revit elements foreach (var id in objects) { - var el = _converterSettings.Current.Document.GetElement(id); - if (el != null) + var el = converterSettings.Current.Document.GetElement(id); + if (el == null) { - revitElements.Add(el); + continue; } + + if (el.Category == null) + { + continue; + } + + revitElements.Add(el); } if (revitElements.Count == 0) { - throw new SpeckleSendFilterException("No objects were found. Please update your send filter!"); + throw new SpeckleSendFilterException("No objects were found. Please update your publish filter!"); } - List results = new(revitElements.Count); - // Unpack groups (& other complex data structures) - var atomicObjects = _elementUnpacker.UnpackSelectionForConversion(revitElements).ToList(); + var atomicObjects = elementUnpacker.UnpackSelectionForConversion(revitElements).ToList(); var countProgress = 0; var cacheHitCount = 0; - + var skippedObjectCount = 0; foreach (Element revitElement in atomicObjects) { - ct.ThrowIfCancellationRequested(); + cancellationToken.ThrowIfCancellationRequested(); string applicationId = revitElement.UniqueId; string sourceType = revitElement.GetType().Name; try { + if (!SupportedCategoriesUtils.IsSupportedCategory(revitElement.Category)) + { + var cat = revitElement.Category != null ? revitElement.Category.Name : "No category"; + results.Add( + new( + Status.WARNING, + revitElement.UniqueId, + cat, + null, + new SpeckleException($"Category {cat} is not supported.") + ) + ); + skippedObjectCount++; + continue; + } + Base converted; - if (_sendConversionCache.TryGetValue(sendInfo.ProjectId, applicationId, out ObjectReference? value)) + if (sendConversionCache.TryGetValue(sendInfo.ProjectId, applicationId, out ObjectReference? value)) { converted = value; cacheHitCount++; } else { - converted = _converter.Convert(revitElement); + converted = converter.Convert(revitElement); converted.applicationId = applicationId; } - var collection = _sendCollectionManager.GetAndCreateObjectHostCollection(revitElement, rootObject); + var collection = sendCollectionManager.GetAndCreateObjectHostCollection(revitElement, rootObject); collection.elements.Add(converted); results.Add(new(Status.SUCCESS, applicationId, sourceType, converted)); } catch (Exception ex) when (!ex.IsFatal()) { - _logger.LogSendConversionError(ex, sourceType); + logger.LogSendConversionError(ex, sourceType); results.Add(new(Status.ERROR, applicationId, sourceType, null, ex)); } onOperationProgressed.Report(new("Converting", (double)++countProgress / atomicObjects.Count)); } - if (results.All(x => x.Status == Status.ERROR)) + if (results.All(x => x.Status == Status.ERROR) || skippedObjectCount == atomicObjects.Count) { throw new SpeckleException("Failed to convert all objects."); } - var idsAndSubElementIds = _elementUnpacker.GetElementsAndSubelementIdsFromAtomicObjects(atomicObjects); - var materialProxies = _revitToSpeckleCacheSingleton.GetRenderMaterialProxyListForObjects(idsAndSubElementIds); - rootObject[ProxyKeys.RENDER_MATERIAL] = materialProxies; + var idsAndSubElementIds = elementUnpacker.GetElementsAndSubelementIdsFromAtomicObjects(atomicObjects); + var renderMaterialProxies = revitToSpeckleCacheSingleton.GetRenderMaterialProxyListForObjects(idsAndSubElementIds); + rootObject[ProxyKeys.RENDER_MATERIAL] = renderMaterialProxies; // NOTE: these are currently not used anywhere, we'll skip them until someone calls for it back // rootObject[ProxyKeys.PARAMETER_DEFINITIONS] = _parameterDefinitionHandler.Definitions; diff --git a/Connectors/Revit/Speckle.Connectors.RevitShared/Operations/Send/Settings/ToSpeckleSettingsManager.cs b/Connectors/Revit/Speckle.Connectors.RevitShared/Operations/Send/Settings/ToSpeckleSettingsManager.cs index 96ce3fde7..4a789aae3 100644 --- a/Connectors/Revit/Speckle.Connectors.RevitShared/Operations/Send/Settings/ToSpeckleSettingsManager.cs +++ b/Connectors/Revit/Speckle.Connectors.RevitShared/Operations/Send/Settings/ToSpeckleSettingsManager.cs @@ -14,7 +14,6 @@ namespace Speckle.Connectors.Revit.Operations.Send.Settings; public class ToSpeckleSettingsManager : IToSpeckleSettingsManager { private readonly RevitContext _revitContext; - private readonly APIContext _apiContext; private readonly ISendConversionCache _sendConversionCache; private readonly ElementUnpacker _elementUnpacker; @@ -25,13 +24,11 @@ public class ToSpeckleSettingsManager : IToSpeckleSettingsManager public ToSpeckleSettingsManager( RevitContext revitContext, - APIContext apiContext, ISendConversionCache sendConversionCache, ElementUnpacker elementUnpacker ) { _revitContext = revitContext; - _apiContext = apiContext; _elementUnpacker = elementUnpacker; _sendConversionCache = sendConversionCache; } @@ -71,7 +68,7 @@ public class ToSpeckleSettingsManager : IToSpeckleSettingsManager { // get the current transform from setting first // we are doing this because we can't track if reference points were changed between send operations. - Transform? currentTransform = GetTransform(_revitContext, referencePoint); + Transform? currentTransform = GetTransform(referencePoint); if (_referencePointCache.TryGetValue(modelCard.ModelCardId.NotNull(), out Transform? previousTransform)) { @@ -112,11 +109,11 @@ public class ToSpeckleSettingsManager : IToSpeckleSettingsManager _sendConversionCache.EvictObjects(unpackedObjectIds); } - private Transform? GetTransform(RevitContext context, ReferencePointType referencePointType) + private Transform? GetTransform(ReferencePointType referencePointType) { Transform? referencePointTransform = null; - if (context.UIApplication is UIApplication uiApplication) + if (_revitContext.UIApplication is UIApplication uiApplication) { // first get the main doc base points and reference setting transform using FilteredElementCollector filteredElementCollector = new(uiApplication.ActiveUIDocument.Document); diff --git a/Connectors/Revit/Speckle.Connectors.RevitShared/Plugin/DUI3ControlWebViewDockable.cs b/Connectors/Revit/Speckle.Connectors.RevitShared/Plugin/DUI3ControlWebViewDockable.cs deleted file mode 100644 index 544b55f38..000000000 --- a/Connectors/Revit/Speckle.Connectors.RevitShared/Plugin/DUI3ControlWebViewDockable.cs +++ /dev/null @@ -1,28 +0,0 @@ -#if REVIT2025 -using System.Windows.Controls; -using Autodesk.Revit.UI; - -namespace Speckle.Connectors.DUI.WebView; - -/// -/// Wrapper that takes the and wraps it so that it can implement a revit specific -/// interface -/// -public sealed partial class DUI3ControlWebViewDockable : UserControl, Autodesk.Revit.UI.IDockablePaneProvider -{ - public DUI3ControlWebViewDockable(DUI3ControlWebView dUI3ControlWebView) - { - Content = dUI3ControlWebView; - } - - public void SetupDockablePane(DockablePaneProviderData data) - { - data.FrameworkElement = this; - data.InitialState = new Autodesk.Revit.UI.DockablePaneState - { - DockPosition = DockPosition.Tabbed, - TabBehind = DockablePanes.BuiltInDockablePanes.ProjectBrowser - }; - } -} -#endif diff --git a/Connectors/Revit/Speckle.Connectors.RevitShared/Plugin/RevitExternalApplication.cs b/Connectors/Revit/Speckle.Connectors.RevitShared/Plugin/RevitExternalApplication.cs index 0cd5c1b60..ea0751321 100644 --- a/Connectors/Revit/Speckle.Connectors.RevitShared/Plugin/RevitExternalApplication.cs +++ b/Connectors/Revit/Speckle.Connectors.RevitShared/Plugin/RevitExternalApplication.cs @@ -1,6 +1,7 @@ using Autodesk.Revit.UI; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using Revit.Async; using Speckle.Connectors.Common; using Speckle.Connectors.DUI; using Speckle.Connectors.Revit.DependencyInjection; @@ -50,6 +51,7 @@ internal sealed class RevitExternalApplication : IExternalApplication _container = services.BuildServiceProvider(); _container.UseDUI(); + RevitTask.Initialize(application); // resolve root object _revitPlugin = _container.GetRequiredService(); _revitPlugin.Initialise(); diff --git a/Connectors/Revit/Speckle.Connectors.RevitShared/Plugin/RevitIdleManager.cs b/Connectors/Revit/Speckle.Connectors.RevitShared/Plugin/RevitIdleManager.cs index cce89b5dd..3154fb3bd 100644 --- a/Connectors/Revit/Speckle.Connectors.RevitShared/Plugin/RevitIdleManager.cs +++ b/Connectors/Revit/Speckle.Connectors.RevitShared/Plugin/RevitIdleManager.cs @@ -6,12 +6,7 @@ using Speckle.Sdk.Common; namespace Speckle.Connectors.Revit.Plugin; -public interface IRevitIdleManager : IAppIdleManager -{ - public void RunAsync(Action action); -} - -public sealed class RevitIdleManager : AppIdleManager, IRevitIdleManager +public sealed class RevitIdleManager : AppIdleManager { private readonly UIApplication _uiApplication; private readonly IIdleCallManager _idleCallManager; @@ -42,13 +37,4 @@ public sealed class RevitIdleManager : AppIdleManager, IRevitIdleManager private void RevitAppOnIdle(object? sender, IdlingEventArgs e) => _idleCallManager.AppOnIdle(() => OnIdle -= RevitAppOnIdle); - - public void RunAsync(Action action) - { -#if REVIT2025 - global::Revit.Async.RevitTask.RunAsync(action); -#else - action(); -#endif - } } diff --git a/Connectors/Revit/Speckle.Connectors.RevitShared/Plugin/RevitThreadContext.cs b/Connectors/Revit/Speckle.Connectors.RevitShared/Plugin/RevitThreadContext.cs new file mode 100644 index 000000000..1b3866b4c --- /dev/null +++ b/Connectors/Revit/Speckle.Connectors.RevitShared/Plugin/RevitThreadContext.cs @@ -0,0 +1,89 @@ +using Revit.Async; +using Speckle.Connectors.Common.Threading; +using Speckle.Sdk; + +namespace Speckle.Connectors.Revit.Plugin; + +public class RevitThreadContext : ThreadContext +{ + protected override Task MainToWorkerAsync(Func> action) => action(); + + protected override Task WorkerToMainAsync(Func> action) => CatchExceptions(action); + + protected override Task MainToWorker(Func action) => Task.FromResult(action()); + + protected override Task WorkerToMain(Func action) => CatchExceptions(action); + + protected override Task RunMainAsync(Func action) => CatchExceptions(action); + + protected override Task RunMainAsync(Func action) => CatchExceptions(action); + + protected override Task RunMainAsync(Func> action) => CatchExceptions(action); + + private static async Task CatchExceptions(Func action) + { + Exception? ex = null; + //force the usage of the application overload + var ret = await RevitTask.RunAsync(_ => + { + try + { + return action(); + } + catch (Exception e) when (!e.IsFatal()) + { + ex = e; + return default; + } + }); + if (ex is not null) + { + throw new SpeckleRevitTaskException(ex); + } + return ret!; + } + + private static async Task CatchExceptions(Func> action) + { + Exception? ex = null; + //force the usage of the application overload + var ret = await RevitTask.RunAsync(async _ => + { + try + { + return await action(); + } + catch (Exception e) when (!e.IsFatal()) + { + ex = e; + return default; + } + }); + if (ex is not null) + { + throw new SpeckleRevitTaskException(ex); + } + return ret!; + } + + private static async Task CatchExceptions(Func action) + { + Exception? ex = null; + //force the usage of the application overload + await RevitTask.RunAsync(async _ => + { + try + { + await action(); + } + catch (Exception e) when (!e.IsFatal()) + { + ex = e; + } + }); + if (ex is not null) + { + throw new SpeckleRevitTaskException(ex); + } + } +} diff --git a/Connectors/Revit/Speckle.Connectors.RevitShared/Plugin/RevitWebViewPlugin.cs b/Connectors/Revit/Speckle.Connectors.RevitShared/Plugin/RevitWebViewPlugin.cs deleted file mode 100644 index 5f6e3b135..000000000 --- a/Connectors/Revit/Speckle.Connectors.RevitShared/Plugin/RevitWebViewPlugin.cs +++ /dev/null @@ -1,136 +0,0 @@ -#if REVIT2025 -using System.IO; -using System.Reflection; -using System.Windows.Media; -using System.Windows.Media.Imaging; -using Autodesk.Revit.ApplicationServices; -using Autodesk.Revit.UI; -using Revit.Async; -using Speckle.Connectors.DUI.WebView; -using Speckle.Connectors.Common; -using Speckle.Converters.RevitShared.Helpers; -using Speckle.Sdk; - -namespace Speckle.Connectors.Revit.Plugin; - -internal sealed class RevitWebViewPlugin : IRevitPlugin -{ - private readonly UIControlledApplication _uIControlledApplication; - private readonly RevitContext _revitContext; - private readonly DUI3ControlWebViewDockable _webViewPanel; - private readonly ISpeckleApplication _speckleApplication; - - [System.Diagnostics.CodeAnalysis.SuppressMessage( - "Style", - "IDE0290:Use primary constructor", - Justification = "" - )] - public RevitWebViewPlugin( - UIControlledApplication uIControlledApplication, - RevitContext revitContext, - DUI3ControlWebViewDockable webViewPanel, - ISpeckleApplication speckleApplication - ) - { - _uIControlledApplication = uIControlledApplication; - _revitContext = revitContext; - _webViewPanel = webViewPanel; - _speckleApplication = speckleApplication; - } - - public void Initialise() - { - // Create and register panels before app initialized. this is needed for double-click file open - CreateTabAndRibbonPanel(_uIControlledApplication); - RegisterDockablePane(); - _uIControlledApplication.ControlledApplication.ApplicationInitialized += OnApplicationInitialized; - } - - public void Shutdown() - { - // POC: should we be cleaning up the RibbonPanel etc... - // Should we be indicating to any active in-flight functions that we are being closed? - } - - // POC: Could be injected but maybe not worthwhile - private void CreateTabAndRibbonPanel(UIControlledApplication application) - { - // POC: some top-level handling and feedback here - try - { - application.CreateRibbonTab(Connector.TabName); - } - catch (ArgumentException) - { - // exception occurs when the speckle tab has already been created. - // this happens when both the dui2 and the dui3 connectors are installed. Can be safely ignored. - } - - RibbonPanel specklePanel = application.CreateRibbonPanel(Connector.TabName, Connector.TabTitle); - var dui3Button = (PushButton) - specklePanel.AddItem( - new PushButtonData( - "Speckle (Beta) for Revit", - Connector.TabTitle, - typeof(RevitExternalApplication).Assembly.Location, - typeof(SpeckleRevitCommand).FullName - ) - ); - - string path = typeof(RevitWebViewPlugin).Assembly.Location; - dui3Button.Image = LoadPngImgSource( - $"Speckle.Connectors.Revit{_speckleApplication.HostApplicationVersion}.Assets.logo16.png", - path - ); - dui3Button.LargeImage = LoadPngImgSource( - $"Speckle.Connectors.Revit{_speckleApplication.HostApplicationVersion}.Assets.logo32.png", - path - ); - dui3Button.ToolTipImage = LoadPngImgSource( - $"Speckle.Connectors.Revit{_speckleApplication.HostApplicationVersion}.Assets.logo32.png", - path - ); - dui3Button.ToolTip = "Speckle (Beta) for Revit"; - //dui3Button.AvailabilityClassName = typeof(CmdAvailabilityViews).FullName; - dui3Button.SetContextualHelp(new ContextualHelp(ContextualHelpType.Url, "https://speckle.systems")); - } - - private void OnApplicationInitialized(object? sender, Autodesk.Revit.DB.Events.ApplicationInitializedEventArgs e) - { - var uiApplication = new UIApplication(sender as Application); - _revitContext.UIApplication = uiApplication; - - // POC: might be worth to interface this out, we shall see... - RevitTask.Initialize(uiApplication); - } - - private void RegisterDockablePane() - { - // Registering dockable pane should happen before UiApplication is initialized with RevitTask. - // Otherwise pane cannot be registered for double-click file open. - _uIControlledApplication.RegisterDockablePane( - RevitExternalApplication.DockablePanelId, - Connector.TabTitle, - _webViewPanel - ); - } - - private ImageSource? LoadPngImgSource(string sourceName, string path) - { - try - { - var assembly = Assembly.LoadFrom(Path.Combine(path)); - var icon = assembly.GetManifestResourceStream(sourceName); - PngBitmapDecoder decoder = new(icon, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default); - ImageSource source = decoder.Frames[0]; - return source; - } - catch (Exception ex) when (!ex.IsFatal()) - { - // POC: logging - } - - return null; - } -} -#endif diff --git a/Connectors/Revit/Speckle.Connectors.RevitShared/Plugin/SpeckleRevitTaskException.cs b/Connectors/Revit/Speckle.Connectors.RevitShared/Plugin/SpeckleRevitTaskException.cs new file mode 100644 index 000000000..ecdc769f8 --- /dev/null +++ b/Connectors/Revit/Speckle.Connectors.RevitShared/Plugin/SpeckleRevitTaskException.cs @@ -0,0 +1,38 @@ +using Microsoft.Extensions.Logging; +using Speckle.Connectors.DUI.Bindings; +using Speckle.Connectors.DUI.Logging; +using Speckle.Sdk; +using Speckle.Sdk.Common; + +namespace Speckle.Connectors.Revit.Plugin; + +#pragma warning disable CA1032 +public class SpeckleRevitTaskException(Exception exception) : SpeckleException("Revit operation failed", exception) +#pragma warning restore CA1032 +{ + public static async Task ProcessException( + string modelCardId, + SpeckleRevitTaskException ex, + ILogger logger, + BasicConnectorBindingCommands commands + ) + where T : IBinding + { + Exception e = ex.InnerException.NotNull(); + while (e is SpeckleRevitTaskException srte) + { + e = srte.InnerException.NotNull(); + } + if (e is OperationCanceledException) + { + // SWALLOW -> UI handles it immediately, so we do not need to handle anything for now! + // Idea for later -> when cancel called, create promise from UI to solve it later with this catch block. + // So have 3 state on UI -> Cancellation clicked -> Cancelling -> Cancelled + return; + } + //log everything though + logger.LogModelCardHandledError(ex); + //always process the inner exception + await commands.SetModelError(modelCardId, e); + } +} diff --git a/Connectors/Revit/Speckle.Connectors.RevitShared/Speckle.Connectors.RevitShared.projitems b/Connectors/Revit/Speckle.Connectors.RevitShared/Speckle.Connectors.RevitShared.projitems index 350c552f7..0ccff3ac6 100644 --- a/Connectors/Revit/Speckle.Connectors.RevitShared/Speckle.Connectors.RevitShared.projitems +++ b/Connectors/Revit/Speckle.Connectors.RevitShared/Speckle.Connectors.RevitShared.projitems @@ -19,10 +19,10 @@ - + @@ -44,12 +44,12 @@ - - + + \ No newline at end of file diff --git a/Connectors/Rhino/Directory.Build.targets b/Connectors/Rhino/Directory.Build.targets new file mode 100644 index 000000000..323108eca --- /dev/null +++ b/Connectors/Rhino/Directory.Build.targets @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/Connectors/Rhino/Speckle.Connectors.Rhino7/Speckle.Connectors.Rhino7.csproj b/Connectors/Rhino/Speckle.Connectors.Rhino7/Speckle.Connectors.Rhino7.csproj index 241571403..98fdcd57d 100644 --- a/Connectors/Rhino/Speckle.Connectors.Rhino7/Speckle.Connectors.Rhino7.csproj +++ b/Connectors/Rhino/Speckle.Connectors.Rhino7/Speckle.Connectors.Rhino7.csproj @@ -29,4 +29,9 @@ + + + + + diff --git a/Connectors/Rhino/Speckle.Connectors.Rhino7/packages.lock.json b/Connectors/Rhino/Speckle.Connectors.Rhino7/packages.lock.json index da4e90ff7..c658af4ec 100644 --- a/Connectors/Rhino/Speckle.Connectors.Rhino7/packages.lock.json +++ b/Connectors/Rhino/Speckle.Connectors.Rhino7/packages.lock.json @@ -168,11 +168,6 @@ "resolved": "8.0.0", "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" }, - "Speckle.DoubleNumerics": { - "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w==" - }, "Speckle.Newtonsoft.Json": { "type": "Transitive", "resolved": "13.0.2", @@ -268,9 +263,9 @@ "dependencies": { "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", "Speckle.Connectors.Logging": "[1.0.0, )", - "Speckle.Objects": "[3.1.0-dev.191, )", - "Speckle.Sdk": "[3.1.0-dev.191, )", - "Speckle.Sdk.Dependencies": "[3.1.0-dev.191, )" + "Speckle.Objects": "[3.1.1, )", + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" } }, "speckle.connectors.dui": { @@ -278,9 +273,8 @@ "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", "Speckle.Connectors.Common": "[1.0.0, )", - "Speckle.Sdk": "[3.1.0-dev.191, )", - "Speckle.Sdk.Dependencies": "[3.1.0-dev.191, )", - "System.Threading.Tasks.Dataflow": "[6.0.0, )" + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" } }, "speckle.connectors.dui.webview": { @@ -297,7 +291,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.191, )" + "Speckle.Objects": "[3.1.1, )" } }, "speckle.converters.rhino7": { @@ -346,20 +340,26 @@ "resolved": "1.0.1938.49", "contentHash": "z8KnFnaTYzhA/ZnyRX0qGfS1NU5ZBJeClAH64F0fVDvdDJTvME7xl6zTJ0Jlfe1BtL3C0NH9xTy64shg2baKdw==" }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "+m7jRFm0ABbkcSz2UphdxAsislY10IpQ1u79c8a3aVvegLjnsVQZ1sXfRIRO1aFdulkhjYKXYpB3N9M8Z+epgQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.191" + "Speckle.Sdk": "3.1.1" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "VVT3LJiYlhqnggxxdeTt1QLrqfxDb044x0yX6kxS9b5MRzeDvK2Vz86pLDfuHs+SXvDimRVfYx1M42IW/aPcTQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.Bcl.AsyncInterfaces": "5.0.0", @@ -367,22 +367,16 @@ "Microsoft.Data.Sqlite": "7.0.5", "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", "Microsoft.Extensions.Logging": "2.2.0", - "Speckle.DoubleNumerics": "4.0.1", + "Speckle.DoubleNumerics": "4.1.0", "Speckle.Newtonsoft.Json": "13.0.2", - "Speckle.Sdk.Dependencies": "3.1.0-dev.191" + "Speckle.Sdk.Dependencies": "3.1.1" } }, "Speckle.Sdk.Dependencies": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "EmEOyjsGsNi56Z/ZoBOn8WirTmIT2yqWvlUeUh0BSPX2TDMZXHTKOM/kHmP6HSd10KVFn2Zo/ItY7/K9iRtL1Q==" - }, - "System.Threading.Tasks.Dataflow": { - "type": "CentralTransitive", - "requested": "[6.0.0, )", - "resolved": "6.0.0", - "contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA==" + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" } } } diff --git a/Connectors/Rhino/Speckle.Connectors.Rhino8/Speckle.Connectors.Rhino8.csproj b/Connectors/Rhino/Speckle.Connectors.Rhino8/Speckle.Connectors.Rhino8.csproj index 11250faa3..d8fa6d0d1 100644 --- a/Connectors/Rhino/Speckle.Connectors.Rhino8/Speckle.Connectors.Rhino8.csproj +++ b/Connectors/Rhino/Speckle.Connectors.Rhino8/Speckle.Connectors.Rhino8.csproj @@ -30,5 +30,8 @@ - + + + + diff --git a/Connectors/Rhino/Speckle.Connectors.Rhino8/packages.lock.json b/Connectors/Rhino/Speckle.Connectors.Rhino8/packages.lock.json index 9b12afbb4..69b9f9b7a 100644 --- a/Connectors/Rhino/Speckle.Connectors.Rhino8/packages.lock.json +++ b/Connectors/Rhino/Speckle.Connectors.Rhino8/packages.lock.json @@ -168,11 +168,6 @@ "resolved": "8.0.0", "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" }, - "Speckle.DoubleNumerics": { - "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w==" - }, "Speckle.Newtonsoft.Json": { "type": "Transitive", "resolved": "13.0.2", @@ -268,9 +263,9 @@ "dependencies": { "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", "Speckle.Connectors.Logging": "[1.0.0, )", - "Speckle.Objects": "[3.1.0-dev.191, )", - "Speckle.Sdk": "[3.1.0-dev.191, )", - "Speckle.Sdk.Dependencies": "[3.1.0-dev.191, )" + "Speckle.Objects": "[3.1.1, )", + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" } }, "speckle.connectors.dui": { @@ -278,9 +273,8 @@ "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", "Speckle.Connectors.Common": "[1.0.0, )", - "Speckle.Sdk": "[3.1.0-dev.191, )", - "Speckle.Sdk.Dependencies": "[3.1.0-dev.191, )", - "System.Threading.Tasks.Dataflow": "[6.0.0, )" + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" } }, "speckle.connectors.dui.webview": { @@ -304,7 +298,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.191, )" + "Speckle.Objects": "[3.1.1, )" } }, "speckle.converters.rhino8": { @@ -353,20 +347,26 @@ "resolved": "1.0.1938.49", "contentHash": "z8KnFnaTYzhA/ZnyRX0qGfS1NU5ZBJeClAH64F0fVDvdDJTvME7xl6zTJ0Jlfe1BtL3C0NH9xTy64shg2baKdw==" }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "+m7jRFm0ABbkcSz2UphdxAsislY10IpQ1u79c8a3aVvegLjnsVQZ1sXfRIRO1aFdulkhjYKXYpB3N9M8Z+epgQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.191" + "Speckle.Sdk": "3.1.1" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "VVT3LJiYlhqnggxxdeTt1QLrqfxDb044x0yX6kxS9b5MRzeDvK2Vz86pLDfuHs+SXvDimRVfYx1M42IW/aPcTQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.Bcl.AsyncInterfaces": "5.0.0", @@ -374,22 +374,16 @@ "Microsoft.Data.Sqlite": "7.0.5", "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", "Microsoft.Extensions.Logging": "2.2.0", - "Speckle.DoubleNumerics": "4.0.1", + "Speckle.DoubleNumerics": "4.1.0", "Speckle.Newtonsoft.Json": "13.0.2", - "Speckle.Sdk.Dependencies": "3.1.0-dev.191" + "Speckle.Sdk.Dependencies": "3.1.1" } }, "Speckle.Sdk.Dependencies": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "EmEOyjsGsNi56Z/ZoBOn8WirTmIT2yqWvlUeUh0BSPX2TDMZXHTKOM/kHmP6HSd10KVFn2Zo/ItY7/K9iRtL1Q==" - }, - "System.Threading.Tasks.Dataflow": { - "type": "CentralTransitive", - "requested": "[6.0.0, )", - "resolved": "6.0.0", - "contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA==" + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" } } } diff --git a/Connectors/Rhino/Speckle.Connectors.RhinoShared/Bindings/RhinoBasicConnectorBinding.cs b/Connectors/Rhino/Speckle.Connectors.RhinoShared/Bindings/RhinoBasicConnectorBinding.cs index 769eb48ae..c457c85cf 100644 --- a/Connectors/Rhino/Speckle.Connectors.RhinoShared/Bindings/RhinoBasicConnectorBinding.cs +++ b/Connectors/Rhino/Speckle.Connectors.RhinoShared/Bindings/RhinoBasicConnectorBinding.cs @@ -21,27 +21,32 @@ public sealed class RhinoBasicConnectorBinding : IBasicConnectorBinding private readonly DocumentModelStore _store; private readonly ISendConversionCache _sendConversionCache; private readonly ISpeckleApplication _speckleApplication; + private readonly ITopLevelExceptionHandler _topLevelExceptionHandler; public RhinoBasicConnectorBinding( DocumentModelStore store, IBrowserBridge parent, ISendConversionCache sendConversionCache, - ISpeckleApplication speckleApplication + ISpeckleApplication speckleApplication, + ITopLevelExceptionHandler topLevelExceptionHandler ) { _store = store; Parent = parent; _sendConversionCache = sendConversionCache; _speckleApplication = speckleApplication; + _topLevelExceptionHandler = topLevelExceptionHandler; Commands = new BasicConnectorBindingCommands(parent); _store.DocumentChanged += (_, _) => - parent.TopLevelExceptionHandler.FireAndForget(async () => + _topLevelExceptionHandler.FireAndForget(async () => { - await Commands.NotifyDocumentChanged().ConfigureAwait(false); + await Commands.NotifyDocumentChanged(); // Note: this prevents scaling issues when copy-pasting from one rhino doc to another in the same session. _sendConversionCache.ClearCache(); }); + + // eventAggregator.GetEvent().Subscribe(OnDocumentStoreChangedEvent); } public string GetConnectorVersion() => _speckleApplication.SpeckleVersion; @@ -61,7 +66,7 @@ public sealed class RhinoBasicConnectorBinding : IBasicConnectorBinding public DocumentModelStore GetDocumentState() => _store; - public void AddModel(ModelCard model) => _store.Models.Add(model); + public void AddModel(ModelCard model) => _store.AddModel(model); public void UpdateModel(ModelCard model) => _store.UpdateModel(model); @@ -100,9 +105,7 @@ public sealed class RhinoBasicConnectorBinding : IBasicConnectorBinding if (objectIds.Count == 0) { - await Commands - .SetModelError(modelCardId, new OperationCanceledException("No objects found to highlight.")) - .ConfigureAwait(false); + await Commands.SetModelError(modelCardId, new OperationCanceledException("No objects found to highlight.")); return; } @@ -112,9 +115,7 @@ public sealed class RhinoBasicConnectorBinding : IBasicConnectorBinding if (objects.rhinoObjects.Count == 0 && objects.groups.Count == 0) { - await Commands - .SetModelError(modelCardId, new OperationCanceledException("No objects found to highlight.")) - .ConfigureAwait(false); + await Commands.SetModelError(modelCardId, new OperationCanceledException("No objects found to highlight.")); return; } diff --git a/Connectors/Rhino/Speckle.Connectors.RhinoShared/Bindings/RhinoReceiveBinding.cs b/Connectors/Rhino/Speckle.Connectors.RhinoShared/Bindings/RhinoReceiveBinding.cs index dcf60ab73..bbb1d5145 100644 --- a/Connectors/Rhino/Speckle.Connectors.RhinoShared/Bindings/RhinoReceiveBinding.cs +++ b/Connectors/Rhino/Speckle.Connectors.RhinoShared/Bindings/RhinoReceiveBinding.cs @@ -20,7 +20,7 @@ public class RhinoReceiveBinding : IReceiveBinding public string Name => "receiveBinding"; public IBrowserBridge Parent { get; } - private readonly CancellationManager _cancellationManager; + private readonly ICancellationManager _cancellationManager; private readonly DocumentModelStore _store; private readonly IServiceProvider _serviceProvider; private readonly IOperationProgressManager _operationProgressManager; @@ -31,7 +31,7 @@ public class RhinoReceiveBinding : IReceiveBinding public RhinoReceiveBinding( DocumentModelStore store, - CancellationManager cancellationManager, + ICancellationManager cancellationManager, IBrowserBridge parent, IOperationProgressManager operationProgressManager, ILogger logger, @@ -59,6 +59,8 @@ public class RhinoReceiveBinding : IReceiveBinding scope .ServiceProvider.GetRequiredService>() .Initialize(_rhinoConversionSettingsFactory.Create(RhinoDoc.ActiveDoc)); + + uint undoRecord = 0; try { // Get receiver card @@ -68,22 +70,24 @@ public class RhinoReceiveBinding : IReceiveBinding throw new InvalidOperationException("No download model card was found."); } - CancellationToken cancellationToken = _cancellationManager.InitCancellationTokenSource(modelCardId); + using var cancellationItem = _cancellationManager.GetCancellationItem(modelCardId); + undoRecord = RhinoDoc.ActiveDoc.BeginUndoRecord($"Receive Speckle model {modelCard.ModelName}"); // Receive host objects HostObjectBuilderResult conversionResults = await scope .ServiceProvider.GetRequiredService() .Execute( modelCard.GetReceiveInfo(_speckleApplication.Slug), - _operationProgressManager.CreateOperationProgressEventHandler(Parent, modelCardId, cancellationToken), - cancellationToken - ) - .ConfigureAwait(false); + _operationProgressManager.CreateOperationProgressEventHandler(Parent, modelCardId, cancellationItem.Token), + cancellationItem.Token + ); modelCard.BakedObjectIds = conversionResults.BakedObjectIds.ToList(); - await Commands - .SetModelReceiveResult(modelCardId, conversionResults.BakedObjectIds, conversionResults.ConversionResults) - .ConfigureAwait(false); + await Commands.SetModelReceiveResult( + modelCardId, + conversionResults.BakedObjectIds, + conversionResults.ConversionResults + ); } catch (OperationCanceledException) { @@ -95,7 +99,11 @@ public class RhinoReceiveBinding : IReceiveBinding catch (Exception ex) when (!ex.IsFatal()) // UX reasons - we will report operation exceptions as model card error. We may change this later when we have more exception documentation { _logger.LogModelCardHandledError(ex); - await Commands.SetModelError(modelCardId, ex).ConfigureAwait(false); + await Commands.SetModelError(modelCardId, ex); + } + finally + { + RhinoDoc.ActiveDoc.EndUndoRecord(undoRecord); } } diff --git a/Connectors/Rhino/Speckle.Connectors.RhinoShared/Bindings/RhinoSelectionBinding.cs b/Connectors/Rhino/Speckle.Connectors.RhinoShared/Bindings/RhinoSelectionBinding.cs index d2679b8b3..5794622bf 100644 --- a/Connectors/Rhino/Speckle.Connectors.RhinoShared/Bindings/RhinoSelectionBinding.cs +++ b/Connectors/Rhino/Speckle.Connectors.RhinoShared/Bindings/RhinoSelectionBinding.cs @@ -24,7 +24,7 @@ public class RhinoSelectionBinding : ISelectionBinding } private void OnSelectionChange(object? o, EventArgs eventArgs) => - _idleManager.SubscribeToIdle(nameof(RhinoSelectionBinding), UpdateSelection); + _idleManager.SubscribeToIdle(nameof(UpdateSelection), UpdateSelection); private void UpdateSelection() { diff --git a/Connectors/Rhino/Speckle.Connectors.RhinoShared/Bindings/RhinoSendBinding.cs b/Connectors/Rhino/Speckle.Connectors.RhinoShared/Bindings/RhinoSendBinding.cs index 1d4e454b8..1fc834d76 100644 --- a/Connectors/Rhino/Speckle.Connectors.RhinoShared/Bindings/RhinoSendBinding.cs +++ b/Connectors/Rhino/Speckle.Connectors.RhinoShared/Bindings/RhinoSendBinding.cs @@ -4,6 +4,7 @@ using Microsoft.Extensions.Logging; using Rhino; using Rhino.Commands; using Rhino.DocObjects; +using Rhino.DocObjects.Tables; using Speckle.Connectors.Common.Caching; using Speckle.Connectors.Common.Cancellation; using Speckle.Connectors.Common.Operations; @@ -30,26 +31,34 @@ public sealed class RhinoSendBinding : ISendBinding public IBrowserBridge Parent { get; } private readonly DocumentModelStore _store; - private readonly IAppIdleManager _idleManager; private readonly IServiceProvider _serviceProvider; private readonly List _sendFilters; - private readonly CancellationManager _cancellationManager; + private readonly ICancellationManager _cancellationManager; private readonly ISendConversionCache _sendConversionCache; private readonly IOperationProgressManager _operationProgressManager; private readonly ILogger _logger; - private readonly ITopLevelExceptionHandler _topLevelExceptionHandler; private readonly IRhinoConversionSettingsFactory _rhinoConversionSettingsFactory; private readonly ISpeckleApplication _speckleApplication; private readonly ISdkActivityFactory _activityFactory; + private readonly ITopLevelExceptionHandler _topLevelExceptionHandler; + private readonly IAppIdleManager _idleManager; /// - /// Used internally to aggregate the changed objects' id. Note we're using a concurrent dictionary here as the expiry check method is not thread safe, and this was causing problems. See: + /// Used internally to aggregate the changed objects' id. Objects in this list will be reconverted. + /// + /// Note we're using a concurrent dictionary here as the expiry check method is not thread safe, and this was causing problems. See: /// [CNX-202: Unhandled Exception Occurred when receiving in Rhino](https://linear.app/speckle/issue/CNX-202/unhandled-exception-occurred-when-receiving-in-rhino) /// As to why a concurrent dictionary, it's because it's the cheapest/easiest way to do so. /// https://stackoverflow.com/questions/18922985/concurrent-hashsett-in-net-framework /// private ConcurrentDictionary ChangedObjectIds { get; set; } = new(); + /// + /// Stores objects that have "changed" only the commit structure/proxies - they do not need to be reconverted. + /// + private ConcurrentDictionary ChangedObjectIdsInGroupsOrLayers { get; set; } = new(); + private ConcurrentDictionary ChangedMaterialIndexes { get; set; } = new(); + private UnitSystem PreviousUnitSystem { get; set; } public RhinoSendBinding( @@ -58,13 +67,14 @@ public sealed class RhinoSendBinding : ISendBinding IBrowserBridge parent, IEnumerable sendFilters, IServiceProvider serviceProvider, - CancellationManager cancellationManager, + ICancellationManager cancellationManager, ISendConversionCache sendConversionCache, IOperationProgressManager operationProgressManager, ILogger logger, IRhinoConversionSettingsFactory rhinoConversionSettingsFactory, ISpeckleApplication speckleApplication, - ISdkActivityFactory activityFactory + ISdkActivityFactory activityFactory, + ITopLevelExceptionHandler topLevelExceptionHandler ) { _store = store; @@ -77,15 +87,17 @@ public sealed class RhinoSendBinding : ISendBinding _logger = logger; _rhinoConversionSettingsFactory = rhinoConversionSettingsFactory; _speckleApplication = speckleApplication; - _topLevelExceptionHandler = parent.TopLevelExceptionHandler.Parent.TopLevelExceptionHandler; Parent = parent; + _topLevelExceptionHandler = topLevelExceptionHandler; Commands = new SendBindingUICommands(parent); // POC: Commands are tightly coupled with their bindings, at least for now, saves us injecting a factory. _activityFactory = activityFactory; PreviousUnitSystem = RhinoDoc.ActiveDoc.ModelUnitSystem; SubscribeToRhinoEvents(); } +#pragma warning disable CA1502 private void SubscribeToRhinoEvents() +#pragma warning restore CA1502 { Command.BeginCommand += (_, e) => { @@ -94,6 +106,15 @@ public sealed class RhinoSendBinding : ISendBinding var selectedObject = RhinoDoc.ActiveDoc.Objects.GetSelectedObjects(false, false).First(); ChangedObjectIds[selectedObject.Id.ToString()] = 1; } + + if (e.CommandEnglishName == "Ungroup") + { + foreach (RhinoObject selectedObject in RhinoDoc.ActiveDoc.Objects.GetSelectedObjects(false, false)) + { + ChangedObjectIdsInGroupsOrLayers[selectedObject.Id.ToString()] = 1; + } + _idleManager.SubscribeToIdle(nameof(RunExpirationChecks), RunExpirationChecks); + } }; RhinoDoc.ActiveDocumentChanged += (_, e) => @@ -109,69 +130,150 @@ public sealed class RhinoSendBinding : ISendBinding { PreviousUnitSystem = newUnit; - await InvalidateAllSender().ConfigureAwait(false); + await InvalidateAllSender(); } }; RhinoDoc.AddRhinoObject += (_, e) => _topLevelExceptionHandler.CatchUnhandled(() => { - // NOTE: This does not work if rhino starts and opens a blank doc; - // These events always happen in a doc. Why guard agains a null doc? - // if (!_store.IsDocumentInit) - // { - // return; - // } + if (!_store.IsDocumentInit) + { + return; + } ChangedObjectIds[e.ObjectId.ToString()] = 1; - _idleManager.SubscribeToIdle(nameof(RhinoSendBinding), RunExpirationChecks); + _idleManager.SubscribeToIdle(nameof(RunExpirationChecks), RunExpirationChecks); }); RhinoDoc.DeleteRhinoObject += (_, e) => _topLevelExceptionHandler.CatchUnhandled(() => { - // NOTE: This does not work if rhino starts and opens a blank doc; - // These events always happen in a doc. Why guard agains a null doc? - // if (!_store.IsDocumentInit) - // { - // return; - // } + if (!_store.IsDocumentInit) + { + return; + } ChangedObjectIds[e.ObjectId.ToString()] = 1; - _idleManager.SubscribeToIdle(nameof(RhinoSendBinding), RunExpirationChecks); + _idleManager.SubscribeToIdle(nameof(RunExpirationChecks), RunExpirationChecks); + }); + + // NOTE: Catches an object's material change from one user defined doc material to another. Does not catch (as the top event is not triggered) swapping material sources for an object or moving to/from the default material (this is handled below)! + RhinoDoc.RenderMaterialsTableEvent += (_, args) => + _topLevelExceptionHandler.CatchUnhandled(() => + { + if (!_store.IsDocumentInit) + { + return; + } + + if (args is RhinoDoc.RenderMaterialAssignmentChangedEventArgs changedEventArgs) + { + ChangedObjectIds[changedEventArgs.ObjectId.ToString()] = 1; + _idleManager.SubscribeToIdle(nameof(RunExpirationChecks), RunExpirationChecks); + } + }); + + RhinoDoc.GroupTableEvent += (_, args) => + _topLevelExceptionHandler.CatchUnhandled(() => + { + if (!_store.IsDocumentInit) + { + return; + } + + foreach (var obj in RhinoDoc.ActiveDoc.Groups.GroupMembers(args.GroupIndex)) + { + ChangedObjectIdsInGroupsOrLayers[obj.Id.ToString()] = 1; + } + _idleManager.SubscribeToIdle(nameof(RunExpirationChecks), RunExpirationChecks); + }); + + RhinoDoc.LayerTableEvent += (_, args) => + _topLevelExceptionHandler.CatchUnhandled(() => + { + if (!_store.IsDocumentInit) + { + return; + } + + if ( + args.EventType == LayerTableEventType.Deleted + || args.EventType == LayerTableEventType.Current + || args.EventType == LayerTableEventType.Added + ) + { + return; + } + + var layer = RhinoDoc.ActiveDoc.Layers[args.LayerIndex]; + + var allLayers = args.Document.Layers.Where(l => /* NOTE: layer path may actually be null in some cases (rhino's fault, not ours) */ + l.FullPath != null && l.FullPath.Contains(layer.Name) + ); // not e imperfect, but layer.GetChildren(true) is valid only in v8 and above; this filter will include the original layer. + foreach (var childLayer in allLayers) + { + var sublayerObjs = RhinoDoc.ActiveDoc.Objects.FindByLayer(childLayer) ?? []; + foreach (var obj in sublayerObjs) + { + ChangedObjectIdsInGroupsOrLayers[obj.Id.ToString()] = 1; + } + } + _idleManager.SubscribeToIdle(nameof(RunExpirationChecks), RunExpirationChecks); + }); + + // Catches and stores changed material ids. These are then used in the expiry checks to invalidate all objects that have assigned any of those material ids. + RhinoDoc.MaterialTableEvent += (_, args) => + _topLevelExceptionHandler.CatchUnhandled(() => + { + if (!_store.IsDocumentInit) + { + return; + } + + if (args.EventType == MaterialTableEventType.Modified) + { + ChangedMaterialIndexes[args.Index] = 1; + _idleManager.SubscribeToIdle(nameof(RunExpirationChecks), RunExpirationChecks); + } }); RhinoDoc.ModifyObjectAttributes += (_, e) => _topLevelExceptionHandler.CatchUnhandled(() => { - // NOTE: This does not work if rhino starts and opens a blank doc; - // These events always happen in a doc. Why guard agains a null doc? - // if (!_store.IsDocumentInit) - // { - // return; - // } + if (!_store.IsDocumentInit) + { + return; + } - // NOTE: not sure yet we want to track every attribute changes yet. TBD - if (e.OldAttributes.LayerIndex != e.NewAttributes.LayerIndex) + // NOTE: not sure yet we want to track every attribute changes yet. Explicitly tracking atts that change commit data. TBD + if ( + e.OldAttributes.LayerIndex != e.NewAttributes.LayerIndex + || e.OldAttributes.MaterialSource != e.NewAttributes.MaterialSource + || e.OldAttributes.MaterialIndex != e.NewAttributes.MaterialIndex // NOTE: this does not work when swapping around from custom doc materials, it works when you swap TO/FROM default material + || e.OldAttributes.ColorSource != e.NewAttributes.ColorSource + || e.OldAttributes.ObjectColor != e.NewAttributes.ObjectColor + || e.OldAttributes.Name != e.NewAttributes.Name + || e.OldAttributes.UserStringCount != e.NewAttributes.UserStringCount + || e.OldAttributes.GetUserStrings() != e.NewAttributes.GetUserStrings() + ) { ChangedObjectIds[e.RhinoObject.Id.ToString()] = 1; - _idleManager.SubscribeToIdle(nameof(RhinoSendBinding), RunExpirationChecks); + _idleManager.SubscribeToIdle(nameof(RunExpirationChecks), RunExpirationChecks); } }); RhinoDoc.ReplaceRhinoObject += (_, e) => _topLevelExceptionHandler.CatchUnhandled(() => { - // NOTE: This does not work if rhino starts and opens a blank doc; - // These events always happen in a doc. Why guard agains a null doc? - // if (!_store.IsDocumentInit) - // { - // return; - // } + if (!_store.IsDocumentInit) + { + return; + } ChangedObjectIds[e.NewRhinoObject.Id.ToString()] = 1; ChangedObjectIds[e.OldRhinoObject.Id.ToString()] = 1; - _idleManager.SubscribeToIdle(nameof(RhinoSendBinding), RunExpirationChecks); + _idleManager.SubscribeToIdle(nameof(RunExpirationChecks), RunExpirationChecks); }); } @@ -194,7 +296,7 @@ public sealed class RhinoSendBinding : ISendBinding throw new InvalidOperationException("No publish model card was found."); } - CancellationToken cancellationToken = _cancellationManager.InitCancellationTokenSource(modelCardId); + using var cancellationItem = _cancellationManager.GetCancellationItem(modelCardId); List rhinoObjects = modelCard .SendFilter.NotNull() @@ -213,15 +315,12 @@ public sealed class RhinoSendBinding : ISendBinding .ServiceProvider.GetRequiredService>() .Execute( rhinoObjects, - modelCard.GetSendInfo(_speckleApplication.Slug), - _operationProgressManager.CreateOperationProgressEventHandler(Parent, modelCardId, cancellationToken), - cancellationToken - ) - .ConfigureAwait(false); + modelCard.GetSendInfo(_speckleApplication.ApplicationAndVersion), + _operationProgressManager.CreateOperationProgressEventHandler(Parent, modelCardId, cancellationItem.Token), + cancellationItem.Token + ); - await Commands - .SetModelSendResult(modelCardId, sendResult.RootObjId, sendResult.ConversionResults) - .ConfigureAwait(false); + await Commands.SetModelSendResult(modelCardId, sendResult.RootObjId, sendResult.ConversionResults); } catch (OperationCanceledException) { @@ -233,7 +332,7 @@ public sealed class RhinoSendBinding : ISendBinding catch (Exception ex) when (!ex.IsFatal()) // UX reasons - we will report operation exceptions as model card error. We may change this later when we have more exception documentation { _logger.LogModelCardHandledError(ex); - await Commands.SetModelError(modelCardId, ex).ConfigureAwait(false); + await Commands.SetModelError(modelCardId, ex); } } @@ -250,30 +349,61 @@ public sealed class RhinoSendBinding : ISendBinding _logger.LogError("Rhino expiration checks were running without an active doc."); return; } - var senders = _store.GetSenders(); - string[] objectIdsList = ChangedObjectIds.Keys.ToArray(); // NOTE: could not copy to array happens here - List expiredSenderIds = new(); - _sendConversionCache.EvictObjects(objectIdsList); - - foreach (SenderModelCard modelCard in senders) + // Invalidate any objects whose materials have changed + if (!ChangedMaterialIndexes.IsEmpty) { - var intersection = modelCard.SendFilter.NotNull().SelectedObjectIds.Intersect(objectIdsList).ToList(); - var isExpired = intersection.Count != 0; - if (isExpired) + var changedMaterialIndexes = ChangedMaterialIndexes.Keys.ToArray(); + foreach (var rhinoObject in RhinoDoc.ActiveDoc.Objects) { - expiredSenderIds.Add(modelCard.ModelCardId.NotNull()); + if (changedMaterialIndexes.Contains(rhinoObject.Attributes.MaterialIndex)) + { + ChangedObjectIds[rhinoObject.Id.ToString()] = 1; + } } } - await Commands.SetModelsExpired(expiredSenderIds).ConfigureAwait(false); + if (ChangedObjectIds.IsEmpty && ChangedObjectIdsInGroupsOrLayers.IsEmpty) + { + return; + } + + // Actual model card invalidation + string[] objectIdsList = ChangedObjectIds.Keys.ToArray(); + var changedObjectIdsInGroupsOrLayers = ChangedObjectIdsInGroupsOrLayers.Keys.ToArray(); + _sendConversionCache.EvictObjects(objectIdsList); + var senders = _store.GetSenders(); + List expiredSenderIds = new(); + + foreach (SenderModelCard modelCard in senders) + { + var intersection = modelCard.SendFilter.NotNull().SelectedObjectIds.Intersect(objectIdsList); + if (intersection.Any()) + { + expiredSenderIds.Add(modelCard.ModelCardId.NotNull()); + continue; + } + + var groupOrLayerIntersection = modelCard + .SendFilter.NotNull() + .SelectedObjectIds.Intersect(changedObjectIdsInGroupsOrLayers); + if (groupOrLayerIntersection.Any()) + { + expiredSenderIds.Add(modelCard.ModelCardId.NotNull()); + continue; + } + } + + await Commands.SetModelsExpired(expiredSenderIds); ChangedObjectIds = new(); + ChangedObjectIdsInGroupsOrLayers = new(); + ChangedMaterialIndexes = new(); } private async Task InvalidateAllSender() { _sendConversionCache.ClearCache(); var senderModelCardIds = _store.GetSenders().Select(s => s.ModelCardId.NotNull()); - await Commands.SetModelsExpired(senderModelCardIds).ConfigureAwait(false); + await Commands.SetModelsExpired(senderModelCardIds); } } diff --git a/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/Properties/PropertiesExtractor.cs b/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/Properties/PropertiesExtractor.cs new file mode 100644 index 000000000..ca685cd4e --- /dev/null +++ b/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/Properties/PropertiesExtractor.cs @@ -0,0 +1,37 @@ +using Rhino; +using Rhino.DocObjects; +using Speckle.Sdk; + +namespace Speckle.Connectors.Rhino.HostApp.Properties; + +/// +/// Extracts properties for rhino objects. +/// +public class PropertiesExtractor +{ + public Dictionary GetProperties(RhinoObject rhObject) + { + Dictionary properties = new(); + var userStrings = rhObject.Attributes.GetUserStrings(); + foreach (var key in userStrings.AllKeys) + { + try + { + if (userStrings[key].StartsWith("%<")) + { + var value = RhinoApp.ParseTextField(userStrings[key], rhObject, null); + properties[key] = value; + continue; + } + } + catch (Exception ex) when (!ex.IsFatal()) + { + // Shh. We can fail silently here - it's not even worth logging. I expect users will complain properties are missing. + } + + properties[key] = userStrings[key]; + } + + return properties; + } +} diff --git a/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/RhinoColorBaker.cs b/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/RhinoColorBaker.cs index 7c1f38fd8..c931aafb5 100644 --- a/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/RhinoColorBaker.cs +++ b/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/RhinoColorBaker.cs @@ -23,7 +23,7 @@ public class RhinoColorBaker /// Parse Color Proxies and stores in ObjectColorsIdMap the relationship between object ids and colors /// /// - public void ParseColors(List colorProxies) + public void ParseColors(IReadOnlyCollection colorProxies) { foreach (ColorProxy colorProxy in colorProxies) { diff --git a/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/RhinoColorUnpacker.cs b/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/RhinoColorUnpacker.cs index 11b87fba1..085c7ec06 100644 --- a/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/RhinoColorUnpacker.cs +++ b/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/RhinoColorUnpacker.cs @@ -1,4 +1,4 @@ -using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging; using Rhino; using Rhino.DocObjects; using Speckle.Connectors.Rhino.Extensions; @@ -99,7 +99,7 @@ public class RhinoColorUnpacker /// Iterates through a given set of rhino objects and layers to collect colors. /// /// atomic root objects, including instance objects - /// layers used by atomic objects + /// the layers corresponding to collections on the root collection /// public List UnpackColors(List atomicObjects, List layers) { diff --git a/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/RhinoDocumentStore.cs b/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/RhinoDocumentStore.cs index 32f4cf0e8..9df8fff23 100644 --- a/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/RhinoDocumentStore.cs +++ b/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/RhinoDocumentStore.cs @@ -7,53 +7,42 @@ namespace Speckle.Connectors.Rhino.HostApp; public class RhinoDocumentStore : DocumentModelStore { + private readonly ITopLevelExceptionHandler _topLevelExceptionHandler; private const string SPECKLE_KEY = "Speckle_DUI3"; public override bool IsDocumentInit { get; set; } = true; // Note: because of rhino implementation details regarding expiry checking of sender cards. public RhinoDocumentStore(IJsonSerializer jsonSerializer, ITopLevelExceptionHandler topLevelExceptionHandler) - : base(jsonSerializer, true) + : base(jsonSerializer) { + _topLevelExceptionHandler = topLevelExceptionHandler; RhinoDoc.BeginOpenDocument += (_, _) => topLevelExceptionHandler.CatchUnhandled(() => IsDocumentInit = false); RhinoDoc.EndOpenDocument += (_, e) => topLevelExceptionHandler.CatchUnhandled(() => { - if (e.Merge) - { - return; - } - if (e.Document == null) { return; } IsDocumentInit = true; - ReadFromFile(); + LoadState(); OnDocumentChanged(); }); } - public override void WriteToFile() + protected override void HostAppSaveState(string modelCardState) { if (RhinoDoc.ActiveDoc == null) { return; // Should throw } - RhinoDoc.ActiveDoc.Strings.Delete(SPECKLE_KEY); - - string serializedState = Serialize(); - RhinoDoc.ActiveDoc.Strings.SetString(SPECKLE_KEY, SPECKLE_KEY, serializedState); + RhinoDoc.ActiveDoc.Strings.SetString(SPECKLE_KEY, SPECKLE_KEY, modelCardState); } - public override void ReadFromFile() + protected override void LoadState() { string stateString = RhinoDoc.ActiveDoc.Strings.GetValue(SPECKLE_KEY, SPECKLE_KEY); - if (stateString == null) - { - Models = new(); - return; - } - Models = Deserialize(stateString) ?? new(); + LoadFromString(stateString); } } diff --git a/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/RhinoGroupBaker.cs b/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/RhinoGroupBaker.cs index fcf52d524..3ed1852f0 100644 --- a/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/RhinoGroupBaker.cs +++ b/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/RhinoGroupBaker.cs @@ -25,8 +25,8 @@ public class RhinoGroupBaker } public void BakeGroups( - List groupProxies, - Dictionary> applicationIdMap, + IReadOnlyCollection groupProxies, + Dictionary> applicationIdMap, string baseLayerName ) { diff --git a/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/RhinoInstanceBaker.cs b/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/RhinoInstanceBaker.cs index 8cc716e9f..b497d5588 100644 --- a/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/RhinoInstanceBaker.cs +++ b/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/RhinoInstanceBaker.cs @@ -1,4 +1,4 @@ -using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging; using Rhino; using Rhino.DocObjects; using Rhino.Geometry; @@ -16,7 +16,7 @@ using Speckle.Sdk.Models.Instances; namespace Speckle.Connectors.Rhino.HostApp; -public class RhinoInstanceBaker : IInstanceBaker> +public class RhinoInstanceBaker : IInstanceBaker> { private readonly RhinoMaterialBaker _materialBaker; private readonly RhinoLayerBaker _layerBaker; @@ -42,9 +42,9 @@ public class RhinoInstanceBaker : IInstanceBaker> /// Instance definitions and instances that need creating. /// A dict mapping { original application id -> [resulting application ids post conversion] } /// - public async Task BakeInstances( - IReadOnlyCollection<(Collection[] collectionPath, IInstanceComponent obj)> instanceComponents, - Dictionary> applicationIdMap, + public BakeResult BakeInstances( + ICollection<(Collection[] collectionPath, IInstanceComponent obj)> instanceComponents, + Dictionary> applicationIdMap, string baseLayerName, IProgress onOperationProgressed ) @@ -59,9 +59,9 @@ public class RhinoInstanceBaker : IInstanceBaker> var definitionIdAndApplicationIdMap = new Dictionary(); var count = 0; - var conversionResults = new List(); - var createdObjectIds = new List(); - var consumedObjectIds = new List(); + var conversionResults = new HashSet(); + var createdObjectIds = new HashSet(); + var consumedObjectIds = new HashSet(); foreach (var (layerCollection, instanceOrDefinition) in sortedInstanceComponents) { onOperationProgressed.Report(new("Converting blocks", (double)++count / sortedInstanceComponents.Count)); @@ -70,10 +70,10 @@ public class RhinoInstanceBaker : IInstanceBaker> if (instanceOrDefinition is InstanceDefinitionProxy definitionProxy) { var currentApplicationObjectsIds = definitionProxy - .objects.Select(x => applicationIdMap.TryGetValue(x, out List? value) ? value : null) + .objects.Select(x => applicationIdMap.TryGetValue(x, out IReadOnlyCollection? value) ? value : null) .Where(x => x is not null) .SelectMany(id => id.NotNull()) - .ToList(); + .ToHashSet(); var definitionGeometryList = new List(); var attributes = new List(); @@ -81,6 +81,12 @@ public class RhinoInstanceBaker : IInstanceBaker> foreach (var id in currentApplicationObjectsIds) { var docObject = doc.Objects.FindId(new Guid(id)); + // NOTE: we're here being lenient on incomplete block creation. If a block contains unsupported elements that somehow threw/didn't manage to get baked as atomic objects, + // we just continue rather than throw on a null when accessing the docObject's Geometry. + if (docObject is null) + { + continue; + } definitionGeometryList.Add(docObject.Geometry); attributes.Add(docObject.Attributes); } @@ -108,8 +114,8 @@ public class RhinoInstanceBaker : IInstanceBaker> // Rhino deletes original objects on block creation - we should do the same. doc.Objects.Delete(currentApplicationObjectsIds.Select(stringId => new Guid(stringId)), false); - consumedObjectIds.AddRange(currentApplicationObjectsIds); - createdObjectIds.RemoveAll(id => consumedObjectIds.Contains(id)); // in case we've consumed some existing instances + consumedObjectIds.UnionWith(currentApplicationObjectsIds); + createdObjectIds.RemoveWhere(id => consumedObjectIds.Contains(id)); // in case we've consumed some existing instances } if ( @@ -119,7 +125,7 @@ public class RhinoInstanceBaker : IInstanceBaker> { var transform = MatrixToTransform(instanceProxy.transform, instanceProxy.units); int layerIndex = _layerBaker.GetLayerIndex(layerCollection, baseLayerName); - string instanceProxyId = instanceProxy.applicationId ?? instanceProxy.id; + string instanceProxyId = instanceProxy.applicationId ?? instanceProxy.id.NotNull(); ObjectAttributes atts = new() { LayerIndex = layerIndex }; if (_materialBaker.ObjectIdAndMaterialIndexMap.TryGetValue(instanceProxyId, out int mIndex)) @@ -153,7 +159,6 @@ public class RhinoInstanceBaker : IInstanceBaker> } } - await Task.Yield(); return new(createdObjectIds, consumedObjectIds, conversionResults); } diff --git a/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/RhinoLayerBaker.cs b/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/RhinoLayerBaker.cs index 4814cd531..4bb1fb24b 100644 --- a/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/RhinoLayerBaker.cs +++ b/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/RhinoLayerBaker.cs @@ -2,6 +2,7 @@ using Rhino; using Rhino.DocObjects; using Speckle.Connectors.Common.Operations.Receive; using Speckle.Sdk; +using Speckle.Sdk.Common; using Speckle.Sdk.Models.Collections; using Layer = Rhino.DocObjects.Layer; @@ -82,7 +83,7 @@ public class RhinoLayerBaker : TraversalContextUnpacker return existingLayerIndex; } - throw new SpeckleException("Did not find a layer in the cache."); + throw new SpeckleException($"Did not find a layer in the cache with the name {layerFullName}"); } /// @@ -98,7 +99,8 @@ public class RhinoLayerBaker : TraversalContextUnpacker Layer? previousLayer = currentDocument.Layers.FindName(currentLayerName); foreach (Collection collection in collectionPath) { - currentLayerName += s_pathSeparator + collection.name; + currentLayerName += s_pathSeparator + (string.IsNullOrWhiteSpace(collection.name) ? "unnamed" : collection.name); + currentLayerName = currentLayerName.Replace("{", "").Replace("}", ""); // Rhino specific cleanup for gh (see RemoveInvalidRhinoChars) if (_hostLayerCache.TryGetValue(currentLayerName, out int value)) { @@ -112,7 +114,7 @@ public class RhinoLayerBaker : TraversalContextUnpacker // set material if ( _materialBaker.ObjectIdAndMaterialIndexMap.TryGetValue( - collection.applicationId ?? collection.id, + collection.applicationId ?? collection.id.NotNull(), out int mIndex ) ) @@ -123,7 +125,7 @@ public class RhinoLayerBaker : TraversalContextUnpacker // set color if ( _colorBaker.ObjectColorsIdMap.TryGetValue( - collection.applicationId ?? collection.id, + collection.applicationId ?? collection.id.NotNull(), out (Color, ObjectColorSource) color ) ) @@ -132,6 +134,10 @@ public class RhinoLayerBaker : TraversalContextUnpacker } int index = currentDocument.Layers.Add(newLayer); + if (index == -1) + { + throw new SpeckleException($"Could not create layer {currentLayerName}."); + } _hostLayerCache.Add(currentLayerName, index); previousLayer = currentDocument.Layers.FindIndex(index); // note we need to get the correct id out, hence why we're double calling this } diff --git a/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/RhinoLayerUnpacker.cs b/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/RhinoLayerUnpacker.cs index cf8e05d2e..ea465669d 100644 --- a/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/RhinoLayerUnpacker.cs +++ b/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/RhinoLayerUnpacker.cs @@ -1,4 +1,5 @@ using Rhino; +using Speckle.Sdk; using Speckle.Sdk.Models.Collections; using Layer = Rhino.DocObjects.Layer; using SpeckleLayer = Speckle.Sdk.Models.Collections.Layer; @@ -23,6 +24,35 @@ public class RhinoLayerUnpacker #endif private static readonly string[] s_pathSeparatorSplit = [s_pathSeparator]; + /// + /// Use this method to get all of the layers that correspond to collection created in the root collection. + /// + /// + /// Throws when a layer could not be retrieved from a stored collection application id + public IEnumerable GetUsedLayers() + { + var currentDoc = RhinoDoc.ActiveDoc; // POC: too much right now to interface around + + foreach (string layerId in _layerCollectionCache.Values.Select(o => o.applicationId ?? string.Empty).ToList()) + { + if (Guid.TryParse(layerId, out Guid layerGuid)) + { + if (currentDoc.Layers.FindId(layerGuid) is Layer layer) + { + yield return layer; + } + else + { + throw new SpeckleException($"Could not retrieve layer with guid: {layerId}."); + } + } + else + { + throw new SpeckleException($"Invalid Collection Layer id: {layerId}. Should be convertible to a Guid."); + } + } + } + /// /// Use this method to construct the root commit object while converting objects. /// Returns the host collection corresponding to the provided layer. If it's the first time that it is being asked for, it will be created and stored in the root object collection. diff --git a/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/RhinoMaterialBaker.cs b/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/RhinoMaterialBaker.cs index b550c4592..aff089088 100644 --- a/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/RhinoMaterialBaker.cs +++ b/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/RhinoMaterialBaker.cs @@ -1,9 +1,10 @@ -using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging; using Rhino; using Speckle.Converters.Common; using Speckle.Converters.Rhino; using Speckle.Objects.Other; using Speckle.Sdk; +using Speckle.Sdk.Common; using Speckle.Sdk.Common.Exceptions; using Material = Rhino.DocObjects.Material; @@ -28,7 +29,7 @@ public class RhinoMaterialBaker /// public Dictionary ObjectIdAndMaterialIndexMap { get; } = new(); - public void BakeMaterials(List speckleRenderMaterialProxies, string baseLayerName) + public void BakeMaterials(IReadOnlyCollection speckleRenderMaterialProxies, string baseLayerName) { var doc = _converterSettings.Current.Document; // POC: too much right now to interface around // List conversionResults = new(); // TODO: return this guy @@ -40,7 +41,7 @@ public class RhinoMaterialBaker try { // POC: Currently we're relying on the render material name for identification if it's coming from speckle and from which model; could we do something else? - string materialId = speckleRenderMaterial.applicationId ?? speckleRenderMaterial.id; + string materialId = speckleRenderMaterial.applicationId ?? speckleRenderMaterial.id.NotNull(); string matName = $"{speckleRenderMaterial.name}-({materialId})-{baseLayerName}"; matName = matName.Replace("[", "").Replace("]", ""); // "Material" doesn't like square brackets if we create from here. Once they created from Rhino UI, all good.. Color diffuse = Color.FromArgb(speckleRenderMaterial.diffuse); diff --git a/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/RhinoMaterialUnpacker.cs b/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/RhinoMaterialUnpacker.cs index d4b16bb5f..f3a49464b 100644 --- a/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/RhinoMaterialUnpacker.cs +++ b/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/RhinoMaterialUnpacker.cs @@ -15,137 +15,162 @@ public class RhinoMaterialUnpacker { private readonly ILogger _logger; + /// + /// For send operations + /// + private Dictionary RenderMaterialProxies { get; } = new(); + public RhinoMaterialUnpacker(ILogger logger) { _logger = logger; } - public List UnpackRenderMaterial(List atomicObjects) + /// + /// Processes an object's material and adds the object id to a material proxy in if object color is set ByObject or ByParent. + /// + /// + private void ProcessObjectMaterial( + string objId, + RenderMaterial? renderMaterial, + Material? material, + ObjectMaterialSource source + ) { - Dictionary renderMaterialProxies = new(); - Dictionary usedLayerMap = new(); - - // Stage 1: unpack materials from objects, and collect their uniqe layers in the process - foreach (RhinoObject rhinoObject in atomicObjects) + switch (source) { + case ObjectMaterialSource.MaterialFromObject: + AddObjectIdToRenderMaterialProxy(objId, renderMaterial, material); + break; + + // POC: skip if object material source is *not* by object. we don't support render material inheritance atm bc alex disagrees with the concept + default: + break; + } + } + + private void AddObjectIdToRenderMaterialProxy(string objectId, RenderMaterial? renderMaterial, Material? material) + { + // NOTE: material ids are not the same, even if the underlying material is. the number of materials in the mat table corresponds + // with the number of objects, and each material will get a new id, even if it is THE SAME. shockingly, TY bob, material names + // are unique so we use those for identity checks rather than the material's id. + string? renderMaterialId = renderMaterial?.Name ?? material?.Name; //renderMaterial?.Id.ToString() ?? material?.Id.ToString(); + + if (renderMaterialId is not null) + { + if (RenderMaterialProxies.TryGetValue(renderMaterialId, out RenderMaterialProxy? proxy)) + { + proxy.objects.Add(objectId); + } + else + { + if ( + ConvertMaterialToRenderMaterialProxy(renderMaterialId, renderMaterial, material) + is RenderMaterialProxy newRenderMaterial + ) + { + newRenderMaterial.objects.Add(objectId); + RenderMaterialProxies[renderMaterialId] = newRenderMaterial; + } + } + } + } + + private RenderMaterialProxy? ConvertMaterialToRenderMaterialProxy( + string materialId, + RenderMaterial? renderMaterial, + Material? material + ) + { + // TY Rhino api for being a bit confused about materials 💖 + SpeckleRenderMaterial? myMaterial = null; + if (renderMaterial is not null) + { + myMaterial = ConvertRenderMaterialToSpeckle(renderMaterial); + } + else if (material is not null) + { + RenderMaterial convertedRender = ConvertMaterialToRenderMaterial(material); + myMaterial = ConvertRenderMaterialToSpeckle(convertedRender); + } + + if (myMaterial is null) + { + return null; + } + + RenderMaterialProxy renderMaterialProxy = + new() + { + value = myMaterial, + applicationId = materialId, + objects = new() + }; + + // POC: we are not attaching source information here, since we do not support material inheritance + return renderMaterialProxy; + } + + /// + /// Iterates through a given set of rhino objects and layers to collect render materials. + /// + /// atomic root objects, including instance objects + /// the layers corresponding to collections on the root collection + /// + public List UnpackRenderMaterials(List atomicObjects, List layers) + { + var currentDoc = RhinoDoc.ActiveDoc; // POC: too much right now to interface around + + // Stage 1: unpack materials from objects + foreach (RhinoObject rootObj in atomicObjects) + { + if (rootObj.Attributes.MaterialSource == ObjectMaterialSource.MaterialFromLayer) + { + continue; + } + + // materials are confusing in rhino. we need both render material and material because objects can have either assigned + RenderMaterial? rhinoRenderMaterial = rootObj.GetRenderMaterial(true); + Material? rhinoMaterial = rootObj.GetMaterial(true); + try { - var layer = RhinoDoc.ActiveDoc.Layers[rhinoObject.Attributes.LayerIndex]; - usedLayerMap[layer.Id.ToString()] = layer; - - if (rhinoObject.Attributes.MaterialSource != ObjectMaterialSource.MaterialFromObject) - { - continue; // TODO: will not catch layer materials - } - - var rhinoRenderMaterial = rhinoObject.GetRenderMaterial(true); - var rhinoMaterial = rhinoObject.GetMaterial(true); - var rhinoMaterialId = rhinoRenderMaterial?.Id.ToString() ?? rhinoMaterial?.Id.ToString(); - - if (rhinoMaterialId == null) - { - continue; - } - - if (renderMaterialProxies.TryGetValue(rhinoMaterialId, out RenderMaterialProxy? value)) - { - value.objects.Add(rhinoObject.Id.ToString()); - } - else - { - // TY Rhino api for being a bit confused about materials 💖 - SpeckleRenderMaterial? myMaterial = null; - if (rhinoRenderMaterial is not null) - { - myMaterial = ConvertRenderMaterialToSpeckle(rhinoRenderMaterial); - } - else if (rhinoMaterial is not null) - { - RenderMaterial convertedRender = ConvertMaterialToRenderMaterial(rhinoMaterial); - myMaterial = ConvertRenderMaterialToSpeckle(convertedRender); - } - - if (myMaterial is not null) - { - renderMaterialProxies[rhinoMaterialId] = new RenderMaterialProxy() - { - value = myMaterial, - objects = [rhinoObject.Id.ToString()] - }; - } - } + ProcessObjectMaterial( + rootObj.Id.ToString(), + rhinoRenderMaterial, + rhinoMaterial, + rootObj.Attributes.MaterialSource + ); } catch (Exception ex) when (!ex.IsFatal()) { - _logger.LogError(ex, "Failed to unpack render material from RhinoObject"); + _logger.LogError(ex, "Failed to unpack material from Rhino Object"); } } // Stage 2: make sure we collect layer materials as well - foreach (var layer in usedLayerMap.Values) + foreach (Layer layer in layers) { + // materials are confusing in rhino. we will first try to get layer render material and then material by index if null + RenderMaterial? rhinoRenderMaterial = layer.RenderMaterial; + Material? rhinoMaterial = + layer.RenderMaterialIndex == -1 ? null : currentDoc.Materials[layer.RenderMaterialIndex]; + try { - // 1. Try to create from RenderMaterial - var renderMaterial = layer.RenderMaterial; - if (renderMaterial is not null) - { - if (renderMaterialProxies.TryGetValue(renderMaterial.Id.ToString(), out RenderMaterialProxy? value)) - { - value.objects.Add(layer.Id.ToString()); - } - else - { - renderMaterialProxies[renderMaterial.Id.ToString()] = new RenderMaterialProxy() - { - value = ConvertRenderMaterialToSpeckle(renderMaterial), - objects = [layer.Id.ToString()] - }; - } - - continue; - } - - // 2. As fallback, try to create from index - // ON RECEIVE: when creating a layer on receive we cannot set RenderMaterial to layer. (RhinoCommon API limitation, it tested) - // We can only set render material index of layer. So for the second send, we also need to check its index! - var renderMaterialIndex = layer.RenderMaterialIndex; - if (renderMaterialIndex == -1) - { - continue; - } - - var renderMaterialFromIndex = RhinoDoc.ActiveDoc.Materials[renderMaterialIndex]; - if (renderMaterialFromIndex is null) - { - continue; - } - - if ( - renderMaterialProxies.TryGetValue( - renderMaterialFromIndex.Id.ToString(), - out RenderMaterialProxy? renderMaterialProxy - ) - ) - { - renderMaterialProxy.objects.Add(layer.Id.ToString()); - } - else - { - renderMaterialProxies[renderMaterialFromIndex.Id.ToString()] = new RenderMaterialProxy() - { - value = ConvertRenderMaterialToSpeckle(ConvertMaterialToRenderMaterial(renderMaterialFromIndex)), - objects = [layer.Id.ToString()] - }; - } + ProcessObjectMaterial( + layer.Id.ToString(), + rhinoRenderMaterial, + rhinoMaterial, + ObjectMaterialSource.MaterialFromObject + ); } catch (Exception ex) when (!ex.IsFatal()) { - _logger.LogError(ex, "Failed to unpack render material from Rhino Layer"); + _logger.LogError(ex, "Failed to unpack materials from Rhino Layer"); } } - return renderMaterialProxies.Values.ToList(); + return RenderMaterialProxies.Values.ToList(); } // converts a rhino material to a rhino render material diff --git a/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/SpeckleRhinoPanelHost.cs b/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/SpeckleRhinoPanelHost.cs index 02654613c..00803ed6e 100644 --- a/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/SpeckleRhinoPanelHost.cs +++ b/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/SpeckleRhinoPanelHost.cs @@ -22,6 +22,33 @@ public class SpeckleRhinoPanelHost : RhinoWindows.Controls.WpfElementHost Panels.Closed += PanelsOnClosed; } + /// + /// This is a lot like PanelsOnClosed but called when trying to show the panel to clear out a lingering parent as PanelsOnClosed isn't called + /// + /// + public static void Reinitialize(DUI3ControlWebView? webView) + { + if (webView == null) + { + return; + } + // This check comes from behavioral difference on closing Rhino Panels. + // IsPanelVisible returns; + // - True, when docked Panel closed from the list on right click on panel tab, + // whenever it is closed with this way, Rhino.Panels tries to reinit this object and expect the different UIElement, that's why we disconnect Child. + // - False, when detached Panel is closed by 'X' close button. + // whenever it is closed with this way, Rhino.Panels don't create this object, that's why we do not disconnect Child UIElement. + if (Panels.IsPanelVisible(typeof(SpeckleRhinoPanelHost).GUID)) + { + return; + } + // Disconnect UIElement from WpfElementHost. Otherwise, we can't reinit panel with same DUI3ControlWebView + if (LogicalTreeHelper.GetParent(webView) is Border border) + { + border.Child = null; + } + } + private void PanelsOnClosed(object? sender, PanelEventArgs e) { if (e.PanelId == typeof(SpeckleRhinoPanelHost).GUID) diff --git a/Connectors/Rhino/Speckle.Connectors.RhinoShared/Operations/Receive/RhinoHostObjectBuilder.cs b/Connectors/Rhino/Speckle.Connectors.RhinoShared/Operations/Receive/RhinoHostObjectBuilder.cs index 939051fea..6a203371f 100644 --- a/Connectors/Rhino/Speckle.Connectors.RhinoShared/Operations/Receive/RhinoHostObjectBuilder.cs +++ b/Connectors/Rhino/Speckle.Connectors.RhinoShared/Operations/Receive/RhinoHostObjectBuilder.cs @@ -3,12 +3,15 @@ using Rhino.DocObjects; using Rhino.Geometry; using Speckle.Connectors.Common.Builders; using Speckle.Connectors.Common.Conversion; +using Speckle.Connectors.Common.Extensions; using Speckle.Connectors.Common.Operations; using Speckle.Connectors.Common.Operations.Receive; +using Speckle.Connectors.Common.Threading; using Speckle.Connectors.Rhino.HostApp; using Speckle.Converters.Common; using Speckle.Converters.Rhino; using Speckle.Sdk; +using Speckle.Sdk.Common; using Speckle.Sdk.Logging; using Speckle.Sdk.Models; using Speckle.Sdk.Models.Collections; @@ -30,6 +33,7 @@ public class RhinoHostObjectBuilder : IHostObjectBuilder private readonly RhinoGroupBaker _groupBaker; private readonly RootObjectUnpacker _rootObjectUnpacker; private readonly ISdkActivityFactory _activityFactory; + private readonly IThreadContext _threadContext; public RhinoHostObjectBuilder( IRootToHostConverter converter, @@ -40,7 +44,8 @@ public class RhinoHostObjectBuilder : IHostObjectBuilder RhinoMaterialBaker materialBaker, RhinoColorBaker colorBaker, RhinoGroupBaker groupBaker, - ISdkActivityFactory activityFactory + ISdkActivityFactory activityFactory, + IThreadContext threadContext ) { _converter = converter; @@ -52,10 +57,11 @@ public class RhinoHostObjectBuilder : IHostObjectBuilder _layerBaker = layerBaker; _groupBaker = groupBaker; _activityFactory = activityFactory; + _threadContext = threadContext; } #pragma warning disable CA1506 - public async Task Build( + public Task Build( #pragma warning restore CA1506 Base rootObject, string projectName, @@ -72,14 +78,15 @@ public class RhinoHostObjectBuilder : IHostObjectBuilder PreReceiveDeepClean(baseLayerName); // 1 - Unpack objects and proxies from root commit object - var unpackedRoot = _rootObjectUnpacker.Unpack(rootObject); // 2 - Split atomic objects and instance components with their path - var (atomicObjects, instanceComponents) = _rootObjectUnpacker.SplitAtomicObjectsAndInstances( - unpackedRoot.ObjectsToConvert + var (atomicObjectsWithoutInstanceComponentsForConverter, instanceComponents) = + _rootObjectUnpacker.SplitAtomicObjectsAndInstances(unpackedRoot.ObjectsToConvert); + + var atomicObjectsWithoutInstanceComponentsWithPath = _layerBaker.GetAtomicObjectsWithPath( + atomicObjectsWithoutInstanceComponentsForConverter ); - var atomicObjectsWithPath = _layerBaker.GetAtomicObjectsWithPath(atomicObjects); var instanceComponentsWithPath = _layerBaker.GetInstanceComponentsWithPath(instanceComponents); // 2.1 - these are not captured by traversal, so we need to re-add them here @@ -96,7 +103,10 @@ public class RhinoHostObjectBuilder : IHostObjectBuilder if (unpackedRoot.RenderMaterialProxies != null) { using var _ = _activityFactory.Start("Render Materials"); - _materialBaker.BakeMaterials(unpackedRoot.RenderMaterialProxies, baseLayerName); + _threadContext.RunOnMain(() => + { + _materialBaker.BakeMaterials(unpackedRoot.RenderMaterialProxies, baseLayerName); + }); } if (unpackedRoot.ColorProxies != null) @@ -109,26 +119,34 @@ public class RhinoHostObjectBuilder : IHostObjectBuilder onOperationProgressed.Report(new("Baking layers (redraw disabled)", null)); using (var _ = _activityFactory.Start("Pre baking layers")) { - RhinoApp.InvokeAndWait(() => - { - using var layerNoDraw = new DisableRedrawScope(_converterSettings.Current.Document.Views); - _layerBaker.CreateAllLayersForReceive(atomicObjectsWithPath.Select(t => t.path), baseLayerName); - }); + //Rhino 8 doesn't play nice with Eto and layers + _threadContext + .RunOnMain(() => + { + using var layerNoDraw = new DisableRedrawScope(_converterSettings.Current.Document.Views); + var paths = atomicObjectsWithoutInstanceComponentsWithPath.Select(t => t.path).ToList(); + paths.AddRange(instanceComponentsWithPath.Select(t => t.path)); + _layerBaker.CreateAllLayersForReceive(paths, baseLayerName); + }) + .Wait(cancellationToken); } // 5 - Convert atomic objects - List bakedObjectIds = new(); - Dictionary> applicationIdMap = new(); // This map is used in converting blocks in stage 2. keeps track of original app id => resulting new app ids post baking - List conversionResults = new(); + var bakedObjectIds = new HashSet(); + Dictionary> applicationIdMap = new(); // This map is used in converting blocks in stage 2. keeps track of original app id => resulting new app ids post baking + HashSet conversionResults = new(); int count = 0; using (var _ = _activityFactory.Start("Converting objects")) { - foreach (var (path, obj) in atomicObjectsWithPath) + foreach (var (path, obj) in atomicObjectsWithoutInstanceComponentsWithPath) { using (var convertActivity = _activityFactory.Start("Converting object")) { - onOperationProgressed.Report(new("Converting objects", (double)++count / atomicObjects.Count)); + onOperationProgressed.Report( + new("Converting objects", (double)++count / atomicObjectsWithoutInstanceComponentsForConverter.Count) + ); + cancellationToken.ThrowIfCancellationRequested(); try { // 0: get pre-created layer from cache in layer baker @@ -145,7 +163,7 @@ public class RhinoHostObjectBuilder : IHostObjectBuilder var conversionIds = new List(); if (result is GeometryBase geometryBase) { - var guid = BakeObject(geometryBase, obj, atts); + var guid = BakeObject(geometryBase, obj, null, atts); conversionIds.Add(guid.ToString()); } else if (result is List geometryBases) // one to many raw encoding case @@ -156,11 +174,11 @@ public class RhinoHostObjectBuilder : IHostObjectBuilder // EXTRA EXTRA NOTE: TY Ogu, i am no longer than unhappy about it. It's legit "mess". foreach (var gb in geometryBases) { - var guid = BakeObject(gb, obj, atts); + var guid = BakeObject(gb, obj, null, atts); conversionIds.Add(guid.ToString()); } } - else if (result is IEnumerable<(object, Base)> fallbackConversionResult) // one to many fallback conversion + else if (result is List<(GeometryBase, Base)> fallbackConversionResult) // one to many fallback conversion { var guids = BakeObjectsAsFallbackGroup(fallbackConversionResult, obj, atts, baseLayerName); conversionIds.AddRange(guids.Select(id => id.ToString())); @@ -168,6 +186,7 @@ public class RhinoHostObjectBuilder : IHostObjectBuilder if (conversionIds.Count == 0) { + // TODO: add this condition to report object - same as in autocad throw new SpeckleException($"Failed to convert object."); } @@ -186,7 +205,7 @@ public class RhinoHostObjectBuilder : IHostObjectBuilder } // 5: populate app id map - applicationIdMap[obj.applicationId ?? obj.id] = conversionIds; + applicationIdMap[obj.applicationId ?? obj.id.NotNull()] = conversionIds; convertActivity?.SetStatus(SdkActivityStatusCode.Ok); } catch (Exception ex) when (!ex.IsFatal()) @@ -202,14 +221,17 @@ public class RhinoHostObjectBuilder : IHostObjectBuilder // 6 - Convert instances using (var _ = _activityFactory.Start("Converting instances")) { - var (createdInstanceIds, consumedObjectIds, instanceConversionResults) = await _instanceBaker - .BakeInstances(instanceComponentsWithPath, applicationIdMap, baseLayerName, onOperationProgressed) - .ConfigureAwait(false); + var (createdInstanceIds, consumedObjectIds, instanceConversionResults) = _instanceBaker.BakeInstances( + instanceComponentsWithPath, + applicationIdMap, + baseLayerName, + onOperationProgressed + ); - bakedObjectIds.RemoveAll(id => consumedObjectIds.Contains(id)); // remove all objects that have been "consumed" - bakedObjectIds.AddRange(createdInstanceIds); // add instance ids - conversionResults.RemoveAll(result => result.ResultId != null && consumedObjectIds.Contains(result.ResultId)); // remove all conversion results for atomic objects that have been consumed (POC: not that cool, but prevents problems on object highlighting) - conversionResults.AddRange(instanceConversionResults); // add instance conversion results to our list + bakedObjectIds.RemoveWhere(id => consumedObjectIds.Contains(id)); // remove all objects that have been "consumed" + bakedObjectIds.UnionWith(createdInstanceIds); // add instance ids + conversionResults.RemoveWhere(result => result.ResultId != null && consumedObjectIds.Contains(result.ResultId)); // remove all conversion results for atomic objects that have been consumed (POC: not that cool, but prevents problems on object highlighting) + conversionResults.UnionWith(instanceConversionResults); // add instance conversion results to our list } // 7 - Create groups @@ -219,7 +241,7 @@ public class RhinoHostObjectBuilder : IHostObjectBuilder } _converterSettings.Current.Document.Views.Redraw(); - return new HostObjectBuilderResult(bakedObjectIds, conversionResults); + return Task.FromResult(new HostObjectBuilderResult(bakedObjectIds, conversionResults)); } private void PreReceiveDeepClean(string baseLayerName) @@ -231,35 +253,38 @@ public class RhinoHostObjectBuilder : IHostObjectBuilder RhinoMath.UnsetIntIndex ); - RhinoApp.InvokeAndWait(() => - { - _instanceBaker.PurgeInstances(baseLayerName); - _materialBaker.PurgeMaterials(baseLayerName); - - var doc = _converterSettings.Current.Document; - // Cleans up any previously received objects - if (rootLayerIndex != RhinoMath.UnsetIntIndex) + //Rhino 8 doesn't play nice with Eto and layers + _threadContext + .RunOnMain(() => { - var documentLayer = doc.Layers[rootLayerIndex]; - var childLayers = documentLayer.GetChildren(); - if (childLayers != null) + _instanceBaker.PurgeInstances(baseLayerName); + _materialBaker.PurgeMaterials(baseLayerName); + + var doc = _converterSettings.Current.Document; + // Cleans up any previously received objects + if (rootLayerIndex != RhinoMath.UnsetIntIndex) { - using var layerNoDraw = new DisableRedrawScope(doc.Views); - foreach (var layer in childLayers) + var documentLayer = doc.Layers[rootLayerIndex]; + var childLayers = documentLayer.GetChildren(); + if (childLayers != null) { - var purgeSuccess = doc.Layers.Purge(layer.Index, true); - if (!purgeSuccess) + using var layerNoDraw = new DisableRedrawScope(doc.Views); + foreach (var layer in childLayers) { - Console.WriteLine($"Failed to purge layer: {layer}"); + var purgeSuccess = doc.Layers.Purge(layer.Index, true); + if (!purgeSuccess) + { + Console.WriteLine($"Failed to purge layer: {layer}"); + } } } + doc.Layers.Purge(documentLayer.Index, true); } - doc.Layers.Purge(documentLayer.Index, true); - } - // Cleans up any previously received group - _groupBaker.PurgeGroups(baseLayerName); - }); + // Cleans up any previously received group + _groupBaker.PurgeGroups(baseLayerName); + }) + .Wait(); } /// @@ -267,59 +292,78 @@ public class RhinoHostObjectBuilder : IHostObjectBuilder /// /// /// + /// Parent object ID for color and material proxies search (if fallback conversion was used) /// /// /// /// Material and Color attributes are processed here due to those properties existing sometimes on fallback geometry (instead of parent). - /// and this method is called by + /// and this method is called by /// - private Guid BakeObject(GeometryBase obj, Base originalObject, ObjectAttributes atts) + private Guid BakeObject(GeometryBase obj, Base originalObject, string? parentObjectId, ObjectAttributes atts) { - var objectId = originalObject.applicationId ?? originalObject.id; + var objectId = originalObject.applicationId ?? originalObject.id.NotNull(); if (_materialBaker.ObjectIdAndMaterialIndexMap.TryGetValue(objectId, out int mIndex)) { atts.MaterialIndex = mIndex; atts.MaterialSource = ObjectMaterialSource.MaterialFromObject; } + else if ( + parentObjectId is not null + && (_materialBaker.ObjectIdAndMaterialIndexMap.TryGetValue(parentObjectId, out int mIndexSpeckleObj)) + ) + { + atts.MaterialIndex = mIndexSpeckleObj; + atts.MaterialSource = ObjectMaterialSource.MaterialFromObject; + } if (_colorBaker.ObjectColorsIdMap.TryGetValue(objectId, out (Color, ObjectColorSource) color)) { atts.ObjectColor = color.Item1; atts.ColorSource = color.Item2; } + else if ( + parentObjectId is not null + && (_colorBaker.ObjectColorsIdMap.TryGetValue(parentObjectId, out (Color, ObjectColorSource) colorSpeckleObj)) + ) + { + atts.ObjectColor = colorSpeckleObj.Item1; + atts.ColorSource = colorSpeckleObj.Item2; + } return _converterSettings.Current.Document.Objects.Add(obj, atts); } private List BakeObjectsAsFallbackGroup( - IEnumerable<(object, Base)> fallbackConversionResult, + IEnumerable<(GeometryBase, Base)> fallbackConversionResult, Base originatingObject, ObjectAttributes atts, string baseLayerName ) { List objectIds = new(); + string parentId = originatingObject.applicationId ?? originatingObject.id.NotNull(); + int objCount = 0; foreach (var (conversionResult, originalBaseObject) in fallbackConversionResult) { - if (conversionResult is not GeometryBase geometryBase) - { - // TODO: throw? - continue; - } - - var id = BakeObject(geometryBase, originalBaseObject, atts); + var id = BakeObject(conversionResult, originalBaseObject, parentId, atts); objectIds.Add(id); + objCount++; } - var groupIndex = _converterSettings.Current.Document.Groups.Add( - $@"{originatingObject.speckle_type.Split('.').Last()} - {originatingObject.applicationId ?? originatingObject.id} ({baseLayerName})", - objectIds - ); + // only create groups if we really need to, ie if the fallback conversion result count is bigger than one. + if (objCount > 1) + { + var groupIndex = _converterSettings.Current.Document.Groups.Add( + $@"{originatingObject.speckle_type.Split('.').Last()} - {parentId} ({baseLayerName})", + objectIds + ); - var group = _converterSettings.Current.Document.Groups.FindIndex(groupIndex); + var group = _converterSettings.Current.Document.Groups.FindIndex(groupIndex); + + objectIds.Insert(0, group.Id); + } - objectIds.Insert(0, group.Id); return objectIds; } } diff --git a/Connectors/Rhino/Speckle.Connectors.RhinoShared/Operations/Send/RhinoRootObjectBuilder.cs b/Connectors/Rhino/Speckle.Connectors.RhinoShared/Operations/Send/RhinoRootObjectBuilder.cs index 5f337e491..979da1525 100644 --- a/Connectors/Rhino/Speckle.Connectors.RhinoShared/Operations/Send/RhinoRootObjectBuilder.cs +++ b/Connectors/Rhino/Speckle.Connectors.RhinoShared/Operations/Send/RhinoRootObjectBuilder.cs @@ -8,6 +8,7 @@ using Speckle.Connectors.Common.Instances; using Speckle.Connectors.Common.Operations; using Speckle.Connectors.DUI.Models.Card.SendFilter; using Speckle.Connectors.Rhino.HostApp; +using Speckle.Connectors.Rhino.HostApp.Properties; using Speckle.Converters.Common; using Speckle.Converters.Rhino; using Speckle.Sdk; @@ -32,6 +33,7 @@ public class RhinoRootObjectBuilder : IRootObjectBuilder private readonly RhinoGroupUnpacker _groupUnpacker; private readonly RhinoMaterialUnpacker _materialUnpacker; private readonly RhinoColorUnpacker _colorUnpacker; + private readonly PropertiesExtractor _propertiesExtractor; private readonly ILogger _logger; private readonly ISdkActivityFactory _activityFactory; @@ -44,6 +46,7 @@ public class RhinoRootObjectBuilder : IRootObjectBuilder RhinoGroupUnpacker groupUnpacker, RhinoMaterialUnpacker materialUnpacker, RhinoColorUnpacker colorUnpacker, + PropertiesExtractor propertiesExtractor, ILogger logger, ISdkActivityFactory activityFactory ) @@ -56,6 +59,7 @@ public class RhinoRootObjectBuilder : IRootObjectBuilder _rootToSpeckleConverter = rootToSpeckleConverter; _materialUnpacker = materialUnpacker; _colorUnpacker = colorUnpacker; + _propertiesExtractor = propertiesExtractor; _logger = logger; _activityFactory = activityFactory; } @@ -64,7 +68,7 @@ public class RhinoRootObjectBuilder : IRootObjectBuilder IReadOnlyList rhinoObjects, SendInfo sendInfo, IProgress onOperationProgressed, - CancellationToken cancellationToken = default + CancellationToken cancellationToken ) { using var activity = _activityFactory.Start("Build"); @@ -89,18 +93,17 @@ public class RhinoRootObjectBuilder : IRootObjectBuilder // 3 - Convert atomic objects List results = new(atomicObjects.Count); - HashSet versionLayers = new(); int count = 0; using (var _ = _activityFactory.Start("Convert all")) { foreach (RhinoObject rhinoObject in atomicObjects) { - using var _2 = _activityFactory.Start("Convert"); cancellationToken.ThrowIfCancellationRequested(); + using var _2 = _activityFactory.Start("Convert"); - // handle layer + // handle layer and store object layer *and all layer parents* to the version layers + // this is important because we need to unpack colors and materials on intermediate layers that do not have objects as well. Layer layer = _converterSettings.Current.Document.Layers[rhinoObject.Attributes.LayerIndex]; - versionLayers.Add(layer); Collection collectionHost = _layerUnpacker.GetHostObjectCollection(layer, rootObjectCollection); var result = ConvertRhinoObject(rhinoObject, collectionHost, instanceProxies, sendInfo.ProjectId); @@ -108,6 +111,7 @@ public class RhinoRootObjectBuilder : IRootObjectBuilder ++count; onOperationProgressed.Report(new("Converting", (double)count / atomicObjects.Count)); + await Task.Yield(); // NOTE: useful for testing ui states, pls keep for now so we can easily uncomment // Thread.Sleep(550); @@ -119,18 +123,20 @@ public class RhinoRootObjectBuilder : IRootObjectBuilder throw new SpeckleException("Failed to convert all objects."); // fail fast instead creating empty commit! It will appear as model card error with red color. } + // Get all layers from the created collections on the root object commit for proxy processing + List layers = _layerUnpacker.GetUsedLayers().ToList(); + using (var _ = _activityFactory.Start("UnpackRenderMaterials")) { // 4 - Unpack the render material proxies - rootObjectCollection[ProxyKeys.RENDER_MATERIAL] = _materialUnpacker.UnpackRenderMaterial(atomicObjects); + rootObjectCollection[ProxyKeys.RENDER_MATERIAL] = _materialUnpacker.UnpackRenderMaterials(atomicObjects, layers); } using (var _ = _activityFactory.Start("UnpackColors")) { // 5 - Unpack the color proxies - rootObjectCollection[ProxyKeys.COLOR] = _colorUnpacker.UnpackColors(atomicObjects, versionLayers.ToList()); + rootObjectCollection[ProxyKeys.COLOR] = _colorUnpacker.UnpackColors(atomicObjects, layers); } - await Task.Yield(); return new RootObjectBuilderResult(rootObjectCollection, results); } @@ -163,6 +169,19 @@ public class RhinoRootObjectBuilder : IRootObjectBuilder converted.applicationId = applicationId; } + // add name and properties + // POC: this is NOT done in the converter because we don't have a RootToSpeckle converter that captures all top level converters + if (!string.IsNullOrEmpty(rhinoObject.Attributes.Name)) + { + converted["name"] = rhinoObject.Attributes.Name; + } + + var properties = _propertiesExtractor.GetProperties(rhinoObject); + if (properties.Count > 0) + { + converted["properties"] = properties; + } + // add to host collectionHost.elements.Add(converted); diff --git a/Connectors/Rhino/Speckle.Connectors.RhinoShared/Plugin/RhinoPlugin.cs b/Connectors/Rhino/Speckle.Connectors.RhinoShared/Plugin/RhinoPlugin.cs deleted file mode 100644 index 3a060c253..000000000 --- a/Connectors/Rhino/Speckle.Connectors.RhinoShared/Plugin/RhinoPlugin.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Rhino; -using Speckle.Connectors.DUI.Bridge; -using Speckle.Connectors.Rhino.Plugin; -using Speckle.InterfaceGenerator; - -namespace Speckle.Connectors.Rhino.DependencyInjection; - -[GenerateAutoInterface] -public class RhinoPlugin : IRhinoPlugin -{ - private readonly IAppIdleManager _idleManager; - - public RhinoPlugin(IAppIdleManager idleManager) - { - _idleManager = idleManager; - } - - public void Initialise() => - _idleManager.SubscribeToIdle( - nameof(RhinoPlugin), - () => RhinoApp.RunScript(SpeckleConnectorsRhinoCommand.Instance.EnglishName, false) - ); - - public void Shutdown() { } -} diff --git a/Connectors/Rhino/Speckle.Connectors.RhinoShared/Plugin/Speckle.Connectors.RhinoCommand.cs b/Connectors/Rhino/Speckle.Connectors.RhinoShared/Plugin/Speckle.Connectors.RhinoCommand.cs index 029029529..3305bb036 100644 --- a/Connectors/Rhino/Speckle.Connectors.RhinoShared/Plugin/Speckle.Connectors.RhinoCommand.cs +++ b/Connectors/Rhino/Speckle.Connectors.RhinoShared/Plugin/Speckle.Connectors.RhinoCommand.cs @@ -5,8 +5,10 @@ using Rhino.Commands; using Rhino.Input.Custom; using Rhino.UI; using Speckle.Connectors.Rhino.HostApp; - -// using Speckle.Connectorss.Rhino.Properties; +#if RHINO8_OR_GREATER +using Microsoft.Extensions.DependencyInjection; +using Speckle.Connectors.DUI.WebView; +#endif namespace Speckle.Connectors.Rhino.Plugin; @@ -21,7 +23,7 @@ public class SpeckleConnectorsRhinoCommand : Command Panels.RegisterPanel( SpeckleConnectorsRhinoPlugin.Instance, typeof(SpeckleRhinoPanelHost), - "Speckle (Beta) for Rhino", + "Speckle (Beta)", new Icon(iconPath), PanelType.System ); @@ -65,6 +67,11 @@ public class SpeckleConnectorsRhinoCommand : Command if (mode == RunMode.Interactive) { +#if RHINO8_OR_GREATER + SpeckleRhinoPanelHost.Reinitialize( + SpeckleConnectorsRhinoPlugin.Instance.Container?.GetRequiredService() + ); +#endif Panels.OpenPanel(panelId); return Result.Success; } diff --git a/Connectors/Rhino/Speckle.Connectors.RhinoShared/Plugin/Speckle.Connectors.RhinoPlugin.cs b/Connectors/Rhino/Speckle.Connectors.RhinoShared/Plugin/Speckle.Connectors.RhinoPlugin.cs index a172c693e..e8eaef441 100644 --- a/Connectors/Rhino/Speckle.Connectors.RhinoShared/Plugin/Speckle.Connectors.RhinoPlugin.cs +++ b/Connectors/Rhino/Speckle.Connectors.RhinoShared/Plugin/Speckle.Connectors.RhinoPlugin.cs @@ -20,10 +20,9 @@ namespace Speckle.Connectors.Rhino.Plugin; /// public class SpeckleConnectorsRhinoPlugin : PlugIn { - private IRhinoPlugin? _rhinoPlugin; private IDisposable? _disposableLogger; - protected override string LocalPlugInName => "Speckle (Beta) for Rhino"; + protected override string LocalPlugInName => "Speckle (Beta)"; public ServiceProvider? Container { get; private set; } public SpeckleConnectorsRhinoPlugin() @@ -54,10 +53,6 @@ public class SpeckleConnectorsRhinoPlugin : PlugIn Container = services.BuildServiceProvider(); Container.UseDUI(); - // Resolve root plugin object and initialise. - _rhinoPlugin = Container.GetRequiredService(); - _rhinoPlugin.Initialise(); - return LoadReturnCode.Success; } catch (Exception e) when (!e.IsFatal()) @@ -80,7 +75,6 @@ public class SpeckleConnectorsRhinoPlugin : PlugIn protected override void OnShutdown() { - _rhinoPlugin?.Shutdown(); _disposableLogger?.Dispose(); Container?.Dispose(); base.OnShutdown(); diff --git a/Connectors/Rhino/Speckle.Connectors.RhinoShared/Registration/ServiceRegistration.cs b/Connectors/Rhino/Speckle.Connectors.RhinoShared/Registration/ServiceRegistration.cs index daf21e89f..da179e6d1 100644 --- a/Connectors/Rhino/Speckle.Connectors.RhinoShared/Registration/ServiceRegistration.cs +++ b/Connectors/Rhino/Speckle.Connectors.RhinoShared/Registration/ServiceRegistration.cs @@ -5,18 +5,18 @@ using Rhino.PlugIns; using Speckle.Connectors.Common; using Speckle.Connectors.Common.Builders; using Speckle.Connectors.Common.Caching; -using Speckle.Connectors.Common.Cancellation; using Speckle.Connectors.Common.Instances; using Speckle.Connectors.Common.Operations; +using Speckle.Connectors.Common.Threading; using Speckle.Connectors.DUI; using Speckle.Connectors.DUI.Bindings; using Speckle.Connectors.DUI.Bridge; -using Speckle.Connectors.DUI.Models; using Speckle.Connectors.DUI.Models.Card.SendFilter; using Speckle.Connectors.DUI.WebView; using Speckle.Connectors.Rhino.Bindings; using Speckle.Connectors.Rhino.Filters; using Speckle.Connectors.Rhino.HostApp; +using Speckle.Connectors.Rhino.HostApp.Properties; using Speckle.Connectors.Rhino.Operations.Receive; using Speckle.Connectors.Rhino.Operations.Send; using Speckle.Connectors.Rhino.Plugin; @@ -33,24 +33,14 @@ public static class ServiceRegistration serviceCollection.AddSingleton(SpeckleConnectorsRhinoCommand.Instance); serviceCollection.AddConnectorUtils(); - serviceCollection.AddDUI(); + serviceCollection.AddDUI(); serviceCollection.AddDUIView(); - // POC: Overwriting the SyncToMainThread to SyncToCurrentThread for Rhino! - // builder.AddSingletonInstance(); - - // Register other connector specific types - serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(); - // Register bindings serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); // POC: Easier like this for now, should be cleaned up later serviceCollection.AddSingleton(); - serviceCollection.RegisterTopLevelExceptionHandler(); - serviceCollection.AddSingleton(sp => sp.GetRequiredService()); serviceCollection.AddSingleton(); @@ -58,15 +48,13 @@ public static class ServiceRegistration serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); - // binding dependencies - serviceCollection.AddTransient(); - // register send filters serviceCollection.AddScoped(); serviceCollection.AddScoped(); // register send conversion cache serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); // register send operation and dependencies serviceCollection.AddScoped>(); @@ -94,6 +82,8 @@ public static class ServiceRegistration serviceCollection.AddScoped(); serviceCollection.AddScoped(); + serviceCollection.AddScoped(); + // operation progress manager serviceCollection.AddSingleton(); } diff --git a/Connectors/Rhino/Speckle.Connectors.RhinoShared/RhinoEvents.cs b/Connectors/Rhino/Speckle.Connectors.RhinoShared/RhinoEvents.cs new file mode 100644 index 000000000..7f6121ac9 --- /dev/null +++ b/Connectors/Rhino/Speckle.Connectors.RhinoShared/RhinoEvents.cs @@ -0,0 +1,87 @@ +// using Rhino; +// using Rhino.Commands; +// using Rhino.DocObjects; +// using Rhino.DocObjects.Tables; +// using Speckle.Connectors.Common.Threading; +// using Speckle.Connectors.DUI.Bridge; +// using Speckle.Connectors.DUI.Eventing; +// +// namespace Speckle.Connectors.RhinoShared; +// +// public class BeginOpenDocument(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler) +// : ThreadedEvent(threadContext, exceptionHandler); +// +// public class EndOpenDocument(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler) +// : ThreadedEvent(threadContext, exceptionHandler); +// +// public class SelectObjects(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler) +// : ThreadedEvent(threadContext, exceptionHandler); +// +// public class DeselectObjects(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler) +// : ThreadedEvent(threadContext, exceptionHandler); +// +// public class DeselectAllObjects(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler) +// : ThreadedEvent(threadContext, exceptionHandler); +// +// public class ActiveDocumentChanged(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler) +// : ThreadedEvent(threadContext, exceptionHandler); +// +// public class DocumentPropertiesChanged(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler) +// : ThreadedEvent(threadContext, exceptionHandler); +// +// public class AddRhinoObject(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler) +// : ThreadedEvent(threadContext, exceptionHandler); +// +// public class DeleteRhinoObject(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler) +// : ThreadedEvent(threadContext, exceptionHandler); +// +// public class RenderMaterialsTableEvent(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler) +// : ThreadedEvent(threadContext, exceptionHandler); +// +// public class MaterialTableEvent(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler) +// : ThreadedEvent(threadContext, exceptionHandler); +// +// public class ModifyObjectAttributes(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler) +// : ThreadedEvent(threadContext, exceptionHandler); +// +// public class ReplaceRhinoObject(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler) +// : ThreadedEvent(threadContext, exceptionHandler); +// +// public class GroupTableEvent(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler) +// : ThreadedEvent(threadContext, exceptionHandler); +// +// public class LayerTableEvent(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler) +// : ThreadedEvent(threadContext, exceptionHandler); +// +// public class BeginCommandEvent(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler) +// : ThreadedEvent(threadContext, exceptionHandler); +// +// public static class RhinoEvents +// { +// public static void Register(IEventAggregator eventAggregator) +// { +// RhinoApp.Idle += async (_, e) => await eventAggregator.GetEvent().PublishAsync(e); +// +// RhinoDoc.BeginOpenDocument += async (_, e) => await eventAggregator.GetEvent().PublishAsync(e); +// RhinoDoc.EndOpenDocument += async (_, e) => await eventAggregator.GetEvent().PublishAsync(e); +// RhinoDoc.SelectObjects += async (_, e) => await eventAggregator.GetEvent().PublishAsync(e); +// RhinoDoc.DeselectObjects += async (_, e) => await eventAggregator.GetEvent().PublishAsync(e); +// RhinoDoc.DeselectAllObjects += async (_, e) => await eventAggregator.GetEvent().PublishAsync(e); +// RhinoDoc.ActiveDocumentChanged += async (_, e) => +// await eventAggregator.GetEvent().PublishAsync(e); +// RhinoDoc.DocumentPropertiesChanged += async (_, e) => +// await eventAggregator.GetEvent().PublishAsync(e); +// RhinoDoc.AddRhinoObject += async (_, e) => await eventAggregator.GetEvent().PublishAsync(e); +// RhinoDoc.DeleteRhinoObject += async (_, e) => await eventAggregator.GetEvent().PublishAsync(e); +// RhinoDoc.RenderMaterialsTableEvent += async (_, e) => +// await eventAggregator.GetEvent().PublishAsync(e); +// RhinoDoc.MaterialTableEvent += async (_, e) => await eventAggregator.GetEvent().PublishAsync(e); +// RhinoDoc.ModifyObjectAttributes += async (_, e) => +// await eventAggregator.GetEvent().PublishAsync(e); +// RhinoDoc.ReplaceRhinoObject += async (_, e) => await eventAggregator.GetEvent().PublishAsync(e); +// RhinoDoc.GroupTableEvent += async (_, e) => await eventAggregator.GetEvent().PublishAsync(e); +// RhinoDoc.LayerTableEvent += async (_, e) => await eventAggregator.GetEvent().PublishAsync(e); +// +// Command.BeginCommand += async (_, e) => await eventAggregator.GetEvent().PublishAsync(e); +// } +// } diff --git a/Connectors/Rhino/Speckle.Connectors.RhinoShared/Speckle.Connectors.RhinoShared.projitems b/Connectors/Rhino/Speckle.Connectors.RhinoShared/Speckle.Connectors.RhinoShared.projitems index 42d4d5842..b1769682c 100644 --- a/Connectors/Rhino/Speckle.Connectors.RhinoShared/Speckle.Connectors.RhinoShared.projitems +++ b/Connectors/Rhino/Speckle.Connectors.RhinoShared/Speckle.Connectors.RhinoShared.projitems @@ -21,6 +21,9 @@ + + + @@ -32,7 +35,6 @@ - @@ -43,7 +45,6 @@ - diff --git a/Connectors/Rhino/Toolbars/Speckle.Connectors.Rhino.rui b/Connectors/Rhino/Toolbars/Speckle.Connectors.Rhino.rui new file mode 100644 index 000000000..10eb32fc2 --- /dev/null +++ b/Connectors/Rhino/Toolbars/Speckle.Connectors.Rhino.rui @@ -0,0 +1,147 @@ + + + + + + + + Speckle (Beta) + + + + Toolbar 00 + + a0d74d7e-bd20-499e-9984-fca521394029 + + + + + + + + Speckle (Beta) + + + + Toolbar item + + 5937e6d4-d2f5-4831-abdb-2c54f5903731 + + + + 7c8cd3c6-87fb-4047-9c5a-cb38defaa5c1 + + + + + + + Macro + + + The Next Gen Speckle Connector (Beta) for Rhino + + + + + + Speckle (Beta) + + + + + + + + + Macro 00 + + + Opens the Speckle Community Help Page in your browser + + + + + + Get Help + + + + + + + + + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAwCAYAAAAYX/pXAAAAAXNSR0IArs4c6QAAAARnQU1BAACx +jwv8YQUAAAAJcEhZcwAAEnQAABJ0Ad5mH3gAAAH8SURBVEhL7ZRfSFNRHMdHZWaPZShUMA2yFwlf +esgHR2kvQRT5EiTYe/RU2BztzmihCGNEZUJORKIeKgz6M9jCIqG/tJwPmqUZsXLadtcM2r3bPZ+4 +d2yQuw3q+X7gy/n3Ox/OOQ/HZltF1yPsRkI4usJ0OkN4zgY1vyukDTvD2nh3KPfJFQa9xtjQ1BxB +n/ifOMMcNgRmktNBOPlA0HFHcOiG4MCIoGVIo+lqjnpfjs0X8ykK7O0LrHNn/zmmgmpvCiXWURzz +dYiqnnxfXTxFXd9iecG+gUlWJlup9X5n8P5dQObh40E2ehR+Rts5MvC0vEDPekmlsX8WId8CUvBj +jJoLy1R5Mn/UlQiWnh0k96EbEfODfBPEFyAOxGDlHmLpOnzuQx7fby6o642TmvOhqS9BTAEzwCzw +HpgGLUp6vp89vhlzgZ4Kd5Zvbzvh123IBUEL5VtljPTU0fJXqPQoVEoKm3pSZD+2IRLHEfIJRPIY +2flW2vwTxrpeVyGpJgIp/0iXLp9BfbGTTKSB9JsGlOgu1Nf1BEfzJyjUlQgKiV/bSjJQzXSg0Ri/ +CjSTHN1CYqS2/BWKgis17PU+N/rKO4fR7jg/x/LwthKBx0xQyAYpQyKwHUfvRMmaIdDZ3RKx/01g +GldGXntOXVjjzjxZ9RtYWFhYWFhYWNh+AwX3pT9YtYnUAAAAAElFTkSuQmCC + + + + + + iVBORw0KGgoAAAANSUhEUgAAABgAAABICAYAAADs+TUPAAAAAXNSR0IArs4c6QAAAARnQU1BAACx +jwv8YQUAAAAJcEhZcwAAEnQAABJ0Ad5mH3gAAAgSSURBVFhH7ZZ7VBTXAca37ooaU9PTxppoTGJz +TNMF01CPsOsjqFRtBV8FKbC7idI2ttZ4NEFQcB+IVOQomig+MGrNqW9hwSiogBgVEAwKAvLaJSQI +KoIYxN2Z2Zn79cwsrOzsIrb5l++c32Fn7r3fb+buzFkkkoEMZCADGchABjKQgTwz0dl4k2dtDpkU +nYPp0blYwrMuB4a157iEmBwuNTaHO7gul8tfl8uWxOSw38bksK08/DpH0fxVjSWfpFkrYy6wTd0T +qNhc4MfA96zNIYGrC8kwifeUmwj4eyM+TWdcJv4oLnD3YnNJjCDgmbPU7Dqpm8hzwIqzBMtOE2jS +CIKOEcw/TDD7EIHffg4+e1h472LhtYPFr5JZ/OJfdobF2eAQ8MjDGjEmicXITSxGxLN4cQMLD70N +Mt3/j5PgzeBGlwk8yp11GB5ndRyH7v4af0m94DgeaqAxbVedy7p+BcMMVqw+lQW6WYN5qdcc57+9 +uR2PKuKELRAuIMUE5t5KxH9lxIgNT55f4LOjBrfK/glLbSA+M34ODz2NoH1VIB1ZwA95WHfsMgbr +bNAbM0CZl6KudDlm7Cx/fsHwOAvmpBShIO9vyM5eidEJrSgr3Q2wjQDbhMbK7Ri3uQ3pWTqU5y9F +QEohfhr3P9xBDx56BnN3Xsb8vRUgHccAtAN4CPyQgfD91zBvRz6GGSiXdX0KhupphO7Jxcf/yUf0 +yUIYMouQfK4ARRWZgK0GQJsdrh636jKw5+IVJGUVQJtRjMjjhVDtyXHciVsBP2i6FAprQxK4jq+A +rjzAWgCwtwB8D+AOgGb7X64aYEoBaxHI4zxQ3+1C+yV/jE540LeAZ1zifZSXJOLJg3RwTDHAlQGo +AsDfQW0v+OPbAHcL1vYMNF3/BD7JNc/eoh5+nXQXh899gc6mbeAsGYAtH+CuAqSwmwKAuwyOOgNL +SzLO5G6GT3J1/98Bz2AdIzB20wPEHj2FJw0rgMfbAPowwJwCmJP2z13bwTQtw77MrRiX2OxU3qeA +f2qGGGg7eho/j3sES7EPuOaFIO1qkI4ldh6Ggbu7ANSNSZi1/So8dE/XeRjovgX8lQ/RU8KLxR9/ +/FkqLHlvg739O9hMvmBMSjBmBdgGH7A13rBcGY+tX+i6L85+UT1r3QqEiTr7BJ7MlBB0ZbwGa85Y +dF54HS3Z7+Du+XfQefF1WPPHouvMGNQdeQ8v6O2PJr+Wv8hnCnp4Y8N3uL9nDB7uG4m2AyNRfWAC +Zmy+Au9N5Sg5MAXtX/4SHV+ORPuhVyBPePr09NCvQLHxGlp3jULJ5z7Qbt2InxkeIWL/GcQfO4CX +DJ1YsSUFV3dPQ9vBVxCYlO2yvl/BKMM9fJj4b3jH33RswfHjkajLXiTs9RAdBc/4KqiTjmL0hhaX +9bzA8CyBmKF6Cnf2j8eD/WMxPfGqy7gY4Yffe/JN9W+n3DA9j+C1uGZs2RYtMGtTrvP4epqTaWmL +bD39RKqjW2U6ukUQ+Pnly7ynlvqPDWpslWlpSqqlW6U6plG6nq4apKWKBsVSl2RaJlOmY05IY6kU +AZ1tk1RrM8j0zHIeqY5dLNXagqTrbYFSLRUg1Vuny/TMVEGAmrCXuVr11q6Tc/Po0pA8y8VFWaQ+ +fIbjf5vuQC8ZhNOvvoATcg/x2DNDGtUqW3UY15U2F7YbIaAu/wlcg6aJlKmHkywfOYxeC7k0+XHO +6NXCGeUMlyZ/wqXLr5N0z/UkzXMS9k4cLO50CjFrIkitCk4CU3gXVx1yhCsNqOQLYfSCO7h0uZmk +eS2DRPITca8jgqBeDWvuArAVoWBKFgMNGjtFs11KxXBpXoQYPTXiXkd4gaOwN7eDgexJwOn3XErF +cGnyLnLyN57ibiF9Cr72AzImADfnARcULqViOKNXvLhbCKlWRaBGBSduhwKZ7wLFc+yy2lDgUrew +p/TsRJHA8wggGSTul9DRyggmWgkntEqgNND1rurDgfIFQIE/kDcFuBEIZNlFnNEzAwffGCrul9Br +lBFMlBK9sSW8by80a3Dv0gIQcy+JWYOO4iCQ8oWASQVc634Q0iecQL6fTNzvXrBlulDWkj8fH70s +xZ3c+Q5BZ+libJn9KuqM3dtXFWy/gwyvfeJuIW4FO/yFxY+uByP5j2Pw8FqQQ2CpCMXJVV64d3nh +0+8n3RMwehrE3ULcCdjUWY7CrrI/O29RgwbWyrCn5+rCBAHJfHemuFuIW8GhPzgVsrVqtBhD0HAw +GEylymmM3yIuTX4L6ONtdidgDs5u5kwa0lPCX62lVIXOwnCQeue74UoDWojRc7K41xF3AtvGKXFc +g6aAL6DKVWg7H+pEV3G4vbw+/C65EfCBuNMp7gR0lCKcNKr8ObOqlX8sK5MW4co/5gl8E7MAXUV2 +AcwaA/R615erd9wJbJFTfw/oB5H6D2ZwJvV5tk5tYSrVoCvUsNWpGJjV35B6jQZYLBX3ucSt4FPl +zMcrfUfxkFT/tzlTeL1jz03qdlKyWN0zLszR+4wQ9zrCRik+FAvEkOshji+VVIXDtm2m0zgV6Wuj +I5Vx369WDBP3S6yRk2fTkb4PxaW94U7PA0xqu6AgGLb4913mUFFKCxWlDBb3S8hqxRgq0rdAvMAJ +wzSwh+aAPToXtqTpruM9rFHoxP0S/gWhohR7XSY/J21/He/4TEcpEsX9Qu4v93uRjlKcpaJ8KXFB +fzyIeKt/AR9ET3zJukb5Eb1GcYpZoyhjonwr+sMa5VvRuuStXucUq3p3/hevS6SebTfArQAAAABJ +RU5ErkJggg== + + + + + + iVBORw0KGgoAAAANSUhEUgAAACAAAABgCAYAAAB8InCYAAAAAXNSR0IArs4c6QAAAARnQU1BAACx +jwv8YQUAAAAJcEhZcwAAEnQAABJ0Ad5mH3gAAAAiSURBVGhD7cEBAQAAAIIg/69uSEABAAAAAAAA +AAAAAADvBjBgAAExh6+sAAAAAElFTkSuQmCCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + + + + \ No newline at end of file diff --git a/Connectors/Tekla/Directory.Build.targets b/Connectors/Tekla/Directory.Build.targets new file mode 100644 index 000000000..bd36be4fa --- /dev/null +++ b/Connectors/Tekla/Directory.Build.targets @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + $(ProgramData)\Trimble\Tekla Structures\$(TeklaVersion).0 + C:\TeklaStructures\$(TeklaVersion).0 + + + $(ProgramDataTeklaPath)\Environments\common\system\Ribbons\CustomTabs\Modeling + $(DirectTeklaPath)\Environments\common\system\Ribbons\CustomTabs\Modeling + + $(ProgramDataTeklaPath)\Environments\common\extensions\Speckle3TeklaStructures + $(DirectTeklaPath)\Environments\common\extensions\Speckle3TeklaStructures + + + + + + + + $(ProgramDataTeklaPath) + $(DirectTeklaPath) + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Connectors/Tekla/Speckle.Connector.Tekla2023/Speckle.Connector.Tekla2023.csproj b/Connectors/Tekla/Speckle.Connector.Tekla2023/Speckle.Connector.Tekla2023.csproj index 2739dcef3..41b6f1c03 100644 --- a/Connectors/Tekla/Speckle.Connector.Tekla2023/Speckle.Connector.Tekla2023.csproj +++ b/Connectors/Tekla/Speckle.Connector.Tekla2023/Speckle.Connector.Tekla2023.csproj @@ -6,38 +6,14 @@ 2023 true $(DefineConstants);TEKLA2023 + Debug;Release;Local - - - - - - - - - - - - - - - - - - - - - - + + + + + @@ -45,10 +21,6 @@ - - - - diff --git a/Connectors/Tekla/Speckle.Connector.Tekla2023/packages.lock.json b/Connectors/Tekla/Speckle.Connector.Tekla2023/packages.lock.json index f81abcdfd..b425f0a03 100644 --- a/Connectors/Tekla/Speckle.Connector.Tekla2023/packages.lock.json +++ b/Connectors/Tekla/Speckle.Connector.Tekla2023/packages.lock.json @@ -33,15 +33,6 @@ "resolved": "0.9.6", "contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w==" }, - "Speckle.Objects": { - "type": "Direct", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "+m7jRFm0ABbkcSz2UphdxAsislY10IpQ1u79c8a3aVvegLjnsVQZ1sXfRIRO1aFdulkhjYKXYpB3N9M8Z+epgQ==", - "dependencies": { - "Speckle.Sdk": "3.1.0-dev.191" - } - }, "Tekla.Structures.Dialog": { "type": "Direct", "requested": "[2023.0.1, )", @@ -207,11 +198,6 @@ "resolved": "8.0.0", "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" }, - "Speckle.DoubleNumerics": { - "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w==" - }, "Speckle.Newtonsoft.Json": { "type": "Transitive", "resolved": "13.0.2", @@ -322,14 +308,21 @@ "resolved": "2023.0.1", "contentHash": "24KbR++C2WYEIyDymIHX/AVuHYcgwyQrvgExMKDnMobQPNM+AmMNnwdyYxBbw4AvYXd8QHoi+lmmavhTCL8E3w==" }, + "speckle.common.meshtriangulation": { + "type": "Project", + "dependencies": { + "LibTessDotNet": "[1.1.15, )", + "Speckle.DoubleNumerics": "[4.1.0, )" + } + }, "speckle.connectors.common": { "type": "Project", "dependencies": { "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", "Speckle.Connectors.Logging": "[1.0.0, )", - "Speckle.Objects": "[3.1.0-dev.191, )", - "Speckle.Sdk": "[3.1.0-dev.191, )", - "Speckle.Sdk.Dependencies": "[3.1.0-dev.191, )" + "Speckle.Objects": "[3.1.1, )", + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" } }, "speckle.connectors.dui": { @@ -337,9 +330,8 @@ "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", "Speckle.Connectors.Common": "[1.0.0, )", - "Speckle.Sdk": "[3.1.0-dev.191, )", - "Speckle.Sdk.Dependencies": "[3.1.0-dev.191, )", - "System.Threading.Tasks.Dataflow": "[6.0.0, )" + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" } }, "speckle.connectors.dui.webview": { @@ -355,6 +347,7 @@ "speckle.converter.tekla2023": { "type": "Project", "dependencies": { + "Speckle.Common.MeshTriangulation": "[1.0.0, )", "Speckle.Converters.Common": "[1.0.0, )", "Tekla.Structures.Drawing": "[2023.0.1, )", "Tekla.Structures.Model": "[2023.0.1, )" @@ -364,9 +357,15 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.191, )" + "Speckle.Objects": "[3.1.1, )" } }, + "LibTessDotNet": { + "type": "CentralTransitive", + "requested": "[1.1.15, )", + "resolved": "1.1.15", + "contentHash": "KuA7N3Nv/lIeawJdQBQJR6oqWD9KETHLbWzBqapwFs+Tby+R5I4crkKujKMm5bXcSuFZ8LNtflFQVadsWCbBjg==" + }, "Microsoft.Extensions.DependencyInjection": { "type": "CentralTransitive", "requested": "[2.2.0, )", @@ -406,11 +405,26 @@ "resolved": "1.0.1938.49", "contentHash": "z8KnFnaTYzhA/ZnyRX0qGfS1NU5ZBJeClAH64F0fVDvdDJTvME7xl6zTJ0Jlfe1BtL3C0NH9xTy64shg2baKdw==" }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, + "Speckle.Objects": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", + "dependencies": { + "Speckle.Sdk": "3.1.1" + } + }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "VVT3LJiYlhqnggxxdeTt1QLrqfxDb044x0yX6kxS9b5MRzeDvK2Vz86pLDfuHs+SXvDimRVfYx1M42IW/aPcTQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.Bcl.AsyncInterfaces": "5.0.0", @@ -418,22 +432,16 @@ "Microsoft.Data.Sqlite": "7.0.5", "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", "Microsoft.Extensions.Logging": "2.2.0", - "Speckle.DoubleNumerics": "4.0.1", + "Speckle.DoubleNumerics": "4.1.0", "Speckle.Newtonsoft.Json": "13.0.2", - "Speckle.Sdk.Dependencies": "3.1.0-dev.191" + "Speckle.Sdk.Dependencies": "3.1.1" } }, "Speckle.Sdk.Dependencies": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "EmEOyjsGsNi56Z/ZoBOn8WirTmIT2yqWvlUeUh0BSPX2TDMZXHTKOM/kHmP6HSd10KVFn2Zo/ItY7/K9iRtL1Q==" - }, - "System.Threading.Tasks.Dataflow": { - "type": "CentralTransitive", - "requested": "[6.0.0, )", - "resolved": "6.0.0", - "contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA==" + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" } } } diff --git a/Connectors/Tekla/Speckle.Connector.Tekla2024/Speckle.Connector.Tekla2024.csproj b/Connectors/Tekla/Speckle.Connector.Tekla2024/Speckle.Connector.Tekla2024.csproj index b391f3023..749aeb473 100644 --- a/Connectors/Tekla/Speckle.Connector.Tekla2024/Speckle.Connector.Tekla2024.csproj +++ b/Connectors/Tekla/Speckle.Connector.Tekla2024/Speckle.Connector.Tekla2024.csproj @@ -6,33 +6,10 @@ 2024 true $(DefineConstants);TEKLA2024 + Debug;Release;Local - - - - - - - - - - - - - - - - - + @@ -44,10 +21,6 @@ - - - - diff --git a/Connectors/Tekla/Speckle.Connector.Tekla2024/packages.lock.json b/Connectors/Tekla/Speckle.Connector.Tekla2024/packages.lock.json index 804b1215b..280626fc0 100644 --- a/Connectors/Tekla/Speckle.Connector.Tekla2024/packages.lock.json +++ b/Connectors/Tekla/Speckle.Connector.Tekla2024/packages.lock.json @@ -33,15 +33,6 @@ "resolved": "0.9.6", "contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w==" }, - "Speckle.Objects": { - "type": "Direct", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "+m7jRFm0ABbkcSz2UphdxAsislY10IpQ1u79c8a3aVvegLjnsVQZ1sXfRIRO1aFdulkhjYKXYpB3N9M8Z+epgQ==", - "dependencies": { - "Speckle.Sdk": "3.1.0-dev.191" - } - }, "Tekla.Structures.Dialog": { "type": "Direct", "requested": "[2024.0.4, )", @@ -226,11 +217,6 @@ "resolved": "0.11.4", "contentHash": "IC1h5g0NeJGHIUgzM1P82ld57knhP0IcQfrYITDPXlNpMYGUrsG5TxuaWTjaeqDNQMBDNZkB8L0rBnwsY6JHuQ==" }, - "Speckle.DoubleNumerics": { - "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w==" - }, "Speckle.Newtonsoft.Json": { "type": "Transitive", "resolved": "13.0.2", @@ -403,14 +389,21 @@ "resolved": "2.2.22312", "contentHash": "OMwJAhps/tnmV99suab65b7Ex+H9mebbdNRaHwogPEwCRefWUr7/WaA4p43nfb+FxmBFO5FtudN3EEarggTSow==" }, + "speckle.common.meshtriangulation": { + "type": "Project", + "dependencies": { + "LibTessDotNet": "[1.1.15, )", + "Speckle.DoubleNumerics": "[4.1.0, )" + } + }, "speckle.connectors.common": { "type": "Project", "dependencies": { "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", "Speckle.Connectors.Logging": "[1.0.0, )", - "Speckle.Objects": "[3.1.0-dev.191, )", - "Speckle.Sdk": "[3.1.0-dev.191, )", - "Speckle.Sdk.Dependencies": "[3.1.0-dev.191, )" + "Speckle.Objects": "[3.1.1, )", + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" } }, "speckle.connectors.dui": { @@ -418,9 +411,8 @@ "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", "Speckle.Connectors.Common": "[1.0.0, )", - "Speckle.Sdk": "[3.1.0-dev.191, )", - "Speckle.Sdk.Dependencies": "[3.1.0-dev.191, )", - "System.Threading.Tasks.Dataflow": "[6.0.0, )" + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" } }, "speckle.connectors.dui.webview": { @@ -436,6 +428,7 @@ "speckle.converter.tekla2024": { "type": "Project", "dependencies": { + "Speckle.Common.MeshTriangulation": "[1.0.0, )", "Speckle.Converters.Common": "[1.0.0, )", "Tekla.Structures.Drawing": "[2024.0.4, )", "Tekla.Structures.Model": "[2024.0.4, )" @@ -445,9 +438,15 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.191, )" + "Speckle.Objects": "[3.1.1, )" } }, + "LibTessDotNet": { + "type": "CentralTransitive", + "requested": "[1.1.15, )", + "resolved": "1.1.15", + "contentHash": "KuA7N3Nv/lIeawJdQBQJR6oqWD9KETHLbWzBqapwFs+Tby+R5I4crkKujKMm5bXcSuFZ8LNtflFQVadsWCbBjg==" + }, "Microsoft.Extensions.DependencyInjection": { "type": "CentralTransitive", "requested": "[2.2.0, )", @@ -487,11 +486,26 @@ "resolved": "1.0.1938.49", "contentHash": "z8KnFnaTYzhA/ZnyRX0qGfS1NU5ZBJeClAH64F0fVDvdDJTvME7xl6zTJ0Jlfe1BtL3C0NH9xTy64shg2baKdw==" }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, + "Speckle.Objects": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", + "dependencies": { + "Speckle.Sdk": "3.1.1" + } + }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "VVT3LJiYlhqnggxxdeTt1QLrqfxDb044x0yX6kxS9b5MRzeDvK2Vz86pLDfuHs+SXvDimRVfYx1M42IW/aPcTQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.Bcl.AsyncInterfaces": "5.0.0", @@ -499,22 +513,16 @@ "Microsoft.Data.Sqlite": "7.0.5", "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", "Microsoft.Extensions.Logging": "2.2.0", - "Speckle.DoubleNumerics": "4.0.1", + "Speckle.DoubleNumerics": "4.1.0", "Speckle.Newtonsoft.Json": "13.0.2", - "Speckle.Sdk.Dependencies": "3.1.0-dev.191" + "Speckle.Sdk.Dependencies": "3.1.1" } }, "Speckle.Sdk.Dependencies": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "EmEOyjsGsNi56Z/ZoBOn8WirTmIT2yqWvlUeUh0BSPX2TDMZXHTKOM/kHmP6HSd10KVFn2Zo/ItY7/K9iRtL1Q==" - }, - "System.Threading.Tasks.Dataflow": { - "type": "CentralTransitive", - "requested": "[6.0.0, )", - "resolved": "6.0.0", - "contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA==" + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" } } } diff --git a/Connectors/Tekla/Speckle.Connector.TeklaShared/Bindings/TeklaBasicConnectorBinding.cs b/Connectors/Tekla/Speckle.Connector.TeklaShared/Bindings/TeklaBasicConnectorBinding.cs index e20d5e886..ec82522b6 100644 --- a/Connectors/Tekla/Speckle.Connector.TeklaShared/Bindings/TeklaBasicConnectorBinding.cs +++ b/Connectors/Tekla/Speckle.Connector.TeklaShared/Bindings/TeklaBasicConnectorBinding.cs @@ -8,7 +8,7 @@ using Speckle.Sdk; using Tekla.Structures; using Tekla.Structures.Geometry3d; -namespace Speckle.Connector.Tekla2024.Bindings; +namespace Speckle.Connectors.TeklaShared.Bindings; public class TeklaBasicConnectorBinding : IBasicConnectorBinding { @@ -19,13 +19,15 @@ public class TeklaBasicConnectorBinding : IBasicConnectorBinding public IBrowserBridge Parent { get; } private readonly ILogger _logger; private readonly TSM.Model _model; + private readonly ITopLevelExceptionHandler _topLevelExceptionHandler; public TeklaBasicConnectorBinding( IBrowserBridge parent, ISpeckleApplication speckleApplication, DocumentModelStore store, ILogger logger, - TSM.Model model + TSM.Model model, + ITopLevelExceptionHandler topLevelExceptionHandler ) { _speckleApplication = speckleApplication; @@ -33,11 +35,12 @@ public class TeklaBasicConnectorBinding : IBasicConnectorBinding Parent = parent; _logger = logger; _model = model; + _topLevelExceptionHandler = topLevelExceptionHandler; Commands = new BasicConnectorBindingCommands(parent); _store.DocumentChanged += (_, _) => - parent.TopLevelExceptionHandler.FireAndForget(async () => + _topLevelExceptionHandler.FireAndForget(async () => { - await Commands.NotifyDocumentChanged().ConfigureAwait(false); + await Commands.NotifyDocumentChanged(); }); } @@ -51,7 +54,7 @@ public class TeklaBasicConnectorBinding : IBasicConnectorBinding public DocumentModelStore GetDocumentState() => _store; - public void AddModel(ModelCard model) => _store.Models.Add(model); + public void AddModel(ModelCard model) => _store.AddModel(model); public void UpdateModel(ModelCard model) => _store.UpdateModel(model); @@ -80,18 +83,16 @@ public class TeklaBasicConnectorBinding : IBasicConnectorBinding if (objectIds.Count == 0) { - await Commands - .SetModelError(modelCardId, new OperationCanceledException("No objects found to highlight.")) - .ConfigureAwait(false); + await Commands.SetModelError(modelCardId, new OperationCanceledException("No objects found to highlight.")); return; } - await HighlightObjects(objectIds).ConfigureAwait(false); + await HighlightObjects(objectIds); } catch (InvalidOperationException ex) { _logger.LogError(ex, "Failed to highlight model"); - await Commands.SetModelError(modelCardId, ex).ConfigureAwait(false); + await Commands.SetModelError(modelCardId, ex); } } @@ -100,48 +101,47 @@ public class TeklaBasicConnectorBinding : IBasicConnectorBinding try { await Task.Run(() => + { + // passing an empty list to create current selection + var selector = new TSMUI.ModelObjectSelector(); + selector.Select(new ArrayList()); + + if (objectIds.Count > 0) { - // passing an empty list to create current selection - var selector = new TSMUI.ModelObjectSelector(); - selector.Select(new ArrayList()); + var modelObjects = objectIds + .Select(id => _model.SelectModelObject(new Identifier(new Guid(id)))) + .Where(obj => obj != null) + .ToList(); - if (objectIds.Count > 0) + selector.Select(new ArrayList(modelObjects)); + + // to find the min and max coordinates of the selected objects + // with that we can create a bounding box and zoom selected + var points = new List(); + foreach (var obj in modelObjects) { - var modelObjects = objectIds - .Select(id => _model.SelectModelObject(new Identifier(new Guid(id)))) - .Where(obj => obj != null) - .ToList(); - - selector.Select(new ArrayList(modelObjects)); - - // to find the min and max coordinates of the selected objects - // with that we can create a bounding box and zoom selected - var points = new List(); - foreach (var obj in modelObjects) + points.Add(obj.GetCoordinateSystem().Origin); + foreach (TSM.ModelObject child in obj.GetChildren()) { - points.Add(obj.GetCoordinateSystem().Origin); - foreach (TSM.ModelObject child in obj.GetChildren()) - { - points.Add(child.GetCoordinateSystem().Origin); - } + points.Add(child.GetCoordinateSystem().Origin); } - - var minX = points.Min(p => p.X); - var minY = points.Min(p => p.Y); - var minZ = points.Min(p => p.Z); - var maxX = points.Max(p => p.X); - var maxY = points.Max(p => p.Y); - var maxZ = points.Max(p => p.Z); - - // create the bounding box - var bounds = new AABB { MinPoint = new Point(minX, minY, minZ), MaxPoint = new Point(maxX, maxY, maxZ) }; - - // zoom in to bounding box - TSMUI.ViewHandler.ZoomToBoundingBox(bounds); } - _model.CommitChanges(); - }) - .ConfigureAwait(false); + + var minX = points.Min(p => p.X); + var minY = points.Min(p => p.Y); + var minZ = points.Min(p => p.Z); + var maxX = points.Max(p => p.X); + var maxY = points.Max(p => p.Y); + var maxZ = points.Max(p => p.Z); + + // create the bounding box + var bounds = new AABB { MinPoint = new Point(minX, minY, minZ), MaxPoint = new Point(maxX, maxY, maxZ) }; + + // zoom in to bounding box + TSMUI.ViewHandler.ZoomToBoundingBox(bounds); + } + _model.CommitChanges(); + }); } catch (InvalidOperationException ex) { diff --git a/Connectors/Tekla/Speckle.Connector.TeklaShared/Bindings/TeklaSelectionBinding.cs b/Connectors/Tekla/Speckle.Connector.TeklaShared/Bindings/TeklaSelectionBinding.cs index cd1ca4303..195feb76c 100644 --- a/Connectors/Tekla/Speckle.Connector.TeklaShared/Bindings/TeklaSelectionBinding.cs +++ b/Connectors/Tekla/Speckle.Connector.TeklaShared/Bindings/TeklaSelectionBinding.cs @@ -2,14 +2,14 @@ using Speckle.Connectors.DUI.Bindings; using Speckle.Connectors.DUI.Bridge; using Tekla.Structures.Model; -namespace Speckle.Connector.Tekla2024.Bindings; +namespace Speckle.Connectors.TeklaShared.Bindings; public class TeklaSelectionBinding : ISelectionBinding { - private readonly IAppIdleManager _idleManager; private const string SELECTION_EVENT = "setSelection"; - private readonly Tekla.Structures.Model.Events _events; private readonly object _selectionEventHandlerLock = new object(); + private readonly IAppIdleManager _idleManager; + private readonly Events _events; private readonly Tekla.Structures.Model.UI.ModelObjectSelector _selector; public string Name => "selectionBinding"; @@ -24,20 +24,18 @@ public class TeklaSelectionBinding : ISelectionBinding { _idleManager = idleManager; Parent = parent; - _events = events; _selector = selector; + _events = events; - _events.SelectionChange += Events_SelectionChangeEvent; + _events.SelectionChange += OnSelectionChangeEvent; _events.Register(); - - UpdateSelection(); } - private void Events_SelectionChangeEvent() + private void OnSelectionChangeEvent() { lock (_selectionEventHandlerLock) { - _idleManager.SubscribeToIdle(nameof(TeklaSelectionBinding), UpdateSelection); + _idleManager.SubscribeToIdle(nameof(UpdateSelection), UpdateSelection); UpdateSelection(); } } @@ -45,25 +43,34 @@ public class TeklaSelectionBinding : ISelectionBinding private void UpdateSelection() { SelectionInfo selInfo = GetSelection(); - Parent.Send(SELECTION_EVENT, selInfo); + Parent.Send2(SELECTION_EVENT, selInfo); } public SelectionInfo GetSelection() { + var objectIds = new List(); + var objectTypes = new List(); + ModelObjectEnumerator selectedObjects = _selector.GetSelectedObjects(); - List objectIds = new List(); - List objectTypes = new List(); + if (selectedObjects == null) + { + return new SelectionInfo(new List(), "No objects selected."); + } while (selectedObjects.MoveNext()) { ModelObject modelObject = selectedObjects.Current; + if (modelObject?.Identifier?.GUID == null) + { + continue; // Skip if any part is null + } + string globalId = modelObject.Identifier.GUID.ToString(); objectIds.Add(globalId); objectTypes.Add(modelObject.GetType().Name); } string typesString = string.Join(", ", objectTypes.Distinct()); - return new SelectionInfo( objectIds, objectIds.Count == 0 ? "No objects selected." : $"{objectIds.Count} objects ({typesString})" diff --git a/Connectors/Tekla/Speckle.Connector.TeklaShared/Bindings/TeklaSendBinding.cs b/Connectors/Tekla/Speckle.Connector.TeklaShared/Bindings/TeklaSendBinding.cs index 0300800ca..a0002f2d8 100644 --- a/Connectors/Tekla/Speckle.Connector.TeklaShared/Bindings/TeklaSendBinding.cs +++ b/Connectors/Tekla/Speckle.Connector.TeklaShared/Bindings/TeklaSendBinding.cs @@ -1,7 +1,6 @@ using System.Collections.Concurrent; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -using Speckle.Connector.Tekla2024.Operations.Send.Settings; using Speckle.Connectors.Common.Caching; using Speckle.Connectors.Common.Cancellation; using Speckle.Connectors.Common.Operations; @@ -13,8 +12,9 @@ using Speckle.Connectors.DUI.Models; using Speckle.Connectors.DUI.Models.Card; using Speckle.Connectors.DUI.Models.Card.SendFilter; using Speckle.Connectors.DUI.Settings; -using Speckle.Converter.Tekla2024; +using Speckle.Connectors.TeklaShared.Operations.Send.Settings; using Speckle.Converters.Common; +using Speckle.Converters.TeklaShared; using Speckle.Sdk; using Speckle.Sdk.Common; using Speckle.Sdk.Logging; @@ -22,19 +22,18 @@ using Tekla.Structures; using Tekla.Structures.Model; using Task = System.Threading.Tasks.Task; -namespace Speckle.Connector.Tekla2024.Bindings; +namespace Speckle.Connectors.TeklaShared.Bindings; -public sealed class TeklaSendBinding : ISendBinding, IDisposable +public sealed class TeklaSendBinding : ISendBinding { public string Name => "sendBinding"; public SendBindingUICommands Commands { get; } public IBrowserBridge Parent { get; } private readonly DocumentModelStore _store; - private readonly IAppIdleManager _idleManager; private readonly IServiceProvider _serviceProvider; private readonly List _sendFilters; - private readonly CancellationManager _cancellationManager; + private readonly ICancellationManager _cancellationManager; private readonly ISendConversionCache _sendConversionCache; private readonly IOperationProgressManager _operationProgressManager; private readonly ILogger _logger; @@ -42,18 +41,17 @@ public sealed class TeklaSendBinding : ISendBinding, IDisposable private readonly ISpeckleApplication _speckleApplication; private readonly ISdkActivityFactory _activityFactory; private readonly Model _model; - private readonly Events _events; private readonly ToSpeckleSettingsManager _toSpeckleSettingsManager; + private readonly Events _events; private ConcurrentDictionary ChangedObjectIds { get; set; } = new(); public TeklaSendBinding( DocumentModelStore store, - IAppIdleManager idleManager, IBrowserBridge parent, IEnumerable sendFilters, IServiceProvider serviceProvider, - CancellationManager cancellationManager, + ICancellationManager cancellationManager, ISendConversionCache sendConversionCache, IOperationProgressManager operationProgressManager, ILogger logger, @@ -64,7 +62,6 @@ public sealed class TeklaSendBinding : ISendBinding, IDisposable ) { _store = store; - _idleManager = idleManager; _serviceProvider = serviceProvider; _sendFilters = sendFilters.ToList(); _cancellationManager = cancellationManager; @@ -85,12 +82,12 @@ public sealed class TeklaSendBinding : ISendBinding, IDisposable private void SubscribeToTeklaEvents() { - _events.ModelObjectChanged += ModelHandler_OnChange; + _events.ModelObjectChanged += OnModelObjectChanged; _events.Register(); } // subscribes the all changes in a modelobject - private void ModelHandler_OnChange(List changes) + private void OnModelObjectChanged(List changes) { foreach (var change in changes) { @@ -129,7 +126,7 @@ public sealed class TeklaSendBinding : ISendBinding, IDisposable _teklaConversionSettingsFactory.Create(_model, _toSpeckleSettingsManager.GetSendRebarsAsSolid(modelCard)) ); - CancellationToken cancellationToken = _cancellationManager.InitCancellationTokenSource(modelCardId); + using var cancellationItem = _cancellationManager.GetCancellationItem(modelCardId); List teklaObjects = modelCard .SendFilter.NotNull() @@ -147,15 +144,12 @@ public sealed class TeklaSendBinding : ISendBinding, IDisposable .ServiceProvider.GetRequiredService>() .Execute( teklaObjects, - modelCard.GetSendInfo(_speckleApplication.Slug), - _operationProgressManager.CreateOperationProgressEventHandler(Parent, modelCardId, cancellationToken), - cancellationToken - ) - .ConfigureAwait(false); + modelCard.GetSendInfo(_speckleApplication.ApplicationAndVersion), + _operationProgressManager.CreateOperationProgressEventHandler(Parent, modelCardId, cancellationItem.Token), + cancellationItem.Token + ); - await Commands - .SetModelSendResult(modelCardId, sendResult.RootObjId, sendResult.ConversionResults) - .ConfigureAwait(false); + await Commands.SetModelSendResult(modelCardId, sendResult.RootObjId, sendResult.ConversionResults); } catch (OperationCanceledException) { @@ -164,7 +158,7 @@ public sealed class TeklaSendBinding : ISendBinding, IDisposable catch (Exception ex) when (!ex.IsFatal()) { _logger.LogModelCardHandledError(ex); - await Commands.SetModelError(modelCardId, ex).ConfigureAwait(false); + await Commands.SetModelError(modelCardId, ex); } } @@ -194,19 +188,8 @@ public sealed class TeklaSendBinding : ISendBinding, IDisposable } } - await Commands.SetModelsExpired(expiredSenderIds).ConfigureAwait(false); + await Commands.SetModelsExpired(expiredSenderIds); ChangedObjectIds = new ConcurrentDictionary(); } - - private bool _disposed; - - public void Dispose() - { - if (!_disposed) - { - _events.UnRegister(); - _disposed = true; - } - } } diff --git a/Connectors/Tekla/Speckle.Connector.TeklaShared/Extensions/ModelObjectExtension.cs b/Connectors/Tekla/Speckle.Connector.TeklaShared/Extensions/ModelObjectExtension.cs index 7e0437033..00a0348e6 100644 --- a/Connectors/Tekla/Speckle.Connector.TeklaShared/Extensions/ModelObjectExtension.cs +++ b/Connectors/Tekla/Speckle.Connector.TeklaShared/Extensions/ModelObjectExtension.cs @@ -1,4 +1,4 @@ -namespace Speckle.Connector.Tekla2024.Extensions; +namespace Speckle.Connectors.TeklaShared.Extensions; public static class ModelObjectExtensions { diff --git a/Connectors/Tekla/Speckle.Connector.TeklaShared/Extensions/SpeckleApplicationIdExtensions.cs b/Connectors/Tekla/Speckle.Connector.TeklaShared/Extensions/SpeckleApplicationIdExtensions.cs index f0e465bc2..ad00e5397 100644 --- a/Connectors/Tekla/Speckle.Connector.TeklaShared/Extensions/SpeckleApplicationIdExtensions.cs +++ b/Connectors/Tekla/Speckle.Connector.TeklaShared/Extensions/SpeckleApplicationIdExtensions.cs @@ -1,4 +1,4 @@ -namespace Speckle.Connector.Tekla2024.Extensions; +namespace Speckle.Connectors.TeklaShared.Extensions; public static class SpeckleApplicationIdExtensions { diff --git a/Connectors/Tekla/Speckle.Connector.TeklaShared/Filters/TeklaSelectionFilter.cs b/Connectors/Tekla/Speckle.Connector.TeklaShared/Filters/TeklaSelectionFilter.cs index f593ff117..bf3eb9849 100644 --- a/Connectors/Tekla/Speckle.Connector.TeklaShared/Filters/TeklaSelectionFilter.cs +++ b/Connectors/Tekla/Speckle.Connector.TeklaShared/Filters/TeklaSelectionFilter.cs @@ -1,6 +1,6 @@ using Speckle.Connectors.DUI.Models.Card.SendFilter; -namespace Speckle.Connector.Tekla2024.Filters; +namespace Speckle.Connectors.TeklaShared.Filters; public class TeklaSelectionFilter : DirectSelectionSendFilter { diff --git a/Connectors/Tekla/Speckle.Connector.TeklaShared/HostApp/SendCollectionManager.cs b/Connectors/Tekla/Speckle.Connector.TeklaShared/HostApp/SendCollectionManager.cs index 6fd6e924b..80611a894 100644 --- a/Connectors/Tekla/Speckle.Connector.TeklaShared/HostApp/SendCollectionManager.cs +++ b/Connectors/Tekla/Speckle.Connector.TeklaShared/HostApp/SendCollectionManager.cs @@ -1,8 +1,8 @@ -using Speckle.Converter.Tekla2024; using Speckle.Converters.Common; +using Speckle.Converters.TeklaShared; using Speckle.Sdk.Models.Collections; -namespace Speckle.Connector.Tekla2024.HostApp; +namespace Speckle.Connectors.TeklaShared.HostApp; public class SendCollectionManager { diff --git a/Connectors/Tekla/Speckle.Connector.TeklaShared/HostApp/TeklaDocumentModelStore.cs b/Connectors/Tekla/Speckle.Connector.TeklaShared/HostApp/TeklaDocumentModelStore.cs index 3dab149ed..05727bae7 100644 --- a/Connectors/Tekla/Speckle.Connector.TeklaShared/HostApp/TeklaDocumentModelStore.cs +++ b/Connectors/Tekla/Speckle.Connector.TeklaShared/HostApp/TeklaDocumentModelStore.cs @@ -1,70 +1,57 @@ -using System.IO; using Microsoft.Extensions.Logging; using Speckle.Connectors.DUI.Models; using Speckle.Connectors.DUI.Utils; using Speckle.Sdk; using Speckle.Sdk.Helpers; -using Speckle.Sdk.Logging; +using Speckle.Sdk.SQLite; -namespace Speckle.Connector.Tekla2024.HostApp; +namespace Speckle.Connectors.TeklaShared.HostApp; public class TeklaDocumentModelStore : DocumentModelStore { - private readonly ISpeckleApplication _speckleApplication; private readonly ILogger _logger; + private readonly ISqLiteJsonCacheManager _jsonCacheManager; private readonly TSM.Model _model; + private string? _modelKey; private readonly TSM.Events _events; - private string HostAppUserDataPath { get; set; } - private string DocumentStateFile { get; set; } - private string ModelPathHash { get; set; } public TeklaDocumentModelStore( IJsonSerializer jsonSerializer, - ISpeckleApplication speckleApplication, - ILogger logger + ILogger logger, + ISqLiteJsonCacheManagerFactory jsonCacheManagerFactory ) - : base(jsonSerializer, true) + : base(jsonSerializer) { - _speckleApplication = speckleApplication; _logger = logger; - _model = new TSM.Model(); - SetPaths(); + _jsonCacheManager = jsonCacheManagerFactory.CreateForUser("ConnectorsFileData"); _events = new TSM.Events(); + _model = new TSM.Model(); + GenerateKey(); _events.ModelLoad += () => { - SetPaths(); - ReadFromFile(); + GenerateKey(); + LoadState(); OnDocumentChanged(); }; _events.Register(); if (SpeckleTeklaPanelHost.IsInitialized) { - ReadFromFile(); + LoadState(); OnDocumentChanged(); } } - private void SetPaths() - { - ModelPathHash = Crypt.Md5(_model.GetInfo().ModelPath, length: 32); - HostAppUserDataPath = Path.Combine( - SpecklePathProvider.UserSpeckleFolderPath, - "Connectors", - _speckleApplication.Slug - ); - DocumentStateFile = Path.Combine(HostAppUserDataPath, $"{ModelPathHash}.json"); - } + private void GenerateKey() => _modelKey = Crypt.Md5(_model.GetInfo().ModelPath, length: 32); - public override void WriteToFile() + protected override void HostAppSaveState(string modelCardState) { - string serializedState = Serialize(); try { - if (!Directory.Exists(HostAppUserDataPath)) + if (_modelKey is null) { - Directory.CreateDirectory(HostAppUserDataPath); + return; } - File.WriteAllText(DocumentStateFile, serializedState); + _jsonCacheManager.UpdateObject(_modelKey, modelCardState); } catch (Exception ex) when (!ex.IsFatal()) { @@ -72,29 +59,13 @@ public class TeklaDocumentModelStore : DocumentModelStore } } - public override void ReadFromFile() + protected override void LoadState() { - try + if (_modelKey is null) { - if (!Directory.Exists(HostAppUserDataPath)) - { - Models = new(); - return; - } - - if (!File.Exists(DocumentStateFile)) - { - Models = new(); - return; - } - - string serializedState = File.ReadAllText(DocumentStateFile); - Models = Deserialize(serializedState) ?? new(); - } - catch (Exception ex) when (!ex.IsFatal()) - { - Models = new(); - _logger.LogError(ex.Message); + return; } + var state = _jsonCacheManager.GetObject(_modelKey); + LoadFromString(state); } } diff --git a/Connectors/Tekla/Speckle.Connector.TeklaShared/HostApp/TeklaIdleManager.cs b/Connectors/Tekla/Speckle.Connector.TeklaShared/HostApp/TeklaIdleManager.cs index bef02dd54..851020e61 100644 --- a/Connectors/Tekla/Speckle.Connector.TeklaShared/HostApp/TeklaIdleManager.cs +++ b/Connectors/Tekla/Speckle.Connector.TeklaShared/HostApp/TeklaIdleManager.cs @@ -1,7 +1,7 @@ using Speckle.Connectors.DUI.Bridge; using Tekla.Structures.Model; -namespace Speckle.Connector.Tekla2024.HostApp; +namespace Speckle.Connectors.TeklaShared.HostApp; public sealed class TeklaIdleManager : AppIdleManager { diff --git a/Connectors/Tekla/Speckle.Connector.TeklaShared/HostApp/TeklaMaterialUnpacker.cs b/Connectors/Tekla/Speckle.Connector.TeklaShared/HostApp/TeklaMaterialUnpacker.cs index 5d4bd9164..18cc8dd8b 100644 --- a/Connectors/Tekla/Speckle.Connector.TeklaShared/HostApp/TeklaMaterialUnpacker.cs +++ b/Connectors/Tekla/Speckle.Connector.TeklaShared/HostApp/TeklaMaterialUnpacker.cs @@ -1,80 +1,55 @@ using System.Drawing; -using Microsoft.Extensions.Logging; -using Speckle.Connector.Tekla2024.Extensions; +using Speckle.Connectors.TeklaShared.Extensions; using Speckle.Objects.Other; -namespace Speckle.Connector.Tekla2024.HostApp; +namespace Speckle.Connectors.TeklaShared.HostApp; public class TeklaMaterialUnpacker { - private readonly ILogger _logger; - - public TeklaMaterialUnpacker(ILogger logger) - { - _logger = logger; - } - public List UnpackRenderMaterial(List atomicObjects) { - var renderMaterialProxies = new Dictionary(); - var processedObjects = new HashSet(); + Dictionary renderMaterialProxies = new(); + + var flattenedAtomicObjects = new List(); foreach (var atomicObject in atomicObjects) { - ProcessModelObject(atomicObject, renderMaterialProxies, processedObjects); + flattenedAtomicObjects.Add(atomicObject); + flattenedAtomicObjects.AddRange(atomicObject.GetSupportedChildren().ToList()); + } + + foreach (TSM.ModelObject flattenedAtomicObject in flattenedAtomicObjects) + { + var color = new TSMUI.Color(); + TSMUI.ModelObjectVisualization.GetRepresentation(flattenedAtomicObject, ref color); + int r = (int)(color.Red * 255); + int g = (int)(color.Green * 255); + int b = (int)(color.Blue * 255); + int a = (int)(color.Transparency * 255); + int argb = (a << 24) | (r << 16) | (g << 8) | b; + + Color systemColor = Color.FromArgb(argb); + + var colorId = color.GetSpeckleApplicationId(); + var objectId = flattenedAtomicObject.GetSpeckleApplicationId(); + if (renderMaterialProxies.TryGetValue(colorId, out RenderMaterialProxy? value)) + { + value.objects.Add(objectId); + } + else + { + var renderMaterial = new RenderMaterial() { name = colorId, diffuse = systemColor.ToArgb() }; + RenderMaterialProxy proxyRenderMaterial = + new() + { + value = renderMaterial, + objects = [objectId], + applicationId = colorId + }; + renderMaterialProxies[colorId] = proxyRenderMaterial; + } } return renderMaterialProxies.Values.ToList(); } - - private void ProcessModelObject( - TSM.ModelObject modelObject, - Dictionary renderMaterialProxies, - HashSet processedObjects - ) - { - var objectId = modelObject.GetSpeckleApplicationId(); - - // NOTE: Related to CNX 798, processing of BooleanPart led to renderMaterial overwrites. Hence, it was excluded - // If duplicate objectIds are still appearing, there is another type causing issues. - if (processedObjects.Contains(objectId)) - { - _logger.LogError( - $"The objectId {objectId} had already been processed. Check ModelObjectExtension.cs for nested object circular references." - ); - } - - processedObjects.Add(objectId); - - var color = new TSMUI.Color(); - TSMUI.ModelObjectVisualization.GetRepresentation(modelObject, ref color); - int r = (int)(color.Red * 255); - int g = (int)(color.Green * 255); - int b = (int)(color.Blue * 255); - int a = (int)(color.Transparency * 255); - int argb = (a << 24) | (r << 16) | (g << 8) | b; - - Color systemColor = Color.FromArgb(argb); - var colorId = color.GetSpeckleApplicationId(); - - // Ensure unique RenderMaterialProxy for each color - if (!renderMaterialProxies.TryGetValue(colorId, out RenderMaterialProxy? renderMaterialProxy)) - { - renderMaterialProxy = new RenderMaterialProxy - { - value = new RenderMaterial { name = colorId, diffuse = systemColor.ToArgb() }, - objects = new List(), - applicationId = colorId - }; - renderMaterialProxies[colorId] = renderMaterialProxy; - } - - renderMaterialProxy.objects.Add(objectId); - - // Recursively process children (not included in s_excludedTypes) - foreach (var child in modelObject.GetSupportedChildren()) - { - ProcessModelObject(child, renderMaterialProxies, processedObjects); - } - } } diff --git a/Connectors/Tekla/Speckle.Connector.TeklaShared/Operations/Send/Settings/SendRebarsAsSolidSetting.cs b/Connectors/Tekla/Speckle.Connector.TeklaShared/Operations/Send/Settings/SendRebarsAsSolidSetting.cs index 0d1f8f31e..fb5f0640d 100644 --- a/Connectors/Tekla/Speckle.Connector.TeklaShared/Operations/Send/Settings/SendRebarsAsSolidSetting.cs +++ b/Connectors/Tekla/Speckle.Connector.TeklaShared/Operations/Send/Settings/SendRebarsAsSolidSetting.cs @@ -1,6 +1,6 @@ using Speckle.Connectors.DUI.Settings; -namespace Speckle.Connector.Tekla2024.Operations.Send.Settings; +namespace Speckle.Connectors.TeklaShared.Operations.Send.Settings; public class SendRebarsAsSolidSetting(bool value) : ICardSetting { diff --git a/Connectors/Tekla/Speckle.Connector.TeklaShared/Operations/Send/Settings/ToSpeckleSettingsManager.cs b/Connectors/Tekla/Speckle.Connector.TeklaShared/Operations/Send/Settings/ToSpeckleSettingsManager.cs index 84cb7db37..5cdc808e3 100644 --- a/Connectors/Tekla/Speckle.Connector.TeklaShared/Operations/Send/Settings/ToSpeckleSettingsManager.cs +++ b/Connectors/Tekla/Speckle.Connector.TeklaShared/Operations/Send/Settings/ToSpeckleSettingsManager.cs @@ -3,7 +3,7 @@ using Speckle.Connectors.DUI.Models.Card; using Speckle.InterfaceGenerator; using Speckle.Sdk.Common; -namespace Speckle.Connector.Tekla2024.Operations.Send.Settings; +namespace Speckle.Connectors.TeklaShared.Operations.Send.Settings; [GenerateAutoInterface] public class ToSpeckleSettingsManager : IToSpeckleSettingsManager diff --git a/Connectors/Tekla/Speckle.Connector.TeklaShared/Operations/Send/TeklaRootObjectBuilder.cs b/Connectors/Tekla/Speckle.Connector.TeklaShared/Operations/Send/TeklaRootObjectBuilder.cs index c58241e1d..84bf9be2d 100644 --- a/Connectors/Tekla/Speckle.Connector.TeklaShared/Operations/Send/TeklaRootObjectBuilder.cs +++ b/Connectors/Tekla/Speckle.Connector.TeklaShared/Operations/Send/TeklaRootObjectBuilder.cs @@ -1,19 +1,18 @@ using Microsoft.Extensions.Logging; -using Speckle.Connector.Tekla2024.Extensions; -using Speckle.Connector.Tekla2024.HostApp; using Speckle.Connectors.Common.Builders; using Speckle.Connectors.Common.Caching; using Speckle.Connectors.Common.Conversion; using Speckle.Connectors.Common.Operations; -using Speckle.Converter.Tekla2024; +using Speckle.Connectors.TeklaShared.Extensions; +using Speckle.Connectors.TeklaShared.HostApp; using Speckle.Converters.Common; +using Speckle.Converters.TeklaShared; using Speckle.Sdk; using Speckle.Sdk.Logging; using Speckle.Sdk.Models; using Speckle.Sdk.Models.Collections; -using Task = System.Threading.Tasks.Task; -namespace Speckle.Connector.Tekla2024.Operations.Send; +namespace Speckle.Connectors.TeklaShared.Operations.Send; public class TeklaRootObjectBuilder : IRootObjectBuilder { @@ -48,7 +47,7 @@ public class TeklaRootObjectBuilder : IRootObjectBuilder IReadOnlyList teklaObjects, SendInfo sendInfo, IProgress onOperationProgressed, - CancellationToken cancellationToken = default + CancellationToken cancellationToken ) { using var activity = _activityFactory.Start("Build"); @@ -66,14 +65,15 @@ public class TeklaRootObjectBuilder : IRootObjectBuilder { foreach (TSM.ModelObject teklaObject in teklaObjects) { - using var _2 = _activityFactory.Start("Convert"); cancellationToken.ThrowIfCancellationRequested(); + using var _2 = _activityFactory.Start("Convert"); var result = ConvertTeklaObject(teklaObject, rootObjectCollection, sendInfo.ProjectId); results.Add(result); ++count; onOperationProgressed.Report(new("Converting", (double)count / teklaObjects.Count)); + await Task.Yield(); } } @@ -88,7 +88,6 @@ public class TeklaRootObjectBuilder : IRootObjectBuilder rootObjectCollection[ProxyKeys.RENDER_MATERIAL] = renderMaterialProxies; } - await Task.Yield(); return new RootObjectBuilderResult(rootObjectCollection, results); } diff --git a/Connectors/Tekla/Speckle.Connector.TeklaShared/Plugin/TeklaPlugin.cs b/Connectors/Tekla/Speckle.Connector.TeklaShared/Plugin/TeklaPlugin.cs index 87a7fc644..28f2ff0b7 100644 --- a/Connectors/Tekla/Speckle.Connector.TeklaShared/Plugin/TeklaPlugin.cs +++ b/Connectors/Tekla/Speckle.Connector.TeklaShared/Plugin/TeklaPlugin.cs @@ -1,9 +1,9 @@ using Tekla.Structures.Plugins; -namespace Speckle.Connector.Tekla2024.Plugin; +namespace Speckle.Connectors.TeklaShared.Plugin; [Plugin("Speckle")] -[PluginUserInterface("Speckle.Connector.Tekla2024.SpeckleTeklaPanelHost")] +[PluginUserInterface("Speckle.Connectors.TeklaShared.SpeckleTeklaPanelHost")] [InputObjectDependency(InputObjectDependency.NOT_DEPENDENT)] public class TeklaPlugin : PluginBase { diff --git a/Connectors/Tekla/Speckle.Connector.TeklaShared/Resources/Speckle-Ribbon.xml b/Connectors/Tekla/Speckle.Connector.TeklaShared/Resources/Speckle-Ribbon.xml new file mode 100644 index 000000000..b2033a73c --- /dev/null +++ b/Connectors/Tekla/Speckle.Connector.TeklaShared/Resources/Speckle-Ribbon.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/Connectors/Tekla/Speckle.Connector.TeklaShared/Resources/speckle.svg b/Connectors/Tekla/Speckle.Connector.TeklaShared/Resources/speckle.svg new file mode 100644 index 000000000..e170ec2e2 --- /dev/null +++ b/Connectors/Tekla/Speckle.Connector.TeklaShared/Resources/speckle.svg @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Connectors/Tekla/Speckle.Connector.TeklaShared/ServiceRegistration.cs b/Connectors/Tekla/Speckle.Connector.TeklaShared/ServiceRegistration.cs index 71da6f5c4..d569bc59a 100644 --- a/Connectors/Tekla/Speckle.Connector.TeklaShared/ServiceRegistration.cs +++ b/Connectors/Tekla/Speckle.Connector.TeklaShared/ServiceRegistration.cs @@ -1,27 +1,26 @@ using Microsoft.Extensions.DependencyInjection; -using Speckle.Connector.Tekla2024.Bindings; -using Speckle.Connector.Tekla2024.Filters; -using Speckle.Connector.Tekla2024.HostApp; -using Speckle.Connector.Tekla2024.Operations.Send; -using Speckle.Connector.Tekla2024.Operations.Send.Settings; using Speckle.Connectors.Common; using Speckle.Connectors.Common.Builders; using Speckle.Connectors.Common.Caching; -using Speckle.Connectors.Common.Cancellation; using Speckle.Connectors.Common.Operations; +using Speckle.Connectors.Common.Threading; using Speckle.Connectors.DUI; using Speckle.Connectors.DUI.Bindings; using Speckle.Connectors.DUI.Bridge; -using Speckle.Connectors.DUI.Models; using Speckle.Connectors.DUI.Models.Card.SendFilter; using Speckle.Connectors.DUI.WebView; -using Speckle.Converter.Tekla2024; +using Speckle.Connectors.TeklaShared.Bindings; +using Speckle.Connectors.TeklaShared.Filters; +using Speckle.Connectors.TeklaShared.HostApp; +using Speckle.Connectors.TeklaShared.Operations.Send; +using Speckle.Connectors.TeklaShared.Operations.Send.Settings; using Speckle.Converters.Common; +using Speckle.Converters.TeklaShared; using Speckle.Sdk; using Speckle.Sdk.Models.GraphTraversal; using Tekla.Structures.Model; -namespace Speckle.Connector.Tekla2024; +namespace Speckle.Connectors.TeklaShared; public static class ServiceRegistration { @@ -32,10 +31,9 @@ public static class ServiceRegistration services.AddSingleton(); services.AddConnectorUtils(); - services.AddDUI(); + services.AddDUI(); services.AddDUIView(); - services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); @@ -43,8 +41,6 @@ public static class ServiceRegistration services.AddSingleton(); services.AddSingleton(); - services.RegisterTopLevelExceptionHandler(); - services.AddSingleton(sp => sp.GetRequiredService()); services.AddSingleton(); services.AddSingleton(); @@ -62,7 +58,6 @@ public static class ServiceRegistration services.AddSingleton(); - services.AddTransient(); services.AddSingleton(); services.AddScoped(); diff --git a/Connectors/Tekla/Speckle.Connector.TeklaShared/Speckle.Connectors.TeklaShared.projitems b/Connectors/Tekla/Speckle.Connector.TeklaShared/Speckle.Connectors.TeklaShared.projitems index e9782ec4d..86a8ddc2d 100644 --- a/Connectors/Tekla/Speckle.Connector.TeklaShared/Speckle.Connectors.TeklaShared.projitems +++ b/Connectors/Tekla/Speckle.Connector.TeklaShared/Speckle.Connectors.TeklaShared.projitems @@ -13,6 +13,16 @@ Always + + + Always + + + + + PreserveNewest + + @@ -36,4 +46,8 @@ + + + + diff --git a/Connectors/Tekla/Speckle.Connector.TeklaShared/SpeckleTeklaPanelHost.cs b/Connectors/Tekla/Speckle.Connector.TeklaShared/SpeckleTeklaPanelHost.cs index b535cd82b..cfa9bfcb6 100644 --- a/Connectors/Tekla/Speckle.Connector.TeklaShared/SpeckleTeklaPanelHost.cs +++ b/Connectors/Tekla/Speckle.Connector.TeklaShared/SpeckleTeklaPanelHost.cs @@ -5,14 +5,15 @@ using System.Windows.Forms; using System.Windows.Forms.Integration; using Microsoft.Extensions.DependencyInjection; using Speckle.Connectors.Common; +using Speckle.Connectors.DUI; using Speckle.Connectors.DUI.WebView; -using Speckle.Converter.Tekla2024; +using Speckle.Converters.TeklaShared; using Speckle.Sdk.Host; using Tekla.Structures.Dialog; using Tekla.Structures.Model; using Tekla.Structures.Model.Operations; -namespace Speckle.Connector.Tekla2024; +namespace Speckle.Connectors.TeklaShared; public class SpeckleTeklaPanelHost : PluginFormBase { @@ -67,8 +68,8 @@ public class SpeckleTeklaPanelHost : PluginFormBase { s_instance = this; // Assign the current instance to the static field - this.Text = "Speckle (Beta)"; - this.Name = "Speckle (Beta)"; + Text = "Speckle (Beta)"; + Name = "Speckle (Beta)"; string assemblyName = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name; string resourcePath = $"{assemblyName}.Resources.et_element_Speckle.bmp"; @@ -80,7 +81,7 @@ public class SpeckleTeklaPanelHost : PluginFormBase } using var bmp = new Bitmap(stream); - this.Icon = Icon.FromHandle(bmp.GetHicon()); + Icon = Icon.FromHandle(bmp.GetHicon()); } var services = new ServiceCollection(); @@ -89,6 +90,7 @@ public class SpeckleTeklaPanelHost : PluginFormBase services.AddTeklaConverters(); Container = services.BuildServiceProvider(); + Container.UseDUI(); Model = new Model(); if (!Model.GetConnectionStatus()) @@ -106,7 +108,7 @@ public class SpeckleTeklaPanelHost : PluginFormBase Controls.Add(Host); Operation.DisplayPrompt("Speckle connector initialized."); - this.TopLevel = true; + TopLevel = true; SetWindowLongPtr(Handle, GWL_HWNDPARENT, MainWindow.Frame.Handle); Show(); Activate(); diff --git a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ArcGISConversionSettingsFactory.cs b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ArcGISConversionSettingsFactory.cs index 5ae9ae250..d1aa36b1e 100644 --- a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ArcGISConversionSettingsFactory.cs +++ b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ArcGISConversionSettingsFactory.cs @@ -1,7 +1,6 @@ using ArcGIS.Core.Data; using ArcGIS.Core.Data.DDL; using ArcGIS.Desktop.Core; -using ArcGIS.Desktop.Framework.Threading.Tasks; using ArcGIS.Desktop.Mapping; using Speckle.Converters.ArcGIS3.Utils; using Speckle.Converters.Common; @@ -61,15 +60,15 @@ public class ArcGISConversionSettingsFactory(IHostToSpeckleUnitConverter Project.Current.AddItem(folderToAdd as IProjectItem)); + Project.Current.AddItem(folderToAdd as IProjectItem); // Add a file geodatabase or a SQLite or enterprise database connection to a project try @@ -149,8 +150,7 @@ public class ArcGISConversionSettingsFactory(IHostToSpeckleUnitConverter Project.Current.AddItem(gdbToAdd as IProjectItem)); + var addedGeodatabase = Project.Current.AddItem(gdbToAdd as IProjectItem); } } catch (NullReferenceException ex) diff --git a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ArcGISConverterModule.cs b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ArcGISConverterModule.cs index 6597df04a..c7c0a1c49 100644 --- a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ArcGISConverterModule.cs +++ b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ArcGISConverterModule.cs @@ -1,6 +1,6 @@ using System.Reflection; -using ArcGIS.Core.Geometry; using Microsoft.Extensions.DependencyInjection; +using Speckle.Converters.ArcGIS3.ToSpeckle.Helpers; using Speckle.Converters.ArcGIS3.Utils; using Speckle.Converters.Common; using Speckle.Converters.Common.Registration; @@ -20,14 +20,15 @@ public static class ArcGISConverterModule serviceCollection.AddRootCommon(converterAssembly); // add application converters - serviceCollection.AddApplicationConverters(converterAssembly); + serviceCollection.AddApplicationConverters(converterAssembly); // most things should be InstancePerLifetimeScope so we get one per operation serviceCollection.AddScoped(); - serviceCollection.AddScoped(); serviceCollection.AddScoped(); serviceCollection.AddScoped(); serviceCollection.AddScoped(); + serviceCollection.AddScoped(); + serviceCollection.AddScoped(); // single stack per conversion serviceCollection.AddScoped< diff --git a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ArcGISToSpeckleUnitConverter.cs b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ArcGISToSpeckleUnitConverter.cs index 7c8586365..656edfc6f 100644 --- a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ArcGISToSpeckleUnitConverter.cs +++ b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ArcGISToSpeckleUnitConverter.cs @@ -22,7 +22,6 @@ public class ArcGISToSpeckleUnitConverter : IHostToSpeckleUnitConverter dict[LinearUnit.Feet.FactoryCode] = Units.Feet; dict[LinearUnit.Yards.FactoryCode] = Units.Yards; dict[LinearUnit.Miles.FactoryCode] = Units.Miles; - //dict[9003] = Units.USFeet; return dict; } diff --git a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/GlobalUsings.cs b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/GlobalUsings.cs index d29d85270..ea0876c8c 100644 --- a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/GlobalUsings.cs +++ b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/GlobalUsings.cs @@ -1,3 +1,4 @@ +global using AC = ArcGIS.Core; +global using ACD = ArcGIS.Core.Data; global using ACG = ArcGIS.Core.Geometry; -global using SGIS = Speckle.Objects.GIS; global using SOG = Speckle.Objects.Geometry; diff --git a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Speckle.Converters.ArcGIS3.csproj b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Speckle.Converters.ArcGIS3.csproj index 579e00a68..adbee8c0b 100644 --- a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Speckle.Converters.ArcGIS3.csproj +++ b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Speckle.Converters.ArcGIS3.csproj @@ -15,4 +15,8 @@ + + + + diff --git a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/Raw/GeometryToHostConverter.cs b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/Raw/GeometryToHostConverter.cs index ce80dd378..31ef91337 100644 --- a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/Raw/GeometryToHostConverter.cs +++ b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/Raw/GeometryToHostConverter.cs @@ -8,23 +8,14 @@ public class GeometryToHostConverter : ITypedConverter, ACG. { private readonly ITypedConverter, ACG.Polyline> _polylineConverter; private readonly ITypedConverter, ACG.Multipoint> _multipointConverter; - private readonly ITypedConverter, ACG.Multipatch> _polygon3dConverter; - private readonly ITypedConverter, ACG.Polygon> _polygonConverter; - private readonly ITypedConverter, ACG.Multipatch> _multipatchConverter; public GeometryToHostConverter( ITypedConverter, ACG.Polyline> polylineConverter, - ITypedConverter, ACG.Multipoint> multipointConverter, - ITypedConverter, ACG.Multipatch> polygon3dConverter, - ITypedConverter, ACG.Polygon> polygonConverter, - ITypedConverter, ACG.Multipatch> multipatchConverter + ITypedConverter, ACG.Multipoint> multipointConverter ) { _polylineConverter = polylineConverter; _multipointConverter = multipointConverter; - _polygon3dConverter = polygon3dConverter; - _polygonConverter = polygonConverter; - _multipatchConverter = multipatchConverter; } public ACG.Geometry Convert(IReadOnlyList target) @@ -38,9 +29,6 @@ public class GeometryToHostConverter : ITypedConverter, ACG. { SOG.Point => _multipointConverter.Convert(target.Cast().ToList()), SOG.Polyline => _polylineConverter.Convert(target.Cast().ToList()), - SGIS.PolygonGeometry3d => _polygon3dConverter.Convert(target.Cast().ToList()), - SGIS.PolygonGeometry => _polygonConverter.Convert(target.Cast().ToList()), - SGIS.GisMultipatchGeometry => _multipatchConverter.Convert(target.Cast().ToList()), _ => throw new ValidationException($"No conversion found for type {target[0]}") }; } diff --git a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/Raw/MeshListToHostConverter.cs b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/Raw/MeshListToHostConverter.cs index c355b681a..462f22368 100644 --- a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/Raw/MeshListToHostConverter.cs +++ b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/Raw/MeshListToHostConverter.cs @@ -25,7 +25,8 @@ public class MeshListToHostConverter : ITypedConverter, ACG.Multi { throw new ValidationException("Feature contains no geometries"); } - ACG.MultipatchBuilderEx multipatchPart = new(_settingsStore.Current.ActiveCRSoffsetRotation.SpatialReference); + + ACG.MultipatchBuilderEx multipatchPart = new(); //_settingsStore.Current.ActiveCRSoffsetRotation.SpatialReference); foreach (SOG.Mesh part in target) { diff --git a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/Raw/MultipatchListToHostConverter.cs b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/Raw/MultipatchListToHostConverter.cs deleted file mode 100644 index 0060a2a20..000000000 --- a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/Raw/MultipatchListToHostConverter.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Speckle.Converters.Common.Objects; -using Speckle.Sdk.Common.Exceptions; - -namespace Speckle.Converters.ArcGIS3.ToHost.Raw; - -public class MultipatchListToHostConverter : ITypedConverter, ACG.Multipatch> -{ - private readonly ITypedConverter _pointConverter; - - public MultipatchListToHostConverter(ITypedConverter pointConverter) - { - _pointConverter = pointConverter; - } - - public ACG.Multipatch Convert(List target) - { - if (target.Count == 0) - { - throw new ValidationException("Feature contains no geometries"); - } - ACG.MultipatchBuilderEx multipatchPart = new(); - foreach (SGIS.GisMultipatchGeometry part in target) - { - ACG.Patch newPatch = multipatchPart.MakePatch(ACG.PatchType.Triangles); - for (int i = 0; i < part.vertices.Count / 3; i++) - { - newPatch.AddPoint( - _pointConverter.Convert( - new SOG.Point(part.vertices[i * 3], part.vertices[i * 3 + 1], part.vertices[i * 3 + 2], part.units) - ) - ); - } - multipatchPart.Patches.Add(newPatch); - } - return multipatchPart.ToGeometry(); - } -} diff --git a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/Raw/PointcloudLayerToHostConverter.cs b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/Raw/PointcloudLayerToHostConverter.cs deleted file mode 100644 index 6cc57af37..000000000 --- a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/Raw/PointcloudLayerToHostConverter.cs +++ /dev/null @@ -1,17 +0,0 @@ -using ArcGIS.Desktop.Mapping; -using Speckle.Converters.Common.Objects; -using Speckle.Objects.GIS; -using Speckle.Sdk.Models; - -namespace Speckle.Converters.ArcGIS3.ToHost.Raw; - -public class PointcloudLayerToHostConverter : ITypedConverter -{ - public object Convert(Base target) => Convert((VectorLayer)target); - - public LasDatasetLayer Convert(VectorLayer target) - { - // POC: - throw new NotImplementedException($"Receiving Pointclouds is not supported"); - } -} diff --git a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/Raw/Polygon3dListToHostConverter.cs b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/Raw/Polygon3dListToHostConverter.cs deleted file mode 100644 index 4f532d197..000000000 --- a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/Raw/Polygon3dListToHostConverter.cs +++ /dev/null @@ -1,51 +0,0 @@ -using Speckle.Converters.Common.Objects; -using Speckle.Sdk.Common.Exceptions; - -namespace Speckle.Converters.ArcGIS3.ToHost.Raw; - -public class Polygon3dListToHostConverter : ITypedConverter, ACG.Multipatch> -{ - private readonly ITypedConverter _pointConverter; - private readonly ITypedConverter _polylineConverter; - - public Polygon3dListToHostConverter( - ITypedConverter pointConverter, - ITypedConverter polylineConverter - ) - { - _pointConverter = pointConverter; - _polylineConverter = polylineConverter; - } - - public ACG.Multipatch Convert(List target) - { - if (target.Count == 0) - { - throw new ValidationException("Feature contains no geometries"); - } - - ACG.MultipatchBuilderEx multipatchPart = new(); - foreach (SGIS.PolygonGeometry3d part in target) - { - ACG.Patch newPatch = multipatchPart.MakePatch(ACG.PatchType.FirstRing); - List boundaryPts = part.boundary.GetPoints(); - foreach (SOG.Point pt in boundaryPts) - { - newPatch.AddPoint(_pointConverter.Convert(pt)); - } - multipatchPart.Patches.Add(newPatch); - - foreach (SOG.Polyline loop in part.voids) - { - ACG.Patch newLoopPatch = multipatchPart.MakePatch(ACG.PatchType.Ring); - List loopPts = loop.GetPoints(); - foreach (SOG.Point pt in loopPts) - { - newLoopPatch.AddPoint(_pointConverter.Convert(pt)); - } - multipatchPart.Patches.Add(newLoopPatch); - } - } - return multipatchPart.ToGeometry(); - } -} diff --git a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/Raw/PolygonListToHostConverter.cs b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/Raw/PolygonListToHostConverter.cs deleted file mode 100644 index f59d8d58f..000000000 --- a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/Raw/PolygonListToHostConverter.cs +++ /dev/null @@ -1,38 +0,0 @@ -using Speckle.Converters.Common.Objects; -using Speckle.Sdk.Common.Exceptions; - -namespace Speckle.Converters.ArcGIS3.ToHost.Raw; - -public class PolygonListToHostConverter : ITypedConverter, ACG.Polygon> -{ - private readonly ITypedConverter _polylineConverter; - - public PolygonListToHostConverter(ITypedConverter polylineConverter) - { - _polylineConverter = polylineConverter; - } - - public ACG.Polygon Convert(List target) - { - if (target.Count == 0) - { - throw new ValidationException("Feature contains no geometries"); - } - List polyList = new(); - foreach (SGIS.PolygonGeometry poly in target) - { - ACG.Polyline boundary = _polylineConverter.Convert(poly.boundary); - ACG.PolygonBuilderEx polyOuterRing = new(boundary); - - foreach (SOG.Polyline loop in poly.voids) - { - // adding inner loops: https://github.com/esri/arcgis-pro-sdk/wiki/ProSnippets-Geometry#build-a-donut-polygon - ACG.Polyline loopNative = _polylineConverter.Convert(loop); - polyOuterRing.AddPart(loopNative.Copy3DCoordinatesToList()); - } - ACG.Polygon polygon = polyOuterRing.ToGeometry(); - polyList.Add(polygon); - } - return new ACG.PolygonBuilderEx(polyList, ACG.AttributeFlags.HasZ).ToGeometry(); - } -} diff --git a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/ArcToHostConverter.cs b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/ArcToHostConverter.cs index 83db8e40d..70656c175 100644 --- a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/ArcToHostConverter.cs +++ b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/ArcToHostConverter.cs @@ -4,7 +4,7 @@ using Speckle.Sdk.Models; namespace Speckle.Converters.ArcGIS3.ToHost.TopLevel; -[NameAndRankValue(nameof(SOG.Arc), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +[NameAndRankValue(typeof(SOG.Arc), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] public class ArcToHostConverter : IToHostTopLevelConverter, ITypedConverter { private readonly ITypedConverter _pointConverter; diff --git a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/CircleToHostConverter.cs b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/CircleToHostConverter.cs index 993f84d9a..758d6a42f 100644 --- a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/CircleToHostConverter.cs +++ b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/CircleToHostConverter.cs @@ -5,7 +5,7 @@ using Speckle.Sdk.Models; namespace Speckle.Converters.ArcGIS3.ToHost.TopLevel; -[NameAndRankValue(nameof(SOG.Circle), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +[NameAndRankValue(typeof(SOG.Circle), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] public class CircleToHostConverter : IToHostTopLevelConverter, ITypedConverter { private readonly ITypedConverter _pointConverter; diff --git a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/EllipseToHostConverter.cs b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/EllipseToHostConverter.cs index 27b87b537..7ca52d1eb 100644 --- a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/EllipseToHostConverter.cs +++ b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/EllipseToHostConverter.cs @@ -5,7 +5,7 @@ using Speckle.Sdk.Models; namespace Speckle.Converters.ArcGIS3.ToHost.TopLevel; -[NameAndRankValue(nameof(SOG.Ellipse), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +[NameAndRankValue(typeof(SOG.Ellipse), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] public class EllipseToHostConverter : IToHostTopLevelConverter, ITypedConverter { private readonly ITypedConverter _pointConverter; diff --git a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/FallbackToHostConverter.cs b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/FallbackToHostConverter.cs index 730dd09ab..b75d9b01b 100644 --- a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/FallbackToHostConverter.cs +++ b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/FallbackToHostConverter.cs @@ -6,7 +6,7 @@ using Speckle.Sdk.Models; namespace Speckle.Converters.ArcGIS3.ToHost.TopLevel; -[NameAndRankValue(nameof(DisplayableObject), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +[NameAndRankValue(typeof(DisplayableObject), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] public class FallbackToHostConverter : IToHostTopLevelConverter, ITypedConverter { private readonly ITypedConverter, ACG.Multipatch> _meshListConverter; diff --git a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/GisMultipatchFeatureToHostConverter.cs b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/GisMultipatchFeatureToHostConverter.cs deleted file mode 100644 index aeb9f2d7f..000000000 --- a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/GisMultipatchFeatureToHostConverter.cs +++ /dev/null @@ -1,55 +0,0 @@ -using Speckle.Converters.Common; -using Speckle.Converters.Common.Objects; -using Speckle.Objects; -using Speckle.Sdk.Common.Exceptions; -using Speckle.Sdk.Models; - -namespace Speckle.Converters.ArcGIS3.ToHost.TopLevel; - -/// -/// Converter for with geometry. -/// -[NameAndRankValue(nameof(SGIS.GisMultipatchFeature), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] -public class GisMultipatchFeatureToHostConverter - : IToHostTopLevelConverter, - ITypedConverter -{ - private readonly ITypedConverter, ACG.Multipatch> _polygon3dConverter; - private readonly ITypedConverter, ACG.Multipatch> _multipatchConverter; - - public GisMultipatchFeatureToHostConverter( - ITypedConverter, ACG.Multipatch> polygon3dConverter, - ITypedConverter, ACG.Multipatch> multipatchConverter - ) - { - _polygon3dConverter = polygon3dConverter; - _multipatchConverter = multipatchConverter; - } - - public object Convert(Base target) => Convert((SGIS.GisMultipatchFeature)target); - - public ACG.Geometry Convert(SGIS.GisMultipatchFeature target) - { - if (target.geometry.Count == 0) - { - throw new ArgumentException("Multipatch Feature contains no geometries"); - } - - ACG.Multipatch? multipatch; - try - { - multipatch = _multipatchConverter.Convert(target.geometry.Cast().ToList()); - } - catch (InvalidCastException) - { - multipatch = _polygon3dConverter.Convert(target.geometry.Cast().ToList()); - } - - if (multipatch is null) - { - throw new ConversionException("Multipatch conversion did not return valid geometry"); - } - - return multipatch; - } -} diff --git a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/GisPointFeatureToHostConverter.cs b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/GisPointFeatureToHostConverter.cs deleted file mode 100644 index debdbb923..000000000 --- a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/GisPointFeatureToHostConverter.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Speckle.Converters.Common; -using Speckle.Converters.Common.Objects; -using Speckle.Objects; -using Speckle.Sdk.Models; - -namespace Speckle.Converters.ArcGIS3.ToHost.TopLevel; - -/// -/// Converter for with geometry. -/// -/// Thrown when IGisFeature is because it has no geometry, or when Multipatch geometry contained invalid types. -/// Thrown for unsupported classes. -[NameAndRankValue(nameof(SGIS.GisPointFeature), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] -public class GisPointFeatureToHostConverter - : IToHostTopLevelConverter, - ITypedConverter -{ - private readonly ITypedConverter, ACG.Multipoint> _multipointConverter; - - public GisPointFeatureToHostConverter(ITypedConverter, ACG.Multipoint> multipointConverter) - { - _multipointConverter = multipointConverter; - } - - public object Convert(Base target) => Convert((SGIS.GisPointFeature)target); - - public ACG.Geometry Convert(SGIS.GisPointFeature target) - { - ACG.Multipoint multipoint = _multipointConverter.Convert(target.geometry); - return multipoint; - } -} diff --git a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/GisPolygonFeatureToHostConverter.cs b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/GisPolygonFeatureToHostConverter.cs deleted file mode 100644 index 06e6482a7..000000000 --- a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/GisPolygonFeatureToHostConverter.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Speckle.Converters.Common; -using Speckle.Converters.Common.Objects; -using Speckle.Objects; -using Speckle.Sdk.Models; - -namespace Speckle.Converters.ArcGIS3.ToHost.TopLevel; - -/// -/// Converter for with geometry. -/// -/// Thrown when IGisFeature is because it has no geometry, or when Multipatch geometry contained invalid types. -/// Thrown for unsupported classes. -[NameAndRankValue(nameof(SGIS.GisPolygonFeature), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] -public class GisPolygonFeatureToHostConverter - : IToHostTopLevelConverter, - ITypedConverter -{ - private readonly ITypedConverter, ACG.Polygon> _polygonConverter; - - public GisPolygonFeatureToHostConverter(ITypedConverter, ACG.Polygon> polygonConverter) - { - _polygonConverter = polygonConverter; - } - - public object Convert(Base target) => Convert((SGIS.GisPolygonFeature)target); - - public ACG.Geometry Convert(SGIS.GisPolygonFeature target) - { - ACG.Polygon polygon = _polygonConverter.Convert(target.geometry); - return polygon; - } -} diff --git a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/GisPolylineFeatureToHostConverter.cs b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/GisPolylineFeatureToHostConverter.cs deleted file mode 100644 index c1ff776bc..000000000 --- a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/GisPolylineFeatureToHostConverter.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Speckle.Converters.Common; -using Speckle.Converters.Common.Objects; -using Speckle.Objects; -using Speckle.Sdk.Models; - -namespace Speckle.Converters.ArcGIS3.ToHost.TopLevel; - -/// -/// Converter for with geometry. -/// -/// Thrown when IGisFeature is because it has no geometry, or when Multipatch geometry contained invalid types. -/// Thrown for unsupported classes. -[NameAndRankValue(nameof(SGIS.GisPolylineFeature), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] -public class GisPolylineFeatureToHostConverter - : IToHostTopLevelConverter, - ITypedConverter -{ - private readonly ITypedConverter, ACG.Polyline> _polylineConverter; - - public GisPolylineFeatureToHostConverter(ITypedConverter, ACG.Polyline> polylineConverter) - { - _polylineConverter = polylineConverter; - } - - public object Convert(Base target) => Convert((SGIS.GisPolylineFeature)target); - - public ACG.Geometry Convert(SGIS.GisPolylineFeature target) - { - ACG.Polyline polyline = _polylineConverter.Convert(target.geometry); - return polyline; - } -} diff --git a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/LineToHostConverter.cs b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/LineToHostConverter.cs index 47e6b5c9d..ec19f153c 100644 --- a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/LineToHostConverter.cs +++ b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/LineToHostConverter.cs @@ -4,7 +4,7 @@ using Speckle.Sdk.Models; namespace Speckle.Converters.ArcGIS3.ToHost.TopLevel; -[NameAndRankValue(nameof(SOG.Line), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +[NameAndRankValue(typeof(SOG.Line), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] public class LineSingleToHostConverter : IToHostTopLevelConverter, ITypedConverter { private readonly ITypedConverter _pointConverter; diff --git a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/MeshToHostConverter.cs b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/MeshToHostConverter.cs index cbb55f3ce..123c79c84 100644 --- a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/MeshToHostConverter.cs +++ b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/MeshToHostConverter.cs @@ -4,7 +4,7 @@ using Speckle.Sdk.Models; namespace Speckle.Converters.ArcGIS3.ToHost.TopLevel; -[NameAndRankValue(nameof(SOG.Mesh), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +[NameAndRankValue(typeof(SOG.Mesh), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] public class MeshToHostConverter : IToHostTopLevelConverter, ITypedConverter { private readonly ITypedConverter, ACG.Multipatch> _meshConverter; diff --git a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/PointToHostConverter.cs b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/PointToHostConverter.cs index 6f7bd23f0..ef00a9307 100644 --- a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/PointToHostConverter.cs +++ b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/PointToHostConverter.cs @@ -4,7 +4,7 @@ using Speckle.Sdk.Models; namespace Speckle.Converters.ArcGIS3.ToHost.TopLevel; -[NameAndRankValue(nameof(SOG.Point), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +[NameAndRankValue(typeof(SOG.Point), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] public class PointToHostConverter : IToHostTopLevelConverter { private readonly ITypedConverter, ACG.Multipoint> _pointConverter; diff --git a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/PolycurveToHostConverter.cs b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/PolycurveToHostConverter.cs index fbda9459b..003bc2fa1 100644 --- a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/PolycurveToHostConverter.cs +++ b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/PolycurveToHostConverter.cs @@ -5,7 +5,7 @@ using Speckle.Sdk.Models; namespace Speckle.Converters.ArcGIS3.ToHost.TopLevel; -[NameAndRankValue(nameof(SOG.Polycurve), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +[NameAndRankValue(typeof(SOG.Polycurve), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] public class PolycurveToHostConverter : IToHostTopLevelConverter, ITypedConverter { private readonly IRootToHostConverter _converter; diff --git a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/PolylineToHostConverter.cs b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/PolylineToHostConverter.cs index b6842cdc9..38e5dcbd4 100644 --- a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/PolylineToHostConverter.cs +++ b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/PolylineToHostConverter.cs @@ -4,7 +4,7 @@ using Speckle.Sdk.Models; namespace Speckle.Converters.ArcGIS3.ToHost.TopLevel; -[NameAndRankValue(nameof(SOG.Polyline), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +[NameAndRankValue(typeof(SOG.Polyline), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] public class PolylineToHostConverter : IToHostTopLevelConverter, ITypedConverter { private readonly ITypedConverter _pointConverter; diff --git a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/RasterLayerToHostConverter.cs b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/RasterLayerToHostConverter.cs deleted file mode 100644 index bcc036b49..000000000 --- a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/RasterLayerToHostConverter.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Speckle.Converters.Common; -using Speckle.Converters.Common.Objects; -using Speckle.Objects.GIS; -using Speckle.Sdk.Models; - -namespace Speckle.Converters.ArcGIS3.ToHost.TopLevel; - -[NameAndRankValue(nameof(RasterLayer), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] -public class RasterLayerToHostConverter : IToHostTopLevelConverter, ITypedConverter -{ - public object Convert(Base target) => Convert((RasterLayer)target); - - public string Convert(RasterLayer target) - { - // POC: - throw new NotImplementedException($"Receiving Rasters is not supported"); - } -} diff --git a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/Helpers/DisplayValueExtractor.cs b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/Helpers/DisplayValueExtractor.cs new file mode 100644 index 000000000..9a5b36f41 --- /dev/null +++ b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/Helpers/DisplayValueExtractor.cs @@ -0,0 +1,111 @@ +using Speckle.Converters.Common.Objects; +using Speckle.Sdk.Common.Exceptions; +using Speckle.Sdk.Models; + +namespace Speckle.Converters.ArcGIS3.ToSpeckle.Helpers; + +public sealed class DisplayValueExtractor +{ + private readonly ITypedConverter _pointConverter; + private readonly ITypedConverter> _multiPointConverter; + private readonly ITypedConverter> _polylineConverter; + private readonly ITypedConverter> _polygonConverter; + private readonly ITypedConverter> _multipatchConverter; + private readonly ITypedConverter _gisRasterConverter; + + public DisplayValueExtractor( + ITypedConverter pointConverter, + ITypedConverter> multiPointConverter, + ITypedConverter> polylineConverter, + ITypedConverter> polygonConverter, + ITypedConverter> multipatchConverter, + ITypedConverter gisRasterConverter + ) + { + _pointConverter = pointConverter; + _multiPointConverter = multiPointConverter; + _polylineConverter = polylineConverter; + _polygonConverter = polygonConverter; + _multipatchConverter = multipatchConverter; + _gisRasterConverter = gisRasterConverter; + } + + public IEnumerable GetDisplayValue(AC.CoreObjectsBase coreObjectsBase) + { + switch (coreObjectsBase) + { + case ACD.Row row: + foreach (Base shape in GetRowGeometries(row)) + { + yield return shape; + } + break; + + case ACD.Raster.Raster raster: + yield return _gisRasterConverter.Convert(raster); + break; + + case ACD.Analyst3D.LasPoint point: + yield return _pointConverter.Convert(point.ToMapPoint()); + break; + + default: + // TODO: log that no display value is supported for this type + yield break; + } + } + + private IEnumerable GetRowGeometries(ACD.Row row) + { + // see if this row contains any geometry fields + // POC: is it possible to have multiple geometry fields in a row? + string? geometryField = row.GetFields() + .Where(o => o.FieldType == ACD.FieldType.Geometry) + ?.Select(o => o.Name) + ?.First(); + + if (geometryField is null) + { + yield break; + } + + var shape = (ACG.Geometry)row[geometryField]; + switch (shape) + { + case ACG.MapPoint point: + yield return _pointConverter.Convert(point); + break; + + case ACG.Multipoint multipoint: + foreach (SOG.Point converted in _multiPointConverter.Convert(multipoint)) + { + yield return converted; + } + break; + + case ACG.Polyline polyline: + foreach (SOG.Polyline converted in _polylineConverter.Convert(polyline)) + { + yield return converted; + } + break; + + case ACG.Polygon polygon: + foreach (Base converted in _polygonConverter.Convert(polygon)) + { + yield return converted; + } + break; + + case ACG.Multipatch multipatch: + foreach (SOG.Mesh converted in _multipatchConverter.Convert(multipatch)) + { + yield return converted; + } + break; + + default: + throw new ValidationException($"No geometry conversion found for {shape.GetType().Name}"); + } + } +} diff --git a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/Helpers/PropertiesExtractor.cs b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/Helpers/PropertiesExtractor.cs new file mode 100644 index 000000000..1cd9ba587 --- /dev/null +++ b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/Helpers/PropertiesExtractor.cs @@ -0,0 +1,54 @@ +namespace Speckle.Converters.ArcGIS3.ToSpeckle.Helpers; + +public sealed class PropertiesExtractor +{ + public PropertiesExtractor() { } + + public Dictionary GetProperties(AC.CoreObjectsBase coreObjectsBase) + { + switch (coreObjectsBase) + { + case ACD.Row row: + return GetRowFields(row); + } + + return new(); + } + + public Dictionary GetRowFields(ACD.Row row) + { + Dictionary rowFields = new(); + foreach (ACD.Field field in row.GetFields()) + { + // POC: do not set null values + // POC: we are not filtering by the layer visible fields + if (FieldValueToSpeckle(row, field) is object value) + { + rowFields[field.Name] = value; + } + } + + return rowFields; + } + + private object? FieldValueToSpeckle(ACD.Row row, ACD.Field field) + { + switch (field.FieldType) + { + // these FieldTypes are not properly supported through API + case ACD.FieldType.Geometry: + case ACD.FieldType.Raster: + case ACD.FieldType.Blob: + case ACD.FieldType.XML: + return null; + + case ACD.FieldType.DateOnly: + case ACD.FieldType.TimeOnly: + case ACD.FieldType.TimestampOffset: + return row[field.Name]?.ToString(); + + default: + return row[field.Name]; + } + } +} diff --git a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/Raw/AttributeToSpeckleConverter.cs b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/Raw/AttributeToSpeckleConverter.cs deleted file mode 100644 index 5c106b8b3..000000000 --- a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/Raw/AttributeToSpeckleConverter.cs +++ /dev/null @@ -1,51 +0,0 @@ -using ArcGIS.Core.Data; -using Speckle.Converters.Common.Objects; -using Speckle.Sdk.Models; - -namespace Speckle.Converters.ArcGIS3.ToSpeckle.Raw; - -public class AttributesToSpeckleConverter : ITypedConverter -{ - public AttributesToSpeckleConverter() { } - - public Base Convert(Row target) - { - Base attributes = new(); - IReadOnlyList fields = target.GetFields(); - foreach (Field field in fields) - { - if (field.FieldType == FieldType.Geometry) - { - continue; // ignore fields with geometry - } - else - { - // TODO: currently we are setting raster, blob, and xml fields to null with this logic. Why are these sent as null and not skipped over? - attributes[field.Name] = FieldValueToSpeckle(target, field); - } - } - - return attributes; - } - - // TODO: often skipping over geometry, raster, blob, and xml fields. This happens in vector layer conversion as well. Why are we returning null here? We should encapsulate this in a field converter util. - private object? FieldValueToSpeckle(Row row, Field field) - { - switch (field.FieldType) - { - // these FieldTypes are not properly supported through API - case FieldType.Raster: - case FieldType.Blob: - case FieldType.XML: - return null; - - case FieldType.DateOnly: - case FieldType.TimeOnly: - case FieldType.TimestampOffset: - return row[field.Name]?.ToString(); - - default: - return row[field.Name]; - } - } -} diff --git a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/Raw/EnvelopBoxToSpeckleConverter.cs b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/Raw/EnvelopBoxToSpeckleConverter.cs deleted file mode 100644 index a871117ef..000000000 --- a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/Raw/EnvelopBoxToSpeckleConverter.cs +++ /dev/null @@ -1,60 +0,0 @@ -using ArcGIS.Core.Geometry; -using Speckle.Converters.Common; -using Speckle.Converters.Common.Objects; -using Speckle.Objects.Primitive; - -namespace Speckle.Converters.ArcGIS3.ToSpeckle.Raw; - -public class EnvelopToSpeckleConverter : ITypedConverter -{ - private readonly IConverterSettingsStore _settingsStore; - private readonly ITypedConverter _pointConverter; - - public EnvelopToSpeckleConverter( - IConverterSettingsStore settingsStore, - ITypedConverter pointConverter - ) - { - _settingsStore = settingsStore; - _pointConverter = pointConverter; - } - - public SOG.Box Convert(Envelope target) - { - MapPoint pointMin = new MapPointBuilderEx( - target.XMin, - target.YMin, - target.ZMin, - _settingsStore.Current.ActiveCRSoffsetRotation.SpatialReference - ).ToGeometry(); - MapPoint pointMax = new MapPointBuilderEx( - target.XMax, - target.YMax, - target.ZMax, - _settingsStore.Current.ActiveCRSoffsetRotation.SpatialReference - ).ToGeometry(); - SOG.Point minPtSpeckle = _pointConverter.Convert(pointMin); - SOG.Point maxPtSpeckle = _pointConverter.Convert(pointMax); - - var units = _settingsStore.Current.SpeckleUnits; - - SOG.Plane plane = - new() - { - origin = minPtSpeckle, - normal = new SOG.Vector(0, 0, 1, units), - xdir = new SOG.Vector(1, 0, 0, units), - ydir = new SOG.Vector(0, 1, 0, units), - units = units - }; - - return new SOG.Box() - { - plane = plane, - xSize = new Interval { start = minPtSpeckle.x, end = maxPtSpeckle.x }, - ySize = new Interval { start = minPtSpeckle.y, end = maxPtSpeckle.y }, - zSize = new Interval { start = minPtSpeckle.z, end = maxPtSpeckle.z }, - units = _settingsStore.Current.SpeckleUnits - }; - } -} diff --git a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/Raw/GisFeatureToSpeckleConverter.cs b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/Raw/GisFeatureToSpeckleConverter.cs deleted file mode 100644 index 87ac8669b..000000000 --- a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/Raw/GisFeatureToSpeckleConverter.cs +++ /dev/null @@ -1,198 +0,0 @@ -using ArcGIS.Core.Data; -using Speckle.Converters.ArcGIS3.Utils; -using Speckle.Converters.Common; -using Speckle.Converters.Common.Objects; -using Speckle.Objects; -using Speckle.Sdk.Common.Exceptions; -using Speckle.Sdk.Models; - -namespace Speckle.Converters.ArcGIS3.ToSpeckle.Raw; - -public class GisFeatureToSpeckleConverter : ITypedConverter<(Row, string), IGisFeature> -{ - private readonly ITypedConverter _pointConverter; - private readonly ITypedConverter> _multiPointConverter; - private readonly ITypedConverter> _polylineConverter; - private readonly ITypedConverter> _polygonConverter; - private readonly ITypedConverter> _multipatchConverter; - private readonly ITypedConverter _attributeConverter; - private readonly IConverterSettingsStore _settingsStore; - - public GisFeatureToSpeckleConverter( - ITypedConverter pointConverter, - ITypedConverter> multiPointConverter, - ITypedConverter> polylineConverter, - ITypedConverter> polygonConverter, - ITypedConverter> multipatchConverter, - ITypedConverter attributeConverter, - IConverterSettingsStore settingsStore - ) - { - _pointConverter = pointConverter; - _multiPointConverter = multiPointConverter; - _polylineConverter = polylineConverter; - _polygonConverter = polygonConverter; - _multipatchConverter = multipatchConverter; - _attributeConverter = attributeConverter; - _settingsStore = settingsStore; - } - - private List GetPolygonDisplayMeshes(List polygons) - { - List displayVal = new(); - foreach (SGIS.PolygonGeometry polygon in polygons) - { - // POC: check for voids, we cannot generate display value correctly if any of the polygons have voids - // Return meshed boundary for now, ignore voids - // if (polygon.voids.Count > 0) - // { - // return new(); - // } - - // ensure counter-clockwise orientation for up-facing mesh faces - bool isClockwise = polygon.boundary.IsClockwisePolygon(); - List boundaryPts = polygon.boundary.GetPoints(); - if (isClockwise) - { - boundaryPts.Reverse(); - } - - // generate Mesh - List faces = new() { boundaryPts.Count }; - faces.AddRange(Enumerable.Range(0, boundaryPts.Count).ToList()); - SOG.Mesh mesh = - new() - { - vertices = boundaryPts.SelectMany(x => new List { x.x, x.y, x.z }).ToList(), - faces = faces, - units = _settingsStore.Current.SpeckleUnits - }; - displayVal.Add(mesh); - } - - return displayVal; - } - - private List GetMultipatchDisplayMeshes(List multipatch) - { - List displayVal = new(); - foreach (SGIS.GisMultipatchGeometry geo in multipatch) - { - SOG.Mesh displayMesh = - new() - { - vertices = geo.vertices, - faces = geo.faces, - units = _settingsStore.Current.SpeckleUnits - }; - displayVal.Add(displayMesh); - } - - return displayVal; - } - - private List GetDisplayMeshes(List geometry) - { - List displayValue = new(); - List polygons = new(); - List multipatches = new(); - foreach (Base geo in geometry) - { - if (geo is SGIS.GisMultipatchGeometry multipatch) - { - multipatches.Add(multipatch); - } - else if (geo is SGIS.PolygonGeometry polygon) - { - polygons.Add(polygon); - } - } - - displayValue.AddRange(GetPolygonDisplayMeshes(polygons)); - displayValue.AddRange(GetMultipatchDisplayMeshes(multipatches)); - return displayValue; - } - - public IGisFeature Convert((Row, string) target) - { - Row row = target.Item1; - string appId = target.Item2; - - // get attributes - Base attributes = _attributeConverter.Convert(row); - - bool hasGeometry = false; - string geometryField = "Shape"; - foreach (Field field in row.GetFields()) - { - // POC: check for all possible reserved Shape names - if (field.FieldType == FieldType.Geometry) // ignore the field with geometry itself - { - hasGeometry = true; - geometryField = field.Name; - } - } - - // return GisFeatures that don't have geometry - if (!hasGeometry) - { - return new SGIS.GisNonGeometricFeature() { attributes = attributes, applicationId = appId }; - } - - var shape = (ACG.Geometry)row[geometryField]; - switch (shape) - { - case ACG.MapPoint point: - SOG.Point specklePoint = _pointConverter.Convert(point); - return new SGIS.GisPointFeature() - { - geometry = new() { specklePoint }, - attributes = attributes, - applicationId = appId - }; - - case ACG.Multipoint multipoint: - List specklePoints = _multiPointConverter.Convert(multipoint).ToList(); - return new SGIS.GisPointFeature() - { - geometry = specklePoints, - attributes = attributes, - applicationId = appId - }; - - case ACG.Polyline polyline: - List polylines = _polylineConverter.Convert(polyline).ToList(); - return new SGIS.GisPolylineFeature() - { - geometry = polylines, - attributes = attributes, - applicationId = appId - }; - - case ACG.Polygon polygon: - List polygons = _polygonConverter.Convert(polygon).ToList(); - List meshes = GetPolygonDisplayMeshes(polygons); - return new SGIS.GisPolygonFeature() - { - geometry = polygons, - displayValue = meshes, - attributes = attributes, - applicationId = appId - }; - - case ACG.Multipatch multipatch: - List geometry = _multipatchConverter.Convert(multipatch).ToList(); - List display = GetDisplayMeshes(geometry); - return new SGIS.GisMultipatchFeature() - { - geometry = geometry, - displayValue = display, - attributes = attributes, - applicationId = appId - }; - - default: - throw new ValidationException($"No geometry conversion found for {shape.GetType().Name}"); - } - } -} diff --git a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/Raw/MultipatchFeatureToSpeckleConverter.cs b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/Raw/MultipatchFeatureToSpeckleConverter.cs index 46ba7c663..ba589b4d7 100644 --- a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/Raw/MultipatchFeatureToSpeckleConverter.cs +++ b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/Raw/MultipatchFeatureToSpeckleConverter.cs @@ -1,15 +1,13 @@ -using Speckle.Converters.ArcGIS3.Utils; using Speckle.Converters.Common; using Speckle.Converters.Common.Objects; -using Speckle.Sdk.Models; using ValidationException = System.ComponentModel.DataAnnotations.ValidationException; namespace Speckle.Converters.ArcGIS3.ToSpeckle.Raw; /// -/// Converts Multipatch objects into a list containing some combination of GisMultipatchGeometry or PolygonGeometry3d objects +/// Converts Multipatch objects into Meshes /// -public class MultipatchFeatureToSpeckleConverter : ITypedConverter> +public class MultipatchFeatureToSpeckleConverter : ITypedConverter> { private readonly IConverterSettingsStore _settingsStore; private readonly ITypedConverter _pointConverter; @@ -23,11 +21,11 @@ public class MultipatchFeatureToSpeckleConverter : ITypedConverter Convert(ACG.Multipatch target) + public IReadOnlyList Convert(ACG.Multipatch target) { - List converted = new(); + List converted = new(); // placeholder, needs to be declared in order to be used in the Ring patch type - SGIS.PolygonGeometry3d polygonGeom = new() { units = _settingsStore.Current.SpeckleUnits }; + //SOG.Polygon polygonGeom = new() { units = _settingsStore.Current.SpeckleUnits }; // convert and store all multipatch points per Part List> allPoints = new(); @@ -43,88 +41,128 @@ public class MultipatchFeatureToSpeckleConverter : ITypedConverter points = new(); + int ptStartIndex = target.GetPatchStartPointIndex(i); + for (int ptIdx = ptStartIndex; ptIdx < ptStartIndex + target.GetPatchPointCount(i); ptIdx++) { - SGIS.GisMultipatchGeometry multipatch = target.CompleteMultipatchTriangleStrip(allPoints, idx); - multipatch.units = _settingsStore.Current.SpeckleUnits; - converted.Add(multipatch); + points.Add(target.Points[ptIdx]); } - else if (patchType == ACG.PatchType.Triangles) - { - SGIS.GisMultipatchGeometry multipatch = target.CompleteMultipatchTriangles(allPoints, idx); - multipatch.units = _settingsStore.Current.SpeckleUnits; - converted.Add(multipatch); - } - else if (patchType == ACG.PatchType.TriangleFan) - { - SGIS.GisMultipatchGeometry multipatch = target.CompleteMultipatchTriangleFan(allPoints, idx); - multipatch.units = _settingsStore.Current.SpeckleUnits; - converted.Add(multipatch); - } - // in case of RingMultipatch - return PolygonGeometry3d - // the following Patch Parts cannot be pushed to external method, as they will possibly, add voids/rings to the same GisPolygon - else if (patchType == ACG.PatchType.FirstRing) - { - // chech if there were already Polygons, add them to list - if (polygonGeom.boundary != null) - { - converted.Add(polygonGeom); - } - // first ring means a start of a new PolygonGeometry3d - polygonGeom = new() { voids = new List(), units = _settingsStore.Current.SpeckleUnits }; - List pointCoords = allPoints[idx].SelectMany(x => new List() { x.x, x.y, x.z }).ToList(); - - SOG.Polyline polyline = new() { value = pointCoords, units = _settingsStore.Current.SpeckleUnits }; - polygonGeom.boundary = polyline; - - // if it's already the last part, add to list - if (idx == target.PartCount - 1) - { - converted.Add(polygonGeom); - } - } - else if (patchType == ACG.PatchType.Ring) + switch (patchType) { - List pointCoords = allPoints[idx].SelectMany(x => new List() { x.x, x.y, x.z }).ToList(); - SOG.Polyline polyline = new() { value = pointCoords, units = _settingsStore.Current.SpeckleUnits }; + case ACG.PatchType.TriangleStrip: + SOG.Mesh triangleStripPatch = GetMeshFromTriangleStripPatch(points); + converted.Add(triangleStripPatch); + break; + case ACG.PatchType.Triangles: + SOG.Mesh trianglesPatch = GetMeshFromTrianglesPatch(points); + converted.Add(trianglesPatch); + break; + case ACG.PatchType.TriangleFan: + SOG.Mesh triangleFanPatch = GetMeshFromTriangleFanPatch(points); + converted.Add(triangleFanPatch); + break; + case ACG.PatchType.FirstRing: + SOG.Mesh firstRingPatch = GetMeshFromFirstRingPatch(points); + converted.Add(firstRingPatch); + break; - // every outer ring is oriented clockwise - bool isClockwise = polyline.IsClockwisePolygon(); - if (!isClockwise) - { - // add void to existing polygon - polygonGeom.voids.Add(polyline); - } - else - { - // add existing polygon to list, start a new polygon with a boundary - converted.Add(polygonGeom); - polygonGeom = new() - { - voids = new List(), - boundary = polyline, - units = _settingsStore.Current.SpeckleUnits - }; - } - // if it's already the last part, add to list - if (idx == target.PartCount - 1) - { - converted.Add(polygonGeom); - } - } - else - { - throw new ValidationException($"Patch type {patchType} is not supported"); + default: + throw new ValidationException($"{patchType} patch type is not supported"); } } return converted; } + + private SOG.Mesh GetMeshFromTriangleStripPatch(List points) + { + List pointCoords = points.SelectMany(x => new List() { x.X, x.Y, x.Z }).ToList(); + List faces = new(); + List vertices = new(); + + for (int i = 0; i < points.Count; i++) + { + if (i >= 2) // every new point adds a triangle + { + faces.AddRange(new List() { 3, i - 2, i - 1, i }); + vertices.AddRange(pointCoords.GetRange(3 * (i - 2), 9).ToList()); + } + } + + return new() + { + faces = faces, + vertices = vertices, + units = _settingsStore.Current.SpeckleUnits + }; + } + + private SOG.Mesh GetMeshFromTrianglesPatch(List points) + { + List pointCoords = points.SelectMany(x => new List() { x.X, x.Y, x.Z }).ToList(); + List faces = new(); + List vertices = new(); + + for (int i = 0; i < points.Count; i++) + { + if (i >= 2 && (i + 1) % 3 == 0) // every 3 new points is a new triangle + { + faces.AddRange(new List() { 3, i - 2, i - 1, i }); + vertices.AddRange(pointCoords.GetRange(3 * (i - 2), 9).ToList()); + } + } + + return new() + { + faces = faces, + vertices = vertices, + units = _settingsStore.Current.SpeckleUnits + }; + } + + private SOG.Mesh GetMeshFromTriangleFanPatch(List points) + { + List pointCoords = points.SelectMany(x => new List() { x.X, x.Y, x.Z }).ToList(); + List faces = new(); + List vertices = new(); + + for (int i = 0; i < points.Count; i++) + { + if (i >= 2) // every new point adds a triangle (originates from 0) + { + faces.AddRange(new List() { 3, 0, i - 1, i }); + vertices.AddRange(pointCoords.GetRange(2 * (i - 2), 6).ToList()); + } + } + + return new() + { + faces = faces, + vertices = vertices, + units = _settingsStore.Current.SpeckleUnits + }; + } + + // first ring means a start of a new PolygonGeometry3d + // POC: guess we are skipping inner rings for now, though we could send as polylines + private SOG.Mesh GetMeshFromFirstRingPatch(List points) + { + List pointCoords = points.SelectMany(x => new List() { x.X, x.Y, x.Z }).ToList(); + List faces = Enumerable.Range(0, pointCoords.Count).ToList(); + + return new() + { + faces = faces, + vertices = pointCoords, + units = _settingsStore.Current.SpeckleUnits + }; + } } diff --git a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/Raw/PointToSpeckleConverter.cs b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/Raw/PointToSpeckleConverter.cs index 657077906..3716ef3ab 100644 --- a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/Raw/PointToSpeckleConverter.cs +++ b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/Raw/PointToSpeckleConverter.cs @@ -1,12 +1,10 @@ -using System.ComponentModel.DataAnnotations; -using ArcGIS.Core.Geometry; using Speckle.Converters.Common; using Speckle.Converters.Common.Objects; -using Speckle.Sdk; +using Speckle.Sdk.Common.Exceptions; namespace Speckle.Converters.ArcGIS3.ToSpeckle.Raw; -public class PointToSpeckleConverter : ITypedConverter +public class PointToSpeckleConverter : ITypedConverter { private readonly IConverterSettingsStore _settingsStore; @@ -15,48 +13,42 @@ public class PointToSpeckleConverter : ITypedConverter _settingsStore = settingsStore; } - public SOG.Point Convert(MapPoint target) + public SOG.Point Convert(ACG.MapPoint target) { + ACG.MapPoint point; try { // reproject to Active CRS - if ( - GeometryEngine.Instance.Project(target, _settingsStore.Current.ActiveCRSoffsetRotation.SpatialReference) - is not MapPoint reprojectedPt - ) - { - throw new ValidationException( - $"Conversion to Spatial Reference {_settingsStore.Current.ActiveCRSoffsetRotation.SpatialReference.Name} failed" - ); - } - - if ( - Double.IsNaN(reprojectedPt.X) - || Double.IsInfinity(reprojectedPt.X) - || Double.IsNaN(reprojectedPt.Y) - || Double.IsInfinity(reprojectedPt.Y) - ) - { - throw new ValidationException( - $"Conversion to Spatial Reference {_settingsStore.Current.ActiveCRSoffsetRotation.SpatialReference.Name} failed: coordinates undefined" - ); - } - - // convert to Speckle Pt - SOG.Point reprojectedSpecklePt = - new(reprojectedPt.X, reprojectedPt.Y, reprojectedPt.Z, _settingsStore.Current.SpeckleUnits); - SOG.Point scaledMovedRotatedPoint = _settingsStore.Current.ActiveCRSoffsetRotation.OffsetRotateOnSend( - reprojectedSpecklePt, - _settingsStore.Current.SpeckleUnits - ); - return scaledMovedRotatedPoint; + point = (ACG.MapPoint) + ACG.GeometryEngine.Instance.Project(target, _settingsStore.Current.ActiveCRSoffsetRotation.SpatialReference); } - catch (ArgumentException ex) + catch (ArgumentNullException anEx) { - throw new SpeckleException( - $"Conversion to Spatial Reference {_settingsStore.Current.ActiveCRSoffsetRotation.SpatialReference.Name} failed", - ex + throw new ConversionException("MapPoint was null", anEx); + } + catch (ArgumentException aEx) + { + throw new ConversionException("Spatial reference was not supported", aEx); + } + catch (NotImplementedException niEx) + { + throw new ConversionException("", niEx); + } + + if (double.IsNaN(point.X) || double.IsInfinity(point.X) || double.IsNaN(point.Y) || double.IsInfinity(point.Y)) + { + throw new ConversionException( + $"Conversion to Spatial Reference {_settingsStore.Current.ActiveCRSoffsetRotation.SpatialReference.Name} failed: coordinates undefined" ); } + + // convert to Speckle Pt + SOG.Point reprojectedSpecklePt = new(point.X, point.Y, point.Z, _settingsStore.Current.SpeckleUnits); + SOG.Point scaledMovedRotatedPoint = _settingsStore.Current.ActiveCRSoffsetRotation.OffsetRotateOnSend( + reprojectedSpecklePt, + _settingsStore.Current.SpeckleUnits + ); + + return scaledMovedRotatedPoint; } } diff --git a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/Raw/PolygonFeatureToSpeckleConverter.cs b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/Raw/PolygonFeatureToSpeckleConverter.cs index ddfaff318..1e3273e5d 100644 --- a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/Raw/PolygonFeatureToSpeckleConverter.cs +++ b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/Raw/PolygonFeatureToSpeckleConverter.cs @@ -1,64 +1,41 @@ -using Speckle.Converters.Common; using Speckle.Converters.Common.Objects; -using Speckle.Objects.GIS; using Speckle.Sdk.Common.Exceptions; namespace Speckle.Converters.ArcGIS3.ToSpeckle.Raw; -public class PolygonFeatureToSpeckleConverter : ITypedConverter> +/// +/// Converts a Polygon feature to a list of polylines from the polygon boundary and inner loops. +/// This is a placeholder conversion since we don't have a polygon class or meshing strategy for interior loops yet. +/// +public class PolygonFeatureToSpeckleConverter : ITypedConverter> { private readonly ITypedConverter _segmentConverter; - private readonly IConverterSettingsStore _settingsStore; - public PolygonFeatureToSpeckleConverter( - ITypedConverter segmentConverter, - IConverterSettingsStore settingsStore - ) + public PolygonFeatureToSpeckleConverter(ITypedConverter segmentConverter) { _segmentConverter = segmentConverter; - _settingsStore = settingsStore; } - public IReadOnlyList Convert(ACG.Polygon target) + public IReadOnlyList Convert(ACG.Polygon target) { // https://pro.arcgis.com/en/pro-app/latest/sdk/api-reference/topic30235.html - List polygonList = new(); int partCount = target.PartCount; + List parts = new(partCount); if (partCount == 0) { throw new ValidationException("ArcGIS Polygon contains no parts"); } - PolygonGeometry? polygon = null; - - // test each part for "exterior ring" - for (int idx = 0; idx < partCount; idx++) + for (int i = 0; i < partCount; i++) { - ACG.ReadOnlySegmentCollection segmentCollection = target.Parts[idx]; + // get the part polyline + ACG.ReadOnlySegmentCollection segmentCollection = target.Parts[i]; SOG.Polyline polyline = _segmentConverter.Convert(segmentCollection); - - bool isExteriorRing = target.IsExteriorRing(idx); - if (isExteriorRing) - { - polygon = new() - { - boundary = polyline, - voids = new List(), - units = _settingsStore.Current.SpeckleUnits - }; - polygonList.Add(polygon); - } - else // interior part - { - if (polygon == null) - { - throw new ValidationException("Invalid ArcGIS Polygon. Interior part preceeding the exterior ring."); - } - polygon.voids.Add(polyline); - } + // POC: we could create a mesh from exterior polyline: target.IsExteriorRing(idx) + parts.Add(polyline); } - return polygonList; + return parts; } } diff --git a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/Raw/GisRasterToSpeckleConverter.cs b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/Raw/RasterToSpeckleConverter.cs similarity index 73% rename from Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/Raw/GisRasterToSpeckleConverter.cs rename to Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/Raw/RasterToSpeckleConverter.cs index 2c4938dc8..5f3a49686 100644 --- a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/Raw/GisRasterToSpeckleConverter.cs +++ b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/Raw/RasterToSpeckleConverter.cs @@ -1,28 +1,29 @@ using ArcGIS.Core.Data.Raster; using Speckle.Converters.Common; using Speckle.Converters.Common.Objects; -using Speckle.Objects.GIS; +using Speckle.Sdk.Common.Exceptions; using Speckle.Sdk.Models; namespace Speckle.Converters.ArcGIS3.ToSpeckle.Raw; -public class GisRasterToSpeckleConverter : ITypedConverter +public class RasterToSpeckleConverter : ITypedConverter { private readonly IConverterSettingsStore _settingsStore; - public GisRasterToSpeckleConverter(IConverterSettingsStore settingsStore) + public RasterToSpeckleConverter(IConverterSettingsStore settingsStore) { _settingsStore = settingsStore; } public Base Convert(object target) => Convert((Raster)target); - private List GetRasterMeshCoords(Raster target, List> pixelValsPerBand) + public SOG.Mesh Convert(Raster target) { - List pixelsList = pixelValsPerBand[^1]; + // assisting variables var extent = target.GetExtent(); var cellSize = target.GetMeanCellSize(); + // variables to assign int bandCount = target.GetBandCount(); float xOrigin = (float)extent.XMin; float yOrigin = (float)extent.YMax; @@ -31,6 +32,124 @@ public class GisRasterToSpeckleConverter : ITypedConverter(), xOrigin, yOrigin, xSize, ySize, xResolution, yResolution, new List()); + + // prepare to construct a mesh + List newCoords = new(); + List newFaces = new(); + List newColors = new(); + + // store band values for renderer + List> pixelValsPerBand = new(); + + for (int i = 0; i < bandCount; i++) + { + // Get a pixel block for quicker reading and read from pixel top left pixel + PixelBlock block = target.CreatePixelBlock(target.GetWidth(), target.GetHeight()); + target.Read(0, 0, block); + + //RasterBandDefinition bandDef = target.GetBand(i).GetDefinition(); + //string bandName = bandDef.GetName(); + //rasterElement.band_names.Add(bandName); + + // Read 2-dimensional pixel values into 1-dimensional byte array + // TODO: format to list of float + Array pixels2D = block.GetPixelData(i, false); + List pixelsList = pixels2D.Cast().ToList(); + pixelValsPerBand.Add(pixelsList); + + /* ignoring for now, only needed for interop + // transpose to match QGIS data structure + var transposedPixelList = Enumerable + .Range(0, ySize) + .SelectMany((_, ind) => Enumerable.Range(0, xSize).Select(x => pixels2D.GetValue(x, ind))) + .ToArray(); + + rasterElement[$"@(10000){bandName}_values"] = transposedPixelList; + + // null or float for noDataValue + float? noDataVal = null; + var noDataValOriginal = bandDef.GetNoDataValue(); + if (noDataValOriginal != null) + { + noDataVal = (float)noDataValOriginal; + } + rasterElement.noDataValue.Add(noDataVal); + */ + + // construct mesh + newFaces = pixelsList + .SelectMany((_, ind) => new List() { 4, 4 * ind, 4 * ind + 1, 4 * ind + 2, 4 * ind + 3 }) + .ToList(); + + newCoords = GetRasterMeshCoords(target, pixelValsPerBand); + + // Construct colors only once, when i=last band index + // ATM, RGB for 3 or 4 bands, greyscale from 1st band for anything else + if (i == bandCount - 1) + { + newColors = GetRasterColors(bandCount, pixelValsPerBand); + } + } + + SOG.Mesh mesh = + new() + { + vertices = newCoords, + faces = newFaces, + colors = newColors, + units = _settingsStore.Current.SpeckleUnits + }; + //rasterElement.displayValue = new List() { mesh }; + + return mesh; + } + + private List GetRasterMeshCoords(Raster target, List> pixelValsPerBand) + { + List pixelsList = pixelValsPerBand[^1]; + var extent = target.GetExtent(); + var cellSize = target.GetMeanCellSize(); + + // reproject raster origin to Active CRS + var originMin = new ACG.MapPointBuilderEx( + (float)extent.XMin, + (float)extent.YMax, + 0, + target.GetSpatialReference() + ).ToGeometry(); + + var originMax = new ACG.MapPointBuilderEx( + (float)extent.XMax, + (float)extent.YMin, + 0, + target.GetSpatialReference() + ).ToGeometry(); + + if ( + ACG.GeometryEngine.Instance.Project(originMin, _settingsStore.Current.ActiveCRSoffsetRotation.SpatialReference) + is not ACG.MapPoint originMinProjected + || ACG.GeometryEngine.Instance.Project(originMax, _settingsStore.Current.ActiveCRSoffsetRotation.SpatialReference) + is not ACG.MapPoint originMaxProjected + ) + { + throw new ValidationException( + $"Conversion to Spatial Reference {_settingsStore.Current.ActiveCRSoffsetRotation.SpatialReference.Name} failed" + ); + } + + // int bandCount = target.GetBandCount(); + double xOrigin = originMinProjected.X; + double yOrigin = originMinProjected.Y; + int xSize = target.GetWidth(); + int ySize = target.GetHeight(); + double xResolution = (originMaxProjected.X - originMinProjected.X) / xSize; // (float)cellSize.Item1; + double yResolution = (originMaxProjected.Y - originMinProjected.Y) / ySize; // -1 * (float)cellSize.Item2; + List newCoords = pixelsList .SelectMany( (_, ind) => @@ -94,94 +213,4 @@ public class GisRasterToSpeckleConverter : ITypedConverter(), xOrigin, yOrigin, xSize, ySize, xResolution, yResolution, new List()); - - // prepare to construct a mesh - List newCoords = new(); - List newFaces = new(); - List newColors = new(); - - // store band values for renderer - List> pixelValsPerBand = new(); - - for (int i = 0; i < bandCount; i++) - { - // Get a pixel block for quicker reading and read from pixel top left pixel - PixelBlock block = target.CreatePixelBlock(target.GetWidth(), target.GetHeight()); - target.Read(0, 0, block); - - RasterBandDefinition bandDef = target.GetBand(i).GetDefinition(); - string bandName = bandDef.GetName(); - rasterElement.band_names.Add(bandName); - - // Read 2-dimensional pixel values into 1-dimensional byte array - // TODO: format to list of float - Array pixels2D = block.GetPixelData(i, false); - List pixelsList = pixels2D.Cast().ToList(); - pixelValsPerBand.Add(pixelsList); - - // transpose to match QGIS data structure - var transposedPixelList = Enumerable - .Range(0, ySize) - .SelectMany((_, ind) => Enumerable.Range(0, xSize).Select(x => pixels2D.GetValue(x, ind))) - .ToArray(); - - rasterElement[$"@(10000){bandName}_values"] = transposedPixelList; - - // null or float for noDataValue - float? noDataVal = null; - var noDataValOriginal = bandDef.GetNoDataValue(); - if (noDataValOriginal != null) - { - noDataVal = (float)noDataValOriginal; - } - rasterElement.noDataValue.Add(noDataVal); - - // construct mesh - newFaces = pixelsList - .SelectMany((_, ind) => new List() { 4, 4 * ind, 4 * ind + 1, 4 * ind + 2, 4 * ind + 3 }) - .ToList(); - - newCoords = GetRasterMeshCoords(target, pixelValsPerBand); - - // Construct colors only once, when i=last band index - // ATM, RGB for 3 or 4 bands, greyscale from 1st band for anything else - if (i == bandCount - 1) - { - newColors = GetRasterColors(bandCount, pixelValsPerBand); - } - } - - SOG.Mesh mesh = - new() - { - vertices = newCoords, - faces = newFaces, - colors = newColors, - units = _settingsStore.Current.SpeckleUnits - }; - rasterElement.displayValue = new List() { mesh }; - - return rasterElement; - } } diff --git a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/TopLevel/CoreObjectsBaseToSpeckleTopLevelConverter.cs b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/TopLevel/CoreObjectsBaseToSpeckleTopLevelConverter.cs new file mode 100644 index 000000000..75c4a5bf2 --- /dev/null +++ b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/TopLevel/CoreObjectsBaseToSpeckleTopLevelConverter.cs @@ -0,0 +1,52 @@ +using Speckle.Converters.ArcGIS3.ToSpeckle.Helpers; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Objects.Data; +using Speckle.Sdk.Models; + +namespace Speckle.Converters.ArcGIS3.ToSpeckle.TopLevel; + +[NameAndRankValue(typeof(AC.CoreObjectsBase), 0)] +public class CoreObjectsBaseToSpeckleTopLevelConverter : IToSpeckleTopLevelConverter +{ + private readonly DisplayValueExtractor _displayValueExtractor; + private readonly PropertiesExtractor _propertiesExtractor; + private readonly IConverterSettingsStore _settingsStore; + + public CoreObjectsBaseToSpeckleTopLevelConverter( + DisplayValueExtractor displayValueExtractor, + PropertiesExtractor propertiesExtractor, + IConverterSettingsStore settingsStore + ) + { + _displayValueExtractor = displayValueExtractor; + _propertiesExtractor = propertiesExtractor; + _settingsStore = settingsStore; + } + + public Base Convert(object target) => Convert((AC.CoreObjectsBase)target); + + private ArcgisObject Convert(AC.CoreObjectsBase target) + { + string type = target.GetType().Name; + + // get display value + List display = _displayValueExtractor.GetDisplayValue(target).ToList(); + + // get properties + Dictionary properties = _propertiesExtractor.GetProperties(target); + + ArcgisObject result = + new() + { + name = type, + type = type, + displayValue = display, + properties = properties, + units = _settingsStore.Current.SpeckleUnits, + applicationId = "" + }; + + return result; + } +} diff --git a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/TopLevel/PointcloudLayerToSpeckleConverter.cs b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/TopLevel/PointcloudLayerToSpeckleConverter.cs deleted file mode 100644 index 978d036b4..000000000 --- a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/TopLevel/PointcloudLayerToSpeckleConverter.cs +++ /dev/null @@ -1,112 +0,0 @@ -using ArcGIS.Core.CIM; -using ArcGIS.Core.Data.Analyst3D; -using ArcGIS.Desktop.Mapping; -using Speckle.Converters.ArcGIS3.Utils; -using Speckle.Converters.Common; -using Speckle.Converters.Common.Objects; -using Speckle.Sdk.Models; - -namespace Speckle.Converters.ArcGIS3.ToSpeckle.TopLevel; - -[NameAndRankValue(nameof(LasDatasetLayer), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] -public class PointCloudToSpeckleConverter - : IToSpeckleTopLevelConverter, - ITypedConverter -{ - private readonly ITypedConverter _pointConverter; - private readonly ITypedConverter _boxConverter; - private readonly IConverterSettingsStore _settingsStore; - - public PointCloudToSpeckleConverter( - ITypedConverter pointConverter, - ITypedConverter boxConverter, - IConverterSettingsStore settingsStore - ) - { - _pointConverter = pointConverter; - _boxConverter = boxConverter; - _settingsStore = settingsStore; - } - - private int GetPointColor(LasPoint pt, object renderer) - { - // get color - int color = 0; - string classCode = pt.ClassCode.ToString(); - if (renderer is CIMTinUniqueValueRenderer uniqueRenderer) - { - foreach (CIMUniqueValueGroup group in uniqueRenderer.Groups) - { - if (color != 0) - { - break; - } - foreach (CIMUniqueValueClass groupClass in group.Classes) - { - if (color != 0) - { - break; - } - for (int i = 0; i < groupClass.Values.Length; i++) - { - if (classCode == groupClass.Values[i].FieldValues[0]) - { - CIMColor symbolColor = groupClass.Symbol.Symbol.GetColor(); - color = symbolColor.CIMColorToInt(); - break; - } - } - } - } - } - else - { - color = pt.RGBColor.RGBToInt(); - } - return color; - } - - public Base Convert(object target) - { - return Convert((LasDatasetLayer)target); - } - - public SGIS.VectorLayer Convert(LasDatasetLayer target) - { - SGIS.VectorLayer speckleLayer = - new() { nativeGeomType = target.MapLayerType.ToString(), geomType = GISLayerGeometryType.POINTCLOUD }; - - // prepare data for pointcloud - List specklePts = new(); - List values = new(); - List speckleColors = new(); - var renderer = target.GetRenderers()[0]; - - using (LasPointCursor ptCursor = target.SearchPoints(new LasPointFilter())) - { - while (ptCursor.MoveNext()) - { - using (LasPoint pt = ptCursor.Current) - { - specklePts.Add(_pointConverter.Convert(pt.ToMapPoint())); - values.Add(pt.ClassCode); - int color = GetPointColor(pt, renderer); - speckleColors.Add(color); - } - } - } - - SOG.Pointcloud cloud = - new() - { - points = specklePts.SelectMany(pt => new List() { pt.x, pt.y, pt.z }).ToList(), - colors = speckleColors, - sizes = values, - bbox = _boxConverter.Convert(target.QueryExtent()), - units = _settingsStore.Current.SpeckleUnits - }; - - speckleLayer.elements.Add(cloud); - return speckleLayer; - } -} diff --git a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/TopLevel/RasterLayerToSpeckleConverter.cs b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/TopLevel/RasterLayerToSpeckleConverter.cs deleted file mode 100644 index c806ba9a3..000000000 --- a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/TopLevel/RasterLayerToSpeckleConverter.cs +++ /dev/null @@ -1,55 +0,0 @@ -using ArcGIS.Core.Data.Raster; -using Speckle.Converters.Common; -using Speckle.Converters.Common.Objects; -using Speckle.Objects.GIS; -using Speckle.Sdk.Models; -using RasterLayer = ArcGIS.Desktop.Mapping.RasterLayer; - -namespace Speckle.Converters.ArcGIS3.ToSpeckle.TopLevel; - -[NameAndRankValue(nameof(RasterLayer), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] -public class RasterLayerToSpeckleConverter : IToSpeckleTopLevelConverter, ITypedConverter -{ - private readonly ITypedConverter _gisRasterConverter; - private readonly IConverterSettingsStore _settingsStore; - - public RasterLayerToSpeckleConverter( - ITypedConverter gisRasterConverter, - IConverterSettingsStore settingsStore - ) - { - _gisRasterConverter = gisRasterConverter; - _settingsStore = settingsStore; - } - - public Base Convert(object target) - { - return Convert((RasterLayer)target); - } - - public SGIS.RasterLayer Convert(RasterLayer target) - { - var speckleLayer = new SGIS.RasterLayer(); - - // layer native crs (for writing properties e.g. resolution, origin etc.) - var spatialRefRaster = target.GetSpatialReference(); - // get active map CRS if layer CRS is empty - if (spatialRefRaster.Unit is null) - { - spatialRefRaster = _settingsStore.Current.ActiveCRSoffsetRotation.SpatialReference; - } - speckleLayer.rasterCrs = new CRS - { - wkt = spatialRefRaster.Wkt, - name = spatialRefRaster.Name, - units_native = spatialRefRaster.Unit.ToString(), - }; - - // write details about the Raster - RasterElement element = _gisRasterConverter.Convert(target.GetRaster()); - element.applicationId = $"{target.URI}_0"; - speckleLayer.elements.Add(element); - - return speckleLayer; - } -} diff --git a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/TopLevel/TableToSpeckleConverter.cs b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/TopLevel/TableToSpeckleConverter.cs deleted file mode 100644 index b3d8a3a9b..000000000 --- a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/TopLevel/TableToSpeckleConverter.cs +++ /dev/null @@ -1,81 +0,0 @@ -using ArcGIS.Core.Data; -using ArcGIS.Desktop.Mapping; -using Speckle.Converters.Common; -using Speckle.Converters.Common.Objects; -using Speckle.Objects; -using Speckle.Objects.GIS; -using Speckle.Sdk.Models; - -namespace Speckle.Converters.ArcGIS3.ToSpeckle.TopLevel; - -[NameAndRankValue(nameof(StandaloneTable), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] -public class StandaloneTableToSpeckleConverter - : IToSpeckleTopLevelConverter, - ITypedConverter -{ - private readonly ITypedConverter<(Row, string), IGisFeature> _gisFeatureConverter; - - public StandaloneTableToSpeckleConverter(ITypedConverter<(Row, string), IGisFeature> gisFeatureConverter) - { - _gisFeatureConverter = gisFeatureConverter; - } - - public Base Convert(object target) - { - return Convert((StandaloneTable)target); - } - - public VectorLayer Convert(StandaloneTable target) - { - VectorLayer speckleLayer = new() { }; - - // get feature class fields - var attributes = new Base(); - var displayTable = target as IDisplayTable; - HashSet visibleFields = new(); - foreach (FieldDescription field in displayTable.GetFieldDescriptions()) - { - if (field.IsVisible) - { - visibleFields.Add(field.Name); - string name = field.Name; - attributes[name] = (int)field.Type; - } - } - - speckleLayer.attributes = attributes; - string spekleGeometryType = "None"; - - int count = 1; - using (RowCursor rowCursor = displayTable.Search()) - { - while (rowCursor.MoveNext()) - { - // Same IDisposable issue appears to happen on Row class too. Docs say it should always be disposed of manually by the caller. - using (Row row = rowCursor.Current) - { - string appId = $"{target.URI}_{count}"; - IGisFeature element = _gisFeatureConverter.Convert((row, appId)); - - // create new element attributes from the existing attributes, based on the vector layer visible fields - // POC: this should be refactored to store the feature layer properties in the context stack, so this logic can be done in the gisFeatureConverter - Base elementAttributes = new(); - foreach (string elementAtt in element.attributes.DynamicPropertyKeys) - { - if (visibleFields.Contains(elementAtt)) - { - elementAttributes[elementAtt] = element.attributes[elementAtt]; - } - } - - element.attributes = elementAttributes; - speckleLayer.elements.Add((Base)element); - } - count++; - } - } - - speckleLayer.geomType = spekleGeometryType; - return speckleLayer; - } -} diff --git a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/TopLevel/VectorLayerToSpeckleConverter.cs b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/TopLevel/VectorLayerToSpeckleConverter.cs deleted file mode 100644 index eff7ea7d8..000000000 --- a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/TopLevel/VectorLayerToSpeckleConverter.cs +++ /dev/null @@ -1,97 +0,0 @@ -using ArcGIS.Core.Data; -using ArcGIS.Desktop.Mapping; -using Speckle.Converters.ArcGIS3.Utils; -using Speckle.Converters.Common; -using Speckle.Converters.Common.Objects; -using Speckle.Objects; -using Speckle.Objects.GIS; -using Speckle.Sdk.Models; - -namespace Speckle.Converters.ArcGIS3.ToSpeckle.TopLevel; - -[NameAndRankValue(nameof(FeatureLayer), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] -public class VectorLayerToSpeckleConverter : IToSpeckleTopLevelConverter, ITypedConverter -{ - private readonly ITypedConverter<(Row, string), IGisFeature> _gisFeatureConverter; - - public VectorLayerToSpeckleConverter(ITypedConverter<(Row, string), IGisFeature> gisFeatureConverter) - { - _gisFeatureConverter = gisFeatureConverter; - } - - public Base Convert(object target) - { - return Convert((FeatureLayer)target); - } - - public VectorLayer Convert(FeatureLayer target) - { - VectorLayer speckleLayer = new(); - - // get feature class fields - var allLayerAttributes = new Base(); - var dispayTable = target as IDisplayTable; - HashSet visibleFieldDescriptions = new(); - - // POC: this should be refactored into a stored method of supported/unsupported field types, since this logic is duplicated in GisFeature converter - foreach (FieldDescription field in dispayTable.GetFieldDescriptions()) - { - if (field.IsVisible) - { - string name = field.Name; - if ( - field.Type == FieldType.Geometry - || field.Type == FieldType.Raster - || field.Type == FieldType.XML - || field.Type == FieldType.Blob - ) - { - continue; - } - - visibleFieldDescriptions.Add(field.Name); - allLayerAttributes[name] = GISAttributeFieldType.FieldTypeToSpeckle(field.Type); - } - } - speckleLayer.attributes = allLayerAttributes; - - // get a simple geometry type - string spekleGeometryType = GISLayerGeometryType.LayerGeometryTypeToSpeckle(target.ShapeType); - speckleLayer.geomType = spekleGeometryType; - - // search the rows - // RowCursor is IDisposable but is not being correctly picked up by IDE warnings. - // This means we need to be carefully adding using statements based on the API documentation coming from each method/class - - int count = 1; - using (RowCursor rowCursor = target.Search()) - { - while (rowCursor.MoveNext()) - { - // Same IDisposable issue appears to happen on Row class too. Docs say it should always be disposed of manually by the caller. - using (Row row = rowCursor.Current) - { - string appId = $"{target.URI}_{count}"; - IGisFeature element = _gisFeatureConverter.Convert((row, appId)); - - // create new element attributes from the existing attributes, based on the vector layer visible fields - // POC: this should be refactored to store the feeature layer properties in the context stack, so this logic can be done in the gisFeatureConverter - Base elementAttributes = new(); - foreach (string elementAtt in element.attributes.DynamicPropertyKeys) - { - if (visibleFieldDescriptions.Contains(elementAtt)) - { - elementAttributes[elementAtt] = element.attributes[elementAtt]; - } - } - - element.attributes = elementAttributes; - speckleLayer.elements.Add((Base)element); - } - count++; - } - } - - return speckleLayer; - } -} diff --git a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/ArcGISFieldUtils.cs b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/ArcGISFieldUtils.cs index 5e48fb6de..758c99aa0 100644 --- a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/ArcGISFieldUtils.cs +++ b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/ArcGISFieldUtils.cs @@ -2,10 +2,10 @@ using System.Collections; using ArcGIS.Core.Data; using ArcGIS.Core.Data.Exceptions; using Speckle.InterfaceGenerator; -using Speckle.Objects; -using Speckle.Objects.GIS; using Speckle.Sdk; +using Speckle.Sdk.Common.Exceptions; using Speckle.Sdk.Models; +using Speckle.Sdk.Models.Collections; using Speckle.Sdk.Models.GraphTraversal; using FieldDescription = ArcGIS.Core.Data.DDL.FieldDescription; @@ -80,48 +80,54 @@ public class ArcGISFieldUtils : IArcGISFieldUtils return rowBuffer; } - public List GetFieldsFromSpeckleLayer(VectorLayer target) + public List GetFieldsFromSpeckleLayer(Collection target) { - List fields = new(); - List fieldAdded = new(); - - foreach (var field in target.attributes.GetMembers(DynamicBaseMemberType.Dynamic)) + if (target["fields"] is Dictionary attributes) { - if (!fieldAdded.Contains(field.Key) && field.Key != FID_FIELD_NAME) - { - // POC: TODO check for the forbidden characters/combinations: https://support.esri.com/en-us/knowledge-base/what-characters-should-not-be-used-in-arcgis-for-field--000005588 - try - { - if (field.Value is not null) - { - string key = field.Key; - FieldType fieldType = GISAttributeFieldType.FieldTypeToNative(field.Value); + List fields = new(); + List fieldAdded = new(); - FieldDescription fieldDescription = - new(_characterCleaner.CleanCharacters(key), fieldType) { AliasName = key }; - fields.Add(fieldDescription); - fieldAdded.Add(key); + foreach (var field in attributes) + { + if (!fieldAdded.Contains(field.Key) && field.Key != FID_FIELD_NAME) + { + // POC: TODO check for the forbidden characters/combinations: https://support.esri.com/en-us/knowledge-base/what-characters-should-not-be-used-in-arcgis-for-field--000005588 + try + { + if (field.Value is not null) + { + string key = field.Key; + FieldType fieldType = GISAttributeFieldType.FieldTypeToNative(field.Value); + + FieldDescription fieldDescription = + new(_characterCleaner.CleanCharacters(key), fieldType) { AliasName = key }; + fields.Add(fieldDescription); + fieldAdded.Add(key); + } + else + { + // log missing field + } } - else + catch (GeodatabaseFieldException) { // log missing field } } - catch (GeodatabaseFieldException) - { - // log missing field - } } + + // every feature needs Speckle_ID to be colored (before we implement native GIS renderers on Receive) + if (!fieldAdded.Contains("Speckle_ID")) + { + FieldDescription fieldDescriptionId = + new(_characterCleaner.CleanCharacters("Speckle_ID"), FieldType.String) { AliasName = "Speckle_ID" }; + fields.Add(fieldDescriptionId); + } + + return fields; } - // every feature needs Speckle_ID to be colored (before we implement native GIS renderers on Receive) - if (!fieldAdded.Contains("Speckle_ID")) - { - FieldDescription fieldDescriptionId = - new(_characterCleaner.CleanCharacters("Speckle_ID"), FieldType.String) { AliasName = "Speckle_ID" }; - fields.Add(fieldDescriptionId); - } - return fields; + throw new ValidationException("Creation of the custom fields failed: provided object is not a valid Vector Layer"); } public List<(FieldDescription, Func)> CreateFieldsFromListOfBase(List target) @@ -281,11 +287,14 @@ public class ArcGISFieldUtils : IArcGISFieldUtils List fields = new(); // Get Fields, geomType and attributeFunction - separately for GIS and non-GIS - if (listOfContextAndTrackers.FirstOrDefault().Item1.Parent?.Current is SGIS.VectorLayer vLayer) // GIS + if ( + listOfContextAndTrackers.FirstOrDefault().Item1.Parent?.Current is Collection vLayer + && vLayer["fields"] is Dictionary + ) // GIS { fields = GetFieldsFromSpeckleLayer(vLayer); fieldsAndFunctions = fields - .Select(x => (x, (Func)(y => (y as IGisFeature)?.attributes[x.Name]))) + .Select(x => (x, (Func)(y => (y?["properties"] as Base)?[x.Name]))) .ToList(); } else // non-GIS diff --git a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/CRSorigin.cs b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/CRSorigin.cs index eeb12cd01..e70eecf08 100644 --- a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/CRSorigin.cs +++ b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/CRSorigin.cs @@ -1,5 +1,3 @@ -using ArcGIS.Core.Geometry; - namespace Speckle.Converters.ArcGIS3.Utils; /// @@ -21,13 +19,13 @@ public readonly struct CRSorigin LonDegrees = lonDegrees; } - public SpatialReference CreateCustomCRS() + public ACG.SpatialReference CreateCustomCRS() { string wktString = // QGIS example: $"PROJCS[\"unknown\", GEOGCS[\"unknown\", DATUM[\"WGS_1984\", SPHEROID[\"WGS 84\", 6378137, 298.257223563], AUTHORITY[\"EPSG\", \"6326\"]], PRIMEM[\"Greenwich\", 0, AUTHORITY[\"EPSG\", \"8901\"]], UNIT[\"degree\", 0.0174532925199433]], PROJECTION[\"Transverse_Mercator\"], PARAMETER[\"latitude_of_origin\", {LatDegrees}], PARAMETER[\"central_meridian\", {LonDegrees}], PARAMETER[\"scale_factor\", 1], PARAMETER[\"false_easting\", 0], PARAMETER[\"false_northing\", 0], UNIT[\"metre\", 1, AUTHORITY[\"EPSG\", \"9001\"]], AXIS[\"Easting\", EAST], AXIS[\"Northing\", NORTH]]"; // replicating ArcGIS created custom WKT: $"PROJCS[\"SpeckleSpatialReference_latlon_{LatDegrees}_{LonDegrees}\", GEOGCS[\"GCS_WGS_1984\", DATUM[\"D_WGS_1984\", SPHEROID[\"WGS_1984\", 6378137.0, 298.257223563]], PRIMEM[\"Greenwich\", 0.0], UNIT[\"Degree\", 0.0174532925199433]], PROJECTION[\"Transverse_Mercator\"], PARAMETER[\"False_Easting\", 0.0], PARAMETER[\"False_Northing\", 0.0], PARAMETER[\"Central_Meridian\", {LonDegrees}], PARAMETER[\"Scale_Factor\", 1.0], PARAMETER[\"Latitude_Of_Origin\", {LatDegrees}], UNIT[\"Meter\", 1.0]]"; - return SpatialReferenceBuilder.CreateSpatialReference(wktString); + return ACG.SpatialReferenceBuilder.CreateSpatialReference(wktString); } } diff --git a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/CrsUtils.cs b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/CrsUtils.cs deleted file mode 100644 index 8177e3096..000000000 --- a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/CrsUtils.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Speckle.Converters.Common; -using Speckle.InterfaceGenerator; -using Speckle.Sdk.Models; - -namespace Speckle.Converters.ArcGIS3.Utils; - -[GenerateAutoInterface] -public class CrsUtils(IConverterSettingsStore settingsStore) : ICrsUtils -{ - public IDisposable? FindSetCrsDataOnReceive(Base? rootObj) - { - if (rootObj is SGIS.VectorLayer vLayer) - { - // create Spatial Reference (i.e. Coordinate Reference System - CRS) - string wktString = string.Empty; - if (vLayer.crs is not null && vLayer.crs.wkt is not null) - { - wktString = vLayer.crs.wkt; - } - - // ATM, GIS commit CRS is stored per layer, but should be moved to the Root level too, and created once per Receive - ACG.SpatialReference spatialRef = ACG.SpatialReferenceBuilder.CreateSpatialReference(wktString); - - double trueNorthRadians = System.Convert.ToDouble((vLayer.crs?.rotation == null) ? 0 : vLayer.crs.rotation); - double latOffset = System.Convert.ToDouble((vLayer.crs?.offset_y == null) ? 0 : vLayer.crs.offset_y); - double lonOffset = System.Convert.ToDouble((vLayer.crs?.offset_x == null) ? 0 : vLayer.crs.offset_x); - return settingsStore.Push(x => - x with - { - ActiveCRSoffsetRotation = new CRSoffsetRotation(spatialRef, latOffset, lonOffset, trueNorthRadians) - } - ); - } - - return null; - } -} diff --git a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/FeatureClassUtils.cs b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/FeatureClassUtils.cs index 0cd9690f1..81bd19a87 100644 --- a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/FeatureClassUtils.cs +++ b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/FeatureClassUtils.cs @@ -73,7 +73,7 @@ public class FeatureClassUtils : IFeatureClassUtils return geodatabase; } - public async Task>> GroupConversionTrackers( + public Dictionary> GroupConversionTrackers( Dictionary conversionTracker, Action onOperationProgressed ) @@ -113,10 +113,12 @@ public class FeatureClassUtils : IFeatureClassUtils $"speckle_{speckleType}_SR_{activeSR.SpatialReference.Name[..Math.Min(15, activeSR.SpatialReference.Name.Length - 1)]}_X_{xOffset}_Y_{yOffset}_North_{trueNorth}_speckleID_{parentId}"; // for gis elements, use a parent layer ID - if (item.Key.Parent?.Current is SGIS.VectorLayer vLayer) + /* + if (item.Key.Parent?.Current is SGIS.GisLayer vLayer) { uniqueKey = "speckleID_" + vLayer.id; } + */ if (!geometryGroups.TryGetValue(uniqueKey, out _)) { @@ -132,13 +134,12 @@ public class FeatureClassUtils : IFeatureClassUtils ClearExistingDataset(uniqueKey); onOperationProgressed.Invoke("Grouping features into layers", count++ / conversionTracker.Count); - await Task.Yield(); } return geometryGroups; } - public async Task CreateDatasets( + public void CreateDatasets( Dictionary conversionTracker, Dictionary> featureClassElements, Action onOperationProgressed @@ -201,7 +202,6 @@ public class FeatureClassUtils : IFeatureClassUtils } onOperationProgressed.Invoke("Writing to Database", count++ / featureClassElements.Count); - await Task.Yield(); } } @@ -281,20 +281,23 @@ public class FeatureClassUtils : IFeatureClassUtils ) { ACG.GeometryType geomType; - if (listOfContextAndTrackers.FirstOrDefault().Item1.Parent?.Current is SGIS.VectorLayer vLayer) // GIS + // remove all native geometry type checks for now + /* + if ( + listOfContextAndTrackers.FirstOrDefault().Item1.Parent?.Current is SGIS.GisLayer vLayer + && vLayer["attributes"] is Base + ) // GIS { geomType = GISLayerGeometryType.GetNativeLayerGeometryType(vLayer); - } - else // non-GIS + }*/ + + var hostAppGeom = listOfContextAndTrackers[0].Item2.HostAppGeom; + if (hostAppGeom is null) // type check, should not happen { - var hostAppGeom = listOfContextAndTrackers[0].Item2.HostAppGeom; - if (hostAppGeom is null) // type check, should not happen - { - // TODO: Unsure about the type this exception should be. - throw new SpeckleException("Conversion failed"); - } - geomType = hostAppGeom.GeometryType; + // TODO: Unsure about the type this exception should be. + throw new SpeckleException("Conversion failed"); } + geomType = hostAppGeom.GeometryType; return geomType; } diff --git a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/GISAttributeFieldType.cs b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/GISAttributeFieldType.cs index c9d848119..f2b7150b1 100644 --- a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/GISAttributeFieldType.cs +++ b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/GISAttributeFieldType.cs @@ -18,26 +18,6 @@ public static class GISAttributeFieldType public const string TIMESTAMPOFFSET = "TimeStampOffset"; public const string BOOL = "Bool"; // not supported in ArcGIS, only in QGIS - public static string FieldTypeToSpeckle(FieldType fieldType) - { - return fieldType switch - { - FieldType.GUID => GUID_TYPE, - FieldType.OID => OID, - FieldType.String => STRING_TYPE, - FieldType.Single => FLOAT_TYPE, - FieldType.Integer => INTEGER_TYPE, - FieldType.BigInteger => BIGINTEGER, - FieldType.SmallInteger => SMALLINTEGER, - FieldType.Double => DOUBLE_TYPE, - FieldType.Date => DATETIME, - FieldType.DateOnly => DATEONLY, - FieldType.TimeOnly => TIMEONLY, - FieldType.TimestampOffset => TIMESTAMPOFFSET, - _ => throw new ArgumentOutOfRangeException(nameof(fieldType)), - }; - } - public static FieldType FieldTypeToNative(object fieldType) { if (fieldType is string fieldStringType) diff --git a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/GISLayerGeometryType.cs b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/GISLayerGeometryType.cs deleted file mode 100644 index f10c872d6..000000000 --- a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/GISLayerGeometryType.cs +++ /dev/null @@ -1,43 +0,0 @@ -using ArcGIS.Core.CIM; - -namespace Speckle.Converters.ArcGIS3.Utils; - -public static class GISLayerGeometryType -{ - public const string NONE = "None"; - public const string POINT = "Point"; - public const string POLYLINE = "Polyline"; - public const string POLYGON = "Polygon"; - public const string POLYGON3D = "Polygon3d"; - public const string MULTIPATCH = "Multipatch"; - public const string POINTCLOUD = "Pointcloud"; - - public static string LayerGeometryTypeToSpeckle(esriGeometryType nativeGeometryType) - { - return nativeGeometryType switch - { - esriGeometryType.esriGeometryMultipoint => GISLayerGeometryType.POINT, - esriGeometryType.esriGeometryPoint => GISLayerGeometryType.POINT, - esriGeometryType.esriGeometryLine => GISLayerGeometryType.POLYLINE, - esriGeometryType.esriGeometryPolyline => GISLayerGeometryType.POLYLINE, - esriGeometryType.esriGeometryPolygon => GISLayerGeometryType.POLYGON, - esriGeometryType.esriGeometryMultiPatch => GISLayerGeometryType.MULTIPATCH, - _ => GISLayerGeometryType.NONE, - }; - } - - public static ACG.GeometryType GetNativeLayerGeometryType(Objects.GIS.VectorLayer target) - { - string? originalGeomType = target.geomType != null ? target.geomType : target.nativeGeomType; - return originalGeomType switch - { - GISLayerGeometryType.NONE => ACG.GeometryType.Unknown, - GISLayerGeometryType.POINT => ACG.GeometryType.Multipoint, - GISLayerGeometryType.POLYGON => ACG.GeometryType.Polygon, - GISLayerGeometryType.POLYLINE => ACG.GeometryType.Polyline, - GISLayerGeometryType.MULTIPATCH => ACG.GeometryType.Multipatch, - GISLayerGeometryType.POLYGON3D => ACG.GeometryType.Multipatch, - _ => throw new ArgumentOutOfRangeException(nameof(target)), - }; - } -} diff --git a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/GeometryExtension.cs b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/GeometryExtension.cs deleted file mode 100644 index 7d873f8a2..000000000 --- a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/GeometryExtension.cs +++ /dev/null @@ -1,144 +0,0 @@ -using ArcGIS.Core.CIM; - -namespace Speckle.Converters.ArcGIS3.Utils; - -public static class GeometryUtils -{ - public static bool ValidateMesh(this SOG.Mesh mesh) - { - if (mesh.vertices.Count < 3) - { - return false; - } - else if (mesh.faces.Count < 4) - { - return false; - } - return true; - } - - public static int RGBToInt(this CIMRGBColor color) - { - return (255 << 24) | ((int)Math.Round(color.R) << 16) | ((int)Math.Round(color.G) << 8) | (int)Math.Round(color.B); - } - - public static int CIMColorToInt(this CIMColor color) - { - return (255 << 24) - | ((int)Math.Round(color.Values[0]) << 16) - | ((int)Math.Round(color.Values[1]) << 8) - | (int)Math.Round(color.Values[2]); - } - - public static List Values(this SOG.Arc arc) - { - List coords = - new() - { - arc.startPoint.x, - arc.startPoint.y, - arc.startPoint.z, - arc.midPoint.x, - arc.midPoint.y, - arc.midPoint.z, - arc.endPoint.x, - arc.endPoint.y, - arc.endPoint.z - }; - return coords; - } - - public static bool IsClockwisePolygon(this SOG.Polyline polyline) - { - bool isClockwise; - double sum = 0; - - List points = polyline.GetPoints(); - - if (points.Count < 3) - { - throw new ArgumentException("Not enough points for polygon orientation check"); - } - if (points[0] != points[^1]) - { - points.Add(points[0]); - } - - for (int i = 0; i < points.Count - 1; i++) - { - sum += (points[i + 1].x - points[i].x) * (points[i + 1].y + points[i].y); - } - isClockwise = sum > 0; - return isClockwise; - } - - public static SGIS.GisMultipatchGeometry CompleteMultipatchTriangleStrip( - this ACG.Multipatch target, - List> allPoints, - int idx - ) - { - SGIS.GisMultipatchGeometry multipatch = new(); - List pointCoords = allPoints[idx].SelectMany(x => new List() { x.x, x.y, x.z }).ToList(); - - // get data for this multipatch part - int ptCount = target.GetPatchPointCount(idx); - - for (int ptIdx = 0; ptIdx < ptCount; ptIdx++) - { - if (ptIdx >= 2) // every new point adds a triangle - { - multipatch.faces.AddRange(new List() { 3, ptIdx - 2, ptIdx - 1, ptIdx }); - multipatch.vertices.AddRange(pointCoords.GetRange(3 * (ptIdx - 2), 9).ToList()); - } - } - return multipatch; - } - - public static SGIS.GisMultipatchGeometry CompleteMultipatchTriangles( - this ACG.Multipatch target, - List> allPoints, - int idx - ) - { - SGIS.GisMultipatchGeometry multipatch = new(); - List pointCoords = allPoints[idx].SelectMany(x => new List() { x.x, x.y, x.z }).ToList(); - - // get data for this multipatch part - int ptCount = target.GetPatchPointCount(idx); - for (int ptIdx = 0; ptIdx < ptCount; ptIdx++) - { - var convertedPt = allPoints[idx][ptIdx]; - if (ptIdx >= 2 && (ptIdx + 1) % 3 == 0) // every 3 new points is a new triangle - { - multipatch.faces.AddRange(new List() { 3, ptIdx - 2, ptIdx - 1, ptIdx }); - multipatch.vertices.AddRange(pointCoords.GetRange(3 * (ptIdx - 2), 9).ToList()); - } - } - return multipatch; - } - - public static SGIS.GisMultipatchGeometry CompleteMultipatchTriangleFan( - this ACG.Multipatch target, - List> allPoints, - int idx - ) - { - SGIS.GisMultipatchGeometry multipatch = new(); - List pointCoords = allPoints[idx].SelectMany(x => new List() { x.x, x.y, x.z }).ToList(); - - // get data for this multipatch part - int ptCount = target.GetPatchPointCount(idx); - - for (int ptIdx = 0; ptIdx < ptCount; ptIdx++) - { - var convertedPt = allPoints[idx][ptIdx]; - if (ptIdx >= 2) // every new point adds a triangle (originates from 0) - { - multipatch.faces.AddRange(new List() { 3, 0, ptIdx - 1, ptIdx }); - multipatch.vertices.AddRange(pointCoords.GetRange(2 * (ptIdx - 2), 6).ToList()); - } - } - return multipatch; - } -} diff --git a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/packages.lock.json b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/packages.lock.json index 8b2f374fe..4d225e8dd 100644 --- a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/packages.lock.json +++ b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/packages.lock.json @@ -155,11 +155,6 @@ "resolved": "8.0.0", "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" }, - "Speckle.DoubleNumerics": { - "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w==" - }, "Speckle.Newtonsoft.Json": { "type": "Transitive", "resolved": "13.0.2", @@ -219,7 +214,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.191, )" + "Speckle.Objects": "[3.1.1, )" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { @@ -246,20 +241,26 @@ "resolved": "2.2.0", "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "+m7jRFm0ABbkcSz2UphdxAsislY10IpQ1u79c8a3aVvegLjnsVQZ1sXfRIRO1aFdulkhjYKXYpB3N9M8Z+epgQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.191" + "Speckle.Sdk": "3.1.1" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "VVT3LJiYlhqnggxxdeTt1QLrqfxDb044x0yX6kxS9b5MRzeDvK2Vz86pLDfuHs+SXvDimRVfYx1M42IW/aPcTQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.Bcl.AsyncInterfaces": "5.0.0", @@ -267,16 +268,16 @@ "Microsoft.Data.Sqlite": "7.0.5", "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", "Microsoft.Extensions.Logging": "2.2.0", - "Speckle.DoubleNumerics": "4.0.1", + "Speckle.DoubleNumerics": "4.1.0", "Speckle.Newtonsoft.Json": "13.0.2", - "Speckle.Sdk.Dependencies": "3.1.0-dev.191" + "Speckle.Sdk.Dependencies": "3.1.1" } }, "Speckle.Sdk.Dependencies": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "EmEOyjsGsNi56Z/ZoBOn8WirTmIT2yqWvlUeUh0BSPX2TDMZXHTKOM/kHmP6HSd10KVFn2Zo/ItY7/K9iRtL1Q==" + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" } } } diff --git a/Converters/Autocad/Speckle.Converters.Autocad2022/packages.lock.json b/Converters/Autocad/Speckle.Converters.Autocad2022/packages.lock.json index aa3d568e0..c8f04f7bd 100644 --- a/Converters/Autocad/Speckle.Converters.Autocad2022/packages.lock.json +++ b/Converters/Autocad/Speckle.Converters.Autocad2022/packages.lock.json @@ -159,11 +159,6 @@ "resolved": "8.0.0", "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" }, - "Speckle.DoubleNumerics": { - "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w==" - }, "Speckle.Newtonsoft.Json": { "type": "Transitive", "resolved": "13.0.2", @@ -258,7 +253,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.191, )" + "Speckle.Objects": "[3.1.1, )" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { @@ -285,20 +280,26 @@ "resolved": "2.2.0", "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "+m7jRFm0ABbkcSz2UphdxAsislY10IpQ1u79c8a3aVvegLjnsVQZ1sXfRIRO1aFdulkhjYKXYpB3N9M8Z+epgQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.191" + "Speckle.Sdk": "3.1.1" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "VVT3LJiYlhqnggxxdeTt1QLrqfxDb044x0yX6kxS9b5MRzeDvK2Vz86pLDfuHs+SXvDimRVfYx1M42IW/aPcTQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.Bcl.AsyncInterfaces": "5.0.0", @@ -306,16 +307,16 @@ "Microsoft.Data.Sqlite": "7.0.5", "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", "Microsoft.Extensions.Logging": "2.2.0", - "Speckle.DoubleNumerics": "4.0.1", + "Speckle.DoubleNumerics": "4.1.0", "Speckle.Newtonsoft.Json": "13.0.2", - "Speckle.Sdk.Dependencies": "3.1.0-dev.191" + "Speckle.Sdk.Dependencies": "3.1.1" } }, "Speckle.Sdk.Dependencies": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "EmEOyjsGsNi56Z/ZoBOn8WirTmIT2yqWvlUeUh0BSPX2TDMZXHTKOM/kHmP6HSd10KVFn2Zo/ItY7/K9iRtL1Q==" + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" } } } diff --git a/Converters/Autocad/Speckle.Converters.Autocad2023/packages.lock.json b/Converters/Autocad/Speckle.Converters.Autocad2023/packages.lock.json index 6c2e0279b..4b8c12390 100644 --- a/Converters/Autocad/Speckle.Converters.Autocad2023/packages.lock.json +++ b/Converters/Autocad/Speckle.Converters.Autocad2023/packages.lock.json @@ -159,11 +159,6 @@ "resolved": "8.0.0", "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" }, - "Speckle.DoubleNumerics": { - "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w==" - }, "Speckle.Newtonsoft.Json": { "type": "Transitive", "resolved": "13.0.2", @@ -258,7 +253,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.191, )" + "Speckle.Objects": "[3.1.1, )" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { @@ -285,20 +280,26 @@ "resolved": "2.2.0", "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "+m7jRFm0ABbkcSz2UphdxAsislY10IpQ1u79c8a3aVvegLjnsVQZ1sXfRIRO1aFdulkhjYKXYpB3N9M8Z+epgQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.191" + "Speckle.Sdk": "3.1.1" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "VVT3LJiYlhqnggxxdeTt1QLrqfxDb044x0yX6kxS9b5MRzeDvK2Vz86pLDfuHs+SXvDimRVfYx1M42IW/aPcTQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.Bcl.AsyncInterfaces": "5.0.0", @@ -306,16 +307,16 @@ "Microsoft.Data.Sqlite": "7.0.5", "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", "Microsoft.Extensions.Logging": "2.2.0", - "Speckle.DoubleNumerics": "4.0.1", + "Speckle.DoubleNumerics": "4.1.0", "Speckle.Newtonsoft.Json": "13.0.2", - "Speckle.Sdk.Dependencies": "3.1.0-dev.191" + "Speckle.Sdk.Dependencies": "3.1.1" } }, "Speckle.Sdk.Dependencies": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "EmEOyjsGsNi56Z/ZoBOn8WirTmIT2yqWvlUeUh0BSPX2TDMZXHTKOM/kHmP6HSd10KVFn2Zo/ItY7/K9iRtL1Q==" + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" } } } diff --git a/Converters/Autocad/Speckle.Converters.Autocad2024/packages.lock.json b/Converters/Autocad/Speckle.Converters.Autocad2024/packages.lock.json index 3a57b49ae..8f8b0194b 100644 --- a/Converters/Autocad/Speckle.Converters.Autocad2024/packages.lock.json +++ b/Converters/Autocad/Speckle.Converters.Autocad2024/packages.lock.json @@ -159,11 +159,6 @@ "resolved": "8.0.0", "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" }, - "Speckle.DoubleNumerics": { - "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w==" - }, "Speckle.Newtonsoft.Json": { "type": "Transitive", "resolved": "13.0.2", @@ -259,9 +254,9 @@ "dependencies": { "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", "Speckle.Connectors.Logging": "[1.0.0, )", - "Speckle.Objects": "[3.1.0-dev.191, )", - "Speckle.Sdk": "[3.1.0-dev.191, )", - "Speckle.Sdk.Dependencies": "[3.1.0-dev.191, )" + "Speckle.Objects": "[3.1.1, )", + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" } }, "speckle.connectors.dui": { @@ -269,9 +264,8 @@ "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", "Speckle.Connectors.Common": "[1.0.0, )", - "Speckle.Sdk": "[3.1.0-dev.191, )", - "Speckle.Sdk.Dependencies": "[3.1.0-dev.191, )", - "System.Threading.Tasks.Dataflow": "[6.0.0, )" + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" } }, "speckle.connectors.dui.webview": { @@ -288,7 +282,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.191, )" + "Speckle.Objects": "[3.1.1, )" } }, "Microsoft.Extensions.DependencyInjection": { @@ -330,20 +324,26 @@ "resolved": "1.0.1938.49", "contentHash": "z8KnFnaTYzhA/ZnyRX0qGfS1NU5ZBJeClAH64F0fVDvdDJTvME7xl6zTJ0Jlfe1BtL3C0NH9xTy64shg2baKdw==" }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "+m7jRFm0ABbkcSz2UphdxAsislY10IpQ1u79c8a3aVvegLjnsVQZ1sXfRIRO1aFdulkhjYKXYpB3N9M8Z+epgQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.191" + "Speckle.Sdk": "3.1.1" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "VVT3LJiYlhqnggxxdeTt1QLrqfxDb044x0yX6kxS9b5MRzeDvK2Vz86pLDfuHs+SXvDimRVfYx1M42IW/aPcTQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.Bcl.AsyncInterfaces": "5.0.0", @@ -351,22 +351,16 @@ "Microsoft.Data.Sqlite": "7.0.5", "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", "Microsoft.Extensions.Logging": "2.2.0", - "Speckle.DoubleNumerics": "4.0.1", + "Speckle.DoubleNumerics": "4.1.0", "Speckle.Newtonsoft.Json": "13.0.2", - "Speckle.Sdk.Dependencies": "3.1.0-dev.191" + "Speckle.Sdk.Dependencies": "3.1.1" } }, "Speckle.Sdk.Dependencies": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "EmEOyjsGsNi56Z/ZoBOn8WirTmIT2yqWvlUeUh0BSPX2TDMZXHTKOM/kHmP6HSd10KVFn2Zo/ItY7/K9iRtL1Q==" - }, - "System.Threading.Tasks.Dataflow": { - "type": "CentralTransitive", - "requested": "[6.0.0, )", - "resolved": "6.0.0", - "contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA==" + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" } } } diff --git a/Converters/Autocad/Speckle.Converters.Autocad2025/packages.lock.json b/Converters/Autocad/Speckle.Converters.Autocad2025/packages.lock.json index eaf70b6b3..7a432021e 100644 --- a/Converters/Autocad/Speckle.Converters.Autocad2025/packages.lock.json +++ b/Converters/Autocad/Speckle.Converters.Autocad2025/packages.lock.json @@ -150,11 +150,6 @@ "resolved": "8.0.0", "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" }, - "Speckle.DoubleNumerics": { - "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w==" - }, "Speckle.Newtonsoft.Json": { "type": "Transitive", "resolved": "13.0.2", @@ -215,9 +210,9 @@ "dependencies": { "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", "Speckle.Connectors.Logging": "[1.0.0, )", - "Speckle.Objects": "[3.1.0-dev.191, )", - "Speckle.Sdk": "[3.1.0-dev.191, )", - "Speckle.Sdk.Dependencies": "[3.1.0-dev.191, )" + "Speckle.Objects": "[3.1.1, )", + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" } }, "speckle.connectors.dui": { @@ -225,9 +220,8 @@ "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", "Speckle.Connectors.Common": "[1.0.0, )", - "Speckle.Sdk": "[3.1.0-dev.191, )", - "Speckle.Sdk.Dependencies": "[3.1.0-dev.191, )", - "System.Threading.Tasks.Dataflow": "[6.0.0, )" + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" } }, "speckle.connectors.dui.webview": { @@ -244,7 +238,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.191, )" + "Speckle.Objects": "[3.1.1, )" } }, "Microsoft.Extensions.DependencyInjection": { @@ -286,42 +280,42 @@ "resolved": "1.0.1938.49", "contentHash": "z8KnFnaTYzhA/ZnyRX0qGfS1NU5ZBJeClAH64F0fVDvdDJTvME7xl6zTJ0Jlfe1BtL3C0NH9xTy64shg2baKdw==" }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "+m7jRFm0ABbkcSz2UphdxAsislY10IpQ1u79c8a3aVvegLjnsVQZ1sXfRIRO1aFdulkhjYKXYpB3N9M8Z+epgQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.191" + "Speckle.Sdk": "3.1.1" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "VVT3LJiYlhqnggxxdeTt1QLrqfxDb044x0yX6kxS9b5MRzeDvK2Vz86pLDfuHs+SXvDimRVfYx1M42IW/aPcTQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.CSharp": "4.7.0", "Microsoft.Data.Sqlite": "7.0.5", "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", "Microsoft.Extensions.Logging": "2.2.0", - "Speckle.DoubleNumerics": "4.0.1", + "Speckle.DoubleNumerics": "4.1.0", "Speckle.Newtonsoft.Json": "13.0.2", - "Speckle.Sdk.Dependencies": "3.1.0-dev.191" + "Speckle.Sdk.Dependencies": "3.1.1" } }, "Speckle.Sdk.Dependencies": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "EmEOyjsGsNi56Z/ZoBOn8WirTmIT2yqWvlUeUh0BSPX2TDMZXHTKOM/kHmP6HSd10KVFn2Zo/ItY7/K9iRtL1Q==" - }, - "System.Threading.Tasks.Dataflow": { - "type": "CentralTransitive", - "requested": "[6.0.0, )", - "resolved": "6.0.0", - "contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA==" + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" } } } diff --git a/Converters/Autocad/Speckle.Converters.AutocadShared/AutocadRootToSpeckleConverter.cs b/Converters/Autocad/Speckle.Converters.AutocadShared/AutocadRootToSpeckleConverter.cs index c6cdfe407..c9178af28 100644 --- a/Converters/Autocad/Speckle.Converters.AutocadShared/AutocadRootToSpeckleConverter.cs +++ b/Converters/Autocad/Speckle.Converters.AutocadShared/AutocadRootToSpeckleConverter.cs @@ -1,4 +1,5 @@ using Autodesk.AutoCAD.DatabaseServices; +// using Speckle.Converters.AutocadShared.ToSpeckle; using Speckle.Converters.Common; using Speckle.Converters.Common.Objects; using Speckle.Converters.Common.Registration; @@ -12,13 +13,17 @@ public class AutocadRootToSpeckleConverter : IRootToSpeckleConverter private readonly IConverterManager _toSpeckle; private readonly IConverterSettingsStore _settingsStore; + // private readonly PropertiesExtractor _propertiesExtractor; // NOTE: commented out as we can't test (no sample file) + public AutocadRootToSpeckleConverter( IConverterManager toSpeckle, IConverterSettingsStore settingsStore + // PropertiesExtractor propertiesExtractor // NOTE: commented out as we can't test (no sample file) ) { _toSpeckle = toSpeckle; _settingsStore = settingsStore; + // _propertiesExtractor = propertiesExtractor; // NOTE: commented out as we can't test (no sample file) } public Base Convert(object target) @@ -39,6 +44,15 @@ public class AutocadRootToSpeckleConverter : IRootToSpeckleConverter var objectConverter = _toSpeckle.ResolveConverter(type); var convertedObject = objectConverter.Convert(dbObject); + + // NOTE: commented out as we can't test (no sample file) + // add properties + // Dictionary properties = _propertiesExtractor.GetProperties((Entity)dbObject); + // if (properties.Count > 0) + // { + // convertedObject["properties"] = properties; + // } + tr.Commit(); return convertedObject; } diff --git a/Converters/Autocad/Speckle.Converters.AutocadShared/ServiceRegistration.cs b/Converters/Autocad/Speckle.Converters.AutocadShared/ServiceRegistration.cs index 3db647bd7..7afaca4de 100644 --- a/Converters/Autocad/Speckle.Converters.AutocadShared/ServiceRegistration.cs +++ b/Converters/Autocad/Speckle.Converters.AutocadShared/ServiceRegistration.cs @@ -1,5 +1,6 @@ using System.Reflection; using Microsoft.Extensions.DependencyInjection; +using Speckle.Converters.AutocadShared.ToSpeckle; using Speckle.Converters.Common; using Speckle.Converters.Common.Registration; using Speckle.Sdk; @@ -23,5 +24,9 @@ public static class ServiceRegistration ConverterSettingsStore >(); serviceCollection.AddMatchingInterfacesAsTransient(Assembly.GetExecutingAssembly()); + + // add other classes + // serviceCollection.AddScoped(); // NOTE: commented out until we can test in acad + serviceCollection.AddScoped(); } } diff --git a/Converters/Autocad/Speckle.Converters.AutocadShared/Speckle.Converters.AutocadShared.projitems b/Converters/Autocad/Speckle.Converters.AutocadShared/Speckle.Converters.AutocadShared.projitems index b4966066f..36963d641 100644 --- a/Converters/Autocad/Speckle.Converters.AutocadShared/Speckle.Converters.AutocadShared.projitems +++ b/Converters/Autocad/Speckle.Converters.AutocadShared/Speckle.Converters.AutocadShared.projitems @@ -18,8 +18,12 @@ + + + + - + @@ -41,7 +45,12 @@ + + + + + diff --git a/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/ArcToHostConverter.cs b/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/ArcToHostConverter.cs index bc1b7fb1c..65f2ef5cb 100644 --- a/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/ArcToHostConverter.cs +++ b/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/ArcToHostConverter.cs @@ -4,7 +4,7 @@ using Speckle.Sdk.Models; namespace Speckle.Converters.Autocad.ToHost.Geometry; -[NameAndRankValue(nameof(SOG.Arc), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +[NameAndRankValue(typeof(SOG.Arc), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] public class ArcToHostConverter : IToHostTopLevelConverter, ITypedConverter { private readonly ITypedConverter _arcConverter; diff --git a/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/AutocadPolycurveToHostConverter.cs b/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/AutocadPolycurveToHostConverter.cs index 3f98ed847..65a3028b5 100644 --- a/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/AutocadPolycurveToHostConverter.cs +++ b/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/AutocadPolycurveToHostConverter.cs @@ -5,7 +5,7 @@ using Speckle.Sdk.Models; namespace Speckle.Converters.Autocad2023.ToHost.Geometry; -[NameAndRankValue(nameof(SOG.Autocad.AutocadPolycurve), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +[NameAndRankValue(typeof(SOG.Autocad.AutocadPolycurve), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] public class AutocadPolycurveToHostConverter : IToHostTopLevelConverter { private readonly ITypedConverter _polylineConverter; diff --git a/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/BrepToHostConverter.cs b/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/BrepToHostConverter.cs new file mode 100644 index 000000000..70a4f06a4 --- /dev/null +++ b/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/BrepToHostConverter.cs @@ -0,0 +1,39 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Sdk.Models; + +namespace Speckle.Converters.Autocad.Geometry; + +/// +/// Converts a Brep to a List(PolyFaceMesh,Mesh)> as fallback conversion +/// +/// +/// The return type is (Entity,Base) instead of the specific type (PolyfaceMesh, Mesh) so this result can be picked up by a generic list case in the SpeckleToHost connector object baking. This is essentially one-to-many fallback conversion. +/// +[NameAndRankValue(typeof(SOG.Brep), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class BrepToHostConverter : IToHostTopLevelConverter, ITypedConverter> +{ + private readonly ITypedConverter _meshConverter; + + public BrepToHostConverter(ITypedConverter meshConverter) + { + _meshConverter = meshConverter; + } + + public object Convert(Base target) => Convert((SOG.Brep)target); + + /// + /// Unlikey case, but we need to handle multiple meshes inside of brepx displayvalue + /// + public List<(ADB.Entity a, Base b)> Convert(SOG.Brep target) + { + var result = new List(); + foreach (SOG.Mesh mesh in target.displayValue) + { + ADB.PolyFaceMesh convertedMesh = _meshConverter.Convert(mesh); + result.Add(convertedMesh); + } + + return result.Zip(target.displayValue, (a, b) => ((ADB.Entity)a, (Base)b)).ToList(); + } +} diff --git a/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/BrepXToHostConverter.cs b/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/BrepXToHostConverter.cs new file mode 100644 index 000000000..5b668149a --- /dev/null +++ b/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/BrepXToHostConverter.cs @@ -0,0 +1,39 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Sdk.Models; + +namespace Speckle.Converters.Autocad.Geometry; + +/// +/// Converts a BrepX to a List(PolyFaceMesh,Mesh)> as fallback conversion +/// +/// +/// The return type is (Entity,Base) instead of the specific type (PolyfaceMesh, Mesh) so this result can be picked up by a generic list case in the SpeckleToHost connector object baking. This is essentially one-to-many fallback conversion. +/// +[NameAndRankValue(typeof(SOG.BrepX), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class BrepXToHostConverter : IToHostTopLevelConverter, ITypedConverter> +{ + private readonly ITypedConverter _meshConverter; + + public BrepXToHostConverter(ITypedConverter meshConverter) + { + _meshConverter = meshConverter; + } + + public object Convert(Base target) => Convert((SOG.BrepX)target); + + /// + /// Unlikey case, but we need to handle multiple meshes inside of brepx displayvalue. + /// + public List<(ADB.Entity a, Base b)> Convert(SOG.BrepX target) + { + var result = new List(); + foreach (SOG.Mesh mesh in target.displayValue) + { + ADB.PolyFaceMesh convertedMesh = _meshConverter.Convert(mesh); + result.Add(convertedMesh); + } + + return result.Zip(target.displayValue, (a, b) => ((ADB.Entity)a, (Base)b)).ToList(); + } +} diff --git a/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/CircleToHostConverter.cs b/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/CircleToHostConverter.cs index 67faf5769..aba0c711e 100644 --- a/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/CircleToHostConverter.cs +++ b/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/CircleToHostConverter.cs @@ -5,7 +5,7 @@ using Speckle.Sdk.Models; namespace Speckle.Converters.Autocad.ToHost.Geometry; -[NameAndRankValue(nameof(SOG.Circle), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +[NameAndRankValue(typeof(SOG.Circle), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] public class CircleToHostConverter : IToHostTopLevelConverter, ITypedConverter { private readonly ITypedConverter _pointConverter; diff --git a/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/CurveToHostConverter.cs b/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/CurveToHostConverter.cs index 5da630ce9..299862754 100644 --- a/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/CurveToHostConverter.cs +++ b/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/CurveToHostConverter.cs @@ -4,7 +4,7 @@ using Speckle.Sdk.Models; namespace Speckle.Converters.AutocadShared.ToHost.Geometry; -[NameAndRankValue(nameof(SOG.Curve), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +[NameAndRankValue(typeof(SOG.Curve), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] public class CurveToHostConverter : IToHostTopLevelConverter, ITypedConverter { private readonly ITypedConverter _curveConverter; diff --git a/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/DataObjectConverter.cs b/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/DataObjectConverter.cs new file mode 100644 index 000000000..4181d0b38 --- /dev/null +++ b/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/DataObjectConverter.cs @@ -0,0 +1,123 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Objects.Data; +using Speckle.Sdk.Common.Exceptions; +using Speckle.Sdk.Models; + +namespace Speckle.Converters.AutocadShared.ToHost.Geometry; + +[NameAndRankValue(typeof(DataObject), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class DataObjectConverter : IToHostTopLevelConverter, ITypedConverter> +{ + private readonly ITypedConverter _arcConverter; + private readonly ITypedConverter> _brepXConverter; + private readonly ITypedConverter _circleConverter; + private readonly ITypedConverter _curveConverter; + private readonly ITypedConverter _ellipseConverter; + private readonly ITypedConverter> _extrusionXConverter; + private readonly ITypedConverter _lineConverter; + private readonly ITypedConverter _meshConverter; + private readonly ITypedConverter _pointConverter; + private readonly ITypedConverter> _polycurveConverter; + private readonly ITypedConverter _polylineConverter; + private readonly ITypedConverter> _subDXConverter; + + public DataObjectConverter( + ITypedConverter arcConverter, + ITypedConverter> brepXConverter, + ITypedConverter circleConverter, + ITypedConverter curveConverter, + ITypedConverter ellipseConverter, + ITypedConverter> extrusionXConverter, + ITypedConverter lineConverter, + ITypedConverter meshConverter, + ITypedConverter pointConverter, + ITypedConverter> polycurveConverter, + ITypedConverter polylineConverter, + ITypedConverter> subDXConverter + ) + { + _arcConverter = arcConverter; + _brepXConverter = brepXConverter; + _circleConverter = circleConverter; + _curveConverter = curveConverter; + _ellipseConverter = ellipseConverter; + _extrusionXConverter = extrusionXConverter; + _lineConverter = lineConverter; + _meshConverter = meshConverter; + _pointConverter = pointConverter; + _polycurveConverter = polycurveConverter; + _polylineConverter = polylineConverter; + _subDXConverter = subDXConverter; + } + + public object Convert(Base target) => Convert((DataObject)target); + + public List<(ADB.Entity a, Base b)> Convert(DataObject target) + { + var result = new List<(ADB.Entity a, Base b)>(); + foreach (var item in target.displayValue) + { + result.AddRange(ConvertDisplayObject(item)); + } + return result; + } + + public IEnumerable<(ADB.Entity a, Base b)> ConvertDisplayObject(Base displayObject) + { + switch (displayObject) + { + case SOG.Arc arc: + yield return (_arcConverter.Convert(arc), arc); + break; + case SOG.BrepX brepX: + foreach (var i in _brepXConverter.Convert(brepX)) + { + yield return i; + } + break; + case SOG.Circle circle: + yield return (_circleConverter.Convert(circle), circle); + break; + case SOG.Curve curve: + yield return (_curveConverter.Convert(curve), curve); + break; + case SOG.Ellipse ellipse: + yield return (_ellipseConverter.Convert(ellipse), ellipse); + break; + case SOG.ExtrusionX extrusionX: + foreach (var i in _extrusionXConverter.Convert(extrusionX)) + { + yield return i; + } + break; + case SOG.Line line: + yield return (_lineConverter.Convert(line), line); + break; + case SOG.Mesh mesh: + yield return (_meshConverter.Convert(mesh), mesh); + break; + case SOG.Point point: + yield return (_pointConverter.Convert(point), point); + break; + case SOG.Polycurve polycurve: + foreach (var i in _polycurveConverter.Convert(polycurve)) + { + yield return i; + } + break; + case SOG.Polyline polyline: + yield return (_polylineConverter.Convert(polyline), polyline); + break; + case SOG.SubDX subDX: + foreach (var i in _subDXConverter.Convert(subDX)) + { + yield return i; + } + break; + + default: + throw new ConversionException($"Found unsupported geometry: {displayObject.GetType()}"); + } + } +} diff --git a/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/EllipseToHostConverter.cs b/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/EllipseToHostConverter.cs index e179d15da..04572a8ef 100644 --- a/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/EllipseToHostConverter.cs +++ b/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/EllipseToHostConverter.cs @@ -5,7 +5,7 @@ using Speckle.Sdk.Models; namespace Speckle.Converters.Autocad.ToHost.Geometry; -[NameAndRankValue(nameof(SOG.Ellipse), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +[NameAndRankValue(typeof(SOG.Ellipse), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] public class EllipseToHostConverter : IToHostTopLevelConverter, ITypedConverter { private readonly ITypedConverter _pointConverter; diff --git a/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/ExtrusionXToHostConverter.cs b/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/ExtrusionXToHostConverter.cs new file mode 100644 index 000000000..4b6d75384 --- /dev/null +++ b/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/ExtrusionXToHostConverter.cs @@ -0,0 +1,41 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Sdk.Models; + +namespace Speckle.Converters.Autocad.Geometry; + +/// +/// Converts a ExtrusionX to a List(PolyFaceMesh,Mesh)> as fallback conversion +/// +/// +/// The return type is (Entity,Base) instead of the specific type (PolyfaceMesh, Mesh) so this result can be picked up by a generic list case in the SpeckleToHost connector object baking. This is essentially one-to-many fallback conversion. +/// +[NameAndRankValue(typeof(SOG.ExtrusionX), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class ExtrusionXToHostConverter + : IToHostTopLevelConverter, + ITypedConverter> +{ + private readonly ITypedConverter _meshConverter; + + public ExtrusionXToHostConverter(ITypedConverter meshConverter) + { + _meshConverter = meshConverter; + } + + public object Convert(Base target) => Convert((SOG.ExtrusionX)target); + + /// + /// Unlikey case, but we need to handle multiple meshes inside of extrusionx displayvalue + /// + public List<(ADB.Entity a, Base b)> Convert(SOG.ExtrusionX target) + { + var result = new List(); + foreach (SOG.Mesh mesh in target.displayValue) + { + ADB.PolyFaceMesh convertedMesh = _meshConverter.Convert(mesh); + result.Add(convertedMesh); + } + + return result.Zip(target.displayValue, (a, b) => ((ADB.Entity)a, (Base)b)).ToList(); + } +} diff --git a/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/LineToHostConverter.cs b/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/LineToHostConverter.cs index ab390a46a..a087a5c6b 100644 --- a/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/LineToHostConverter.cs +++ b/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/LineToHostConverter.cs @@ -4,7 +4,7 @@ using Speckle.Sdk.Models; namespace Speckle.Converters.Autocad.ToHost.Geometry; -[NameAndRankValue(nameof(SOG.Line), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +[NameAndRankValue(typeof(SOG.Line), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] public class LineToHostConverter : IToHostTopLevelConverter, ITypedConverter { private readonly ITypedConverter _pointConverter; diff --git a/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/MeshToHostConverter.cs b/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/MeshToHostConverter.cs index 340a601c4..a3856168c 100644 --- a/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/MeshToHostConverter.cs +++ b/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/MeshToHostConverter.cs @@ -6,7 +6,7 @@ using Speckle.Sdk.Models; namespace Speckle.Converters.Autocad.Geometry; -[NameAndRankValue(nameof(SOG.Mesh), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +[NameAndRankValue(typeof(SOG.Mesh), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] public class MeshToHostConverter : IToHostTopLevelConverter, ITypedConverter { private readonly ITypedConverter _pointConverter; diff --git a/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/PointToHostConverter.cs b/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/PointToHostConverter.cs index 4feb6cdcd..6f0efd5f0 100644 --- a/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/PointToHostConverter.cs +++ b/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/PointToHostConverter.cs @@ -4,7 +4,7 @@ using Speckle.Sdk.Models; namespace Speckle.Converters.Autocad.ToHost.Geometry; -[NameAndRankValue(nameof(SOG.Point), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +[NameAndRankValue(typeof(SOG.Point), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] public class PointToHostConverter : IToHostTopLevelConverter, ITypedConverter { private readonly ITypedConverter _pointConverter; diff --git a/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/PolycurveToHostConverter.cs b/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/PolycurveToHostConverter.cs index 3f1b9731d..9b0f6e3bc 100644 --- a/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/PolycurveToHostConverter.cs +++ b/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/PolycurveToHostConverter.cs @@ -1,3 +1,4 @@ +using Autodesk.AutoCAD.DatabaseServices; using Speckle.Converters.Common; using Speckle.Converters.Common.Objects; using Speckle.Sdk.Models; @@ -9,34 +10,37 @@ namespace Speckle.Converters.AutocadShared.ToHost.Geometry; /// If polycurve segments are planar and only of type and , it can be represented as Polyline in Autocad. /// Otherwise we convert it as spline (list of ADB.Entity) that switch cases according to each segment type. /// -[NameAndRankValue(nameof(SOG.Polycurve), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] -public class PolycurveToHostConverter : IToHostTopLevelConverter +[NameAndRankValue(typeof(SOG.Polycurve), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class PolycurveToHostConverter + : IToHostTopLevelConverter, + ITypedConverter> { private readonly ITypedConverter _polylineConverter; - private readonly ITypedConverter> _splineConverter; + private readonly ITypedConverter> _splineConverter; public PolycurveToHostConverter( ITypedConverter polylineConverter, - ITypedConverter> splineConverter + ITypedConverter> splineConverter ) { _polylineConverter = polylineConverter; _splineConverter = splineConverter; } - public object Convert(Base target) + public object Convert(Base target) => Convert((SOG.Polycurve)target); + + public List<(Entity, Base)> Convert(SOG.Polycurve target) { - SOG.Polycurve polycurve = (SOG.Polycurve)target; - bool convertAsSpline = polycurve.segments.Any(s => s is not SOG.Line and not SOG.Arc); - bool isPlanar = IsPolycurvePlanar(polycurve); + bool convertAsSpline = target.segments.Any(s => s is not SOG.Line and not SOG.Arc); + bool isPlanar = IsPolycurvePlanar(target); if (convertAsSpline || !isPlanar) { - return _splineConverter.Convert(polycurve); + return _splineConverter.Convert(target); } else { - return _polylineConverter.Convert(polycurve); + return new() { (_polylineConverter.Convert(target), target) }; } } diff --git a/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/PolylineToHostConverter.cs b/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/PolylineToHostConverter.cs index 9809303d0..a6c26bec5 100644 --- a/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/PolylineToHostConverter.cs +++ b/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/PolylineToHostConverter.cs @@ -4,7 +4,7 @@ using Speckle.Sdk.Models; namespace Speckle.Converters.Autocad.ToHost.Geometry; -[NameAndRankValue(nameof(SOG.Polyline), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +[NameAndRankValue(typeof(SOG.Polyline), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] public class PolylineToHostConverter : IToHostTopLevelConverter, ITypedConverter { private readonly ITypedConverter _pointConverter; diff --git a/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/SpeckleFallbackToHostConversion.cs b/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/SpeckleFallbackToHostConversion.cs deleted file mode 100644 index 7008b8e37..000000000 --- a/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/SpeckleFallbackToHostConversion.cs +++ /dev/null @@ -1,55 +0,0 @@ -using Speckle.Converters.Common; -using Speckle.Converters.Common.Objects; -using Speckle.Sdk.Common.Exceptions; -using Speckle.Sdk.Models; - -namespace Speckle.Converters.Rhino7.ToHost.TopLevel; - -[NameAndRankValue(nameof(DisplayableObject), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] -public class SpeckleFallbackToAutocadTopLevelConverter - : IToHostTopLevelConverter, - ITypedConverter> -{ - private readonly ITypedConverter _lineConverter; - private readonly ITypedConverter _polylineConverter; - private readonly ITypedConverter _meshConverter; - private readonly ITypedConverter _arcConverter; - private readonly ITypedConverter _pointConverter; - - public SpeckleFallbackToAutocadTopLevelConverter( - ITypedConverter lineConverter, - ITypedConverter polylineConverter, - ITypedConverter meshConverter, - ITypedConverter arcConverter, - ITypedConverter pointConverter - ) - { - _lineConverter = lineConverter; - _polylineConverter = polylineConverter; - _meshConverter = meshConverter; - _arcConverter = arcConverter; - _pointConverter = pointConverter; - } - - public object Convert(Base target) => Convert((DisplayableObject)target); - - public List Convert(DisplayableObject target) - { - var result = new List(); - foreach (var item in target.displayValue) - { - ADB.Entity x = item switch - { - SOG.Line line => _lineConverter.Convert(line), - SOG.Polyline polyline => _polylineConverter.Convert(polyline), - SOG.Mesh mesh => _meshConverter.Convert(mesh), - SOG.Arc arc => _arcConverter.Convert(arc), - SOG.Point point => _pointConverter.Convert(point), - _ => throw new ConversionException($"Found unsupported fallback geometry: {item.GetType()}") - }; - result.Add(x); - } - - return result; - } -} diff --git a/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/SubDXToHostConverter.cs b/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/SubDXToHostConverter.cs new file mode 100644 index 000000000..7694c1dc8 --- /dev/null +++ b/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/SubDXToHostConverter.cs @@ -0,0 +1,39 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Sdk.Models; + +namespace Speckle.Converters.Autocad.Geometry; + +/// +/// Converts a SubDX to a List(PolyFaceMesh,Mesh)> as fallback conversion +/// +/// +/// The return type is (Entity,Base) instead of the specific type (PolyfaceMesh, Mesh) so this result can be picked up by a generic list case in the SpeckleToHost connector object baking. This is essentially one-to-many fallback conversion. +/// +[NameAndRankValue(typeof(SOG.SubDX), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class SubDXToHostConverter : IToHostTopLevelConverter, ITypedConverter> +{ + private readonly ITypedConverter _meshConverter; + + public SubDXToHostConverter(ITypedConverter meshConverter) + { + _meshConverter = meshConverter; + } + + public object Convert(Base target) => Convert((SOG.SubDX)target); + + /// + /// Unlikey case, but we need to handle multiple meshes inside of subdx displayvalue + /// + public List<(ADB.Entity a, Base b)> Convert(SOG.SubDX target) + { + var result = new List(); + foreach (SOG.Mesh mesh in target.displayValue) + { + ADB.PolyFaceMesh convertedMesh = _meshConverter.Convert(mesh); + result.Add(convertedMesh); + } + + return result.Zip(target.displayValue, (a, b) => ((ADB.Entity)a, (Base)b)).ToList(); + } +} diff --git a/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Raw/ArcToHostRawConverter.cs b/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Raw/ArcToHostRawConverter.cs index 6a68b43ac..a5d09151d 100644 --- a/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Raw/ArcToHostRawConverter.cs +++ b/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Raw/ArcToHostRawConverter.cs @@ -4,7 +4,7 @@ using Speckle.Sdk.Models; namespace Speckle.Converters.Autocad.ToHost.Raw; -[NameAndRankValue(nameof(SOG.Arc), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +[NameAndRankValue(typeof(SOG.Arc), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] public class ArcToHostRowConverter : ITypedConverter { private readonly ITypedConverter _pointConverter; diff --git a/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Raw/PolycurveToHostSplineRawConverter.cs b/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Raw/PolycurveToHostSplineRawConverter.cs index c9c1e1ce6..02aaffbbf 100644 --- a/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Raw/PolycurveToHostSplineRawConverter.cs +++ b/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Raw/PolycurveToHostSplineRawConverter.cs @@ -1,11 +1,13 @@ +using Autodesk.AutoCAD.DatabaseServices; using Speckle.Converters.Common.Objects; +using Speckle.Sdk.Models; namespace Speckle.Converters.AutocadShared.ToHost.Raw; /// /// Polycurve segments might appear in different ICurve types which requires to handle separately for each segment. /// -public class PolycurveToHostSplineRawConverter : ITypedConverter> +public class PolycurveToHostSplineRawConverter : ITypedConverter> { private readonly ITypedConverter _lineConverter; private readonly ITypedConverter _polylineConverter; @@ -25,7 +27,7 @@ public class PolycurveToHostSplineRawConverter : ITypedConverter Convert(SOG.Polycurve target) + public List<(Entity, Base)> Convert(SOG.Polycurve target) { // POC: We can improve this once we have IIndex of raw converters and we can get rid of case converters? // POC: Should we join entities? @@ -52,6 +54,6 @@ public class PolycurveToHostSplineRawConverter : ITypedConverter ((ADB.Entity)a, (Base)b)).ToList(); } } diff --git a/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/ArcToSpeckleConverter.cs b/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/ArcToSpeckleConverter.cs index e179f15fa..edbf962e4 100644 --- a/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/ArcToSpeckleConverter.cs +++ b/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/ArcToSpeckleConverter.cs @@ -4,7 +4,7 @@ using Speckle.Sdk.Models; namespace Speckle.Converters.Autocad.ToSpeckle.Geometry; -[NameAndRankValue(nameof(ADB.Arc), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +[NameAndRankValue(typeof(ADB.Arc), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] public class DBArcToSpeckleConverter : IToSpeckleTopLevelConverter { private readonly ITypedConverter _arcConverter; diff --git a/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/CircleToSpeckleConverter.cs b/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/CircleToSpeckleConverter.cs index 89415efd7..21f8c7e95 100644 --- a/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/CircleToSpeckleConverter.cs +++ b/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/CircleToSpeckleConverter.cs @@ -4,7 +4,7 @@ using Speckle.Sdk.Models; namespace Speckle.Converters.Autocad.ToSpeckle.Geometry; -[NameAndRankValue(nameof(ADB.Circle), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +[NameAndRankValue(typeof(ADB.Circle), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] public class DBCircleToSpeckleConverter : IToSpeckleTopLevelConverter { private readonly ITypedConverter _circleConverter; diff --git a/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/EllipseToSpeckleConverter.cs b/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/EllipseToSpeckleConverter.cs index 89d5b9af3..ec170697b 100644 --- a/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/EllipseToSpeckleConverter.cs +++ b/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/EllipseToSpeckleConverter.cs @@ -4,7 +4,7 @@ using Speckle.Sdk.Models; namespace Speckle.Converters.Autocad.ToSpeckle.Geometry; -[NameAndRankValue(nameof(ADB.Ellipse), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +[NameAndRankValue(typeof(ADB.Ellipse), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] public class DBEllipseToSpeckleConverter : IToSpeckleTopLevelConverter { private readonly ITypedConverter _ellipseConverter; diff --git a/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/LineToSpeckleConverter.cs b/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/LineToSpeckleConverter.cs index ea4be5229..e9c0eb5b1 100644 --- a/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/LineToSpeckleConverter.cs +++ b/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/LineToSpeckleConverter.cs @@ -4,7 +4,7 @@ using Speckle.Sdk.Models; namespace Speckle.Converters.Autocad.ToSpeckle.Geometry; -[NameAndRankValue(nameof(ADB.Line), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +[NameAndRankValue(typeof(ADB.Line), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] public class LineToSpeckleConverter : IToSpeckleTopLevelConverter { private readonly ITypedConverter _lineConverter; diff --git a/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/PointToSpeckleConverter.cs b/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/PointToSpeckleConverter.cs index 0dc4bc102..ffe163dcc 100644 --- a/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/PointToSpeckleConverter.cs +++ b/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/PointToSpeckleConverter.cs @@ -4,7 +4,7 @@ using Speckle.Sdk.Models; namespace Speckle.Converters.Autocad.ToSpeckle.Geometry; -[NameAndRankValue(nameof(ADB.DBPoint), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +[NameAndRankValue(typeof(ADB.DBPoint), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] public class PointToSpeckleConverter : IToSpeckleTopLevelConverter { private readonly ITypedConverter _pointConverter; diff --git a/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/PolyfaceMeshToSpeckleConverter.cs b/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/PolyfaceMeshToSpeckleConverter.cs index 5e5cffd92..a8d23bbef 100644 --- a/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/PolyfaceMeshToSpeckleConverter.cs +++ b/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/PolyfaceMeshToSpeckleConverter.cs @@ -11,7 +11,7 @@ namespace Speckle.Converters.Autocad.Geometry; /// /// The IToSpeckleTopLevelConverter inheritance should only expect database-resident objects. IRawConversion inheritance can expect non database-resident objects, when generated from other converters. /// -[NameAndRankValue(nameof(ADB.PolyFaceMesh), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +[NameAndRankValue(typeof(ADB.PolyFaceMesh), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] public class DBPolyfaceMeshToSpeckleConverter : IToSpeckleTopLevelConverter { private readonly ITypedConverter _pointConverter; diff --git a/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/Polyline2dToSpeckleConverter.cs b/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/Polyline2dToSpeckleConverter.cs index 519e7615f..e6cdfe890 100644 --- a/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/Polyline2dToSpeckleConverter.cs +++ b/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/Polyline2dToSpeckleConverter.cs @@ -14,7 +14,7 @@ namespace Speckle.Converters.Autocad.Geometry; /// of type and will have only one in . /// The IToSpeckleTopLevelConverter inheritance should only expect database-resident objects. IRawConversion inheritance can expect non database-resident objects, when generated from other converters. /// -[NameAndRankValue(nameof(ADB.Polyline2d), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +[NameAndRankValue(typeof(ADB.Polyline2d), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] public class Polyline2dToSpeckleConverter : IToSpeckleTopLevelConverter, ITypedConverter diff --git a/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/Polyline3dToSpeckleConverter.cs b/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/Polyline3dToSpeckleConverter.cs index f01ff31f9..76218142d 100644 --- a/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/Polyline3dToSpeckleConverter.cs +++ b/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/Polyline3dToSpeckleConverter.cs @@ -13,7 +13,7 @@ namespace Speckle.Converters.Autocad.Geometry; /// of type and will have only one in . /// The IToSpeckleTopLevelConverter inheritance should only expect database-resident Polyline2d objects. IRawConversion inheritance can expect non database-resident objects, when generated from other converters. /// -[NameAndRankValue(nameof(ADB.Polyline3d), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +[NameAndRankValue(typeof(ADB.Polyline3d), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] public class Polyline3dToSpeckleConverter : IToSpeckleTopLevelConverter, ITypedConverter diff --git a/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/PolylineToSpeckleConverter.cs b/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/PolylineToSpeckleConverter.cs index d0a63cb02..da89a945b 100644 --- a/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/PolylineToSpeckleConverter.cs +++ b/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/PolylineToSpeckleConverter.cs @@ -10,7 +10,7 @@ namespace Speckle.Converters.Autocad.Geometry; /// /// is of type and will have only s and s in . /// -[NameAndRankValue(nameof(ADB.Polyline), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +[NameAndRankValue(typeof(ADB.Polyline), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] public class PolylineToSpeckleConverter : IToSpeckleTopLevelConverter, ITypedConverter diff --git a/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/RegionToSpeckleConverter.cs b/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/RegionToSpeckleConverter.cs new file mode 100644 index 000000000..72ba00eef --- /dev/null +++ b/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/RegionToSpeckleConverter.cs @@ -0,0 +1,33 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Sdk.Common.Exceptions; +using Speckle.Sdk.Models; + +namespace Speckle.Converters.Autocad.Geometry; + +[NameAndRankValue(typeof(ADB.Region), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class RegionToSpeckleConverter : IToSpeckleTopLevelConverter, ITypedConverter +{ + private readonly ITypedConverter _brepConverter; + + public RegionToSpeckleConverter(ITypedConverter brepConverter) + { + _brepConverter = brepConverter; + } + + public Base Convert(object target) => Convert((ADB.Region)target); + + public SOG.Mesh Convert(ADB.Region target) + { + using ABR.Brep brep = new(target); + if (brep.IsNull) + { + throw new ConversionException("Could not retrieve brep from the region."); + } + + SOG.Mesh mesh = _brepConverter.Convert(brep); + mesh.area = target.Area; + + return mesh; + } +} diff --git a/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/Solid3dToSpeckleConverter.cs b/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/Solid3dToSpeckleConverter.cs index f0989e1f2..02a8a840c 100644 --- a/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/Solid3dToSpeckleConverter.cs +++ b/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/Solid3dToSpeckleConverter.cs @@ -4,7 +4,7 @@ using Speckle.Sdk.Models; namespace Speckle.Converters.Autocad.Geometry; -[NameAndRankValue(nameof(ADB.Solid3d), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +[NameAndRankValue(typeof(ADB.Solid3d), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] public class Solid3dToSpeckleConverter : IToSpeckleTopLevelConverter { private readonly ITypedConverter _solidConverter; diff --git a/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/SplineToSpeckleConverter.cs b/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/SplineToSpeckleConverter.cs index 955096c7c..4512da61a 100644 --- a/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/SplineToSpeckleConverter.cs +++ b/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/SplineToSpeckleConverter.cs @@ -4,7 +4,7 @@ using Speckle.Sdk.Models; namespace Speckle.Converters.Autocad.ToSpeckle.Geometry; -[NameAndRankValue(nameof(ADB.Spline), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +[NameAndRankValue(typeof(ADB.Spline), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] public class SplineToSpeckleConverter : IToSpeckleTopLevelConverter { private readonly ITypedConverter _splineConverter; diff --git a/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/SubDMeshToSpeckleConverter.cs b/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/SubDMeshToSpeckleConverter.cs index 6e168f9a7..608c31243 100644 --- a/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/SubDMeshToSpeckleConverter.cs +++ b/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/SubDMeshToSpeckleConverter.cs @@ -1,24 +1,17 @@ using Speckle.Converters.Common; using Speckle.Converters.Common.Objects; +using Speckle.Sdk; using Speckle.Sdk.Models; namespace Speckle.Converters.Autocad.Geometry; -[NameAndRankValue(nameof(ADB.SubDMesh), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +[NameAndRankValue(typeof(ADB.SubDMesh), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] public class DBSubDMeshToSpeckleConverter : IToSpeckleTopLevelConverter { - private readonly ITypedConverter _pointConverter; - private readonly ITypedConverter _boxConverter; private readonly IConverterSettingsStore _settingsStore; - public DBSubDMeshToSpeckleConverter( - ITypedConverter pointConverter, - ITypedConverter boxConverter, - IConverterSettingsStore settingsStore - ) + public DBSubDMeshToSpeckleConverter(IConverterSettingsStore settingsStore) { - _pointConverter = pointConverter; - _boxConverter = boxConverter; _settingsStore = settingsStore; } @@ -71,9 +64,6 @@ public class DBSubDMeshToSpeckleConverter : IToSpeckleTopLevelConverter ) .ToList(); - // bbox - SOG.Box bbox = _boxConverter.Convert(target.GeometricExtents); - SOG.Mesh speckleMesh = new() { @@ -81,9 +71,15 @@ public class DBSubDMeshToSpeckleConverter : IToSpeckleTopLevelConverter faces = faces, colors = colors, units = _settingsStore.Current.SpeckleUnits, - bbox = bbox + area = target.ComputeSurfaceArea() }; + try + { + speckleMesh.volume = target.ComputeVolume(); + } + catch (Exception e) when (!e.IsFatal()) { } // for non-volumetric meshes + return speckleMesh; } } diff --git a/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/SurfaceToSpeckleConverter.cs b/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/SurfaceToSpeckleConverter.cs new file mode 100644 index 000000000..786a91ced --- /dev/null +++ b/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/SurfaceToSpeckleConverter.cs @@ -0,0 +1,30 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Sdk.Common.Exceptions; +using Speckle.Sdk.Models; + +namespace Speckle.Converters.Autocad.Geometry; + +[NameAndRankValue(typeof(ADB.Surface), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class SurfaceToSpeckleConverter : IToSpeckleTopLevelConverter, ITypedConverter +{ + private readonly ITypedConverter _brepConverter; + + public SurfaceToSpeckleConverter(ITypedConverter brepConverter) + { + _brepConverter = brepConverter; + } + + public Base Convert(object target) => Convert((ADB.Surface)target); + + public SOG.Mesh Convert(ADB.Surface target) + { + using ABR.Brep brep = new(target); + if (brep.IsNull) + { + throw new ConversionException("Could not retrieve brep from the plane surface."); + } + + return _brepConverter.Convert(brep); + } +} diff --git a/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Properties/ExtensionDictionaryExtractor.cs b/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Properties/ExtensionDictionaryExtractor.cs new file mode 100644 index 000000000..7c2d00ffb --- /dev/null +++ b/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Properties/ExtensionDictionaryExtractor.cs @@ -0,0 +1,58 @@ +using Speckle.Converters.Autocad; +using Speckle.Converters.Common; + +namespace Speckle.Converters.AutocadShared.ToSpeckle; + +/// +/// Extracts extension dictionaries out from an element. Expects to be scoped per operation. +/// +public class ExtensionDictionaryExtractor +{ + private readonly IConverterSettingsStore _settingsStore; + + public ExtensionDictionaryExtractor(IConverterSettingsStore settingsStore) + { + _settingsStore = settingsStore; + } + + /// + /// Extracts extension dictionary out from an entity. Expects to be scoped per operation. + /// + /// + /// + public Dictionary? GetExtensionDictionary(ADB.Entity entity) + { + if (entity is null || entity.ExtensionDictionary == ADB.ObjectId.Null) + { + return null; + } + + Dictionary extensionDictionaryDict = new(); + + using (ADB.Transaction tr = _settingsStore.Current.Document.TransactionManager.StartTransaction()) + { + var extensionDictionary = (ADB.DBDictionary)tr.GetObject(entity.ExtensionDictionary, ADB.OpenMode.ForRead, false); + + foreach (ADB.DBDictionaryEntry entry in extensionDictionary) + { + if (tr.GetObject(entry.Value, ADB.OpenMode.ForRead) is ADB.Xrecord xRecord) // sometimes these can be RXClass objects, in property sets + { + Dictionary entryDict = new(); + foreach (ADB.TypedValue xEntry in xRecord.Data) + { + entryDict[xEntry.TypeCode.ToString()] = xEntry.Value; + } + + if (entryDict.Count > 0) + { + extensionDictionaryDict[$"{entry.Key}"] = entryDict; + } + } + } + + tr.Commit(); + } + + return extensionDictionaryDict.Count > 0 ? extensionDictionaryDict : null; + } +} diff --git a/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Properties/PropertiesExtractor.cs b/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Properties/PropertiesExtractor.cs new file mode 100644 index 000000000..6d0bea663 --- /dev/null +++ b/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Properties/PropertiesExtractor.cs @@ -0,0 +1,38 @@ +namespace Speckle.Converters.AutocadShared.ToSpeckle; + +/// +/// Extracts properties for autocad entities. NOTE: currently not in use in acad +/// +public class PropertiesExtractor +{ + private readonly ExtensionDictionaryExtractor _extensionDictionaryExtractor; + + public PropertiesExtractor(ExtensionDictionaryExtractor extensionDictionaryExtractor) + { + _extensionDictionaryExtractor = extensionDictionaryExtractor; + } + + public Dictionary GetProperties(ADB.Entity entity) + { + Dictionary properties = new(); + AddDictionaryToPropertyDictionary( + _extensionDictionaryExtractor.GetExtensionDictionary(entity), + "Extension Dictionary", + properties + ); + + return properties; + } + + private void AddDictionaryToPropertyDictionary( + Dictionary? entryDictionary, + string entryName, + Dictionary propertyDictionary + ) + { + if (entryDictionary is not null && entryDictionary.Count > 0) + { + propertyDictionary.Add(entryName, entryDictionary); + } + } +} diff --git a/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Raw/BrepToSpeckleRawConverter.cs b/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Raw/BrepToSpeckleRawConverter.cs new file mode 100644 index 000000000..b97ed7727 --- /dev/null +++ b/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Raw/BrepToSpeckleRawConverter.cs @@ -0,0 +1,82 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Sdk; +using Speckle.Sdk.Common.Exceptions; + +namespace Speckle.Converters.Autocad.ToSpeckle.Raw; + +public class BrepToSpeckleRawConverter : ITypedConverter +{ + private readonly IConverterSettingsStore _settingsStore; + + public BrepToSpeckleRawConverter(IConverterSettingsStore settingsStore) + { + _settingsStore = settingsStore; + } + + public SOG.Mesh Convert(ABR.Brep target) + { + if (target.IsNull) + { + throw new ConversionException("Brep was null."); + } + + List faces = new(); + List vertices = new(); + int vertexCount = 0; + + using (var control = new ABR.Mesh2dControl()) + { + // These settings may need adjusting + control.MaxSubdivisions = 10000; + + // create mesh filters + using (var filter = new ABR.Mesh2dFilter()) + { + filter.Insert(target, control); + using (ABR.Mesh2d m = new(filter)) + { + foreach (ABR.Element2d? e in m.Element2ds) + { + // add number of vertices for this face + int nodeCount = e.Nodes.Count(); + faces.Add(nodeCount); + + foreach (var n in e.Nodes) + { + // add index of current vertex to face + faces.Add(vertexCount); + vertexCount++; + + // add vertex coords + vertices.Add(n.Point.X); + vertices.Add(n.Point.Y); + vertices.Add(n.Point.Z); + n.Dispose(); + } + + e.Dispose(); + } + } + } + + // create speckle mesh + SOG.Mesh mesh = + new() + { + faces = faces, + vertices = vertices, + units = _settingsStore.Current.SpeckleUnits, + area = target.GetSurfaceArea() + }; + + try + { + mesh.volume = target.GetVolume(); + } + catch (ABR.Exception e) when (!e.IsFatal()) { } // exceptions can be thrown for non-volumetric breps + + return mesh; + } + } +} diff --git a/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Raw/DBBodyToSpeckleRawConverter.cs b/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Raw/DBBodyToSpeckleRawConverter.cs index 39eb631eb..af31c3469 100644 --- a/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Raw/DBBodyToSpeckleRawConverter.cs +++ b/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Raw/DBBodyToSpeckleRawConverter.cs @@ -1,6 +1,4 @@ -using Speckle.Converters.Common; using Speckle.Converters.Common.Objects; -using Speckle.Sdk; using Speckle.Sdk.Common.Exceptions; using Speckle.Sdk.Models; @@ -8,19 +6,11 @@ namespace Speckle.Converters.Autocad.ToSpeckle.Raw; public class DBBodyToSpeckleRawConverter : ITypedConverter { - private readonly ITypedConverter _pointConverter; - private readonly ITypedConverter _boxConverter; - private readonly IConverterSettingsStore _settingsStore; + private readonly ITypedConverter _brepConverter; - public DBBodyToSpeckleRawConverter( - ITypedConverter pointConverter, - ITypedConverter boxConverter, - IConverterSettingsStore settingsStore - ) + public DBBodyToSpeckleRawConverter(ITypedConverter brepConverter) { - _pointConverter = pointConverter; - _boxConverter = boxConverter; - _settingsStore = settingsStore; + _brepConverter = brepConverter; } public Base Convert(object target) => Convert((ADB.Body)target); @@ -33,59 +23,6 @@ public class DBBodyToSpeckleRawConverter : ITypedConverter throw new ConversionException("Could not retrieve brep from the body."); } - var vertices = new List(); - var faces = new List(); - - // create mesh from solid with mesh filter - using ABR.Mesh2dControl control = new(); - control.MaxSubdivisions = 10000; // POC: these settings may need adjusting - using ABR.Mesh2dFilter filter = new(); - filter.Insert(brep, control); - using ABR.Mesh2d m = new(filter); - foreach (ABR.Element2d e in m.Element2ds) - { - // get vertices - List faceIndices = new(); - foreach (ABR.Node n in e.Nodes) - { - faceIndices.Add(vertices.Count); - vertices.Add(n.Point); - n.Dispose(); - } - - // get faces - List faceList = new() { e.Nodes.Count() }; - for (int i = 0; i < e.Nodes.Count(); i++) - { - faceList.Add(faceIndices[i]); - } - - faces.AddRange(faceList); - - e.Dispose(); - } - - // mesh props - var convertedVertices = vertices.SelectMany(o => _pointConverter.Convert(o).ToList()).ToList(); - SOG.Box bbox = _boxConverter.Convert(target.GeometricExtents); - - // create speckle mesh - SOG.Mesh mesh = - new() - { - vertices = convertedVertices, - faces = faces, - area = brep.GetSurfaceArea(), - units = _settingsStore.Current.SpeckleUnits, - bbox = bbox - }; - - try - { - mesh.volume = brep.GetVolume(); - } - catch (Autodesk.AutoCAD.BoundaryRepresentation.Exception e) when (!e.IsFatal()) { } - - return mesh; + return _brepConverter.Convert(brep); } } diff --git a/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Raw/DBSolid3dToSpeckleRawConverter.cs b/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Raw/DBSolid3dToSpeckleRawConverter.cs index 80ca83be0..6aecd0554 100644 --- a/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Raw/DBSolid3dToSpeckleRawConverter.cs +++ b/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Raw/DBSolid3dToSpeckleRawConverter.cs @@ -1,4 +1,3 @@ -using Speckle.Converters.Common; using Speckle.Converters.Common.Objects; using Speckle.Sdk.Common.Exceptions; using Speckle.Sdk.Models; @@ -7,19 +6,11 @@ namespace Speckle.Converters.Autocad.ToSpeckle.Raw; public class Solid3dToSpeckleRawConverter : ITypedConverter { - private readonly ITypedConverter _pointConverter; - private readonly ITypedConverter _boxConverter; - private readonly IConverterSettingsStore _settingsStore; + private readonly ITypedConverter _brepConverter; - public Solid3dToSpeckleRawConverter( - ITypedConverter pointConverter, - ITypedConverter boxConverter, - IConverterSettingsStore settingsStore - ) + public Solid3dToSpeckleRawConverter(ITypedConverter brepConverter) { - _pointConverter = pointConverter; - _boxConverter = boxConverter; - _settingsStore = settingsStore; + _brepConverter = brepConverter; } public Base Convert(object target) => Convert((ADB.Solid3d)target); @@ -32,55 +23,7 @@ public class Solid3dToSpeckleRawConverter : ITypedConverter(); - var faces = new List(); - - // create mesh from solid with mesh filter - using ABR.Mesh2dControl control = new(); - control.MaxSubdivisions = 10000; // POC: these settings may need adjusting - using ABR.Mesh2dFilter filter = new(); - filter.Insert(brep, control); - using ABR.Mesh2d m = new(filter); - foreach (ABR.Element2d e in m.Element2ds) - { - // get vertices - List faceIndices = new(); - foreach (ABR.Node n in e.Nodes) - { - faceIndices.Add(vertices.Count); - vertices.Add(n.Point); - n.Dispose(); - } - - // get faces - List faceList = new() { e.Nodes.Count() }; - for (int i = 0; i < e.Nodes.Count(); i++) - { - faceList.Add(faceIndices[i]); - } - - faces.AddRange(faceList); - - e.Dispose(); - } - - // mesh props - var convertedVertices = vertices.SelectMany(o => _pointConverter.Convert(o).ToList()).ToList(); - double volume = target.MassProperties.Volume; - double area = target.Area; - SOG.Box bbox = _boxConverter.Convert(target.GeometricExtents); - - // create speckle mesh - SOG.Mesh mesh = - new() - { - vertices = convertedVertices, - faces = faces, - units = _settingsStore.Current.SpeckleUnits, - bbox = bbox, - area = area, - volume = volume - }; + SOG.Mesh mesh = _brepConverter.Convert(brep); return mesh; } diff --git a/Converters/CSi/Speckle.Converters.CSiShared/CSiConversionSettings.cs b/Converters/CSi/Speckle.Converters.CSiShared/CSiConversionSettings.cs new file mode 100644 index 000000000..313c12637 --- /dev/null +++ b/Converters/CSi/Speckle.Converters.CSiShared/CSiConversionSettings.cs @@ -0,0 +1,3 @@ +namespace Speckle.Converters.CSiShared; + +public record CsiConversionSettings(cSapModel SapModel, string SpeckleUnits); diff --git a/Converters/CSi/Speckle.Converters.CSiShared/CsiConversionSettingsFactory.cs b/Converters/CSi/Speckle.Converters.CSiShared/CsiConversionSettingsFactory.cs new file mode 100644 index 000000000..bc6b13e3d --- /dev/null +++ b/Converters/CSi/Speckle.Converters.CSiShared/CsiConversionSettingsFactory.cs @@ -0,0 +1,16 @@ +using Speckle.Converters.Common; +using Speckle.InterfaceGenerator; + +namespace Speckle.Converters.CSiShared; + +[GenerateAutoInterface] +public class CsiConversionSettingsFactory( + IHostToSpeckleUnitConverter unitsConverter, + IConverterSettingsStore settingsStore +) : ICsiConversionSettingsFactory +{ + public CsiConversionSettings Current => settingsStore.Current; + + public CsiConversionSettings Create(cSapModel document) => + new(document, unitsConverter.ConvertOrThrow(document.GetPresentUnits())); +} diff --git a/Converters/CSi/Speckle.Converters.CSiShared/CsiRootToSpeckleConverter.cs b/Converters/CSi/Speckle.Converters.CSiShared/CsiRootToSpeckleConverter.cs new file mode 100644 index 000000000..a44a9d863 --- /dev/null +++ b/Converters/CSi/Speckle.Converters.CSiShared/CsiRootToSpeckleConverter.cs @@ -0,0 +1,41 @@ +using Microsoft.Extensions.Logging; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Converters.Common.Registration; +using Speckle.Sdk.Common.Exceptions; +using Speckle.Sdk.Models; + +namespace Speckle.Converters.CSiShared; + +public class CsiRootToSpeckleConverter : IRootToSpeckleConverter +{ + private readonly IConverterManager _toSpeckle; + private readonly IConverterSettingsStore _settingsStore; + private readonly ILogger _logger; + + public CsiRootToSpeckleConverter( + IConverterManager toSpeckle, + IConverterSettingsStore settingsStore, + ILogger logger + ) + { + _toSpeckle = toSpeckle; + _settingsStore = settingsStore; + _logger = logger; + } + + public Base Convert(object target) + { + if (target is not ICsiWrapper) + { + throw new ValidationException($"Target object is not a CSiWrapper. It's a ${target.GetType()}"); + } + + Type type = target.GetType(); + var objectConverter = _toSpeckle.ResolveConverter(type, true); + + Base result = objectConverter.Convert(target); + + return result; + } +} diff --git a/Converters/CSi/Speckle.Converters.CSiShared/CsiToSpeckleUnitConverter.cs b/Converters/CSi/Speckle.Converters.CSiShared/CsiToSpeckleUnitConverter.cs new file mode 100644 index 000000000..4082abd8f --- /dev/null +++ b/Converters/CSi/Speckle.Converters.CSiShared/CsiToSpeckleUnitConverter.cs @@ -0,0 +1,42 @@ +using Speckle.Converters.Common; +using Speckle.Sdk.Common; +using Speckle.Sdk.Common.Exceptions; + +namespace Speckle.Converters.CSiShared; + +/// +/// Convert CSi eUnits enumeration to Speckle units. +/// +/// +/// CSi GetPresentUnits() valid for both SAP 2000 and ETABS. +/// Represents units transmitted through API calls and not necessarily those displayed in GUI. +/// +public class CsiToSpeckleUnitConverter : IHostToSpeckleUnitConverter +{ + private readonly Dictionary _unitMapping = new Dictionary(); + + public CsiToSpeckleUnitConverter() + { + _unitMapping[eUnits.lb_in_F] = Units.Inches; + _unitMapping[eUnits.lb_ft_F] = Units.Feet; + _unitMapping[eUnits.kip_in_F] = Units.Inches; + _unitMapping[eUnits.kip_ft_F] = Units.Feet; + _unitMapping[eUnits.kN_mm_C] = Units.Millimeters; + _unitMapping[eUnits.kN_m_C] = Units.Meters; + _unitMapping[eUnits.kgf_mm_C] = Units.Millimeters; + _unitMapping[eUnits.kgf_m_C] = Units.Meters; + _unitMapping[eUnits.N_mm_C] = Units.Millimeters; + _unitMapping[eUnits.N_m_C] = Units.Meters; + _unitMapping[eUnits.Ton_mm_C] = Units.Millimeters; + _unitMapping[eUnits.Ton_m_C] = Units.Meters; + _unitMapping[eUnits.kN_cm_C] = Units.Centimeters; + _unitMapping[eUnits.kgf_cm_C] = Units.Centimeters; + _unitMapping[eUnits.N_cm_C] = Units.Centimeters; + _unitMapping[eUnits.Ton_cm_C] = Units.Centimeters; + } + + public string ConvertOrThrow(eUnits hostUnit) => + _unitMapping.TryGetValue(hostUnit, out string? value) + ? value + : throw new UnitNotSupportedException($"The Unit System \"{hostUnit}\" is unsupported."); +} diff --git a/Converters/CSi/Speckle.Converters.CSiShared/CsiWrappers.cs b/Converters/CSi/Speckle.Converters.CSiShared/CsiWrappers.cs new file mode 100644 index 000000000..ab5b02da0 --- /dev/null +++ b/Converters/CSi/Speckle.Converters.CSiShared/CsiWrappers.cs @@ -0,0 +1,90 @@ +using Speckle.Converters.CSiShared.Utils; + +namespace Speckle.Converters.CSiShared; + +public interface ICsiWrapper +{ + string Name { get; set; } + ModelObjectType ObjectType { get; } + string ObjectName { get; } +} + +/// +/// Based on the GetSelected() returns of objectType and objectName, we need to create a CSiWrapper object. +/// +/// +/// Creating a class that can be used to pass a type to the converter. +/// Since the API only provides a framework for us to query the model, we don't get instances. +/// The types are the same for both SAP 2000 and ETABS. +/// +public abstract class CsiWrapperBase : ICsiWrapper +{ + public required string Name { get; set; } + public abstract ModelObjectType ObjectType { get; } + public abstract string ObjectName { get; } +} + +public class CsiJointWrapper : CsiWrapperBase +{ + public override ModelObjectType ObjectType => ModelObjectType.JOINT; + public override string ObjectName => ModelObjectType.JOINT.ToString(); +} + +public class CsiFrameWrapper : CsiWrapperBase +{ + public override ModelObjectType ObjectType => ModelObjectType.FRAME; + public override string ObjectName => ModelObjectType.FRAME.ToString(); +} + +public class CsiCableWrapper : CsiWrapperBase +{ + public override ModelObjectType ObjectType => ModelObjectType.CABLE; + public override string ObjectName => ModelObjectType.CABLE.ToString(); +} + +public class CsiTendonWrapper : CsiWrapperBase +{ + public override ModelObjectType ObjectType => ModelObjectType.TENDON; + public override string ObjectName => ModelObjectType.TENDON.ToString(); +} + +public class CsiShellWrapper : CsiWrapperBase +{ + public override ModelObjectType ObjectType => ModelObjectType.SHELL; + public override string ObjectName => ModelObjectType.SHELL.ToString(); +} + +public class CsiSolidWrapper : CsiWrapperBase +{ + public override ModelObjectType ObjectType => ModelObjectType.SOLID; + public override string ObjectName => ModelObjectType.SOLID.ToString(); +} + +public class CsiLinkWrapper : CsiWrapperBase +{ + public override ModelObjectType ObjectType => ModelObjectType.LINK; + public override string ObjectName => ModelObjectType.LINK.ToString(); +} + +/// +/// ObjectType specific wrappers created during bindings. +/// +/// +/// Switch statements based off of the objectType int return. +/// Used in the connectors and allows converters to be resolved effectively. +/// +public static class CsiWrapperFactory +{ + public static ICsiWrapper Create(int objectType, string name) => + objectType switch + { + 1 => new CsiJointWrapper { Name = name }, + 2 => new CsiFrameWrapper { Name = name }, + 3 => new CsiCableWrapper { Name = name }, // TODO: CsiCableWrapper + 4 => new CsiTendonWrapper { Name = name }, // TODO: CsiTendonWrapper + 5 => new CsiShellWrapper { Name = name }, + 6 => new CsiSolidWrapper { Name = name }, // TODO: CsiSolidWrapper + 7 => new CsiLinkWrapper { Name = name }, // TODO: CsiLinkWrapper + _ => throw new ArgumentOutOfRangeException(nameof(objectType), $"Unsupported object type: {objectType}") + }; +} diff --git a/Converters/CSi/Speckle.Converters.CSiShared/Extensions/DatabaseTableExtensions.cs b/Converters/CSi/Speckle.Converters.CSiShared/Extensions/DatabaseTableExtensions.cs new file mode 100644 index 000000000..3b1569064 --- /dev/null +++ b/Converters/CSi/Speckle.Converters.CSiShared/Extensions/DatabaseTableExtensions.cs @@ -0,0 +1,129 @@ +namespace Speckle.Converters.CSiShared.Extensions; + +/// +/// Csi Api returns a one-dimensional array of the table data. Any cDatabaseTable queries will require some processing. +/// The TableData extension processes queries for GetTableForDisplayArray. +/// +/// +/// TableData implemented as a record. Reasons for this include: +/// +/// Keeping data immutable (preventing accidental modifications). +/// Better choice for large data sets (heap allocation). +/// +/// Notes: +/// +/// A cDatabaseTable query returns ALL objects of a type. This is an expensive operation. However, the typical use-case involves sending the entire Etabs/Sap model. +/// High initial memory usage when creating dictionaries for all rows of data. +/// Benefits of the dictionary evident during send operations when most/all objects are sent (and thus queried). +/// Single upfront dictionary creation preferred over repeated on-demand creation +/// Yes, Csi returns all data as strings. Even int, double etc. +/// +/// +public record TableData +{ + private readonly string[] _columnNames; // "fieldKeys" in api docs + private readonly string[] _rawTableData; // indicating raw, one-dimensional array of table data (before processing) + private readonly int _rowCount; // Number of rows + private IReadOnlyDictionary>? _processedRows; // Cached data structure + private readonly string _indexColumn; // column used to index/identify rows (typically, "UniqueName") + + public TableData(string[] columnNames, string[] rawTableData, int rowCount, string indexColumn) + { + _columnNames = columnNames; + _rawTableData = rawTableData; + _rowCount = rowCount; + _indexColumn = indexColumn; + } + + /// + /// Gets table data as a dictionary mapping indexColumn (typically "UniqueName" to _processedRows). + /// Each row is itself a dictionary mapping column names to their values. + /// Computed once on first access and cached. + /// + /// + /// Motivation: + /// + /// One-dimensional array => structured dictionary format + /// Each row keyed by its "UniqueName" value + /// Each row value is itself a dictionary of field keys to values + /// + /// + public IReadOnlyDictionary> Rows + { + get + { + if (_processedRows != null) // Lazy loading - only build dictionary when first accessed + { + return _processedRows; + } + + var columnsPerRow = _columnNames.Length; + var indexColumnIndex = Array.IndexOf(_columnNames, _indexColumn); + + if (indexColumnIndex == -1) + { + throw new InvalidOperationException( + $"Row data structured according specified '{_indexColumn}' field. This was not found in the database." + ); + } + + // Pre-size dictionary with known capacity + var rows = new Dictionary>(_rowCount); + + // Create a field index lookup to avoid repeated Array.IndexOf calls + var fieldIndexLookup = new Dictionary(columnsPerRow); + for (int i = 0; i < _columnNames.Length; i++) + { + fieldIndexLookup[_columnNames[i]] = i; + } + + // Process each row + for (int rowStart = 0; rowStart < _rawTableData.Length; rowStart += columnsPerRow) + { + var keyValue = _rawTableData[rowStart + indexColumnIndex]; + + // Pre-size the row dictionary + var row = new Dictionary(columnsPerRow, StringComparer.Ordinal); + + // Use index lookup instead of repeated string comparisons + foreach (var kvp in fieldIndexLookup) + { + row[kvp.Key] = _rawTableData[rowStart + kvp.Value]; + } + + rows[keyValue] = row; + } + + _processedRows = rows; + return _processedRows; + } + } + + /// + /// Retrieves a string value from a specific row and column from the table data. + /// + /// The unique identifier for the row, matching the value in the index column (e.g., "UniqueName") + /// The name of the column containing the desired value + /// The string value found at the specified row and column intersection + /// Thrown when either the row or column is not found in the table + public string GetRowValue(string rowKey, string columnName) + { + if (TryGetValue(rowKey, columnName, out var value)) + { + return value; + } + + throw new InvalidOperationException($"Failed to get value for row '{rowKey}', column '{columnName}'"); + } + + private bool TryGetValue(string rowKey, string columnName, out string value) + { + if (Rows.TryGetValue(rowKey, out var row) && row.TryGetValue(columnName, out value!)) + { + return true; + } + + value = string.Empty; + return false; + } +} diff --git a/Converters/CSi/Speckle.Converters.CSiShared/Extensions/SpeckleApplicationIdExtensions.cs b/Converters/CSi/Speckle.Converters.CSiShared/Extensions/SpeckleApplicationIdExtensions.cs new file mode 100644 index 000000000..845aacfb4 --- /dev/null +++ b/Converters/CSi/Speckle.Converters.CSiShared/Extensions/SpeckleApplicationIdExtensions.cs @@ -0,0 +1,64 @@ +namespace Speckle.Converters.CSiShared.Extensions; + +public static class SpeckleApplicationIdExtensions +{ + /// + /// Retrieves the Speckle object application id for a joint object + /// + public static string GetSpeckleApplicationId(this CsiJointWrapper wrapper, cSapModel sapModel) + { + string applicationId = string.Empty; + _ = sapModel.PointObj.GetGUID(wrapper.Name, ref applicationId); + return applicationId; + } + + /// + /// Retrieves the Speckle object application id for a frame object + /// + public static string GetSpeckleApplicationId(this CsiFrameWrapper wrapper, cSapModel sapModel) + { + string applicationId = string.Empty; + _ = sapModel.FrameObj.GetGUID(wrapper.Name, ref applicationId); + return applicationId; + } + + /// + /// Retrieves the Speckle object application id for a cable object + /// + public static string GetSpeckleApplicationId(this CsiCableWrapper wrapper, cSapModel sapModel) + { + string applicationId = string.Empty; + _ = sapModel.CableObj.GetGUID(wrapper.Name, ref applicationId); + return applicationId; + } + + /// + /// Retrieves the Speckle object application id for a shell object + /// + public static string GetSpeckleApplicationId(this CsiShellWrapper wrapper, cSapModel sapModel) + { + string applicationId = string.Empty; + _ = sapModel.AreaObj.GetGUID(wrapper.Name, ref applicationId); + return applicationId; + } + + /// + /// Retrieves the Speckle object application id for a solid object + /// + public static string GetSpeckleApplicationId(this CsiSolidWrapper wrapper, cSapModel sapModel) + { + string applicationId = string.Empty; + _ = sapModel.SolidObj.GetGUID(wrapper.Name, ref applicationId); + return applicationId; + } + + /// + /// Retrieves the Speckle object application id for a link object + /// + public static string GetSpeckleApplicationId(this CsiLinkWrapper wrapper, cSapModel sapModel) + { + string applicationId = string.Empty; + _ = sapModel.LinkObj.GetGUID(wrapper.Name, ref applicationId); + return applicationId; + } +} diff --git a/Converters/CSi/Speckle.Converters.CSiShared/GlobalUsing.cs b/Converters/CSi/Speckle.Converters.CSiShared/GlobalUsing.cs new file mode 100644 index 000000000..2209f341b --- /dev/null +++ b/Converters/CSi/Speckle.Converters.CSiShared/GlobalUsing.cs @@ -0,0 +1 @@ +global using CSiAPIv1; diff --git a/Converters/CSi/Speckle.Converters.CSiShared/ServiceRegistration.cs b/Converters/CSi/Speckle.Converters.CSiShared/ServiceRegistration.cs new file mode 100644 index 000000000..b9a07b34c --- /dev/null +++ b/Converters/CSi/Speckle.Converters.CSiShared/ServiceRegistration.cs @@ -0,0 +1,41 @@ +using System.Reflection; +using Microsoft.Extensions.DependencyInjection; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Registration; +using Speckle.Converters.CSiShared.ToSpeckle.Helpers; +using Speckle.Sdk; + +namespace Speckle.Converters.CSiShared; + +public static class ServiceRegistration +{ + public static IServiceCollection AddCsiConverters(this IServiceCollection serviceCollection) + { + var converterAssembly = Assembly.GetExecutingAssembly(); + + // Register top-level converters + serviceCollection.AddRootCommon(converterAssembly); + + // Register property extractors + serviceCollection.AddScoped(); + serviceCollection.AddScoped(); + serviceCollection.AddScoped(); + serviceCollection.AddScoped(); + serviceCollection.AddScoped(); + serviceCollection.AddScoped(); + + // Register connector caches + serviceCollection.AddScoped(); + + // Settings and unit conversions + serviceCollection.AddApplicationConverters(converterAssembly); + serviceCollection.AddScoped< + IConverterSettingsStore, + ConverterSettingsStore + >(); + + serviceCollection.AddMatchingInterfacesAsTransient(converterAssembly); + + return serviceCollection; + } +} diff --git a/Converters/CSi/Speckle.Converters.CSiShared/Speckle.Converters.CSiShared.projitems b/Converters/CSi/Speckle.Converters.CSiShared/Speckle.Converters.CSiShared.projitems new file mode 100644 index 000000000..c8804b8fa --- /dev/null +++ b/Converters/CSi/Speckle.Converters.CSiShared/Speckle.Converters.CSiShared.projitems @@ -0,0 +1,37 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + 1b5c5fb2-3b22-4371-9aa5-3edf3b4d62de + + + Speckle.Converters.CSiShared + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Converters/CSi/Speckle.Converters.CSiShared/Speckle.Converters.CSiShared.shproj b/Converters/CSi/Speckle.Converters.CSiShared/Speckle.Converters.CSiShared.shproj new file mode 100644 index 000000000..2de5c6157 --- /dev/null +++ b/Converters/CSi/Speckle.Converters.CSiShared/Speckle.Converters.CSiShared.shproj @@ -0,0 +1,13 @@ + + + + 1b5c5fb2-3b22-4371-9aa5-3edf3b4d62de + 14.0 + + + + + + + + diff --git a/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Helpers/CsiFramePropertiesExtractor.cs b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Helpers/CsiFramePropertiesExtractor.cs new file mode 100644 index 000000000..ba3093b69 --- /dev/null +++ b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Helpers/CsiFramePropertiesExtractor.cs @@ -0,0 +1,200 @@ +using Speckle.Converters.Common; +using Speckle.Converters.CSiShared.Extensions; +using Speckle.Converters.CSiShared.Utils; + +namespace Speckle.Converters.CSiShared.ToSpeckle.Helpers; + +/// +/// Extracts properties common to frame elements across CSi products (e.g., Etabs, Sap2000) +/// using the FrameObj API calls. +/// +/// +/// Design Decisions: +/// +/// +/// +/// Individual methods preferred over batched calls due to: +/// +/// Independent API calls with no performance gain from batching (?) +/// Easier debugging and error tracing +/// Simpler maintenance as each method maps to one API concept +/// +/// +/// +/// +/// +public sealed class CsiFramePropertiesExtractor +{ + private readonly IConverterSettingsStore _settingsStore; + + private readonly CsiToSpeckleCacheSingleton _csiToSpeckleCacheSingleton; + + private static readonly string[] s_releaseKeys = + [ + "Axial", + "Shear 2 (Major)", + "Shear 3 (Minor)", + "Torsion", + "Moment 22 (Minor)", + "Moment 33 (Major)" + ]; // Note: caching keys for better performance + + public CsiFramePropertiesExtractor( + CsiToSpeckleCacheSingleton csiToSpeckleCacheSingleton, + IConverterSettingsStore settingsStore + ) + { + _csiToSpeckleCacheSingleton = csiToSpeckleCacheSingleton; + _settingsStore = settingsStore; + } + + public void ExtractProperties(CsiFrameWrapper frame, PropertyExtractionResult frameData) + { + frameData.ApplicationId = frame.GetSpeckleApplicationId(_settingsStore.Current.SapModel); + + var geometry = frameData.Properties.EnsureNested(ObjectPropertyCategory.GEOMETRY); + (geometry["I-End Joint"], geometry["J-End Joint"]) = GetEndPointNames(frame); + + var assignments = frameData.Properties.EnsureNested(ObjectPropertyCategory.ASSIGNMENTS); + assignments[CommonObjectProperty.GROUPS] = GetGroupAssigns(frame); + assignments[CommonObjectProperty.MATERIAL_OVERWRITE] = GetMaterialOverwrite(frame); + assignments[CommonObjectProperty.LOCAL_AXIS_2_ANGLE] = GetLocalAxes(frame); + assignments[CommonObjectProperty.PROPERTY_MODIFIERS] = GetModifiers(frame); + assignments["End Releases"] = GetReleases(frame); + + // NOTE: sectionId and materialId a "quick-fix" to enable filtering in the viewer etc. + // Assign sectionId to variable as this will be an argument for the GetMaterialName method + string sectionId = GetSectionName(frame); + string materialId = GetMaterialName(sectionId); + assignments[ObjectPropertyKey.SECTION_ID] = sectionId; + assignments[ObjectPropertyKey.MATERIAL_ID] = materialId; + + // store the object, section, and material id relationships in their corresponding caches to be accessed by the connector + if (!string.IsNullOrEmpty(sectionId)) + { + if (_csiToSpeckleCacheSingleton.FrameSectionCache.TryGetValue(sectionId, out List? frameIds)) + { + frameIds.Add(frameData.ApplicationId); + } + else + { + _csiToSpeckleCacheSingleton.FrameSectionCache.Add(sectionId, [frameData.ApplicationId]); + } + + if (!string.IsNullOrEmpty(materialId)) + { + if (_csiToSpeckleCacheSingleton.MaterialCache.TryGetValue(materialId, out List? sectionIds)) + { + // Since this is happening on the object level, we could be processing the same sectionIds (from different + // objects) many times. This is not necessary since we just want a set of sectionId corresponding to material + if (!sectionIds.Contains(sectionId)) + { + sectionIds.Add(sectionId); + } + } + else + { + _csiToSpeckleCacheSingleton.MaterialCache.Add(materialId, [sectionId]); + } + } + } + } + + private string[] GetGroupAssigns(CsiFrameWrapper frame) + { + int numberGroups = 0; + string[] groups = []; + _ = _settingsStore.Current.SapModel.FrameObj.GetGroupAssign(frame.Name, ref numberGroups, ref groups); + return (groups); + } + + private Dictionary GetLocalAxes(CsiFrameWrapper frame) + { + double angle = 0; + bool advanced = false; + _ = _settingsStore.Current.SapModel.FrameObj.GetLocalAxes(frame.Name, ref angle, ref advanced); + + Dictionary resultsDictionary = []; + resultsDictionary.AddWithUnits(CommonObjectProperty.ANGLE, angle, "Degrees"); + resultsDictionary[CommonObjectProperty.ADVANCED] = advanced.ToString(); + + return resultsDictionary; + } + + private string GetMaterialOverwrite(CsiFrameWrapper frame) + { + string propName = string.Empty; + _ = _settingsStore.Current.SapModel.FrameObj.GetMaterialOverwrite(frame.Name, ref propName); + return propName; + } + + private Dictionary GetModifiers(CsiFrameWrapper frame) + { + double[] value = []; + _ = _settingsStore.Current.SapModel.FrameObj.GetModifiers(frame.Name, ref value); + return new Dictionary + { + ["Area"] = value[0], + ["As2"] = value[1], + ["As3"] = value[2], + ["Torsion"] = value[3], + ["I22"] = value[4], + ["I33"] = value[5], + ["Mass"] = value[6], + ["Weight"] = value[7] + }; + } + + private (string point1, string point2) GetEndPointNames(CsiFrameWrapper frame) + { + string point1 = string.Empty, + point2 = string.Empty; + _ = _settingsStore.Current.SapModel.FrameObj.GetPoints(frame.Name, ref point1, ref point2); + return (point1, point2); + } + + private Dictionary GetReleases(CsiFrameWrapper frame) + { + bool[] ii = [], + jj = []; + double[] startValue = [], + endValue = []; + + _ = _settingsStore.Current.SapModel.FrameObj.GetReleases(frame.Name, ref ii, ref jj, ref startValue, ref endValue); + + return new Dictionary + { + ["End-I"] = CreateNodeReleases(ii, startValue), + ["End-J"] = CreateNodeReleases(jj, endValue), + }; + } + + // NOTE: Avoid duplicate dictionary creation logic for End-I and End-J in GetReleases() method + private static Dictionary CreateNodeReleases(bool[] releases, double[] values) => + s_releaseKeys + .Select( + (key, i) => // for each key, we want both the key (string) and index + new KeyValuePair( // for each key, create dictionary with Release and Stiffness + key, + new Dictionary { ["Release"] = releases[i], ["Stiffness"] = values[i] } + ) + ) + .ToDictionary(x => x.Key, x => x.Value); + + private string GetSectionName(CsiFrameWrapper frame) + { + string sectionName = string.Empty, + sAuto = string.Empty; + _ = _settingsStore.Current.SapModel.FrameObj.GetSection(frame.Name, ref sectionName, ref sAuto); + return sectionName; + } + + // NOTE: This is a little convoluted as we aren't on the cFrameObj level, but one deeper. + // As noted in ExtractProperties, this is just a quick-fix to get some displayable materialId parameter + private string GetMaterialName(string sectionName) + { + string materialName = string.Empty; + _ = _settingsStore.Current.SapModel.PropFrame.GetMaterial(sectionName, ref materialName); + return materialName; + } +} diff --git a/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Helpers/CsiJointPropertiesExtractor.cs b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Helpers/CsiJointPropertiesExtractor.cs new file mode 100644 index 000000000..5a74c4903 --- /dev/null +++ b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Helpers/CsiJointPropertiesExtractor.cs @@ -0,0 +1,72 @@ +using Speckle.Converters.Common; +using Speckle.Converters.CSiShared.Extensions; +using Speckle.Converters.CSiShared.Utils; + +namespace Speckle.Converters.CSiShared.ToSpeckle.Helpers; + +/// +/// Extracts properties common to joint elements across CSi products (e.g., ETABS, SAP2000) +/// using the PointObj API calls. +/// +/// +/// Design Decisions: +/// +/// +/// +/// Individual methods preferred over batched calls due to: +/// +/// Independent API calls with no performance gain from batching (?) +/// Easier debugging and error tracing +/// Simpler maintenance as each method maps to one API concept +/// +/// +/// +/// +/// +/// Responsibilities: +/// +/// Provides a focused interface for extracting properties specific to joint elements. +/// Ensures consistency in property extraction logic across supported CSi products. +/// +/// +public sealed class CsiJointPropertiesExtractor +{ + private readonly IConverterSettingsStore _settingsStore; + + public CsiJointPropertiesExtractor(IConverterSettingsStore settingsStore) + { + _settingsStore = settingsStore; + } + + public void ExtractProperties(CsiJointWrapper joint, PropertyExtractionResult jointData) + { + jointData.ApplicationId = joint.GetSpeckleApplicationId(_settingsStore.Current.SapModel); + + var assignments = jointData.Properties.EnsureNested(ObjectPropertyCategory.ASSIGNMENTS); + assignments[CommonObjectProperty.GROUPS] = new List(GetGroupAssigns(joint)); + assignments["Restraints"] = GetRestraints(joint); + } + + private string[] GetGroupAssigns(CsiJointWrapper joint) + { + int numberGroups = 0; + string[] groups = []; + _ = _settingsStore.Current.SapModel.PointObj.GetGroupAssign(joint.Name, ref numberGroups, ref groups); + return (groups); + } + + private Dictionary GetRestraints(CsiJointWrapper joint) + { + bool[] restraints = []; + _ = _settingsStore.Current.SapModel.PointObj.GetRestraint(joint.Name, ref restraints); + return new Dictionary + { + ["UX Restrained"] = restraints[0], + ["UY Restrained"] = restraints[1], + ["UZ Restrained"] = restraints[2], + ["RX Restrained"] = restraints[3], + ["RY Restrained"] = restraints[4], + ["RZ Restrained"] = restraints[5], + }; + } +} diff --git a/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Helpers/CsiShellPropertiesExtractor.cs b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Helpers/CsiShellPropertiesExtractor.cs new file mode 100644 index 000000000..03b2f9075 --- /dev/null +++ b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Helpers/CsiShellPropertiesExtractor.cs @@ -0,0 +1,103 @@ +using Speckle.Converters.Common; +using Speckle.Converters.CSiShared.Extensions; +using Speckle.Converters.CSiShared.Utils; + +namespace Speckle.Converters.CSiShared.ToSpeckle.Helpers; + +/// +/// Extracts properties common to shell elements across CSi products (e.g., Etabs, Sap2000) +/// using the AreaObj API calls. +/// +/// +/// Design Decisions: +/// +/// +/// +/// Individual methods preferred over batched calls due to: +/// +/// Independent API calls with no performance gain from batching (?) +/// Easier debugging and error tracing +/// Simpler maintenance as each method maps to one API concept +/// +/// +/// +/// +/// +public sealed class CsiShellPropertiesExtractor +{ + private readonly IConverterSettingsStore _settingsStore; + + public CsiShellPropertiesExtractor(IConverterSettingsStore settingsStore) + { + _settingsStore = settingsStore; + } + + public void ExtractProperties(CsiShellWrapper shell, PropertyExtractionResult shellData) + { + shellData.ApplicationId = shell.GetSpeckleApplicationId(_settingsStore.Current.SapModel); + + var geometry = shellData.Properties.EnsureNested(ObjectPropertyCategory.GEOMETRY); + geometry["Joints"] = GetPointNames(shell); // TODO: 🪲 Viewer shows 4 but only displays 3 + + var assignments = shellData.Properties.EnsureNested(ObjectPropertyCategory.ASSIGNMENTS); + assignments[CommonObjectProperty.GROUPS] = GetGroupAssigns(shell); + assignments[CommonObjectProperty.LOCAL_AXIS_2_ANGLE] = GetLocalAxes(shell); + assignments[CommonObjectProperty.MATERIAL_OVERWRITE] = GetMaterialOverwrite(shell); + assignments[CommonObjectProperty.PROPERTY_MODIFIERS] = GetModifiers(shell); + } + + private string[] GetGroupAssigns(CsiShellWrapper shell) + { + int numberGroups = 0; + string[] groups = []; + _ = _settingsStore.Current.SapModel.AreaObj.GetGroupAssign(shell.Name, ref numberGroups, ref groups); + return (groups); + } + + private Dictionary GetLocalAxes(CsiShellWrapper shell) + { + double angle = 0; + bool advanced = false; + _ = _settingsStore.Current.SapModel.AreaObj.GetLocalAxes(shell.Name, ref angle, ref advanced); + + Dictionary resultsDictionary = []; + resultsDictionary.AddWithUnits(CommonObjectProperty.ANGLE, angle, "Degrees"); + resultsDictionary[CommonObjectProperty.ADVANCED] = advanced.ToString(); + + return resultsDictionary; + } + + private string GetMaterialOverwrite(CsiShellWrapper shell) + { + string propName = string.Empty; + _ = _settingsStore.Current.SapModel.AreaObj.GetMaterialOverwrite(shell.Name, ref propName); + return propName; + } + + private Dictionary GetModifiers(CsiShellWrapper shell) + { + double[] value = []; + _ = _settingsStore.Current.SapModel.AreaObj.GetModifiers(shell.Name, ref value); + return new Dictionary + { + ["Membrane F11 Modifier"] = value[0], + ["Membrane F22 Modifier"] = value[1], + ["Membrane F12 Modifier"] = value[2], + ["Bending M11 Modifier"] = value[3], + ["Bending M22 Modifier"] = value[4], + ["Bending M12 Modifier"] = value[5], + ["Shear V13 Modifier"] = value[6], + ["Shear V23 Modifier"] = value[7], + ["Mass"] = value[8], + ["Weight"] = value[9] + }; + } + + private string[] GetPointNames(CsiShellWrapper shell) + { + int numberPoints = 0; + string[] pointNames = []; + _ = _settingsStore.Current.SapModel.AreaObj.GetPoints(shell.Name, ref numberPoints, ref pointNames); + return pointNames; + } +} diff --git a/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Helpers/CsiToSpeckleCacheSingleton.cs b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Helpers/CsiToSpeckleCacheSingleton.cs new file mode 100644 index 000000000..c192dc39f --- /dev/null +++ b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Helpers/CsiToSpeckleCacheSingleton.cs @@ -0,0 +1,19 @@ +namespace Speckle.Converters.CSiShared.ToSpeckle.Helpers; + +public class CsiToSpeckleCacheSingleton +{ + /// + /// A map of (material id, section ids). Assumes the material id is the unique name of the material + /// + public Dictionary> MaterialCache { get; set; } = new(); + + /// + /// A map of (section id, frame object id). Assumes the section id is the unique name of the section + /// + public Dictionary> FrameSectionCache { get; set; } = new(); + + /// + /// A map of (section id, shell object id). Assumes the section id is the unique name of the section + /// + public Dictionary> ShellSectionCache { get; set; } = new(); +} diff --git a/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Helpers/DatabaseTableExtractor.cs b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Helpers/DatabaseTableExtractor.cs new file mode 100644 index 000000000..b3580943a --- /dev/null +++ b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Helpers/DatabaseTableExtractor.cs @@ -0,0 +1,87 @@ +using Speckle.Converters.Common; +using Speckle.Converters.CSiShared.Extensions; + +namespace Speckle.Converters.CSiShared.ToSpeckle.Helpers; + +/// +/// Implementation of database table extraction and caching for CSI API. +/// +/// +/// In the current context, an interface was not deemed necessary. We only have this implementation. +/// Consider introducing an interface IDatabaseTableExtractor if the need arises. +/// +public class DatabaseTableExtractor +{ + private readonly IConverterSettingsStore _settingsStore; + private readonly Dictionary _tableCache; + private const string DEFAULT_KEY_FIELD = "UniqueName"; + + public DatabaseTableExtractor(IConverterSettingsStore settingsStore) + { + _settingsStore = settingsStore; + _tableCache = []; + } + + /// + /// Uses the cDatabaseTables.GetTableForDisplayArray() to request data for a specified table name. + /// Processes the one-dimensional array return with the + /// extension for improved workability/reliability. + /// + /// String identifying the table to fetch. This typically matches the UI. + /// Key used to organize and (later) lookup specific rows of data. Optional argument, default is "UniqueName" + /// Optional list of specific fields to fetch. If null or empty, all fields will be returned. Ask Björn about how to determine these strings. + /// TableData containing the requested fields and records + public TableData GetTableData(string tableName, string? indexingColumn = null, string[]? requestedColumns = null) + { + string tableKeyField = indexingColumn ?? DEFAULT_KEY_FIELD; // most queries will use "UniqueName" + string cacheKey = $"{tableName}_{tableKeyField}"; + if (_tableCache.TryGetValue(cacheKey, out var cachedData)) + { + return cachedData; + } + + var tableData = FetchTableData(tableName, tableKeyField, requestedColumns); + _tableCache[cacheKey] = tableData; + return tableData; + } + + public void RefreshTable(string tableKey, string? keyField = null) => + _tableCache.Remove($"{tableKey}_{keyField ?? DEFAULT_KEY_FIELD}"); + + public void ClearCache() => _tableCache.Clear(); + + private TableData FetchTableData(string tableName, string indexingColumn, string[]? requestedColumns = null) + { + string[] requestedFields = requestedColumns ?? []; // only fetch the keys needed (memory reduction potential) + string[] fieldsKeysIncluded = []; + string[] tableData = []; // one-dimensional gross mess + int tableVersion = 0; + int numberOfRecords = 0; + + // ensure indexingColumn is included in the requested fields + // if user forgets to include indexingColumn in requestedColumns => problem when it comes to creating dictionaries! + if (requestedFields != Array.Empty() && !requestedFields.Contains(indexingColumn)) + { + requestedFields = [.. requestedFields, indexingColumn]; + } + + var result = _settingsStore.Current.SapModel.DatabaseTables.GetTableForDisplayArray( + tableName, + ref requestedFields, + string.Empty, // empty means all objects (not group-specific) + ref tableVersion, + ref fieldsKeysIncluded, + ref numberOfRecords, + ref tableData + ); + + if (result != 0) + { + throw new InvalidOperationException( + $"Failed to fetch table data for {tableName}. Check correctness of tableName and requestedColumns." + ); + } + + return new TableData(fieldsKeysIncluded, tableData, numberOfRecords, indexingColumn); + } +} diff --git a/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Helpers/DisplayValueExtractor.cs b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Helpers/DisplayValueExtractor.cs new file mode 100644 index 000000000..c5e9b16e3 --- /dev/null +++ b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Helpers/DisplayValueExtractor.cs @@ -0,0 +1,49 @@ +using Speckle.Converters.Common.Objects; +using Speckle.Objects.Geometry; +using Speckle.Sdk.Models; + +namespace Speckle.Converters.CSiShared.ToSpeckle.Helpers; + +public class DisplayValueExtractor +{ + private readonly ITypedConverter _jointConverter; + private readonly ITypedConverter _frameConverter; + private readonly ITypedConverter _shellConverter; + + public DisplayValueExtractor( + ITypedConverter jointConverter, + ITypedConverter frameConverter, + ITypedConverter shellConverter + ) + { + _jointConverter = jointConverter; + _frameConverter = frameConverter; + _shellConverter = shellConverter; + } + + public IEnumerable GetDisplayValue(ICsiWrapper wrapper) + { + return wrapper switch + { + CsiJointWrapper joint => ExtractJoint(joint), + CsiFrameWrapper frame => ExtractFrame(frame), + CsiShellWrapper shell => ExtractShell(shell), + _ => Enumerable.Empty() + }; + } + + private IEnumerable ExtractJoint(CsiJointWrapper target) + { + yield return _jointConverter.Convert(target); + } + + private IEnumerable ExtractFrame(CsiFrameWrapper target) + { + yield return _frameConverter.Convert(target); + } + + private IEnumerable ExtractShell(CsiShellWrapper target) + { + yield return _shellConverter.Convert(target); + } +} diff --git a/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Helpers/IApplicationPropertiesExtractor.cs b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Helpers/IApplicationPropertiesExtractor.cs new file mode 100644 index 000000000..1dbb03e2e --- /dev/null +++ b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Helpers/IApplicationPropertiesExtractor.cs @@ -0,0 +1,20 @@ +namespace Speckle.Converters.CSiShared.ToSpeckle.Helpers; + +public class PropertyExtractionResult +{ + public string Name { get; set; } + public string Type { get; set; } + public string ApplicationId { get; set; } + public Dictionary Properties { get; set; } +} + +/// +/// Interface for extracting application-specific properties (e.g., ETABS-specific properties). +/// Implementations must compose with SharedPropertiesExtractor to ensure both shared and +/// application-specific properties are extracted. +/// +public interface IApplicationPropertiesExtractor +{ + SharedPropertiesExtractor SharedPropertiesExtractor { get; } + PropertyExtractionResult ExtractProperties(ICsiWrapper wrapper); +} diff --git a/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Helpers/SharedPropertiesExtractor.cs b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Helpers/SharedPropertiesExtractor.cs new file mode 100644 index 000000000..b189abaaf --- /dev/null +++ b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Helpers/SharedPropertiesExtractor.cs @@ -0,0 +1,80 @@ +namespace Speckle.Converters.CSiShared.ToSpeckle.Helpers; + +/// +/// Extracts properties common to all CSi products (SAP2000, ETABS). +/// +public class SharedPropertiesExtractor +{ + private readonly CsiFramePropertiesExtractor _csiFramePropertiesExtractor; + private readonly CsiJointPropertiesExtractor _csiJointPropertiesExtractor; + private readonly CsiShellPropertiesExtractor _csiShellPropertiesExtractor; + + /// + /// Initializes a new instance of the class. + /// + /// The extractor for frame-specific properties. + /// The extractor for joint-specific properties. + /// The extractor for shell-specific properties. + /// + /// The sub-extractors are resolved by the DI container and injected into this class. + /// + public SharedPropertiesExtractor( + CsiFramePropertiesExtractor csiFramePropertiesExtractor, + CsiJointPropertiesExtractor csiJointPropertiesExtractor, + CsiShellPropertiesExtractor csiShellPropertiesExtractor + ) + { + _csiFramePropertiesExtractor = csiFramePropertiesExtractor; + _csiJointPropertiesExtractor = csiJointPropertiesExtractor; + _csiShellPropertiesExtractor = csiShellPropertiesExtractor; + } + + /// + /// Extracts properties from a CSi element wrapper, delegating to the appropriate sub-extractor based on the wrapper type. + /// + /// + /// A representing a CSi element (Frame, Joint, or Shell). + /// + /// + /// A containing common and specific properties of the CSi element, + /// or null if the wrapper type is unsupported. + /// + /// + /// Supported wrapper types: + /// (FrameObj API), + /// (PointObj API), + /// and (AreaObj API). + /// + public PropertyExtractionResult Extract(ICsiWrapper wrapper) + { + var objectData = new PropertyExtractionResult + { + Name = wrapper.Name, + Type = wrapper.ObjectName, + ApplicationId = string.Empty, // Populated in ExtractProperties + Properties = new Dictionary() + }; + + switch (wrapper) + { + case CsiJointWrapper joint: + _csiJointPropertiesExtractor.ExtractProperties(joint, objectData); + break; + case CsiFrameWrapper frame: + _csiFramePropertiesExtractor.ExtractProperties(frame, objectData); + break; + case CsiShellWrapper shell: + _csiShellPropertiesExtractor.ExtractProperties(shell, objectData); + break; + case CsiTendonWrapper: + case CsiLinkWrapper: + case CsiCableWrapper: + case CsiSolidWrapper: + throw new NotImplementedException($"Data extraction for {wrapper.ObjectName} not yet supported."); + default: + throw new ArgumentException($"Unsupported wrapper type: {nameof(wrapper)}"); + } + + return objectData; + } +} diff --git a/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Raw/LineToSpeckleConverter.cs b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Raw/LineToSpeckleConverter.cs new file mode 100644 index 000000000..25a99a91e --- /dev/null +++ b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Raw/LineToSpeckleConverter.cs @@ -0,0 +1,64 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Objects.Geometry; + +namespace Speckle.Converters.CSiShared.ToSpeckle.Raw; + +/// +/// Every frame has as its displayValue a Speckle line. This is defined by the start and end points. +/// +/// +/// Display value extraction is always handled by CsiShared. +/// This is because geometry representation is the same for both Sap2000 and Etabs products. +/// TODO: Point caching +/// +public class LineToSpeckleConverter : ITypedConverter +{ + private readonly IConverterSettingsStore _settingsStore; + private readonly ITypedConverter _pointConverter; + + public LineToSpeckleConverter( + IConverterSettingsStore settingsStore, + ITypedConverter pointConverter + ) + { + _settingsStore = settingsStore; + _pointConverter = pointConverter; + } + + public Line Convert(CsiFrameWrapper target) + { + // TODO: Better exception handling + string startPoint = "", + endPoint = ""; + if (_settingsStore.Current.SapModel.FrameObj.GetPoints(target.Name, ref startPoint, ref endPoint) != 0) + { + throw new ArgumentException($"Failed to convert frame {target.Name}"); + } + + // TODO: Point caching. This is gross! + double startX = 0, + startY = 0, + startZ = 0; + if (_settingsStore.Current.SapModel.PointObj.GetCoordCartesian(startPoint, ref startX, ref startY, ref startZ) != 0) + { + throw new ArgumentException($"Failed to convert point {startPoint}"); + } + + // TODO: Point caching. This is gross! + double endX = 0, + endY = 0, + endZ = 0; + if (_settingsStore.Current.SapModel.PointObj.GetCoordCartesian(endPoint, ref endX, ref endY, ref endZ) != 0) + { + throw new ArgumentException($"Failed to convert point {endPoint}"); + } + + return new() + { + start = new Point(startX, startY, startZ, _settingsStore.Current.SpeckleUnits), + end = new Point(endX, endY, endZ, _settingsStore.Current.SpeckleUnits), + units = _settingsStore.Current.SpeckleUnits + }; + } +} diff --git a/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Raw/MeshToSpeckleConverter.cs b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Raw/MeshToSpeckleConverter.cs new file mode 100644 index 000000000..827dd4185 --- /dev/null +++ b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Raw/MeshToSpeckleConverter.cs @@ -0,0 +1,78 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Objects.Geometry; + +namespace Speckle.Converters.CSiShared.ToSpeckle.Raw; + +/// +/// Every shell has as its displayValue a planar 2D Speckle mesh. This is defined by the vertices. +/// +/// +/// Display value extraction is always handled by CsiShared. +/// This is because geometry representation is the same for both Sap2000 and Etabs products. +/// TODO: Point caching and weak referencing to joint objects for better performance +/// TODO: Investigate if SAP2000 has other freeform non-planar surface definitions? +/// +public class MeshToSpeckleConverter : ITypedConverter +{ + private readonly IConverterSettingsStore _settingsStore; + + public MeshToSpeckleConverter(IConverterSettingsStore settingsStore) + { + _settingsStore = settingsStore; + } + + public Mesh Convert(CsiShellWrapper target) + { + int numberPoints = 0; + string[] pointNames = Array.Empty(); + int result = _settingsStore.Current.SapModel.AreaObj.GetPoints(target.Name, ref numberPoints, ref pointNames); + + if (result != 0) + { + throw new ArgumentException($"Failed to convert {target.Name} to Speckle Mesh"); + } + + // NOTE: Face indices format: + // - First value is the number of vertices in the face + // - Followed by indices into the vertex list + List vertices = new List(numberPoints * 3); + List faces = new List(numberPoints + 1); + + for (int i = 0; i < numberPoints; i++) + { + double pointX = 0; + double pointY = 0; + double pointZ = 0; + + result = _settingsStore.Current.SapModel.PointObj.GetCoordCartesian( + pointNames[i], + ref pointX, + ref pointY, + ref pointZ + ); + + if (result != 0) + { + throw new ArgumentException($"Failed to retrieve coordinate of vertex point name {pointNames[i]}."); + } + + vertices.Add(pointX); + vertices.Add(pointY); + vertices.Add(pointZ); + } + + faces.Add(numberPoints); + for (int i = 0; i < numberPoints; i++) + { + faces.Add(i); + } + + return new Mesh() + { + vertices = vertices, + faces = faces, + units = _settingsStore.Current.SpeckleUnits + }; + } +} diff --git a/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Raw/PointToSpeckleConverter.cs b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Raw/PointToSpeckleConverter.cs new file mode 100644 index 000000000..c0e83705d --- /dev/null +++ b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Raw/PointToSpeckleConverter.cs @@ -0,0 +1,43 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Objects.Geometry; + +namespace Speckle.Converters.CSiShared.ToSpeckle.Raw; + +/// +/// Every joint has as its displayValue a Speckle point. This is defined by extracting their coordinates. +/// +/// +/// Display value extraction is always handled by CsiShared. +/// This is because geometry representation is the same for both Sap2000 and Etabs products. +/// +public class PointToSpeckleConverter : ITypedConverter +{ + private readonly IConverterSettingsStore _settingStore; + + public PointToSpeckleConverter(IConverterSettingsStore settingStore) + { + _settingStore = settingStore; + } + + public Point Convert(CsiJointWrapper target) // NOTE: This is just a temporary POC + { + double pointX = 0; + double pointY = 0; + double pointZ = 0; + + int result = _settingStore.Current.SapModel.PointObj.GetCoordCartesian( + target.Name, + ref pointX, + ref pointY, + ref pointZ + ); + + if (result != 0) + { + throw new ArgumentException($"Failed to convert {target.Name} to {typeof(Point)}"); + } + + return new(pointX, pointY, pointZ, _settingStore.Current.SpeckleUnits); + } +} diff --git a/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/TopLevel/CsiObjectToSpeckleConverterBase.cs b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/TopLevel/CsiObjectToSpeckleConverterBase.cs new file mode 100644 index 000000000..04134251c --- /dev/null +++ b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/TopLevel/CsiObjectToSpeckleConverterBase.cs @@ -0,0 +1,69 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Converters.CSiShared.ToSpeckle.Helpers; +using Speckle.Objects; +using Speckle.Sdk.Models; + +namespace Speckle.Converters.CSiShared.ToSpeckle.TopLevel; + +/// +/// Abstract base converter that serves as the foundation for product-specific CSi converters (ETABS, SAP2000). +/// Implements a Template Method pattern for object conversion while allowing product-specific customization. +/// +/// +/// Core Components: +/// 1. DisplayValueExtractor: Handles geometry conversion common to all CSi products +/// 2. IApplicationPropertiesExtractor: Handles both shared and product-specific properties through composition +/// +/// The Convert method defines the template for conversion: +/// - Extracts display geometry +/// - Gathers properties through the application-specific implementation +/// - Delegates final object creation to product-specific implementations +/// +public abstract class CsiObjectToSpeckleConverterBase : IToSpeckleTopLevelConverter +{ + private readonly IConverterSettingsStore _settingsStore; + private readonly DisplayValueExtractor _displayValueExtractor; + private readonly IApplicationPropertiesExtractor _applicationPropertiesExtractor; + + protected CsiObjectToSpeckleConverterBase( + IConverterSettingsStore settingsStore, + DisplayValueExtractor displayValueExtractor, + IApplicationPropertiesExtractor applicationPropertiesExtractor + ) + { + _settingsStore = settingsStore; + _displayValueExtractor = displayValueExtractor; + _applicationPropertiesExtractor = applicationPropertiesExtractor; + } + + public Base Convert(object target) => Convert((CsiWrapperBase)target); + + public Base Convert(CsiWrapperBase wrapper) + { + var displayValue = _displayValueExtractor.GetDisplayValue(wrapper).ToList(); + var objectData = _applicationPropertiesExtractor.ExtractProperties(wrapper); + + var baseObject = CreateTargetObject( + objectData.Name, + objectData.Type, + new List(), + displayValue, + objectData.Properties, + _settingsStore.Current.SpeckleUnits, + objectData.ApplicationId + ); + + return baseObject; + } + + protected abstract Base CreateTargetObject( + string name, + string type, + List elements, + List displayValue, + Dictionary properties, + string units, + string applicationId + ); +} diff --git a/Converters/CSi/Speckle.Converters.CSiShared/Utils/Constants.cs b/Converters/CSi/Speckle.Converters.CSiShared/Utils/Constants.cs new file mode 100644 index 000000000..49995873a --- /dev/null +++ b/Converters/CSi/Speckle.Converters.CSiShared/Utils/Constants.cs @@ -0,0 +1,54 @@ +namespace Speckle.Converters.CSiShared.Utils; + +// NOTE: Space for string consts used across multiple files and in multiple contexts +// Separate onto dedicated files if this gets too long + +/// +/// These categories mirror the UI. Nested within the properties are the repeated categories +/// This happens across frame, shell and joint objects. +/// The consts formalise the assignable property categories and reduce typos. +/// +public static class ObjectPropertyCategory +{ + public const string ASSIGNMENTS = "Assignments"; + public const string DESIGN = "Design"; + public const string GEOMETRY = "Geometry"; + public const string OBJECT_ID = "Object ID"; +} + +/// +/// These strings are repeatedly used as keys when building the properties dictionary for objects +/// +public static class ObjectPropertyKey +{ + public const string MATERIAL_ID = "Material"; + public const string SECTION_ID = "Section Property"; +} + +/// +/// These strings are repeatedly used group properties (mimics the host app UI) +/// +public static class SectionPropertyCategory +{ + public const string GENERAL_DATA = "General Data"; + public const string SECTION_PROPERTIES = "Section Properties"; + public const string SECTION_DIMENSIONS = "Section Dimensions"; + public const string PROPERTY_DATA = "Property Data"; +} + +/// +/// These strings are properties repeated and common to various object types (joint, frame, shell etc.) +/// +public static class CommonObjectProperty +{ + public const string LABEL = "Label"; + public const string LEVEL = "Level"; + public const string GROUPS = "Groups"; + public const string SPRING_ASSIGNMENT = "Spring Assignment"; + public const string LOCAL_AXIS_2_ANGLE = "Local Axis 2 Angle"; + public const string MATERIAL_OVERWRITE = "Material Overwrite"; + public const string PROPERTY_MODIFIERS = "Property Modifiers"; + public const string ANGLE = "Angle"; + public const string ADVANCED = "Advanced"; + public const string DESIGN_ORIENTATION = "Design Orientation"; +} diff --git a/Converters/CSi/Speckle.Converters.CSiShared/Utils/DictionaryExtensions.cs b/Converters/CSi/Speckle.Converters.CSiShared/Utils/DictionaryExtensions.cs new file mode 100644 index 000000000..bf936b519 --- /dev/null +++ b/Converters/CSi/Speckle.Converters.CSiShared/Utils/DictionaryExtensions.cs @@ -0,0 +1,42 @@ +namespace Speckle.Converters.CSiShared.Utils; + +/// +/// Provides extension methods for dictionary operations common across the CSI converter. +/// +public static class DictionaryExtensions +{ + /// + /// Ensures a nested dictionary exists at the specified key, creating it if necessary. + /// Used for organizing properties into hierarchical categories (e.g., "Geometry", "Assignments", "Design"). + /// + /// + /// This pattern is used throughout property extractors to maintain consistent property organization. + /// + public static Dictionary EnsureNested(this Dictionary dictionary, string key) + { + if (!dictionary.TryGetValue(key, out var obj) || obj is not Dictionary nestedDictionary) + { + nestedDictionary = []; + dictionary[key] = nestedDictionary; + } + + return nestedDictionary; + } + + /// + /// Adds a value with its associated units to a parent dictionary using a standardized format. + /// Creates a nested dictionary with 'name', 'value', and 'units' keys. + /// + public static void AddWithUnits( + this Dictionary dictionary, + string key, + object value, + string? units = null + ) => + dictionary[key] = new Dictionary + { + ["name"] = key, + ["value"] = value, + ["units"] = units + }; +} diff --git a/Converters/CSi/Speckle.Converters.CSiShared/Utils/Enums.cs b/Converters/CSi/Speckle.Converters.CSiShared/Utils/Enums.cs new file mode 100644 index 000000000..78e36e495 --- /dev/null +++ b/Converters/CSi/Speckle.Converters.CSiShared/Utils/Enums.cs @@ -0,0 +1,42 @@ +namespace Speckle.Converters.CSiShared.Utils; + +// NOTE: Should number of enums become too large -> dedicated files. +public enum ModelObjectType +{ + NONE = 0, + JOINT = 1, + FRAME = 2, + CABLE = 3, + TENDON = 4, + SHELL = 5, + SOLID = 6, + LINK = 7 +} + +public enum ElementCategory +{ + COLUMN, + BEAM, + BRACE, + WALL, + FLOOR, + RAMP, + JOINT, + OTHER +} + +public enum DirectionalSymmetryType +{ + ISOTROPIC, + ORTHOTROPIC, + ANISOTROPIC, + UNIAXIAL +} + +public enum AreaPropertyType +{ + NONE = 0, + SHELL = 1, + PLANE = 2, + ASOLID = 3 +} diff --git a/Converters/CSi/Speckle.Converters.ETABS21/Speckle.Converters.ETABS21.csproj b/Converters/CSi/Speckle.Converters.ETABS21/Speckle.Converters.ETABS21.csproj new file mode 100644 index 000000000..9cce8135f --- /dev/null +++ b/Converters/CSi/Speckle.Converters.ETABS21/Speckle.Converters.ETABS21.csproj @@ -0,0 +1,20 @@ + + + + net48 + Debug;Release;Local + + + + + + + + + + + + + + + diff --git a/Converters/CSi/Speckle.Converters.ETABS21/packages.lock.json b/Converters/CSi/Speckle.Converters.ETABS21/packages.lock.json new file mode 100644 index 000000000..4a10b1b8d --- /dev/null +++ b/Converters/CSi/Speckle.Converters.ETABS21/packages.lock.json @@ -0,0 +1,322 @@ +{ + "version": 2, + "dependencies": { + ".NETFramework,Version=v4.8": { + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net48": "1.0.3" + } + }, + "Microsoft.SourceLink.GitHub": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "dependencies": { + "Microsoft.Build.Tasks.Git": "8.0.0", + "Microsoft.SourceLink.Common": "8.0.0" + } + }, + "PolySharp": { + "type": "Direct", + "requested": "[1.14.1, )", + "resolved": "1.14.1", + "contentHash": "mOOmFYwad3MIOL14VCjj02LljyF1GNw1wP0YVlxtcPvqdxjGGMNdNJJxHptlry3MOd8b40Flm8RPOM8JOlN2sQ==" + }, + "Speckle.CSI.API": { + "type": "Direct", + "requested": "[1.30.0, )", + "resolved": "1.30.0", + "contentHash": "4S5Udr+YDU43YgB+TXgnPtGioRj1hDnucHlr42ikr72h1yQwzmkC2HwWJibjZD+sOrAke67q1N8geIqJj9Ss4Q==" + }, + "Speckle.InterfaceGenerator": { + "type": "Direct", + "requested": "[0.9.6, )", + "resolved": "0.9.6", + "contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w==" + }, + "GraphQL.Client": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "8yPNBbuVBpTptivyAlak4GZvbwbUcjeQTL4vN1HKHRuOykZ4r7l5fcLS6vpyPyLn0x8FsL31xbOIKyxbmR9rbA==", + "dependencies": { + "GraphQL.Client.Abstractions": "6.0.0", + "GraphQL.Client.Abstractions.Websocket": "6.0.0", + "System.Net.WebSockets.Client.Managed": "1.0.22", + "System.Reactive": "5.0.0" + } + }, + "GraphQL.Client.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "h7uzWFORHZ+CCjwr/ThAyXMr0DPpzEANDa4Uo54wqCQ+j7qUKwqYTgOrb1W40sqbvNaZm9v/X7It31SUw0maHA==", + "dependencies": { + "GraphQL.Primitives": "6.0.0" + } + }, + "GraphQL.Client.Abstractions.Websocket": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "Nr9bPf8gIOvLuXpqEpqr9z9jslYFJOvd0feHth3/kPqeR3uMbjF5pjiwh4jxyMcxHdr8Pb6QiXkV3hsSyt0v7A==", + "dependencies": { + "GraphQL.Client.Abstractions": "6.0.0" + } + }, + "GraphQL.Primitives": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "yg72rrYDapfsIUrul7aF6wwNnTJBOFvuA9VdDTQpPa8AlAriHbufeXYLBcodKjfUdkCnaiggX1U/nEP08Zb5GA==" + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "W8DPQjkMScOMTtJbPwmPyj9c3zYSFGawDW3jwlBOOsnY+EzZFLgNQ/UMkK35JmkNOVPdCyPr2Tw7Vv9N+KA3ZQ==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.CSharp": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==" + }, + "Microsoft.Data.Sqlite": { + "type": "Transitive", + "resolved": "7.0.5", + "contentHash": "KGxbPeWsQMnmQy43DSBxAFtHz3l2JX8EWBSGUCvT3CuZ8KsuzbkqMIJMDOxWtG8eZSoCDI04aiVQjWuuV8HmSw==", + "dependencies": { + "Microsoft.Data.Sqlite.Core": "7.0.5", + "SQLitePCLRaw.bundle_e_sqlite3": "2.1.4" + } + }, + "Microsoft.Data.Sqlite.Core": { + "type": "Transitive", + "resolved": "7.0.5", + "contentHash": "FTerRmQPqHrCrnoUzhBu+E+1DNGwyrAMLqHkAqOOOu5pGfyMOj8qQUBxI/gDtWtG11p49UxSfWmBzRNlwZqfUg==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "nOP8R1mVb/6mZtm2qgAJXn/LFm/2kMjHDAg/QJLFG6CuWYJtaD3p1BwQhufBVvRzL9ceJ/xF0SQ0qsI2GkDQAA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "65MrmXCziWaQFrI0UHkQbesrX5wTwf9XPjY5yFm/VkgJKFJ5gqvXRoXjIZcf2wLi5ZlwGz/oMYfyURVCWbM5iw==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "vJ9xvOZCnUAIHcGC3SU35r3HKmHTVIeHzo6u/qzlHAqD8m6xv92MLin4oJntTvkpKxVX3vI1GFFkIQtU3AdlsQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "2.2.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw==" + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "UpZLNLBpIZ0GTebShui7xXYh6DmBHjWM8NxGxZbdQh/bPZ5e6YswqI+bru6BnEL5eWiOdodsXtEz3FROcgi/qg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Primitives": "2.2.0", + "System.ComponentModel.Annotations": "4.5.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "azyQtqbm4fSaDzZHD/J+V6oWMFaf2tWP4WEGIYePLCMw3+b2RQdj9ybgbQyjCshcitQKQ4lEDOZjmSlTTrHxUg==", + "dependencies": { + "System.Memory": "4.5.1", + "System.Runtime.CompilerServices.Unsafe": "4.5.1" + } + }, + "Microsoft.NETFramework.ReferenceAssemblies.net48": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "zMk4D+9zyiEWByyQ7oPImPN/Jhpj166Ky0Nlla4eXlNL8hI/BtSJsgR8Inldd4NNpIAH3oh8yym0W2DrhXdSLQ==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "Speckle.Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.2", + "contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA==" + }, + "SQLitePCLRaw.bundle_e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "EWI1olKDjFEBMJu0+3wuxwziIAdWDVMYLhuZ3Qs84rrz+DHwD00RzWPZCa+bLnHCf3oJwuFZIRsHT5p236QXww==", + "dependencies": { + "SQLitePCLRaw.lib.e_sqlite3": "2.1.4", + "SQLitePCLRaw.provider.dynamic_cdecl": "2.1.4" + } + }, + "SQLitePCLRaw.core": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "inBjvSHo9UDKneGNzfUfDjK08JzlcIhn1+SP5Y3m6cgXpCxXKCJDy6Mka7LpgSV+UZmKSnC8rTwB0SQ0xKu5pA==", + "dependencies": { + "System.Memory": "4.5.3" + } + }, + "SQLitePCLRaw.lib.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "2C9Q9eX7CPLveJA0rIhf9RXAvu+7nWZu1A2MdG6SD/NOu26TakGgL1nsbc0JAspGijFOo3HoN79xrx8a368fBg==" + }, + "SQLitePCLRaw.provider.dynamic_cdecl": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "ZsaKKhgYF9B1fvcnOGKl3EycNAwd9CRWX7v0rEfuPWhQQ5Jjpvf2VEHahiLIGHio3hxi3EIKFJw9KvyowWOUAw==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "System.Buffers": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "AwarXzzoDwX6BgrhjoJsk6tUezZEozOT5Y9QKF94Gl4JK91I4PIIBkBco9068Y9/Dra8Dkbie99kXB8+1BaYKw==" + }, + "System.ComponentModel.Annotations": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "UxYQ3FGUOtzJ7LfSdnYSFd7+oEv6M8NgUatatIN2HxNtDdlcvFAf+VIq4Of9cDMJEJC0aSRv/x898RYhB4Yppg==" + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.3", + "contentHash": "3oDzvc/zzetpTKWMShs1AADwZjQ/36HnsufHRPcOjyRAAMLDlu2iD33MBI2opxnezcVUtXyqDXXjoFMOU9c7SA==", + "dependencies": { + "System.Buffers": "4.4.0", + "System.Numerics.Vectors": "4.4.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.2" + } + }, + "System.Net.WebSockets.Client.Managed": { + "type": "Transitive", + "resolved": "1.0.22", + "contentHash": "WqEOxPlXjuZrIjUtXNE9NxEfU/n5E35iV2PtoZdJSUC4tlrqwHnTee+wvMIM4OUaJWmwrymeqcgYrE0IkGAgLA==", + "dependencies": { + "System.Buffers": "4.4.0", + "System.Numerics.Vectors": "4.4.0" + } + }, + "System.Numerics.Vectors": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ==" + }, + "System.Reactive": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "4.5.3", + "contentHash": "3TIsJhD1EiiT0w2CcDMN/iSSwnNnsrnbzeVHSKkaEgV85txMprmuO+Yq2AdSbeVGcg28pdNDTPK87tJhX7VFHw==" + }, + "System.Threading.Tasks.Extensions": { + "type": "Transitive", + "resolved": "4.5.4", + "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + }, + "speckle.converters.common": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", + "Speckle.Objects": "[3.1.1, )" + } + }, + "Microsoft.Extensions.Logging": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "Nxqhadc9FCmFHzU+fz3oc8sFlE6IadViYg8dfUdGzJZ2JUxnCsRghBhhOWdM4B2zSZqEc+0BjliBh/oNdRZuig==", + "dependencies": { + "Microsoft.Extensions.Configuration.Binder": "2.2.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging.Abstractions": "2.2.0", + "Microsoft.Extensions.Options": "2.2.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" + }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, + "Speckle.Objects": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", + "dependencies": { + "Speckle.Sdk": "3.1.1" + } + }, + "Speckle.Sdk": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", + "dependencies": { + "GraphQL.Client": "6.0.0", + "Microsoft.Bcl.AsyncInterfaces": "5.0.0", + "Microsoft.CSharp": "4.7.0", + "Microsoft.Data.Sqlite": "7.0.5", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging": "2.2.0", + "Speckle.DoubleNumerics": "4.1.0", + "Speckle.Newtonsoft.Json": "13.0.2", + "Speckle.Sdk.Dependencies": "3.1.1" + } + }, + "Speckle.Sdk.Dependencies": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" + } + } + } +} \ No newline at end of file diff --git a/Converters/CSi/Speckle.Converters.ETABS22/Speckle.Converters.ETABS22.csproj b/Converters/CSi/Speckle.Converters.ETABS22/Speckle.Converters.ETABS22.csproj new file mode 100644 index 000000000..f07e3fb8d --- /dev/null +++ b/Converters/CSi/Speckle.Converters.ETABS22/Speckle.Converters.ETABS22.csproj @@ -0,0 +1,20 @@ + + + + net8.0-windows + Debug;Release;Local + + + + + + + + + + + + + + + diff --git a/Converters/CSi/Speckle.Converters.ETABS22/packages.lock.json b/Converters/CSi/Speckle.Converters.ETABS22/packages.lock.json new file mode 100644 index 000000000..aff4a7860 --- /dev/null +++ b/Converters/CSi/Speckle.Converters.ETABS22/packages.lock.json @@ -0,0 +1,277 @@ +{ + "version": 2, + "dependencies": { + "net8.0-windows7.0": { + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net461": "1.0.3" + } + }, + "Microsoft.SourceLink.GitHub": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "dependencies": { + "Microsoft.Build.Tasks.Git": "8.0.0", + "Microsoft.SourceLink.Common": "8.0.0" + } + }, + "PolySharp": { + "type": "Direct", + "requested": "[1.14.1, )", + "resolved": "1.14.1", + "contentHash": "mOOmFYwad3MIOL14VCjj02LljyF1GNw1wP0YVlxtcPvqdxjGGMNdNJJxHptlry3MOd8b40Flm8RPOM8JOlN2sQ==" + }, + "Speckle.CSI.API": { + "type": "Direct", + "requested": "[2.4.0, )", + "resolved": "2.4.0", + "contentHash": "/n3qIBeamiYlWm77/2+dDPYExm/MoDEtnu5IPB2G9Dei06wMgkdBefaSDKWnh3u4iuyha6TvrBZgVGosUylRDg==" + }, + "Speckle.InterfaceGenerator": { + "type": "Direct", + "requested": "[0.9.6, )", + "resolved": "0.9.6", + "contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w==" + }, + "GraphQL.Client": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "8yPNBbuVBpTptivyAlak4GZvbwbUcjeQTL4vN1HKHRuOykZ4r7l5fcLS6vpyPyLn0x8FsL31xbOIKyxbmR9rbA==", + "dependencies": { + "GraphQL.Client.Abstractions": "6.0.0", + "GraphQL.Client.Abstractions.Websocket": "6.0.0", + "System.Reactive": "5.0.0" + } + }, + "GraphQL.Client.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "h7uzWFORHZ+CCjwr/ThAyXMr0DPpzEANDa4Uo54wqCQ+j7qUKwqYTgOrb1W40sqbvNaZm9v/X7It31SUw0maHA==", + "dependencies": { + "GraphQL.Primitives": "6.0.0" + } + }, + "GraphQL.Client.Abstractions.Websocket": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "Nr9bPf8gIOvLuXpqEpqr9z9jslYFJOvd0feHth3/kPqeR3uMbjF5pjiwh4jxyMcxHdr8Pb6QiXkV3hsSyt0v7A==", + "dependencies": { + "GraphQL.Client.Abstractions": "6.0.0" + } + }, + "GraphQL.Primitives": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "yg72rrYDapfsIUrul7aF6wwNnTJBOFvuA9VdDTQpPa8AlAriHbufeXYLBcodKjfUdkCnaiggX1U/nEP08Zb5GA==" + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.CSharp": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==" + }, + "Microsoft.Data.Sqlite": { + "type": "Transitive", + "resolved": "7.0.5", + "contentHash": "KGxbPeWsQMnmQy43DSBxAFtHz3l2JX8EWBSGUCvT3CuZ8KsuzbkqMIJMDOxWtG8eZSoCDI04aiVQjWuuV8HmSw==", + "dependencies": { + "Microsoft.Data.Sqlite.Core": "7.0.5", + "SQLitePCLRaw.bundle_e_sqlite3": "2.1.4" + } + }, + "Microsoft.Data.Sqlite.Core": { + "type": "Transitive", + "resolved": "7.0.5", + "contentHash": "FTerRmQPqHrCrnoUzhBu+E+1DNGwyrAMLqHkAqOOOu5pGfyMOj8qQUBxI/gDtWtG11p49UxSfWmBzRNlwZqfUg==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "nOP8R1mVb/6mZtm2qgAJXn/LFm/2kMjHDAg/QJLFG6CuWYJtaD3p1BwQhufBVvRzL9ceJ/xF0SQ0qsI2GkDQAA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "65MrmXCziWaQFrI0UHkQbesrX5wTwf9XPjY5yFm/VkgJKFJ5gqvXRoXjIZcf2wLi5ZlwGz/oMYfyURVCWbM5iw==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "vJ9xvOZCnUAIHcGC3SU35r3HKmHTVIeHzo6u/qzlHAqD8m6xv92MLin4oJntTvkpKxVX3vI1GFFkIQtU3AdlsQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "2.2.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw==" + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "UpZLNLBpIZ0GTebShui7xXYh6DmBHjWM8NxGxZbdQh/bPZ5e6YswqI+bru6BnEL5eWiOdodsXtEz3FROcgi/qg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Primitives": "2.2.0", + "System.ComponentModel.Annotations": "4.5.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "azyQtqbm4fSaDzZHD/J+V6oWMFaf2tWP4WEGIYePLCMw3+b2RQdj9ybgbQyjCshcitQKQ4lEDOZjmSlTTrHxUg==", + "dependencies": { + "System.Memory": "4.5.1", + "System.Runtime.CompilerServices.Unsafe": "4.5.1" + } + }, + "Microsoft.NETFramework.ReferenceAssemblies.net461": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "AmOJZwCqnOCNp6PPcf9joyogScWLtwy0M1WkqfEQ0M9nYwyDD7EX9ZjscKS5iYnyvteX7kzSKFCKt9I9dXA6mA==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "Speckle.Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.2", + "contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA==" + }, + "SQLitePCLRaw.bundle_e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "EWI1olKDjFEBMJu0+3wuxwziIAdWDVMYLhuZ3Qs84rrz+DHwD00RzWPZCa+bLnHCf3oJwuFZIRsHT5p236QXww==", + "dependencies": { + "SQLitePCLRaw.lib.e_sqlite3": "2.1.4", + "SQLitePCLRaw.provider.e_sqlite3": "2.1.4" + } + }, + "SQLitePCLRaw.core": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "inBjvSHo9UDKneGNzfUfDjK08JzlcIhn1+SP5Y3m6cgXpCxXKCJDy6Mka7LpgSV+UZmKSnC8rTwB0SQ0xKu5pA==", + "dependencies": { + "System.Memory": "4.5.3" + } + }, + "SQLitePCLRaw.lib.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "2C9Q9eX7CPLveJA0rIhf9RXAvu+7nWZu1A2MdG6SD/NOu26TakGgL1nsbc0JAspGijFOo3HoN79xrx8a368fBg==" + }, + "SQLitePCLRaw.provider.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "CSlb5dUp1FMIkez9Iv5EXzpeq7rHryVNqwJMWnpq87j9zWZexaEMdisDktMsnnrzKM6ahNrsTkjqNodTBPBxtQ==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "System.ComponentModel.Annotations": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "UxYQ3FGUOtzJ7LfSdnYSFd7+oEv6M8NgUatatIN2HxNtDdlcvFAf+VIq4Of9cDMJEJC0aSRv/x898RYhB4Yppg==" + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.3", + "contentHash": "3oDzvc/zzetpTKWMShs1AADwZjQ/36HnsufHRPcOjyRAAMLDlu2iD33MBI2opxnezcVUtXyqDXXjoFMOU9c7SA==" + }, + "System.Reactive": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==" + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Zh8t8oqolRaFa9vmOZfdQm/qKejdqz0J9kr7o2Fu0vPeoH3BL1EOXipKWwkWtLT1JPzjByrF19fGuFlNbmPpiw==" + }, + "speckle.converters.common": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", + "Speckle.Objects": "[3.1.1, )" + } + }, + "Microsoft.Extensions.Logging": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "Nxqhadc9FCmFHzU+fz3oc8sFlE6IadViYg8dfUdGzJZ2JUxnCsRghBhhOWdM4B2zSZqEc+0BjliBh/oNdRZuig==", + "dependencies": { + "Microsoft.Extensions.Configuration.Binder": "2.2.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging.Abstractions": "2.2.0", + "Microsoft.Extensions.Options": "2.2.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" + }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, + "Speckle.Objects": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", + "dependencies": { + "Speckle.Sdk": "3.1.1" + } + }, + "Speckle.Sdk": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", + "dependencies": { + "GraphQL.Client": "6.0.0", + "Microsoft.CSharp": "4.7.0", + "Microsoft.Data.Sqlite": "7.0.5", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging": "2.2.0", + "Speckle.DoubleNumerics": "4.1.0", + "Speckle.Newtonsoft.Json": "13.0.2", + "Speckle.Sdk.Dependencies": "3.1.1" + } + }, + "Speckle.Sdk.Dependencies": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" + } + } + } +} \ No newline at end of file diff --git a/Converters/CSi/Speckle.Converters.ETABSShared/ServiceRegistration.cs b/Converters/CSi/Speckle.Converters.ETABSShared/ServiceRegistration.cs new file mode 100644 index 000000000..0a79b9353 --- /dev/null +++ b/Converters/CSi/Speckle.Converters.ETABSShared/ServiceRegistration.cs @@ -0,0 +1,28 @@ +using System.Reflection; +using Microsoft.Extensions.DependencyInjection; +using Speckle.Converters.CSiShared.ToSpeckle.Helpers; +using Speckle.Converters.CSiShared.ToSpeckle.TopLevel; +using Speckle.Converters.ETABSShared.ToSpeckle.Helpers; +using Speckle.Converters.ETABSShared.ToSpeckle.TopLevel; +using Speckle.Sdk; + +namespace Speckle.Converters.ETABSShared; + +public static class ServiceRegistration +{ + public static IServiceCollection AddEtabsConverters(this IServiceCollection serviceCollection) + { + var converterAssembly = Assembly.GetExecutingAssembly(); + + // Etabs-specific implementations + serviceCollection.AddScoped(); + serviceCollection.AddScoped(); + serviceCollection.AddScoped(); + serviceCollection.AddScoped(); + serviceCollection.AddScoped(); + + serviceCollection.AddMatchingInterfacesAsTransient(converterAssembly); + + return serviceCollection; + } +} diff --git a/Converters/CSi/Speckle.Converters.ETABSShared/Speckle.Converters.ETABSShared.projitems b/Converters/CSi/Speckle.Converters.ETABSShared/Speckle.Converters.ETABSShared.projitems new file mode 100644 index 000000000..c5feb494b --- /dev/null +++ b/Converters/CSi/Speckle.Converters.ETABSShared/Speckle.Converters.ETABSShared.projitems @@ -0,0 +1,19 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + 36377858-d696-4567-ab05-637f4ec841f5 + + + Speckle.Converters.ETABSShared + + + + + + + + + + diff --git a/Converters/CSi/Speckle.Converters.ETABSShared/Speckle.Converters.ETABSShared.shproj b/Converters/CSi/Speckle.Converters.ETABSShared/Speckle.Converters.ETABSShared.shproj new file mode 100644 index 000000000..ec47ff880 --- /dev/null +++ b/Converters/CSi/Speckle.Converters.ETABSShared/Speckle.Converters.ETABSShared.shproj @@ -0,0 +1,13 @@ + + + + 36377858-d696-4567-ab05-637f4ec841f5 + 14.0 + + + + + + + + diff --git a/Converters/CSi/Speckle.Converters.ETABSShared/ToSpeckle/Helpers/EtabsFramePropertiesExtractor.cs b/Converters/CSi/Speckle.Converters.ETABSShared/ToSpeckle/Helpers/EtabsFramePropertiesExtractor.cs new file mode 100644 index 000000000..4d51525cd --- /dev/null +++ b/Converters/CSi/Speckle.Converters.ETABSShared/ToSpeckle/Helpers/EtabsFramePropertiesExtractor.cs @@ -0,0 +1,105 @@ +using Speckle.Converters.Common; +using Speckle.Converters.CSiShared; +using Speckle.Converters.CSiShared.ToSpeckle.Helpers; +using Speckle.Converters.CSiShared.Utils; + +namespace Speckle.Converters.ETABSShared.ToSpeckle.Helpers; + +/// +/// Extracts ETABS-specific properties from frame elements using the FrameObj API calls. +/// +/// +/// Responsibilities: +/// +/// Extracts properties only available in ETABS (e.g., Label, Level) +/// Complements by adding product-specific data +/// Follows same pattern of single-purpose methods for clear API mapping +/// +/// +/// Design Decisions: +/// +/// Maintains separate methods for each property following CSI API structure +/// Properties are organized by their functional groups (Object ID, Assignments, Design) +/// +/// +public sealed class EtabsFramePropertiesExtractor +{ + private readonly IConverterSettingsStore _settingsStore; + private readonly DatabaseTableExtractor _databaseTableExtractor; + + public EtabsFramePropertiesExtractor( + IConverterSettingsStore settingsStore, + DatabaseTableExtractor databaseTableExtractor + ) + { + _settingsStore = settingsStore; + _databaseTableExtractor = databaseTableExtractor; + } + + public void ExtractProperties(CsiFrameWrapper frame, Dictionary properties) + { + var objectId = properties.EnsureNested(ObjectPropertyCategory.OBJECT_ID); + objectId[CommonObjectProperty.DESIGN_ORIENTATION] = GetDesignOrientation(frame); + (objectId[CommonObjectProperty.LABEL], objectId[CommonObjectProperty.LEVEL]) = GetLabelAndLevel(frame); + + var assignments = properties.EnsureNested(ObjectPropertyCategory.ASSIGNMENTS); + assignments[CommonObjectProperty.SPRING_ASSIGNMENT] = GetSpringAssignmentName(frame); + + var design = properties.EnsureNested(ObjectPropertyCategory.DESIGN); + design["Design Procedure"] = GetDesignProcedure(frame); + + var geometry = properties.EnsureNested(ObjectPropertyCategory.GEOMETRY); + double length = GetLength(frame); + geometry.AddWithUnits("Length", length, _settingsStore.Current.SpeckleUnits); + } + + private (string label, string level) GetLabelAndLevel(CsiFrameWrapper frame) + { + string label = string.Empty, + level = string.Empty; + _ = _settingsStore.Current.SapModel.FrameObj.GetLabelFromName(frame.Name, ref label, ref level); + return (label, level); + } + + private string GetDesignOrientation(CsiFrameWrapper frame) + { + eFrameDesignOrientation designOrientation = eFrameDesignOrientation.Null; + _ = _settingsStore.Current.SapModel.FrameObj.GetDesignOrientation(frame.Name, ref designOrientation); + return designOrientation.ToString(); + } + + private string GetDesignProcedure(CsiFrameWrapper frame) + { + int myType = 0; + _ = _settingsStore.Current.SapModel.FrameObj.GetDesignProcedure(frame.Name, ref myType); + return myType switch + { + 1 => "Steel Frame Design", + 2 => "Concrete Frame Design", + 3 => "Composite Beam Design", + 4 => "Steel Joist Design", + 7 => "No Design", + 13 => "Composite Column Design", + _ => "Program determined" + }; + } + + private string GetSpringAssignmentName(CsiFrameWrapper frame) + { + string springPropertyName = string.Empty; + _ = _settingsStore.Current.SapModel.FrameObj.GetSpringAssignment(frame.Name, ref springPropertyName); + return springPropertyName; + } + + private double GetLength(CsiFrameWrapper frame) + { + // using the DatabaseTableExtractor fetch table with key "Frame Assignments - Summary" + // limit query size to "UniqueName" and "Length" fields + string length = _databaseTableExtractor + .GetTableData("Frame Assignments - Summary", requestedColumns: ["UniqueName", "Length"]) + .GetRowValue(frame.Name, "Length"); + + // all database data is returned as strings + return double.TryParse(length, out double result) ? result : double.NaN; + } +} diff --git a/Converters/CSi/Speckle.Converters.ETABSShared/ToSpeckle/Helpers/EtabsJointPropertiesExtractor.cs b/Converters/CSi/Speckle.Converters.ETABSShared/ToSpeckle/Helpers/EtabsJointPropertiesExtractor.cs new file mode 100644 index 000000000..2c404029b --- /dev/null +++ b/Converters/CSi/Speckle.Converters.ETABSShared/ToSpeckle/Helpers/EtabsJointPropertiesExtractor.cs @@ -0,0 +1,66 @@ +using Speckle.Converters.Common; +using Speckle.Converters.CSiShared; +using Speckle.Converters.CSiShared.ToSpeckle.Helpers; +using Speckle.Converters.CSiShared.Utils; + +namespace Speckle.Converters.ETABSShared.ToSpeckle.Helpers; + +/// +/// Extracts ETABS-specific properties from joint elements using the PointObj API calls. +/// +/// +/// Responsibilities: +/// +/// Extracts properties only available in ETABS (e.g., Diaphragm) +/// Complements by adding product-specific data +/// Follows same pattern of single-purpose methods for clear API mapping +/// +/// +/// Design Decisions: +/// +/// Maintains separate methods for each property following CSI API structure +/// Properties are organized by their functional groups (Object ID, Assignments, Design) +/// +/// +public sealed class EtabsJointPropertiesExtractor +{ + private readonly IConverterSettingsStore _settingsStore; + + public EtabsJointPropertiesExtractor(IConverterSettingsStore settingsStore) + { + _settingsStore = settingsStore; + } + + public void ExtractProperties(CsiJointWrapper joint, Dictionary properties) + { + var objectId = properties.EnsureNested(ObjectPropertyCategory.OBJECT_ID); + (objectId[CommonObjectProperty.LABEL], objectId[CommonObjectProperty.LEVEL]) = GetLabelAndLevel(joint); + + var assignments = properties.EnsureNested(ObjectPropertyCategory.ASSIGNMENTS); + (assignments["Diaphragm Option"], assignments["Diaphragm Name"]) = GetAssignedDiaphragm(joint); + assignments[CommonObjectProperty.SPRING_ASSIGNMENT] = GetSpringAssignmentName(joint); + } + + private (string diaphramOption, string diaphragmName) GetAssignedDiaphragm(CsiJointWrapper joint) + { + eDiaphragmOption diaphragmOption = eDiaphragmOption.Disconnect; + string diaphragmName = string.Empty; + _ = _settingsStore.Current.SapModel.PointObj.GetDiaphragm(joint.Name, ref diaphragmOption, ref diaphragmName); + return (diaphragmOption.ToString(), diaphragmName); + } + + private (string label, string level) GetLabelAndLevel(CsiJointWrapper joint) + { + string label = string.Empty, + level = string.Empty; + _ = _settingsStore.Current.SapModel.PointObj.GetLabelFromName(joint.Name, ref label, ref level); + return (label, level); + } + + private string GetSpringAssignmentName(CsiJointWrapper joint) + { + string springPropertyName = string.Empty; + _ = _settingsStore.Current.SapModel.PointObj.GetSpringAssignment(joint.Name, ref springPropertyName); + return springPropertyName; + } +} diff --git a/Converters/CSi/Speckle.Converters.ETABSShared/ToSpeckle/Helpers/EtabsPropertiesExtractor.cs b/Converters/CSi/Speckle.Converters.ETABSShared/ToSpeckle/Helpers/EtabsPropertiesExtractor.cs new file mode 100644 index 000000000..e39eed0f4 --- /dev/null +++ b/Converters/CSi/Speckle.Converters.ETABSShared/ToSpeckle/Helpers/EtabsPropertiesExtractor.cs @@ -0,0 +1,58 @@ +using Speckle.Converters.CSiShared; +using Speckle.Converters.CSiShared.ToSpeckle.Helpers; + +namespace Speckle.Converters.ETABSShared.ToSpeckle.Helpers; + +/// +/// ETABS-specific property extractor that composes with SharedPropertiesExtractor to provide +/// both shared and ETABS-specific properties. +/// +/// +/// Follows the composition pattern where SharedPropertiesExtractor handles common CSI properties, +/// while this class adds ETABS-specific properties. The extraction order is important: +/// 1. Extract shared properties first via SharedPropertiesExtractor +/// 2. Augment with ETABS-specific properties +/// This ensures consistent base properties with ETABS-specific enrichment. +/// +public class EtabsPropertiesExtractor : IApplicationPropertiesExtractor +{ + public SharedPropertiesExtractor SharedPropertiesExtractor { get; } + private readonly EtabsFramePropertiesExtractor _etabsFramePropertiesExtractor; + private readonly EtabsJointPropertiesExtractor _etabsJointPropertiesExtractor; + private readonly EtabsShellPropertiesExtractor _etabsShellPropertiesExtractor; + + public EtabsPropertiesExtractor( + SharedPropertiesExtractor sharedPropertiesExtractor, + EtabsFramePropertiesExtractor etabsFramePropertiesExtractor, + EtabsJointPropertiesExtractor etabsJointPropertiesExtractor, + EtabsShellPropertiesExtractor etabsShellPropertiesExtractor + ) + { + SharedPropertiesExtractor = sharedPropertiesExtractor; + _etabsFramePropertiesExtractor = etabsFramePropertiesExtractor; + _etabsJointPropertiesExtractor = etabsJointPropertiesExtractor; + _etabsShellPropertiesExtractor = etabsShellPropertiesExtractor; + } + + public PropertyExtractionResult ExtractProperties(ICsiWrapper wrapper) + { + // Extract shared properties first + var propertiesExtractionResult = SharedPropertiesExtractor.Extract(wrapper); + + // Then we go into Etabs-specific stuff + switch (wrapper) + { + case CsiFrameWrapper frame: + _etabsFramePropertiesExtractor.ExtractProperties(frame, propertiesExtractionResult.Properties); + break; + case CsiJointWrapper joint: + _etabsJointPropertiesExtractor.ExtractProperties(joint, propertiesExtractionResult.Properties); + break; + case CsiShellWrapper shell: + _etabsShellPropertiesExtractor.ExtractProperties(shell, propertiesExtractionResult.Properties); + break; + } + + return propertiesExtractionResult; + } +} diff --git a/Converters/CSi/Speckle.Converters.ETABSShared/ToSpeckle/Helpers/EtabsShellPropertiesExtractor.cs b/Converters/CSi/Speckle.Converters.ETABSShared/ToSpeckle/Helpers/EtabsShellPropertiesExtractor.cs new file mode 100644 index 000000000..08139448c --- /dev/null +++ b/Converters/CSi/Speckle.Converters.ETABSShared/ToSpeckle/Helpers/EtabsShellPropertiesExtractor.cs @@ -0,0 +1,191 @@ +using Speckle.Converters.Common; +using Speckle.Converters.CSiShared; +using Speckle.Converters.CSiShared.Extensions; +using Speckle.Converters.CSiShared.ToSpeckle.Helpers; +using Speckle.Converters.CSiShared.Utils; + +namespace Speckle.Converters.ETABSShared.ToSpeckle.Helpers; + +/// +/// Extracts ETABS-specific properties from shell elements using the AreaObj API calls. +/// +/// +/// Responsibilities: +/// +/// Extracts properties only available in ETABS (e.g., Label, Level) +/// Complements by adding product-specific data +/// Follows same pattern of single-purpose methods for clear API mapping +/// +/// +/// Design Decisions: +/// +/// Maintains separate methods for each property following CSI API structure +/// Properties are organized by their functional groups (Object ID, Assignments, Design) +/// +/// +public sealed class EtabsShellPropertiesExtractor +{ + private readonly IConverterSettingsStore _settingsStore; + private readonly CsiToSpeckleCacheSingleton _csiToSpeckleCacheSingleton; + private readonly DatabaseTableExtractor _databaseTableExtractor; + + public EtabsShellPropertiesExtractor( + CsiToSpeckleCacheSingleton csiToSpeckleCacheSingleton, + IConverterSettingsStore settingsStore, + DatabaseTableExtractor databaseTableExtractor + ) + { + _settingsStore = settingsStore; + _csiToSpeckleCacheSingleton = csiToSpeckleCacheSingleton; + _databaseTableExtractor = databaseTableExtractor; + } + + public void ExtractProperties(CsiShellWrapper shell, Dictionary properties) + { + var objectId = properties.EnsureNested(ObjectPropertyCategory.OBJECT_ID); + string designOrientation = GetDesignOrientation(shell); + objectId[CommonObjectProperty.DESIGN_ORIENTATION] = designOrientation; + (objectId[CommonObjectProperty.LABEL], objectId[CommonObjectProperty.LEVEL]) = GetLabelAndLevel(shell); + + var assignments = properties.EnsureNested(ObjectPropertyCategory.ASSIGNMENTS); + assignments["Diaphragm"] = GetAssignedDiaphragmName(shell); + assignments["Opening"] = IsOpening(shell); + assignments["Pier"] = GetPierAssignmentName(shell); + assignments["Spandrel"] = GetSpandrelAssignmentName(shell); + assignments[CommonObjectProperty.SPRING_ASSIGNMENT] = GetSpringAssignmentName(shell); + + // NOTE: Section Property and Material are a "quick-fix" to enable filtering in the viewer etc. + // Assign Section Property to variable as this will be an argument for the GetMaterialName method + string shellAppId = shell.GetSpeckleApplicationId(_settingsStore.Current.SapModel); + string sectionId = GetSectionName(shell); + string materialId = GetMaterialForSection(sectionId, designOrientation); + assignments[ObjectPropertyKey.SECTION_ID] = sectionId; + assignments[ObjectPropertyKey.MATERIAL_ID] = materialId; + + var geometry = properties.EnsureNested(ObjectPropertyCategory.GEOMETRY); + double area = GetArea(shell, designOrientation); + geometry.AddWithUnits("Area", area, $"{_settingsStore.Current.SpeckleUnits}²"); + + // store the object, section, and material id relationships in their corresponding caches to be accessed by the connector + if (!string.IsNullOrEmpty(sectionId)) + { + if (_csiToSpeckleCacheSingleton.ShellSectionCache.TryGetValue(sectionId, out List? shellIds)) + { + shellIds.Add(shellAppId); + } + else + { + _csiToSpeckleCacheSingleton.ShellSectionCache.Add(sectionId, new List() { shellAppId }); + } + + if (!string.IsNullOrEmpty(materialId)) + { + if (_csiToSpeckleCacheSingleton.MaterialCache.TryGetValue(materialId, out List? sectionIds)) + { + // Since this is happening on the object level, we could be processing the same sectionIds (from different + // objects) many times. This is not necessary since we just want a set of sectionId corresponding to material + if (!sectionIds.Contains(sectionId)) + { + sectionIds.Add(sectionId); + } + } + else + { + _csiToSpeckleCacheSingleton.MaterialCache.Add(materialId, new List() { sectionId }); + } + } + } + } + + private (string label, string level) GetLabelAndLevel(CsiShellWrapper shell) + { + string label = string.Empty, + level = string.Empty; + _ = _settingsStore.Current.SapModel.AreaObj.GetLabelFromName(shell.Name, ref label, ref level); + return (label, level); + } + + private string GetDesignOrientation(CsiShellWrapper shell) + { + eAreaDesignOrientation designOrientation = eAreaDesignOrientation.Null; + _ = _settingsStore.Current.SapModel.AreaObj.GetDesignOrientation(shell.Name, ref designOrientation); + return designOrientation.ToString(); + } + + private string GetAssignedDiaphragmName(CsiShellWrapper shell) + { + string diaphragmName = string.Empty; + _ = _settingsStore.Current.SapModel.AreaObj.GetDiaphragm(shell.Name, ref diaphragmName); + return diaphragmName; + } + + private string IsOpening(CsiShellWrapper shell) + { + bool isOpening = false; + _ = _settingsStore.Current.SapModel.AreaObj.GetOpening(shell.Name, ref isOpening); + return isOpening.ToString(); + } + + private string GetPierAssignmentName(CsiShellWrapper shell) + { + string pierAssignment = string.Empty; + _ = _settingsStore.Current.SapModel.AreaObj.GetPier(shell.Name, ref pierAssignment); + return pierAssignment; + } + + private string GetSpandrelAssignmentName(CsiShellWrapper shell) + { + string spandrelAssignment = string.Empty; + _ = _settingsStore.Current.SapModel.AreaObj.GetSpandrel(shell.Name, ref spandrelAssignment); + return spandrelAssignment; + } + + private string GetSpringAssignmentName(CsiShellWrapper shell) + { + string springAssignmentName = string.Empty; + _ = _settingsStore.Current.SapModel.AreaObj.GetSpringAssignment(shell.Name, ref springAssignmentName); + return springAssignmentName; + } + + // NOTE: Moved from CsiShellPropertiesExtractor because of the materialId issue. + // Results of the cDatabaseTable query for "Area Section Property Definitions - Summary" vary between Sap and Etabs + private string GetSectionName(CsiShellWrapper shell) + { + string sectionName = string.Empty; + _ = _settingsStore.Current.SapModel.AreaObj.GetProperty(shell.Name, ref sectionName); + return sectionName; + } + + private string GetMaterialForSection(string sectionName, string designOrientation) + { + if (designOrientation == "Null") // openings don't have a material + { + return string.Empty; + } + string materialId = _databaseTableExtractor + .GetTableData("Area Section Property Definitions - Summary", "Name", ["Name", "Material"]) + .GetRowValue(sectionName, "Material"); + return materialId; + } + + private double GetArea(CsiShellWrapper shell, string designOrientation) + { + // database to use depends on sub shell-type + string tableKey = designOrientation switch + { + "Floor" => "Floor Object Connectivity", + "Wall" => "Wall Object Connectivity", + "Null" => "Null Area Object Connectivity", + _ => throw new ArgumentException($"Unexpected design orientation: {designOrientation}") + }; + + // using the DatabaseTableExtractor fetch table with key from the designOrientation + // limit query size to "UniqueName" and "Area" + string area = _databaseTableExtractor + .GetTableData(tableKey, requestedColumns: ["UniqueName", "Area"]) + .GetRowValue(shell.Name, "Area"); + + // all database data is returned as strings + return double.TryParse(area, out var result) ? result : double.NaN; + } +} diff --git a/Converters/CSi/Speckle.Converters.ETABSShared/ToSpeckle/TopLevel/EtabsObjectToSpeckleConverter.cs b/Converters/CSi/Speckle.Converters.ETABSShared/ToSpeckle/TopLevel/EtabsObjectToSpeckleConverter.cs new file mode 100644 index 000000000..349a65ede --- /dev/null +++ b/Converters/CSi/Speckle.Converters.ETABSShared/ToSpeckle/TopLevel/EtabsObjectToSpeckleConverter.cs @@ -0,0 +1,54 @@ +using Speckle.Converters.Common; +using Speckle.Converters.CSiShared; +using Speckle.Converters.CSiShared.ToSpeckle.Helpers; +using Speckle.Converters.CSiShared.ToSpeckle.TopLevel; +using Speckle.Objects; +using Speckle.Objects.Data; +using Speckle.Sdk.Models; + +namespace Speckle.Converters.ETABSShared.ToSpeckle.TopLevel; + +/// +/// Top level converter responsible for converting Etabs objects to Speckle objects. +/// Implements the Template Method pattern through inheritance from (abstract) CsiObjectToSpeckleConverterBase. +/// +/// +/// Conversion Flow: +/// 1. EtabsObjectToSpeckleConverter inherits base conversion logic from CsiObjectToSpeckleConverterBase +/// 2. Base Convert method orchestrates the conversion process: +/// - DisplayValue extraction (handled by CsiShared - shared geometry conversion) +/// - Object data querying (combination of shared and application-specific data) +/// * SharedPropertiesExtractor for common CSi data +/// * IApplicationPropertiesExtractor for ETABS-specific data +/// 3. CreateTargetObject method ensures type-safe conversion to EtabsObject +/// +[NameAndRankValue(typeof(CsiWrapperBase), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class EtabsObjectToSpeckleConverter : CsiObjectToSpeckleConverterBase +{ + public EtabsObjectToSpeckleConverter( + IConverterSettingsStore settingsStore, + DisplayValueExtractor displayValueExtractor, + IApplicationPropertiesExtractor applicationPropertiesExtractor + ) + : base(settingsStore, displayValueExtractor, applicationPropertiesExtractor) { } + + protected override Base CreateTargetObject( + string name, + string type, + List elements, + List displayValue, + Dictionary properties, + string units, + string applicationId + ) => + new EtabsObject + { + name = name, + type = type, + elements = elements.Cast().ToList(), + displayValue = displayValue, + properties = properties, + units = units, + applicationId = applicationId + }; +} diff --git a/Converters/Civil3d/Speckle.Converters.Civil3d2022/packages.lock.json b/Converters/Civil3d/Speckle.Converters.Civil3d2022/packages.lock.json index d304231db..01c8323dd 100644 --- a/Converters/Civil3d/Speckle.Converters.Civil3d2022/packages.lock.json +++ b/Converters/Civil3d/Speckle.Converters.Civil3d2022/packages.lock.json @@ -168,11 +168,6 @@ "resolved": "8.0.0", "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" }, - "Speckle.DoubleNumerics": { - "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w==" - }, "Speckle.Newtonsoft.Json": { "type": "Transitive", "resolved": "13.0.2", @@ -267,7 +262,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.191, )" + "Speckle.Objects": "[3.1.1, )" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { @@ -294,20 +289,26 @@ "resolved": "2.2.0", "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "+m7jRFm0ABbkcSz2UphdxAsislY10IpQ1u79c8a3aVvegLjnsVQZ1sXfRIRO1aFdulkhjYKXYpB3N9M8Z+epgQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.191" + "Speckle.Sdk": "3.1.1" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "VVT3LJiYlhqnggxxdeTt1QLrqfxDb044x0yX6kxS9b5MRzeDvK2Vz86pLDfuHs+SXvDimRVfYx1M42IW/aPcTQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.Bcl.AsyncInterfaces": "5.0.0", @@ -315,16 +316,16 @@ "Microsoft.Data.Sqlite": "7.0.5", "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", "Microsoft.Extensions.Logging": "2.2.0", - "Speckle.DoubleNumerics": "4.0.1", + "Speckle.DoubleNumerics": "4.1.0", "Speckle.Newtonsoft.Json": "13.0.2", - "Speckle.Sdk.Dependencies": "3.1.0-dev.191" + "Speckle.Sdk.Dependencies": "3.1.1" } }, "Speckle.Sdk.Dependencies": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "EmEOyjsGsNi56Z/ZoBOn8WirTmIT2yqWvlUeUh0BSPX2TDMZXHTKOM/kHmP6HSd10KVFn2Zo/ItY7/K9iRtL1Q==" + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" } } } diff --git a/Converters/Civil3d/Speckle.Converters.Civil3d2023/packages.lock.json b/Converters/Civil3d/Speckle.Converters.Civil3d2023/packages.lock.json index 38c2191a3..f2c39855b 100644 --- a/Converters/Civil3d/Speckle.Converters.Civil3d2023/packages.lock.json +++ b/Converters/Civil3d/Speckle.Converters.Civil3d2023/packages.lock.json @@ -168,11 +168,6 @@ "resolved": "8.0.0", "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" }, - "Speckle.DoubleNumerics": { - "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w==" - }, "Speckle.Newtonsoft.Json": { "type": "Transitive", "resolved": "13.0.2", @@ -267,7 +262,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.191, )" + "Speckle.Objects": "[3.1.1, )" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { @@ -294,20 +289,26 @@ "resolved": "2.2.0", "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "+m7jRFm0ABbkcSz2UphdxAsislY10IpQ1u79c8a3aVvegLjnsVQZ1sXfRIRO1aFdulkhjYKXYpB3N9M8Z+epgQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.191" + "Speckle.Sdk": "3.1.1" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "VVT3LJiYlhqnggxxdeTt1QLrqfxDb044x0yX6kxS9b5MRzeDvK2Vz86pLDfuHs+SXvDimRVfYx1M42IW/aPcTQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.Bcl.AsyncInterfaces": "5.0.0", @@ -315,16 +316,16 @@ "Microsoft.Data.Sqlite": "7.0.5", "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", "Microsoft.Extensions.Logging": "2.2.0", - "Speckle.DoubleNumerics": "4.0.1", + "Speckle.DoubleNumerics": "4.1.0", "Speckle.Newtonsoft.Json": "13.0.2", - "Speckle.Sdk.Dependencies": "3.1.0-dev.191" + "Speckle.Sdk.Dependencies": "3.1.1" } }, "Speckle.Sdk.Dependencies": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "EmEOyjsGsNi56Z/ZoBOn8WirTmIT2yqWvlUeUh0BSPX2TDMZXHTKOM/kHmP6HSd10KVFn2Zo/ItY7/K9iRtL1Q==" + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" } } } diff --git a/Converters/Civil3d/Speckle.Converters.Civil3d2024/packages.lock.json b/Converters/Civil3d/Speckle.Converters.Civil3d2024/packages.lock.json index 89a914982..14e87ffcb 100644 --- a/Converters/Civil3d/Speckle.Converters.Civil3d2024/packages.lock.json +++ b/Converters/Civil3d/Speckle.Converters.Civil3d2024/packages.lock.json @@ -168,11 +168,6 @@ "resolved": "8.0.0", "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" }, - "Speckle.DoubleNumerics": { - "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w==" - }, "Speckle.Newtonsoft.Json": { "type": "Transitive", "resolved": "13.0.2", @@ -267,7 +262,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.191, )" + "Speckle.Objects": "[3.1.1, )" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { @@ -294,20 +289,26 @@ "resolved": "2.2.0", "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "+m7jRFm0ABbkcSz2UphdxAsislY10IpQ1u79c8a3aVvegLjnsVQZ1sXfRIRO1aFdulkhjYKXYpB3N9M8Z+epgQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.191" + "Speckle.Sdk": "3.1.1" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "VVT3LJiYlhqnggxxdeTt1QLrqfxDb044x0yX6kxS9b5MRzeDvK2Vz86pLDfuHs+SXvDimRVfYx1M42IW/aPcTQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.Bcl.AsyncInterfaces": "5.0.0", @@ -315,16 +316,16 @@ "Microsoft.Data.Sqlite": "7.0.5", "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", "Microsoft.Extensions.Logging": "2.2.0", - "Speckle.DoubleNumerics": "4.0.1", + "Speckle.DoubleNumerics": "4.1.0", "Speckle.Newtonsoft.Json": "13.0.2", - "Speckle.Sdk.Dependencies": "3.1.0-dev.191" + "Speckle.Sdk.Dependencies": "3.1.1" } }, "Speckle.Sdk.Dependencies": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "EmEOyjsGsNi56Z/ZoBOn8WirTmIT2yqWvlUeUh0BSPX2TDMZXHTKOM/kHmP6HSd10KVFn2Zo/ItY7/K9iRtL1Q==" + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" } } } diff --git a/Converters/Civil3d/Speckle.Converters.Civil3d2025/packages.lock.json b/Converters/Civil3d/Speckle.Converters.Civil3d2025/packages.lock.json index b5c6b4bba..0d95b0b3d 100644 --- a/Converters/Civil3d/Speckle.Converters.Civil3d2025/packages.lock.json +++ b/Converters/Civil3d/Speckle.Converters.Civil3d2025/packages.lock.json @@ -159,11 +159,6 @@ "resolved": "8.0.0", "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" }, - "Speckle.DoubleNumerics": { - "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w==" - }, "Speckle.Newtonsoft.Json": { "type": "Transitive", "resolved": "13.0.2", @@ -224,9 +219,9 @@ "dependencies": { "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", "Speckle.Connectors.Logging": "[1.0.0, )", - "Speckle.Objects": "[3.1.0-dev.191, )", - "Speckle.Sdk": "[3.1.0-dev.191, )", - "Speckle.Sdk.Dependencies": "[3.1.0-dev.191, )" + "Speckle.Objects": "[3.1.1, )", + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" } }, "speckle.connectors.dui": { @@ -234,9 +229,8 @@ "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", "Speckle.Connectors.Common": "[1.0.0, )", - "Speckle.Sdk": "[3.1.0-dev.191, )", - "Speckle.Sdk.Dependencies": "[3.1.0-dev.191, )", - "System.Threading.Tasks.Dataflow": "[6.0.0, )" + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" } }, "speckle.connectors.dui.webview": { @@ -253,7 +247,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.191, )" + "Speckle.Objects": "[3.1.1, )" } }, "Microsoft.Extensions.DependencyInjection": { @@ -295,42 +289,42 @@ "resolved": "1.0.1938.49", "contentHash": "z8KnFnaTYzhA/ZnyRX0qGfS1NU5ZBJeClAH64F0fVDvdDJTvME7xl6zTJ0Jlfe1BtL3C0NH9xTy64shg2baKdw==" }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "+m7jRFm0ABbkcSz2UphdxAsislY10IpQ1u79c8a3aVvegLjnsVQZ1sXfRIRO1aFdulkhjYKXYpB3N9M8Z+epgQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.191" + "Speckle.Sdk": "3.1.1" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "VVT3LJiYlhqnggxxdeTt1QLrqfxDb044x0yX6kxS9b5MRzeDvK2Vz86pLDfuHs+SXvDimRVfYx1M42IW/aPcTQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.CSharp": "4.7.0", "Microsoft.Data.Sqlite": "7.0.5", "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", "Microsoft.Extensions.Logging": "2.2.0", - "Speckle.DoubleNumerics": "4.0.1", + "Speckle.DoubleNumerics": "4.1.0", "Speckle.Newtonsoft.Json": "13.0.2", - "Speckle.Sdk.Dependencies": "3.1.0-dev.191" + "Speckle.Sdk.Dependencies": "3.1.1" } }, "Speckle.Sdk.Dependencies": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "EmEOyjsGsNi56Z/ZoBOn8WirTmIT2yqWvlUeUh0BSPX2TDMZXHTKOM/kHmP6HSd10KVFn2Zo/ItY7/K9iRtL1Q==" - }, - "System.Threading.Tasks.Dataflow": { - "type": "CentralTransitive", - "requested": "[6.0.0, )", - "resolved": "6.0.0", - "contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA==" + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" } } } diff --git a/Converters/Civil3d/Speckle.Converters.Civil3dShared/Civil3dRootToSpeckleConverter.cs b/Converters/Civil3d/Speckle.Converters.Civil3dShared/Civil3dRootToSpeckleConverter.cs index b09be19c2..15e2172a2 100644 --- a/Converters/Civil3d/Speckle.Converters.Civil3dShared/Civil3dRootToSpeckleConverter.cs +++ b/Converters/Civil3d/Speckle.Converters.Civil3dShared/Civil3dRootToSpeckleConverter.cs @@ -1,6 +1,4 @@ using System.ComponentModel.DataAnnotations; -using Autodesk.AutoCAD.DatabaseServices; -using Speckle.Converters.Civil3dShared.ToSpeckle; using Speckle.Converters.Common; using Speckle.Converters.Common.Objects; using Speckle.Converters.Common.Registration; @@ -13,49 +11,41 @@ public class Civil3dRootToSpeckleConverter : IRootToSpeckleConverter { private readonly IConverterManager _toSpeckle; private readonly IConverterSettingsStore _settingsStore; - private readonly PartDataExtractor _partDataExtractor; - private readonly PropertySetExtractor _propertySetExtractor; - private readonly GeneralPropertiesExtractor _generalPropertiesExtractor; - private readonly ExtensionDictionaryExtractor _extensionDictionaryExtractor; + private readonly ToSpeckle.PropertiesExtractor _propertiesExtractor; public Civil3dRootToSpeckleConverter( IConverterManager toSpeckle, IConverterSettingsStore settingsStore, - PartDataExtractor partDataExtractor, - PropertySetExtractor propertySetExtractor, - GeneralPropertiesExtractor generalPropertiesExtractor, - ExtensionDictionaryExtractor extensionDictionaryExtractor + ToSpeckle.PropertiesExtractor propertiesExtractor ) { _toSpeckle = toSpeckle; _settingsStore = settingsStore; - _partDataExtractor = partDataExtractor; - _propertySetExtractor = propertySetExtractor; - _generalPropertiesExtractor = generalPropertiesExtractor; - _extensionDictionaryExtractor = extensionDictionaryExtractor; + _propertiesExtractor = propertiesExtractor; } public Base Convert(object target) { - if (target is not DBObject dbObject) + if (target is not ADB.DBObject dbObject) { throw new ValidationException( $"Conversion of {target.GetType().Name} to Speckle is not supported. Only objects that inherit from DBObject are." ); } + if (target is CDB.AlignmentLabelGroup) // TODO: this should not throw and be reported from connector instead, similar to supported categories in Revit. + { + throw new ValidationException($"Conversion of {target.GetType().Name} to Speckle is not supported yet."); + } + Type type = dbObject.GetType(); - object objectToConvert = dbObject; - Dictionary properties = new(); // check first for civil type objects + // POC: some classes (eg Civil.DatabaseServices.CogoPoint) actually inherit from Autocad.DatabaseServices.Entity instead of Civil!! + // These need top level converters in Civil for now, but in the future we should implement a EntityToSpeckleTopLevelConverter for Autocad as well. if (target is CDB.Entity civilEntity) { type = civilEntity.GetType(); - objectToConvert = civilEntity; - - // get properties like partdata, property sets, general properties - properties = GetCivilEntityProperties(civilEntity); } var objectConverter = _toSpeckle.ResolveConverter(type); @@ -66,12 +56,18 @@ public class Civil3dRootToSpeckleConverter : IRootToSpeckleConverter { using (var tr = _settingsStore.Current.Document.Database.TransactionManager.StartTransaction()) { - var result = objectConverter.Convert(objectToConvert); + var result = objectConverter.Convert(target); - if (properties.Count > 0) - { - result["properties"] = properties; - } + // NOTE: we can not test acad objects props, so commented out. + // // we need to capture properties on autocad entities + // if (target is ADB.Entity autocadEntity) + // { + // var properties = _propertiesExtractor.GetProperties(autocadEntity); + // if (properties.Count > 0) + // { + // result["properties"] = properties; + // } + // } tr.Commit(); return result; @@ -84,36 +80,4 @@ public class Civil3dRootToSpeckleConverter : IRootToSpeckleConverter throw; // Just rethrowing for now, Logs may be needed here. } } - - private Dictionary GetCivilEntityProperties(CDB.Entity entity) - { - Dictionary properties = new(); - - AddDictionaryToPropertyDictionary( - _generalPropertiesExtractor.GetGeneralProperties(entity), - "General Properties", - properties - ); - AddDictionaryToPropertyDictionary(_partDataExtractor.GetPartData(entity), "Part Data", properties); - AddDictionaryToPropertyDictionary(_propertySetExtractor.GetPropertySets(entity), "Property Sets", properties); - AddDictionaryToPropertyDictionary( - _extensionDictionaryExtractor.GetExtensionDictionary(entity), - "Extension Dictionary", - properties - ); - - return properties; - } - - private void AddDictionaryToPropertyDictionary( - Dictionary? entryDictionary, - string entryName, - Dictionary propertyDictionary - ) - { - if (entryDictionary is not null && entryDictionary.Count > 0) - { - propertyDictionary.Add(entryName, entryDictionary); - } - } } diff --git a/Converters/Civil3d/Speckle.Converters.Civil3dShared/Helpers/BaseCurveExtractor.cs b/Converters/Civil3d/Speckle.Converters.Civil3dShared/Helpers/BaseCurveExtractor.cs index 74b697b6f..a549a5aff 100644 --- a/Converters/Civil3d/Speckle.Converters.Civil3dShared/Helpers/BaseCurveExtractor.cs +++ b/Converters/Civil3d/Speckle.Converters.Civil3dShared/Helpers/BaseCurveExtractor.cs @@ -45,7 +45,6 @@ public sealed class BaseCurveExtractor { // rant: if this is a pipe, the BaseCurve prop is fake news && will return a DB.line with start and endpoints set to [0,0,0] & [0,0,1] // do not use basecurve for pipes 😡 - // currently not handling arc pipes due to lack of CircularArc2D converter, and also way to properly retrieve 2d arc curve case CDB.Pipe pipe: return GetPipeBaseCurves(pipe); @@ -55,6 +54,7 @@ public sealed class BaseCurveExtractor case CDB.FeatureLine: case CDB.Parcel: case CDB.ParcelSegment: + case CDB.Catchment: return new() { _curveConverter.Convert(entity.BaseCurve) }; // for any entities where basecurve prop doesn't make sense diff --git a/Converters/Civil3d/Speckle.Converters.Civil3dShared/Helpers/CatchmentGroupHandler.cs b/Converters/Civil3d/Speckle.Converters.Civil3dShared/Helpers/CatchmentGroupHandler.cs deleted file mode 100644 index fd0fbf76c..000000000 --- a/Converters/Civil3d/Speckle.Converters.Civil3dShared/Helpers/CatchmentGroupHandler.cs +++ /dev/null @@ -1,61 +0,0 @@ -using Speckle.Converters.Civil3dShared.Extensions; -using Speckle.Converters.Common; -using Speckle.Sdk.Models.Proxies; - -namespace Speckle.Converters.Civil3dShared.Helpers; - -public sealed class CatchmentGroupHandler -{ - /// - /// Keeps track of all catchment groups used by catchments in the current send operation. - /// (catchmentGroup objectId, catchmentGroupProxy). - /// This should be added to the root commit object post conversion. - /// - /// POC: Using group proxies for now - public Dictionary CatchmentGroupProxiesCache { get; } = new(); - - private readonly IConverterSettingsStore _converterSettings; - - public CatchmentGroupHandler(IConverterSettingsStore converterSettings) - { - _converterSettings = converterSettings; - } - - /// - /// Extracts the Catchment group from a catchment and stores in the appId of the catchment. - /// - /// - /// - public void HandleCatchmentGroup(CDB.Catchment catchment) - { - ADB.ObjectId catchmentGroupId = catchment.ContainingGroupId; - - if (catchmentGroupId == ADB.ObjectId.Null) - { - return; - } - - string catchmentApplicationId = catchment.GetSpeckleApplicationId(); - if (CatchmentGroupProxiesCache.TryGetValue(catchmentGroupId, out GroupProxy? value)) - { - value.objects.Add(catchmentApplicationId); - } - else - { - using (var tr = _converterSettings.Current.Document.Database.TransactionManager.StartTransaction()) - { - var catchmentGroup = (CDB.CatchmentGroup)tr.GetObject(catchmentGroupId, ADB.OpenMode.ForRead); - - CatchmentGroupProxiesCache[catchmentGroupId] = new() - { - name = catchmentGroup.Name, - objects = new() { catchmentApplicationId }, - applicationId = catchmentGroup.Handle.Value.ToString() - }; - - tr.Commit(); - } - } - return; - } -} diff --git a/Converters/Civil3d/Speckle.Converters.Civil3dShared/Helpers/CorridorDisplayValueExtractor.cs b/Converters/Civil3d/Speckle.Converters.Civil3dShared/Helpers/CorridorDisplayValueExtractor.cs index 8f49b1017..0f2c2e695 100644 --- a/Converters/Civil3d/Speckle.Converters.Civil3dShared/Helpers/CorridorDisplayValueExtractor.cs +++ b/Converters/Civil3d/Speckle.Converters.Civil3dShared/Helpers/CorridorDisplayValueExtractor.cs @@ -94,6 +94,11 @@ public sealed class CorridorDisplayValueExtractor { foreach (ADB.ObjectId solidId in corridor.ExportSolids(param, corridor.Database)) { + if (solidId.IsNull) // unclear why this happens + { + continue; + } + SOG.Mesh? mesh = null; var solid = tr.GetObject(solidId, ADB.OpenMode.ForRead); if (solid is ADB.Solid3d solid3d) diff --git a/Converters/Civil3d/Speckle.Converters.Civil3dShared/Helpers/CorridorHandler.cs b/Converters/Civil3d/Speckle.Converters.Civil3dShared/Helpers/CorridorHandler.cs index 371f614b4..d55ea3343 100644 --- a/Converters/Civil3d/Speckle.Converters.Civil3dShared/Helpers/CorridorHandler.cs +++ b/Converters/Civil3d/Speckle.Converters.Civil3dShared/Helpers/CorridorHandler.cs @@ -91,7 +91,7 @@ public sealed class CorridorHandler } if (mainFeatureLines.Count > 0) { - convertedBaseline["mainBaselineFeatureLines"] = mainFeatureLines; + convertedBaseline["@mainBaselineFeatureLines"] = mainFeatureLines; } List offsetFeatureLines = new(); @@ -109,7 +109,7 @@ public sealed class CorridorHandler } if (offsetFeatureLines.Count > 0) { - convertedBaseline["offsetBaselineFeatureLines"] = mainFeatureLines; + convertedBaseline["@offsetBaselineFeatureLines"] = mainFeatureLines; } // get the baseline regions @@ -220,7 +220,7 @@ public sealed class CorridorHandler { string subassemblyId = appliedSubassembly.SubassemblyId.GetSpeckleApplicationId(); string name = subassemblyNameCache.TryGetValue(appliedSubassembly.SubassemblyId, out string? cachedName) - ? cachedName + ? cachedName! : subassemblyId; Dictionary appliedSubassemblyDict = diff --git a/Converters/Civil3d/Speckle.Converters.Civil3dShared/Helpers/DisplayValueExtractor.cs b/Converters/Civil3d/Speckle.Converters.Civil3dShared/Helpers/DisplayValueExtractor.cs index 495915200..c56ece6f0 100644 --- a/Converters/Civil3d/Speckle.Converters.Civil3dShared/Helpers/DisplayValueExtractor.cs +++ b/Converters/Civil3d/Speckle.Converters.Civil3dShared/Helpers/DisplayValueExtractor.cs @@ -36,6 +36,11 @@ public sealed class DisplayValueExtractor { switch (entity) { + // POC: we are sending featurelines as approximated polylines because they are 2.5d curves: + // they can have line or arc segments, and each vertex can have different elevations. + // there is no native type that can capture the full 3d representation of these curves. + // if this becomes essential, can explore a hack where each point is converted to 2d, and separate line/arc segments are calculated, and then their points readjusted with 3d z values + // SurveyFigures inherit from featureline case CDB.FeatureLine featureline: SOG.Polyline featurelinePolyline = _pointCollectionConverter.Convert( featureline.GetPoints(Autodesk.Civil.FeatureLinePointType.PIPoint) @@ -60,6 +65,13 @@ public sealed class DisplayValueExtractor yield return gridSurfaceMesh; break; + // catchments + case CDB.Catchment catchment: + SOG.Polyline catchmentPolyline = _pointCollectionConverter.Convert(catchment.BoundaryPolyline3d); + catchmentPolyline.closed = true; + yield return catchmentPolyline; + break; + // Corridors are complicated: their display values are extracted in the CorridorHandler when processing corridor children, since they are attached to the corridor subassemblies. case CDB.Corridor: yield break; diff --git a/Converters/Civil3d/Speckle.Converters.Civil3dShared/Helpers/PipeNetworkHandler.cs b/Converters/Civil3d/Speckle.Converters.Civil3dShared/Helpers/PipeNetworkHandler.cs deleted file mode 100644 index 8ed0f2a9c..000000000 --- a/Converters/Civil3d/Speckle.Converters.Civil3dShared/Helpers/PipeNetworkHandler.cs +++ /dev/null @@ -1,60 +0,0 @@ -using Speckle.Converters.Civil3dShared.Extensions; -using Speckle.Converters.Common; -using Speckle.Sdk.Models.Proxies; - -namespace Speckle.Converters.Civil3dShared.Helpers; - -public sealed class PipeNetworkHandler -{ - /// - /// Keeps track of all networks used by parts in the current send operation. - /// (network app id, network Proxy). - /// This should be added to the root commit object post conversion. - /// - /// POC: Using group proxies for now - public Dictionary PipeNetworkProxiesCache { get; } = new(); - - private readonly IConverterSettingsStore _converterSettings; - - public PipeNetworkHandler(IConverterSettingsStore converterSettings) - { - _converterSettings = converterSettings; - } - - /// - /// Extracts the pipe network from a part and stores in the appId of the part. - /// - /// - /// - public void HandlePipeNetwork(CDB.Part part) - { - if (part.NetworkId == ADB.ObjectId.Null) - { - return; - } - - string networkApplicationId = part.NetworkId.GetSpeckleApplicationId(); - string partApplicationId = part.GetSpeckleApplicationId(); - if (PipeNetworkProxiesCache.TryGetValue(networkApplicationId, out GroupProxy? value)) - { - value.objects.Add(partApplicationId); - } - else - { - using (var tr = _converterSettings.Current.Document.Database.TransactionManager.StartTransaction()) - { - var network = (CDB.Network)tr.GetObject(part.NetworkId, ADB.OpenMode.ForRead); - - PipeNetworkProxiesCache[networkApplicationId] = new() - { - name = network.Name, - objects = new() { partApplicationId }, - applicationId = networkApplicationId - }; - - tr.Commit(); - } - } - return; - } -} diff --git a/Converters/Civil3d/Speckle.Converters.Civil3dShared/ServiceRegistration.cs b/Converters/Civil3d/Speckle.Converters.Civil3dShared/ServiceRegistration.cs index dc990d7b0..462bc26d5 100644 --- a/Converters/Civil3d/Speckle.Converters.Civil3dShared/ServiceRegistration.cs +++ b/Converters/Civil3d/Speckle.Converters.Civil3dShared/ServiceRegistration.cs @@ -35,16 +35,15 @@ public static class ServiceRegistration >(); // add other classes + serviceCollection.AddScoped(); // for civil + // serviceCollection.AddScoped(); // for autocad // NOTE: we can't test for acad, so we're kicking this out from acad serviceCollection.AddScoped(); serviceCollection.AddScoped(); serviceCollection.AddScoped(); serviceCollection.AddScoped(); serviceCollection.AddScoped(); - serviceCollection.AddScoped(); serviceCollection.AddScoped(); serviceCollection.AddScoped(); - serviceCollection.AddScoped(); - serviceCollection.AddScoped(); serviceCollection.AddScoped(); serviceCollection.AddScoped(); } diff --git a/Converters/Civil3d/Speckle.Converters.Civil3dShared/Speckle.Converters.Civil3dShared.projitems b/Converters/Civil3d/Speckle.Converters.Civil3dShared/Speckle.Converters.Civil3dShared.projitems index 9a5150531..94c5d7e69 100644 --- a/Converters/Civil3d/Speckle.Converters.Civil3dShared/Speckle.Converters.Civil3dShared.projitems +++ b/Converters/Civil3d/Speckle.Converters.Civil3dShared/Speckle.Converters.Civil3dShared.projitems @@ -17,14 +17,13 @@ - - - + + + - diff --git a/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/Properties/ClassPropertiesExtractor.cs b/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/Properties/ClassPropertiesExtractor.cs index 82407ab78..edc5240ea 100644 --- a/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/Properties/ClassPropertiesExtractor.cs +++ b/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/Properties/ClassPropertiesExtractor.cs @@ -1,36 +1,40 @@ +using System.Reflection; +using Autodesk.Civil.Runtime; using Speckle.Converters.Civil3dShared.Extensions; -using Speckle.Converters.Civil3dShared.Helpers; using Speckle.Converters.Common; -using Speckle.Converters.Common.Objects; namespace Speckle.Converters.Civil3dShared.ToSpeckle; /// -/// Extracts class properties deemed important from a civil entity. +/// Extracts class properties deemed important for business intelligence workflows from a civil entity. /// Should not repeat any data that would be included on property sets and general properties on the object. /// Expects to be scoped per operation. /// public class ClassPropertiesExtractor { - private readonly IConverterSettingsStore _settingsStore; - private readonly ITypedConverter _point3dCollectionConverter; - private readonly ITypedConverter _pointConverter; - private readonly CatchmentGroupHandler _catchmentGroupHandler; - private readonly PipeNetworkHandler _pipeNetworkHandler; + private const string ASSIGNMENT_PROP = "Assignments"; + private const string DIMENSIONAL_PROP = "Dimensional Properties"; + private const string SITEID_PROP = "siteId"; + private const string SITENAME_PROP = "siteName"; + private const string SURFACEID_PROP = "surfaceId"; + private const string SURFACENAME_PROP = "surfaceName"; + private const string NETWORKID_PROP = "networkId"; + private const string NETWORKNAME_PROP = "networkName"; + private const string ALIGNMENTID_PROP = "alignmentId"; + private const string ALIGNMENTNAME_PROP = "alignmentName"; + private const string CODES_PROP = "codes"; + private const string SHAPES_PROP = "shapes"; + private const string LINKS_PROP = "links"; + private const string POINTS_PROP = "points"; - public ClassPropertiesExtractor( - IConverterSettingsStore settingsStore, - ITypedConverter point3dCollectionConverter, - ITypedConverter pointConverter, - CatchmentGroupHandler catchmentGroupHandler, - PipeNetworkHandler pipeNetworkHandler - ) + private readonly IConverterSettingsStore _converterSettings; + + private readonly Dictionary _catchmentGroupCache = new(); + private readonly Dictionary _networkCache = new(); + + public ClassPropertiesExtractor(IConverterSettingsStore converterSettings) { - _point3dCollectionConverter = point3dCollectionConverter; - _pointConverter = pointConverter; - _settingsStore = settingsStore; - _catchmentGroupHandler = catchmentGroupHandler; - _pipeNetworkHandler = pipeNetworkHandler; + _converterSettings = converterSettings; } /// @@ -38,16 +42,19 @@ public class ClassPropertiesExtractor /// /// /// - public Dictionary? GetClassProperties(CDB.Entity entity) + public Dictionary GetClassProperties(CDB.Entity entity) { switch (entity) { - case CDB.Catchment catchment: - return ExtractCatchmentProperties(catchment); + // site case CDB.Site site: return ExtractSiteProperties(site); + case CDB.Catchment catchment: + return ExtractCatchmentProperties(catchment); case CDB.Parcel parcel: return ExtractParcelProperties(parcel); + case CDB.Surface surface: + return ExtractSurfaceProperties(surface); // pipe networks case CDB.Pipe pipe: @@ -55,82 +62,537 @@ public class ClassPropertiesExtractor case CDB.Structure structure: return ExtractStructureProperties(structure); - // alignments + // corridors, alignments, profiles + case CDB.Corridor corridor: + return ExtractCorridorProperties(corridor); case CDB.Alignment alignment: return ExtractAlignmentProperties(alignment); case CDB.Profile profile: return ExtractProfileProperties(profile); - // assemblies + // assemblies, subassemblies case CDB.Subassembly subassembly: return ExtractSubassemblyProperties(subassembly); default: - return null; + return new(); } } + private Dictionary ExtractSiteProperties(CDB.Site site) + { + // get general props + Dictionary properties = new() { }; + + // get assignments like catchment group, reference surface, reference pipe networks + Dictionary assignmentProps = new(); + if (site.GetAlignmentIds().Count > 0) + { + assignmentProps[ALIGNMENTID_PROP] = GetSpeckleApplicationIdsFromCollection(site.GetAlignmentIds()); + } + if (site.GetFeatureLineIds().Count > 0) + { + assignmentProps["featureLineId"] = GetSpeckleApplicationIdsFromCollection(site.GetFeatureLineIds()); + } + + AddDictionaryToDictionary(assignmentProps, properties, ASSIGNMENT_PROP); + return properties; + } + + private Dictionary ExtractCatchmentProperties(CDB.Catchment catchment) + { + // get general props + Dictionary properties = new() { }; + + // get assignments like catchment group, reference surface, reference pipe networks + Dictionary assignmentProps = new(); + if (catchment.ContainingGroupId != ADB.ObjectId.Null) + { + assignmentProps["catchmentGroupId"] = catchment.ContainingGroupId.GetSpeckleApplicationId(); + if (_catchmentGroupCache.TryGetValue(catchment.ContainingGroupId, out string? name)) + { + assignmentProps["catchmentGroupName"] = name; + } + else + { + using (var tr = _converterSettings.Current.Document.Database.TransactionManager.StartTransaction()) + { + var catchmentGroup = (CDB.CatchmentGroup)tr.GetObject(catchment.ContainingGroupId, ADB.OpenMode.ForRead); + _catchmentGroupCache[catchment.ContainingGroupId] = catchmentGroup.Name; + assignmentProps["catchmentGroupName"] = catchmentGroup.Name; + tr.Commit(); + } + } + } + + if (catchment.ReferenceSurfaceId != ADB.ObjectId.Null) + { + assignmentProps[SURFACEID_PROP] = catchment.ReferenceSurfaceId.GetSpeckleApplicationId(); + assignmentProps[SURFACENAME_PROP] = catchment.ReferenceSurfaceName; + } + + if (catchment.ReferencePipeNetworkId != ADB.ObjectId.Null) + { + assignmentProps[NETWORKID_PROP] = catchment.ReferencePipeNetworkId.GetSpeckleApplicationId(); + assignmentProps[NETWORKNAME_PROP] = catchment.ReferencePipeNetworkName; + } + + if (catchment.ReferencePipeNetworkStructureId != ADB.ObjectId.Null) + { + assignmentProps["networkStructureId"] = catchment.ReferencePipeNetworkStructureId.GetSpeckleApplicationId(); + assignmentProps["networkStructureName"] = catchment.ReferencePipeNetworkStructureName; + } + + AddDictionaryToDictionary(assignmentProps, properties, ASSIGNMENT_PROP); + + // get dimensional props + properties[DIMENSIONAL_PROP] = new Dictionary() + { + ["area"] = catchment.Area, + ["area2d"] = catchment.Area2d, + ["imperviousArea"] = catchment.ImperviousArea, + ["perimeter2d"] = catchment.Perimeter2d + }; + + // get hydrological props + properties["Hydrological Properties"] = new Dictionary() + { + ["timeOfConcentration"] = catchment.TimeOfConcentration, + ["timeOfConcentrationCalculationMethod"] = catchment.TimeOfConcentrationCalculationMethod, + ["hydrologicallyMostDistantPoint"] = catchment.HydrologicallyMostDistantPoint.ToArray(), + ["hydrologicallyMostDistantLength"] = catchment.HydrologicallyMostDistantLength, + ["runoffCoefficient"] = catchment.RunoffCoefficient, + ["hydrologicalSoilGroup"] = catchment.HydrologicalSoilGroup.ToString(), + ["antecedentWetness"] = catchment.AntecedentWetness + }; + + // get hydraulic props + properties["Hydraulic Properties"] = new Dictionary() + { + ["manningsCoefficient"] = catchment.ManningsCoefficient, +#if CIVIL3D2024_OR_GREATER + ["sheetFlowSegments"] = catchment.SheetFlowSegments, + ["sheetFlowTravelTime"] = catchment.SheetFlowTravelTime, + ["shallowFlowSegments"] = catchment.ShallowFlowSegments, + ["shallowFlowTravelTime"] = catchment.ShallowFlowTravelTime, + ["channelFlowSegments"] = catchment.ChannelFlowSegments, + ["channelFlowTravelTime"] = catchment.ChannelFlowTravelTime +#endif + }; + + return properties; + } + private Dictionary ExtractParcelProperties(CDB.Parcel parcel) { + // get general props + Dictionary properties = new() { ["number"] = parcel.Number, ["area"] = parcel.Area }; #if CIVIL3D2023_OR_GREATER - return new() { ["number"] = parcel.Number, ["taxId"] = parcel.TaxId }; -#else - return new() { ["number"] = parcel.Number }; + properties["taxId"] = parcel.TaxId; #endif + return properties; } - private Dictionary ExtractSubassemblyProperties(CDB.Subassembly subassembly) + private Dictionary ExtractSurfaceProperties(CDB.Surface surface) { - Dictionary subassemblyProperties = new(); + // get general props + Dictionary properties = new() { }; - subassemblyProperties["origin"] = _pointConverter.Convert(subassembly.Origin); + // get statistics props + Dictionary statisticsProps = ExtractPropertiesGeneric( + surface.GetGeneralProperties() + ); - // get shapes > links > points info - Dictionary shapes = new(); - int shapeCount = 0; - foreach (CDB.Shape shape in subassembly.Shapes) + switch (surface) { - Dictionary links = new(); - int linkCount = 0; - foreach (CDB.Link link in shape.Links) + case CDB.TinSurface tinSurface: + AddDictionaryToDictionary( + ExtractPropertiesGeneric(tinSurface.GetTerrainProperties()), + statisticsProps, + "Terrain" + ); + AddDictionaryToDictionary( + ExtractPropertiesGeneric(tinSurface.GetTinProperties()), + statisticsProps, + "TIN" + ); + break; + + case CDB.TinVolumeSurface tinVolumeSurface: + AddDictionaryToDictionary( + ExtractPropertiesGeneric(tinVolumeSurface.GetTinProperties()), + statisticsProps, + "TIN" + ); + AddDictionaryToDictionary( + ExtractPropertiesGeneric(tinVolumeSurface.GetVolumeProperties()), + statisticsProps, + "Volume" + ); + break; + + case CDB.GridSurface gridSurface: + AddDictionaryToDictionary( + ExtractPropertiesGeneric(gridSurface.GetTerrainProperties()), + statisticsProps, + "Terrain" + ); + AddDictionaryToDictionary( + ExtractPropertiesGeneric(gridSurface.GetGridProperties()), + statisticsProps, + "Grid" + ); + break; + + case CDB.GridVolumeSurface gridVolumeSurface: + AddDictionaryToDictionary( + ExtractPropertiesGeneric(gridVolumeSurface.GetGridProperties()), + statisticsProps, + "Grid" + ); + AddDictionaryToDictionary( + ExtractPropertiesGeneric(gridVolumeSurface.GetVolumeProperties()), + statisticsProps, + "Volume" + ); + break; + } + + AddDictionaryToDictionary(statisticsProps, properties, "Statistics"); + return properties; + } + + private Dictionary ExtractPipeProperties(CDB.Pipe pipe) + { + // get general props + Dictionary properties = + new() { - Dictionary points = new(); - int pointCount = 0; - foreach (CDB.Point point in link.Points) + ["domain"] = pipe.Domain.ToString(), // part prop + ["partType"] = pipe.PartType.ToString(), // part prop + ["bearing"] = pipe.Bearing, + ["slope"] = pipe.Slope, + ["shape"] = pipe.CrossSectionalShape.ToString(), + ["minimumCover"] = pipe.MinimumCover, + ["maximumCover"] = pipe.MaximumCover, + ["junctionLoss"] = pipe.JunctionLoss, + ["flowDirection"] = pipe.FlowDirection.ToString(), + ["flowRate"] = pipe.FlowRate + }; + + // get assignments like catchment group, reference surface, reference pipe networks + Dictionary assignmentProps = GetPartAssignments(pipe); + + if (pipe.StartStructureId != ADB.ObjectId.Null) + { + assignmentProps["startStructureId"] = pipe.StartStructureId.GetSpeckleApplicationId(); + } + if (pipe.EndStructureId != ADB.ObjectId.Null) + { + assignmentProps["endStructureId"] = pipe.EndStructureId.GetSpeckleApplicationId(); + } + + AddDictionaryToDictionary(assignmentProps, properties, ASSIGNMENT_PROP); + + // get dimensional props + properties[DIMENSIONAL_PROP] = new Dictionary() + { + ["innerDiameterOrWidth"] = pipe.InnerDiameterOrWidth, + ["innerHeight"] = pipe.InnerHeight, +#pragma warning disable CS0618 // Type or member is obsolete + ["length2d"] = pipe.Length2D, //Length2D was un-obsoleted in 2023, but is still marked obsolete in 2022 +#pragma warning restore CS0618 // Type or member is obsolete + }; + + return properties; + } + + private Dictionary ExtractStructureProperties(CDB.Structure structure) + { + // get general props + Dictionary properties = + new() + { + ["domain"] = structure.Domain.ToString(), // part prop + ["partType"] = structure.PartType.ToString(), // part prop + ["northing"] = structure.Northing, + ["rotation"] = structure.Rotation, + }; + + // get assignments like catchment group, reference surface, reference pipe networks + Dictionary assignmentProps = GetPartAssignments(structure); + AddDictionaryToDictionary(assignmentProps, properties, ASSIGNMENT_PROP); + + // get dimensional props + Dictionary dimensionalProps = + new() + { + ["sumpDepth"] = structure.SumpDepth, + ["sumpElevation"] = structure.SumpElevation, + ["innerDiameterOrWidth"] = structure.InnerDiameterOrWidth + }; + + if (structure.BoundingShape == CDB.BoundingShapeType.Box) + { + dimensionalProps["innerLength"] = structure.InnerLength; + dimensionalProps["length"] = structure.Length; + } + properties[DIMENSIONAL_PROP] = dimensionalProps; + + // get location + properties["Location"] = new Dictionary() + { + ["x"] = structure.Location.X, + ["y"] = structure.Location.Y, + ["z"] = structure.Location.Z, + }; + + return properties; + } + + private Dictionary GetPartAssignments(CDB.Part part) + { + Dictionary partAssignments = new(); + + // get part family + if (part.PartFamilyId != ADB.ObjectId.Null) + { + partAssignments["partFamilyId"] = part.PartFamilyId.GetSpeckleApplicationId(); + partAssignments["partFamilyName"] = part.PartFamilyName; + } + + // get network + if (part.NetworkId != ADB.ObjectId.Null) + { + partAssignments[NETWORKID_PROP] = part.NetworkId.GetSpeckleApplicationId(); + if (_networkCache.TryGetValue(part.NetworkId, out string? name)) + { + partAssignments[NETWORKNAME_PROP] = name; + } + else + { + using (var tr = _converterSettings.Current.Document.Database.TransactionManager.StartTransaction()) { - points[pointCount.ToString()] = new Dictionary() - { - ["elevation"] = point.Elevation, - ["codes"] = point.Codes.ToList(), - ["offset"] = point.Offset, - }; - pointCount++; + var network = (CDB.Network)tr.GetObject(part.NetworkId, ADB.OpenMode.ForRead); + _networkCache[part.NetworkId] = network.Name; + partAssignments[NETWORKNAME_PROP] = name; + tr.Commit(); } + } + } - links[linkCount.ToString()] = new Dictionary() + // get surface + if (part.RefSurfaceId != ADB.ObjectId.Null) + { + partAssignments[SURFACEID_PROP] = part.RefSurfaceId.GetSpeckleApplicationId(); + partAssignments[SURFACENAME_PROP] = part.RefSurfaceName; + } + + // get alignment + if (part.RefAlignmentId != ADB.ObjectId.Null) + { + partAssignments[ALIGNMENTID_PROP] = part.RefAlignmentId.GetSpeckleApplicationId(); + partAssignments[ALIGNMENTNAME_PROP] = part.RefAlignmentName; + } + + return partAssignments; + } + + private void ProcessCorridorFeaturelinePoints( + CDB.CorridorFeatureLine featureline, + Dictionary> featureLinesDict + ) + { + if (featureLinesDict.TryGetValue(featureline.CodeName, out Dictionary? value)) + { + Dictionary pointsDict = new(featureline.FeatureLinePoints.Count); + int pointCount = 0; + foreach (CDB.FeatureLinePoint point in featureline.FeatureLinePoints) + { + Dictionary pointPropertiesDict = + new() + { + ["station"] = point.Station, + ["x"] = point.XYZ.X, + ["y"] = point.XYZ.Y, + ["z"] = point.XYZ.Z, + ["isBreak"] = point.IsBreak + }; + + // not all points have offsets. Accessing the offset property in this case will throw. + try { - ["codes"] = link.Codes.ToList(), - ["points"] = points - }; + pointPropertiesDict["offset"] = point.Offset; + } + catch (ArgumentException) { } // do nothing - offset property will not be included - linkCount++; + pointsDict[pointCount.ToString()] = pointPropertiesDict; + pointCount++; } - shapes[shapeCount.ToString()] = new Dictionary() + value["featureLinePoints"] = pointsDict; + } + } + + private Dictionary ExtractCorridorProperties(CDB.Corridor corridor) + { + static void AddArrayToDict(string[] array, Dictionary dict, string key) + { + if (array.Length > 0) { - ["codes"] = shape.Codes.ToList(), - ["links"] = links + dict[key] = array; + } + } + + // get general props + Dictionary properties = new(); + + // get codes + Dictionary codesDict = new(); + AddArrayToDict(corridor.GetShapeCodes(), codesDict, SHAPES_PROP); + AddArrayToDict(corridor.GetLinkCodes(), codesDict, LINKS_PROP); + AddArrayToDict(corridor.GetPointCodes(), codesDict, POINTS_PROP); + AddDictionaryToDictionary(codesDict, properties, CODES_PROP); + + // get feature lines props + // this is pretty complicated: need to extract featureline points as dicts, but can only do this by iterating through baselines. Need to match the iterated featurelines with the featureline code info. + Dictionary> featureLinesDict = new(); + // first build dict from the code info + foreach (CDB.FeatureLineCodeInfo featureLineCode in corridor.FeatureLineCodeInfos) + { + featureLinesDict[featureLineCode.CodeName] = new Dictionary() + { + ["codeName"] = featureLineCode.CodeName, + ["isConnected"] = featureLineCode.IsConnected, + ["payItems"] = featureLineCode.PayItems }; } - subassemblyProperties["shapes"] = shapes; - - if (subassembly.HasSide) + // then iterate through baseline featurelines to populate point info + foreach (CDB.Baseline baseline in corridor.Baselines) { - subassemblyProperties["side"] = subassembly.Side; + // main featurelines + foreach ( + CDB.FeatureLineCollection mainFeaturelineCollection in baseline + .MainBaselineFeatureLines + .FeatureLineCollectionMap + ) + { + foreach (CDB.CorridorFeatureLine featureline in mainFeaturelineCollection) + { + ProcessCorridorFeaturelinePoints(featureline, featureLinesDict); + } + } + + // offset featurelines + foreach (CDB.BaselineFeatureLines offsetFeaturelineCollection in baseline.OffsetBaselineFeatureLinesCol) + { + foreach ( + CDB.FeatureLineCollection featurelineCollection in offsetFeaturelineCollection.FeatureLineCollectionMap + ) + { + foreach (CDB.CorridorFeatureLine featureline in featurelineCollection) + { + ProcessCorridorFeaturelinePoints(featureline, featureLinesDict); + } + } + } } - return subassemblyProperties; + if (featureLinesDict.Count > 0) + { + properties["Feature Lines"] = featureLinesDict; + } + + return properties; + } + + private Dictionary ExtractAlignmentProperties(CDB.Alignment alignment) + { + // get general props + Dictionary properties = + new() + { + ["startingStation"] = alignment.StartingStation, + ["endingStation"] = alignment.EndingStation, + ["alignmentType"] = alignment.AlignmentType.ToString() + }; + + // get assignments + Dictionary assignmentProps = new(); + if (!alignment.IsSiteless) + { + assignmentProps[SITEID_PROP] = alignment.SiteId.GetSpeckleApplicationId(); + assignmentProps[SITENAME_PROP] = alignment.SiteName; + } + if (alignment.GetProfileIds().Count > 0) + { + assignmentProps["profileId"] = GetSpeckleApplicationIdsFromCollection(alignment.GetProfileIds()); + } + AddDictionaryToDictionary(assignmentProps, properties, ASSIGNMENT_PROP); + + // get station control props + Dictionary stationControlDict = new(); + + Dictionary stationEquationsDict = new(); + int equationCount = 0; + foreach (var stationEquation in alignment.StationEquations) + { + stationEquationsDict[equationCount.ToString()] = new Dictionary() + { + ["rawStationBack"] = stationEquation.RawStationBack, + ["stationBack"] = stationEquation.StationBack, + ["stationAhead"] = stationEquation.StationAhead, + ["equationType"] = stationEquation.EquationType.ToString() + }; + equationCount++; + } + AddDictionaryToDictionary(stationEquationsDict, stationControlDict, "Station Equations"); + + stationControlDict["Reference Point"] = new Dictionary() + { + ["x"] = alignment.ReferencePoint.X, + ["y"] = alignment.ReferencePoint.Y, + ["station"] = alignment.ReferencePointStation + }; + + AddDictionaryToDictionary(stationControlDict, properties, "Station Control"); + + // get design speeds + Dictionary designSpeedsDict = new(); + int speedsCount = 0; + foreach (CDB.DesignSpeed designSpeed in alignment.DesignSpeeds) + { + designSpeedsDict[speedsCount.ToString()] = new Dictionary() + { + ["number"] = designSpeed.SpeedNumber, + ["station"] = designSpeed.Station, + ["value"] = designSpeed.Value + }; + speedsCount++; + } + AddDictionaryToDictionary(designSpeedsDict, properties, "Design Speeds"); + + // get offset alignment props + if (alignment.IsOffsetAlignment) + { + try + { + // accessing "OffsetAlignmentInfo" on offset alignments will sometimes throw /shrug. + // this happens when an offset alignment is unlinked from the parent and the CreateMode is still set to "ManuallyCreation" + // https://help.autodesk.com/view/CIV3D/2024/ENU/?guid=2ecbe421-4c08-cbde-d078-56a9f03b93f9 + var offsetInfo = alignment.OffsetAlignmentInfo; + properties["Offset Parameters"] = new Dictionary + { + ["side"] = offsetInfo.Side.ToString(), + ["parentAlignmentId"] = offsetInfo.ParentAlignmentId.GetSpeckleApplicationId(), + ["nominalOffset"] = offsetInfo.NominalOffset + }; + } + catch (InvalidOperationException) { } // do nothing + } + + return properties; } private Dictionary ExtractProfileProperties(CDB.Profile profile) @@ -146,138 +608,94 @@ public class ClassPropertiesExtractor }; } - private Dictionary ExtractAlignmentProperties(CDB.Alignment alignment) + private Dictionary ExtractSubassemblyProperties(CDB.Subassembly subassembly) { - Dictionary alignmentProperties = - new() + static void AddCodesToDict(CDB.CodeCollection codes, Dictionary dict) + { + if (codes.Count > 0) { - ["startingStation"] = alignment.StartingStation, - ["endingStation"] = alignment.EndingStation, - ["alignmentType"] = alignment.AlignmentType.ToString() - }; - - if (!alignment.IsSiteless) - { - alignmentProperties["siteId"] = alignment.SiteId.GetSpeckleApplicationId(); + dict[CODES_PROP] = codes.ToList(); + } } - return alignmentProperties; - } - - private Dictionary ExtractPipeProperties(CDB.Pipe pipe) - { - Dictionary pipeProperties = - new() - { - ["bearing"] = pipe.Bearing, - ["innerDiameterOrWidth"] = pipe.InnerDiameterOrWidth, - ["innerHeight"] = pipe.InnerHeight, - ["slope"] = pipe.Slope, - ["shape"] = pipe.CrossSectionalShape.ToString(), -#pragma warning disable CS0618 // Type or member is obsolete - ["length2d"] = pipe.Length2D, //Length2D was un-obsoleted in 2023, but is still marked obsolete in 2022 -#pragma warning restore CS0618 // Type or member is obsolete - ["minimumCover"] = pipe.MinimumCover, - ["maximumCover"] = pipe.MaximumCover, - ["junctionLoss"] = pipe.JunctionLoss, - ["flowDirection"] = pipe.FlowDirection.ToString(), - ["flowRate"] = pipe.FlowRate - }; - - if (pipe.StartStructureId != ADB.ObjectId.Null) + // get general props + Dictionary properties = new(); + if (subassembly.HasSide) { - pipeProperties["startStructureId"] = pipe.StartStructureId.GetSpeckleApplicationId(); + properties["side"] = subassembly.Side.ToString(); } - if (pipe.EndStructureId != ADB.ObjectId.Null) + // get assignments + Dictionary assignmentProps = new(); + if (subassembly.AssemblyId != ADB.ObjectId.Null) { - pipeProperties["endStructureId"] = pipe.EndStructureId.GetSpeckleApplicationId(); + assignmentProps["assemblyId"] = subassembly.AssemblyId.GetSpeckleApplicationId(); } + AddDictionaryToDictionary(assignmentProps, properties, ASSIGNMENT_PROP); - ExtractPartProperties(pipe, pipeProperties); - - return pipeProperties; - } - - private Dictionary ExtractStructureProperties(CDB.Structure structure) - { - var location = _pointConverter.Convert(structure.Location); - - Dictionary structureProperties = - new() - { - ["location"] = location, - ["northing"] = structure.Northing, - ["rotation"] = structure.Rotation, - ["sumpDepth"] = structure.SumpDepth, - ["sumpElevation"] = structure.SumpElevation, - ["innerDiameterOrWidth"] = structure.InnerDiameterOrWidth - }; - - if (structure.BoundingShape == CDB.BoundingShapeType.Box) + // get parameters + Dictionary parametersDict = new(); + foreach (ParamBool p in subassembly.ParamsBool) { - structureProperties["innerLength"] = structure.InnerLength; - structureProperties["length"] = structure.Length; + parametersDict[p.DisplayName] = p.Value; } - - ExtractPartProperties(structure, structureProperties); - - return structureProperties; - } - - private void ExtractPartProperties(CDB.Part part, Dictionary dict) - { - // process the part's pipe network with the pipe network handler - _pipeNetworkHandler.HandlePipeNetwork(part); - - dict["domain"] = part.Domain.ToString(); - dict["partType"] = part.PartType.ToString(); - if (part.RefSurfaceId != ADB.ObjectId.Null) + foreach (ParamDouble p in subassembly.ParamsDouble) { - dict["surfaceId"] = part.RefSurfaceId.GetSpeckleApplicationId(); + parametersDict[p.DisplayName] = p.Value; } - - return; - } - - private Dictionary ExtractSiteProperties(CDB.Site site) - { - Dictionary catchmentProperties = new(); - - if (site.GetAlignmentIds().Count > 0) + foreach (ParamString p in subassembly.ParamsString) { - catchmentProperties["alignmentIds"] = GetSpeckleApplicationIdsFromCollection(site.GetAlignmentIds()); + parametersDict[p.DisplayName] = p.Value; } - - if (site.GetFeatureLineIds().Count > 0) + foreach (ParamLong p in subassembly.ParamsLong) { - catchmentProperties["featureLineIds"] = GetSpeckleApplicationIdsFromCollection(site.GetFeatureLineIds()); + parametersDict[p.DisplayName] = p.Value; } + AddDictionaryToDictionary(parametersDict, properties, "Parameters"); - return catchmentProperties; - } - - private Dictionary ExtractCatchmentProperties(CDB.Catchment catchment) - { - // get the bounding curve of the catchment - SOG.Polyline boundary = _point3dCollectionConverter.Convert(catchment.BoundaryPolyline3d); - boundary.closed = true; - - // use the catchment group handler to process the catchment's group - _catchmentGroupHandler.HandleCatchmentGroup(catchment); - - return new() + // get location + properties["Location"] = new Dictionary() { - ["antecedentWetness"] = catchment.AntecedentWetness, - ["area"] = catchment.Area, - ["area2d"] = catchment.Area2d, - ["boundary"] = boundary, - ["hydrologicalSoilGroup"] = catchment.HydrologicalSoilGroup.ToString(), - ["imperviousArea"] = catchment.ImperviousArea, - ["manningsCoefficient"] = catchment.ManningsCoefficient, - ["perimeter2d"] = catchment.Perimeter2d, - ["timeOfConcentration"] = catchment.TimeOfConcentration + ["x"] = subassembly.Origin.X, + ["y"] = subassembly.Origin.Y, + ["z"] = subassembly.Origin.Z }; + + // get shapes > links > points info + Dictionary shapes = new(); + int shapeCount = 0; + foreach (CDB.Shape shape in subassembly.Shapes) + { + Dictionary shapeDict = new(); + AddCodesToDict(shape.Codes, shapeDict); + + Dictionary links = new(); + int linkCount = 0; + foreach (CDB.Link link in shape.Links) + { + Dictionary linkDict = new(); + AddCodesToDict(link.Codes, linkDict); + + Dictionary points = new(); + int pointCount = 0; + foreach (CDB.Point point in link.Points) + { + Dictionary pointDict = new() { ["elevation"] = point.Elevation, ["offset"] = point.Offset }; + AddCodesToDict(point.Codes, pointDict); + pointCount++; + } + + AddDictionaryToDictionary(points, linkDict, POINTS_PROP); + AddDictionaryToDictionary(linkDict, links, linkCount.ToString()); + linkCount++; + } + + AddDictionaryToDictionary(links, shapeDict, LINKS_PROP); + AddDictionaryToDictionary(shapeDict, shapes, shapeCount.ToString()); + } + AddDictionaryToDictionary(shapes, properties, SHAPES_PROP); + + return properties; } private List GetSpeckleApplicationIdsFromCollection(ADB.ObjectIdCollection collection) @@ -290,4 +708,45 @@ public class ClassPropertiesExtractor return speckleAppIds; } + + // A generic method to create a dictionary from an object types's properties + private Dictionary ExtractPropertiesGeneric(T obj) + { + Dictionary propertiesDict = new(); + + var type = typeof(T); + PropertyInfo[] properties = type.GetProperties(); + foreach (PropertyInfo? property in properties) + { + var value = property.GetValue(obj); + if (value is ADB.ObjectId id) + { + value = id.GetSpeckleApplicationId(); + } + + propertiesDict[property.Name] = value; + } + + return propertiesDict; + } + + private void AddDictionaryToDictionary( + Dictionary dictionary, + Dictionary parentDictionary, + string name + ) + { + if (dictionary.Count == 0) + { + return; + } + + if (parentDictionary.ContainsKey(name)) + { + // TODO: log this + return; + } + + parentDictionary[name] = dictionary; + } } diff --git a/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/Properties/GeneralPropertiesExtractor.cs b/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/Properties/GeneralPropertiesExtractor.cs deleted file mode 100644 index 3c2478d59..000000000 --- a/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/Properties/GeneralPropertiesExtractor.cs +++ /dev/null @@ -1,387 +0,0 @@ -using System.Reflection; -using Autodesk.Civil.Runtime; -using Speckle.Converters.Civil3dShared.Extensions; - -namespace Speckle.Converters.Civil3dShared.ToSpeckle; - -/// -/// Extracts general properties related to analysis, statistics, and calculations out from a civil entity. Expects to be scoped per operation. -/// -public class GeneralPropertiesExtractor -{ - public GeneralPropertiesExtractor() { } - - /// - /// Extracts general properties from a civil entity. Expects to be scoped per operation. - /// - /// - /// - public Dictionary? GetGeneralProperties(CDB.Entity entity) - { - switch (entity) - { - // catchment -> properties -> Catchment Properties - case CDB.Catchment catchment: - return ExtractCatchmentProperties(catchment); - - // surface -> properties -> statistics -> general, extended, and tin/grid properties - case CDB.Surface surface: - return ExtractSurfaceProperties(surface); - - // alignment -> properties -> station control -> station equations, station information, reference point - // alignment -> properties -> offset parameters -> parent alignment - case CDB.Alignment alignment: - return ExtractAlignmentProperties(alignment); - - // corridor -> properties -> codes, featurelines, surfaces - case CDB.Corridor corridor: - return ExtractCorridorProperties(corridor); - - // subassembly -> properties -> parameters, codes - case CDB.Subassembly subassembly: - return ExtractSubassemblyProperties(subassembly); - - default: - return null; - } - } - - private Dictionary ExtractCatchmentProperties(CDB.Catchment catchment) - { - Dictionary generalPropertiesDict = new(); - - // get catchment properties props - Dictionary catchmentPropertiesDict = new(); - - Dictionary hydrologicalProps = new() { ["runoffCoefficient"] = catchment.RunoffCoefficient }; - catchmentPropertiesDict["Hydrological Properties"] = hydrologicalProps; - -#if CIVIL3D2024_OR_GREATER - Dictionary sheetFlow = - new() - { - ["sheetFlowSegments"] = catchment.SheetFlowSegments, - ["sheetFlowTravelTime"] = catchment.SheetFlowTravelTime - }; - catchmentPropertiesDict["Sheet Flow"] = sheetFlow; - - Dictionary shallowConcentratedFlow = - new() - { - ["shallowFlowSegments"] = catchment.ShallowFlowSegments, - ["shallowFlowTravelTime"] = catchment.ShallowFlowTravelTime - }; - catchmentPropertiesDict["Shallow Concentrated Flow"] = shallowConcentratedFlow; - - Dictionary channelFlow = - new() - { - ["channelFlowSegments"] = catchment.ChannelFlowSegments, - ["channelFlowTravelTime"] = catchment.ChannelFlowTravelTime - }; - catchmentPropertiesDict["Channel Flow"] = channelFlow; -#endif - - Dictionary timeOfConcentration = - new() - { - ["timeOfConcentration"] = catchment.TimeOfConcentration, - ["timeOfConcentrationCalculationMethod"] = catchment.TimeOfConcentrationCalculationMethod, - ["hydrologicallyMostDistantPoint"] = catchment.HydrologicallyMostDistantPoint.ToArray(), - ["hydrologicallyMostDistantLength"] = catchment.HydrologicallyMostDistantLength - }; - catchmentPropertiesDict["Time of Concentration"] = timeOfConcentration; - - if (catchmentPropertiesDict.Count > 0) - { - generalPropertiesDict["Catchment Properties"] = catchmentPropertiesDict; - } - - return generalPropertiesDict; - } - - private Dictionary ExtractSubassemblyProperties(CDB.Subassembly subassembly) - { - Dictionary generalPropertiesDict = new(); - - // get parameters props - Dictionary parametersDict = new(); - foreach (ParamBool p in subassembly.ParamsBool) - { - parametersDict[p.DisplayName] = p.Value; - } - foreach (ParamDouble p in subassembly.ParamsDouble) - { - parametersDict[p.DisplayName] = p.Value; - } - foreach (ParamString p in subassembly.ParamsString) - { - parametersDict[p.DisplayName] = p.Value; - } - foreach (ParamLong p in subassembly.ParamsLong) - { - parametersDict[p.DisplayName] = p.Value; - } - if (parametersDict.Count > 0) - { - generalPropertiesDict["Parameters"] = parametersDict; - } - - return generalPropertiesDict; - } - - private void ProcessCorridorFeaturelinePoints( - CDB.CorridorFeatureLine featureline, - Dictionary> featureLinesDict - ) - { - if (featureLinesDict.TryGetValue(featureline.CodeName, out Dictionary? value)) - { - Dictionary pointsDict = new(featureline.FeatureLinePoints.Count); - int pointCount = 0; - foreach (CDB.FeatureLinePoint point in featureline.FeatureLinePoints) - { - pointsDict[pointCount.ToString()] = new Dictionary() - { - ["station"] = point.Station, - ["xyz"] = point.XYZ.ToArray(), - ["isBreak"] = point.IsBreak, - ["offset"] = point.Offset - }; - - pointCount++; - } - - value["featureLinePoints"] = pointsDict; - } - } - - private Dictionary ExtractCorridorProperties(CDB.Corridor corridor) - { - Dictionary generalPropertiesDict = new(); - - // get codes props - Dictionary codesDict = - new() - { - ["link"] = corridor.GetLinkCodes(), - ["point"] = corridor.GetPointCodes(), - ["shape"] = corridor.GetShapeCodes() - }; - generalPropertiesDict["codes"] = codesDict; - - // get feature lines props - // this is pretty complicated: need to extract featureline points as dicts, but can only do this by iterating through baselines. Need to match the iterated featurelines with the featureline code info. - Dictionary> featureLinesDict = new(); - // first build dict from the code info - foreach (CDB.FeatureLineCodeInfo featureLineCode in corridor.FeatureLineCodeInfos) - { - featureLinesDict[featureLineCode.CodeName] = new Dictionary() - { - ["codeName"] = featureLineCode.CodeName, - ["isConnected"] = featureLineCode.IsConnected, - ["payItems"] = featureLineCode.PayItems - }; - } - // then iterate through baseline featurelines to populate point info - foreach (CDB.Baseline baseline in corridor.Baselines) - { - // main featurelines - foreach ( - CDB.FeatureLineCollection mainFeaturelineCollection in baseline - .MainBaselineFeatureLines - .FeatureLineCollectionMap - ) - { - foreach (CDB.CorridorFeatureLine featureline in mainFeaturelineCollection) - { - ProcessCorridorFeaturelinePoints(featureline, featureLinesDict); - } - } - - // offset featurelines - foreach (CDB.BaselineFeatureLines offsetFeaturelineCollection in baseline.OffsetBaselineFeatureLinesCol) - { - foreach ( - CDB.FeatureLineCollection featurelineCollection in offsetFeaturelineCollection.FeatureLineCollectionMap - ) - { - foreach (CDB.CorridorFeatureLine featureline in featurelineCollection) - { - ProcessCorridorFeaturelinePoints(featureline, featureLinesDict); - } - } - } - } - if (featureLinesDict.Count > 0) - { - generalPropertiesDict["Feature Lines"] = featureLinesDict; - } - - // get surfaces props - Dictionary surfacesDict = new(); - foreach (CDB.CorridorSurface surface in corridor.CorridorSurfaces) - { - surfacesDict[surface.Name] = new Dictionary() - { - ["name"] = surface.Name, - ["surfaceId"] = surface.SurfaceId.GetSpeckleApplicationId(), - ["description"] = surface.Description, - ["overhangCorrection"] = surface.OverhangCorrection.ToString() - }; - } - if (surfacesDict.Count > 0) - { - generalPropertiesDict["Surfaces"] = surfacesDict; - } - - return generalPropertiesDict; - } - - private Dictionary ExtractAlignmentProperties(CDB.Alignment alignment) - { - Dictionary generalPropertiesDict = new(); - - // get station control props - Dictionary stationControlDict = new(); - - Dictionary stationEquationsDict = new(); - int equationCount = 0; - foreach (var stationEquation in alignment.StationEquations) - { - stationEquationsDict[equationCount.ToString()] = new Dictionary() - { - ["rawStationBack"] = stationEquation.RawStationBack, - ["stationBack"] = stationEquation.StationBack, - ["stationAhead"] = stationEquation.StationAhead, - ["equationType"] = stationEquation.EquationType.ToString() - }; - equationCount++; - } - if (stationEquationsDict.Count > 0) - { - stationControlDict["Station Equations"] = stationEquationsDict; - } - - Dictionary referencePointDict = - new() - { - ["x"] = alignment.ReferencePoint.X, - ["y"] = alignment.ReferencePoint.Y, - ["station"] = alignment.ReferencePointStation - }; - stationControlDict["Reference Point"] = referencePointDict; - - if (stationControlDict.Count > 0) - { - generalPropertiesDict["Station Control"] = stationControlDict; - } - - // get design criteria props - Dictionary designCriteriaDict = new(); - - Dictionary designSpeedsDict = new(); - int speedsCount = 0; - foreach (CDB.DesignSpeed designSpeed in alignment.DesignSpeeds) - { - designSpeedsDict[speedsCount.ToString()] = new Dictionary() - { - ["number"] = designSpeed.SpeedNumber, - ["station"] = designSpeed.Station, - ["value"] = designSpeed.Value - }; - speedsCount++; - } - - if (designSpeedsDict.Count > 0) - { - designCriteriaDict["Design Speeds"] = designSpeedsDict; - } - - if (designCriteriaDict.Count > 0) - { - generalPropertiesDict["Design Critera"] = designCriteriaDict; - } - - // get offset alignment props - if (alignment.IsOffsetAlignment) - { - var offsetInfo = alignment.OffsetAlignmentInfo; - Dictionary offsetAlignmentDict = - new() - { - ["side"] = offsetInfo.Side.ToString(), - ["parentAlignmentId"] = offsetInfo.ParentAlignmentId.GetSpeckleApplicationId(), - ["nominalOffset"] = offsetInfo.NominalOffset - }; - - generalPropertiesDict["Offset Parameters"] = offsetAlignmentDict; - } - - return generalPropertiesDict; - } - - private Dictionary ExtractSurfaceProperties(CDB.Surface surface) - { - Dictionary generalPropertiesDict = new(); - - // get statistics props - Dictionary statisticsDict = new(); - statisticsDict["General"] = ExtractPropertiesGeneric(surface.GetGeneralProperties()); - switch (surface) - { - case CDB.TinSurface tinSurface: - statisticsDict["Terrain"] = ExtractPropertiesGeneric( - tinSurface.GetTerrainProperties() - ); - statisticsDict["TIN"] = ExtractPropertiesGeneric(tinSurface.GetTinProperties()); - - break; - case CDB.TinVolumeSurface tinVolumeSurface: - statisticsDict["TIN"] = ExtractPropertiesGeneric(tinVolumeSurface.GetTinProperties()); - statisticsDict["Volume"] = ExtractPropertiesGeneric( - tinVolumeSurface.GetVolumeProperties() - ); - break; - case CDB.GridSurface gridSurface: - statisticsDict["Terrain"] = ExtractPropertiesGeneric( - gridSurface.GetTerrainProperties() - ); - statisticsDict["Grid"] = ExtractPropertiesGeneric(gridSurface.GetGridProperties()); - break; - case CDB.GridVolumeSurface gridVolumeSurface: - statisticsDict["Grid"] = ExtractPropertiesGeneric( - gridVolumeSurface.GetGridProperties() - ); - statisticsDict["Volume"] = ExtractPropertiesGeneric( - gridVolumeSurface.GetVolumeProperties() - ); - break; - } - - // set all general props - generalPropertiesDict["Statistics"] = statisticsDict; - return generalPropertiesDict; - } - - // A generic method to create a dictionary from an object types's properties - private Dictionary ExtractPropertiesGeneric(T obj) - { - Dictionary propertiesDict = new(); - - var type = typeof(T); - PropertyInfo[] properties = type.GetProperties(); - foreach (PropertyInfo? property in properties) - { - var value = property.GetValue(obj); - if (value is ADB.ObjectId id) - { - value = id.GetSpeckleApplicationId(); - } - - propertiesDict[property.Name] = value; - } - - return propertiesDict; - } -} diff --git a/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/Properties/PartDataExtractor.cs b/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/Properties/PartDataExtractor.cs index 338ca6650..a0f743e73 100644 --- a/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/Properties/PartDataExtractor.cs +++ b/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/Properties/PartDataExtractor.cs @@ -31,6 +31,12 @@ public class PartDataExtractor foreach (CDB.PartDataField field in partData.GetAllDataFields()) { var value = GetValue(field); + + if (value is null) + { + continue; // don't send null props + } + string fieldName = field.Context.ToString(); // we're using the context for the field name because it is more human-readable than the name prop var fieldDictionary = new Dictionary() diff --git a/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/Properties/PropertiesExtractor.cs b/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/Properties/PropertiesExtractor.cs new file mode 100644 index 000000000..82ad1fbad --- /dev/null +++ b/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/Properties/PropertiesExtractor.cs @@ -0,0 +1,54 @@ +namespace Speckle.Converters.Civil3dShared.ToSpeckle; + +/// +/// Extracts properties for the CivilObject class. +/// +public class PropertiesExtractor +{ + private readonly ClassPropertiesExtractor _classPropertiesExtractor; + private readonly PartDataExtractor _partDataExtractor; + private readonly PropertySetExtractor _propertySetExtractor; + private readonly ExtensionDictionaryExtractor _extensionDictionaryExtractor; + + public PropertiesExtractor( + ClassPropertiesExtractor classPropertiesExtractor, + PartDataExtractor partDataExtractor, + PropertySetExtractor propertySetExtractor, + ExtensionDictionaryExtractor extensionDictionaryExtractor + ) + { + _classPropertiesExtractor = classPropertiesExtractor; + _partDataExtractor = partDataExtractor; + _propertySetExtractor = propertySetExtractor; + _extensionDictionaryExtractor = extensionDictionaryExtractor; + } + + public Dictionary GetProperties(CDB.Entity entity) + { + // first get all class properties, which will be at the root level of props dictionary + Dictionary properties = _classPropertiesExtractor.GetClassProperties(entity); + + // add part data, property sets, and extension dictionaries to the properties dict + AddDictionaryToPropertyDictionary(_partDataExtractor.GetPartData(entity), "Part Data", properties); + AddDictionaryToPropertyDictionary(_propertySetExtractor.GetPropertySets(entity), "Property Sets", properties); + AddDictionaryToPropertyDictionary( + _extensionDictionaryExtractor.GetExtensionDictionary(entity), + "Extension Dictionary", + properties + ); + + return properties; + } + + private void AddDictionaryToPropertyDictionary( + Dictionary? entryDictionary, + string entryName, + Dictionary propertyDictionary + ) + { + if (entryDictionary is not null && entryDictionary.Count > 0) + { + propertyDictionary.Add(entryName, entryDictionary); + } + } +} diff --git a/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/Raw/AlignmentSubentitySpiralToSpeckleRawConverter.cs b/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/Raw/AlignmentSubentitySpiralToSpeckleRawConverter.cs index 939548081..82ebbfb11 100644 --- a/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/Raw/AlignmentSubentitySpiralToSpeckleRawConverter.cs +++ b/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/Raw/AlignmentSubentitySpiralToSpeckleRawConverter.cs @@ -37,11 +37,10 @@ public class AlignmentSubentitySpiralToSpeckleRawConverter { double x = 0; double y = 0; - double z = 0; - alignment.PointLocation(spiral.StartStation + i * spiralSegmentLength, 0, 0.001, ref x, ref y, ref z); + alignment.PointLocation(spiral.StartStation + i * spiralSegmentLength, 0, ref x, ref y); polylineValue.Add(x); polylineValue.Add(y); - polylineValue.Add(z); + polylineValue.Add(0); } polylineValue.Add(spiral.EndPoint.X); polylineValue.Add(spiral.EndPoint.Y); diff --git a/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/Raw/Point3dCollectionToSpeckleRawConverter.cs b/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/Raw/Point3dCollectionToSpeckleRawConverter.cs index b2c29160b..53ab432db 100644 --- a/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/Raw/Point3dCollectionToSpeckleRawConverter.cs +++ b/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/Raw/Point3dCollectionToSpeckleRawConverter.cs @@ -5,15 +5,10 @@ namespace Speckle.Converters.Civil3dShared.ToSpeckle.Raw; public class Point3dCollectionToSpeckleRawConverter : ITypedConverter { - private readonly ITypedConverter _pointConverter; private readonly IConverterSettingsStore _settingsStore; - public Point3dCollectionToSpeckleRawConverter( - ITypedConverter pointConverter, - IConverterSettingsStore settingsStore - ) + public Point3dCollectionToSpeckleRawConverter(IConverterSettingsStore settingsStore) { - _pointConverter = pointConverter; _settingsStore = settingsStore; } diff --git a/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/BuiltElements/CivilEntityToSpeckleTopLevelConverter.cs b/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/TopLevel/CivilEntityToSpeckleTopLevelConverter.cs similarity index 77% rename from Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/BuiltElements/CivilEntityToSpeckleTopLevelConverter.cs rename to Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/TopLevel/CivilEntityToSpeckleTopLevelConverter.cs index 1960ea1b3..14eac34a2 100644 --- a/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/BuiltElements/CivilEntityToSpeckleTopLevelConverter.cs +++ b/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/TopLevel/CivilEntityToSpeckleTopLevelConverter.cs @@ -3,18 +3,20 @@ using Speckle.Converters.Civil3dShared.Helpers; using Speckle.Converters.Common; using Speckle.Converters.Common.Objects; using Speckle.Objects; +using Speckle.Objects.Data; using Speckle.Sdk; using Speckle.Sdk.Models; -namespace Speckle.Converters.Civil3dShared.ToSpeckle.BuiltElements; +namespace Speckle.Converters.Civil3dShared.ToSpeckle.TopLevel; -[NameAndRankValue(nameof(CDB.Entity), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +[NameAndRankValue(typeof(CDB.Entity), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] public class CivilEntityToSpeckleTopLevelConverter : IToSpeckleTopLevelConverter { private readonly IConverterSettingsStore _settingsStore; private readonly DisplayValueExtractor _displayValueExtractor; private readonly BaseCurveExtractor _baseCurveExtractor; private readonly ClassPropertiesExtractor _classPropertiesExtractor; + private readonly PropertiesExtractor _propertiesExtractor; private readonly CorridorHandler _corridorHandler; public CivilEntityToSpeckleTopLevelConverter( @@ -22,6 +24,7 @@ public class CivilEntityToSpeckleTopLevelConverter : IToSpeckleTopLevelConverter DisplayValueExtractor displayValueExtractor, BaseCurveExtractor baseCurveExtractor, ClassPropertiesExtractor classPropertiesExtractor, + PropertiesExtractor propertiesExtractor, CorridorHandler corridorHandler ) { @@ -29,12 +32,13 @@ public class CivilEntityToSpeckleTopLevelConverter : IToSpeckleTopLevelConverter _displayValueExtractor = displayValueExtractor; _baseCurveExtractor = baseCurveExtractor; _classPropertiesExtractor = classPropertiesExtractor; + _propertiesExtractor = propertiesExtractor; _corridorHandler = corridorHandler; } public Base Convert(object target) => Convert((CDB.Entity)target); - public Base Convert(CDB.Entity target) + public Civil3dObject Convert(CDB.Entity target) { string name = target.DisplayName; try @@ -43,21 +47,8 @@ public class CivilEntityToSpeckleTopLevelConverter : IToSpeckleTopLevelConverter } catch (Exception e) when (!e.IsFatal()) { } - Base civilObject = - new() - { - ["type"] = target.GetType().ToString().Split('.').Last(), - ["name"] = name, - ["units"] = _settingsStore.Current.SpeckleUnits, - applicationId = target.GetSpeckleApplicationId() - }; - // get basecurve List? baseCurves = _baseCurveExtractor.GetBaseCurves(target); - if (baseCurves is not null) - { - civilObject["baseCurves"] = baseCurves; - } // extract display value. // If object has no display but has basecurves, use basecurves for display instead (for viewer selection) @@ -66,28 +57,26 @@ public class CivilEntityToSpeckleTopLevelConverter : IToSpeckleTopLevelConverter { displayValue = _displayValueExtractor.ProcessICurvesForDisplay(baseCurves).ToList(); } - if (displayValue.Count > 0) - { - civilObject["displayValue"] = displayValue; - } - // add any additional class properties - Dictionary? classProperties = _classPropertiesExtractor.GetClassProperties(target); - if (classProperties is not null) - { - foreach (string key in classProperties.Keys) - { - civilObject[$"{key}"] = classProperties[key]; - } - } + // get properties + Dictionary properties = _propertiesExtractor.GetProperties(target); // determine if this entity has any children elements that need to be converted. // this is a bespoke method by class type. var children = GetEntityChildren(target).ToList(); - if (children.Count > 0) - { - civilObject["elements"] = children; - } + + Civil3dObject civilObject = + new() + { + name = name, + type = target.GetType().ToString().Split('.').Last(), + baseCurves = baseCurves, + elements = children, + displayValue = displayValue, + properties = properties, + units = _settingsStore.Current.SpeckleUnits, + applicationId = target.GetSpeckleApplicationId() + }; return civilObject; } @@ -103,13 +92,6 @@ public class CivilEntityToSpeckleTopLevelConverter : IToSpeckleTopLevelConverter yield return child; } break; - case CDB.Corridor corridor: - var corridorChildren = _corridorHandler.GetCorridorChildren(corridor); - foreach (var child in corridorChildren) - { - yield return child; - } - break; case CDB.Site site: var siteChildren = GetSiteChildren(site).ToList(); @@ -118,10 +100,18 @@ public class CivilEntityToSpeckleTopLevelConverter : IToSpeckleTopLevelConverter yield return child; } break; + + case CDB.Corridor corridor: + var corridorChildren = _corridorHandler.GetCorridorChildren(corridor); + foreach (var child in corridorChildren) + { + yield return child; + } + break; } } - private IEnumerable GetSiteChildren(CDB.Site site) + private IEnumerable GetSiteChildren(CDB.Site site) { using (var tr = _settingsStore.Current.Document.Database.TransactionManager.StartTransaction()) { @@ -135,7 +125,7 @@ public class CivilEntityToSpeckleTopLevelConverter : IToSpeckleTopLevelConverter } } - private IEnumerable GetAlignmentChildren(CDB.Alignment alignment) + private IEnumerable GetAlignmentChildren(CDB.Alignment alignment) { using (var tr = _settingsStore.Current.Document.Database.TransactionManager.StartTransaction()) { diff --git a/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/TopLevel/CogoPointToSpeckleTopLevelConverter.cs b/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/TopLevel/CogoPointToSpeckleTopLevelConverter.cs new file mode 100644 index 000000000..223e951dd --- /dev/null +++ b/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/TopLevel/CogoPointToSpeckleTopLevelConverter.cs @@ -0,0 +1,62 @@ +using Speckle.Converters.Civil3dShared.Extensions; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Objects.Data; +using Speckle.Sdk; +using Speckle.Sdk.Models; + +namespace Speckle.Converters.Civil3dShared.ToSpeckle.TopLevel; + +/// +/// This top level converter is needed because the namespace of CogoPoint is Autodesk.Civil.DatabaseServices, but the inheritance of CogoPoint is Autodesk.Autocad.Entity +/// This means cogo points will *not* be picked up by the top level civil entity converter. +/// POC: implementing a top level autocad entity converter can probably replace this converter. +/// +[NameAndRankValue(typeof(CDB.CogoPoint), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class CogoPointToSpeckleTopLevelConverter : IToSpeckleTopLevelConverter +{ + private readonly IConverterSettingsStore _settingsStore; + private readonly ITypedConverter _pointConverter; + + public CogoPointToSpeckleTopLevelConverter( + IConverterSettingsStore settingsStore, + ITypedConverter pointConverter + ) + { + _settingsStore = settingsStore; + _pointConverter = pointConverter; + } + + public Base Convert(object target) => Convert((CDB.CogoPoint)target); + + public Civil3dObject Convert(CDB.CogoPoint target) + { + string name = ""; + try + { + name = target.PointName; + } + catch (Autodesk.Civil.CivilException e) when (!e.IsFatal()) { } // throws if name doesn't exist + + // extract display value as point + SOG.Point displayPoint = _pointConverter.Convert(target.Location); + + // get additional class properties + Dictionary props = new() { ["number"] = target.PointNumber, ["northing"] = target.Northing }; + + Civil3dObject civilObject = + new() + { + name = name, + type = target.GetType().ToString().Split('.').Last(), + baseCurves = null, + elements = new(), + displayValue = new() { displayPoint }, + properties = props, + units = _settingsStore.Current.SpeckleUnits, + applicationId = target.Id.GetSpeckleApplicationId() + }; + + return civilObject; + } +} diff --git a/Converters/Navisworks/Speckle.Converters.Navisworks2020/Speckle.Converters.Navisworks2020.csproj b/Converters/Navisworks/Speckle.Converters.Navisworks2020/Speckle.Converters.Navisworks2020.csproj new file mode 100644 index 000000000..e00724486 --- /dev/null +++ b/Converters/Navisworks/Speckle.Converters.Navisworks2020/Speckle.Converters.Navisworks2020.csproj @@ -0,0 +1,20 @@ + + + net48 + x64 + $(DefineConstants);NAVIS2020 + Debug;Release;Local + + + + + + + + + + + + + + diff --git a/Converters/Navisworks/Speckle.Converters.Navisworks2020/packages.lock.json b/Converters/Navisworks/Speckle.Converters.Navisworks2020/packages.lock.json new file mode 100644 index 000000000..e986bfc6b --- /dev/null +++ b/Converters/Navisworks/Speckle.Converters.Navisworks2020/packages.lock.json @@ -0,0 +1,353 @@ +{ + "version": 2, + "dependencies": { + ".NETFramework,Version=v4.8": { + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net48": "1.0.3" + } + }, + "Microsoft.SourceLink.GitHub": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "dependencies": { + "Microsoft.Build.Tasks.Git": "8.0.0", + "Microsoft.SourceLink.Common": "8.0.0" + } + }, + "PolySharp": { + "type": "Direct", + "requested": "[1.14.1, )", + "resolved": "1.14.1", + "contentHash": "mOOmFYwad3MIOL14VCjj02LljyF1GNw1wP0YVlxtcPvqdxjGGMNdNJJxHptlry3MOd8b40Flm8RPOM8JOlN2sQ==" + }, + "Speckle.InterfaceGenerator": { + "type": "Direct", + "requested": "[0.9.6, )", + "resolved": "0.9.6", + "contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w==" + }, + "Speckle.Navisworks.API": { + "type": "Direct", + "requested": "[2020.0.0, )", + "resolved": "2020.0.0", + "contentHash": "biB2RR0HNxrbHD7zBZoJUhwzPwVE5IFg9l4/747bHOLRJC3FM5UtzdjGwvRZwfOlFyM4P26NYARSiCaxSNIBpg==" + }, + "GraphQL.Client": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "8yPNBbuVBpTptivyAlak4GZvbwbUcjeQTL4vN1HKHRuOykZ4r7l5fcLS6vpyPyLn0x8FsL31xbOIKyxbmR9rbA==", + "dependencies": { + "GraphQL.Client.Abstractions": "6.0.0", + "GraphQL.Client.Abstractions.Websocket": "6.0.0", + "System.Net.WebSockets.Client.Managed": "1.0.22", + "System.Reactive": "5.0.0" + } + }, + "GraphQL.Client.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "h7uzWFORHZ+CCjwr/ThAyXMr0DPpzEANDa4Uo54wqCQ+j7qUKwqYTgOrb1W40sqbvNaZm9v/X7It31SUw0maHA==", + "dependencies": { + "GraphQL.Primitives": "6.0.0" + } + }, + "GraphQL.Client.Abstractions.Websocket": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "Nr9bPf8gIOvLuXpqEpqr9z9jslYFJOvd0feHth3/kPqeR3uMbjF5pjiwh4jxyMcxHdr8Pb6QiXkV3hsSyt0v7A==", + "dependencies": { + "GraphQL.Client.Abstractions": "6.0.0" + } + }, + "GraphQL.Primitives": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "yg72rrYDapfsIUrul7aF6wwNnTJBOFvuA9VdDTQpPa8AlAriHbufeXYLBcodKjfUdkCnaiggX1U/nEP08Zb5GA==" + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "W8DPQjkMScOMTtJbPwmPyj9c3zYSFGawDW3jwlBOOsnY+EzZFLgNQ/UMkK35JmkNOVPdCyPr2Tw7Vv9N+KA3ZQ==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.CSharp": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==" + }, + "Microsoft.Data.Sqlite": { + "type": "Transitive", + "resolved": "7.0.5", + "contentHash": "KGxbPeWsQMnmQy43DSBxAFtHz3l2JX8EWBSGUCvT3CuZ8KsuzbkqMIJMDOxWtG8eZSoCDI04aiVQjWuuV8HmSw==", + "dependencies": { + "Microsoft.Data.Sqlite.Core": "7.0.5", + "SQLitePCLRaw.bundle_e_sqlite3": "2.1.4" + } + }, + "Microsoft.Data.Sqlite.Core": { + "type": "Transitive", + "resolved": "7.0.5", + "contentHash": "FTerRmQPqHrCrnoUzhBu+E+1DNGwyrAMLqHkAqOOOu5pGfyMOj8qQUBxI/gDtWtG11p49UxSfWmBzRNlwZqfUg==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "nOP8R1mVb/6mZtm2qgAJXn/LFm/2kMjHDAg/QJLFG6CuWYJtaD3p1BwQhufBVvRzL9ceJ/xF0SQ0qsI2GkDQAA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "65MrmXCziWaQFrI0UHkQbesrX5wTwf9XPjY5yFm/VkgJKFJ5gqvXRoXjIZcf2wLi5ZlwGz/oMYfyURVCWbM5iw==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "vJ9xvOZCnUAIHcGC3SU35r3HKmHTVIeHzo6u/qzlHAqD8m6xv92MLin4oJntTvkpKxVX3vI1GFFkIQtU3AdlsQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "2.2.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw==" + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "UpZLNLBpIZ0GTebShui7xXYh6DmBHjWM8NxGxZbdQh/bPZ5e6YswqI+bru6BnEL5eWiOdodsXtEz3FROcgi/qg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Primitives": "2.2.0", + "System.ComponentModel.Annotations": "4.5.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "azyQtqbm4fSaDzZHD/J+V6oWMFaf2tWP4WEGIYePLCMw3+b2RQdj9ybgbQyjCshcitQKQ4lEDOZjmSlTTrHxUg==", + "dependencies": { + "System.Memory": "4.5.1", + "System.Runtime.CompilerServices.Unsafe": "4.5.1" + } + }, + "Microsoft.NETFramework.ReferenceAssemblies.net48": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "zMk4D+9zyiEWByyQ7oPImPN/Jhpj166Ky0Nlla4eXlNL8hI/BtSJsgR8Inldd4NNpIAH3oh8yym0W2DrhXdSLQ==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "Speckle.Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.2", + "contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA==" + }, + "SQLitePCLRaw.bundle_e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "EWI1olKDjFEBMJu0+3wuxwziIAdWDVMYLhuZ3Qs84rrz+DHwD00RzWPZCa+bLnHCf3oJwuFZIRsHT5p236QXww==", + "dependencies": { + "SQLitePCLRaw.lib.e_sqlite3": "2.1.4", + "SQLitePCLRaw.provider.dynamic_cdecl": "2.1.4" + } + }, + "SQLitePCLRaw.core": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "inBjvSHo9UDKneGNzfUfDjK08JzlcIhn1+SP5Y3m6cgXpCxXKCJDy6Mka7LpgSV+UZmKSnC8rTwB0SQ0xKu5pA==", + "dependencies": { + "System.Memory": "4.5.3" + } + }, + "SQLitePCLRaw.lib.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "2C9Q9eX7CPLveJA0rIhf9RXAvu+7nWZu1A2MdG6SD/NOu26TakGgL1nsbc0JAspGijFOo3HoN79xrx8a368fBg==" + }, + "SQLitePCLRaw.provider.dynamic_cdecl": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "ZsaKKhgYF9B1fvcnOGKl3EycNAwd9CRWX7v0rEfuPWhQQ5Jjpvf2VEHahiLIGHio3hxi3EIKFJw9KvyowWOUAw==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "System.Buffers": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "AwarXzzoDwX6BgrhjoJsk6tUezZEozOT5Y9QKF94Gl4JK91I4PIIBkBco9068Y9/Dra8Dkbie99kXB8+1BaYKw==" + }, + "System.ComponentModel.Annotations": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "UxYQ3FGUOtzJ7LfSdnYSFd7+oEv6M8NgUatatIN2HxNtDdlcvFAf+VIq4Of9cDMJEJC0aSRv/x898RYhB4Yppg==" + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.3", + "contentHash": "3oDzvc/zzetpTKWMShs1AADwZjQ/36HnsufHRPcOjyRAAMLDlu2iD33MBI2opxnezcVUtXyqDXXjoFMOU9c7SA==", + "dependencies": { + "System.Buffers": "4.4.0", + "System.Numerics.Vectors": "4.4.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.2" + } + }, + "System.Net.WebSockets.Client.Managed": { + "type": "Transitive", + "resolved": "1.0.22", + "contentHash": "WqEOxPlXjuZrIjUtXNE9NxEfU/n5E35iV2PtoZdJSUC4tlrqwHnTee+wvMIM4OUaJWmwrymeqcgYrE0IkGAgLA==", + "dependencies": { + "System.Buffers": "4.4.0", + "System.Numerics.Vectors": "4.4.0" + } + }, + "System.Numerics.Vectors": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ==" + }, + "System.Reactive": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "4.5.3", + "contentHash": "3TIsJhD1EiiT0w2CcDMN/iSSwnNnsrnbzeVHSKkaEgV85txMprmuO+Yq2AdSbeVGcg28pdNDTPK87tJhX7VFHw==" + }, + "System.Threading.Tasks.Extensions": { + "type": "Transitive", + "resolved": "4.5.4", + "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + }, + "speckle.connectors.common": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", + "Speckle.Connectors.Logging": "[1.0.0, )", + "Speckle.Objects": "[3.1.1, )", + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" + } + }, + "speckle.connectors.dui": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", + "Speckle.Connectors.Common": "[1.0.0, )", + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" + } + }, + "speckle.connectors.logging": { + "type": "Project" + }, + "speckle.converters.common": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", + "Speckle.Objects": "[3.1.1, )" + } + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "MZtBIwfDFork5vfjpJdG5g8wuJFt7d/y3LOSVVtDK/76wlbtz6cjltfKHqLx2TKVqTj5/c41t77m1+h20zqtPA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0" + } + }, + "Microsoft.Extensions.Logging": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "Nxqhadc9FCmFHzU+fz3oc8sFlE6IadViYg8dfUdGzJZ2JUxnCsRghBhhOWdM4B2zSZqEc+0BjliBh/oNdRZuig==", + "dependencies": { + "Microsoft.Extensions.Configuration.Binder": "2.2.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging.Abstractions": "2.2.0", + "Microsoft.Extensions.Options": "2.2.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" + }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, + "Speckle.Objects": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", + "dependencies": { + "Speckle.Sdk": "3.1.1" + } + }, + "Speckle.Sdk": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", + "dependencies": { + "GraphQL.Client": "6.0.0", + "Microsoft.Bcl.AsyncInterfaces": "5.0.0", + "Microsoft.CSharp": "4.7.0", + "Microsoft.Data.Sqlite": "7.0.5", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging": "2.2.0", + "Speckle.DoubleNumerics": "4.1.0", + "Speckle.Newtonsoft.Json": "13.0.2", + "Speckle.Sdk.Dependencies": "3.1.1" + } + }, + "Speckle.Sdk.Dependencies": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" + } + } + } +} \ No newline at end of file diff --git a/Converters/Navisworks/Speckle.Converters.Navisworks2021/Speckle.Converters.Navisworks2021.csproj b/Converters/Navisworks/Speckle.Converters.Navisworks2021/Speckle.Converters.Navisworks2021.csproj new file mode 100644 index 000000000..79e45bc9a --- /dev/null +++ b/Converters/Navisworks/Speckle.Converters.Navisworks2021/Speckle.Converters.Navisworks2021.csproj @@ -0,0 +1,20 @@ + + + net48 + x64 + $(DefineConstants);NAVIS2021 + Debug;Release;Local + + + + + + + + + + + + + + diff --git a/Converters/Navisworks/Speckle.Converters.Navisworks2021/packages.lock.json b/Converters/Navisworks/Speckle.Converters.Navisworks2021/packages.lock.json new file mode 100644 index 000000000..77b93c139 --- /dev/null +++ b/Converters/Navisworks/Speckle.Converters.Navisworks2021/packages.lock.json @@ -0,0 +1,353 @@ +{ + "version": 2, + "dependencies": { + ".NETFramework,Version=v4.8": { + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net48": "1.0.3" + } + }, + "Microsoft.SourceLink.GitHub": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "dependencies": { + "Microsoft.Build.Tasks.Git": "8.0.0", + "Microsoft.SourceLink.Common": "8.0.0" + } + }, + "PolySharp": { + "type": "Direct", + "requested": "[1.14.1, )", + "resolved": "1.14.1", + "contentHash": "mOOmFYwad3MIOL14VCjj02LljyF1GNw1wP0YVlxtcPvqdxjGGMNdNJJxHptlry3MOd8b40Flm8RPOM8JOlN2sQ==" + }, + "Speckle.InterfaceGenerator": { + "type": "Direct", + "requested": "[0.9.6, )", + "resolved": "0.9.6", + "contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w==" + }, + "Speckle.Navisworks.API": { + "type": "Direct", + "requested": "[2021.0.0, )", + "resolved": "2021.0.0", + "contentHash": "cY7sU8dxISfTQLinUIOki/azS+bIX28uEZQO4ijrq0eOUhJlKcKWS273kHPoL0+T0Xrkd+1OWj2YFa2PbHGgwQ==" + }, + "GraphQL.Client": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "8yPNBbuVBpTptivyAlak4GZvbwbUcjeQTL4vN1HKHRuOykZ4r7l5fcLS6vpyPyLn0x8FsL31xbOIKyxbmR9rbA==", + "dependencies": { + "GraphQL.Client.Abstractions": "6.0.0", + "GraphQL.Client.Abstractions.Websocket": "6.0.0", + "System.Net.WebSockets.Client.Managed": "1.0.22", + "System.Reactive": "5.0.0" + } + }, + "GraphQL.Client.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "h7uzWFORHZ+CCjwr/ThAyXMr0DPpzEANDa4Uo54wqCQ+j7qUKwqYTgOrb1W40sqbvNaZm9v/X7It31SUw0maHA==", + "dependencies": { + "GraphQL.Primitives": "6.0.0" + } + }, + "GraphQL.Client.Abstractions.Websocket": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "Nr9bPf8gIOvLuXpqEpqr9z9jslYFJOvd0feHth3/kPqeR3uMbjF5pjiwh4jxyMcxHdr8Pb6QiXkV3hsSyt0v7A==", + "dependencies": { + "GraphQL.Client.Abstractions": "6.0.0" + } + }, + "GraphQL.Primitives": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "yg72rrYDapfsIUrul7aF6wwNnTJBOFvuA9VdDTQpPa8AlAriHbufeXYLBcodKjfUdkCnaiggX1U/nEP08Zb5GA==" + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "W8DPQjkMScOMTtJbPwmPyj9c3zYSFGawDW3jwlBOOsnY+EzZFLgNQ/UMkK35JmkNOVPdCyPr2Tw7Vv9N+KA3ZQ==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.CSharp": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==" + }, + "Microsoft.Data.Sqlite": { + "type": "Transitive", + "resolved": "7.0.5", + "contentHash": "KGxbPeWsQMnmQy43DSBxAFtHz3l2JX8EWBSGUCvT3CuZ8KsuzbkqMIJMDOxWtG8eZSoCDI04aiVQjWuuV8HmSw==", + "dependencies": { + "Microsoft.Data.Sqlite.Core": "7.0.5", + "SQLitePCLRaw.bundle_e_sqlite3": "2.1.4" + } + }, + "Microsoft.Data.Sqlite.Core": { + "type": "Transitive", + "resolved": "7.0.5", + "contentHash": "FTerRmQPqHrCrnoUzhBu+E+1DNGwyrAMLqHkAqOOOu5pGfyMOj8qQUBxI/gDtWtG11p49UxSfWmBzRNlwZqfUg==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "nOP8R1mVb/6mZtm2qgAJXn/LFm/2kMjHDAg/QJLFG6CuWYJtaD3p1BwQhufBVvRzL9ceJ/xF0SQ0qsI2GkDQAA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "65MrmXCziWaQFrI0UHkQbesrX5wTwf9XPjY5yFm/VkgJKFJ5gqvXRoXjIZcf2wLi5ZlwGz/oMYfyURVCWbM5iw==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "vJ9xvOZCnUAIHcGC3SU35r3HKmHTVIeHzo6u/qzlHAqD8m6xv92MLin4oJntTvkpKxVX3vI1GFFkIQtU3AdlsQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "2.2.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw==" + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "UpZLNLBpIZ0GTebShui7xXYh6DmBHjWM8NxGxZbdQh/bPZ5e6YswqI+bru6BnEL5eWiOdodsXtEz3FROcgi/qg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Primitives": "2.2.0", + "System.ComponentModel.Annotations": "4.5.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "azyQtqbm4fSaDzZHD/J+V6oWMFaf2tWP4WEGIYePLCMw3+b2RQdj9ybgbQyjCshcitQKQ4lEDOZjmSlTTrHxUg==", + "dependencies": { + "System.Memory": "4.5.1", + "System.Runtime.CompilerServices.Unsafe": "4.5.1" + } + }, + "Microsoft.NETFramework.ReferenceAssemblies.net48": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "zMk4D+9zyiEWByyQ7oPImPN/Jhpj166Ky0Nlla4eXlNL8hI/BtSJsgR8Inldd4NNpIAH3oh8yym0W2DrhXdSLQ==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "Speckle.Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.2", + "contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA==" + }, + "SQLitePCLRaw.bundle_e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "EWI1olKDjFEBMJu0+3wuxwziIAdWDVMYLhuZ3Qs84rrz+DHwD00RzWPZCa+bLnHCf3oJwuFZIRsHT5p236QXww==", + "dependencies": { + "SQLitePCLRaw.lib.e_sqlite3": "2.1.4", + "SQLitePCLRaw.provider.dynamic_cdecl": "2.1.4" + } + }, + "SQLitePCLRaw.core": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "inBjvSHo9UDKneGNzfUfDjK08JzlcIhn1+SP5Y3m6cgXpCxXKCJDy6Mka7LpgSV+UZmKSnC8rTwB0SQ0xKu5pA==", + "dependencies": { + "System.Memory": "4.5.3" + } + }, + "SQLitePCLRaw.lib.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "2C9Q9eX7CPLveJA0rIhf9RXAvu+7nWZu1A2MdG6SD/NOu26TakGgL1nsbc0JAspGijFOo3HoN79xrx8a368fBg==" + }, + "SQLitePCLRaw.provider.dynamic_cdecl": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "ZsaKKhgYF9B1fvcnOGKl3EycNAwd9CRWX7v0rEfuPWhQQ5Jjpvf2VEHahiLIGHio3hxi3EIKFJw9KvyowWOUAw==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "System.Buffers": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "AwarXzzoDwX6BgrhjoJsk6tUezZEozOT5Y9QKF94Gl4JK91I4PIIBkBco9068Y9/Dra8Dkbie99kXB8+1BaYKw==" + }, + "System.ComponentModel.Annotations": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "UxYQ3FGUOtzJ7LfSdnYSFd7+oEv6M8NgUatatIN2HxNtDdlcvFAf+VIq4Of9cDMJEJC0aSRv/x898RYhB4Yppg==" + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.3", + "contentHash": "3oDzvc/zzetpTKWMShs1AADwZjQ/36HnsufHRPcOjyRAAMLDlu2iD33MBI2opxnezcVUtXyqDXXjoFMOU9c7SA==", + "dependencies": { + "System.Buffers": "4.4.0", + "System.Numerics.Vectors": "4.4.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.2" + } + }, + "System.Net.WebSockets.Client.Managed": { + "type": "Transitive", + "resolved": "1.0.22", + "contentHash": "WqEOxPlXjuZrIjUtXNE9NxEfU/n5E35iV2PtoZdJSUC4tlrqwHnTee+wvMIM4OUaJWmwrymeqcgYrE0IkGAgLA==", + "dependencies": { + "System.Buffers": "4.4.0", + "System.Numerics.Vectors": "4.4.0" + } + }, + "System.Numerics.Vectors": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ==" + }, + "System.Reactive": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "4.5.3", + "contentHash": "3TIsJhD1EiiT0w2CcDMN/iSSwnNnsrnbzeVHSKkaEgV85txMprmuO+Yq2AdSbeVGcg28pdNDTPK87tJhX7VFHw==" + }, + "System.Threading.Tasks.Extensions": { + "type": "Transitive", + "resolved": "4.5.4", + "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + }, + "speckle.connectors.common": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", + "Speckle.Connectors.Logging": "[1.0.0, )", + "Speckle.Objects": "[3.1.1, )", + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" + } + }, + "speckle.connectors.dui": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", + "Speckle.Connectors.Common": "[1.0.0, )", + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" + } + }, + "speckle.connectors.logging": { + "type": "Project" + }, + "speckle.converters.common": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", + "Speckle.Objects": "[3.1.1, )" + } + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "MZtBIwfDFork5vfjpJdG5g8wuJFt7d/y3LOSVVtDK/76wlbtz6cjltfKHqLx2TKVqTj5/c41t77m1+h20zqtPA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0" + } + }, + "Microsoft.Extensions.Logging": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "Nxqhadc9FCmFHzU+fz3oc8sFlE6IadViYg8dfUdGzJZ2JUxnCsRghBhhOWdM4B2zSZqEc+0BjliBh/oNdRZuig==", + "dependencies": { + "Microsoft.Extensions.Configuration.Binder": "2.2.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging.Abstractions": "2.2.0", + "Microsoft.Extensions.Options": "2.2.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" + }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, + "Speckle.Objects": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", + "dependencies": { + "Speckle.Sdk": "3.1.1" + } + }, + "Speckle.Sdk": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", + "dependencies": { + "GraphQL.Client": "6.0.0", + "Microsoft.Bcl.AsyncInterfaces": "5.0.0", + "Microsoft.CSharp": "4.7.0", + "Microsoft.Data.Sqlite": "7.0.5", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging": "2.2.0", + "Speckle.DoubleNumerics": "4.1.0", + "Speckle.Newtonsoft.Json": "13.0.2", + "Speckle.Sdk.Dependencies": "3.1.1" + } + }, + "Speckle.Sdk.Dependencies": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" + } + } + } +} \ No newline at end of file diff --git a/Converters/Navisworks/Speckle.Converters.Navisworks2022/Speckle.Converters.Navisworks2022.csproj b/Converters/Navisworks/Speckle.Converters.Navisworks2022/Speckle.Converters.Navisworks2022.csproj new file mode 100644 index 000000000..c95f1af02 --- /dev/null +++ b/Converters/Navisworks/Speckle.Converters.Navisworks2022/Speckle.Converters.Navisworks2022.csproj @@ -0,0 +1,20 @@ + + + net48 + x64 + $(DefineConstants);NAVIS2022 + Debug;Release;Local + + + + + + + + + + + + + + diff --git a/Converters/Navisworks/Speckle.Converters.Navisworks2022/packages.lock.json b/Converters/Navisworks/Speckle.Converters.Navisworks2022/packages.lock.json new file mode 100644 index 000000000..2085b1890 --- /dev/null +++ b/Converters/Navisworks/Speckle.Converters.Navisworks2022/packages.lock.json @@ -0,0 +1,353 @@ +{ + "version": 2, + "dependencies": { + ".NETFramework,Version=v4.8": { + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net48": "1.0.3" + } + }, + "Microsoft.SourceLink.GitHub": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "dependencies": { + "Microsoft.Build.Tasks.Git": "8.0.0", + "Microsoft.SourceLink.Common": "8.0.0" + } + }, + "PolySharp": { + "type": "Direct", + "requested": "[1.14.1, )", + "resolved": "1.14.1", + "contentHash": "mOOmFYwad3MIOL14VCjj02LljyF1GNw1wP0YVlxtcPvqdxjGGMNdNJJxHptlry3MOd8b40Flm8RPOM8JOlN2sQ==" + }, + "Speckle.InterfaceGenerator": { + "type": "Direct", + "requested": "[0.9.6, )", + "resolved": "0.9.6", + "contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w==" + }, + "Speckle.Navisworks.API": { + "type": "Direct", + "requested": "[2022.0.0, )", + "resolved": "2022.0.0", + "contentHash": "x0RW4Iqw8YHVK4ZiLEyLLfI5ffuRBR0KhEvmy9ZpT8SLNeDL/c6jn+7JWBVFUJPu+ObVnU+KqJjLdHmphN0lMQ==" + }, + "GraphQL.Client": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "8yPNBbuVBpTptivyAlak4GZvbwbUcjeQTL4vN1HKHRuOykZ4r7l5fcLS6vpyPyLn0x8FsL31xbOIKyxbmR9rbA==", + "dependencies": { + "GraphQL.Client.Abstractions": "6.0.0", + "GraphQL.Client.Abstractions.Websocket": "6.0.0", + "System.Net.WebSockets.Client.Managed": "1.0.22", + "System.Reactive": "5.0.0" + } + }, + "GraphQL.Client.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "h7uzWFORHZ+CCjwr/ThAyXMr0DPpzEANDa4Uo54wqCQ+j7qUKwqYTgOrb1W40sqbvNaZm9v/X7It31SUw0maHA==", + "dependencies": { + "GraphQL.Primitives": "6.0.0" + } + }, + "GraphQL.Client.Abstractions.Websocket": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "Nr9bPf8gIOvLuXpqEpqr9z9jslYFJOvd0feHth3/kPqeR3uMbjF5pjiwh4jxyMcxHdr8Pb6QiXkV3hsSyt0v7A==", + "dependencies": { + "GraphQL.Client.Abstractions": "6.0.0" + } + }, + "GraphQL.Primitives": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "yg72rrYDapfsIUrul7aF6wwNnTJBOFvuA9VdDTQpPa8AlAriHbufeXYLBcodKjfUdkCnaiggX1U/nEP08Zb5GA==" + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "W8DPQjkMScOMTtJbPwmPyj9c3zYSFGawDW3jwlBOOsnY+EzZFLgNQ/UMkK35JmkNOVPdCyPr2Tw7Vv9N+KA3ZQ==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.CSharp": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==" + }, + "Microsoft.Data.Sqlite": { + "type": "Transitive", + "resolved": "7.0.5", + "contentHash": "KGxbPeWsQMnmQy43DSBxAFtHz3l2JX8EWBSGUCvT3CuZ8KsuzbkqMIJMDOxWtG8eZSoCDI04aiVQjWuuV8HmSw==", + "dependencies": { + "Microsoft.Data.Sqlite.Core": "7.0.5", + "SQLitePCLRaw.bundle_e_sqlite3": "2.1.4" + } + }, + "Microsoft.Data.Sqlite.Core": { + "type": "Transitive", + "resolved": "7.0.5", + "contentHash": "FTerRmQPqHrCrnoUzhBu+E+1DNGwyrAMLqHkAqOOOu5pGfyMOj8qQUBxI/gDtWtG11p49UxSfWmBzRNlwZqfUg==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "nOP8R1mVb/6mZtm2qgAJXn/LFm/2kMjHDAg/QJLFG6CuWYJtaD3p1BwQhufBVvRzL9ceJ/xF0SQ0qsI2GkDQAA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "65MrmXCziWaQFrI0UHkQbesrX5wTwf9XPjY5yFm/VkgJKFJ5gqvXRoXjIZcf2wLi5ZlwGz/oMYfyURVCWbM5iw==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "vJ9xvOZCnUAIHcGC3SU35r3HKmHTVIeHzo6u/qzlHAqD8m6xv92MLin4oJntTvkpKxVX3vI1GFFkIQtU3AdlsQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "2.2.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw==" + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "UpZLNLBpIZ0GTebShui7xXYh6DmBHjWM8NxGxZbdQh/bPZ5e6YswqI+bru6BnEL5eWiOdodsXtEz3FROcgi/qg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Primitives": "2.2.0", + "System.ComponentModel.Annotations": "4.5.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "azyQtqbm4fSaDzZHD/J+V6oWMFaf2tWP4WEGIYePLCMw3+b2RQdj9ybgbQyjCshcitQKQ4lEDOZjmSlTTrHxUg==", + "dependencies": { + "System.Memory": "4.5.1", + "System.Runtime.CompilerServices.Unsafe": "4.5.1" + } + }, + "Microsoft.NETFramework.ReferenceAssemblies.net48": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "zMk4D+9zyiEWByyQ7oPImPN/Jhpj166Ky0Nlla4eXlNL8hI/BtSJsgR8Inldd4NNpIAH3oh8yym0W2DrhXdSLQ==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "Speckle.Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.2", + "contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA==" + }, + "SQLitePCLRaw.bundle_e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "EWI1olKDjFEBMJu0+3wuxwziIAdWDVMYLhuZ3Qs84rrz+DHwD00RzWPZCa+bLnHCf3oJwuFZIRsHT5p236QXww==", + "dependencies": { + "SQLitePCLRaw.lib.e_sqlite3": "2.1.4", + "SQLitePCLRaw.provider.dynamic_cdecl": "2.1.4" + } + }, + "SQLitePCLRaw.core": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "inBjvSHo9UDKneGNzfUfDjK08JzlcIhn1+SP5Y3m6cgXpCxXKCJDy6Mka7LpgSV+UZmKSnC8rTwB0SQ0xKu5pA==", + "dependencies": { + "System.Memory": "4.5.3" + } + }, + "SQLitePCLRaw.lib.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "2C9Q9eX7CPLveJA0rIhf9RXAvu+7nWZu1A2MdG6SD/NOu26TakGgL1nsbc0JAspGijFOo3HoN79xrx8a368fBg==" + }, + "SQLitePCLRaw.provider.dynamic_cdecl": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "ZsaKKhgYF9B1fvcnOGKl3EycNAwd9CRWX7v0rEfuPWhQQ5Jjpvf2VEHahiLIGHio3hxi3EIKFJw9KvyowWOUAw==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "System.Buffers": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "AwarXzzoDwX6BgrhjoJsk6tUezZEozOT5Y9QKF94Gl4JK91I4PIIBkBco9068Y9/Dra8Dkbie99kXB8+1BaYKw==" + }, + "System.ComponentModel.Annotations": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "UxYQ3FGUOtzJ7LfSdnYSFd7+oEv6M8NgUatatIN2HxNtDdlcvFAf+VIq4Of9cDMJEJC0aSRv/x898RYhB4Yppg==" + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.3", + "contentHash": "3oDzvc/zzetpTKWMShs1AADwZjQ/36HnsufHRPcOjyRAAMLDlu2iD33MBI2opxnezcVUtXyqDXXjoFMOU9c7SA==", + "dependencies": { + "System.Buffers": "4.4.0", + "System.Numerics.Vectors": "4.4.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.2" + } + }, + "System.Net.WebSockets.Client.Managed": { + "type": "Transitive", + "resolved": "1.0.22", + "contentHash": "WqEOxPlXjuZrIjUtXNE9NxEfU/n5E35iV2PtoZdJSUC4tlrqwHnTee+wvMIM4OUaJWmwrymeqcgYrE0IkGAgLA==", + "dependencies": { + "System.Buffers": "4.4.0", + "System.Numerics.Vectors": "4.4.0" + } + }, + "System.Numerics.Vectors": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ==" + }, + "System.Reactive": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "4.5.3", + "contentHash": "3TIsJhD1EiiT0w2CcDMN/iSSwnNnsrnbzeVHSKkaEgV85txMprmuO+Yq2AdSbeVGcg28pdNDTPK87tJhX7VFHw==" + }, + "System.Threading.Tasks.Extensions": { + "type": "Transitive", + "resolved": "4.5.4", + "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + }, + "speckle.connectors.common": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", + "Speckle.Connectors.Logging": "[1.0.0, )", + "Speckle.Objects": "[3.1.1, )", + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" + } + }, + "speckle.connectors.dui": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", + "Speckle.Connectors.Common": "[1.0.0, )", + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" + } + }, + "speckle.connectors.logging": { + "type": "Project" + }, + "speckle.converters.common": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", + "Speckle.Objects": "[3.1.1, )" + } + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "MZtBIwfDFork5vfjpJdG5g8wuJFt7d/y3LOSVVtDK/76wlbtz6cjltfKHqLx2TKVqTj5/c41t77m1+h20zqtPA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0" + } + }, + "Microsoft.Extensions.Logging": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "Nxqhadc9FCmFHzU+fz3oc8sFlE6IadViYg8dfUdGzJZ2JUxnCsRghBhhOWdM4B2zSZqEc+0BjliBh/oNdRZuig==", + "dependencies": { + "Microsoft.Extensions.Configuration.Binder": "2.2.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging.Abstractions": "2.2.0", + "Microsoft.Extensions.Options": "2.2.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" + }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, + "Speckle.Objects": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", + "dependencies": { + "Speckle.Sdk": "3.1.1" + } + }, + "Speckle.Sdk": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", + "dependencies": { + "GraphQL.Client": "6.0.0", + "Microsoft.Bcl.AsyncInterfaces": "5.0.0", + "Microsoft.CSharp": "4.7.0", + "Microsoft.Data.Sqlite": "7.0.5", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging": "2.2.0", + "Speckle.DoubleNumerics": "4.1.0", + "Speckle.Newtonsoft.Json": "13.0.2", + "Speckle.Sdk.Dependencies": "3.1.1" + } + }, + "Speckle.Sdk.Dependencies": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" + } + } + } +} \ No newline at end of file diff --git a/Converters/Navisworks/Speckle.Converters.Navisworks2023/Speckle.Converters.Navisworks2023.csproj b/Converters/Navisworks/Speckle.Converters.Navisworks2023/Speckle.Converters.Navisworks2023.csproj new file mode 100644 index 000000000..e81f35e97 --- /dev/null +++ b/Converters/Navisworks/Speckle.Converters.Navisworks2023/Speckle.Converters.Navisworks2023.csproj @@ -0,0 +1,20 @@ + + + net48 + x64 + $(DefineConstants);NAVIS2023 + Debug;Release;Local + + + + + + + + + + + + + + diff --git a/Converters/Navisworks/Speckle.Converters.Navisworks2023/packages.lock.json b/Converters/Navisworks/Speckle.Converters.Navisworks2023/packages.lock.json new file mode 100644 index 000000000..a4e066469 --- /dev/null +++ b/Converters/Navisworks/Speckle.Converters.Navisworks2023/packages.lock.json @@ -0,0 +1,353 @@ +{ + "version": 2, + "dependencies": { + ".NETFramework,Version=v4.8": { + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net48": "1.0.3" + } + }, + "Microsoft.SourceLink.GitHub": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "dependencies": { + "Microsoft.Build.Tasks.Git": "8.0.0", + "Microsoft.SourceLink.Common": "8.0.0" + } + }, + "PolySharp": { + "type": "Direct", + "requested": "[1.14.1, )", + "resolved": "1.14.1", + "contentHash": "mOOmFYwad3MIOL14VCjj02LljyF1GNw1wP0YVlxtcPvqdxjGGMNdNJJxHptlry3MOd8b40Flm8RPOM8JOlN2sQ==" + }, + "Speckle.InterfaceGenerator": { + "type": "Direct", + "requested": "[0.9.6, )", + "resolved": "0.9.6", + "contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w==" + }, + "Speckle.Navisworks.API": { + "type": "Direct", + "requested": "[2023.0.0, )", + "resolved": "2023.0.0", + "contentHash": "+qRmcyLD3DpuSSwX2IbEwp0gJllbiKgv313PZfEfh8I2uvxf+5YNqDzY0OGOxWIdPKqaQmdUZ1ELzoDtucCWzA==" + }, + "GraphQL.Client": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "8yPNBbuVBpTptivyAlak4GZvbwbUcjeQTL4vN1HKHRuOykZ4r7l5fcLS6vpyPyLn0x8FsL31xbOIKyxbmR9rbA==", + "dependencies": { + "GraphQL.Client.Abstractions": "6.0.0", + "GraphQL.Client.Abstractions.Websocket": "6.0.0", + "System.Net.WebSockets.Client.Managed": "1.0.22", + "System.Reactive": "5.0.0" + } + }, + "GraphQL.Client.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "h7uzWFORHZ+CCjwr/ThAyXMr0DPpzEANDa4Uo54wqCQ+j7qUKwqYTgOrb1W40sqbvNaZm9v/X7It31SUw0maHA==", + "dependencies": { + "GraphQL.Primitives": "6.0.0" + } + }, + "GraphQL.Client.Abstractions.Websocket": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "Nr9bPf8gIOvLuXpqEpqr9z9jslYFJOvd0feHth3/kPqeR3uMbjF5pjiwh4jxyMcxHdr8Pb6QiXkV3hsSyt0v7A==", + "dependencies": { + "GraphQL.Client.Abstractions": "6.0.0" + } + }, + "GraphQL.Primitives": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "yg72rrYDapfsIUrul7aF6wwNnTJBOFvuA9VdDTQpPa8AlAriHbufeXYLBcodKjfUdkCnaiggX1U/nEP08Zb5GA==" + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "W8DPQjkMScOMTtJbPwmPyj9c3zYSFGawDW3jwlBOOsnY+EzZFLgNQ/UMkK35JmkNOVPdCyPr2Tw7Vv9N+KA3ZQ==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.CSharp": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==" + }, + "Microsoft.Data.Sqlite": { + "type": "Transitive", + "resolved": "7.0.5", + "contentHash": "KGxbPeWsQMnmQy43DSBxAFtHz3l2JX8EWBSGUCvT3CuZ8KsuzbkqMIJMDOxWtG8eZSoCDI04aiVQjWuuV8HmSw==", + "dependencies": { + "Microsoft.Data.Sqlite.Core": "7.0.5", + "SQLitePCLRaw.bundle_e_sqlite3": "2.1.4" + } + }, + "Microsoft.Data.Sqlite.Core": { + "type": "Transitive", + "resolved": "7.0.5", + "contentHash": "FTerRmQPqHrCrnoUzhBu+E+1DNGwyrAMLqHkAqOOOu5pGfyMOj8qQUBxI/gDtWtG11p49UxSfWmBzRNlwZqfUg==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "nOP8R1mVb/6mZtm2qgAJXn/LFm/2kMjHDAg/QJLFG6CuWYJtaD3p1BwQhufBVvRzL9ceJ/xF0SQ0qsI2GkDQAA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "65MrmXCziWaQFrI0UHkQbesrX5wTwf9XPjY5yFm/VkgJKFJ5gqvXRoXjIZcf2wLi5ZlwGz/oMYfyURVCWbM5iw==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "vJ9xvOZCnUAIHcGC3SU35r3HKmHTVIeHzo6u/qzlHAqD8m6xv92MLin4oJntTvkpKxVX3vI1GFFkIQtU3AdlsQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "2.2.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw==" + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "UpZLNLBpIZ0GTebShui7xXYh6DmBHjWM8NxGxZbdQh/bPZ5e6YswqI+bru6BnEL5eWiOdodsXtEz3FROcgi/qg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Primitives": "2.2.0", + "System.ComponentModel.Annotations": "4.5.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "azyQtqbm4fSaDzZHD/J+V6oWMFaf2tWP4WEGIYePLCMw3+b2RQdj9ybgbQyjCshcitQKQ4lEDOZjmSlTTrHxUg==", + "dependencies": { + "System.Memory": "4.5.1", + "System.Runtime.CompilerServices.Unsafe": "4.5.1" + } + }, + "Microsoft.NETFramework.ReferenceAssemblies.net48": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "zMk4D+9zyiEWByyQ7oPImPN/Jhpj166Ky0Nlla4eXlNL8hI/BtSJsgR8Inldd4NNpIAH3oh8yym0W2DrhXdSLQ==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "Speckle.Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.2", + "contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA==" + }, + "SQLitePCLRaw.bundle_e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "EWI1olKDjFEBMJu0+3wuxwziIAdWDVMYLhuZ3Qs84rrz+DHwD00RzWPZCa+bLnHCf3oJwuFZIRsHT5p236QXww==", + "dependencies": { + "SQLitePCLRaw.lib.e_sqlite3": "2.1.4", + "SQLitePCLRaw.provider.dynamic_cdecl": "2.1.4" + } + }, + "SQLitePCLRaw.core": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "inBjvSHo9UDKneGNzfUfDjK08JzlcIhn1+SP5Y3m6cgXpCxXKCJDy6Mka7LpgSV+UZmKSnC8rTwB0SQ0xKu5pA==", + "dependencies": { + "System.Memory": "4.5.3" + } + }, + "SQLitePCLRaw.lib.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "2C9Q9eX7CPLveJA0rIhf9RXAvu+7nWZu1A2MdG6SD/NOu26TakGgL1nsbc0JAspGijFOo3HoN79xrx8a368fBg==" + }, + "SQLitePCLRaw.provider.dynamic_cdecl": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "ZsaKKhgYF9B1fvcnOGKl3EycNAwd9CRWX7v0rEfuPWhQQ5Jjpvf2VEHahiLIGHio3hxi3EIKFJw9KvyowWOUAw==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "System.Buffers": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "AwarXzzoDwX6BgrhjoJsk6tUezZEozOT5Y9QKF94Gl4JK91I4PIIBkBco9068Y9/Dra8Dkbie99kXB8+1BaYKw==" + }, + "System.ComponentModel.Annotations": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "UxYQ3FGUOtzJ7LfSdnYSFd7+oEv6M8NgUatatIN2HxNtDdlcvFAf+VIq4Of9cDMJEJC0aSRv/x898RYhB4Yppg==" + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.3", + "contentHash": "3oDzvc/zzetpTKWMShs1AADwZjQ/36HnsufHRPcOjyRAAMLDlu2iD33MBI2opxnezcVUtXyqDXXjoFMOU9c7SA==", + "dependencies": { + "System.Buffers": "4.4.0", + "System.Numerics.Vectors": "4.4.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.2" + } + }, + "System.Net.WebSockets.Client.Managed": { + "type": "Transitive", + "resolved": "1.0.22", + "contentHash": "WqEOxPlXjuZrIjUtXNE9NxEfU/n5E35iV2PtoZdJSUC4tlrqwHnTee+wvMIM4OUaJWmwrymeqcgYrE0IkGAgLA==", + "dependencies": { + "System.Buffers": "4.4.0", + "System.Numerics.Vectors": "4.4.0" + } + }, + "System.Numerics.Vectors": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ==" + }, + "System.Reactive": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "4.5.3", + "contentHash": "3TIsJhD1EiiT0w2CcDMN/iSSwnNnsrnbzeVHSKkaEgV85txMprmuO+Yq2AdSbeVGcg28pdNDTPK87tJhX7VFHw==" + }, + "System.Threading.Tasks.Extensions": { + "type": "Transitive", + "resolved": "4.5.4", + "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + }, + "speckle.connectors.common": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", + "Speckle.Connectors.Logging": "[1.0.0, )", + "Speckle.Objects": "[3.1.1, )", + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" + } + }, + "speckle.connectors.dui": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", + "Speckle.Connectors.Common": "[1.0.0, )", + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" + } + }, + "speckle.connectors.logging": { + "type": "Project" + }, + "speckle.converters.common": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", + "Speckle.Objects": "[3.1.1, )" + } + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "MZtBIwfDFork5vfjpJdG5g8wuJFt7d/y3LOSVVtDK/76wlbtz6cjltfKHqLx2TKVqTj5/c41t77m1+h20zqtPA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0" + } + }, + "Microsoft.Extensions.Logging": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "Nxqhadc9FCmFHzU+fz3oc8sFlE6IadViYg8dfUdGzJZ2JUxnCsRghBhhOWdM4B2zSZqEc+0BjliBh/oNdRZuig==", + "dependencies": { + "Microsoft.Extensions.Configuration.Binder": "2.2.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging.Abstractions": "2.2.0", + "Microsoft.Extensions.Options": "2.2.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" + }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, + "Speckle.Objects": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", + "dependencies": { + "Speckle.Sdk": "3.1.1" + } + }, + "Speckle.Sdk": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", + "dependencies": { + "GraphQL.Client": "6.0.0", + "Microsoft.Bcl.AsyncInterfaces": "5.0.0", + "Microsoft.CSharp": "4.7.0", + "Microsoft.Data.Sqlite": "7.0.5", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging": "2.2.0", + "Speckle.DoubleNumerics": "4.1.0", + "Speckle.Newtonsoft.Json": "13.0.2", + "Speckle.Sdk.Dependencies": "3.1.1" + } + }, + "Speckle.Sdk.Dependencies": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" + } + } + } +} \ No newline at end of file diff --git a/Converters/Navisworks/Speckle.Converters.Navisworks2024/Speckle.Converters.Navisworks2024.csproj b/Converters/Navisworks/Speckle.Converters.Navisworks2024/Speckle.Converters.Navisworks2024.csproj new file mode 100644 index 000000000..4b10afb81 --- /dev/null +++ b/Converters/Navisworks/Speckle.Converters.Navisworks2024/Speckle.Converters.Navisworks2024.csproj @@ -0,0 +1,20 @@ + + + net48 + x64 + $(DefineConstants);NAVIS2024 + Debug;Release;Local + + + + + + + + + + + + + + diff --git a/Converters/Navisworks/Speckle.Converters.Navisworks2024/packages.lock.json b/Converters/Navisworks/Speckle.Converters.Navisworks2024/packages.lock.json new file mode 100644 index 000000000..536ad9312 --- /dev/null +++ b/Converters/Navisworks/Speckle.Converters.Navisworks2024/packages.lock.json @@ -0,0 +1,353 @@ +{ + "version": 2, + "dependencies": { + ".NETFramework,Version=v4.8": { + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net48": "1.0.3" + } + }, + "Microsoft.SourceLink.GitHub": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "dependencies": { + "Microsoft.Build.Tasks.Git": "8.0.0", + "Microsoft.SourceLink.Common": "8.0.0" + } + }, + "PolySharp": { + "type": "Direct", + "requested": "[1.14.1, )", + "resolved": "1.14.1", + "contentHash": "mOOmFYwad3MIOL14VCjj02LljyF1GNw1wP0YVlxtcPvqdxjGGMNdNJJxHptlry3MOd8b40Flm8RPOM8JOlN2sQ==" + }, + "Speckle.InterfaceGenerator": { + "type": "Direct", + "requested": "[0.9.6, )", + "resolved": "0.9.6", + "contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w==" + }, + "Speckle.Navisworks.API": { + "type": "Direct", + "requested": "[2024.0.0, )", + "resolved": "2024.0.0", + "contentHash": "SnkvhcENMy3YLWbzy4lCweMuWdAbNzAtwvffFH2xVHmnm/2INnMGucYGazAHN496d6wgl9YRGa4qftgVsg7T7A==" + }, + "GraphQL.Client": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "8yPNBbuVBpTptivyAlak4GZvbwbUcjeQTL4vN1HKHRuOykZ4r7l5fcLS6vpyPyLn0x8FsL31xbOIKyxbmR9rbA==", + "dependencies": { + "GraphQL.Client.Abstractions": "6.0.0", + "GraphQL.Client.Abstractions.Websocket": "6.0.0", + "System.Net.WebSockets.Client.Managed": "1.0.22", + "System.Reactive": "5.0.0" + } + }, + "GraphQL.Client.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "h7uzWFORHZ+CCjwr/ThAyXMr0DPpzEANDa4Uo54wqCQ+j7qUKwqYTgOrb1W40sqbvNaZm9v/X7It31SUw0maHA==", + "dependencies": { + "GraphQL.Primitives": "6.0.0" + } + }, + "GraphQL.Client.Abstractions.Websocket": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "Nr9bPf8gIOvLuXpqEpqr9z9jslYFJOvd0feHth3/kPqeR3uMbjF5pjiwh4jxyMcxHdr8Pb6QiXkV3hsSyt0v7A==", + "dependencies": { + "GraphQL.Client.Abstractions": "6.0.0" + } + }, + "GraphQL.Primitives": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "yg72rrYDapfsIUrul7aF6wwNnTJBOFvuA9VdDTQpPa8AlAriHbufeXYLBcodKjfUdkCnaiggX1U/nEP08Zb5GA==" + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "W8DPQjkMScOMTtJbPwmPyj9c3zYSFGawDW3jwlBOOsnY+EzZFLgNQ/UMkK35JmkNOVPdCyPr2Tw7Vv9N+KA3ZQ==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.CSharp": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==" + }, + "Microsoft.Data.Sqlite": { + "type": "Transitive", + "resolved": "7.0.5", + "contentHash": "KGxbPeWsQMnmQy43DSBxAFtHz3l2JX8EWBSGUCvT3CuZ8KsuzbkqMIJMDOxWtG8eZSoCDI04aiVQjWuuV8HmSw==", + "dependencies": { + "Microsoft.Data.Sqlite.Core": "7.0.5", + "SQLitePCLRaw.bundle_e_sqlite3": "2.1.4" + } + }, + "Microsoft.Data.Sqlite.Core": { + "type": "Transitive", + "resolved": "7.0.5", + "contentHash": "FTerRmQPqHrCrnoUzhBu+E+1DNGwyrAMLqHkAqOOOu5pGfyMOj8qQUBxI/gDtWtG11p49UxSfWmBzRNlwZqfUg==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "nOP8R1mVb/6mZtm2qgAJXn/LFm/2kMjHDAg/QJLFG6CuWYJtaD3p1BwQhufBVvRzL9ceJ/xF0SQ0qsI2GkDQAA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "65MrmXCziWaQFrI0UHkQbesrX5wTwf9XPjY5yFm/VkgJKFJ5gqvXRoXjIZcf2wLi5ZlwGz/oMYfyURVCWbM5iw==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "vJ9xvOZCnUAIHcGC3SU35r3HKmHTVIeHzo6u/qzlHAqD8m6xv92MLin4oJntTvkpKxVX3vI1GFFkIQtU3AdlsQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "2.2.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw==" + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "UpZLNLBpIZ0GTebShui7xXYh6DmBHjWM8NxGxZbdQh/bPZ5e6YswqI+bru6BnEL5eWiOdodsXtEz3FROcgi/qg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Primitives": "2.2.0", + "System.ComponentModel.Annotations": "4.5.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "azyQtqbm4fSaDzZHD/J+V6oWMFaf2tWP4WEGIYePLCMw3+b2RQdj9ybgbQyjCshcitQKQ4lEDOZjmSlTTrHxUg==", + "dependencies": { + "System.Memory": "4.5.1", + "System.Runtime.CompilerServices.Unsafe": "4.5.1" + } + }, + "Microsoft.NETFramework.ReferenceAssemblies.net48": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "zMk4D+9zyiEWByyQ7oPImPN/Jhpj166Ky0Nlla4eXlNL8hI/BtSJsgR8Inldd4NNpIAH3oh8yym0W2DrhXdSLQ==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "Speckle.Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.2", + "contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA==" + }, + "SQLitePCLRaw.bundle_e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "EWI1olKDjFEBMJu0+3wuxwziIAdWDVMYLhuZ3Qs84rrz+DHwD00RzWPZCa+bLnHCf3oJwuFZIRsHT5p236QXww==", + "dependencies": { + "SQLitePCLRaw.lib.e_sqlite3": "2.1.4", + "SQLitePCLRaw.provider.dynamic_cdecl": "2.1.4" + } + }, + "SQLitePCLRaw.core": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "inBjvSHo9UDKneGNzfUfDjK08JzlcIhn1+SP5Y3m6cgXpCxXKCJDy6Mka7LpgSV+UZmKSnC8rTwB0SQ0xKu5pA==", + "dependencies": { + "System.Memory": "4.5.3" + } + }, + "SQLitePCLRaw.lib.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "2C9Q9eX7CPLveJA0rIhf9RXAvu+7nWZu1A2MdG6SD/NOu26TakGgL1nsbc0JAspGijFOo3HoN79xrx8a368fBg==" + }, + "SQLitePCLRaw.provider.dynamic_cdecl": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "ZsaKKhgYF9B1fvcnOGKl3EycNAwd9CRWX7v0rEfuPWhQQ5Jjpvf2VEHahiLIGHio3hxi3EIKFJw9KvyowWOUAw==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "System.Buffers": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "AwarXzzoDwX6BgrhjoJsk6tUezZEozOT5Y9QKF94Gl4JK91I4PIIBkBco9068Y9/Dra8Dkbie99kXB8+1BaYKw==" + }, + "System.ComponentModel.Annotations": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "UxYQ3FGUOtzJ7LfSdnYSFd7+oEv6M8NgUatatIN2HxNtDdlcvFAf+VIq4Of9cDMJEJC0aSRv/x898RYhB4Yppg==" + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.3", + "contentHash": "3oDzvc/zzetpTKWMShs1AADwZjQ/36HnsufHRPcOjyRAAMLDlu2iD33MBI2opxnezcVUtXyqDXXjoFMOU9c7SA==", + "dependencies": { + "System.Buffers": "4.4.0", + "System.Numerics.Vectors": "4.4.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.2" + } + }, + "System.Net.WebSockets.Client.Managed": { + "type": "Transitive", + "resolved": "1.0.22", + "contentHash": "WqEOxPlXjuZrIjUtXNE9NxEfU/n5E35iV2PtoZdJSUC4tlrqwHnTee+wvMIM4OUaJWmwrymeqcgYrE0IkGAgLA==", + "dependencies": { + "System.Buffers": "4.4.0", + "System.Numerics.Vectors": "4.4.0" + } + }, + "System.Numerics.Vectors": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ==" + }, + "System.Reactive": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "4.5.3", + "contentHash": "3TIsJhD1EiiT0w2CcDMN/iSSwnNnsrnbzeVHSKkaEgV85txMprmuO+Yq2AdSbeVGcg28pdNDTPK87tJhX7VFHw==" + }, + "System.Threading.Tasks.Extensions": { + "type": "Transitive", + "resolved": "4.5.4", + "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + }, + "speckle.connectors.common": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", + "Speckle.Connectors.Logging": "[1.0.0, )", + "Speckle.Objects": "[3.1.1, )", + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" + } + }, + "speckle.connectors.dui": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", + "Speckle.Connectors.Common": "[1.0.0, )", + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" + } + }, + "speckle.connectors.logging": { + "type": "Project" + }, + "speckle.converters.common": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", + "Speckle.Objects": "[3.1.1, )" + } + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "MZtBIwfDFork5vfjpJdG5g8wuJFt7d/y3LOSVVtDK/76wlbtz6cjltfKHqLx2TKVqTj5/c41t77m1+h20zqtPA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0" + } + }, + "Microsoft.Extensions.Logging": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "Nxqhadc9FCmFHzU+fz3oc8sFlE6IadViYg8dfUdGzJZ2JUxnCsRghBhhOWdM4B2zSZqEc+0BjliBh/oNdRZuig==", + "dependencies": { + "Microsoft.Extensions.Configuration.Binder": "2.2.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging.Abstractions": "2.2.0", + "Microsoft.Extensions.Options": "2.2.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" + }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, + "Speckle.Objects": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", + "dependencies": { + "Speckle.Sdk": "3.1.1" + } + }, + "Speckle.Sdk": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", + "dependencies": { + "GraphQL.Client": "6.0.0", + "Microsoft.Bcl.AsyncInterfaces": "5.0.0", + "Microsoft.CSharp": "4.7.0", + "Microsoft.Data.Sqlite": "7.0.5", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging": "2.2.0", + "Speckle.DoubleNumerics": "4.1.0", + "Speckle.Newtonsoft.Json": "13.0.2", + "Speckle.Sdk.Dependencies": "3.1.1" + } + }, + "Speckle.Sdk.Dependencies": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" + } + } + } +} \ No newline at end of file diff --git a/Converters/Navisworks/Speckle.Converters.Navisworks2025/Speckle.Converters.Navisworks2025.csproj b/Converters/Navisworks/Speckle.Converters.Navisworks2025/Speckle.Converters.Navisworks2025.csproj new file mode 100644 index 000000000..0ef228dda --- /dev/null +++ b/Converters/Navisworks/Speckle.Converters.Navisworks2025/Speckle.Converters.Navisworks2025.csproj @@ -0,0 +1,20 @@ + + + net48 + x64 + $(DefineConstants);NAVIS2025 + Debug;Release;Local + + + + + + + + + + + + + + diff --git a/Converters/Navisworks/Speckle.Converters.Navisworks2025/packages.lock.json b/Converters/Navisworks/Speckle.Converters.Navisworks2025/packages.lock.json new file mode 100644 index 000000000..e8b5f0917 --- /dev/null +++ b/Converters/Navisworks/Speckle.Converters.Navisworks2025/packages.lock.json @@ -0,0 +1,353 @@ +{ + "version": 2, + "dependencies": { + ".NETFramework,Version=v4.8": { + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net48": "1.0.3" + } + }, + "Microsoft.SourceLink.GitHub": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "dependencies": { + "Microsoft.Build.Tasks.Git": "8.0.0", + "Microsoft.SourceLink.Common": "8.0.0" + } + }, + "PolySharp": { + "type": "Direct", + "requested": "[1.14.1, )", + "resolved": "1.14.1", + "contentHash": "mOOmFYwad3MIOL14VCjj02LljyF1GNw1wP0YVlxtcPvqdxjGGMNdNJJxHptlry3MOd8b40Flm8RPOM8JOlN2sQ==" + }, + "Speckle.InterfaceGenerator": { + "type": "Direct", + "requested": "[0.9.6, )", + "resolved": "0.9.6", + "contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w==" + }, + "Speckle.Navisworks.API": { + "type": "Direct", + "requested": "[2025.0.0, )", + "resolved": "2025.0.0", + "contentHash": "+q2IObnUGqtC1O/ddy2p0HKm1eXRo7Yi80oD9VIWClidvGb3rVsXKZWBHiv4HwSn5JcOMSEt1cdSlRQLm8Ehjg==" + }, + "GraphQL.Client": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "8yPNBbuVBpTptivyAlak4GZvbwbUcjeQTL4vN1HKHRuOykZ4r7l5fcLS6vpyPyLn0x8FsL31xbOIKyxbmR9rbA==", + "dependencies": { + "GraphQL.Client.Abstractions": "6.0.0", + "GraphQL.Client.Abstractions.Websocket": "6.0.0", + "System.Net.WebSockets.Client.Managed": "1.0.22", + "System.Reactive": "5.0.0" + } + }, + "GraphQL.Client.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "h7uzWFORHZ+CCjwr/ThAyXMr0DPpzEANDa4Uo54wqCQ+j7qUKwqYTgOrb1W40sqbvNaZm9v/X7It31SUw0maHA==", + "dependencies": { + "GraphQL.Primitives": "6.0.0" + } + }, + "GraphQL.Client.Abstractions.Websocket": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "Nr9bPf8gIOvLuXpqEpqr9z9jslYFJOvd0feHth3/kPqeR3uMbjF5pjiwh4jxyMcxHdr8Pb6QiXkV3hsSyt0v7A==", + "dependencies": { + "GraphQL.Client.Abstractions": "6.0.0" + } + }, + "GraphQL.Primitives": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "yg72rrYDapfsIUrul7aF6wwNnTJBOFvuA9VdDTQpPa8AlAriHbufeXYLBcodKjfUdkCnaiggX1U/nEP08Zb5GA==" + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "W8DPQjkMScOMTtJbPwmPyj9c3zYSFGawDW3jwlBOOsnY+EzZFLgNQ/UMkK35JmkNOVPdCyPr2Tw7Vv9N+KA3ZQ==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.CSharp": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==" + }, + "Microsoft.Data.Sqlite": { + "type": "Transitive", + "resolved": "7.0.5", + "contentHash": "KGxbPeWsQMnmQy43DSBxAFtHz3l2JX8EWBSGUCvT3CuZ8KsuzbkqMIJMDOxWtG8eZSoCDI04aiVQjWuuV8HmSw==", + "dependencies": { + "Microsoft.Data.Sqlite.Core": "7.0.5", + "SQLitePCLRaw.bundle_e_sqlite3": "2.1.4" + } + }, + "Microsoft.Data.Sqlite.Core": { + "type": "Transitive", + "resolved": "7.0.5", + "contentHash": "FTerRmQPqHrCrnoUzhBu+E+1DNGwyrAMLqHkAqOOOu5pGfyMOj8qQUBxI/gDtWtG11p49UxSfWmBzRNlwZqfUg==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "nOP8R1mVb/6mZtm2qgAJXn/LFm/2kMjHDAg/QJLFG6CuWYJtaD3p1BwQhufBVvRzL9ceJ/xF0SQ0qsI2GkDQAA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "65MrmXCziWaQFrI0UHkQbesrX5wTwf9XPjY5yFm/VkgJKFJ5gqvXRoXjIZcf2wLi5ZlwGz/oMYfyURVCWbM5iw==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "vJ9xvOZCnUAIHcGC3SU35r3HKmHTVIeHzo6u/qzlHAqD8m6xv92MLin4oJntTvkpKxVX3vI1GFFkIQtU3AdlsQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "2.2.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw==" + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "UpZLNLBpIZ0GTebShui7xXYh6DmBHjWM8NxGxZbdQh/bPZ5e6YswqI+bru6BnEL5eWiOdodsXtEz3FROcgi/qg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Primitives": "2.2.0", + "System.ComponentModel.Annotations": "4.5.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "azyQtqbm4fSaDzZHD/J+V6oWMFaf2tWP4WEGIYePLCMw3+b2RQdj9ybgbQyjCshcitQKQ4lEDOZjmSlTTrHxUg==", + "dependencies": { + "System.Memory": "4.5.1", + "System.Runtime.CompilerServices.Unsafe": "4.5.1" + } + }, + "Microsoft.NETFramework.ReferenceAssemblies.net48": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "zMk4D+9zyiEWByyQ7oPImPN/Jhpj166Ky0Nlla4eXlNL8hI/BtSJsgR8Inldd4NNpIAH3oh8yym0W2DrhXdSLQ==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "Speckle.Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.2", + "contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA==" + }, + "SQLitePCLRaw.bundle_e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "EWI1olKDjFEBMJu0+3wuxwziIAdWDVMYLhuZ3Qs84rrz+DHwD00RzWPZCa+bLnHCf3oJwuFZIRsHT5p236QXww==", + "dependencies": { + "SQLitePCLRaw.lib.e_sqlite3": "2.1.4", + "SQLitePCLRaw.provider.dynamic_cdecl": "2.1.4" + } + }, + "SQLitePCLRaw.core": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "inBjvSHo9UDKneGNzfUfDjK08JzlcIhn1+SP5Y3m6cgXpCxXKCJDy6Mka7LpgSV+UZmKSnC8rTwB0SQ0xKu5pA==", + "dependencies": { + "System.Memory": "4.5.3" + } + }, + "SQLitePCLRaw.lib.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "2C9Q9eX7CPLveJA0rIhf9RXAvu+7nWZu1A2MdG6SD/NOu26TakGgL1nsbc0JAspGijFOo3HoN79xrx8a368fBg==" + }, + "SQLitePCLRaw.provider.dynamic_cdecl": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "ZsaKKhgYF9B1fvcnOGKl3EycNAwd9CRWX7v0rEfuPWhQQ5Jjpvf2VEHahiLIGHio3hxi3EIKFJw9KvyowWOUAw==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "System.Buffers": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "AwarXzzoDwX6BgrhjoJsk6tUezZEozOT5Y9QKF94Gl4JK91I4PIIBkBco9068Y9/Dra8Dkbie99kXB8+1BaYKw==" + }, + "System.ComponentModel.Annotations": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "UxYQ3FGUOtzJ7LfSdnYSFd7+oEv6M8NgUatatIN2HxNtDdlcvFAf+VIq4Of9cDMJEJC0aSRv/x898RYhB4Yppg==" + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.3", + "contentHash": "3oDzvc/zzetpTKWMShs1AADwZjQ/36HnsufHRPcOjyRAAMLDlu2iD33MBI2opxnezcVUtXyqDXXjoFMOU9c7SA==", + "dependencies": { + "System.Buffers": "4.4.0", + "System.Numerics.Vectors": "4.4.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.2" + } + }, + "System.Net.WebSockets.Client.Managed": { + "type": "Transitive", + "resolved": "1.0.22", + "contentHash": "WqEOxPlXjuZrIjUtXNE9NxEfU/n5E35iV2PtoZdJSUC4tlrqwHnTee+wvMIM4OUaJWmwrymeqcgYrE0IkGAgLA==", + "dependencies": { + "System.Buffers": "4.4.0", + "System.Numerics.Vectors": "4.4.0" + } + }, + "System.Numerics.Vectors": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ==" + }, + "System.Reactive": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "4.5.3", + "contentHash": "3TIsJhD1EiiT0w2CcDMN/iSSwnNnsrnbzeVHSKkaEgV85txMprmuO+Yq2AdSbeVGcg28pdNDTPK87tJhX7VFHw==" + }, + "System.Threading.Tasks.Extensions": { + "type": "Transitive", + "resolved": "4.5.4", + "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + }, + "speckle.connectors.common": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", + "Speckle.Connectors.Logging": "[1.0.0, )", + "Speckle.Objects": "[3.1.1, )", + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" + } + }, + "speckle.connectors.dui": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", + "Speckle.Connectors.Common": "[1.0.0, )", + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" + } + }, + "speckle.connectors.logging": { + "type": "Project" + }, + "speckle.converters.common": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", + "Speckle.Objects": "[3.1.1, )" + } + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "MZtBIwfDFork5vfjpJdG5g8wuJFt7d/y3LOSVVtDK/76wlbtz6cjltfKHqLx2TKVqTj5/c41t77m1+h20zqtPA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0" + } + }, + "Microsoft.Extensions.Logging": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "Nxqhadc9FCmFHzU+fz3oc8sFlE6IadViYg8dfUdGzJZ2JUxnCsRghBhhOWdM4B2zSZqEc+0BjliBh/oNdRZuig==", + "dependencies": { + "Microsoft.Extensions.Configuration.Binder": "2.2.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging.Abstractions": "2.2.0", + "Microsoft.Extensions.Options": "2.2.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" + }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, + "Speckle.Objects": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", + "dependencies": { + "Speckle.Sdk": "3.1.1" + } + }, + "Speckle.Sdk": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", + "dependencies": { + "GraphQL.Client": "6.0.0", + "Microsoft.Bcl.AsyncInterfaces": "5.0.0", + "Microsoft.CSharp": "4.7.0", + "Microsoft.Data.Sqlite": "7.0.5", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging": "2.2.0", + "Speckle.DoubleNumerics": "4.1.0", + "Speckle.Newtonsoft.Json": "13.0.2", + "Speckle.Sdk.Dependencies": "3.1.1" + } + }, + "Speckle.Sdk.Dependencies": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" + } + } + } +} \ No newline at end of file diff --git a/Converters/Navisworks/Speckle.Converters.NavisworksShared/DataExtractors/ClassPropertiesExtractor.cs b/Converters/Navisworks/Speckle.Converters.NavisworksShared/DataExtractors/ClassPropertiesExtractor.cs new file mode 100644 index 000000000..941d95395 --- /dev/null +++ b/Converters/Navisworks/Speckle.Converters.NavisworksShared/DataExtractors/ClassPropertiesExtractor.cs @@ -0,0 +1,51 @@ +using static Speckle.Converter.Navisworks.Helpers.PropertyHelpers; + +namespace Speckle.Converter.Navisworks.ToSpeckle; + +public class ClassPropertiesExtractor +{ + public Dictionary? GetClassProperties(NAV.ModelItem modelItem) + { + if (modelItem == null) + { + throw new ArgumentNullException(nameof(modelItem)); + } + + return ExtractClassProperties(modelItem); + } + + /// + /// Extracts property sets from a NAV.ModelItem and adds them to a dictionary, + /// including various details such as ClassName, ClassDisplayName, DisplayName, + /// InstanceGuid, Source, and Source Guid, ensuring that null or empty values + /// are not included in the final dictionary. + /// + /// The NAV.ModelItem from which properties are extracted. + /// A dictionary containing non-null/non-empty properties of the modelItem. + private static Dictionary ExtractClassProperties(NAV.ModelItem modelItem) + { + var propertyDictionary = new Dictionary(); + + // Define properties and their values to be added to the dictionary + var propertiesToAdd = new (string PropertyName, object? Value)[] + { + ("ClassName", modelItem.ClassName), + ("ClassDisplayName", modelItem.ClassDisplayName), + ("DisplayName", modelItem.DisplayName), + ("InstanceGuid", modelItem.InstanceGuid != Guid.Empty ? modelItem.InstanceGuid : null), + ("Source", modelItem.Model?.SourceFileName), + ("Source Guid", modelItem.Model?.SourceGuid) + }; + + // Loop through properties and add them if they are not null or empty + foreach ((string propertyName, object? value) in propertiesToAdd) + { + if (value != null) + { + AddPropertyIfNotNullOrEmpty(propertyDictionary, propertyName, value); + } + } + + return propertyDictionary; + } +} diff --git a/Converters/Navisworks/Speckle.Converters.NavisworksShared/DataExtractors/DisplayValueExtractor.cs b/Converters/Navisworks/Speckle.Converters.NavisworksShared/DataExtractors/DisplayValueExtractor.cs new file mode 100644 index 000000000..7ba6a7b24 --- /dev/null +++ b/Converters/Navisworks/Speckle.Converters.NavisworksShared/DataExtractors/DisplayValueExtractor.cs @@ -0,0 +1,38 @@ +using Microsoft.Extensions.Logging; +using Speckle.Converter.Navisworks.Settings; +using Speckle.Converters.Common; +using Speckle.Sdk.Models; +using static Speckle.Converter.Navisworks.Helpers.ElementSelectionHelper; + +namespace Speckle.Converter.Navisworks.ToSpeckle; + +public class DisplayValueExtractor +{ + private readonly IConverterSettingsStore _converterSettings; + private readonly ILogger _logger; + private readonly GeometryToSpeckleConverter _geometryConverter; + + public DisplayValueExtractor( + IConverterSettingsStore converterSettings, + ILogger logger + ) + { + _converterSettings = converterSettings; + _logger = logger; + _geometryConverter = new GeometryToSpeckleConverter(_converterSettings.Current); + } + + internal List GetDisplayValue(NAV.ModelItem modelItem) + { + if (modelItem == null) + { + throw new ArgumentNullException(nameof(modelItem)); + } + if (!modelItem.HasGeometry) + { + return []; + } + + return !IsElementVisible(modelItem) ? [] : _geometryConverter.Convert(modelItem); + } +} diff --git a/Converters/Navisworks/Speckle.Converters.NavisworksShared/DataExtractors/ModelPropertiesExtractor.cs b/Converters/Navisworks/Speckle.Converters.NavisworksShared/DataExtractors/ModelPropertiesExtractor.cs new file mode 100644 index 000000000..cd6ae2230 --- /dev/null +++ b/Converters/Navisworks/Speckle.Converters.NavisworksShared/DataExtractors/ModelPropertiesExtractor.cs @@ -0,0 +1,59 @@ +using Speckle.Converter.Navisworks.Settings; +using Speckle.Converters.Common; + +namespace Speckle.Converter.Navisworks.ToSpeckle; + +public class ModelPropertiesExtractor +{ + private readonly IConverterSettingsStore _settingsStore; + + public ModelPropertiesExtractor(IConverterSettingsStore settingsStore) + { + _settingsStore = settingsStore; + } + + internal Dictionary? GetModelProperties(NAV.Model model) + { + if (_settingsStore.Current.User.ExcludeProperties) + { + return null; + } + + var propertyDictionary = ExtractModelProperties(model); + + return propertyDictionary; + } + + /// + /// Extracts model properties from a NAV.ModelItem and adds them to a dictionary, + /// + /// The NAV.ModelItem from which model properties are extracted. + /// A dictionary containing model properties of the modelItem. + private static Dictionary ExtractModelProperties(NAV.Model model) + { + var propertyDictionary = new Dictionary(); + + // Define properties and their values to be added to the dictionary + var propertiesToAdd = new (string PropertyName, object? Value)[] + { + ("Creator", model.Creator), + ("Filename", model.FileName), + ("Source Filename", model.SourceFileName), + ("Units", model.Units.ToString()), + ("Transform", model.Transform.ToString()), + ("Guid", model.Guid != Guid.Empty ? model.Guid : null), + ("Source Guid", model.SourceGuid != Guid.Empty ? model.SourceGuid : null) + }; + + // Loop through properties and add them if they are not null or empty + foreach ((string propertyName, object? value) in propertiesToAdd) + { + if (value != null) + { + Helpers.PropertyHelpers.AddPropertyIfNotNullOrEmpty(propertyDictionary, propertyName, value); + } + } + + return propertyDictionary; + } +} diff --git a/Converters/Navisworks/Speckle.Converters.NavisworksShared/DataExtractors/PropertySetsExtractor.cs b/Converters/Navisworks/Speckle.Converters.NavisworksShared/DataExtractors/PropertySetsExtractor.cs new file mode 100644 index 000000000..ea7c8bc8f --- /dev/null +++ b/Converters/Navisworks/Speckle.Converters.NavisworksShared/DataExtractors/PropertySetsExtractor.cs @@ -0,0 +1,64 @@ +using Speckle.Converter.Navisworks.Settings; +using Speckle.Converters.Common; +using static Speckle.Converter.Navisworks.Helpers.PropertyHelpers; + +namespace Speckle.Converter.Navisworks.ToSpeckle; + +public class PropertySetsExtractor(IConverterSettingsStore settingsStore) +{ + internal Dictionary? GetPropertySets(NAV.ModelItem modelItem) + { + if (settingsStore.Current.User.ExcludeProperties) + { + return null; + } + + var propertyDictionary = ExtractPropertySets(modelItem); + + return propertyDictionary; + } + + /// + /// Extracts property sets from a NAV.ModelItem and adds them to a dictionary, + /// PropertySets are specific to the host application source appended to Navisworks and therefore + /// arbitrary in nature. + /// + /// The NAV.ModelItem from which property sets are extracted. + /// A dictionary containing property sets of the modelItem. + private Dictionary ExtractPropertySets(NAV.ModelItem modelItem) + { + var propertySetDictionary = new Dictionary(); + + foreach (var propertyCategory in modelItem.PropertyCategories) + { + if (IsCategoryToBeSkipped(propertyCategory)) + { + continue; + } + + var propertySet = new Dictionary(); + + foreach (var property in propertyCategory.Properties) + { + string sanitizedName = SanitizePropertyName(property.DisplayName); + var propertyValue = ConvertPropertyValue(property.Value, settingsStore.Current.Derived.SpeckleUnits); + + if (propertyValue != null) + { + propertySet[sanitizedName] = propertyValue; + } + } + + if (propertySet.Count <= 0) + { + continue; + } + + string categoryName = SanitizePropertyName(propertyCategory.DisplayName); + + propertySetDictionary[categoryName] = propertySet; + } + + return propertySetDictionary; + } +} diff --git a/Converters/Navisworks/Speckle.Converters.NavisworksShared/DataHandlers/BasePropertyHandler.cs b/Converters/Navisworks/Speckle.Converters.NavisworksShared/DataHandlers/BasePropertyHandler.cs new file mode 100644 index 000000000..d75e79910 --- /dev/null +++ b/Converters/Navisworks/Speckle.Converters.NavisworksShared/DataHandlers/BasePropertyHandler.cs @@ -0,0 +1,92 @@ +namespace Speckle.Converter.Navisworks.ToSpeckle.PropertyHandlers; + +/// +/// Base property handler providing common functionality for property assignment. +/// +public abstract class BasePropertyHandler( + PropertySetsExtractor propertySetsExtractor, + ModelPropertiesExtractor modelPropertiesExtractor +) : IPropertyHandler +{ + public abstract Dictionary GetProperties(NAV.ModelItem modelItem); + private readonly List _excludedProperties = ["Hidden", "Required", "Internal_Type"]; + + protected Dictionary ProcessPropertySets(NAV.ModelItem modelItem) + { + var categorizedProperties = new Dictionary(); + var propertySets = propertySetsExtractor.GetPropertySets(modelItem); + + if (propertySets != null) + { + foreach (var category in propertySets.Where(c => c.Key is not "Internal" and not "Transform")) + { + if (category.Value is not Dictionary properties) + { + continue; + } + var itemProps = CreatePropertyDictionary(properties); + + if (category.Key == "Item") + { + if (itemProps.Count <= 0) + { + continue; + } + + // add all non-excluded properties in the Item category to the root level + foreach (var prop in itemProps.Where(prop => !_excludedProperties.Contains(prop.Key))) + { + categorizedProperties[prop.Key] = prop.Value; + } + } + else + { + if (itemProps.Count > 0) + { + categorizedProperties[category.Key] = itemProps; + } + } + } + } + + if (modelItem == null) + { + throw new ArgumentNullException(nameof(modelItem)); + } + + AddModelProperties(modelItem, categorizedProperties); + return categorizedProperties; + } + + private void AddModelProperties(NAV.ModelItem modelItem, Dictionary categorizedProperties) + { + if (!modelItem.HasModel) + { + return; + } + + var modelProperties = modelPropertiesExtractor.GetModelProperties(modelItem.Model); + if (modelProperties == null) + { + return; + } + + var modelProps = CreatePropertyDictionary(modelProperties); + if (modelProps.Count > 0) + { + categorizedProperties["Model"] = modelProps; + } + } + + private static Dictionary CreatePropertyDictionary(Dictionary properties) + { + var propertyDict = new Dictionary(); + foreach (var prop in properties.Where(prop => IsValidPropertyValue(prop.Value))) + { + propertyDict[prop.Key] = prop.Value; + } + return propertyDict; + } + + protected static bool IsValidPropertyValue(object? value) => value != null && !string.IsNullOrEmpty(value.ToString()); +} diff --git a/Converters/Navisworks/Speckle.Converters.NavisworksShared/DataHandlers/HierarchicalPropertyHandler.cs b/Converters/Navisworks/Speckle.Converters.NavisworksShared/DataHandlers/HierarchicalPropertyHandler.cs new file mode 100644 index 000000000..9e25bf09c --- /dev/null +++ b/Converters/Navisworks/Speckle.Converters.NavisworksShared/DataHandlers/HierarchicalPropertyHandler.cs @@ -0,0 +1,114 @@ +namespace Speckle.Converter.Navisworks.ToSpeckle.PropertyHandlers; + +/// +/// Handles property assignment with hierarchy merging for objects that require ancestor properties. +/// +public class HierarchicalPropertyHandler( + PropertySetsExtractor propertySetsExtractor, + ModelPropertiesExtractor modelPropertiesExtractor, + ClassPropertiesExtractor classPropertiesExtractor +) : BasePropertyHandler(propertySetsExtractor, modelPropertiesExtractor) +{ + public override Dictionary GetProperties(NAV.ModelItem modelItem) + { + var propertyDict = classPropertiesExtractor.GetClassProperties(modelItem) ?? new Dictionary(); + + var hierarchy = GetObjectHierarchy(modelItem); + var propertyCollection = new Dictionary>>(); + + foreach (var item in hierarchy) + { + CollectHierarchicalProperties(item, propertyCollection); + } + + ApplyFilteredProperties(propertyDict, propertyCollection); + + return propertyDict; + } + + private static List GetObjectHierarchy(NAV.ModelItem target) + { + var hierarchy = new List(); + var firstObjectAncestor = target.FindFirstObjectAncestor(); + + if (firstObjectAncestor == null) + { + hierarchy.Add(target); + return hierarchy; + } + + var current = target; + while (current != null) + { + hierarchy.Add(current); + if (current == firstObjectAncestor) + { + break; + } + current = current.Parent; + } + + hierarchy.Reverse(); + return hierarchy; + } + + private void CollectHierarchicalProperties( + NAV.ModelItem item, + Dictionary>> propertyCollection + ) + { + var categoryDictionaries = ProcessPropertySets(item); + + foreach (var kvp in categoryDictionaries) + { + if (!propertyCollection.TryGetValue(kvp.Key, out var categoryProperties)) + { + categoryProperties = []; + propertyCollection.Add(kvp.Key, categoryProperties); + } + + if (kvp.Value is not Dictionary properties) + { + continue; + } + + foreach (var prop in properties.Where(prop => IsValidPropertyValue(prop.Value))) + { + if (!categoryProperties.TryGetValue(prop.Key, out var valueSet)) + { + valueSet = []; + categoryProperties.Add(prop.Key, valueSet); + } + valueSet.Add(prop.Value); + } + } + } + + private static void ApplyFilteredProperties( + Dictionary propertyDict, + Dictionary>> propertyCollection + ) + { + foreach (var kvp in propertyCollection) + { + var categoryDict = new Dictionary(); + bool hasProperties = false; + + foreach (var kvS in kvp.Value) + { + if ((kvS.Value).Count != 1) + { + continue; + } + + categoryDict[kvS.Key] = kvS.Value.First(); + hasProperties = true; + } + + if (hasProperties) + { + propertyDict[kvp.Key] = categoryDict; + } + } + } +} diff --git a/Converters/Navisworks/Speckle.Converters.NavisworksShared/DataHandlers/IPropertyHandler.cs b/Converters/Navisworks/Speckle.Converters.NavisworksShared/DataHandlers/IPropertyHandler.cs new file mode 100644 index 000000000..5afb0080c --- /dev/null +++ b/Converters/Navisworks/Speckle.Converters.NavisworksShared/DataHandlers/IPropertyHandler.cs @@ -0,0 +1,12 @@ +namespace Speckle.Converter.Navisworks.ToSpeckle.PropertyHandlers; + +/// +/// Defines the contract for handling property assignment to Speckle objects. +/// +public interface IPropertyHandler +{ + /// + /// Gets the properties from a Navisworks model item. + /// + Dictionary GetProperties(NAV.ModelItem modelItem); +} diff --git a/Converters/Navisworks/Speckle.Converters.NavisworksShared/DataHandlers/StandardPropertyHandler.cs b/Converters/Navisworks/Speckle.Converters.NavisworksShared/DataHandlers/StandardPropertyHandler.cs new file mode 100644 index 000000000..2c1c4be92 --- /dev/null +++ b/Converters/Navisworks/Speckle.Converters.NavisworksShared/DataHandlers/StandardPropertyHandler.cs @@ -0,0 +1,12 @@ +namespace Speckle.Converter.Navisworks.ToSpeckle.PropertyHandlers; + +/// +/// Handles standard property assignment without any merging or hierarchy processing. +/// +public class StandardPropertyHandler( + PropertySetsExtractor propertySetsExtractor, + ModelPropertiesExtractor modelPropertiesExtractor +) : BasePropertyHandler(propertySetsExtractor, modelPropertiesExtractor) +{ + public override Dictionary GetProperties(NAV.ModelItem modelItem) => ProcessPropertySets(modelItem); +} diff --git a/Converters/Navisworks/Speckle.Converters.NavisworksShared/DependencyInjection/NavisworksConverterServiceRegistration.cs b/Converters/Navisworks/Speckle.Converters.NavisworksShared/DependencyInjection/NavisworksConverterServiceRegistration.cs new file mode 100644 index 000000000..637df2ccb --- /dev/null +++ b/Converters/Navisworks/Speckle.Converters.NavisworksShared/DependencyInjection/NavisworksConverterServiceRegistration.cs @@ -0,0 +1,51 @@ +using System.Reflection; +using Microsoft.Extensions.DependencyInjection; +using Speckle.Converter.Navisworks.Helpers; +using Speckle.Converter.Navisworks.Services; +using Speckle.Converter.Navisworks.Settings; +using Speckle.Converter.Navisworks.ToSpeckle; +using Speckle.Converter.Navisworks.ToSpeckle.PropertyHandlers; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Registration; +using Speckle.Sdk; + +namespace Speckle.Converter.Navisworks.DependencyInjection; + +public static class NavisworksConverterServiceRegistration +{ + public static IServiceCollection AddNavisworksConverter(this IServiceCollection serviceCollection) + { + var converterAssembly = Assembly.GetExecutingAssembly(); + + // Register base converters + serviceCollection.AddMatchingInterfacesAsTransient(converterAssembly); + serviceCollection.AddRootCommon(converterAssembly); + + // Register property handlers + serviceCollection.AddScoped(); + serviceCollection.AddScoped(); + serviceCollection.AddScoped(); + + // Register settings management + serviceCollection.AddScoped(); + serviceCollection.AddScoped< + IConverterSettingsStore, + ConverterSettingsStore + >(); + + // Register unit conversion + serviceCollection.AddSingleton, NavisworksToSpeckleUnitConverter>(); + + // Register converters and handlers + serviceCollection.AddApplicationConverters(converterAssembly); + serviceCollection.AddScoped(); + serviceCollection.AddScoped(); + serviceCollection.AddScoped(); + + // Register geometry conversion + serviceCollection.AddScoped(); + serviceCollection.AddScoped(); + + return serviceCollection; + } +} diff --git a/Converters/Navisworks/Speckle.Converters.NavisworksShared/Extensions/ArrayExtensions.cs b/Converters/Navisworks/Speckle.Converters.NavisworksShared/Extensions/ArrayExtensions.cs new file mode 100644 index 000000000..c8c3b5e74 --- /dev/null +++ b/Converters/Navisworks/Speckle.Converters.NavisworksShared/Extensions/ArrayExtensions.cs @@ -0,0 +1,15 @@ +using System.Runtime.CompilerServices; + +namespace Speckle.Converter.Navisworks.Extensions; + +internal static class ArrayExtension +{ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T[] ToArray(this Array arr) + where T : struct + { + var result = new T[arr.Length]; + Array.Copy(arr, result, result.Length); + return result; + } +} diff --git a/Converters/Navisworks/Speckle.Converters.NavisworksShared/Geometries/Primitives.cs b/Converters/Navisworks/Speckle.Converters.NavisworksShared/Geometries/Primitives.cs new file mode 100644 index 000000000..deb59b5bd --- /dev/null +++ b/Converters/Navisworks/Speckle.Converters.NavisworksShared/Geometries/Primitives.cs @@ -0,0 +1,155 @@ +namespace Speckle.Converter.Navisworks.Geometry; + +public readonly struct SafeBoundingBox +{ + public SafeVertex Center { get; } + public SafeVertex Min { get; } + public SafeVertex Max { get; } + public double SizeX { get; } + public double SizeY { get; } + public double SizeZ { get; } + + public SafeBoundingBox(NAV.BoundingBox3D boundingBox) + { + if (boundingBox == null) + { + throw new ArgumentNullException(nameof(boundingBox)); + } + + Center = new SafeVertex(boundingBox.Center); + Min = new SafeVertex(boundingBox.Min); + Max = new SafeVertex(boundingBox.Max); + SizeX = boundingBox.Size.X; + SizeY = boundingBox.Size.Y; + SizeZ = boundingBox.Size.Z; + } +} + +/// +/// Safe structure to store vector coordinates without COM dependency +/// +public readonly struct SafeVector +{ + public double X { get; } + public double Y { get; } + public double Z { get; } + + public SafeVector(NAV.Vector3D vector) + { + if (vector == null) + { + throw new ArgumentNullException(nameof(vector)); + } + X = vector.X; + Y = vector.Y; + Z = vector.Z; + } + + public SafeVector(NAV.Point3D point) + { + if (point == null) + { + throw new ArgumentNullException(nameof(point)); + } + X = point.X; + Y = point.Y; + Z = point.Z; + } + + // Constructor for raw coordinates + public SafeVector(double x, double y, double z) + { + X = x; + Y = y; + Z = z; + } +} + +public readonly struct SafeVertex +{ + public double X { get; } + public double Y { get; } + public double Z { get; } + + public SafeVertex(NAV.Vector3D vector) + { + if (vector == null) + { + throw new ArgumentNullException(nameof(vector)); + } + + X = vector.X; + Y = vector.Y; + Z = vector.Z; + } + + public SafeVertex(NAV.Point3D point) + { + if (point == null) + { + throw new ArgumentNullException(nameof(point)); + } + X = point.X; + Y = point.Y; + Z = point.Z; + } + + // Constructor for raw coordinates + public SafeVertex(double x, double y, double z) + { + X = x; + Y = y; + Z = z; + } +} + +public readonly struct SafePoint +{ + public SafeVertex Vertex { get; } + + public SafePoint(NAV.Vector3D point) + { + if (point == null) + { + throw new ArgumentNullException(nameof(point)); + } + + Vertex = new SafeVertex(point); + } +} + +public readonly struct SafeTriangle +{ + public SafeVertex Vertex1 { get; } + public SafeVertex Vertex2 { get; } + public SafeVertex Vertex3 { get; } + + public SafeTriangle(NAV.Vector3D v1, NAV.Vector3D v2, NAV.Vector3D v3) + { + if (v1 == null || v2 == null || v3 == null) + { + throw new ArgumentNullException(nameof(v1)); + } + + Vertex1 = new SafeVertex(v1); + Vertex2 = new SafeVertex(v2); + Vertex3 = new SafeVertex(v3); + } +} + +public readonly struct SafeLine +{ + public SafeVertex Start { get; } + public SafeVertex End { get; } + + public SafeLine(NAV.Vector3D start, NAV.Vector3D end) + { + if (start == null || end == null) + { + throw new ArgumentNullException(nameof(start)); + } + + Start = new SafeVertex(start); + End = new SafeVertex(end); + } +} diff --git a/Converters/Navisworks/Speckle.Converters.NavisworksShared/Geometries/TransformMatrix.cs b/Converters/Navisworks/Speckle.Converters.NavisworksShared/Geometries/TransformMatrix.cs new file mode 100644 index 000000000..8fd7dde7d --- /dev/null +++ b/Converters/Navisworks/Speckle.Converters.NavisworksShared/Geometries/TransformMatrix.cs @@ -0,0 +1,59 @@ +using Speckle.DoubleNumerics; + +namespace Speckle.Converter.Navisworks.Geometry; + +public class Transforms +{ + private readonly double[] _elements; + + /// + /// Creates a new 4x4 matrix with the given elements in row-major order. + /// + /// An array of 16 elements representing the matrix. + public Transforms(double[] elements) + { + if (elements == null || elements.Length != 16) + { + throw new ArgumentException("A 4x4 matrix must have exactly 16 elements.", nameof(elements)); + } + + this._elements = elements; + } + + /// + /// Accesses the element at the given row and column. + /// + public double this[int row, int col] + { + get => _elements[row * 4 + col]; + set => _elements[row * 4 + col] = value; + } + + /// + /// Applies the transformation defined by this matrix to a 3D vector. + /// + public NAV.Vector3D Transform(Vector3 vector) + { + var t1 = this[0, 3] * vector.X + this[1, 3] * vector.Y + this[2, 3] * vector.Z + this[3, 3]; + if (t1 == 0) + { + t1 = 1; // Prevent division by zero + } + + var x = (this[0, 0] * vector.X + this[0, 1] * vector.Y + this[0, 2] * vector.Z + this[0, 3]) / t1; + var y = (this[1, 0] * vector.X + this[1, 1] * vector.Y + this[1, 2] * vector.Z + this[1, 3]) / t1; + var z = (this[2, 0] * vector.X + this[2, 1] * vector.Y + this[2, 2] * vector.Z + this[2, 3]) / t1; + + return new NAV.Vector3D(x, y, z); + } + + /// + /// Creates an identity matrix. + /// + public static Transforms Identity() => new([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]); + + /// + /// Returns the matrix as a flat array in row-major order. + /// + public double[] ToArray() => (double[])_elements.Clone(); +} diff --git a/Converters/Navisworks/Speckle.Converters.NavisworksShared/GlobalUsing.cs b/Converters/Navisworks/Speckle.Converters.NavisworksShared/GlobalUsing.cs new file mode 100644 index 000000000..778ce58c3 --- /dev/null +++ b/Converters/Navisworks/Speckle.Converters.NavisworksShared/GlobalUsing.cs @@ -0,0 +1,4 @@ +global using NAV = Autodesk.Navisworks.Api; +global using NavisworksApp = Autodesk.Navisworks.Api.Application; +global using SSC = Speckle.Sdk.Common; +global using SSM = Speckle.Sdk.Models; diff --git a/Converters/Navisworks/Speckle.Converters.NavisworksShared/Helpers/ColorConverter.cs b/Converters/Navisworks/Speckle.Converters.NavisworksShared/Helpers/ColorConverter.cs new file mode 100644 index 000000000..5c2925365 --- /dev/null +++ b/Converters/Navisworks/Speckle.Converters.NavisworksShared/Helpers/ColorConverter.cs @@ -0,0 +1,56 @@ +using Convert = System.Convert; +using Sys = System.Drawing; + +namespace Speckle.Converter.Navisworks.Helpers; + +public static class ColorConverter +{ + /// + /// Converts a Navisworks color to a System.Drawing.Color. + /// + /// The Navisworks color to convert + /// Optional transparency value (0.0 to 1.0) + /// A System.Drawing.Color with the converted values + public static Sys.Color NavisworksColorToColor(NAV.Color color, double? transparency = null) + { + var alpha = transparency.HasValue ? Convert.ToInt32((1.0 - transparency.Value) * 255) : 255; + + return Sys.Color.FromArgb( + alpha: alpha, + red: Convert.ToInt32(color.R * 255), + green: Convert.ToInt32(color.G * 255), + blue: Convert.ToInt32(color.B * 255) + ); + } + + /// + /// Converts RGB values to a color hash string. + /// + /// The Navisworks color + /// Optional transparency value + /// A unique hash string for the color + public static string GetColorHash(NAV.Color color, double? transparency = null) + { + var rgbValues = + Convert.ToInt32(color.R * 255) << 16 | Convert.ToInt32(color.G * 255) << 8 | Convert.ToInt32(color.B * 255); + + if (!transparency.HasValue) + { + return rgbValues.ToString(); + } + + var alpha = Convert.ToInt32((1.0 - transparency.Value) * 255); + return $"{rgbValues}_{alpha}"; + } + + /// + /// Gets a human-readable color name. + /// + /// The Navisworks color to name + /// A descriptive name for the color + public static string GetColorName(NAV.Color color) + { + var converted = NavisworksColorToColor(color); + return converted.IsKnownColor ? converted.Name : $"Custom_{converted.ToArgb():X8}"; + } +} diff --git a/Converters/Navisworks/Speckle.Converters.NavisworksShared/Helpers/ElementSelectionHelper.cs b/Converters/Navisworks/Speckle.Converters.NavisworksShared/Helpers/ElementSelectionHelper.cs new file mode 100644 index 000000000..a54ce84ea --- /dev/null +++ b/Converters/Navisworks/Speckle.Converters.NavisworksShared/Helpers/ElementSelectionHelper.cs @@ -0,0 +1,94 @@ +using Speckle.Converter.Navisworks.Constants; + +namespace Speckle.Converter.Navisworks.Helpers; + +/// +/// Provides extension methods for working with Navisworks ModelItem selections. +/// +public static class ElementSelectionHelper +{ + /// + /// Resolves a Navisworks to its unique index path representation. + /// + /// The model item to resolve. + /// + /// A string representing the model item's path. The path includes the model index and + /// a hierarchical path identifier, separated by the specified separator (e.g., "0.a.b"). + /// For root-level model items, only the model index is included. + /// + /// Thrown if is null. + public static string ResolveModelItemToIndexPath(NAV.ModelItem modelItem) + { + if (modelItem == null) + { + throw new ArgumentNullException(nameof(modelItem)); + } + + var modelItemPathId = NavisworksApp.ActiveDocument.Models.CreatePathId(modelItem); + + var pathIndex = + modelItemPathId.PathId == "a" + ? $"{modelItemPathId.ModelIndex}" // Root-level model item + : $"{modelItemPathId.ModelIndex}{PathConstants.SEPARATOR}{modelItemPathId.PathId}"; // Nested model item + + return pathIndex; + } + + /// + /// Extracts just the path part if the indexPath contains a material signature. + /// + /// The potentially composite path that might include a material signature + /// The clean path without any material signature + public static string GetCleanPath(string indexPath) + { + if (indexPath == null) + { + throw new ArgumentNullException(nameof(indexPath)); + } + + int separatorIndex = indexPath.IndexOf(PathConstants.MATERIAL_SEPARATOR, StringComparison.Ordinal); + return separatorIndex > 0 ? indexPath[..separatorIndex] : indexPath; + } + + public static NAV.ModelItem ResolveIndexPathToModelItem(string indexPath) + { + if (indexPath == null) + { + throw new ArgumentNullException(nameof(indexPath)); + } + + // Extract just the path part if the indexPath contains a material signature + string pathToResolve = GetCleanPath(indexPath); + + var indexPathParts = pathToResolve.Split(PathConstants.SEPARATOR); + + var modelIndex = int.Parse(indexPathParts[0]); + var pathId = string.Join(PathConstants.SEPARATOR.ToString(), indexPathParts.Skip(1)); + + // assign the first part of indexPathParts to modelIndex and parse it to int, the second part to pathId string + NAV.DocumentParts.ModelItemPathId modelItemPathId = new() { ModelIndex = modelIndex, PathId = pathId }; + + var modelItem = NavisworksApp.ActiveDocument.Models.ResolvePathId(modelItemPathId); + return modelItem; + } + + /// + /// Determines whether a Navisworks and all its ancestors are visible. + /// + /// The model item to check for visibility. + /// True if the item and all ancestors are visible; otherwise, false. + /// Thrown if is null. + public static bool IsElementVisible(NAV.ModelItem modelItem) + { + if (modelItem == null) + { + throw new ArgumentNullException(nameof(modelItem)); + } + + // Check visibility status for the item and its ancestors + return modelItem.AncestorsAndSelf.All(item => !item.IsHidden); + } + + public static List ResolveGeometryLeafNodes(NAV.ModelItem modelItem) => + modelItem.DescendantsAndSelf.Where(x => x.HasGeometry).ToList(); +} diff --git a/Converters/Navisworks/Speckle.Converters.NavisworksShared/Helpers/GeometryHelpers.cs b/Converters/Navisworks/Speckle.Converters.NavisworksShared/Helpers/GeometryHelpers.cs new file mode 100644 index 000000000..2ca3b3d6f --- /dev/null +++ b/Converters/Navisworks/Speckle.Converters.NavisworksShared/Helpers/GeometryHelpers.cs @@ -0,0 +1,16 @@ +namespace Speckle.Converter.Navisworks.Helpers; + +public static class GeometryHelpers +{ + /// + /// Compares two vectors to determine if they are approximately equal within a given tolerance. + /// + /// The first comparison vector. + /// The second comparison vector. + /// The tolerance value for the comparison. Default is 1e-9. + /// True if the vectors match within the tolerance; otherwise, false. + internal static bool VectorMatch(NAV.Vector3D vectorA, NAV.Vector3D vectorB, double tolerance = 1e-9) => + Math.Abs(vectorA.X - vectorB.X) < tolerance + && Math.Abs(vectorA.Y - vectorB.Y) < tolerance + && Math.Abs(vectorA.Z - vectorB.Z) < tolerance; +} diff --git a/Converters/Navisworks/Speckle.Converters.NavisworksShared/Helpers/HierarchyHelper.cs b/Converters/Navisworks/Speckle.Converters.NavisworksShared/Helpers/HierarchyHelper.cs new file mode 100644 index 000000000..47113e2c8 --- /dev/null +++ b/Converters/Navisworks/Speckle.Converters.NavisworksShared/Helpers/HierarchyHelper.cs @@ -0,0 +1,59 @@ +namespace Speckle.Converter.Navisworks.Helpers; + +/// +/// Helper class for extracting hierarchical context from Navisworks model items in a single traversal. +/// +public static class HierarchyHelper +{ + private const char PATH_DELIMITER = '>'; + + /// + /// Extracts the meaningful name and path from a ModelItem in a single traversal. + /// + public static (string Name, string Path) ExtractContext(NAV.ModelItem modelItem) + { + if (modelItem == null) + { + throw new ArgumentNullException(nameof(modelItem)); + } + + var ancestors = new List(); + var meaningfulName = string.Empty; + var current = modelItem; + + // Start with the root document name if available + if (modelItem.HasModel && !string.IsNullOrEmpty(modelItem.Model.FileName)) + { + ancestors.Add(Path.GetFileNameWithoutExtension(modelItem.Model.FileName)); + } + + // Traverse up the tree once, collecting both name and path information + while (current != null) + { + if (IsMeaningfulNode(current)) + { + // First meaningful name we find is our object name (if we haven't found one yet) + if (string.IsNullOrEmpty(meaningfulName)) + { + meaningfulName = current.DisplayName; + } + // Add to ancestors if it's not a duplicate + else if (ancestors.Count == 0 || ancestors.Last() != current.DisplayName) + { + ancestors.Add(current.DisplayName); + } + } + current = current.Parent; + } + + // Build path excluding the name we found + ancestors.Reverse(); + + var path = string.Join($" {PATH_DELIMITER} ", ancestors); + + return (meaningfulName, path); + } + + private static bool IsMeaningfulNode(NAV.ModelItem item) => + !string.IsNullOrEmpty(item.DisplayName) && (!item.HasGeometry || !string.IsNullOrEmpty(item.ClassDisplayName)); +} diff --git a/Converters/Navisworks/Speckle.Converters.NavisworksShared/Helpers/PrimitiveProcessor.cs b/Converters/Navisworks/Speckle.Converters.NavisworksShared/Helpers/PrimitiveProcessor.cs new file mode 100644 index 000000000..b646184fd --- /dev/null +++ b/Converters/Navisworks/Speckle.Converters.NavisworksShared/Helpers/PrimitiveProcessor.cs @@ -0,0 +1,173 @@ +using System.Collections.ObjectModel; +using Autodesk.Navisworks.Api.Interop.ComApi; +using Speckle.Converter.Navisworks.Geometry; +using Speckle.DoubleNumerics; + +namespace Speckle.Converter.Navisworks.Helpers; + +public class PrimitiveProcessor : InwSimplePrimitivesCB +{ + private readonly List _coords = []; + private List _faces = []; + private List _lines = []; + private List _points = []; + private List _triangles = []; + private bool IsUpright { get; set; } + + internal PrimitiveProcessor(bool isUpright) + { + IsUpright = isUpright; + SetCoords(new ReadOnlyCollection([])); + SetFaces([]); + SetTriangles([]); + SetLines([]); + SetPoints([]); + } + + public IReadOnlyList Coords => _coords.AsReadOnly(); + private IReadOnlyList Faces => _faces.AsReadOnly(); + public IReadOnlyList Triangles => _triangles.AsReadOnly(); + public IReadOnlyList Lines => _lines.AsReadOnly(); + public IReadOnlyList Points => _points.AsReadOnly(); + internal IEnumerable? LocalToWorldTransformation { get; set; } + + public void Line(InwSimpleVertex? v1, InwSimpleVertex? v2) + { + if (v1 == null || v2 == null) + { + return; + } + + using var vD1 = TransformVectorToOrientation( + ApplyTransformation(VectorFromVertex(v1), LocalToWorldTransformation), + IsUpright + ); + using var vD2 = TransformVectorToOrientation( + ApplyTransformation(VectorFromVertex(v2), LocalToWorldTransformation), + IsUpright + ); + + try + { + var safeLine = new SafeLine(vD1, vD2); + AddLine(safeLine); + } + catch (ArgumentException ex) + { + Console.WriteLine($"ArgumentException caught: {ex.Message}"); + } + catch (InvalidOperationException ex) + { + Console.WriteLine($"InvalidOperationException caught: {ex.Message}"); + } + } + + public void Point(InwSimpleVertex? v1) + { + if (v1 == null) + { + return; + } + + using var vD1 = TransformVectorToOrientation( + ApplyTransformation(VectorFromVertex(v1), LocalToWorldTransformation), + IsUpright + ); + + var safePoint = new SafePoint(vD1); + AddPoint(safePoint); + } + + // TODO: Needed for Splines + public void SnapPoint(InwSimpleVertex? v1) => Point(v1); + + public void Triangle(InwSimpleVertex? v1, InwSimpleVertex? v2, InwSimpleVertex? v3) + { + if (v1 == null || v2 == null || v3 == null) + { + return; + } + + using var vD1 = TransformVectorToOrientation( + ApplyTransformation(VectorFromVertex(v1), LocalToWorldTransformation), + IsUpright + ); + using var vD2 = TransformVectorToOrientation( + ApplyTransformation(VectorFromVertex(v2), LocalToWorldTransformation), + IsUpright + ); + using var vD3 = TransformVectorToOrientation( + ApplyTransformation(VectorFromVertex(v3), LocalToWorldTransformation), + IsUpright + ); + + // Capture values immediately in our safe struct + var safeTriangle = new SafeTriangle(vD1, vD2, vD3); + + var indexPointer = Faces.Count; + AddFace(3); + AddFaces([indexPointer + 0, indexPointer + 1, indexPointer + 2]); + AddCoords( + [ + safeTriangle.Vertex1.X, + safeTriangle.Vertex1.Y, + safeTriangle.Vertex1.Z, + safeTriangle.Vertex2.X, + safeTriangle.Vertex2.Y, + safeTriangle.Vertex2.Z, + safeTriangle.Vertex3.X, + safeTriangle.Vertex3.Y, + safeTriangle.Vertex3.Z + ] + ); + + AddTriangle(safeTriangle); + } + + private void SetCoords(IEnumerable coords) + { + _coords.Clear(); + _coords.AddRange(coords); + } + + private void AddCoords(IEnumerable coords) => _coords.AddRange(coords); + + private void SetFaces(List faces) => _faces = faces ?? throw new ArgumentNullException(nameof(faces)); + + private void AddFace(int face) => _faces.Add(face); + + private void AddFaces(IEnumerable faces) => _faces.AddRange(faces); + + private void SetTriangles(List triangles) => + _triangles = triangles ?? throw new ArgumentNullException(nameof(triangles)); + + private void AddTriangle(SafeTriangle triangle) => _triangles.Add(triangle); + + private void SetLines(List lines) => _lines = lines ?? throw new ArgumentNullException(nameof(lines)); + + private void AddLine(SafeLine line) => _lines.Add(line); + + private void SetPoints(List points) => _points = points ?? throw new ArgumentNullException(nameof(points)); + + private void AddPoint(SafePoint point) => _points.Add(point); + + private static NAV.Vector3D TransformVectorToOrientation(NAV.Vector3D v, bool isUpright) => + isUpright ? v : new NAV.Vector3D(v.X, -v.Z, v.Y); + + private static NAV.Vector3D ApplyTransformation(Vector3 vector3, IEnumerable? matrixStore) + { + var matrix = matrixStore!.ToList(); + var t1 = matrix[3] * vector3.X + matrix[7] * vector3.Y + matrix[11] * vector3.Z + matrix[15]; + var vectorDoubleX = (matrix[0] * vector3.X + matrix[4] * vector3.Y + matrix[8] * vector3.Z + matrix[12]) / t1; + var vectorDoubleY = (matrix[1] * vector3.X + matrix[5] * vector3.Y + matrix[9] * vector3.Z + matrix[13]) / t1; + var vectorDoubleZ = (matrix[2] * vector3.X + matrix[6] * vector3.Y + matrix[10] * vector3.Z + matrix[14]) / t1; + + return new NAV.Vector3D(vectorDoubleX, vectorDoubleY, vectorDoubleZ); + } + + private static Vector3 VectorFromVertex(InwSimpleVertex v) + { + var arrayV = (Array)v.coord; + return new Vector3((float)arrayV.GetValue(1), (float)arrayV.GetValue(2), (float)arrayV.GetValue(3)); + } +} diff --git a/Converters/Navisworks/Speckle.Converters.NavisworksShared/Helpers/PropertyHelpers.cs b/Converters/Navisworks/Speckle.Converters.NavisworksShared/Helpers/PropertyHelpers.cs new file mode 100644 index 000000000..a9e771d64 --- /dev/null +++ b/Converters/Navisworks/Speckle.Converters.NavisworksShared/Helpers/PropertyHelpers.cs @@ -0,0 +1,106 @@ +using System.Globalization; +using System.Text.RegularExpressions; +using Speckle.Objects.Geometry; + +namespace Speckle.Converter.Navisworks.Helpers; + +public static class PropertyHelpers +{ + private static readonly HashSet s_excludedCategories = ["Geometry", "Metadata"]; + + private static readonly Dictionary> s_typeHandlers = + new() + { + { NAV.VariantDataType.Boolean, (value, _) => value.ToBoolean() }, + { NAV.VariantDataType.DisplayString, (value, _) => value.ToDisplayString() }, + { NAV.VariantDataType.IdentifierString, (value, _) => value.ToIdentifierString() }, + { NAV.VariantDataType.Int32, (value, _) => value.ToInt32() }, + { NAV.VariantDataType.Double, (value, _) => value.ToDouble() }, + { NAV.VariantDataType.DoubleAngle, (value, _) => value.ToDoubleAngle() }, + { NAV.VariantDataType.DoubleArea, (value, _) => value.ToDoubleArea() }, + { NAV.VariantDataType.DoubleLength, (value, _) => value.ToDoubleLength() }, + { NAV.VariantDataType.DoubleVolume, (value, _) => value.ToDoubleVolume() }, + { NAV.VariantDataType.DateTime, (value, _) => value.ToDateTime().ToString(CultureInfo.InvariantCulture) }, + { NAV.VariantDataType.NamedConstant, (value, _) => value.ToNamedConstant().DisplayName }, + { NAV.VariantDataType.None, (_, _) => null }, + { NAV.VariantDataType.Point2D, (_, _) => null }, + { + NAV.VariantDataType.Point3D, + (value, units) => + { + var point = value.ToPoint3D(); + var pointProperty = new Point(point.X, point.Y, point.Z, units); + return pointProperty.ToString(); + } + } + }; + + internal static dynamic? ConvertPropertyValue(NAV.VariantData value, string units) + { + if (s_typeHandlers.TryGetValue(value.DataType, out var handler)) + { + return handler(value, units); + } + + // Default case for unsupported types + return value.DataType == NAV.VariantDataType.None || value.DataType == NAV.VariantDataType.Point2D + ? null + : value.ToString(); + } + + /// + /// Adds a property to an object (either a Base object or a Dictionary) if the value is not null or empty. + /// + /// The object to which the property is to be added. Can be either a Base object or a Dictionary. + /// The name of the property to add. + /// The value of the property. + internal static void AddPropertyIfNotNullOrEmpty(object baseObject, string propertyName, object value) + { + switch (value) + { + case string stringValue: + { + if (!string.IsNullOrEmpty(stringValue)) + { + AssignProperty(baseObject, propertyName, value); + } + + break; + } + default: + AssignProperty(baseObject, propertyName, value); + break; + } + } + + /// + /// Helper method to assign the property to the base object or dictionary. + /// + private static void AssignProperty(object baseObject, string propertyName, object value) + { + switch (baseObject) + { + case SSM.Base baseObj: + baseObj[propertyName] = value; + break; + case Dictionary baseDict: + baseDict[propertyName] = value; + break; + default: + throw new ArgumentException("Unsupported object type", nameof(baseObject)); + } + } + + /// + /// Sanitizes property names by replacing invalid characters with underscores. + /// + internal static string SanitizePropertyName(string name) => + // Regex pattern from speckle-sharp/Core/Core/Models/DynamicBase.cs IsPropNameValid + name == "Item" + // Item is a reserved term for Indexed Properties: https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/indexers/using-indexers + ? "Item" + : Regex.Replace(name, @"[\.\/\s]", "_"); + + internal static bool IsCategoryToBeSkipped(NAV.PropertyCategory propertyCategory) => + s_excludedCategories.Contains(propertyCategory.DisplayName); +} diff --git a/Converters/Navisworks/Speckle.Converters.NavisworksShared/PathConstants.cs b/Converters/Navisworks/Speckle.Converters.NavisworksShared/PathConstants.cs new file mode 100644 index 000000000..df6aa0a3e --- /dev/null +++ b/Converters/Navisworks/Speckle.Converters.NavisworksShared/PathConstants.cs @@ -0,0 +1,8 @@ +namespace Speckle.Converter.Navisworks.Constants; + +public static class PathConstants +{ + public const char SEPARATOR = '/'; + public const string MATERIAL_SEPARATOR = "::"; + public const string SET_SEPARATOR = ">"; +} diff --git a/Converters/Navisworks/Speckle.Converters.NavisworksShared/Services/NavisworksToSpeckleUnitConverter.cs b/Converters/Navisworks/Speckle.Converters.NavisworksShared/Services/NavisworksToSpeckleUnitConverter.cs new file mode 100644 index 000000000..8f75388a9 --- /dev/null +++ b/Converters/Navisworks/Speckle.Converters.NavisworksShared/Services/NavisworksToSpeckleUnitConverter.cs @@ -0,0 +1,26 @@ +using Speckle.Converters.Common; +using Speckle.Sdk.Common.Exceptions; + +namespace Speckle.Converter.Navisworks.Services; + +public sealed class NavisworksToSpeckleUnitConverter : IHostToSpeckleUnitConverter +{ + private readonly Dictionary _unitMapping = []; + + public NavisworksToSpeckleUnitConverter() + { + _unitMapping[NAV.Units.Millimeters] = SSC.Units.Millimeters; + _unitMapping[NAV.Units.Centimeters] = SSC.Units.Centimeters; + _unitMapping[NAV.Units.Meters] = SSC.Units.Meters; + _unitMapping[NAV.Units.Inches] = SSC.Units.Inches; + _unitMapping[NAV.Units.Feet] = SSC.Units.Feet; + _unitMapping[NAV.Units.Miles] = SSC.Units.Miles; + _unitMapping[NAV.Units.Kilometers] = SSC.Units.Kilometers; + _unitMapping[NAV.Units.Yards] = SSC.Units.Yards; + } + + public string ConvertOrThrow(NAV.Units hostUnit) => + _unitMapping.TryGetValue(hostUnit, out string? value) + ? value + : throw new UnitNotSupportedException($"The Unit System \"{hostUnit}\" is unsupported."); +} diff --git a/Converters/Navisworks/Speckle.Converters.NavisworksShared/Settings/ConversionModes.cs b/Converters/Navisworks/Speckle.Converters.NavisworksShared/Settings/ConversionModes.cs new file mode 100644 index 000000000..854c2ada6 --- /dev/null +++ b/Converters/Navisworks/Speckle.Converters.NavisworksShared/Settings/ConversionModes.cs @@ -0,0 +1,51 @@ +using System.Diagnostics.CodeAnalysis; + +namespace Speckle.Converter.Navisworks.Settings; + +/// +/// Defines the representation mode to be used during conversion. +/// +[SuppressMessage("ReSharper", "InconsistentNaming")] +public enum RepresentationMode +{ + /// + /// Uses the current active representation (e.g., overrides or temporary settings). + /// + Active, + + /// + /// Preserves the original representation as defined in the source data. + /// + Original, + + /// + /// Applies a fixed or user-defined representation, overriding others. + /// + Permanent +} + +/// +/// Specifies the origin mode, which defines the basis of a transformation +/// applied to the model before conversion. The transformation aligns +/// the model's origin point to a base point. +/// +[SuppressMessage("ReSharper", "InconsistentNaming")] +public enum OriginMode +{ + /// + /// Uses the model origin as the base point. This is the default mode. + /// No transformation is applied other than converting from the local + /// coordinate system to the world coordinate system. + /// + ModelOrigin, + + /// + /// Uses a user-defined project base point as the base point for the transformation. + /// + ProjectBasePoint, + + /// + /// Uses the center of the model's bounding box as the base point for the transformation. + /// + BoundingBoxCenter +} diff --git a/Converters/Navisworks/Speckle.Converters.NavisworksShared/Settings/NavisworksConversionSettings.cs b/Converters/Navisworks/Speckle.Converters.NavisworksShared/Settings/NavisworksConversionSettings.cs new file mode 100644 index 000000000..6705a12ef --- /dev/null +++ b/Converters/Navisworks/Speckle.Converters.NavisworksShared/Settings/NavisworksConversionSettings.cs @@ -0,0 +1,28 @@ +using Speckle.Converter.Navisworks.Geometry; + +namespace Speckle.Converter.Navisworks.Settings; + +/// +/// Represents the settings used for Navisworks conversions. +/// +public record NavisworksConversionSettings(Derived Derived, User User); + +// Derived from Navisworks Application +public record Derived( + NAV.Document Document, // The active Navisworks document to be processed. + SafeBoundingBox ModelBoundingBox, // The bounding box of the model. + SafeVector TransformVector, // Transformation vector applied to the model. + bool IsUpright, // Indicates if the model's orientation is upright relative to canonical up. + string SpeckleUnits // Units used in Speckle for standardised measurements. +); + +// Optional settings for conversion to be offered in UI +public record User( + OriginMode OriginMode, // Defines the base point for transformations. + bool IncludeInternalProperties, // Whether to include internal Navisworks properties in the output. + bool ConvertHiddenElements, // Whether to include hidden elements during the conversion process. + RepresentationMode VisualRepresentationMode, // Specifies the visual representation mode. + bool CoalescePropertiesFromFirstObjectAncestor, // Whether to merge properties from the first object ancestor. + bool ExcludeProperties, // Whether to exclude properties from the output. + bool PreserveModelHierarchy // Whether to maintain the full model hierarchy during conversion. +); diff --git a/Converters/Navisworks/Speckle.Converters.NavisworksShared/Settings/NavisworksConversionSettingsFactory.cs b/Converters/Navisworks/Speckle.Converters.NavisworksShared/Settings/NavisworksConversionSettingsFactory.cs new file mode 100644 index 000000000..e71b16d38 --- /dev/null +++ b/Converters/Navisworks/Speckle.Converters.NavisworksShared/Settings/NavisworksConversionSettingsFactory.cs @@ -0,0 +1,142 @@ +using Microsoft.Extensions.Logging; +using Speckle.Converter.Navisworks.Geometry; +using Speckle.Converter.Navisworks.Helpers; +using Speckle.Converters.Common; +using Speckle.InterfaceGenerator; + +namespace Speckle.Converter.Navisworks.Settings; + +[GenerateAutoInterface] +public class NavisworksConversionSettingsFactory : INavisworksConversionSettingsFactory +{ + private readonly IConverterSettingsStore _settingsStore; + private readonly ILogger _logger; + private readonly IHostToSpeckleUnitConverter _unitsConverter; + + private NAV.Document? _document; + private SafeBoundingBox _modelBoundingBox; + private bool _convertHiddenElements; + + public NavisworksConversionSettingsFactory( + IHostToSpeckleUnitConverter unitsConverter, + IConverterSettingsStore settingsStore, + ILogger logger + ) + { + _logger = logger; + _settingsStore = settingsStore; + _unitsConverter = unitsConverter; + } + + public NavisworksConversionSettings Current => _settingsStore.Current; + + private static readonly NAV.Vector3D s_canonicalUp = new(0, 0, 1); + + private OriginMode _originMode; + + /// + /// Creates a new instance of NavisworksConversionSettings with calculated values. + /// + /// + /// Thrown when no active document is found or document units cannot be converted. + /// + public NavisworksConversionSettings Create( + OriginMode originMode, + RepresentationMode visualRepresentationMode, + bool convertHiddenElements, + bool includeInternalProperties, + bool preserveModelHierarchy + ) + { + _convertHiddenElements = convertHiddenElements; + _originMode = originMode; + + // Initialize document and validate + InitializeDocument(); + + if (_document == null) + { + throw new InvalidOperationException("No active document found."); + } + + var units = _unitsConverter.ConvertOrThrow(_document.Units); + if (string.IsNullOrEmpty(units)) + { + throw new InvalidOperationException("Document units could not be converted."); + } + + // Calculate the transformation vector based on the origin mode + var transformVector = CalculateTransformVector(); + var isUpright = GeometryHelpers.VectorMatch(_document.UpVector, s_canonicalUp); + + return new NavisworksConversionSettings( + // Derived from Navisworks Application + new Derived( + Document: _document, + ModelBoundingBox: _modelBoundingBox, + TransformVector: transformVector, + IsUpright: isUpright, + SpeckleUnits: units + ), + // Optional settings for conversion to be offered in UI + new User( + OriginMode: _originMode, + IncludeInternalProperties: includeInternalProperties, + ConvertHiddenElements: _convertHiddenElements, + VisualRepresentationMode: visualRepresentationMode, + CoalescePropertiesFromFirstObjectAncestor: false, // Not yet exposed in the UI + ExcludeProperties: false, // Not yet exposed in the UI + PreserveModelHierarchy: preserveModelHierarchy + ) + ); + } + + private void InitializeDocument() + { + _document = NavisworksApp.ActiveDocument ?? throw new InvalidOperationException("No active document found."); + _logger.LogInformation("Creating settings for document: {DocumentName}", _document.Title); + _modelBoundingBox = new SafeBoundingBox(_document.GetBoundingBox(_convertHiddenElements)); + } + + private SafeVector CalculateTransformVector() => + _originMode switch + { + OriginMode.ProjectBasePoint => CalculateProjectBasePointTransform(), + OriginMode.BoundingBoxCenter => CalculateBoundingBoxTransform(), + OriginMode.ModelOrigin => new SafeVector(0.0, 0.0, 0.0), // Default identity transform + _ => throw new NotSupportedException($"OriginMode {_originMode} is not supported.") + }; + + /// + /// Calculates the transformation vector based on the project base point. + /// + /// The calculated transformation vector. + /// + /// This uses mocked project base point data and should be replaced with actual logic + /// when finally integrating with UI or external configurations. + /// + private SafeVector CalculateProjectBasePointTransform() + { + // TODO: Replace with actual logic to fetch project base point and units from UI or settings + var projectBasePoint = new SafeVector(10, 20, 0); + // ReSharper disable once ConvertToConstant.Local + var projectBasePointUnits = NAV.Units.Meters; + + var scale = NAV.UnitConversion.ScaleFactor(projectBasePointUnits, _document!.Units); + + // The transformation vector is the negative of the project base point, scaled to the source units. + // These units are independent of the Speckle units, and because they are from user input. + return new SafeVector(-projectBasePoint.X * scale, -projectBasePoint.Y * scale, 0); + } + + /// + /// Calculates the transformation vector based on the bounding box center offset from the origin. + /// + /// The calculated transformation vector. + /// + /// This uses the document active model bounding box center as the base point for the transformation. + /// Assumes no translation in the Z-axis. + /// + private SafeVector CalculateBoundingBoxTransform() => + new(-_modelBoundingBox.Center.X, -_modelBoundingBox.Center.Y, 0); +} diff --git a/Converters/Navisworks/Speckle.Converters.NavisworksShared/Speckle.Converters.NavisworksShared.projitems b/Converters/Navisworks/Speckle.Converters.NavisworksShared/Speckle.Converters.NavisworksShared.projitems new file mode 100644 index 000000000..d791e58b9 --- /dev/null +++ b/Converters/Navisworks/Speckle.Converters.NavisworksShared/Speckle.Converters.NavisworksShared.projitems @@ -0,0 +1,42 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + 6886282C-EFA5-4ED0-85BD-2883E23E092B + + + Speckle.Converters.NavisworksShared + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Converters/Navisworks/Speckle.Converters.NavisworksShared/Speckle.Converters.NavisworksShared.shproj b/Converters/Navisworks/Speckle.Converters.NavisworksShared/Speckle.Converters.NavisworksShared.shproj new file mode 100644 index 000000000..016540e63 --- /dev/null +++ b/Converters/Navisworks/Speckle.Converters.NavisworksShared/Speckle.Converters.NavisworksShared.shproj @@ -0,0 +1,12 @@ + + + + {23830613-87F8-4655-B523-72189A597962} + + + + + + + diff --git a/Converters/Navisworks/Speckle.Converters.NavisworksShared/ToSpeckle/NavisworksRootToSpeckleConverter.cs b/Converters/Navisworks/Speckle.Converters.NavisworksShared/ToSpeckle/NavisworksRootToSpeckleConverter.cs new file mode 100644 index 000000000..eb1bfb10b --- /dev/null +++ b/Converters/Navisworks/Speckle.Converters.NavisworksShared/ToSpeckle/NavisworksRootToSpeckleConverter.cs @@ -0,0 +1,53 @@ +using Microsoft.Extensions.Logging; +using Speckle.Converter.Navisworks.Helpers; +using Speckle.Converter.Navisworks.Settings; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Converters.Common.Registration; +using Speckle.Sdk.Models; + +namespace Speckle.Converter.Navisworks.ToSpeckle; + +/// +/// Converts Navisworks ModelItem objects to Speckle Base objects. +/// +public class NavisworksRootToSpeckleConverter : IRootToSpeckleConverter +{ + private readonly IConverterManager _toSpeckle; + private readonly IConverterSettingsStore _settingsStore; + private readonly ILogger _logger; + + public NavisworksRootToSpeckleConverter( + IConverterSettingsStore settingsStore, + ILogger logger, + IConverterManager toSpeckle + ) + { + _toSpeckle = toSpeckle; + _logger = logger; + _settingsStore = settingsStore; + } + + public Base Convert(object target) + { + if (target == null) + { + throw new ArgumentNullException(nameof(target)); + } + + if (target is not NAV.ModelItem modelItem) + { + throw new InvalidOperationException($"The target object is not a ModelItem. It's a ${target.GetType()}."); + } + + Type type = target.GetType(); + + var objectConverter = _toSpeckle.ResolveConverter(type, true); + + Base result = objectConverter.Convert(modelItem); + + result.applicationId = ElementSelectionHelper.ResolveModelItemToIndexPath(modelItem); + + return result; + } +} diff --git a/Converters/Navisworks/Speckle.Converters.NavisworksShared/ToSpeckle/Raw/BoundingBoxToSpeckleRawConverter.cs b/Converters/Navisworks/Speckle.Converters.NavisworksShared/ToSpeckle/Raw/BoundingBoxToSpeckleRawConverter.cs new file mode 100644 index 000000000..43838ff4a --- /dev/null +++ b/Converters/Navisworks/Speckle.Converters.NavisworksShared/ToSpeckle/Raw/BoundingBoxToSpeckleRawConverter.cs @@ -0,0 +1,52 @@ +using Speckle.Converter.Navisworks.Settings; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Objects.Geometry; +using Speckle.Objects.Primitive; + +namespace Speckle.Converter.Navisworks.ToSpeckle.Raw; + +public class BoundingBoxToSpeckleRawConverter : ITypedConverter +{ + private readonly IConverterSettingsStore _settingsStore; + + public BoundingBoxToSpeckleRawConverter(IConverterSettingsStore settingsStore) + { + _settingsStore = settingsStore; + } + + public Box Convert(object target) => Convert((NAV.BoundingBox3D)target); + + public Box Convert(NAV.BoundingBox3D? target) + { + if (target == null) + { + return default!; // returns null for reference types (Box is a reference type) + } + + var minPoint = target.Min; + var maxPoint = target.Max; + + var units = _settingsStore.Current.Derived.SpeckleUnits; + + var basePlane = new Plane + { + origin = new Point(minPoint.X, minPoint.Y, minPoint.Z, units), + normal = new Vector(0, 0, 1, units), + xdir = new Vector(1, 0, 0, units), + ydir = new Vector(0, 1, 0, units), + units = units + }; + + var boundingBox = new Box + { + units = units, + plane = basePlane, + xSize = new Interval() { start = minPoint.X, end = maxPoint.X }, + ySize = new Interval() { start = minPoint.Y, end = maxPoint.Y }, + zSize = new Interval() { start = minPoint.Z, end = maxPoint.Z } + }; + + return boundingBox; + } +} diff --git a/Converters/Navisworks/Speckle.Converters.NavisworksShared/ToSpeckle/Raw/GeometryToSpeckleConverter.cs b/Converters/Navisworks/Speckle.Converters.NavisworksShared/ToSpeckle/Raw/GeometryToSpeckleConverter.cs new file mode 100644 index 000000000..cf440f256 --- /dev/null +++ b/Converters/Navisworks/Speckle.Converters.NavisworksShared/ToSpeckle/Raw/GeometryToSpeckleConverter.cs @@ -0,0 +1,245 @@ +using System.Runtime.InteropServices; +using Autodesk.Navisworks.Api.Interop.ComApi; +using Speckle.Converter.Navisworks.Extensions; +using Speckle.Converter.Navisworks.Geometry; +using Speckle.Converter.Navisworks.Helpers; +using Speckle.Converter.Navisworks.Settings; +using Speckle.Objects.Geometry; +using Speckle.Sdk.Models; +using ComApiBridge = Autodesk.Navisworks.Api.ComApi.ComApiBridge; + +namespace Speckle.Converter.Navisworks.ToSpeckle; + +/// +/// Converts Navisworks geometry to Speckle displayable geometry. +/// +/// Note: This class does not implement ITypedConverter{ModelGeometry, Base} because Navisworks geometry +/// conversion requires COM interop access that isn't available through the public ModelGeometry class. +/// The conversion process requires: +/// 1. Convert ModelItem to InwOaPath3 via ComApiBridge +/// 2. Use that to get InwOaFragmentList +/// 3. Process each InwOaFragment3 to generate primitives +/// 4. Convert those primitives to Speckle geometry with appropriate transforms +/// +public class GeometryToSpeckleConverter +{ + private readonly NavisworksConversionSettings _settings; + private readonly bool _isUpright; + private readonly SafeVector _transformVector; + private const double SCALE = 1.0; // Default scale factor + + public GeometryToSpeckleConverter(NavisworksConversionSettings settings) + { + _settings = settings ?? throw new ArgumentNullException(nameof(settings)); + _isUpright = settings.Derived.IsUpright; + _transformVector = settings.Derived.TransformVector; + } + + /// + /// Converts a ModelItem's geometry to Speckle display geometry by accessing the underlying COM objects. + /// Applies necessary transformations and unit scaling. + /// + internal List Convert(NAV.ModelItem modelItem) + { + if (modelItem == null) + { + throw new ArgumentNullException(nameof(modelItem)); + } + + if (!modelItem.HasGeometry) + { + return []; + } + + var comSelection = ComApiBridge.ToInwOpSelection([modelItem]); + try + { + var fragmentStack = new Stack(); + var paths = comSelection.Paths(); + try + { + // Populate fragment stack with all fragments + foreach (InwOaPath path in paths) + { + CollectFragments(path, fragmentStack); + } + + return ProcessFragments(fragmentStack, paths); + } + finally + { + if (paths != null) + { + Marshal.ReleaseComObject(paths); + } + } + } + finally + { + if (comSelection != null) + { + Marshal.ReleaseComObject(comSelection); + } + } + } + + private static void CollectFragments(InwOaPath path, Stack fragmentStack) + { + var fragments = path.Fragments(); + foreach (var fragment in fragments.OfType()) + { + if (fragment.path?.ArrayData is not Array pathData1 || path.ArrayData is not Array pathData2) + { + continue; + } + + var pathArray1 = pathData1.ToArray(); + var pathArray2 = pathData2.ToArray(); + + if (pathArray1.Length == pathArray2.Length && pathArray1.SequenceEqual(pathArray2)) + { + fragmentStack.Push(fragment); + } + } + } + + private List ProcessFragments(Stack fragmentStack, InwSelectionPathsColl paths) + { + var callbackListeners = new List(); + + foreach (InwOaPath path in paths) + { + var processor = new PrimitiveProcessor(_isUpright); + + foreach (var fragment in fragmentStack) + { + if (!ValidateFragmentPath(fragment, path)) + { + continue; + } + + var matrix = fragment.GetLocalToWorldMatrix(); + var transform = matrix as InwLTransform3f3; + if (transform?.Matrix is not Array matrixArray) + { + continue; + } + + processor.LocalToWorldTransformation = ConvertArrayToDouble(matrixArray); + fragment.GenerateSimplePrimitives(nwEVertexProperty.eNORMAL, processor); + } + + callbackListeners.Add(processor); + } + + var baseGeometries = ProcessGeometries(callbackListeners); + + return baseGeometries; + } + + private static bool ValidateFragmentPath(InwOaFragment3 fragment, InwOaPath path) + { + if (fragment.path?.ArrayData is not Array fragmentPathData || path.ArrayData is not Array pathData) + { + return false; + } + + return IsSameFragmentPath(fragmentPathData, pathData); + } + + private List ProcessGeometries(List processors) + { + var baseGeometries = new List(); + + foreach (var processor in processors) + { + if (processor.Triangles.Count > 0) + { + var mesh = CreateMesh(processor.Triangles); + baseGeometries.Add(mesh); + } + + if (processor.Lines.Count > 0) + { + var lines = CreateLines(processor.Lines); + baseGeometries.AddRange(lines); + } + } + + return baseGeometries; + } + + private Mesh CreateMesh(IReadOnlyList triangles) + { + var vertices = new List(); + var faces = new List(); + + for (var t = 0; t < triangles.Count; t++) + { + var triangle = triangles[t]; + + // No need to worry about disposal of COM across boundaries - we're working with our safe structs + vertices.AddRange( + [ + (triangle.Vertex1.X + _transformVector.X) * SCALE, + (triangle.Vertex1.Y + _transformVector.Y) * SCALE, + (triangle.Vertex1.Z + _transformVector.Z) * SCALE, + (triangle.Vertex2.X + _transformVector.X) * SCALE, + (triangle.Vertex2.Y + _transformVector.Y) * SCALE, + (triangle.Vertex2.Z + _transformVector.Z) * SCALE, + (triangle.Vertex3.X + _transformVector.X) * SCALE, + (triangle.Vertex3.Y + _transformVector.Y) * SCALE, + (triangle.Vertex3.Z + _transformVector.Z) * SCALE + ] + ); + faces.AddRange([0, t * 3, t * 3 + 1, t * 3 + 2]); + } + + return new Mesh + { + vertices = vertices, + faces = faces, + units = _settings.Derived.SpeckleUnits + }; + } + + private List CreateLines(IReadOnlyList lines) => + ( + from line in lines + select new Line + { + start = new Point( + (line.Start.X + _transformVector.X) * SCALE, + (line.Start.Y + _transformVector.Y) * SCALE, + (line.Start.Z + _transformVector.Z) * SCALE, + _settings.Derived.SpeckleUnits + ), + end = new Point( + (line.End.X + _transformVector.X) * SCALE, + (line.End.Y + _transformVector.Y) * SCALE, + (line.End.Z + _transformVector.Z) * SCALE, + _settings.Derived.SpeckleUnits + ), + units = _settings.Derived.SpeckleUnits + } + ).ToList(); + + private static double[]? ConvertArrayToDouble(Array arr) + { + if (arr.Rank != 1) + { + throw new ArgumentException("The input array must have a rank of 1."); + } + + var doubleArray = new double[arr.GetLength(0)]; + for (var ix = arr.GetLowerBound(0); ix <= arr.GetUpperBound(0); ++ix) + { + doubleArray[ix - arr.GetLowerBound(0)] = (double)arr.GetValue(ix); + } + + return doubleArray; + } + + private static bool IsSameFragmentPath(Array a1, Array a2) => + a1.Length == a2.Length && a1.Cast().SequenceEqual(a2.Cast()); +} diff --git a/Converters/Navisworks/Speckle.Converters.NavisworksShared/ToSpeckle/TopLevel/ModelItemTopLevelConverterToSpeckle.cs b/Converters/Navisworks/Speckle.Converters.NavisworksShared/ToSpeckle/TopLevel/ModelItemTopLevelConverterToSpeckle.cs new file mode 100644 index 000000000..e99a221e3 --- /dev/null +++ b/Converters/Navisworks/Speckle.Converters.NavisworksShared/ToSpeckle/TopLevel/ModelItemTopLevelConverterToSpeckle.cs @@ -0,0 +1,99 @@ +using Speckle.Converter.Navisworks.Settings; +using Speckle.Converter.Navisworks.ToSpeckle.PropertyHandlers; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Objects.Data; +using Speckle.Sdk.Models; +using Speckle.Sdk.Models.Collections; + +namespace Speckle.Converter.Navisworks.ToSpeckle; + +/// +/// Converts Navisworks ModelItem objects to Speckle Base objects. +/// +[NameAndRankValue(typeof(NAV.ModelItem), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class ModelItemToToSpeckleConverter : IToSpeckleTopLevelConverter +{ + private readonly StandardPropertyHandler _standardHandler; + private readonly HierarchicalPropertyHandler _hierarchicalHandler; + private readonly IConverterSettingsStore _settingsStore; + private readonly DisplayValueExtractor _displayValueExtractor; + + public ModelItemToToSpeckleConverter( + IConverterSettingsStore settingsStore, + StandardPropertyHandler standardHandler, + HierarchicalPropertyHandler hierarchicalHandler, + DisplayValueExtractor displayValueExtractor + ) + { + _settingsStore = settingsStore; + _standardHandler = standardHandler; + _hierarchicalHandler = hierarchicalHandler; + _displayValueExtractor = displayValueExtractor; + } + + /// + /// Converts a Navisworks object to a Speckle Base object. + /// + /// The object to convert. + /// The converted Speckle Base object. + public Base Convert(object target) => + target == null ? throw new ArgumentNullException(nameof(target)) : Convert((NAV.ModelItem)target); + + // Converts a Navisworks ModelItem into a Speckle Base object + private Base Convert(NAV.ModelItem target) + { + IPropertyHandler handler = ShouldMergeProperties(target) ? _hierarchicalHandler : _standardHandler; + var name = GetObjectName(target); + + return target.HasGeometry + ? CreateGeometryObject(target, name, handler) + : CreateNonGeometryObject(target, name, handler); + } + + // There are in fact only two types of objects: geometry and non-geometry, the latter being collections of other objects + private NavisworksObject CreateGeometryObject(NAV.ModelItem target, string name, IPropertyHandler propertyHandler) => + new() + { + name = name, + displayValue = _displayValueExtractor.GetDisplayValue(target), + properties = _settingsStore.Current.User.ExcludeProperties ? [] : propertyHandler.GetProperties(target), + units = _settingsStore.Current.Derived.SpeckleUnits, + }; + + private Collection CreateNonGeometryObject(NAV.ModelItem target, string name, IPropertyHandler propertyHandler) => + new() + { + name = name, + elements = [], + ["properties"] = _settingsStore.Current.User.ExcludeProperties ? [] : propertyHandler.GetProperties(target), + }; + + /// + /// Determines whether properties should be merged from ancestors. + /// Only geometry objects should have their properties merged.... For now. + /// + private static bool ShouldMergeProperties(NAV.ModelItem target) => target.HasGeometry; + + private static string GetObjectName(NAV.ModelItem target) + { + var targetName = target.DisplayName; + + var firstObjectAncestor = target.FindFirstObjectAncestor(); + + // while the target node name is null keep cycling through parent objects until displayname is not null or empty OR object is firstObjectAncestor + + while (string.IsNullOrEmpty(targetName) && target != firstObjectAncestor) + { + target = target.Parent; + targetName = target.DisplayName; + } + + if (string.IsNullOrEmpty(targetName)) + { + targetName = "Unnamed model item"; + } + + return targetName; + } +} diff --git a/Converters/Revit/Speckle.Converters.Revit2022.Tests/packages.lock.json b/Converters/Revit/Speckle.Converters.Revit2022.Tests/packages.lock.json index b6dc9391a..cfce2d497 100644 --- a/Converters/Revit/Speckle.Converters.Revit2022.Tests/packages.lock.json +++ b/Converters/Revit/Speckle.Converters.Revit2022.Tests/packages.lock.json @@ -245,11 +245,6 @@ "resolved": "13.0.1", "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" }, - "Speckle.DoubleNumerics": { - "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w==" - }, "Speckle.Newtonsoft.Json": { "type": "Transitive", "resolved": "13.0.2", @@ -340,7 +335,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.191, )" + "Speckle.Objects": "[3.1.1, )" } }, "speckle.testing": { @@ -374,36 +369,42 @@ "resolved": "2.2.0", "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "+m7jRFm0ABbkcSz2UphdxAsislY10IpQ1u79c8a3aVvegLjnsVQZ1sXfRIRO1aFdulkhjYKXYpB3N9M8Z+epgQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.191" + "Speckle.Sdk": "3.1.1" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "VVT3LJiYlhqnggxxdeTt1QLrqfxDb044x0yX6kxS9b5MRzeDvK2Vz86pLDfuHs+SXvDimRVfYx1M42IW/aPcTQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.CSharp": "4.7.0", "Microsoft.Data.Sqlite": "7.0.5", "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", "Microsoft.Extensions.Logging": "2.2.0", - "Speckle.DoubleNumerics": "4.0.1", + "Speckle.DoubleNumerics": "4.1.0", "Speckle.Newtonsoft.Json": "13.0.2", - "Speckle.Sdk.Dependencies": "3.1.0-dev.191" + "Speckle.Sdk.Dependencies": "3.1.1" } }, "Speckle.Sdk.Dependencies": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "EmEOyjsGsNi56Z/ZoBOn8WirTmIT2yqWvlUeUh0BSPX2TDMZXHTKOM/kHmP6HSd10KVFn2Zo/ItY7/K9iRtL1Q==" + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" } } } diff --git a/Converters/Revit/Speckle.Converters.Revit2022/packages.lock.json b/Converters/Revit/Speckle.Converters.Revit2022/packages.lock.json index 2f288314a..2719be676 100644 --- a/Converters/Revit/Speckle.Converters.Revit2022/packages.lock.json +++ b/Converters/Revit/Speckle.Converters.Revit2022/packages.lock.json @@ -159,11 +159,6 @@ "resolved": "8.0.0", "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" }, - "Speckle.DoubleNumerics": { - "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w==" - }, "Speckle.Newtonsoft.Json": { "type": "Transitive", "resolved": "13.0.2", @@ -258,7 +253,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.191, )" + "Speckle.Objects": "[3.1.1, )" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { @@ -285,20 +280,26 @@ "resolved": "2.2.0", "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "+m7jRFm0ABbkcSz2UphdxAsislY10IpQ1u79c8a3aVvegLjnsVQZ1sXfRIRO1aFdulkhjYKXYpB3N9M8Z+epgQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.191" + "Speckle.Sdk": "3.1.1" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "VVT3LJiYlhqnggxxdeTt1QLrqfxDb044x0yX6kxS9b5MRzeDvK2Vz86pLDfuHs+SXvDimRVfYx1M42IW/aPcTQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.Bcl.AsyncInterfaces": "5.0.0", @@ -306,16 +307,16 @@ "Microsoft.Data.Sqlite": "7.0.5", "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", "Microsoft.Extensions.Logging": "2.2.0", - "Speckle.DoubleNumerics": "4.0.1", + "Speckle.DoubleNumerics": "4.1.0", "Speckle.Newtonsoft.Json": "13.0.2", - "Speckle.Sdk.Dependencies": "3.1.0-dev.191" + "Speckle.Sdk.Dependencies": "3.1.1" } }, "Speckle.Sdk.Dependencies": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "EmEOyjsGsNi56Z/ZoBOn8WirTmIT2yqWvlUeUh0BSPX2TDMZXHTKOM/kHmP6HSd10KVFn2Zo/ItY7/K9iRtL1Q==" + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" } } } diff --git a/Converters/Revit/Speckle.Converters.Revit2023.Tests/packages.lock.json b/Converters/Revit/Speckle.Converters.Revit2023.Tests/packages.lock.json index c98580f49..3f416a7cf 100644 --- a/Converters/Revit/Speckle.Converters.Revit2023.Tests/packages.lock.json +++ b/Converters/Revit/Speckle.Converters.Revit2023.Tests/packages.lock.json @@ -245,11 +245,6 @@ "resolved": "13.0.1", "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" }, - "Speckle.DoubleNumerics": { - "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w==" - }, "Speckle.Newtonsoft.Json": { "type": "Transitive", "resolved": "13.0.2", @@ -340,7 +335,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.191, )" + "Speckle.Objects": "[3.1.1, )" } }, "speckle.testing": { @@ -374,36 +369,42 @@ "resolved": "2.2.0", "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "+m7jRFm0ABbkcSz2UphdxAsislY10IpQ1u79c8a3aVvegLjnsVQZ1sXfRIRO1aFdulkhjYKXYpB3N9M8Z+epgQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.191" + "Speckle.Sdk": "3.1.1" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "VVT3LJiYlhqnggxxdeTt1QLrqfxDb044x0yX6kxS9b5MRzeDvK2Vz86pLDfuHs+SXvDimRVfYx1M42IW/aPcTQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.CSharp": "4.7.0", "Microsoft.Data.Sqlite": "7.0.5", "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", "Microsoft.Extensions.Logging": "2.2.0", - "Speckle.DoubleNumerics": "4.0.1", + "Speckle.DoubleNumerics": "4.1.0", "Speckle.Newtonsoft.Json": "13.0.2", - "Speckle.Sdk.Dependencies": "3.1.0-dev.191" + "Speckle.Sdk.Dependencies": "3.1.1" } }, "Speckle.Sdk.Dependencies": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "EmEOyjsGsNi56Z/ZoBOn8WirTmIT2yqWvlUeUh0BSPX2TDMZXHTKOM/kHmP6HSd10KVFn2Zo/ItY7/K9iRtL1Q==" + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" } } } diff --git a/Converters/Revit/Speckle.Converters.Revit2023/packages.lock.json b/Converters/Revit/Speckle.Converters.Revit2023/packages.lock.json index 8ea260a39..46dde829d 100644 --- a/Converters/Revit/Speckle.Converters.Revit2023/packages.lock.json +++ b/Converters/Revit/Speckle.Converters.Revit2023/packages.lock.json @@ -159,11 +159,6 @@ "resolved": "8.0.0", "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" }, - "Speckle.DoubleNumerics": { - "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w==" - }, "Speckle.Newtonsoft.Json": { "type": "Transitive", "resolved": "13.0.2", @@ -258,7 +253,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.191, )" + "Speckle.Objects": "[3.1.1, )" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { @@ -285,20 +280,26 @@ "resolved": "2.2.0", "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "+m7jRFm0ABbkcSz2UphdxAsislY10IpQ1u79c8a3aVvegLjnsVQZ1sXfRIRO1aFdulkhjYKXYpB3N9M8Z+epgQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.191" + "Speckle.Sdk": "3.1.1" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "VVT3LJiYlhqnggxxdeTt1QLrqfxDb044x0yX6kxS9b5MRzeDvK2Vz86pLDfuHs+SXvDimRVfYx1M42IW/aPcTQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.Bcl.AsyncInterfaces": "5.0.0", @@ -306,16 +307,16 @@ "Microsoft.Data.Sqlite": "7.0.5", "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", "Microsoft.Extensions.Logging": "2.2.0", - "Speckle.DoubleNumerics": "4.0.1", + "Speckle.DoubleNumerics": "4.1.0", "Speckle.Newtonsoft.Json": "13.0.2", - "Speckle.Sdk.Dependencies": "3.1.0-dev.191" + "Speckle.Sdk.Dependencies": "3.1.1" } }, "Speckle.Sdk.Dependencies": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "EmEOyjsGsNi56Z/ZoBOn8WirTmIT2yqWvlUeUh0BSPX2TDMZXHTKOM/kHmP6HSd10KVFn2Zo/ItY7/K9iRtL1Q==" + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" } } } diff --git a/Converters/Revit/Speckle.Converters.Revit2024.Tests/packages.lock.json b/Converters/Revit/Speckle.Converters.Revit2024.Tests/packages.lock.json index 66310ac4c..d3f82615f 100644 --- a/Converters/Revit/Speckle.Converters.Revit2024.Tests/packages.lock.json +++ b/Converters/Revit/Speckle.Converters.Revit2024.Tests/packages.lock.json @@ -245,11 +245,6 @@ "resolved": "13.0.1", "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" }, - "Speckle.DoubleNumerics": { - "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w==" - }, "Speckle.Newtonsoft.Json": { "type": "Transitive", "resolved": "13.0.2", @@ -340,7 +335,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.191, )" + "Speckle.Objects": "[3.1.1, )" } }, "speckle.testing": { @@ -374,36 +369,42 @@ "resolved": "2.2.0", "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "+m7jRFm0ABbkcSz2UphdxAsislY10IpQ1u79c8a3aVvegLjnsVQZ1sXfRIRO1aFdulkhjYKXYpB3N9M8Z+epgQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.191" + "Speckle.Sdk": "3.1.1" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "VVT3LJiYlhqnggxxdeTt1QLrqfxDb044x0yX6kxS9b5MRzeDvK2Vz86pLDfuHs+SXvDimRVfYx1M42IW/aPcTQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.CSharp": "4.7.0", "Microsoft.Data.Sqlite": "7.0.5", "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", "Microsoft.Extensions.Logging": "2.2.0", - "Speckle.DoubleNumerics": "4.0.1", + "Speckle.DoubleNumerics": "4.1.0", "Speckle.Newtonsoft.Json": "13.0.2", - "Speckle.Sdk.Dependencies": "3.1.0-dev.191" + "Speckle.Sdk.Dependencies": "3.1.1" } }, "Speckle.Sdk.Dependencies": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "EmEOyjsGsNi56Z/ZoBOn8WirTmIT2yqWvlUeUh0BSPX2TDMZXHTKOM/kHmP6HSd10KVFn2Zo/ItY7/K9iRtL1Q==" + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" } } } diff --git a/Converters/Revit/Speckle.Converters.Revit2024/packages.lock.json b/Converters/Revit/Speckle.Converters.Revit2024/packages.lock.json index 8206fcfd2..f45ecdcaa 100644 --- a/Converters/Revit/Speckle.Converters.Revit2024/packages.lock.json +++ b/Converters/Revit/Speckle.Converters.Revit2024/packages.lock.json @@ -159,11 +159,6 @@ "resolved": "8.0.0", "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" }, - "Speckle.DoubleNumerics": { - "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w==" - }, "Speckle.Newtonsoft.Json": { "type": "Transitive", "resolved": "13.0.2", @@ -258,7 +253,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.191, )" + "Speckle.Objects": "[3.1.1, )" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { @@ -285,20 +280,26 @@ "resolved": "2.2.0", "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "+m7jRFm0ABbkcSz2UphdxAsislY10IpQ1u79c8a3aVvegLjnsVQZ1sXfRIRO1aFdulkhjYKXYpB3N9M8Z+epgQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.191" + "Speckle.Sdk": "3.1.1" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "VVT3LJiYlhqnggxxdeTt1QLrqfxDb044x0yX6kxS9b5MRzeDvK2Vz86pLDfuHs+SXvDimRVfYx1M42IW/aPcTQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.Bcl.AsyncInterfaces": "5.0.0", @@ -306,16 +307,16 @@ "Microsoft.Data.Sqlite": "7.0.5", "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", "Microsoft.Extensions.Logging": "2.2.0", - "Speckle.DoubleNumerics": "4.0.1", + "Speckle.DoubleNumerics": "4.1.0", "Speckle.Newtonsoft.Json": "13.0.2", - "Speckle.Sdk.Dependencies": "3.1.0-dev.191" + "Speckle.Sdk.Dependencies": "3.1.1" } }, "Speckle.Sdk.Dependencies": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "EmEOyjsGsNi56Z/ZoBOn8WirTmIT2yqWvlUeUh0BSPX2TDMZXHTKOM/kHmP6HSd10KVFn2Zo/ItY7/K9iRtL1Q==" + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" } } } diff --git a/Converters/Revit/Speckle.Converters.Revit2025/packages.lock.json b/Converters/Revit/Speckle.Converters.Revit2025/packages.lock.json index f8be84563..69cb854ca 100644 --- a/Converters/Revit/Speckle.Converters.Revit2025/packages.lock.json +++ b/Converters/Revit/Speckle.Converters.Revit2025/packages.lock.json @@ -150,11 +150,6 @@ "resolved": "8.0.0", "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" }, - "Speckle.DoubleNumerics": { - "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w==" - }, "Speckle.Newtonsoft.Json": { "type": "Transitive", "resolved": "13.0.2", @@ -214,7 +209,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.191, )" + "Speckle.Objects": "[3.1.1, )" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { @@ -241,36 +236,42 @@ "resolved": "2.2.0", "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "+m7jRFm0ABbkcSz2UphdxAsislY10IpQ1u79c8a3aVvegLjnsVQZ1sXfRIRO1aFdulkhjYKXYpB3N9M8Z+epgQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.191" + "Speckle.Sdk": "3.1.1" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "VVT3LJiYlhqnggxxdeTt1QLrqfxDb044x0yX6kxS9b5MRzeDvK2Vz86pLDfuHs+SXvDimRVfYx1M42IW/aPcTQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.CSharp": "4.7.0", "Microsoft.Data.Sqlite": "7.0.5", "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", "Microsoft.Extensions.Logging": "2.2.0", - "Speckle.DoubleNumerics": "4.0.1", + "Speckle.DoubleNumerics": "4.1.0", "Speckle.Newtonsoft.Json": "13.0.2", - "Speckle.Sdk.Dependencies": "3.1.0-dev.191" + "Speckle.Sdk.Dependencies": "3.1.1" } }, "Speckle.Sdk.Dependencies": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "EmEOyjsGsNi56Z/ZoBOn8WirTmIT2yqWvlUeUh0BSPX2TDMZXHTKOM/kHmP6HSd10KVFn2Zo/ItY7/K9iRtL1Q==" + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" } } } diff --git a/Converters/Revit/Speckle.Converters.RevitShared/Extensions/CategoryExtensions.cs b/Converters/Revit/Speckle.Converters.RevitShared/Extensions/CategoryExtensions.cs index 15b388dc7..596b53dda 100644 --- a/Converters/Revit/Speckle.Converters.RevitShared/Extensions/CategoryExtensions.cs +++ b/Converters/Revit/Speckle.Converters.RevitShared/Extensions/CategoryExtensions.cs @@ -1,47 +1,13 @@ -using Autodesk.Revit.DB; -using Speckle.Sdk.Common.Exceptions; - namespace Speckle.Converters.RevitShared.Extensions; public static class CategoryExtensions { - public static SOBR.RevitCategory GetSchemaBuilderCategoryFromBuiltIn(this DB.BuiltInCategory builtInCategory) - { - // Clean up built-in name "OST_Walls" to be just "WALLS" - var cleanName = builtInCategory - .ToString() - .Replace("OST_IOS", "") //for OST_IOSModelGroups - .Replace("OST_MEP", "") //for OST_MEPSpaces - .Replace("OST_", "") //for any other OST_blablabla - .Replace("_", " "); - - var res = Enum.TryParse(cleanName, out SOBR.RevitCategory cat); - if (!res) - { - throw new ConversionException($"Built-in category {builtInCategory} is not supported."); - } - - return cat; - } - - public static BuiltInCategory GetBuiltInCategory(this Category category) + public static DB.BuiltInCategory GetBuiltInCategory(this DB.Category category) { #if REVIT2024_OR_GREATER - return (BuiltInCategory)category.Id.Value; + return (DB.BuiltInCategory)category.Id.Value; #else - return (BuiltInCategory)category.Id.IntegerValue; + return (DB.BuiltInCategory)category.Id.IntegerValue; #endif } - - public static string GetBuiltInFromSchemaBuilderCategory(this SOBR.RevitCategory c) - { - var name = Enum.GetName(typeof(SOBR.RevitCategory), c); - return $"OST_{name}"; - } - - public static string GetBuiltInFromSchemaBuilderCategory(this SOBR.RevitFamilyCategory c) - { - var name = Enum.GetName(typeof(SOBR.RevitFamilyCategory), c); - return $"OST_{name}"; - } } diff --git a/Converters/Revit/Speckle.Converters.RevitShared/GlobalUsings.cs b/Converters/Revit/Speckle.Converters.RevitShared/GlobalUsings.cs index 0be936b2c..450c21f5d 100644 --- a/Converters/Revit/Speckle.Converters.RevitShared/GlobalUsings.cs +++ b/Converters/Revit/Speckle.Converters.RevitShared/GlobalUsings.cs @@ -1,5 +1,3 @@ global using DB = Autodesk.Revit.DB; global using DBA = Autodesk.Revit.DB.Architecture; -global using SOBE = Speckle.Objects.BuiltElements; -global using SOBR = Speckle.Objects.BuiltElements.Revit; global using SOG = Speckle.Objects.Geometry; diff --git a/Converters/Revit/Speckle.Converters.RevitShared/Helpers/DisplayValueExtractor.cs b/Converters/Revit/Speckle.Converters.RevitShared/Helpers/DisplayValueExtractor.cs index c2fbc6fd9..92247d468 100644 --- a/Converters/Revit/Speckle.Converters.RevitShared/Helpers/DisplayValueExtractor.cs +++ b/Converters/Revit/Speckle.Converters.RevitShared/Helpers/DisplayValueExtractor.cs @@ -1,8 +1,11 @@ using Microsoft.Extensions.Logging; using Speckle.Converters.Common; using Speckle.Converters.Common.Objects; +using Speckle.Converters.RevitShared.Extensions; using Speckle.Converters.RevitShared.Settings; +using Speckle.Objects; using Speckle.Sdk.Common; +using Speckle.Sdk.Models; namespace Speckle.Converters.RevitShared.Helpers; @@ -13,6 +16,10 @@ public sealed class DisplayValueExtractor (Dictionary> target, DB.ElementId parentElementId, bool makeTransparent), List > _meshByMaterialConverter; + private readonly ITypedConverter _curveConverter; + private readonly ITypedConverter _polylineConverter; + private readonly ITypedConverter _pointConverter; + private readonly ITypedConverter _pointcloudConverter; private readonly ILogger _logger; private readonly IConverterSettingsStore _converterSettings; @@ -21,26 +28,99 @@ public sealed class DisplayValueExtractor (Dictionary> target, DB.ElementId parentElementId, bool makeTransparent), List > meshByMaterialConverter, + ITypedConverter curveConverter, + ITypedConverter polylineConverter, + ITypedConverter pointConverter, + ITypedConverter pointcloudConverter, ILogger logger, IConverterSettingsStore converterSettings ) { _meshByMaterialConverter = meshByMaterialConverter; + _curveConverter = curveConverter; + _polylineConverter = polylineConverter; + _pointConverter = pointConverter; + _pointcloudConverter = pointcloudConverter; _logger = logger; _converterSettings = converterSettings; } - public List GetDisplayValue(DB.Element element, DB.Options? options = null) + public List GetDisplayValue(DB.Element element) { - var (solids, meshes) = GetSolidsAndMeshesFromElement(element, options); + switch (element) + { + // get custom (anything not using element.get_geometry) display values + case DB.PointCloudInstance pointcloud: + return new() { _pointcloudConverter.Convert(pointcloud) }; + case DB.ModelCurve modelCurve: + return new() { GetCurveDisplayValue(modelCurve.GeometryCurve) }; + case DB.Grid grid: + return new() { GetCurveDisplayValue(grid.Curve) }; + case DB.Area area: + List areaDisplay = new(); + using (var options = new DB.SpatialElementBoundaryOptions()) + { + foreach (IList boundarySegmentGroup in area.GetBoundarySegments(options)) + { + foreach (DB.BoundarySegment boundarySegment in boundarySegmentGroup) + { + areaDisplay.Add(GetCurveDisplayValue(boundarySegment.GetCurve())); + } + } + } + return areaDisplay; + // handle specific types of objects with multiple parts or children + // curtain and stacked walls should have their display values in their children + case DB.Wall wall: + return wall.CurtainGrid is not null || wall.IsStackedWall ? new() : GetGeometryDisplayValue(element); + // railings should also include toprail which need to be retrieved separately + case DBA.Railing railing: + List railingDisplay = GetGeometryDisplayValue(railing); + if (railing.TopRail != DB.ElementId.InvalidElementId) + { + var topRail = _converterSettings.Current.Document.GetElement(railing.TopRail); + railingDisplay.AddRange(GetGeometryDisplayValue(topRail)); + } + return railingDisplay; + + // POC: footprint roofs can have curtain walls in them. Need to check if they can also have non-curtain wall parts, bc currently not skipping anything. + // case DB.FootPrintRoof footPrintRoof: + + default: + return GetGeometryDisplayValue(element); + } + } + + private Base GetCurveDisplayValue(DB.Curve curve) => (Base)_curveConverter.Convert(curve); + + private List GetGeometryDisplayValue(DB.Element element, DB.Options? options = null) + { + List displayValue = new(); + var (solids, meshes, curves, polylines, points) = GetSortedGeometryFromElement(element, options); + + // handle all solids and meshes by their material var meshesByMaterial = GetMeshesByMaterial(meshes, solids); - List displayMeshes = _meshByMaterialConverter.Convert( (meshesByMaterial, element.Id, ShouldSetElementDisplayToTransparent(element)) ); + displayValue.AddRange(displayMeshes); - return displayMeshes; + // add rest of geometry + foreach (var curve in curves) + { + displayValue.Add(GetCurveDisplayValue(curve)); + } + foreach (var polyline in polylines) + { + displayValue.Add(_polylineConverter.Convert(polyline)); + } + foreach (var point in points) + { + displayValue.Add(_pointConverter.Convert(point)); + } + + return displayValue; } private static Dictionary> GetMeshesByMaterial( @@ -93,10 +173,17 @@ public sealed class DisplayValueExtractor { DetailLevelType.Fine, DB.ViewDetailLevel.Fine } }; - private (List, List) GetSolidsAndMeshesFromElement(DB.Element element, DB.Options? options) + private ( + List, + List, + List, + List, + List + ) GetSortedGeometryFromElement(DB.Element element, DB.Options? options) { //options = ViewSpecificOptions ?? options ?? new Options() { DetailLevel = DetailLevelSetting }; options ??= new DB.Options { DetailLevel = _detailLevelMap[_converterSettings.Current.DetailLevel] }; + options = OverrideViewOptions(element, options); DB.GeometryElement geom; try @@ -110,16 +197,19 @@ public sealed class DisplayValueExtractor geom = element.get_Geometry(options); } - var solids = new List(); - var meshes = new List(); + List solids = new(); + List meshes = new(); + List curves = new(); + List polylines = new(); + List points = new(); - if (geom != null) + if (geom != null && geom.Any()) { // retrieves all meshes and solids from a geometry element - SortGeometry(element, solids, meshes, geom); + SortGeometry(element, solids, meshes, curves, polylines, points, geom); } - return (solids, meshes); + return (solids, meshes, curves, polylines, points); } /// @@ -138,8 +228,19 @@ public sealed class DisplayValueExtractor /// /// /// + /// + /// + /// /// - private void SortGeometry(DB.Element element, List solids, List meshes, DB.GeometryElement geom) + private void SortGeometry( + DB.Element element, + List solids, + List meshes, + List curves, + List polylines, + List points, + DB.GeometryElement geom + ) { foreach (DB.GeometryObject geomObj in geom) { @@ -156,20 +257,33 @@ public sealed class DisplayValueExtractor { continue; } - solids.Add(solid); break; - case DB.Mesh mesh: + case DB.Mesh mesh: meshes.Add(mesh); break; + + case DB.Curve curve: + curves.Add(curve); + break; + + case DB.PolyLine polyline: + polylines.Add(polyline); + break; + + case DB.Point point: + points.Add(point); + break; + case DB.GeometryInstance instance: // element transforms should not be carried down into nested geometryInstances. // Nested geomInstances should have their geom retreived with GetInstanceGeom, not GetSymbolGeom - SortGeometry(element, solids, meshes, instance.GetInstanceGeometry()); + SortGeometry(element, solids, meshes, curves, polylines, points, instance.GetInstanceGeometry()); break; + case DB.GeometryElement geometryElement: - SortGeometry(element, solids, meshes, geometryElement); + SortGeometry(element, solids, meshes, curves, polylines, points, geometryElement); break; } } @@ -230,4 +344,54 @@ public sealed class DisplayValueExtractor return false; #endif } + + /// + /// Overrides current view options to extract meaningful geometry for various elements. E.g., pipes, plumbing fixtures, steel elements + /// + /// + /// + private DB.Options OverrideViewOptions(DB.Element element, DB.Options currentOptions) + { + // there is no point to progress if element category already null + if (element.Category is null) + { + return currentOptions; + } + + var elementBuiltInCategory = element.Category.GetBuiltInCategory(); + + // Note: some elements do not get display values (you get invalid solids) unless we force the view detail level to be fine. This is annoying, but it's bad ux: people think the + // elements are not there (they are, just invisible). + if ( + elementBuiltInCategory == DB.BuiltInCategory.OST_PipeFitting + || elementBuiltInCategory == DB.BuiltInCategory.OST_PipeAccessory + || elementBuiltInCategory == DB.BuiltInCategory.OST_PlumbingFixtures +#if REVIT2024_OR_GREATER + || element is DB.Toposolid // note, brought back from 2.x.x. +#endif + ) + { + currentOptions.DetailLevel = DB.ViewDetailLevel.Fine; // Force detail level to be fine + return currentOptions; + } + // NOTE: On steel elements. This is an incomplete solution. + // If steel element proxies will be sucked in via category selection, and they are not visible in the current view, they will not be extracted out. + // I'm inclined to go with this as a semi-permanent limitation. See: + // https://speckle.community/t/revit-2025-2-missing-elements-and-colors/14073 + // and https://forums.autodesk.com/t5/revit-api-forum/how-to-get-steelproxyelement-geometry/td-p/10347898 + if ( + elementBuiltInCategory + is DB.BuiltInCategory.OST_StructConnections + or DB.BuiltInCategory.OST_StructConnectionPlates + or DB.BuiltInCategory.OST_StructuralFraming + or DB.BuiltInCategory.OST_StructuralColumns + or DB.BuiltInCategory.OST_StructConnectionBolts + or DB.BuiltInCategory.OST_StructConnectionWelds + or DB.BuiltInCategory.OST_StructConnectionShearStuds + ) + { + return new DB.Options() { View = _converterSettings.Current.Document.NotNull().ActiveView }; // TODO/NOTE: in case it's a view filter, it should use that specific view. This is a limiting partial fix. + } + return currentOptions; + } } diff --git a/Converters/Revit/Speckle.Converters.RevitShared/Helpers/IRevitCategories.cs b/Converters/Revit/Speckle.Converters.RevitShared/Helpers/IRevitCategories.cs deleted file mode 100644 index 1a1796aa7..000000000 --- a/Converters/Revit/Speckle.Converters.RevitShared/Helpers/IRevitCategories.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Speckle.Objects.BuiltElements.Revit; - -namespace Speckle.Converters.RevitShared.Helpers; - -public interface IRevitCategories -{ - string GetBuiltInFromSchemaBuilderCategory(RevitCategory c); - RevitCategory GetSchemaBuilderCategoryFromBuiltIn(string builtInCategory); -} diff --git a/Converters/Revit/Speckle.Converters.RevitShared/Helpers/ISlopeArrowExtractor.cs b/Converters/Revit/Speckle.Converters.RevitShared/Helpers/ISlopeArrowExtractor.cs deleted file mode 100644 index 750f4d107..000000000 --- a/Converters/Revit/Speckle.Converters.RevitShared/Helpers/ISlopeArrowExtractor.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Speckle.Converters.RevitShared.Helpers; - -public interface ISlopeArrowExtractor -{ - DB.ModelLine? GetSlopeArrow(DB.Element element); - SOG.Point GetSlopeArrowHead(DB.ModelLine slopeArrow); - SOG.Point GetSlopeArrowTail(DB.ModelLine slopeArrow); - double GetSlopeArrowTailOffset(DB.ModelLine slopeArrow); - double GetSlopeArrowHeadOffset(DB.ModelLine slopeArrow, double tailOffset, out double slope); -} diff --git a/Converters/Revit/Speckle.Converters.RevitShared/Helpers/RevitCategories.cs b/Converters/Revit/Speckle.Converters.RevitShared/Helpers/RevitCategories.cs deleted file mode 100644 index d207b4869..000000000 --- a/Converters/Revit/Speckle.Converters.RevitShared/Helpers/RevitCategories.cs +++ /dev/null @@ -1,45 +0,0 @@ -using Speckle.Objects.BuiltElements.Revit; -using Speckle.Sdk.Common.Exceptions; - -namespace Speckle.Converters.RevitShared.Helpers; - -/// -/// Contains helper methods related to categories in Revit -/// -/// -public class RevitCategories : IRevitCategories -{ - /// - /// Returns the corresponding based on a given built-in category name - /// - /// The name of the built-in category - /// The RevitCategory enum value that corresponds to the given name - public RevitCategory GetSchemaBuilderCategoryFromBuiltIn(string builtInCategory) - { - // Clean up built-in name "OST_Walls" to be just "WALLS" - var cleanName = builtInCategory - .Replace("OST_IOS", "") //for OST_IOSModelGroups - .Replace("OST_MEP", "") //for OST_MEPSpaces - .Replace("OST_", "") //for any other OST_blablabla - .Replace("_", " "); - - var res = Enum.TryParse(cleanName, out RevitCategory cat); - if (!res) - { - throw new ConversionException($"Built-in category {builtInCategory} is not supported."); - } - - return cat; - } - - /// - /// Returns the corresponding built-in category name from a specific - /// - /// The RevitCategory to convert - /// The name of the built-in category that corresponds to the input RevitCategory - public string GetBuiltInFromSchemaBuilderCategory(RevitCategory c) - { - var name = Enum.GetName(typeof(RevitCategory), c); - return $"OST_{name}"; - } -} diff --git a/Converters/Revit/Speckle.Converters.RevitShared/Helpers/RevitCategoryInfo.cs b/Converters/Revit/Speckle.Converters.RevitShared/Helpers/RevitCategoryInfo.cs deleted file mode 100644 index 08c58e37d..000000000 --- a/Converters/Revit/Speckle.Converters.RevitShared/Helpers/RevitCategoryInfo.cs +++ /dev/null @@ -1,69 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using Autodesk.Revit.DB; - -namespace Speckle.Converters.RevitShared.Helpers; - -// review, maybe it doesn't need injecting, or maybe we inject a factory? -public class RevitCategoryInfo -{ - public RevitCategoryInfo( - string name, - Type instanceType, - Type familyType, - List categories, - List? categoryAliases = null - ) - { - CategoryName = name; - ElementInstanceType = instanceType; - ElementTypeType = familyType; - BuiltInCategories = categories; - CategoryAliases = categoryAliases ?? new List(); - } - - public string CategoryName { get; } - public Type ElementInstanceType { get; } - public Type ElementTypeType { get; } - public ICollection BuiltInCategories { get; } - public List CategoryAliases { get; } - - public bool ContainsRevitCategory(Category category) - { -#if REVIT2024_OR_GREATER - return BuiltInCategories.Select(x => (long)x).Contains(category.Id.Value); -#else - return BuiltInCategories.Select(x => (int)x).Contains(category.Id.IntegerValue); -#endif - } - - public List GetElementTypes(Document document) - { - return GetElementTypes(document); - } - - [SuppressMessage("Reliability", "CA2000:Dispose objects before losing scope")] - public List GetElementTypes(Document document) - where T : ElementType - { - // POC: why is this disabled? surely a using statement is golden here? - var collector = new FilteredElementCollector(document); - - if (BuiltInCategories.Count > 0) - { - using var filter = new ElementMulticategoryFilter(BuiltInCategories); - collector = collector.WherePasses(filter); - } - if (ElementTypeType != null) - { - collector = collector.OfClass(ElementTypeType); - } - var elementTypes = collector.WhereElementIsElementType().Cast().ToList(); - collector.Dispose(); - return elementTypes; - } - - public string GetCategorySpecificTypeName(string typeName) - { - return CategoryName + "_" + typeName; - } -} diff --git a/Converters/Revit/Speckle.Converters.RevitShared/Helpers/SlopeArrowExtractor.cs b/Converters/Revit/Speckle.Converters.RevitShared/Helpers/SlopeArrowExtractor.cs deleted file mode 100644 index 9db6d1458..000000000 --- a/Converters/Revit/Speckle.Converters.RevitShared/Helpers/SlopeArrowExtractor.cs +++ /dev/null @@ -1,88 +0,0 @@ -using Speckle.Converters.Common.Objects; - -namespace Speckle.Converters.RevitShared.Helpers; - -public class SlopeArrowExtractor : ISlopeArrowExtractor -{ - private readonly ITypedConverter _pointConverter; - private readonly ParameterValueExtractor _parameterValueExtractor; - - public SlopeArrowExtractor( - ITypedConverter pointConverter, - ParameterValueExtractor parameterValueExtractor - ) - { - _pointConverter = pointConverter; - _parameterValueExtractor = parameterValueExtractor; - } - - public DB.ModelLine? GetSlopeArrow(DB.Element element) - { - IList? elementIds = null; - if (element is DB.Floor floor) - { - elementIds = ((DB.Sketch)floor.Document.GetElement(floor.SketchId)).GetAllElements(); - } - - if (elementIds == null) - { - using var modelLineFilter = new DB.ElementCategoryFilter(DB.BuiltInCategory.OST_SketchLines); - elementIds = element.GetDependentElements(modelLineFilter); - } - - foreach (var elementId in elementIds) - { - if (element.Document.GetElement(elementId) is not DB.ModelLine line) - { - continue; - } - - var offsetAtTailParameter = line.get_Parameter(DB.BuiltInParameter.SLOPE_START_HEIGHT); - if (offsetAtTailParameter != null) - { - return line; - } - } - return null; - } - - public SOG.Point GetSlopeArrowHead(DB.ModelLine slopeArrow) - { - return _pointConverter.Convert(((DB.LocationCurve)slopeArrow.Location).Curve.GetEndPoint(1)); - } - - public SOG.Point GetSlopeArrowTail(DB.ModelLine slopeArrow) - { - return _pointConverter.Convert(((DB.LocationCurve)slopeArrow.Location).Curve.GetEndPoint(0)); - } - - public double GetSlopeArrowTailOffset(DB.ModelLine slopeArrow) - { - return _parameterValueExtractor.GetValueAsDouble(slopeArrow, DB.BuiltInParameter.SLOPE_START_HEIGHT); - } - - public double GetSlopeArrowHeadOffset(DB.ModelLine slopeArrow, double tailOffset, out double slope) - { - var specifyOffset = _parameterValueExtractor.GetValueAsInt(slopeArrow, DB.BuiltInParameter.SPECIFY_SLOPE_OR_OFFSET); - - var lineLength = _parameterValueExtractor.GetValueAsDouble(slopeArrow, DB.BuiltInParameter.CURVE_ELEM_LENGTH); - - slope = 0; - double headOffset = 0; - // 1 corrosponds to the "slope" option - if (specifyOffset == 1) - { - // in this scenario, slope is returned as a percentage. Divide by 100 to get the unitless form - slope = _parameterValueExtractor.GetValueAsDouble(slopeArrow, DB.BuiltInParameter.ROOF_SLOPE) / 100d; - headOffset = tailOffset + lineLength * Math.Sin(Math.Atan(slope)); - } - else if (specifyOffset == 0) // 0 corrospondes to the "height at tail" option - { - headOffset = _parameterValueExtractor.GetValueAsDouble(slopeArrow, DB.BuiltInParameter.SLOPE_END_HEIGHT); - - slope = (headOffset - tailOffset) / lineLength; - } - - return headOffset; - } -} diff --git a/Converters/Revit/Speckle.Converters.RevitShared/RevitRootToHostConverter.cs b/Converters/Revit/Speckle.Converters.RevitShared/RevitRootToHostConverter.cs index b12cfdd70..96c5e9093 100644 --- a/Converters/Revit/Speckle.Converters.RevitShared/RevitRootToHostConverter.cs +++ b/Converters/Revit/Speckle.Converters.RevitShared/RevitRootToHostConverter.cs @@ -2,6 +2,7 @@ using Autodesk.Revit.DB; using Speckle.Converters.Common; using Speckle.Converters.Common.Objects; using Speckle.Converters.RevitShared.Settings; +using Speckle.Sdk.Common; using Speckle.Sdk.Common.Exceptions; using Speckle.Sdk.Models; @@ -32,7 +33,7 @@ public class RevitRootToHostConverter : IRootToHostConverter throw new ConversionException($"No supported conversion for {target.speckle_type} found."); } - var definitionId = target.applicationId ?? target.id; + var definitionId = target.applicationId ?? target.id.NotNull(); DirectShapeLibrary .GetDirectShapeLibrary(_converterSettings.Current.Document) .AddDefinition(definitionId, geometryObjects); diff --git a/Converters/Revit/Speckle.Converters.RevitShared/RevitRootToSpeckleConverter.cs b/Converters/Revit/Speckle.Converters.RevitShared/RevitRootToSpeckleConverter.cs index 4d74554b2..7d9fdc14b 100644 --- a/Converters/Revit/Speckle.Converters.RevitShared/RevitRootToSpeckleConverter.cs +++ b/Converters/Revit/Speckle.Converters.RevitShared/RevitRootToSpeckleConverter.cs @@ -1,12 +1,6 @@ -using Autodesk.Revit.DB; -using Microsoft.Extensions.Logging; using Speckle.Converters.Common; using Speckle.Converters.Common.Objects; using Speckle.Converters.Common.Registration; -using Speckle.Converters.RevitShared.Extensions; -using Speckle.Converters.RevitShared.Settings; -using Speckle.Converters.RevitShared.ToSpeckle; -using Speckle.Sdk; using Speckle.Sdk.Common.Exceptions; using Speckle.Sdk.Models; @@ -15,26 +9,10 @@ namespace Speckle.Converters.RevitShared; public class RevitRootToSpeckleConverter : IRootToSpeckleConverter { private readonly IConverterManager _toSpeckle; - private readonly ITypedConverter> _materialQuantityConverter; - private readonly IConverterSettingsStore _converterSettings; - private readonly ParameterExtractor _parameterExtractor; - private readonly ILogger _logger; - private readonly Dictionary _worksetCache = new(); - - public RevitRootToSpeckleConverter( - IConverterManager toSpeckle, - ITypedConverter> materialQuantityConverter, - IConverterSettingsStore converterSettings, - ParameterExtractor parameterExtractor, - ILogger logger - ) + public RevitRootToSpeckleConverter(IConverterManager toSpeckle) { _toSpeckle = toSpeckle; - _materialQuantityConverter = materialQuantityConverter; - _converterSettings = converterSettings; - _parameterExtractor = parameterExtractor; - _logger = logger; } public Base Convert(object target) @@ -45,52 +23,8 @@ public class RevitRootToSpeckleConverter : IRootToSpeckleConverter } var objectConverter = _toSpeckle.ResolveConverter(target.GetType()); - Base result = objectConverter.Convert(target); - result.applicationId = element.UniqueId; - - // Add ElementID to the converted objects - result["elementId"] = element.Id.ToString()!; - - // POC DirectShapes have RevitCategory enum as the type or the category property, DS category property is already set in the converter - // trying to set the category as a string will throw - // the category should be moved to be set in each converter instead of the root to speckle converter - if (target is not DB.DirectShape) - { - result["category"] = element.Category?.Name; - result["builtinCategory"] = element.Category?.GetBuiltInCategory().ToString(); - } - - try - { - result["materialQuantities"] = _materialQuantityConverter.Convert(element); - } - catch (Exception e) when (!e.IsFatal()) - { - _logger.LogWarning(e, $"Failed to extract material quantities from element {target.GetType().Name}"); - } - - try - { - var parameters = _parameterExtractor.GetParameters(element); - // NOTE: we're conflicting with a strongly typed (Base) `parameters` property set on revit elements. We can revert this to be back to parameters later, but this will mean frontend legwork to add another special parsing case for the properties view of an object. - result["properties"] = parameters; - } - catch (Exception e) when (!e.IsFatal()) - { - _logger.LogWarning(e, $"Failed to extract parameters from element {target.GetType().Name}"); - } - - result["worksetId"] = element.WorksetId.ToString(); - if (!_worksetCache.TryGetValue(element.WorksetId, out var worksetName)) - { - Workset workset = _converterSettings.Current.Document.GetWorksetTable().GetWorkset(element.WorksetId); - worksetName = workset.Name; - _worksetCache[element.WorksetId] = worksetName; - } - result["worksetName"] = worksetName; - return result; } } diff --git a/Converters/Revit/Speckle.Converters.RevitShared/ServiceRegistration.cs b/Converters/Revit/Speckle.Converters.RevitShared/ServiceRegistration.cs index 4d5859adf..2bdb2303c 100644 --- a/Converters/Revit/Speckle.Converters.RevitShared/ServiceRegistration.cs +++ b/Converters/Revit/Speckle.Converters.RevitShared/ServiceRegistration.cs @@ -2,10 +2,12 @@ using System.Reflection; using Microsoft.Extensions.DependencyInjection; using Speckle.Converters.Common; using Speckle.Converters.Common.Registration; +using Speckle.Converters.Revit2023.ToSpeckle.Properties; using Speckle.Converters.RevitShared.Helpers; using Speckle.Converters.RevitShared.Services; using Speckle.Converters.RevitShared.Settings; using Speckle.Converters.RevitShared.ToSpeckle; +using Speckle.Converters.RevitShared.ToSpeckle.Properties; using Speckle.Sdk; namespace Speckle.Converters.RevitShared; @@ -43,13 +45,15 @@ public static class ServiceRegistration serviceCollection.AddScoped(); + // register extractors serviceCollection.AddScoped(); serviceCollection.AddScoped(); - serviceCollection.AddScoped(); - - serviceCollection.AddScoped(); serviceCollection.AddScoped(); serviceCollection.AddScoped(); + serviceCollection.AddScoped(); + serviceCollection.AddScoped(); + serviceCollection.AddScoped(); + return serviceCollection; } } diff --git a/Converters/Revit/Speckle.Converters.RevitShared/Speckle.Converters.RevitShared.projitems b/Converters/Revit/Speckle.Converters.RevitShared/Speckle.Converters.RevitShared.projitems index 56045cddb..9b47bf984 100644 --- a/Converters/Revit/Speckle.Converters.RevitShared/Speckle.Converters.RevitShared.projitems +++ b/Converters/Revit/Speckle.Converters.RevitShared/Speckle.Converters.RevitShared.projitems @@ -9,11 +9,8 @@ Speckle.Converters.RevitShared - - - @@ -48,12 +45,11 @@ - - - - - - + + + + + @@ -73,30 +69,12 @@ - - - - + + - - - - - - - - - - - - - - - + - - diff --git a/Converters/Revit/Speckle.Converters.RevitShared/ToHost/Raw/Geometry/ICurveConverterToHost.cs b/Converters/Revit/Speckle.Converters.RevitShared/ToHost/Raw/Geometry/ICurveConverterToHost.cs index 59d2d86c0..00de2f948 100644 --- a/Converters/Revit/Speckle.Converters.RevitShared/ToHost/Raw/Geometry/ICurveConverterToHost.cs +++ b/Converters/Revit/Speckle.Converters.RevitShared/ToHost/Raw/Geometry/ICurveConverterToHost.cs @@ -6,8 +6,6 @@ namespace Speckle.Converters.RevitShared.ToSpeckle; public class ICurveConverterToHost : ITypedConverter { - private readonly ITypedConverter _pointConverter; - private readonly ITypedConverter _vectorConverter; private readonly ITypedConverter _arcConverter; private readonly ITypedConverter _lineConverter; private readonly ITypedConverter _circleConverter; @@ -16,8 +14,6 @@ public class ICurveConverterToHost : ITypedConverter private readonly ITypedConverter _curveConverter; public ICurveConverterToHost( - ITypedConverter pointConverter, - ITypedConverter vectorConverter, ITypedConverter arcConverter, ITypedConverter lineConverter, ITypedConverter circleConverter, @@ -26,8 +22,6 @@ public class ICurveConverterToHost : ITypedConverter ITypedConverter curveConverter ) { - _pointConverter = pointConverter; - _vectorConverter = vectorConverter; _arcConverter = arcConverter; _lineConverter = lineConverter; _circleConverter = circleConverter; diff --git a/Converters/Revit/Speckle.Converters.RevitShared/ToHost/Raw/Geometry/IRawEncodedObjectConverter.cs b/Converters/Revit/Speckle.Converters.RevitShared/ToHost/Raw/Geometry/IRawEncodedObjectConverter.cs index ac98b4d25..ccc3dd17b 100644 --- a/Converters/Revit/Speckle.Converters.RevitShared/ToHost/Raw/Geometry/IRawEncodedObjectConverter.cs +++ b/Converters/Revit/Speckle.Converters.RevitShared/ToHost/Raw/Geometry/IRawEncodedObjectConverter.cs @@ -40,7 +40,7 @@ public class IRawEncodedObjectConverter : ITypedConverter public class LocalToGlobalToDirectShapeConverter - : ITypedConverter<(Base atomicObject, List matrix), DB.DirectShape> + : ITypedConverter<(Base atomicObject, IReadOnlyCollection matrix), DB.DirectShape> { private readonly IConverterSettingsStore _converterSettings; private readonly ITypedConverter<(Matrix4x4 matrix, string units), DB.Transform> _transformConverter; @@ -26,7 +27,7 @@ public class LocalToGlobalToDirectShapeConverter _transformConverter = transformConverter; } - public DB.DirectShape Convert((Base atomicObject, List matrix) target) + public DB.DirectShape Convert((Base atomicObject, IReadOnlyCollection matrix) target) { // 1- set ds category var category = target.atomicObject["builtinCategory"] as string; @@ -52,7 +53,7 @@ public class LocalToGlobalToDirectShapeConverter { var def = DB .DirectShapeLibrary.GetDirectShapeLibrary(_converterSettings.Current.Document) - .FindDefinition(target.atomicObject.applicationId ?? target.atomicObject.id); + .FindDefinition(target.atomicObject.applicationId ?? target.atomicObject.id.NotNull()); result.SetShape(def); return result; // note fast exit here } @@ -72,7 +73,7 @@ public class LocalToGlobalToDirectShapeConverter var transformedGeometries = DB.DirectShape.CreateGeometryInstance( _converterSettings.Current.Document, - target.atomicObject.applicationId ?? target.atomicObject.id, + target.atomicObject.applicationId ?? target.atomicObject.id.NotNull(), combinedTransform ); diff --git a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Properties/ClassPropertiesExtractor.cs b/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Properties/ClassPropertiesExtractor.cs new file mode 100644 index 000000000..28fb58f32 --- /dev/null +++ b/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Properties/ClassPropertiesExtractor.cs @@ -0,0 +1,60 @@ +using Speckle.Converters.Common; +using Speckle.Converters.RevitShared.Extensions; +using Speckle.Converters.RevitShared.Settings; + +namespace Speckle.Converters.RevitShared.ToSpeckle.Properties; + +/// +/// For retrieving general properties used for business intelligence on the class, that are not already captured in parameters. +/// +public class ClassPropertiesExtractor +{ + private readonly IConverterSettingsStore _converterSettings; + + private readonly Dictionary _worksetCache = new(); + + public ClassPropertiesExtractor(IConverterSettingsStore converterSettings) + { + _converterSettings = converterSettings; + } + + public Dictionary GetClassProperties(DB.Element element) + { + Dictionary elementPropertiesDict = ExtractElementProperties(element); + + // add type specific props not included in parameters. + // so far, no extra props are needed + /* + switch (element) + { + default: + break; + } + */ + + return elementPropertiesDict; + } + + // gets the properties on the db.element class + private Dictionary ExtractElementProperties(DB.Element element) + { + Dictionary elementProperties = + new() + { + { "elementId", element.Id.ToString()! }, + { "builtInCategory", element.Category?.GetBuiltInCategory().ToString() }, + { "worksetId", element.WorksetId.ToString() } + }; + + // get workset name + if (!_worksetCache.TryGetValue(element.WorksetId, out var worksetName)) + { + DB.Workset workset = _converterSettings.Current.Document.GetWorksetTable().GetWorkset(element.WorksetId); + worksetName = workset.Name; + _worksetCache[element.WorksetId] = worksetName; + } + elementProperties.Add("worksetName", worksetName); + + return elementProperties; + } +} diff --git a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Parameters/ParameterDefinitionHandler.cs b/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Properties/ParameterDefinitionHandler.cs similarity index 100% rename from Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Parameters/ParameterDefinitionHandler.cs rename to Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Properties/ParameterDefinitionHandler.cs diff --git a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Parameters/ParameterExtractor.cs b/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Properties/ParameterExtractor.cs similarity index 90% rename from Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Parameters/ParameterExtractor.cs rename to Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Properties/ParameterExtractor.cs index bc913be68..68fe1d045 100644 --- a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Parameters/ParameterExtractor.cs +++ b/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Properties/ParameterExtractor.cs @@ -3,7 +3,6 @@ using Speckle.Converters.Common; using Speckle.Converters.RevitShared.Services; using Speckle.Converters.RevitShared.Settings; using Speckle.Sdk; -using Speckle.Sdk.Common; namespace Speckle.Converters.RevitShared.ToSpeckle; @@ -71,25 +70,26 @@ public class ParameterExtractor { // NOTE: this could be paired up and merged with material quantities - they're pretty much the same :/ var factor = _scalingServiceToSpeckle.ScaleLength(1); - var structureDictionary = new Dictionary(); - var structure = hostObjectAttr.GetCompoundStructure(); - var layers = structure?.GetLayers(); - foreach (var layer in layers.Empty()) + if (hostObjectAttr.GetCompoundStructure() is DB.CompoundStructure structure) // GetCompoundStructure can return null { - if (_settingsStore.Current.Document.GetElement(layer.MaterialId) is DB.Material material) + Dictionary structureDictionary = new(); + foreach (var layer in structure.GetLayers()) { - var uniqueLayerName = $"{material.Name} ({layer.LayerId})"; - structureDictionary[uniqueLayerName] = new Dictionary() + if (_settingsStore.Current.Document.GetElement(layer.MaterialId) is DB.Material material) { - ["material"] = material.Name, - ["function"] = layer.Function.ToString(), - ["thickness"] = layer.Width * factor, - ["units"] = _settingsStore.Current.SpeckleUnits - }; + var uniqueLayerName = $"{material.Name} ({layer.LayerId})"; + structureDictionary[uniqueLayerName] = new Dictionary() + { + ["material"] = material.Name, + ["function"] = layer.Function.ToString(), + ["thickness"] = layer.Width * factor, + ["units"] = _settingsStore.Current.SpeckleUnits + }; + } } - } - typeParameterDictionary["Structure"] = structureDictionary; + typeParameterDictionary["Structure"] = structureDictionary; + } } _typeParameterCache[typeId] = typeParameterDictionary; diff --git a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Properties/PropertiesExtractor.cs b/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Properties/PropertiesExtractor.cs new file mode 100644 index 000000000..02565d0f8 --- /dev/null +++ b/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Properties/PropertiesExtractor.cs @@ -0,0 +1,43 @@ +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.RevitShared.ToSpeckle.Properties; + +public class PropertiesExtractor +{ + private readonly ClassPropertiesExtractor _classPropertiesExtractor; + private readonly ParameterExtractor _parameterExtractor; + private readonly ITypedConverter> _materialQuantityConverter; + + public PropertiesExtractor( + ClassPropertiesExtractor classPropertiesExtractor, + ParameterExtractor parameterExtractor, + ITypedConverter> materialQuantityConverter + ) + { + _classPropertiesExtractor = classPropertiesExtractor; + _parameterExtractor = parameterExtractor; + _materialQuantityConverter = materialQuantityConverter; + } + + public Dictionary GetProperties(DB.Element element) + { + // by default, always get class properties first + Dictionary properties = _classPropertiesExtractor.GetClassProperties(element); + + // add material quantities + Dictionary matQuantities = _materialQuantityConverter.Convert(element); + if (matQuantities.Count > 0) + { + properties.Add("Material Quantities", matQuantities); + } + + // add parameters + Dictionary parameters = _parameterExtractor.GetParameters(element); + if (parameters.Count > 0) + { + properties.Add("Parameters", parameters); + } + + return properties; + } +} diff --git a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Properties/StructuralMaterialAssetExtractor.cs b/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Properties/StructuralMaterialAssetExtractor.cs new file mode 100644 index 000000000..77c3952bd --- /dev/null +++ b/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Properties/StructuralMaterialAssetExtractor.cs @@ -0,0 +1,116 @@ +using Speckle.Converters.Common; +using Speckle.Converters.RevitShared.Services; +using Speckle.Converters.RevitShared.Settings; +using Speckle.Sdk; +using Speckle.Sdk.Common; + +namespace Speckle.Converters.Revit2023.ToSpeckle.Properties; + +public readonly struct StructuralAssetProperties( + string name, + double density, + DB.ForgeTypeId densityUnitId, + string materialType, + double? compressiveStrength, + DB.ForgeTypeId? compressiveStrengthUnitId +) +{ + public string Name { get; } = name; + public double Density { get; } = density; + public DB.ForgeTypeId DensityUnitId { get; } = densityUnitId; + public string MaterialType { get; } = materialType; + public double? CompressiveStrength { get; } = compressiveStrength; + public DB.ForgeTypeId? CompressiveStrengthUnitId { get; } = compressiveStrengthUnitId; +} + +public class StructuralMaterialAssetExtractor +{ + private readonly ScalingServiceToSpeckle _scalingService; + private readonly IConverterSettingsStore _converterSettings; + private readonly Dictionary _structuralAssetCache = new(); + + public StructuralMaterialAssetExtractor( + ScalingServiceToSpeckle scalingService, + IConverterSettingsStore converterSettings + ) + { + _scalingService = scalingService; + _converterSettings = converterSettings; + } + + /// + /// Attempts to get structural asset properties, using cached values if available. + /// + public StructuralAssetProperties TryGetProperties(DB.ElementId assetId) + { + // ensure safe string conversion + string assetIdString = assetId.ToString().NotNull(); + + // check cache if properties have already been extracted + if (_structuralAssetCache.TryGetValue(assetIdString, out var cachedProperties)) + { + return cachedProperties; + } + + // if not in cache but structural asset id is valid => extract properties + var extractedProperties = ExtractProperties(assetId); + _structuralAssetCache[assetIdString] = extractedProperties; + return extractedProperties; + } + + /// + /// Gets the material properties from a structural asset including density, material type, + /// and material-specific properties like compressive strength for concrete. + /// + /// + /// All values are scaled from internal units to model units + /// + private StructuralAssetProperties ExtractProperties(DB.ElementId structuralAssetId) + { + // NOTE: assetId != DB.ElementId.InvalidElementId checked in calling method. Assuming a valid StructuralAssetId + if ( + _converterSettings.Current.Document.GetElement(structuralAssetId) is not DB.PropertySetElement propertySetElement + ) + { + throw new SpeckleException("Structural material asset is not of expected type."); + } + DB.StructuralAsset structuralAsset = propertySetElement.GetStructuralAsset(); + + // get unit forge type id + DB.ForgeTypeId densityUnitId = _converterSettings + .Current.Document.GetUnits() + .GetFormatOptions(DB.SpecTypeId.MassDensity) + .GetUnitTypeId(); + + // scale from internal to model units + double densityValue = _scalingService.Scale(structuralAsset.Density, densityUnitId); + + // get material type + string materialType = structuralAsset.StructuralAssetClass.ToString(); + + // initialize optional concrete properties + double? compressiveStrength = null; + DB.ForgeTypeId? stressUnitId = null; + + // if concrete, extract compressive strength + if (materialType == DB.StructuralAssetClass.Concrete.ToString()) + { + stressUnitId = _converterSettings + .Current.Document.GetUnits() + .GetFormatOptions(DB.SpecTypeId.AreaForce) + .GetUnitTypeId(); + + compressiveStrength = _scalingService.Scale(structuralAsset.ConcreteCompression, stressUnitId); + } + + // return value and units + return new StructuralAssetProperties( + name: structuralAsset.Name, + density: densityValue, + densityUnitId: densityUnitId, + materialType: materialType, + compressiveStrength: compressiveStrength, + compressiveStrengthUnitId: stressUnitId + ); + } +} diff --git a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/BeamConversionToSpeckle.cs b/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/BeamConversionToSpeckle.cs deleted file mode 100644 index 486b7481b..000000000 --- a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/BeamConversionToSpeckle.cs +++ /dev/null @@ -1,67 +0,0 @@ -using Speckle.Converters.Common; -using Speckle.Converters.Common.Objects; -using Speckle.Converters.RevitShared.Helpers; -using Speckle.Converters.RevitShared.Settings; -using Speckle.Objects; -using Speckle.Sdk.Common.Exceptions; -using Speckle.Sdk.Models; - -namespace Speckle.Converters.RevitShared.ToSpeckle; - -// POC: There is no validation on this converter to prevent conversion from "not a Revit Beam" to a Speckle Beam. -// This will definitely explode if we tried. Goes back to the `CanConvert` functionality conversation. -// As-is, what we are saying is that it can take "any Family Instance" and turn it into a Speckle.RevitBeam, which is far from correct. -// CNX-9312 -public class BeamConversionToSpeckle : ITypedConverter -{ - private readonly ITypedConverter _locationConverter; - private readonly ITypedConverter _levelConverter; - private readonly ParameterValueExtractor _parameterValueExtractor; - private readonly DisplayValueExtractor _displayValueExtractor; - private readonly IConverterSettingsStore _converterSettings; - - public BeamConversionToSpeckle( - ITypedConverter locationConverter, - ITypedConverter levelConverter, - ParameterValueExtractor parameterValueExtractor, - DisplayValueExtractor displayValueExtractor, - IConverterSettingsStore converterSettings - ) - { - _locationConverter = locationConverter; - _levelConverter = levelConverter; - _parameterValueExtractor = parameterValueExtractor; - _displayValueExtractor = displayValueExtractor; - _converterSettings = converterSettings; - } - - public SOBR.RevitBeam Convert(DB.FamilyInstance target) - { - var baseGeometry = _locationConverter.Convert(target.Location); - if (baseGeometry is not ICurve baseCurve) - { - throw new ValidationException( - $"Beam location conversion did not yield an ICurve, instead it yielded an object of type {baseGeometry.GetType()}" - ); - } - var symbol = (DB.FamilySymbol)target.Document.GetElement(target.GetTypeId()); - var level = _parameterValueExtractor.GetValueAsDocumentObject( - target, - DB.BuiltInParameter.INSTANCE_REFERENCE_LEVEL_PARAM - ); - List displayValue = _displayValueExtractor.GetDisplayValue(target); - - SOBR.RevitBeam speckleBeam = - new() - { - family = symbol.FamilyName, - type = target.Document.GetElement(target.GetTypeId()).Name, - baseLine = baseCurve, - level = _levelConverter.Convert(level), - displayValue = displayValue, - units = _converterSettings.Current.SpeckleUnits - }; - - return speckleBeam; - } -} diff --git a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/BoundarySegmentConversionToSpeckle.cs b/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/BoundarySegmentConversionToSpeckle.cs deleted file mode 100644 index dc335f2dc..000000000 --- a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/BoundarySegmentConversionToSpeckle.cs +++ /dev/null @@ -1,44 +0,0 @@ -using Speckle.Converters.Common; -using Speckle.Converters.Common.Objects; -using Speckle.Converters.RevitShared.Settings; -using Speckle.Objects; - -namespace Speckle.Converters.RevitShared.ToSpeckle; - -public class BoundarySegmentConversionToSpeckle : ITypedConverter, SOG.Polycurve> -{ - private readonly ITypedConverter _curveConverter; - private readonly IConverterSettingsStore _converterSettings; - - public BoundarySegmentConversionToSpeckle( - ITypedConverter curveConverter, - IConverterSettingsStore converterSettings - ) - { - _curveConverter = curveConverter; - _converterSettings = converterSettings; - } - - public SOG.Polycurve Convert(IList target) - { - if (target.Count == 0) - { - throw new ArgumentException("Input Boundary segment list must at least have 1 segment"); - } - - List segments = new(target.Count); - foreach (var segment in target) - { - DB.Curve revitCurve = segment.GetCurve(); - var curve = _curveConverter.Convert(revitCurve); - - // POC: We used to attach the `elementID` of every curve in a PolyCurve as a dynamic property. - // We've removed this as it seemed unnecessary. - - segments.Add(curve); - } - - var poly = new SOG.Polycurve { segments = segments, units = _converterSettings.Current.SpeckleUnits }; - return poly; - } -} diff --git a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/BraceToSpeckleConverter.cs b/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/BraceToSpeckleConverter.cs deleted file mode 100644 index 5c6b2f7ea..000000000 --- a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/BraceToSpeckleConverter.cs +++ /dev/null @@ -1,55 +0,0 @@ -using Speckle.Converters.Common; -using Speckle.Converters.Common.Objects; -using Speckle.Converters.RevitShared.Settings; -using Speckle.Sdk.Models; - -namespace Speckle.Converters.RevitShared.ToSpeckle; - -// POC: There is no validation on this converter to prevent conversion from "not a Revit Beam" to a Speckle Beam. -// This will definitely explode if we tried. Goes back to the `CanConvert` functionality conversation. -// As-is, what we are saying is that it can take "any Family Instance" and turn it into a Speckle.RevitBeam, which is far from correct. -// CNX-9312 -public class BraceToSpeckleConverter : ITypedConverter -{ - private readonly ITypedConverter _beamConverter; - private readonly IConverterSettingsStore _converterSettings; - - public BraceToSpeckleConverter( - ITypedConverter beamConverter, - IConverterSettingsStore converterSettings - ) - { - _beamConverter = beamConverter; - _converterSettings = converterSettings; - } - - public SOBR.RevitBrace Convert(DB.FamilyInstance target) - { - // POC: we might want some easy one-liner here to FamilyMatchesOrThrow(target, DB.Structure.StructuralType.Brace) or similar - // and added in each Convert - // POC: this and the beam lost the notes we were returning, though this seems against even the original pattern - - var beam = _beamConverter.Convert(target); - - var brace = new SOBR.RevitBrace() - { - applicationId = beam.applicationId, - type = beam.type, - baseLine = beam.baseLine, - level = beam.level, - family = beam.family, - parameters = beam.parameters, - displayValue = beam.displayValue, - units = _converterSettings.Current.SpeckleUnits - }; - - var dynamicProps = beam.GetMembers(DynamicBaseMemberType.Dynamic); - - foreach (var dp in dynamicProps) - { - brace[dp.Key] = dp.Value; - } - - return brace; - } -} diff --git a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/ColumnConversionToSpeckle.cs b/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/ColumnConversionToSpeckle.cs deleted file mode 100644 index fa100e20f..000000000 --- a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/ColumnConversionToSpeckle.cs +++ /dev/null @@ -1,147 +0,0 @@ -using Autodesk.Revit.DB; -using Speckle.Converters.Common; -using Speckle.Converters.Common.Objects; -using Speckle.Converters.RevitShared.Helpers; -using Speckle.Converters.RevitShared.Settings; -using Speckle.Objects; -using Speckle.Objects.BuiltElements.Revit; -using Speckle.Sdk.Common; -using Speckle.Sdk.Common.Exceptions; -using Speckle.Sdk.Models; - -namespace Speckle.Converters.RevitShared.ToSpeckle; - -// POC: There is no validation on this converter to prevent conversion from "not a Revit Beam" to a Speckle Beam. -// This will definitely explode if we tried. Goes back to the `CanConvert` functionality conversation. -public class ColumnConversionToSpeckle : ITypedConverter -{ - private readonly ITypedConverter _locationConverter; - private readonly ITypedConverter _levelConverter; - private readonly ParameterValueExtractor _parameterValueExtractor; - private readonly DisplayValueExtractor _displayValueExtractor; - private readonly IConverterSettingsStore _converterSettings; - - public ColumnConversionToSpeckle( - ITypedConverter locationConverter, - ITypedConverter levelConverter, - ParameterValueExtractor parameterValueExtractor, - DisplayValueExtractor displayValueExtractor, - IConverterSettingsStore converterSettings - ) - { - _locationConverter = locationConverter; - _levelConverter = levelConverter; - _parameterValueExtractor = parameterValueExtractor; - _displayValueExtractor = displayValueExtractor; - _converterSettings = converterSettings; - } - - public RevitColumn Convert(DB.FamilyInstance target) - { - FamilySymbol symbol = (FamilySymbol)target.Document.GetElement(target.GetTypeId()); - List displayValue = _displayValueExtractor.GetDisplayValue(target); - - RevitColumn speckleColumn = - new() - { - family = symbol.FamilyName, - type = target.Document.GetElement(target.GetTypeId()).Name, - facingFlipped = target.FacingFlipped, - handFlipped = target.HandFlipped, - isSlanted = target.IsSlantedColumn, - displayValue = displayValue, - units = _converterSettings.Current.SpeckleUnits - }; - - if ( - _parameterValueExtractor.TryGetValueAsDocumentObject( - target, - BuiltInParameter.FAMILY_BASE_LEVEL_PARAM, - out var level - ) - ) - { - speckleColumn.level = _levelConverter.Convert(level); - } - if ( - _parameterValueExtractor.TryGetValueAsDocumentObject( - target, - BuiltInParameter.FAMILY_TOP_LEVEL_PARAM, - out var topLevel - ) - ) - { - speckleColumn.topLevel = _levelConverter.Convert(topLevel); - } - - if ( - _parameterValueExtractor.TryGetValueAsDouble( - target, - BuiltInParameter.FAMILY_BASE_LEVEL_OFFSET_PARAM, - out var baseOffset - ) - ) - { - speckleColumn.baseOffset = baseOffset.NotNull(); - } - - if ( - _parameterValueExtractor.TryGetValueAsDouble( - target, - BuiltInParameter.FAMILY_TOP_LEVEL_OFFSET_PARAM, - out var topOffset - ) - ) - { - speckleColumn.topOffset = topOffset.NotNull(); - } - - speckleColumn.baseLine = - GetBaseCurve(target, speckleColumn.topLevel?.elevation ?? -1, speckleColumn.topOffset) - ?? throw new ValidationException("Unable to find a valid baseCurve for column"); - - if (target.Location is LocationPoint locationPoint) - { - speckleColumn.rotation = locationPoint.Rotation; - } - - return speckleColumn; - } - - private ICurve? GetBaseCurve(DB.FamilyInstance target, double topLevelElevation, double topLevelOffset) - { - Base baseGeometry = _locationConverter.Convert(target.Location); - ICurve? baseCurve = baseGeometry as ICurve; - - if (baseGeometry is ICurve) - { - return baseCurve; - } - else if (baseGeometry is SOG.Point basePoint) - { - // POC: in existing connector, we are sending column as Revit Instance instead of Column with the following if. - // I am not sure why. I think this if is checking if the column has a fixed height - //if ( - // symbol.Family.FamilyPlacementType == FamilyPlacementType.OneLevelBased - // || symbol.Family.FamilyPlacementType == FamilyPlacementType.WorkPlaneBased - //) - //{ - // return RevitInstanceToSpeckle(revitColumn, out notes, null); - //} - - return new SOG.Line - { - start = basePoint, - end = new SOG.Point( - basePoint.x, - basePoint.y, - topLevelElevation + topLevelOffset, - _converterSettings.Current.SpeckleUnits - ), - units = _converterSettings.Current.SpeckleUnits, - }; - } - - return null; - } -} diff --git a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/MeshByMaterialDictionaryToSpeckle.cs b/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/MeshByMaterialDictionaryToSpeckle.cs index d9c9bd49d..99dadf28e 100644 --- a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/MeshByMaterialDictionaryToSpeckle.cs +++ b/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/MeshByMaterialDictionaryToSpeckle.cs @@ -23,7 +23,7 @@ public class MeshByMaterialDictionaryToSpeckle { name = "Transparent", diffuse = System.Drawing.Color.Transparent.ToArgb(), - opacity = 0, + opacity = 0.3, applicationId = "material_Transparent" }; diff --git a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/PointcloudTopLevelConverterToSpeckle.cs b/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/PointcloudToSpeckleConverter.cs similarity index 69% rename from Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/PointcloudTopLevelConverterToSpeckle.cs rename to Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/PointcloudToSpeckleConverter.cs index 3c911b8e7..f31bdc580 100644 --- a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/PointcloudTopLevelConverterToSpeckle.cs +++ b/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/PointcloudToSpeckleConverter.cs @@ -1,18 +1,17 @@ using Speckle.Converters.Common; using Speckle.Converters.Common.Objects; using Speckle.Converters.RevitShared.Settings; +using Speckle.Sdk.Models; namespace Speckle.Converters.RevitShared.ToSpeckle; -[NameAndRankValue(nameof(DB.PointCloudInstance), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] -public sealed class PointcloudTopLevelConverterToSpeckle - : BaseTopLevelConverterToSpeckle +public sealed class PointcloudToSpeckleConverter : ITypedConverter { private readonly IConverterSettingsStore _converterSettings; private readonly ITypedConverter _xyzToPointConverter; private readonly ITypedConverter _boundingBoxConverter; - public PointcloudTopLevelConverterToSpeckle( + public PointcloudToSpeckleConverter( IConverterSettingsStore converterSettings, ITypedConverter xyzToPointConverter, ITypedConverter boundingBoxConverter @@ -23,20 +22,21 @@ public sealed class PointcloudTopLevelConverterToSpeckle _boundingBoxConverter = boundingBoxConverter; } - public override SOG.Pointcloud Convert(DB.PointCloudInstance target) + public Base Convert(object target) => Convert((DB.PointCloudInstance)target); + + public SOG.Pointcloud Convert(DB.PointCloudInstance target) { - var boundingBox = target.get_BoundingBox(null!); + var boundingBox = target.get_BoundingBox(null!); // the bounding box in the parent document + var minPlane = DB.Plane.CreateByNormalAndOrigin(DB.XYZ.BasisZ, boundingBox.Min); // the lowest z plane from the bounding box min, in the parent doc using DB.Transform transform = target.GetTransform(); { - var minPlane = DB.Plane.CreateByNormalAndOrigin(DB.XYZ.BasisZ, transform.OfPoint(boundingBox.Min)); var filter = DB.PointClouds.PointCloudFilterFactory.CreateMultiPlaneFilter(new List() { minPlane }); var points = target.GetPoints(filter, 0.0001, 999999); // max limit is 1 mil but 1000000 throws error - // POC: complaining about nullability var specklePointCloud = new SOG.Pointcloud { points = points - .Select(o => _xyzToPointConverter.Convert(transform.OfPoint(o))) + .Select(o => _xyzToPointConverter.Convert(transform.OfPoint(o))) // these points need to be transformed, since coords are in the pointcloud linked doc .SelectMany(o => new List() { o.x, o.y, o.z }) .ToList(), colors = points.Select(o => o.Color).ToList(), diff --git a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/LocationConversionToSpeckle.cs b/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/LocationToSpeckleConverter.cs similarity index 74% rename from Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/LocationConversionToSpeckle.cs rename to Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/LocationToSpeckleConverter.cs index f0f59de09..db3b08144 100644 --- a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/LocationConversionToSpeckle.cs +++ b/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/LocationToSpeckleConverter.cs @@ -5,15 +5,12 @@ using Speckle.Sdk.Models; namespace Speckle.Converters.RevitShared.ToSpeckle; -public class LocationConversionToSpeckle : ITypedConverter +public class LocationToSpeckleConverter : ITypedConverter { private readonly ITypedConverter _curveConverter; private readonly ITypedConverter _xyzConverter; - // POC: review IRawConversion which always returns a Base, this is ToSpeckle, so... this breaks - // the meaning of IRawConversion, it could be IToSpeckleRawConversion - // also a factory type - public LocationConversionToSpeckle( + public LocationToSpeckleConverter( ITypedConverter curveConverter, ITypedConverter xyzConverter ) diff --git a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/MaterialQuantitiesToSpeckle.cs b/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/MaterialQuantitiesToSpeckle.cs index 421fd7d14..91474209e 100644 --- a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/MaterialQuantitiesToSpeckle.cs +++ b/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/MaterialQuantitiesToSpeckle.cs @@ -1,39 +1,52 @@ using Speckle.Converters.Common; using Speckle.Converters.Common.Objects; +using Speckle.Converters.Revit2023.ToSpeckle.Properties; using Speckle.Converters.RevitShared.Services; using Speckle.Converters.RevitShared.Settings; namespace Speckle.Converters.RevitShared.ToSpeckle; /// -/// Lighter converter for material quantities. It basically returns a For each material quantity available on the target element, it will return a dictionary containing: area, volume, units, material name, material class and material category. -/// POC: we need to validate this with user needs. It currently does not include material parameters or any other more complex props to ensure speedy sending of data and a lighter payload. We're though keen to re-add more data provided we can validate it. +/// Lighter converter for material quantities. /// +/// +/// We need to validate this with user needs. Currently limited to: +/// +/// material category +/// material class +/// material name +/// area +/// volume +/// density (if valid StructuralAssetId) +/// type (if valid StructuralAssetId) +/// concrete compressive strength (if valid StructuralAssetId and of type concrete) +/// +/// We're attaching density, type and concrete compression (if concrete) to all objects. This is still "lite". If we add +/// more structural asset properties we should move to a proxy approach. +/// public class MaterialQuantitiesToSpeckleLite : ITypedConverter> { private readonly ScalingServiceToSpeckle _scalingService; private readonly IConverterSettingsStore _converterSettings; + private readonly StructuralMaterialAssetExtractor _structuralAssetExtractor; public MaterialQuantitiesToSpeckleLite( ScalingServiceToSpeckle scalingService, - IConverterSettingsStore converterSettings + IConverterSettingsStore converterSettings, + StructuralMaterialAssetExtractor structuralAssetExtractor ) { _scalingService = scalingService; _converterSettings = converterSettings; + _structuralAssetExtractor = structuralAssetExtractor; } - /// - /// Lighter conversion of material quantities to speckle. For each material quantity available on the target element, it will return a dictionary containing: area, volume, units, material name, material class and material category. - /// - /// - /// public Dictionary Convert(DB.Element target) { Dictionary quantities = new(); if (target.Category?.HasMaterialQuantities ?? false) //category can be null { - foreach (DB.ElementId matId in target.GetMaterialIds(false)) + foreach (DB.ElementId? matId in target.GetMaterialIds(false)) { if (matId is null) { @@ -41,17 +54,65 @@ public class MaterialQuantitiesToSpeckleLite : ITypedConverter(); + var unitSettings = _converterSettings.Current.Document.GetUnits(); - double factor = _scalingService.ScaleLength(1); - materialQuantity["area"] = factor * factor * target.GetMaterialArea(matId, false); - materialQuantity["volume"] = factor * factor * factor * target.GetMaterialVolume(matId); - materialQuantity["units"] = _converterSettings.Current.SpeckleUnits; + var areaUnitType = unitSettings.GetFormatOptions(DB.SpecTypeId.Area).GetUnitTypeId(); + AddMaterialProperty( + materialQuantity, + "area", + _scalingService.Scale(target.GetMaterialArea(matId, false), areaUnitType), + areaUnitType + ); + + var volumeUnitType = unitSettings.GetFormatOptions(DB.SpecTypeId.Volume).GetUnitTypeId(); + AddMaterialProperty( + materialQuantity, + "volume", + _scalingService.Scale(target.GetMaterialVolume(matId), volumeUnitType), + volumeUnitType + ); if (_converterSettings.Current.Document.GetElement(matId) is DB.Material material) { materialQuantity["materialName"] = material.Name; materialQuantity["materialCategory"] = material.MaterialCategory; materialQuantity["materialClass"] = material.MaterialClass; + + // get StructuralAssetId (or try to) + DB.ElementId structuralAssetId = material.StructuralAssetId; + if (structuralAssetId != DB.ElementId.InvalidElementId) + { + StructuralAssetProperties structuralAssetProperties = _structuralAssetExtractor.TryGetProperties( + structuralAssetId + ); + + materialQuantity["structuralAsset"] = structuralAssetProperties.Name; + AddMaterialProperty( + materialQuantity, + "density", + structuralAssetProperties.Density, + structuralAssetProperties.DensityUnitId + ); + + // more reliable way of determining material type (wood/concrete/type) as it uses Revit enum + // materialClass, materialCategory etc. are user string inputs + materialQuantity["materialType"] = structuralAssetProperties.MaterialType; + + // Only add compressive strength for concrete materials (used by F+E for Automate) + if ( + structuralAssetProperties.MaterialType == "Concrete" + && structuralAssetProperties.CompressiveStrength.HasValue + ) + { + AddMaterialProperty( + materialQuantity, + "compressiveStrength", + structuralAssetProperties.CompressiveStrength.Value, + structuralAssetProperties.CompressiveStrengthUnitId! + ); + } + } + quantities[material.Name] = materialQuantity; } } @@ -59,4 +120,29 @@ public class MaterialQuantitiesToSpeckleLite : ITypedConverter + /// Adds a material property to the given dictionary with standardized structure. + /// + /// The dictionary to mutate with the new property + /// The name of the property (e.g., "area", "volume", "density") + /// The numeric value of the property + /// The Forge type ID representing the units of the property + /// + /// Saves code when used repeatedbly. Etabs implements an extension method to dicts (see utils folder). May be worth exploring. + /// + private void AddMaterialProperty( + Dictionary materialQuantity, + string name, + double value, + DB.ForgeTypeId unitId + ) + { + materialQuantity[name] = new Dictionary + { + ["name"] = name, + ["value"] = value, + ["units"] = DB.LabelUtils.GetLabelForUnit(unitId) + }; + } } diff --git a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/BaseTopLevelConverterToSpeckle.cs b/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/BaseTopLevelConverterToSpeckle.cs deleted file mode 100644 index b17f0446d..000000000 --- a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/BaseTopLevelConverterToSpeckle.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Speckle.Converters.Common.Objects; -using Speckle.Sdk.Common.Exceptions; -using Speckle.Sdk.Models; - -namespace Speckle.Converters.RevitShared.ToSpeckle; - -// POC: maybe but could be generic abstract Converters.Common? -// or maybe it's not actually doing very much now and can come out -public abstract class BaseTopLevelConverterToSpeckle - : IToSpeckleTopLevelConverter, - // POC: why do we need to do this for each base conversion? - ITypedConverter - where TSpeckle : Base -{ - public Base Convert(object target) - { - var result = Convert((THost)target); - - // POC: unless I am going bonkers, we've constrained TSpeckle to Base - // so it should always BE base? - if (result is not Base @base) - { - throw new ConversionException($"Expected resulting object to be {typeof(Base)} but was {result.GetType()}"); - } - - return @base; - } - - public abstract TSpeckle Convert(THost target); -} diff --git a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/CeilingTopLevelConverterToSpeckle.cs b/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/CeilingTopLevelConverterToSpeckle.cs deleted file mode 100644 index 2cb8a5022..000000000 --- a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/CeilingTopLevelConverterToSpeckle.cs +++ /dev/null @@ -1,69 +0,0 @@ -using Autodesk.Revit.DB; -using Speckle.Converters.Common; -using Speckle.Converters.Common.Objects; -using Speckle.Converters.RevitShared.Helpers; -using Speckle.Converters.RevitShared.Settings; -using Speckle.Objects; -using Speckle.Objects.BuiltElements.Revit; -using Speckle.Objects.Geometry; - -namespace Speckle.Converters.RevitShared.ToSpeckle; - -[NameAndRankValue(nameof(Ceiling), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] -internal sealed class CeilingTopLevelConverterToSpeckle : BaseTopLevelConverterToSpeckle -{ - private readonly ITypedConverter> _curveArrArrayConverter; - private readonly ITypedConverter _levelConverter; - private readonly ParameterValueExtractor _parameterValueExtractor; - private readonly DisplayValueExtractor _displayValueExtractor; - private readonly IConverterSettingsStore _converterSettings; - - public CeilingTopLevelConverterToSpeckle( - ITypedConverter> curveArrArrayConverter, - ITypedConverter levelConverter, - ParameterValueExtractor parameterValueExtractor, - DisplayValueExtractor displayValueExtractor, - IConverterSettingsStore converterSettings - ) - { - _curveArrArrayConverter = curveArrArrayConverter; - _levelConverter = levelConverter; - _parameterValueExtractor = parameterValueExtractor; - _displayValueExtractor = displayValueExtractor; - _converterSettings = converterSettings; - } - - public override RevitCeiling Convert(Ceiling target) - { - var elementType = (ElementType)target.Document.GetElement(target.GetTypeId()); - // POC: our existing receive operation is checking the "slopeDirection" prop, - // but it is never being set. We should be setting it - var level = _parameterValueExtractor.GetValueAsDocumentObject(target, BuiltInParameter.LEVEL_PARAM); - RevitLevel speckleLevel = _levelConverter.Convert(level); - List displayValue = _displayValueExtractor.GetDisplayValue(target); - - RevitCeiling speckleCeiling = - new() - { - type = elementType.Name, - family = elementType.FamilyName, - level = speckleLevel, - displayValue = displayValue, - units = _converterSettings.Current.SpeckleUnits - }; - - var sketch = (Sketch)target.Document.GetElement(target.SketchId); - List profiles = _curveArrArrayConverter.Convert(sketch.Profile); - // POC: https://spockle.atlassian.net/browse/CNX-9396 - if (profiles.Count > 0) - { - speckleCeiling.outline = profiles[0]; - } - if (profiles.Count > 1) - { - speckleCeiling.voids = profiles.Skip(1).ToList(); - } - - return speckleCeiling; - } -} diff --git a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/DirectShapeTopLevelConverterToSpeckle.cs b/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/DirectShapeTopLevelConverterToSpeckle.cs deleted file mode 100644 index 417a174b9..000000000 --- a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/DirectShapeTopLevelConverterToSpeckle.cs +++ /dev/null @@ -1,48 +0,0 @@ -using Speckle.Converters.Common; -using Speckle.Converters.RevitShared.Extensions; -using Speckle.Converters.RevitShared.Helpers; -using Speckle.Converters.RevitShared.Settings; -using Speckle.Converters.RevitShared.ToSpeckle; -using Speckle.Sdk.Common; -using Speckle.Sdk.Models; - -namespace Speckle.Converters.Revit2023.ToSpeckle; - -[NameAndRankValue(nameof(DB.DirectShape), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] -public class DirectShapeTopLevelConverterToSpeckle : BaseTopLevelConverterToSpeckle -{ - private readonly IConverterSettingsStore _converterSettings; - private readonly DisplayValueExtractor _displayValueExtractor; - - public DirectShapeTopLevelConverterToSpeckle( - IConverterSettingsStore converterSettings, - DisplayValueExtractor displayValueExtractor - ) - { - _converterSettings = converterSettings; - _displayValueExtractor = displayValueExtractor; - } - - public override SOBR.DirectShape Convert(DB.DirectShape target) - { - var category = target.Category.GetBuiltInCategory().GetSchemaBuilderCategoryFromBuiltIn(); - - // POC: Making the analogy that the DisplayValue is the same as the Geometries is only valid while we don't support Solids on send. - var geometries = _displayValueExtractor.GetDisplayValue(target).Cast().ToList(); - - SOBR.DirectShape result = - new() - { - name = target.Name, - category = category, - baseGeometries = geometries, - displayValue = geometries, - units = _converterSettings.Current.SpeckleUnits, - elementId = target.Id.ToString().NotNull() - }; - - result["type"] = target.Name; - - return result; - } -} diff --git a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/ElementTopLevelConverterToSpeckle.cs b/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/ElementTopLevelConverterToSpeckle.cs deleted file mode 100644 index cb38a7464..000000000 --- a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/ElementTopLevelConverterToSpeckle.cs +++ /dev/null @@ -1,45 +0,0 @@ -using Speckle.Converters.Common; -using Speckle.Converters.RevitShared.Helpers; -using Speckle.Converters.RevitShared.Settings; -using Speckle.Objects.BuiltElements.Revit; - -namespace Speckle.Converters.RevitShared.ToSpeckle; - -// POC: used as placeholder for revit instances -[NameAndRankValue(nameof(DB.Element), 0)] -public class ElementTopLevelConverterToSpeckle : BaseTopLevelConverterToSpeckle -{ - private readonly DisplayValueExtractor _displayValueExtractor; - private readonly IConverterSettingsStore _converterSettings; - - public ElementTopLevelConverterToSpeckle( - DisplayValueExtractor displayValueExtractor, - IConverterSettingsStore converterSettings - ) - { - _displayValueExtractor = displayValueExtractor; - _converterSettings = converterSettings; - } - - public override RevitElement Convert(DB.Element target) - { - string family = target.Document.GetElement(target.GetTypeId()) is DB.FamilySymbol symbol - ? symbol.FamilyName - : "no family"; - string category = target.Category?.Name ?? "no category"; - List displayValue = _displayValueExtractor.GetDisplayValue(target); - - RevitElement speckleElement = - new() - { - type = target.Name, - category = category, - family = family, - displayValue = displayValue - }; - - speckleElement["units"] = _converterSettings.Current.SpeckleUnits; - - return speckleElement; - } -} diff --git a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/ExtrusionRoofToSpeckleTopLevelConverter.cs b/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/ExtrusionRoofToSpeckleTopLevelConverter.cs deleted file mode 100644 index 6fe8823a1..000000000 --- a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/ExtrusionRoofToSpeckleTopLevelConverter.cs +++ /dev/null @@ -1,79 +0,0 @@ -using Speckle.Converters.Common; -using Speckle.Converters.Common.Objects; -using Speckle.Converters.RevitShared.Helpers; -using Speckle.Converters.RevitShared.Settings; -using Speckle.Objects.BuiltElements.Revit; -using Speckle.Objects.BuiltElements.Revit.RevitRoof; - -namespace Speckle.Converters.RevitShared.ToSpeckle; - -[NameAndRankValue(nameof(DB.ExtrusionRoof), 0)] -public class ExtrusionRoofToSpeckleTopLevelConverter - : BaseTopLevelConverterToSpeckle -{ - private readonly ITypedConverter _levelConverter; - private readonly ITypedConverter _modelCurveArrayConverter; - private readonly ITypedConverter _pointConverter; - private readonly ParameterValueExtractor _parameterValueExtractor; - private readonly DisplayValueExtractor _displayValueExtractor; - private readonly IConverterSettingsStore _converterSettings; - - public ExtrusionRoofToSpeckleTopLevelConverter( - ITypedConverter levelConverter, - ITypedConverter modelCurveArrayConverter, - ITypedConverter pointConverter, - ParameterValueExtractor parameterValueExtractor, - DisplayValueExtractor displayValueExtractor, - IConverterSettingsStore converterSettings - ) - { - _levelConverter = levelConverter; - _modelCurveArrayConverter = modelCurveArrayConverter; - _pointConverter = pointConverter; - _parameterValueExtractor = parameterValueExtractor; - _displayValueExtractor = displayValueExtractor; - _converterSettings = converterSettings; - } - - public override RevitExtrusionRoof Convert(DB.ExtrusionRoof target) - { - SOG.Line referenceLine = ConvertReferenceLine(target); - var level = _parameterValueExtractor.GetValueAsDocumentObject( - target, - DB.BuiltInParameter.ROOF_CONSTRAINT_LEVEL_PARAM - ); - RevitLevel speckleLevel = _levelConverter.Convert(level); - SOG.Polycurve outline = _modelCurveArrayConverter.Convert(target.GetProfile()); - var elementType = (DB.ElementType)target.Document.GetElement(target.GetTypeId()); - List displayValue = _displayValueExtractor.GetDisplayValue(target); - - RevitExtrusionRoof speckleExtrusionRoof = - new() - { - start = _parameterValueExtractor.GetValueAsDouble(target, DB.BuiltInParameter.EXTRUSION_START_PARAM), - end = _parameterValueExtractor.GetValueAsDouble(target, DB.BuiltInParameter.EXTRUSION_END_PARAM), - type = elementType.Name, - family = elementType.FamilyName, - outline = outline, - referenceLine = referenceLine, - level = speckleLevel, - displayValue = displayValue, - units = _converterSettings.Current.SpeckleUnits - }; - - return speckleExtrusionRoof; - } - - private SOG.Line ConvertReferenceLine(DB.ExtrusionRoof target) - { - var plane = target.GetProfile().get_Item(0).SketchPlane.GetPlane(); - SOG.Line referenceLine = - new() - { - start = _pointConverter.Convert(plane.Origin.Add(plane.XVec.Normalize().Negate())), - end = _pointConverter.Convert(plane.Origin), - units = _converterSettings.Current.SpeckleUnits, - }; - return referenceLine; - } -} diff --git a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/FamilyInstanceTopLevelConverterToSpeckle.cs b/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/FamilyInstanceTopLevelConverterToSpeckle.cs deleted file mode 100644 index 85db4946b..000000000 --- a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/FamilyInstanceTopLevelConverterToSpeckle.cs +++ /dev/null @@ -1,46 +0,0 @@ -using Speckle.Converters.Common; -using Speckle.Converters.Common.Objects; -using Speckle.Sdk.Models; - -namespace Speckle.Converters.RevitShared.ToSpeckle; - -// POC: bin for now? This is also a parent child relationship and may need a pattern for this -// so we don't end up with some god FamilyInstanceTopLevelConverterToSpeckle converter -[NameAndRankValue(nameof(DB.FamilyInstance), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] -public sealed class FamilyInstanceTopLevelConverterToSpeckle : BaseTopLevelConverterToSpeckle -{ - private readonly ITypedConverter _elementConverter; - private readonly ITypedConverter _beamConversion; - private readonly ITypedConverter _columnConversion; - private readonly ITypedConverter _braceConversion; - - public FamilyInstanceTopLevelConverterToSpeckle( - ITypedConverter elementConverter, - ITypedConverter beamConversion, - ITypedConverter columnConversion, - ITypedConverter braceConversion - ) - { - _elementConverter = elementConverter; - _beamConversion = beamConversion; - _columnConversion = columnConversion; - _braceConversion = braceConversion; - } - - public override Base Convert(DB.FamilyInstance target) - { - return target.StructuralType switch - { - DB.Structure.StructuralType.Beam => _beamConversion.Convert(target), - DB.Structure.StructuralType.Column => _columnConversion.Convert(target), - DB.Structure.StructuralType.Brace => _braceConversion.Convert(target), - - // POC: return generic element conversion or throw? - // - //throw new SpeckleConversionException( - // $"No conditional converters registered that could convert object of type {target.GetType()}" - //); - _ => _elementConverter.Convert(target) - }; - } -} diff --git a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/FloorTopLevelConverterToSpeckle.cs b/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/FloorTopLevelConverterToSpeckle.cs deleted file mode 100644 index 0ee46dedf..000000000 --- a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/FloorTopLevelConverterToSpeckle.cs +++ /dev/null @@ -1,121 +0,0 @@ -using Speckle.Converters.Common.Objects; -using Speckle.Converters.RevitShared.Helpers; -using Speckle.Converters.RevitShared.Settings; -using Speckle.Converters.RevitShared.ToSpeckle; -using Speckle.Objects; -using Speckle.Objects.BuiltElements.Revit; - -namespace Speckle.Converters.Common; - -// POC: reminder - writing classes and creating interfaces is a bit like organising your space -// if you have a structure for organising things, your interfaces, then finding your stuff, your classes & methods, becomes easy -// having a lack of interfaces or large interfaces is a bit like lacking structure, when all of your stuff, your classes & methods -// clould be anywhere or all in once place - rooting through box 274 for something you need, when said box has a miriad different -// and unrelated items, is no fun. Plus when you need that item, you end up bringing out the whole box/ -[NameAndRankValue(nameof(DB.Floor), 0)] -public class FloorTopLevelConverterToSpeckle : BaseTopLevelConverterToSpeckle -{ - private readonly ITypedConverter> _curveArrArrayConverter; - private readonly ITypedConverter _levelConverter; - private readonly ParameterValueExtractor _parameterValueExtractor; - private readonly DisplayValueExtractor _displayValueExtractor; - private readonly ISlopeArrowExtractor _slopeArrowExtractor; - private readonly IConverterSettingsStore _converterSettings; - - public FloorTopLevelConverterToSpeckle( - ITypedConverter> curveArrArrayConverter, - ITypedConverter levelConverter, - ParameterValueExtractor parameterValueExtractor, - DisplayValueExtractor displayValueExtractor, - ISlopeArrowExtractor slopeArrowExtractor, - IConverterSettingsStore converterSettings - ) - { - _curveArrArrayConverter = curveArrArrayConverter; - _levelConverter = levelConverter; - _parameterValueExtractor = parameterValueExtractor; - _displayValueExtractor = displayValueExtractor; - _slopeArrowExtractor = slopeArrowExtractor; - _converterSettings = converterSettings; - } - - public override SOBR.RevitFloor Convert(DB.Floor target) - { - var sketch = (DB.Sketch)target.Document.GetElement(target.SketchId); - List profiles = _curveArrArrayConverter.Convert(sketch.Profile); - - DB.ElementType type = (DB.ElementType)target.Document.GetElement(target.GetTypeId()); - var level = _parameterValueExtractor.GetValueAsDocumentObject(target, DB.BuiltInParameter.LEVEL_PARAM); - RevitLevel speckleLevel = _levelConverter.Convert(level); - bool structural = - _parameterValueExtractor.GetValueAsBool(target, DB.BuiltInParameter.FLOOR_PARAM_IS_STRUCTURAL) ?? false; - List displayValue = _displayValueExtractor.GetDisplayValue(target); - - SOBR.RevitFloor speckleFloor = - new() - { - family = type.FamilyName, - type = type.Name, - level = speckleLevel, - structural = structural, - displayValue = displayValue, - units = _converterSettings.Current.SpeckleUnits - }; - - // POC: Re-evaluate Wall sketch curve extraction, assumption of only one outline is wrong. https://spockle.atlassian.net/browse/CNX-9396 - if (profiles.Count > 0) - { - speckleFloor.outline = profiles[0]; - } - - if (profiles.Count > 1) - { - speckleFloor.voids = profiles.Skip(1).ToList(); - } - - double? slopeParam = null; - if (_parameterValueExtractor.TryGetValueAsDouble(target, DB.BuiltInParameter.ROOF_SLOPE, out var slope)) - { - // Divide by 100 to convert from percentage to unitless ratio (rise over run) - slopeParam = slope / 100d; - } - - TryAssignSlopeFromSlopeArrow(target, speckleFloor, slopeParam); - - return speckleFloor; - } - - private void TryAssignSlopeFromSlopeArrow(DB.Floor target, SOBR.RevitFloor speckleFloor, double? slopeParam) - { - if (_slopeArrowExtractor.GetSlopeArrow(target) is not DB.ModelLine slopeArrow) - { - return; - } - - var tail = _slopeArrowExtractor.GetSlopeArrowTail(slopeArrow); - var head = _slopeArrowExtractor.GetSlopeArrowHead(slopeArrow); - var tailOffset = _slopeArrowExtractor.GetSlopeArrowTailOffset(slopeArrow); - _ = _slopeArrowExtractor.GetSlopeArrowHeadOffset(slopeArrow, tailOffset, out var slope); - - slopeParam ??= slope; - speckleFloor.slope = (double)slopeParam; - - speckleFloor.slopeDirection = new SOG.Line() - { - start = tail, - end = head, - units = _converterSettings.Current.SpeckleUnits - }; - - /* The parameters refactor no longer uses the `Parameter` class and now we are only sending parameters as dictionaries in the `properties` field - if ( - speckleFloor["parameters"] is Base parameters - && parameters["FLOOR_HEIGHTABOVELEVEL_PARAM"] is SOBR.Parameter offsetParam - && offsetParam.value is double offset - ) - { - offsetParam.value = offset + tailOffset; - } - */ - } -} diff --git a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/FootPrintRoofToSpeckleTopLevelConverter.cs b/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/FootPrintRoofToSpeckleTopLevelConverter.cs deleted file mode 100644 index 2ec612988..000000000 --- a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/FootPrintRoofToSpeckleTopLevelConverter.cs +++ /dev/null @@ -1,110 +0,0 @@ -using Autodesk.Revit.DB; -using Speckle.Converters.Common; -using Speckle.Converters.Common.Objects; -using Speckle.Converters.RevitShared.Helpers; -using Speckle.Converters.RevitShared.Settings; -using Speckle.Objects; -using Speckle.Objects.BuiltElements.Revit; -using Speckle.Objects.BuiltElements.Revit.RevitRoof; -using Speckle.Objects.Geometry; -using Speckle.Sdk.Common; -using Speckle.Sdk.Models; -using Speckle.Sdk.Models.Extensions; - -namespace Speckle.Converters.RevitShared.ToSpeckle; - -[NameAndRankValue(nameof(FootPrintRoof), 0)] -public class FootPrintRoofToSpeckleTopLevelConverter - : BaseTopLevelConverterToSpeckle -{ - private readonly ITypedConverter _levelConverter; - private readonly ITypedConverter _modelCurveArrArrayConverter; - private readonly ParameterValueExtractor _parameterValueExtractor; - private readonly DisplayValueExtractor _displayValueExtractor; - private readonly IRootToSpeckleConverter _converter; - private readonly IConverterSettingsStore _converterSettings; - - public FootPrintRoofToSpeckleTopLevelConverter( - ITypedConverter levelConverter, - ITypedConverter modelCurveArrArrayConverter, - ParameterValueExtractor parameterValueExtractor, - DisplayValueExtractor displayValueExtractor, - IRootToSpeckleConverter converter, - IConverterSettingsStore converterSettings - ) - { - _levelConverter = levelConverter; - _modelCurveArrArrayConverter = modelCurveArrArrayConverter; - _parameterValueExtractor = parameterValueExtractor; - _displayValueExtractor = displayValueExtractor; - _converter = converter; - _converterSettings = converterSettings; - } - - public override RevitFootprintRoof Convert(FootPrintRoof target) - { - var baseLevel = _parameterValueExtractor.GetValueAsDocumentObject( - target, - BuiltInParameter.ROOF_BASE_LEVEL_PARAM - ); - - // We don't currently validate the success of this TryGet, it is assumed some Roofs don't have a top-level. - _parameterValueExtractor.TryGetValueAsDocumentObject( - target, - BuiltInParameter.ROOF_UPTO_LEVEL_PARAM, - out var topLevel - ); - - var elementType = (ElementType)target.Document.GetElement(target.GetTypeId()); - List displayValue = _displayValueExtractor.GetDisplayValue(target); - - RevitFootprintRoof speckleFootprintRoof = - new() - { - type = elementType.Name, - family = elementType.FamilyName, - level = _levelConverter.Convert(baseLevel), - cutOffLevel = topLevel is not null ? _levelConverter.Convert(topLevel) : null, - displayValue = displayValue, - units = _converterSettings.Current.SpeckleUnits - }; - - // Shockingly, roofs can have curtain grids on them. I guess it makes sense: https://en.wikipedia.org/wiki/Louvre_Pyramid - if (target.CurtainGrids is { } gs) - { - List roofChildren = new(); - foreach (CurtainGrid grid in gs) - { - roofChildren.AddRange(ConvertElements(grid.GetMullionIds())); - roofChildren.AddRange(ConvertElements(grid.GetPanelIds())); - } - - if (speckleFootprintRoof.GetDetachedProp("elements") is List elements) - { - elements.AddRange(roofChildren); - } - else - { - speckleFootprintRoof.SetDetachedProp("elements", roofChildren); - } - } - - // POC: CNX-9396 again with the incorrect assumption that the first profile is the floor and subsequent profiles - // are voids - // POC: CNX-9403 in current connector, we are doing serious gymnastics to get the slope of the floor as defined by - // slope arrow. The way we are doing it relies on dynamic props and only works for Revit <-> Revit - var profiles = _modelCurveArrArrayConverter.Convert(target.GetProfiles()); - speckleFootprintRoof.outline = profiles.FirstOrDefault().NotNull(); - speckleFootprintRoof.voids = profiles.Skip(1).ToList(); - - return speckleFootprintRoof; - } - - private IEnumerable ConvertElements(IEnumerable elementIds) - { - foreach (DB.ElementId elementId in elementIds) - { - yield return _converter.Convert(_converterSettings.Current.Document.GetElement(elementId)); - } - } -} diff --git a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/GridTopLevelConverterToSpeckle.cs b/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/GridTopLevelConverterToSpeckle.cs deleted file mode 100644 index 09c5fb3fa..000000000 --- a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/GridTopLevelConverterToSpeckle.cs +++ /dev/null @@ -1,31 +0,0 @@ -using Speckle.Converters.Common; -using Speckle.Converters.Common.Objects; -using Speckle.Converters.RevitShared.Settings; -using Speckle.Objects; - -namespace Speckle.Converters.RevitShared.ToSpeckle; - -[NameAndRankValue(nameof(DB.Grid), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] -public sealed class GridTopLevelConverterToSpeckle : BaseTopLevelConverterToSpeckle -{ - private readonly ITypedConverter _curveConverter; - private readonly IConverterSettingsStore _converterSettings; - - public GridTopLevelConverterToSpeckle( - ITypedConverter curveConverter, - IConverterSettingsStore converterSettings - ) - { - _curveConverter = curveConverter; - _converterSettings = converterSettings; - } - - public override SOBE.GridLine Convert(DB.Grid target) => - new() - { - baseLine = _curveConverter.Convert(target.Curve), - label = target.Name, - applicationId = target.UniqueId, - units = _converterSettings.Current.SpeckleUnits - }; -} diff --git a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/HostedElementConversionToSpeckle.cs b/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/HostedElementConversionToSpeckle.cs deleted file mode 100644 index 29f2c0dee..000000000 --- a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/HostedElementConversionToSpeckle.cs +++ /dev/null @@ -1,48 +0,0 @@ -using Autodesk.Revit.DB; -using Speckle.Converters.Common; -using Speckle.Converters.RevitShared.Settings; -using Speckle.Sdk; -using Speckle.Sdk.Models; - -namespace Speckle.Converters.RevitShared.ToSpeckle; - -[Obsolete( - "Do not use while as we're rethinking hosted element relationships. This class will most likely go away.", - true -)] -public class HostedElementConversionToSpeckle -{ - private readonly IRootToSpeckleConverter _converter; - private readonly IConverterSettingsStore _converterSettings; - - public HostedElementConversionToSpeckle( - IRootToSpeckleConverter converter, - IConverterSettingsStore converterSettings - ) - { - _converter = converter; - _converterSettings = converterSettings; - } - - public IEnumerable ConvertHostedElements(IEnumerable hostedElementIds) - { - foreach (var elemId in hostedElementIds) - { - Element element = _converterSettings.Current.Document.GetElement(elemId); - - Base @base; - try - { - @base = _converter.Convert(element); - } - catch (SpeckleException) - { - // POC: Handling this correctly should be high priority, as it affects the quality of the report. - // POC: we could aggregate all host element exceptions and throw a single one in the end. - continue; - } - - yield return @base; - } - } -} diff --git a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/LevelTopLevelConverterToSpeckle.cs b/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/LevelTopLevelConverterToSpeckle.cs deleted file mode 100644 index 881d43030..000000000 --- a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/LevelTopLevelConverterToSpeckle.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Speckle.Converters.Common; -using Speckle.Converters.RevitShared.Services; -using Speckle.Converters.RevitShared.Settings; - -namespace Speckle.Converters.RevitShared.ToSpeckle; - -[NameAndRankValue(nameof(DB.Level), 0)] -public class LevelConversionToSpeckle : BaseTopLevelConverterToSpeckle -{ - private readonly ScalingServiceToSpeckle _scalingService; - private readonly IConverterSettingsStore _converterSettings; - - public LevelConversionToSpeckle( - ScalingServiceToSpeckle scalingService, - IConverterSettingsStore converterSettings - ) - { - _scalingService = scalingService; - _converterSettings = converterSettings; - } - - public override SOBR.RevitLevel Convert(DB.Level target) - { - SOBR.RevitLevel level = - new() - { - elevation = _scalingService.ScaleLength(target.Elevation), - name = target.Name, - createView = true, - units = _converterSettings.Current.SpeckleUnits - }; - - return level; - } -} diff --git a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/ModelCurveToSpeckleTopLevelConverter.cs b/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/ModelCurveToSpeckleTopLevelConverter.cs deleted file mode 100644 index a37213406..000000000 --- a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/ModelCurveToSpeckleTopLevelConverter.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Speckle.Converters.Common; -using Speckle.Converters.Common.Objects; -using Speckle.Objects; -using Speckle.Sdk.Models; - -namespace Speckle.Converters.RevitShared.ToSpeckle; - -// Converts model curves to regular speckle curves, since we aren't receiving them and the only property used in V2 was the linestyle (not element ids or parameters). Don't see a need to handle these differently from regular geometry. -[NameAndRankValue(nameof(DB.ModelCurve), 0)] -public class ModelCurveToSpeckleTopLevelConverter : BaseTopLevelConverterToSpeckle -{ - private readonly ITypedConverter _curveConverter; - - public ModelCurveToSpeckleTopLevelConverter(ITypedConverter curveConverter) - { - _curveConverter = curveConverter; - } - - public override Base Convert(DB.ModelCurve target) => (Base)_curveConverter.Convert(target.GeometryCurve); -} diff --git a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/RailingTopLevelConverterToSpeckle.cs b/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/RailingTopLevelConverterToSpeckle.cs deleted file mode 100644 index e7f08d676..000000000 --- a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/RailingTopLevelConverterToSpeckle.cs +++ /dev/null @@ -1,49 +0,0 @@ -using Speckle.Converters.Common; -using Speckle.Converters.RevitShared.Helpers; -using Speckle.Converters.RevitShared.Settings; -using Speckle.Converters.RevitShared.ToSpeckle; - -namespace Speckle.Converters.Revit2023.ToSpeckle.TopLevel; - -[NameAndRankValue(nameof(DBA.Railing), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] -public class RailingTopLevelConverterToSpeckle : BaseTopLevelConverterToSpeckle -{ - private readonly DisplayValueExtractor _displayValueExtractor; - private readonly IConverterSettingsStore _converterSettings; - - public RailingTopLevelConverterToSpeckle( - DisplayValueExtractor displayValueExtractor, - IConverterSettingsStore converterSettings - ) - { - _displayValueExtractor = displayValueExtractor; - _converterSettings = converterSettings; - } - - public override SOBR.RevitElement Convert(DBA.Railing target) - { - string family = target.Document.GetElement(target.GetTypeId()) is DB.FamilySymbol symbol - ? symbol.FamilyName - : "no family"; - string category = target.Category?.Name ?? "no category"; - var displayValue = _displayValueExtractor.GetDisplayValue(target); - - var topRail = _converterSettings.Current.Document.GetElement(target.TopRail); - var topRailDisplayValue = _displayValueExtractor.GetDisplayValue(topRail); - - displayValue.AddRange(topRailDisplayValue); - - SOBR.RevitElement speckleElement = - new() - { - type = target.Name, - category = category, - family = family, - displayValue = displayValue - }; - - speckleElement["units"] = _converterSettings.Current.SpeckleUnits; - - return speckleElement; - } -} diff --git a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/RevitElementTopLevelConverterToSpeckle.cs b/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/RevitElementTopLevelConverterToSpeckle.cs new file mode 100644 index 000000000..8f68fb0e9 --- /dev/null +++ b/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/RevitElementTopLevelConverterToSpeckle.cs @@ -0,0 +1,180 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Converters.RevitShared.Extensions; +using Speckle.Converters.RevitShared.Helpers; +using Speckle.Converters.RevitShared.Settings; +using Speckle.Converters.RevitShared.ToSpeckle.Properties; +using Speckle.Objects.Data; +using Speckle.Sdk.Common.Exceptions; +using Speckle.Sdk.Models; + +namespace Speckle.Converters.RevitShared.ToSpeckle; + +[NameAndRankValue(typeof(DB.Element), 0)] +public class ElementTopLevelConverterToSpeckle : IToSpeckleTopLevelConverter +{ + private readonly DisplayValueExtractor _displayValueExtractor; + private readonly PropertiesExtractor _propertiesExtractor; + private readonly ITypedConverter _locationConverter; + private readonly IConverterSettingsStore _converterSettings; + + public ElementTopLevelConverterToSpeckle( + DisplayValueExtractor displayValueExtractor, + PropertiesExtractor propertiesExtractor, + ITypedConverter locationConverter, + IConverterSettingsStore converterSettings + ) + { + _displayValueExtractor = displayValueExtractor; + _propertiesExtractor = propertiesExtractor; + _locationConverter = locationConverter; + _converterSettings = converterSettings; + } + + public Base Convert(object target) => Convert((DB.Element)target); + + public RevitObject Convert(DB.Element target) + { + string category = target.Category?.Name ?? "none"; + + // special case for direct shapes: use builtin category instead + if (target is DB.DirectShape ds) + { + // Clean up built-in name by removing "OST" prefixes + category = ds + .Category.GetBuiltInCategory() + .ToString() + .Replace("OST_IOS", "") //for OST_IOSModelGroups + .Replace("OST_MEP", "") //for OST_MEPSpaces + .Replace("OST_", "") //for any other OST_blablabla + .Replace("_", " "); + } + + string name = $"{category} - {target.Name}"; // Note: I find this looks better in the frontend. + string familyName = "none"; + string typeName = "none"; + switch (target.Document.GetElement(target.GetTypeId())) + { + case DB.FamilySymbol symbol: + familyName = symbol.FamilyName; + typeName = symbol.Name; + break; + case DB.ElementType type: + familyName = type.FamilyName; + typeName = type.Name; + break; + } + + // get location if any + Base? convertedLocation = null; + switch (target) + { + // skip these objects, if location is redundant + case DB.ModelCurve: + break; + + default: + if (target.Location is DB.Location location and (DB.LocationCurve or DB.LocationPoint)) // location can be null + { + try + { + convertedLocation = _locationConverter.Convert(location); + } + catch (ValidationException) + { + // NOTE: i've improved the if check above to make sure we never reach here + // we were throwing a lot here for various elements (e.g. floors) and we would + // be slowing things down + // location was not a supported, do not attach to base element + } + } + break; + } + + // get the display value + List displayValue = _displayValueExtractor.GetDisplayValue(target); + + // get children elements + // this is a bespoke method by class type. + var children = GetElementChildren(target).ToList(); + + // get properties + Dictionary properties = _propertiesExtractor.GetProperties(target); + + RevitObject revitObject = + new() + { + name = name, + type = typeName, + family = familyName, + category = category, + location = convertedLocation, + elements = children, + displayValue = displayValue.Cast().ToList(), + properties = properties, + units = _converterSettings.Current.SpeckleUnits + }; + + return revitObject; + } + + private IEnumerable GetElementChildren(DB.Element element) + { + switch (element) + { + case DB.Wall wall: + var wallChildren = GetWallChildren(wall); + foreach (var child in wallChildren) + { + yield return child; + } + break; + + case DB.FootPrintRoof footPrintRoof: + var footPrintRoofChildren = GetFootPrintRoofChildren(footPrintRoof); + foreach (var child in footPrintRoofChildren) + { + yield return child; + } + break; + } + } + + private IEnumerable GetWallChildren(DB.Wall wall) + { + List wallChildrenIds = new(); + if (wall.CurtainGrid is DB.CurtainGrid grid) + { + wallChildrenIds.AddRange(grid.GetMullionIds()); + wallChildrenIds.AddRange(grid.GetPanelIds()); + } + else if (wall.IsStackedWall) + { + wallChildrenIds.AddRange(wall.GetStackedWallMemberIds()); + } + + foreach (var childId in wallChildrenIds) + { + yield return Convert(_converterSettings.Current.Document.GetElement(childId)); + } + } + + // Shockingly, roofs can have curtain grids on them. I guess it makes sense: https://en.wikipedia.org/wiki/Louvre_Pyramid + private IEnumerable GetFootPrintRoofChildren(DB.FootPrintRoof footPrintRoof) + { + List footPrintRoofChildrenIds = new(); + if (footPrintRoof.CurtainGrids is { } gs) + { + foreach (DB.CurtainGrid grid in gs) + { + footPrintRoofChildrenIds.AddRange(grid.GetMullionIds()); + footPrintRoofChildrenIds.AddRange(grid.GetPanelIds()); + } + } + + foreach (var childId in footPrintRoofChildrenIds) + { + yield return Convert(_converterSettings.Current.Document.GetElement(childId)); + } + } +} diff --git a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/RoofBaseToSpeckleTopLevelTopLevelConverter.cs b/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/RoofBaseToSpeckleTopLevelTopLevelConverter.cs deleted file mode 100644 index ceb13f677..000000000 --- a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/RoofBaseToSpeckleTopLevelTopLevelConverter.cs +++ /dev/null @@ -1,40 +0,0 @@ -using Autodesk.Revit.DB; -using Speckle.Converters.Common; -using Speckle.Converters.RevitShared.Helpers; -using Speckle.Converters.RevitShared.Settings; -using Speckle.Objects.BuiltElements.Revit.RevitRoof; - -namespace Speckle.Converters.RevitShared.ToSpeckle; - -[NameAndRankValue(nameof(RoofBase), 0)] -internal sealed class RoofBaseToSpeckleTopLevelTopLevelConverter - : BaseTopLevelConverterToSpeckle -{ - private readonly DisplayValueExtractor _displayValueExtractor; - private readonly IConverterSettingsStore _converterSettings; - - public RoofBaseToSpeckleTopLevelTopLevelConverter( - DisplayValueExtractor displayValueExtractor, - IConverterSettingsStore converterSettings - ) - { - _displayValueExtractor = displayValueExtractor; - _converterSettings = converterSettings; - } - - public override RevitRoof Convert(RoofBase target) - { - var elementType = (ElementType)target.Document.GetElement(target.GetTypeId()); - List displayValue = _displayValueExtractor.GetDisplayValue(target); - - RevitRoof revitRoof = - new() - { - type = elementType.Name, - family = elementType.FamilyName, - displayValue = displayValue, - units = _converterSettings.Current.SpeckleUnits - }; - return revitRoof; - } -} diff --git a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/RoomTopLevelConverterToSpeckle.cs b/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/RoomTopLevelConverterToSpeckle.cs deleted file mode 100644 index 42c0480ea..000000000 --- a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/RoomTopLevelConverterToSpeckle.cs +++ /dev/null @@ -1,73 +0,0 @@ -using Speckle.Converters.Common; -using Speckle.Converters.Common.Objects; -using Speckle.Converters.RevitShared.Helpers; -using Speckle.Converters.RevitShared.Settings; -using Speckle.Objects; -using Speckle.Sdk.Models; - -namespace Speckle.Converters.RevitShared.ToSpeckle; - -[NameAndRankValue(nameof(DBA.Room), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] -public class RoomTopLevelConverterToSpeckle : BaseTopLevelConverterToSpeckle -{ - private readonly DisplayValueExtractor _displayValueExtractor; - private readonly ITypedConverter _levelConverter; - private readonly ParameterValueExtractor _parameterValueExtractor; - private readonly ITypedConverter _locationConverter; - private readonly ITypedConverter, SOG.Polycurve> _boundarySegmentConverter; - private readonly IConverterSettingsStore _converterSettings; - - public RoomTopLevelConverterToSpeckle( - DisplayValueExtractor displayValueExtractor, - ITypedConverter levelConverter, - ParameterValueExtractor parameterValueExtractor, - ITypedConverter locationConverter, - ITypedConverter, SOG.Polycurve> boundarySegmentConverter, - IConverterSettingsStore converterSettings - ) - { - _displayValueExtractor = displayValueExtractor; - _levelConverter = levelConverter; - _parameterValueExtractor = parameterValueExtractor; - _locationConverter = locationConverter; - _boundarySegmentConverter = boundarySegmentConverter; - _converterSettings = converterSettings; - } - - public override SOBE.Room Convert(DBA.Room target) - { - var number = target.Number; - var name = _parameterValueExtractor.GetValueAsString(target, DB.BuiltInParameter.ROOM_NAME); - var area = _parameterValueExtractor.GetValueAsDouble(target, DB.BuiltInParameter.ROOM_AREA); - - var displayValue = _displayValueExtractor.GetDisplayValue(target); - var basePoint = (SOG.Point)_locationConverter.Convert(target.Location); - var level = _levelConverter.Convert(target.Level); - - var profiles = target - .GetBoundarySegments(new DB.SpatialElementBoundaryOptions()) - .Select(c => (ICurve)_boundarySegmentConverter.Convert(c)) - .ToList(); - - var outline = profiles.First(); - var voids = profiles.Skip(1).ToList(); - - SOBE.Room speckleRoom = - new() - { - name = name ?? "-", - number = number, - level = level, - basePoint = basePoint, - displayValue = displayValue, - area = area, - outline = outline, - voids = voids, - units = _converterSettings.Current.SpeckleUnits - }; - - // POC: Removed dynamic property `phaseCreated` as it seems the info is included in the parameters already - - return speckleRoom; - } -} diff --git a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/TopographyTopLevelConverterToSpeckle.cs b/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/TopographyTopLevelConverterToSpeckle.cs deleted file mode 100644 index 196d21068..000000000 --- a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/TopographyTopLevelConverterToSpeckle.cs +++ /dev/null @@ -1,38 +0,0 @@ -using Speckle.Converters.Common; -using Speckle.Converters.RevitShared.Helpers; -using Speckle.Converters.RevitShared.Settings; -using Speckle.Sdk.Common; - -namespace Speckle.Converters.RevitShared.ToSpeckle; - -// POC: needs review feels, BIG, feels like it could be broken down.. -// i.e. GetParams(), GetGeom()? feels like it's doing too much -[NameAndRankValue(nameof(DBA.TopographySurface), 0)] -public class TopographyTopLevelConverterToSpeckle - : BaseTopLevelConverterToSpeckle -{ - private readonly DisplayValueExtractor _displayValueExtractor; - private readonly IConverterSettingsStore _converterSettings; - - public TopographyTopLevelConverterToSpeckle( - DisplayValueExtractor displayValueExtractor, - IConverterSettingsStore converterSettings - ) - { - _displayValueExtractor = displayValueExtractor; - _converterSettings = converterSettings; - } - - public override SOBR.RevitTopography Convert(DBA.TopographySurface target) - { - var speckleTopo = new SOBR.RevitTopography - { - units = _converterSettings.Current.SpeckleUnits, - displayValue = _displayValueExtractor.GetDisplayValue(target), - elementId = target.Id.ToString().NotNull(), - baseGeometry = null! //TODO: this can't be correct, see https://linear.app/speckle/issue/CNX-461/revit-check-why-topographytospeckle-sets-no-basegeometry - }; - - return speckleTopo; - } -} diff --git a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/WallTopLevelConverterToSpeckle.cs b/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/WallTopLevelConverterToSpeckle.cs deleted file mode 100644 index e0a8fbcb5..000000000 --- a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/WallTopLevelConverterToSpeckle.cs +++ /dev/null @@ -1,187 +0,0 @@ -using Speckle.Converters.Common; -using Speckle.Converters.Common.Objects; -using Speckle.Converters.RevitShared.Helpers; -using Speckle.Converters.RevitShared.Settings; -using Speckle.Objects; -using Speckle.Objects.BuiltElements.Revit; -using Speckle.Sdk.Common.Exceptions; -using Speckle.Sdk.Models; -using Speckle.Sdk.Models.Extensions; - -namespace Speckle.Converters.RevitShared.ToSpeckle; - -// POC: needs review feels, BIG, feels like it could be broken down.. -// i.e. GetParams(), GetGeom()? feels like it's doing too much -[NameAndRankValue(nameof(DB.Wall), 0)] -public class WallTopLevelConverterToSpeckle : BaseTopLevelConverterToSpeckle -{ - private readonly ITypedConverter _curveConverter; - private readonly ITypedConverter _levelConverter; - private readonly ITypedConverter> _curveArrArrayConverter; - private readonly ParameterValueExtractor _parameterValueExtractor; - private readonly IConverterSettingsStore _converterSettings; - private readonly DisplayValueExtractor _displayValueExtractor; - private readonly IRootToSpeckleConverter _converter; - - public WallTopLevelConverterToSpeckle( - ITypedConverter curveConverter, - ITypedConverter levelConverter, - ITypedConverter> curveArrArrayConverter, - IConverterSettingsStore converterSettings, - ParameterValueExtractor parameterValueExtractor, - DisplayValueExtractor displayValueExtractor, - IRootToSpeckleConverter converter - ) - { - _curveConverter = curveConverter; - _levelConverter = levelConverter; - _curveArrArrayConverter = curveArrArrayConverter; - _converterSettings = converterSettings; - _parameterValueExtractor = parameterValueExtractor; - _displayValueExtractor = displayValueExtractor; - _converter = converter; - } - - public override SOBR.RevitWall Convert(DB.Wall target) - { - SOBR.RevitWall speckleWall = - new() - { - family = target.WallType.FamilyName.ToString(), - type = target.WallType.Name, - units = _converterSettings.Current.SpeckleUnits - }; - - AssignSpecificParameters(target, speckleWall); - AssignVoids(target, speckleWall); - AssignHostedElements(speckleWall, GetChildElements(target)); - AssignDisplayValue(target, speckleWall); - - return speckleWall; - } - - private void AssignSpecificParameters(DB.Wall target, RevitWall speckleWall) - { - if (target.Location is not DB.LocationCurve locationCurve) - { - throw new ValidationException( - "Incorrect assumption was made that all Revit Wall location properties would be of type \"LocationCurve\"" - ); - } - - speckleWall.baseLine = _curveConverter.Convert(locationCurve.Curve); - - var level = _parameterValueExtractor.GetValueAsDocumentObject( - target, - DB.BuiltInParameter.WALL_BASE_CONSTRAINT - ); - speckleWall.level = _levelConverter.Convert(level); - - var topLevel = _parameterValueExtractor.GetValueAsDocumentObject( - target, - DB.BuiltInParameter.WALL_BASE_CONSTRAINT - ); - speckleWall.topLevel = _levelConverter.Convert(topLevel); - - // POC : what to do if these parameters are unset (instead of assigning default) - _ = _parameterValueExtractor.TryGetValueAsDouble( - target, - DB.BuiltInParameter.WALL_USER_HEIGHT_PARAM, - out double? height - ); - speckleWall.height = height ?? 0; - - _ = _parameterValueExtractor.TryGetValueAsDouble( - target, - DB.BuiltInParameter.WALL_BASE_OFFSET, - out double? baseOffset - ); - speckleWall.baseOffset = baseOffset ?? 0; - - _ = _parameterValueExtractor.TryGetValueAsDouble( - target, - DB.BuiltInParameter.WALL_TOP_OFFSET, - out double? topOffset - ); - speckleWall.topOffset = topOffset ?? 0; - - speckleWall.structural = - _parameterValueExtractor.GetValueAsBool(target, DB.BuiltInParameter.WALL_STRUCTURAL_SIGNIFICANT) ?? false; - speckleWall.flipped = target.Flipped; - } - - private List GetChildElements(DB.Wall target) - { - List wallChildren = new(); - - if (target.CurtainGrid is DB.CurtainGrid grid) - { - wallChildren.AddRange(ConvertElements(grid.GetMullionIds())); - wallChildren.AddRange(ConvertElements(grid.GetPanelIds())); - } - else if (target.IsStackedWall) - { - wallChildren.AddRange(ConvertElements(target.GetStackedWallMemberIds())); - } - // POC: removing hosted elements from parents - // wallChildren.AddRange(ConvertElements(target.GetHostedElementIds())); - return wallChildren; - } - - private IEnumerable ConvertElements(IEnumerable elementIds) - { - foreach (DB.ElementId elementId in elementIds) - { - yield return _converter.Convert(_converterSettings.Current.Document.GetElement(elementId)); - } - } - - private void AssignDisplayValue(DB.Wall target, RevitWall speckleWall) - { - if (target.CurtainGrid is null) - { - speckleWall.displayValue = _displayValueExtractor.GetDisplayValue(target); - } - else - { - // Curtain walls have a bunch of subelements (mullions and co) that have their own display values. - speckleWall.displayValue = new List(); - } - } - - private void AssignHostedElements(SOBR.RevitWall speckleWall, List hostedObjects) - { - if (hostedObjects.Count == 0) - { - return; - } - - if (speckleWall.GetDetachedProp("elements") is List elements) - { - elements.AddRange(hostedObjects); - } - else - { - speckleWall.SetDetachedProp("elements", hostedObjects); - } - } - - private void AssignVoids(DB.Wall target, SOBR.RevitWall speckleWall) - { - DB.CurveArrArray? profile = ((DB.Sketch)target.Document.GetElement(target.SketchId))?.Profile; - if (profile is null) - { - return; - } - - List polycurves = _curveArrArrayConverter.Convert(profile); - - if (polycurves.Count > 1) - { - // POC: we have been assuming that the first curve is the element and the rest of the curves are openings - // this isn't always true - // https://spockle.atlassian.net/browse/CNX-9396 - speckleWall["voids"] = polycurves.Skip(1).ToList(); - } - } -} diff --git a/Converters/Rhino/Speckle.Converters.Rhino7.Tests/packages.lock.json b/Converters/Rhino/Speckle.Converters.Rhino7.Tests/packages.lock.json index 94e89df19..7de00d403 100644 --- a/Converters/Rhino/Speckle.Converters.Rhino7.Tests/packages.lock.json +++ b/Converters/Rhino/Speckle.Converters.Rhino7.Tests/packages.lock.json @@ -245,11 +245,6 @@ "resolved": "13.0.1", "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" }, - "Speckle.DoubleNumerics": { - "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w==" - }, "Speckle.Newtonsoft.Json": { "type": "Transitive", "resolved": "13.0.2", @@ -340,7 +335,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.191, )" + "Speckle.Objects": "[3.1.1, )" } }, "speckle.testing": { @@ -374,36 +369,42 @@ "resolved": "2.2.0", "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "+m7jRFm0ABbkcSz2UphdxAsislY10IpQ1u79c8a3aVvegLjnsVQZ1sXfRIRO1aFdulkhjYKXYpB3N9M8Z+epgQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.191" + "Speckle.Sdk": "3.1.1" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "VVT3LJiYlhqnggxxdeTt1QLrqfxDb044x0yX6kxS9b5MRzeDvK2Vz86pLDfuHs+SXvDimRVfYx1M42IW/aPcTQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.CSharp": "4.7.0", "Microsoft.Data.Sqlite": "7.0.5", "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", "Microsoft.Extensions.Logging": "2.2.0", - "Speckle.DoubleNumerics": "4.0.1", + "Speckle.DoubleNumerics": "4.1.0", "Speckle.Newtonsoft.Json": "13.0.2", - "Speckle.Sdk.Dependencies": "3.1.0-dev.191" + "Speckle.Sdk.Dependencies": "3.1.1" } }, "Speckle.Sdk.Dependencies": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "EmEOyjsGsNi56Z/ZoBOn8WirTmIT2yqWvlUeUh0BSPX2TDMZXHTKOM/kHmP6HSd10KVFn2Zo/ItY7/K9iRtL1Q==" + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" } } } diff --git a/Converters/Rhino/Speckle.Converters.Rhino7/packages.lock.json b/Converters/Rhino/Speckle.Converters.Rhino7/packages.lock.json index 2c8990563..381b584e3 100644 --- a/Converters/Rhino/Speckle.Converters.Rhino7/packages.lock.json +++ b/Converters/Rhino/Speckle.Converters.Rhino7/packages.lock.json @@ -159,11 +159,6 @@ "resolved": "8.0.0", "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" }, - "Speckle.DoubleNumerics": { - "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w==" - }, "Speckle.Newtonsoft.Json": { "type": "Transitive", "resolved": "13.0.2", @@ -258,7 +253,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.191, )" + "Speckle.Objects": "[3.1.1, )" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { @@ -285,20 +280,26 @@ "resolved": "2.2.0", "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "+m7jRFm0ABbkcSz2UphdxAsislY10IpQ1u79c8a3aVvegLjnsVQZ1sXfRIRO1aFdulkhjYKXYpB3N9M8Z+epgQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.191" + "Speckle.Sdk": "3.1.1" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "VVT3LJiYlhqnggxxdeTt1QLrqfxDb044x0yX6kxS9b5MRzeDvK2Vz86pLDfuHs+SXvDimRVfYx1M42IW/aPcTQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.Bcl.AsyncInterfaces": "5.0.0", @@ -306,16 +307,16 @@ "Microsoft.Data.Sqlite": "7.0.5", "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", "Microsoft.Extensions.Logging": "2.2.0", - "Speckle.DoubleNumerics": "4.0.1", + "Speckle.DoubleNumerics": "4.1.0", "Speckle.Newtonsoft.Json": "13.0.2", - "Speckle.Sdk.Dependencies": "3.1.0-dev.191" + "Speckle.Sdk.Dependencies": "3.1.1" } }, "Speckle.Sdk.Dependencies": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "EmEOyjsGsNi56Z/ZoBOn8WirTmIT2yqWvlUeUh0BSPX2TDMZXHTKOM/kHmP6HSd10KVFn2Zo/ItY7/K9iRtL1Q==" + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" } } } diff --git a/Converters/Rhino/Speckle.Converters.Rhino8/packages.lock.json b/Converters/Rhino/Speckle.Converters.Rhino8/packages.lock.json index 62a390dbb..3cf4258b3 100644 --- a/Converters/Rhino/Speckle.Converters.Rhino8/packages.lock.json +++ b/Converters/Rhino/Speckle.Converters.Rhino8/packages.lock.json @@ -159,11 +159,6 @@ "resolved": "8.0.0", "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" }, - "Speckle.DoubleNumerics": { - "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w==" - }, "Speckle.Newtonsoft.Json": { "type": "Transitive", "resolved": "13.0.2", @@ -258,7 +253,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.191, )" + "Speckle.Objects": "[3.1.1, )" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { @@ -285,20 +280,26 @@ "resolved": "2.2.0", "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "+m7jRFm0ABbkcSz2UphdxAsislY10IpQ1u79c8a3aVvegLjnsVQZ1sXfRIRO1aFdulkhjYKXYpB3N9M8Z+epgQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.191" + "Speckle.Sdk": "3.1.1" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "VVT3LJiYlhqnggxxdeTt1QLrqfxDb044x0yX6kxS9b5MRzeDvK2Vz86pLDfuHs+SXvDimRVfYx1M42IW/aPcTQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.Bcl.AsyncInterfaces": "5.0.0", @@ -306,16 +307,16 @@ "Microsoft.Data.Sqlite": "7.0.5", "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", "Microsoft.Extensions.Logging": "2.2.0", - "Speckle.DoubleNumerics": "4.0.1", + "Speckle.DoubleNumerics": "4.1.0", "Speckle.Newtonsoft.Json": "13.0.2", - "Speckle.Sdk.Dependencies": "3.1.0-dev.191" + "Speckle.Sdk.Dependencies": "3.1.1" } }, "Speckle.Sdk.Dependencies": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "EmEOyjsGsNi56Z/ZoBOn8WirTmIT2yqWvlUeUh0BSPX2TDMZXHTKOM/kHmP6HSd10KVFn2Zo/ItY7/K9iRtL1Q==" + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" } } } diff --git a/Converters/Rhino/Speckle.Converters.RhinoShared/ServiceRegistration.cs b/Converters/Rhino/Speckle.Converters.RhinoShared/ServiceRegistration.cs index b717ffb2e..aefbe5b09 100644 --- a/Converters/Rhino/Speckle.Converters.RhinoShared/ServiceRegistration.cs +++ b/Converters/Rhino/Speckle.Converters.RhinoShared/ServiceRegistration.cs @@ -1,4 +1,4 @@ -using System.Reflection; +using System.Reflection; using Microsoft.Extensions.DependencyInjection; using Rhino; using Speckle.Converters.Common; @@ -23,6 +23,7 @@ public static class ServiceRegistration IConverterSettingsStore, ConverterSettingsStore >(); + return serviceCollection; } } diff --git a/Converters/Rhino/Speckle.Converters.RhinoShared/Speckle.Converters.RhinoShared.projitems b/Converters/Rhino/Speckle.Converters.RhinoShared/Speckle.Converters.RhinoShared.projitems index d3840250a..d8173d925 100644 --- a/Converters/Rhino/Speckle.Converters.RhinoShared/Speckle.Converters.RhinoShared.projitems +++ b/Converters/Rhino/Speckle.Converters.RhinoShared/Speckle.Converters.RhinoShared.projitems @@ -11,4 +11,4 @@ - \ No newline at end of file + diff --git a/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/Raw/CircleToHostConverter.cs b/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/Raw/CircleToHostConverter.cs index 4b79b7543..a7217668e 100644 --- a/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/Raw/CircleToHostConverter.cs +++ b/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/Raw/CircleToHostConverter.cs @@ -1,4 +1,4 @@ -using Speckle.Converters.Common.Objects; +using Speckle.Converters.Common.Objects; namespace Speckle.Converters.Rhino.ToHost.Raw; diff --git a/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/Raw/FlatPointListToHostConverter.cs b/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/Raw/FlatPointListToHostConverter.cs index 5dd995966..501018160 100644 --- a/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/Raw/FlatPointListToHostConverter.cs +++ b/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/Raw/FlatPointListToHostConverter.cs @@ -5,10 +5,15 @@ using Speckle.Sdk.Common.Exceptions; namespace Speckle.Converters.Rhino.ToHost.Raw; +public interface IFlatPointListToHostConverter : ITypedConverter, Point3dList> +{ + IEnumerable ConvertToEnum(IReadOnlyList target); +} + /// /// Converts a flat list of raw double values to a Point3dList. /// -public class FlatPointListToHostConverter : ITypedConverter, Point3dList> +public class FlatPointListToHostConverter : IFlatPointListToHostConverter { /// /// Converts a flat list of raw double values to a Point3dList. @@ -20,20 +25,19 @@ public class FlatPointListToHostConverter : ITypedConverter /// Throws when the input list count is not a multiple of 3. - public Point3dList Convert(IReadOnlyList target) + public Point3dList Convert(IReadOnlyList target) => new(ConvertToEnum(target)); + + //avoids temporary collection by using this when necessary + public IEnumerable ConvertToEnum(IReadOnlyList target) { if (target.Count % 3 != 0) { throw new ValidationException("Array malformed: length%3 != 0."); } - var points = new List(target.Count / 3); - for (int i = 2; i < target.Count; i += 3) { - points.Add(new RG.Point3d(target[i - 2], target[i - 1], target[i])); + yield return new RG.Point3d(target[i - 2], target[i - 1], target[i]); } - - return new Point3dList(points); } } diff --git a/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/Raw/MeshToHostConverter.cs b/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/Raw/MeshToHostConverter.cs index 1ea03d8e7..389d4fe48 100644 --- a/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/Raw/MeshToHostConverter.cs +++ b/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/Raw/MeshToHostConverter.cs @@ -1,19 +1,12 @@ -using System.Drawing; -using Rhino.Collections; +using System.Drawing; using Speckle.Converters.Common.Objects; using Speckle.Objects.Utils; +using Speckle.Sdk; namespace Speckle.Converters.Rhino.ToHost.Raw; -public class MeshToHostConverter : ITypedConverter +public class MeshToHostConverter(IFlatPointListToHostConverter pointListConverter) : ITypedConverter { - private readonly ITypedConverter, Point3dList> _pointListConverter; - - public MeshToHostConverter(ITypedConverter, Point3dList> pointListConverter) - { - _pointListConverter = pointListConverter; - } - /// /// Converts a Speckle mesh object to a Rhino mesh object. /// @@ -22,33 +15,53 @@ public class MeshToHostConverter : ITypedConverter /// ⚠️ This conversion does NOT perform scaling. public RG.Mesh Convert(SOG.Mesh target) { - target.AlignVerticesWithTexCoordsByIndex(); - RG.Mesh m = new(); - var vertices = _pointListConverter.Convert(target.vertices); - var colors = target.colors.Select(Color.FromArgb).ToArray(); + var vertices = pointListConverter.ConvertToEnum(target.vertices); m.Vertices.AddVertices(vertices); - m.VertexColors.SetColors(colors); + + if (target.colors.Count != 0) + { + var colors = ConvertVertexColors(target.colors); + if (!m.VertexColors.SetColors(colors)) + { + throw new SpeckleException("Failed to set Vertex Colors"); + } + } + + if (target.vertexNormals.Count != 0) + { + var vertexNormals = ConvertVertexNormals(target.vertexNormals); + if (!m.Normals.SetNormals(vertexNormals)) + { + throw new SpeckleException("Failed to set Vertex Normals"); + } + } + + if (target.textureCoordinates.Count != 0) + { + var textureCoordinates = ConvertTextureCoordinates(target.textureCoordinates); + if (!m.TextureCoordinates.SetTextureCoordinates(textureCoordinates)) + { + throw new SpeckleException("Failed to set Texture Coordinates"); + } + } AssignMeshFaces(target, m); - AssignTextureCoordinates(target, m); // POC: CNX-9273 There was a piece of code here about Merging co-planar faces that I've removed for now as this setting does not exist yet. return m; } - // POC: CNX-9274 We should abstract this into the `Mesh` class, or some utility class adjacent to it - // All converters need to do this so it's going to be a source of repetition - // and it is directly tied to how we serialise the data in the mesh. - private void AssignMeshFaces(SOG.Mesh target, RG.Mesh m) + private static void AssignMeshFaces(SOG.Mesh target, RG.Mesh m) { int i = 0; while (i < target.faces.Count) { int n = target.faces[i]; + // For backwards compatibility. Old meshes will have "0" for triangle face, "1" for quad face. // Newer meshes have "3" for triangle face, "4" for quad" face and "5...n" for n-gon face. if (n < 3) @@ -56,47 +69,81 @@ public class MeshToHostConverter : ITypedConverter n += 3; // 0 -> 3, 1 -> 4 } - if (n == 3) + switch (n) { - // triangle - m.Faces.AddFace(new RG.MeshFace(target.faces[i + 1], target.faces[i + 2], target.faces[i + 3])); - } - else if (n == 4) - { - // quad - m.Faces.AddFace( - new RG.MeshFace(target.faces[i + 1], target.faces[i + 2], target.faces[i + 3], target.faces[i + 4]) - ); - } - else - { - // n-gon - var triangles = MeshTriangulationHelper.TriangulateFace(i, target, false); - - var faceIndices = new List(triangles.Count); - for (int t = 0; t < triangles.Count; t += 3) + case 3: + // triangle + m.Faces.AddFace(target.faces[i + 1], target.faces[i + 2], target.faces[i + 3]); + break; + case 4: + // quad + m.Faces.AddFace(target.faces[i + 1], target.faces[i + 2], target.faces[i + 3], target.faces[i + 4]); + break; + default: { - var face = new RG.MeshFace(triangles[t], triangles[t + 1], triangles[t + 2]); - faceIndices.Add(m.Faces.AddFace(face)); + // n-gon + var faceIndices = GetNgonFaceIndices(target, i, m).ToList(); + var vertexIndices = GetNgonVertexIndices(target.faces, i, n).ToList(); + RG.MeshNgon ngon = RG.MeshNgon.Create(vertexIndices, faceIndices); + m.Ngons.AddNgon(ngon); + break; } - - RG.MeshNgon ngon = RG.MeshNgon.Create(target.faces.GetRange(i + 1, n), faceIndices); - m.Ngons.AddNgon(ngon); } i += n + 1; } + + // Its important that this is the last step m.Faces.CullDegenerateFaces(); } - private void AssignTextureCoordinates(SOG.Mesh target, RG.Mesh m) + private static IEnumerable GetNgonFaceIndices(SOG.Mesh target, int start, RG.Mesh m) { - var textureCoordinates = new RG.Point2f[target.TextureCoordinatesCount]; - for (int ti = 0; ti < target.TextureCoordinatesCount; ti++) + var triangles = MeshTriangulationHelper.TriangulateFace(start, target, false); + for (int t = 0; t < triangles.Count; t += 3) { - var (u, v) = target.GetTextureCoordinate(ti); - textureCoordinates[ti] = new RG.Point2f(u, v); + int faceIndex = m.Faces.AddFace(triangles[t], triangles[t + 1], triangles[t + 2]); + yield return faceIndex; } - m.TextureCoordinates.SetTextureCoordinates(textureCoordinates); + } + + private static IEnumerable GetNgonVertexIndices(List faces, int start, int vertexCount) + { + for (int n = 0; n < vertexCount; n++) + { + yield return faces[start + 1 + n]; + } + } + + private static RG.Point2f[] ConvertTextureCoordinates(IReadOnlyList textureCoordinates) + { + var converted = new RG.Point2f[textureCoordinates.Count / 2]; + for (int i = 0, j = 0; i < textureCoordinates.Count; i += 2, j++) + { + float u = (float)textureCoordinates[i]; + float v = (float)textureCoordinates[i + 1]; + converted[j] = new RG.Point2f(u, v); + } + + return converted; + } + + private static RG.Vector3f[] ConvertVertexNormals(IReadOnlyList vertexNormals) + { + RG.Vector3f[] converted = new RG.Vector3f[vertexNormals.Count / 3]; + for (int i = 0, j = 0; i < vertexNormals.Count; i += 3, j++) + { + var x = (float)vertexNormals[i + 0]; + var y = (float)vertexNormals[i + 1]; + var z = (float)vertexNormals[i + 2]; + converted[j] = new RG.Vector3f(x, y, z); + } + + return converted; + } + + private static Color[] ConvertVertexColors(IReadOnlyList vertexColors) + { + return Enumerable.Select(vertexColors, Color.FromArgb).ToArray(); } } diff --git a/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/Raw/PointCloudToHostConverter.cs b/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/Raw/PointCloudToHostConverter.cs index ee2e3052b..da2d3dcff 100644 --- a/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/Raw/PointCloudToHostConverter.cs +++ b/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/Raw/PointCloudToHostConverter.cs @@ -1,6 +1,7 @@ using System.Drawing; using Rhino.Collections; using Speckle.Converters.Common.Objects; +using Speckle.Sdk.Common.Exceptions; namespace Speckle.Converters.Rhino.ToHost.Raw; @@ -22,10 +23,17 @@ public class PointCloudToHostConverter : ITypedConverter { public ArcToHostTopLevelConverter( diff --git a/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/BrepToHostTopLevelConverter.cs b/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/BrepToHostTopLevelConverter.cs index a20d9f1c3..d7908ec6d 100644 --- a/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/BrepToHostTopLevelConverter.cs +++ b/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/BrepToHostTopLevelConverter.cs @@ -3,7 +3,7 @@ using Speckle.Converters.Common.Objects; namespace Speckle.Converters.Rhino.ToHost.TopLevel; -[NameAndRankValue(nameof(SOG.Brep), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +[NameAndRankValue(typeof(SOG.Brep), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] public class BrepToHostTopLevelConverter : SpeckleToHostGeometryBaseTopLevelConverter { public BrepToHostTopLevelConverter( diff --git a/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/BrepXToHostTopLevelConverter.cs b/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/BrepXToHostTopLevelConverter.cs index 08f1c6902..d7ba18740 100644 --- a/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/BrepXToHostTopLevelConverter.cs +++ b/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/BrepXToHostTopLevelConverter.cs @@ -3,7 +3,7 @@ using Speckle.Converters.Common.Objects; namespace Speckle.Converters.Rhino.ToHost.TopLevel; -[NameAndRankValue(nameof(SOG.BrepX), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +[NameAndRankValue(typeof(SOG.BrepX), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] public class BrepXToHostTopLevelConverter : SpeckleToHostGeometryBaseTopLevelConverter> { public BrepXToHostTopLevelConverter( @@ -13,7 +13,7 @@ public class BrepXToHostTopLevelConverter : SpeckleToHostGeometryBaseTopLevelCon : base(settingsStore, geometryBaseConverter) { } } -[NameAndRankValue(nameof(SOG.SubDX), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +[NameAndRankValue(typeof(SOG.SubDX), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] public class SubDXToHostTopLevelConverter : SpeckleToHostGeometryBaseTopLevelConverter> { public SubDXToHostTopLevelConverter( @@ -23,7 +23,7 @@ public class SubDXToHostTopLevelConverter : SpeckleToHostGeometryBaseTopLevelCon : base(settingsStore, geometryBaseConverter) { } } -[NameAndRankValue(nameof(SOG.ExtrusionX), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +[NameAndRankValue(typeof(SOG.ExtrusionX), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] public class ExtrusionXToHostTopLevelConverter : SpeckleToHostGeometryBaseTopLevelConverter> { diff --git a/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/CircleToHostTopLevelConverter.cs b/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/CircleToHostTopLevelConverter.cs index 17e60e248..73de699da 100644 --- a/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/CircleToHostTopLevelConverter.cs +++ b/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/CircleToHostTopLevelConverter.cs @@ -3,7 +3,7 @@ using Speckle.Converters.Common.Objects; namespace Speckle.Converters.Rhino.ToHost.TopLevel; -[NameAndRankValue(nameof(SOG.Circle), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +[NameAndRankValue(typeof(SOG.Circle), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] public class CircleToHostTopLevelConverter : SpeckleToHostGeometryBaseTopLevelConverter { public CircleToHostTopLevelConverter( diff --git a/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/DataObjectConverter.cs b/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/DataObjectConverter.cs new file mode 100644 index 000000000..ab5c5be52 --- /dev/null +++ b/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/DataObjectConverter.cs @@ -0,0 +1,98 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Objects.Data; +using Speckle.Sdk.Common; +using Speckle.Sdk.Common.Exceptions; +using Speckle.Sdk.Models; + +namespace Speckle.Converters.Rhino.ToHost.TopLevel; + +[NameAndRankValue(typeof(DataObject), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class DataObjectConverter + : IToHostTopLevelConverter, + ITypedConverter> +{ + private readonly ITypedConverter _arcConverter; + private readonly ITypedConverter _circleConverter; + private readonly ITypedConverter _curveConverter; + private readonly ITypedConverter _ellipseConverter; + private readonly ITypedConverter _lineConverter; + private readonly ITypedConverter _meshConverter; + private readonly ITypedConverter _pointcloudConverter; + private readonly ITypedConverter _pointConverter; + private readonly ITypedConverter _polycurveConverter; + private readonly ITypedConverter _polylineConverter; + private readonly IConverterSettingsStore _settingsStore; + + public DataObjectConverter( + ITypedConverter arcConverter, + ITypedConverter circleConverter, + ITypedConverter curveConverter, + ITypedConverter ellipseConverter, + ITypedConverter lineConverter, + ITypedConverter meshConverter, + ITypedConverter pointcloudConverter, + ITypedConverter pointConverter, + ITypedConverter polylineConverter, + ITypedConverter polycurveConverter, + IConverterSettingsStore settingsStore + ) + { + _arcConverter = arcConverter; + _circleConverter = circleConverter; + _curveConverter = curveConverter; + _ellipseConverter = ellipseConverter; + _lineConverter = lineConverter; + _meshConverter = meshConverter; + _pointcloudConverter = pointcloudConverter; + _pointConverter = pointConverter; + _polycurveConverter = polycurveConverter; + _polylineConverter = polylineConverter; + _settingsStore = settingsStore; + } + + public object Convert(Base target) => Convert((DataObject)target); + + public List<(RG.GeometryBase a, Base b)> Convert(DataObject target) + { + var result = new List(); + foreach (var item in target.displayValue) + { + RG.GeometryBase x = item switch + { + SOG.Arc arc => _arcConverter.Convert(arc), + SOG.Circle circle => _circleConverter.Convert(circle), + SOG.Curve curve => _curveConverter.Convert(curve), + SOG.Ellipse ellipse => _ellipseConverter.Convert(ellipse), + SOG.Line line => _lineConverter.Convert(line), + SOG.Mesh mesh => _meshConverter.Convert(mesh), + SOG.Pointcloud pointcloud => _pointcloudConverter.Convert(pointcloud), + SOG.Point point => _pointConverter.Convert(point), + SOG.Polycurve polycurve => _polycurveConverter.Convert(polycurve), + SOG.Polyline polyline => _polylineConverter.Convert(polyline), + _ => throw new ConversionException($"Found unsupported fallback geometry: {item.GetType()}") + }; + x.Transform(GetUnitsTransform(item)); + result.Add(x); + } + + return result.Zip(target.displayValue, (a, b) => (a, b)).ToList(); + } + + private RG.Transform GetUnitsTransform(Base speckleObject) + { + /* + * POC: CNX-9270 Looking at a simpler, more performant way of doing unit scaling on `ToNative` + * by fully relying on the transform capabilities of the HostApp, and only transforming top-level stuff. + * This may not hold when adding more complex conversions, but it works for now! + */ + if (speckleObject["units"] is string units) + { + var scaleFactor = Units.GetConversionFactor(units, _settingsStore.Current.SpeckleUnits); + var scale = RG.Transform.Scale(RG.Point3d.Origin, scaleFactor); + return scale; + } + + return RG.Transform.Identity; + } +} diff --git a/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/FallbackToHostTopLevelConverter.cs b/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/DisplayableObjectToHostTopLevelConverter.cs similarity index 88% rename from Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/FallbackToHostTopLevelConverter.cs rename to Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/DisplayableObjectToHostTopLevelConverter.cs index 24beb37ad..24bde1354 100644 --- a/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/FallbackToHostTopLevelConverter.cs +++ b/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/DisplayableObjectToHostTopLevelConverter.cs @@ -6,10 +6,10 @@ using Speckle.Sdk.Models; namespace Speckle.Converters.Rhino.ToHost.TopLevel; -[NameAndRankValue(nameof(DisplayableObject), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] -public class FallbackToHostTopLevelConverter +[NameAndRankValue(typeof(DisplayableObject), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class DisplayableObjectConverter : IToHostTopLevelConverter, - ITypedConverter> + ITypedConverter> { private readonly ITypedConverter _pointConverter; private readonly ITypedConverter _lineConverter; @@ -18,7 +18,7 @@ public class FallbackToHostTopLevelConverter private readonly ITypedConverter _meshConverter; private readonly IConverterSettingsStore _settingsStore; - public FallbackToHostTopLevelConverter( + public DisplayableObjectConverter( ITypedConverter pointConverter, ITypedConverter lineConverter, ITypedConverter polylineConverter, @@ -37,7 +37,7 @@ public class FallbackToHostTopLevelConverter public object Convert(Base target) => Convert((DisplayableObject)target); - public List Convert(DisplayableObject target) + public List<(RG.GeometryBase a, Base b)> Convert(DisplayableObject target) { var result = new List(); foreach (var item in target.displayValue) @@ -55,7 +55,7 @@ public class FallbackToHostTopLevelConverter result.Add(x); } - return result; + return result.Zip(target.displayValue, (a, b) => (a, b)).ToList(); } private RG.Transform GetUnitsTransform(Base speckleObject) diff --git a/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/EllipseToHostTopLevelConverter.cs b/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/EllipseToHostTopLevelConverter.cs index 24851e534..68cf3eab7 100644 --- a/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/EllipseToHostTopLevelConverter.cs +++ b/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/EllipseToHostTopLevelConverter.cs @@ -3,7 +3,7 @@ using Speckle.Converters.Common.Objects; namespace Speckle.Converters.Rhino.ToHost.TopLevel; -[NameAndRankValue(nameof(SOG.Ellipse), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +[NameAndRankValue(typeof(SOG.Ellipse), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] public class EllipseToHostTopLevelConverter : SpeckleToHostGeometryBaseTopLevelConverter { public EllipseToHostTopLevelConverter( diff --git a/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/LineToHostTopLevelConverter.cs b/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/LineToHostTopLevelConverter.cs index 315fcd611..411c9fc87 100644 --- a/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/LineToHostTopLevelConverter.cs +++ b/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/LineToHostTopLevelConverter.cs @@ -3,7 +3,7 @@ using Speckle.Converters.Common.Objects; namespace Speckle.Converters.Rhino.ToHost.TopLevel; -[NameAndRankValue(nameof(SOG.Line), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +[NameAndRankValue(typeof(SOG.Line), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] public class LineToHostTopLevelConverter : SpeckleToHostGeometryBaseTopLevelConverter { public LineToHostTopLevelConverter( diff --git a/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/MeshToHostTopLevelConverter.cs b/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/MeshToHostTopLevelConverter.cs index db768ad0d..9b56b86e9 100644 --- a/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/MeshToHostTopLevelConverter.cs +++ b/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/MeshToHostTopLevelConverter.cs @@ -3,7 +3,7 @@ using Speckle.Converters.Common.Objects; namespace Speckle.Converters.Rhino.ToHost.TopLevel; -[NameAndRankValue(nameof(SOG.Mesh), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +[NameAndRankValue(typeof(SOG.Mesh), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] public class MeshToHostTopLevelConverter : SpeckleToHostGeometryBaseTopLevelConverter { public MeshToHostTopLevelConverter( diff --git a/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/NurbsCurveToHostTopLevelConverter.cs b/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/NurbsCurveToHostTopLevelConverter.cs index 2c507f6ac..9000b281a 100644 --- a/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/NurbsCurveToHostTopLevelConverter.cs +++ b/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/NurbsCurveToHostTopLevelConverter.cs @@ -3,7 +3,7 @@ using Speckle.Converters.Common.Objects; namespace Speckle.Converters.Rhino.ToHost.TopLevel; -[NameAndRankValue(nameof(SOG.Curve), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +[NameAndRankValue(typeof(SOG.Curve), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] public class NurbsCurveToHostTopLevelConverter : SpeckleToHostGeometryBaseTopLevelConverter { public NurbsCurveToHostTopLevelConverter( diff --git a/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/PointCloudToHostTopLevelConverter.cs b/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/PointCloudToHostTopLevelConverter.cs index 99439d9c2..2f7589290 100644 --- a/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/PointCloudToHostTopLevelConverter.cs +++ b/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/PointCloudToHostTopLevelConverter.cs @@ -3,7 +3,7 @@ using Speckle.Converters.Common.Objects; namespace Speckle.Converters.Rhino.ToHost.TopLevel; -[NameAndRankValue(nameof(SOG.Pointcloud), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +[NameAndRankValue(typeof(SOG.Pointcloud), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] public class PointCloudToHostTopLevelConverter : SpeckleToHostGeometryBaseTopLevelConverter { diff --git a/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/PointToHostTopLevelConverter.cs b/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/PointToHostTopLevelConverter.cs index 2211f8227..3b8b0f841 100644 --- a/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/PointToHostTopLevelConverter.cs +++ b/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/PointToHostTopLevelConverter.cs @@ -3,7 +3,7 @@ using Speckle.Converters.Common.Objects; namespace Speckle.Converters.Rhino.ToHost.TopLevel; -[NameAndRankValue(nameof(SOG.Point), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +[NameAndRankValue(typeof(SOG.Point), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] public class PointToHostTopLevelConverter : SpeckleToHostGeometryBaseTopLevelConverter { public PointToHostTopLevelConverter( diff --git a/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/PolycurveToHostTopLevelConverter.cs b/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/PolycurveToHostTopLevelConverter.cs index 7a523999d..84f178e54 100644 --- a/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/PolycurveToHostTopLevelConverter.cs +++ b/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/PolycurveToHostTopLevelConverter.cs @@ -3,7 +3,7 @@ using Speckle.Converters.Common.Objects; namespace Speckle.Converters.Rhino.ToHost.TopLevel; -[NameAndRankValue(nameof(SOG.Polycurve), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +[NameAndRankValue(typeof(SOG.Polycurve), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] public class PolycurveToHostTopLevelConverter : SpeckleToHostGeometryBaseTopLevelConverter { public PolycurveToHostTopLevelConverter( diff --git a/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/PolylineToHostTopLevelConverter.cs b/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/PolylineToHostTopLevelConverter.cs index 6323f955a..d8608ad78 100644 --- a/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/PolylineToHostTopLevelConverter.cs +++ b/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/PolylineToHostTopLevelConverter.cs @@ -3,7 +3,7 @@ using Speckle.Converters.Common.Objects; namespace Speckle.Converters.Rhino.ToHost.TopLevel; -[NameAndRankValue(nameof(SOG.Polyline), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +[NameAndRankValue(typeof(SOG.Polyline), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] public class PolylineToHostTopLevelConverter : SpeckleToHostGeometryBaseTopLevelConverter { diff --git a/Converters/Rhino/Speckle.Converters.RhinoShared/ToSpeckle/Meshing/DisplayMeshExtractor.cs b/Converters/Rhino/Speckle.Converters.RhinoShared/ToSpeckle/Meshing/DisplayMeshExtractor.cs index e7e6984a5..58186d3d1 100644 --- a/Converters/Rhino/Speckle.Converters.RhinoShared/ToSpeckle/Meshing/DisplayMeshExtractor.cs +++ b/Converters/Rhino/Speckle.Converters.RhinoShared/ToSpeckle/Meshing/DisplayMeshExtractor.cs @@ -16,6 +16,11 @@ public static class DisplayMeshExtractor joinedMesh.Append(renderMeshes); return joinedMesh; } + if (renderMeshes == null) + { + //MeshingParametrs with small minimumEdgeLength often leads to `CreateFromBrep` returning null + throw new ConversionException($"Failed to meshify {obj.GetType()} (perhaps the brep is too small?)"); + } return GetDisplayMeshFromGeometry(obj.Geometry); } diff --git a/Converters/Rhino/Speckle.Converters.RhinoShared/ToSpeckle/Raw/MeshToSpeckleConverter.cs b/Converters/Rhino/Speckle.Converters.RhinoShared/ToSpeckle/Raw/MeshToSpeckleConverter.cs index 54c68c543..8214ab5f5 100644 --- a/Converters/Rhino/Speckle.Converters.RhinoShared/ToSpeckle/Raw/MeshToSpeckleConverter.cs +++ b/Converters/Rhino/Speckle.Converters.RhinoShared/ToSpeckle/Raw/MeshToSpeckleConverter.cs @@ -4,20 +4,17 @@ using Speckle.Sdk.Common.Exceptions; namespace Speckle.Converters.Rhino.ToSpeckle.Raw; -[NameAndRankValue(nameof(RG.Mesh), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +[NameAndRankValue(typeof(RG.Mesh), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] public class MeshToSpeckleConverter : ITypedConverter { - private readonly ITypedConverter _pointConverter; private readonly ITypedConverter _boxConverter; private readonly IConverterSettingsStore _settingsStore; public MeshToSpeckleConverter( - ITypedConverter pointConverter, ITypedConverter boxConverter, IConverterSettingsStore settingsStore ) { - _pointConverter = pointConverter; _boxConverter = boxConverter; _settingsStore = settingsStore; } @@ -34,35 +31,51 @@ public class MeshToSpeckleConverter : ITypedConverter { throw new ValidationException("Cannot convert a mesh with 0 vertices/faces"); } - - List vertexCoordinates = new(target.Vertices.Count * 3); - foreach (var v in target.Vertices) + var vertexCoordinates = new double[target.Vertices.Count * 3]; + var x = 0; + for (int i = 0; i < target.Vertices.Count; i++) { - vertexCoordinates.Add(v.X); - vertexCoordinates.Add(v.Y); - vertexCoordinates.Add(v.Z); + var v = target.Vertices[i]; + vertexCoordinates[x++] = v.X; + vertexCoordinates[x++] = v.Y; + vertexCoordinates[x++] = v.Z; } List faces = new(); + foreach (RG.MeshNgon polygon in target.GetNgonAndFacesEnumerable()) { var vertIndices = polygon.BoundaryVertexIndexList(); int n = vertIndices.Length; faces.Add(n); - faces.AddRange(vertIndices.Select(vertIndex => (int)vertIndex)); + for (int i = 0; i < n; i++) + { + faces.Add((int)vertIndices[i]); + } } - List textureCoordinates = new(target.TextureCoordinates.Count * 2); + var textureCoordinates = new double[target.TextureCoordinates.Count * 2]; + x = 0; foreach (var textureCoord in target.TextureCoordinates) { - textureCoordinates.Add(textureCoord.X); - textureCoordinates.Add(textureCoord.Y); + textureCoordinates[x++] = textureCoord.X; + textureCoordinates[x++] = textureCoord.Y; } - List colors = new(target.VertexColors.Count); + var colors = new int[target.VertexColors.Count]; + x = 0; foreach (var c in target.VertexColors) { - colors.Add(c.ToArgb()); + colors[x++] = c.ToArgb(); + } + + var vertexNormals = new double[target.Normals.Count * 3]; + x = 0; + foreach (var n in target.Normals) + { + vertexNormals[x++] = n.X; + vertexNormals[x++] = n.Y; + vertexNormals[x++] = n.Z; } double volume = target.IsClosed ? target.Volume() : 0; @@ -70,10 +83,11 @@ public class MeshToSpeckleConverter : ITypedConverter return new SOG.Mesh { - vertices = vertexCoordinates, + vertices = new(vertexCoordinates), faces = faces, - colors = colors, - textureCoordinates = textureCoordinates, + colors = new(colors), + textureCoordinates = new(textureCoordinates), + vertexNormals = new(vertexNormals), units = _settingsStore.Current.SpeckleUnits, volume = volume, bbox = bbox diff --git a/Converters/Rhino/Speckle.Converters.RhinoShared/ToSpeckle/Raw/NurbsSurfaceToSpeckleConverter.cs b/Converters/Rhino/Speckle.Converters.RhinoShared/ToSpeckle/Raw/NurbsSurfaceToSpeckleConverter.cs new file mode 100644 index 000000000..2cc0e56a6 --- /dev/null +++ b/Converters/Rhino/Speckle.Converters.RhinoShared/ToSpeckle/Raw/NurbsSurfaceToSpeckleConverter.cs @@ -0,0 +1,70 @@ +using Rhino.Geometry.Collections; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.Rhino.ToSpeckle.Raw; + +public class NurbsSurfaceToSpeckleConverter : ITypedConverter +{ + private readonly ITypedConverter _boxConverter; + private readonly ITypedConverter _intervalConverter; + private readonly ITypedConverter _controlPointConverter; + private readonly IConverterSettingsStore _settingsStore; + + public NurbsSurfaceToSpeckleConverter( + ITypedConverter boxConverter, + ITypedConverter intervalConverter, + ITypedConverter controlPointConverter, + IConverterSettingsStore settingsStore + ) + { + _boxConverter = boxConverter; + _intervalConverter = intervalConverter; + _controlPointConverter = controlPointConverter; + _settingsStore = settingsStore; + } + + /// + /// Converts a NurbsSurface object to a Surface object. + /// + /// The NurbsSurface object to convert. + /// A Surface object representing the converted NurbsSurface. + public SOG.Surface Convert(RG.NurbsSurface target) + { + var controlPoints = ControlPointsToSpeckle(target.Points); + var result = new SOG.Surface(controlPoints) + { + degreeU = target.OrderU - 1, + degreeV = target.OrderV - 1, + rational = target.IsRational, + closedU = target.IsClosed(0), + closedV = target.IsClosed(1), + domainU = _intervalConverter.Convert(target.Domain(0)), + domainV = _intervalConverter.Convert(target.Domain(1)), + knotsU = target.KnotsU.ToList(), + knotsV = target.KnotsV.ToList(), + units = _settingsStore.Current.SpeckleUnits, + bbox = _boxConverter.Convert(new RG.Box(target.GetBoundingBox(true))), + }; + + return result; + } + + private List> ControlPointsToSpeckle(NurbsSurfacePointList controlPoints) + { + var points = new List>(); + for (var i = 0; i < controlPoints.CountU; i++) + { + var row = new List(); + for (var j = 0; j < controlPoints.CountV; j++) + { + var pt = controlPoints.GetControlPoint(i, j); + row.Add(_controlPointConverter.Convert(pt)); + } + + points.Add(row); + } + + return points; + } +} diff --git a/Converters/Rhino/Speckle.Converters.RhinoShared/ToSpeckle/TopLevel/CurveObjectToSpeckleTopLevelConverter.cs b/Converters/Rhino/Speckle.Converters.RhinoShared/ToSpeckle/TopLevel/CurveObjectToSpeckleTopLevelConverter.cs index 6db96b2bd..cb5266954 100644 --- a/Converters/Rhino/Speckle.Converters.RhinoShared/ToSpeckle/TopLevel/CurveObjectToSpeckleTopLevelConverter.cs +++ b/Converters/Rhino/Speckle.Converters.RhinoShared/ToSpeckle/TopLevel/CurveObjectToSpeckleTopLevelConverter.cs @@ -5,7 +5,7 @@ using Speckle.Sdk.Models; namespace Speckle.Converters.Rhino.ToSpeckle.TopLevel; -[NameAndRankValue(nameof(CurveObject), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +[NameAndRankValue(typeof(CurveObject), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] public class CurveObjectToSpeckleTopLevelConverter : RhinoObjectToSpeckleTopLevelConverter { public CurveObjectToSpeckleTopLevelConverter(ITypedConverter conversion) diff --git a/Converters/Rhino/Speckle.Converters.RhinoShared/ToSpeckle/TopLevel/MeshObjectToSpeckleTopLevelConverter.cs b/Converters/Rhino/Speckle.Converters.RhinoShared/ToSpeckle/TopLevel/MeshObjectToSpeckleTopLevelConverter.cs index dc528dab3..92a4675b1 100644 --- a/Converters/Rhino/Speckle.Converters.RhinoShared/ToSpeckle/TopLevel/MeshObjectToSpeckleTopLevelConverter.cs +++ b/Converters/Rhino/Speckle.Converters.RhinoShared/ToSpeckle/TopLevel/MeshObjectToSpeckleTopLevelConverter.cs @@ -4,7 +4,7 @@ using Speckle.Converters.Common.Objects; namespace Speckle.Converters.Rhino.ToSpeckle.TopLevel; -[NameAndRankValue(nameof(MeshObject), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +[NameAndRankValue(typeof(MeshObject), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] public class MeshObjectToSpeckleTopLevelConverter : RhinoObjectToSpeckleTopLevelConverter { public MeshObjectToSpeckleTopLevelConverter(ITypedConverter conversion) diff --git a/Converters/Rhino/Speckle.Converters.RhinoShared/ToSpeckle/TopLevel/PointCloudObjectToSpeckleTopLevelConverter.cs b/Converters/Rhino/Speckle.Converters.RhinoShared/ToSpeckle/TopLevel/PointCloudObjectToSpeckleTopLevelConverter.cs index a5af1cfaa..e26ac0fd1 100644 --- a/Converters/Rhino/Speckle.Converters.RhinoShared/ToSpeckle/TopLevel/PointCloudObjectToSpeckleTopLevelConverter.cs +++ b/Converters/Rhino/Speckle.Converters.RhinoShared/ToSpeckle/TopLevel/PointCloudObjectToSpeckleTopLevelConverter.cs @@ -4,7 +4,7 @@ using Speckle.Converters.Common.Objects; namespace Speckle.Converters.Rhino.ToSpeckle.TopLevel; -[NameAndRankValue(nameof(PointCloudObject), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +[NameAndRankValue(typeof(PointCloudObject), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] public class PointCloudObjectToSpeckleTopLevelConverter : RhinoObjectToSpeckleTopLevelConverter { diff --git a/Converters/Rhino/Speckle.Converters.RhinoShared/ToSpeckle/TopLevel/PointObjectToSpeckleTopLevelConverter.cs b/Converters/Rhino/Speckle.Converters.RhinoShared/ToSpeckle/TopLevel/PointObjectToSpeckleTopLevelConverter.cs index 4f480ef2e..3c5493acb 100644 --- a/Converters/Rhino/Speckle.Converters.RhinoShared/ToSpeckle/TopLevel/PointObjectToSpeckleTopLevelConverter.cs +++ b/Converters/Rhino/Speckle.Converters.RhinoShared/ToSpeckle/TopLevel/PointObjectToSpeckleTopLevelConverter.cs @@ -4,7 +4,7 @@ using Speckle.Converters.Common.Objects; namespace Speckle.Converters.Rhino.ToSpeckle.TopLevel; -[NameAndRankValue(nameof(PointObject), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +[NameAndRankValue(typeof(PointObject), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] public class PointObjectToSpeckleTopLevelConverter : RhinoObjectToSpeckleTopLevelConverter { diff --git a/Converters/Rhino/Speckle.Converters.RhinoShared/ToSpeckle/TopLevel/RhinoObjectToSpeckleTopLevelConverter.cs b/Converters/Rhino/Speckle.Converters.RhinoShared/ToSpeckle/TopLevel/RhinoObjectToSpeckleTopLevelConverter.cs index 7ec873b64..b0aa134af 100644 --- a/Converters/Rhino/Speckle.Converters.RhinoShared/ToSpeckle/TopLevel/RhinoObjectToSpeckleTopLevelConverter.cs +++ b/Converters/Rhino/Speckle.Converters.RhinoShared/ToSpeckle/TopLevel/RhinoObjectToSpeckleTopLevelConverter.cs @@ -26,12 +26,7 @@ public abstract class RhinoObjectToSpeckleTopLevelConverternet48 x64 true - Debug;Release;Local false + Debug;Release;Local @@ -15,6 +15,7 @@ + diff --git a/Converters/Tekla/Speckle.Converter.Tekla2024/Speckle.Converter.Tekla2024.csproj b/Converters/Tekla/Speckle.Converter.Tekla2024/Speckle.Converter.Tekla2024.csproj index 6dfdf7d0b..b18fe83b0 100644 --- a/Converters/Tekla/Speckle.Converter.Tekla2024/Speckle.Converter.Tekla2024.csproj +++ b/Converters/Tekla/Speckle.Converter.Tekla2024/Speckle.Converter.Tekla2024.csproj @@ -4,8 +4,8 @@ net48 x64 true - Debug;Release;Local false + Debug;Release;Local @@ -14,6 +14,7 @@ + diff --git a/Converters/Tekla/Speckle.Converters.TeklaShared/Extensions/ModelObjectExtension.cs b/Converters/Tekla/Speckle.Converters.TeklaShared/Extensions/ModelObjectExtension.cs index 930f0483a..48029e841 100644 --- a/Converters/Tekla/Speckle.Converters.TeklaShared/Extensions/ModelObjectExtension.cs +++ b/Converters/Tekla/Speckle.Converters.TeklaShared/Extensions/ModelObjectExtension.cs @@ -1,7 +1,7 @@ // this extension method is copy of the same method from connector // we are using it for to make sure we are traversing children in the same way -namespace Speckle.Converter.Tekla2024.Extensions; +namespace Speckle.Converters.TeklaShared.Extensions; public static class ModelObjectExtensions { diff --git a/Converters/Tekla/Speckle.Converters.TeklaShared/Extensions/SpeckleApplicationIdExtensions.cs b/Converters/Tekla/Speckle.Converters.TeklaShared/Extensions/SpeckleApplicationIdExtensions.cs index dfc7d9938..82bdfe594 100644 --- a/Converters/Tekla/Speckle.Converters.TeklaShared/Extensions/SpeckleApplicationIdExtensions.cs +++ b/Converters/Tekla/Speckle.Converters.TeklaShared/Extensions/SpeckleApplicationIdExtensions.cs @@ -1,4 +1,4 @@ -namespace Speckle.Converter.Tekla2024.Extensions; +namespace Speckle.Converters.TeklaShared.Extensions; public static class SpeckleApplicationIdExtensions { diff --git a/Converters/Tekla/Speckle.Converters.TeklaShared/ServiceRegistration.cs b/Converters/Tekla/Speckle.Converters.TeklaShared/ServiceRegistration.cs index e826ad4e8..c86bb217b 100644 --- a/Converters/Tekla/Speckle.Converters.TeklaShared/ServiceRegistration.cs +++ b/Converters/Tekla/Speckle.Converters.TeklaShared/ServiceRegistration.cs @@ -1,13 +1,13 @@ using System.Reflection; using Microsoft.Extensions.DependencyInjection; -using Speckle.Converter.Tekla2024.ToSpeckle.Helpers; -using Speckle.Converter.Tekla2024.ToSpeckle.TopLevel; using Speckle.Converters.Common; using Speckle.Converters.Common.Registration; +using Speckle.Converters.TeklaShared.ToSpeckle.Helpers; +using Speckle.Converters.TeklaShared.ToSpeckle.TopLevel; using Speckle.Sdk; using Tekla.Structures.Datatype; -namespace Speckle.Converter.Tekla2024; +namespace Speckle.Converters.TeklaShared; public static class ServiceRegistration { @@ -20,6 +20,8 @@ public static class ServiceRegistration serviceCollection.AddScoped(); serviceCollection.AddScoped(); serviceCollection.AddScoped(); + serviceCollection.AddScoped(); + serviceCollection.AddScoped(); serviceCollection.AddRootCommon(converterAssembly); serviceCollection.AddApplicationConverters(converterAssembly); diff --git a/Converters/Tekla/Speckle.Converters.TeklaShared/TeklaConversionSettings.cs b/Converters/Tekla/Speckle.Converters.TeklaShared/TeklaConversionSettings.cs index 7bd6f4930..aaaf8fb96 100644 --- a/Converters/Tekla/Speckle.Converters.TeklaShared/TeklaConversionSettings.cs +++ b/Converters/Tekla/Speckle.Converters.TeklaShared/TeklaConversionSettings.cs @@ -1,5 +1,5 @@ using Tekla.Structures.Model; -namespace Speckle.Converter.Tekla2024; +namespace Speckle.Converters.TeklaShared; public record TeklaConversionSettings(Model Document, bool SendRebarsAsSolid, string SpeckleUnits); diff --git a/Converters/Tekla/Speckle.Converters.TeklaShared/TeklaConversionSettingsFactory.cs b/Converters/Tekla/Speckle.Converters.TeklaShared/TeklaConversionSettingsFactory.cs index 895d8ab23..e0343827f 100644 --- a/Converters/Tekla/Speckle.Converters.TeklaShared/TeklaConversionSettingsFactory.cs +++ b/Converters/Tekla/Speckle.Converters.TeklaShared/TeklaConversionSettingsFactory.cs @@ -3,7 +3,7 @@ using Speckle.InterfaceGenerator; using Tekla.Structures.Datatype; using Tekla.Structures.Model; -namespace Speckle.Converter.Tekla2024; +namespace Speckle.Converters.TeklaShared; [GenerateAutoInterface] public class TeklaConversionSettingsFactory( diff --git a/Converters/Tekla/Speckle.Converters.TeklaShared/TeklaRootToSpeckleConverter.cs b/Converters/Tekla/Speckle.Converters.TeklaShared/TeklaRootToSpeckleConverter.cs index 65318d558..de6c171fa 100644 --- a/Converters/Tekla/Speckle.Converters.TeklaShared/TeklaRootToSpeckleConverter.cs +++ b/Converters/Tekla/Speckle.Converters.TeklaShared/TeklaRootToSpeckleConverter.cs @@ -1,13 +1,13 @@ using Microsoft.Extensions.Logging; -using Speckle.Converter.Tekla2024.Extensions; using Speckle.Converters.Common; using Speckle.Converters.Common.Objects; using Speckle.Converters.Common.Registration; +using Speckle.Converters.TeklaShared.Extensions; using Speckle.Sdk.Common.Exceptions; using Speckle.Sdk.Models; using Tekla.Structures.Model; -namespace Speckle.Converter.Tekla2024; +namespace Speckle.Converters.TeklaShared; public class TeklaRootToSpeckleConverter : IRootToSpeckleConverter { diff --git a/Converters/Tekla/Speckle.Converters.TeklaShared/TeklaToSpeckleUnitConverter.cs b/Converters/Tekla/Speckle.Converters.TeklaShared/TeklaToSpeckleUnitConverter.cs index 60bafb90b..a32675029 100644 --- a/Converters/Tekla/Speckle.Converters.TeklaShared/TeklaToSpeckleUnitConverter.cs +++ b/Converters/Tekla/Speckle.Converters.TeklaShared/TeklaToSpeckleUnitConverter.cs @@ -3,7 +3,7 @@ using Speckle.Sdk.Common.Exceptions; using Tekla.Structures.Datatype; using SSC = Speckle.Sdk.Common; -namespace Speckle.Converter.Tekla2024; +namespace Speckle.Converters.TeklaShared; public class TeklaToSpeckleUnitConverter : IHostToSpeckleUnitConverter { diff --git a/Converters/Tekla/Speckle.Converters.TeklaShared/ToSpeckle/Helpers/ClassPropertyExtractor.cs b/Converters/Tekla/Speckle.Converters.TeklaShared/ToSpeckle/Helpers/ClassPropertyExtractor.cs index 5912d968e..f41b6d5e7 100644 --- a/Converters/Tekla/Speckle.Converters.TeklaShared/ToSpeckle/Helpers/ClassPropertyExtractor.cs +++ b/Converters/Tekla/Speckle.Converters.TeklaShared/ToSpeckle/Helpers/ClassPropertyExtractor.cs @@ -1,4 +1,4 @@ -namespace Speckle.Converter.Tekla2024.ToSpeckle.Helpers; +namespace Speckle.Converters.TeklaShared.ToSpeckle.Helpers; public class ClassPropertyExtractor { @@ -10,11 +10,9 @@ public class ClassPropertyExtractor switch (modelObject) { - case TSM.Beam beam: - AddBeamProperties(beam, properties); - break; - case TSM.ContourPlate plate: - AddContourPlateProperties(plate, properties); + // includes beams and contour plates + case TSM.Part part: + AddPartProperties(part, properties); break; case TSM.BoltArray boltArray: AddBoltArrayProperties(boltArray, properties); @@ -33,18 +31,10 @@ public class ClassPropertyExtractor return properties; } - private void AddBeamProperties(TSM.Beam beam, Dictionary properties) + private void AddPartProperties(TSM.Part part, Dictionary properties) { - properties["name"] = beam.Name; - properties["profile"] = beam.Profile.ProfileString; - properties["material"] = beam.Material.MaterialString; - } - - private void AddContourPlateProperties(TSM.ContourPlate plate, Dictionary properties) - { - properties["name"] = plate.Name; - properties["profile"] = plate.Profile.ProfileString; - properties["material"] = plate.Material.MaterialString; + properties["profile"] = part.Profile.ProfileString; + properties["material"] = part.Material.MaterialString; } private void AddBoltArrayProperties(TSM.BoltArray boltArray, Dictionary properties) @@ -56,20 +46,17 @@ public class ClassPropertyExtractor private void AddSingleRebarProperties(TSM.SingleRebar singleRebar, Dictionary properties) { - properties["name"] = singleRebar.Name; properties["grade"] = singleRebar.Grade; properties["size"] = singleRebar.Size; } private void AddRebarMeshProperties(TSM.RebarMesh rebarMesh, Dictionary properties) { - properties["name"] = rebarMesh.Name; properties["grade"] = rebarMesh.Grade; } private void AddRebarGroupProperties(TSM.RebarGroup rebarGroup, Dictionary properties) { - properties["name"] = rebarGroup.Name; properties["grade"] = rebarGroup.Grade; properties["size"] = rebarGroup.Size; } diff --git a/Converters/Tekla/Speckle.Converters.TeklaShared/ToSpeckle/Helpers/DisplayValueExtractor.cs b/Converters/Tekla/Speckle.Converters.TeklaShared/ToSpeckle/Helpers/DisplayValueExtractor.cs index c6be8659a..b4366d7f1 100644 --- a/Converters/Tekla/Speckle.Converters.TeklaShared/ToSpeckle/Helpers/DisplayValueExtractor.cs +++ b/Converters/Tekla/Speckle.Converters.TeklaShared/ToSpeckle/Helpers/DisplayValueExtractor.cs @@ -3,7 +3,7 @@ using Speckle.Converters.Common.Objects; using Speckle.Sdk.Common.Exceptions; using Speckle.Sdk.Models; -namespace Speckle.Converter.Tekla2024.ToSpeckle.Helpers; +namespace Speckle.Converters.TeklaShared.ToSpeckle.Helpers; public sealed class DisplayValueExtractor { diff --git a/Converters/Tekla/Speckle.Converters.TeklaShared/ToSpeckle/Helpers/PropertiesExtractor.cs b/Converters/Tekla/Speckle.Converters.TeklaShared/ToSpeckle/Helpers/PropertiesExtractor.cs new file mode 100644 index 000000000..b0b8fa4c8 --- /dev/null +++ b/Converters/Tekla/Speckle.Converters.TeklaShared/ToSpeckle/Helpers/PropertiesExtractor.cs @@ -0,0 +1,41 @@ +namespace Speckle.Converters.TeklaShared.ToSpeckle.Helpers; + +public class PropertiesExtractor +{ + private readonly ReportPropertyExtractor _reportPropertyExtractor; + private readonly UserDefinedAttributesExtractor _userDefinedAttributesExtractor; + private readonly ClassPropertyExtractor _classPropertyExtractor; + + public PropertiesExtractor( + ReportPropertyExtractor reportPropertyExtractor, + UserDefinedAttributesExtractor userDefinedAttributesExtractor, + ClassPropertyExtractor classPropertyExtractor + ) + { + _reportPropertyExtractor = reportPropertyExtractor; + _userDefinedAttributesExtractor = userDefinedAttributesExtractor; + _classPropertyExtractor = classPropertyExtractor; + } + + public Dictionary GetProperties(TSM.ModelObject modelObject) + { + // get the top level class properties first + Dictionary properties = _classPropertyExtractor.GetProperties(modelObject); + + Dictionary report = _reportPropertyExtractor.GetReportProperties(modelObject); + if (report.Count > 0) + { + properties.Add("Report", report); + } + + Dictionary userDefinedAttributes = _userDefinedAttributesExtractor.GetUserDefinedAttributes( + modelObject + ); + if (userDefinedAttributes.Count > 0) + { + properties.Add("User Defined Attributes", userDefinedAttributes); + } + + return properties; + } +} diff --git a/Converters/Tekla/Speckle.Converters.TeklaShared/ToSpeckle/Helpers/ReportPropertyExtractor.cs b/Converters/Tekla/Speckle.Converters.TeklaShared/ToSpeckle/Helpers/ReportPropertyExtractor.cs index 55a24e64e..1e3dc812d 100644 --- a/Converters/Tekla/Speckle.Converters.TeklaShared/ToSpeckle/Helpers/ReportPropertyExtractor.cs +++ b/Converters/Tekla/Speckle.Converters.TeklaShared/ToSpeckle/Helpers/ReportPropertyExtractor.cs @@ -1,6 +1,6 @@ using Tekla.Structures.Datatype; -namespace Speckle.Converter.Tekla2024.ToSpeckle.Helpers; +namespace Speckle.Converters.TeklaShared.ToSpeckle.Helpers; public class ReportPropertyExtractor { @@ -46,9 +46,9 @@ public class ReportPropertyExtractor { typeof(TSM.BoltArray), new[] { "BOLT_SIZE", "NUMBER_OF_BOLTS", "BOLT_STANDARD", "BOLT_TYPE", "LENGTH" } } }; - public Dictionary> GetReportProperties(TSM.ModelObject modelObject) + public Dictionary GetReportProperties(TSM.ModelObject modelObject) { - var reportProperties = new Dictionary>(); + var reportProperties = new Dictionary(); if (!s_typeSpecificProperties.TryGetValue(modelObject.GetType(), out var propertyNames)) { @@ -67,7 +67,7 @@ public class ReportPropertyExtractor private void TryGetReportProperty( TSM.ModelObject modelObject, string propertyName, - Dictionary> properties + Dictionary properties ) { var reportProperty = new Dictionary { ["name"] = propertyName }; diff --git a/Converters/Tekla/Speckle.Converters.TeklaShared/ToSpeckle/Helpers/UserDefinedAttributesExtractor.cs b/Converters/Tekla/Speckle.Converters.TeklaShared/ToSpeckle/Helpers/UserDefinedAttributesExtractor.cs new file mode 100644 index 000000000..c191e7b98 --- /dev/null +++ b/Converters/Tekla/Speckle.Converters.TeklaShared/ToSpeckle/Helpers/UserDefinedAttributesExtractor.cs @@ -0,0 +1,34 @@ +using System.Collections; + +namespace Speckle.Converters.TeklaShared.ToSpeckle.Helpers; + +public class UserDefinedAttributesExtractor +{ + public Dictionary GetUserDefinedAttributes(TSM.ModelObject modelObject) + { + var userProperties = new Dictionary(); + var userValues = new Hashtable(); + + if (!modelObject.GetAllUserProperties(ref userValues)) + { + // NOTE: Return empty dictionary if no user properties found or failed to get properties + return userProperties; + } + + foreach (DictionaryEntry entry in userValues) + { + if (entry.Value != null && !string.IsNullOrEmpty(entry.Value.ToString())) + { + var propertyName = entry.Key.ToString(); + var propertyValue = entry.Value; + + if (propertyName != null) + { + userProperties[propertyName] = propertyValue; + } + } + } + + return userProperties; + } +} diff --git a/Converters/Tekla/Speckle.Converters.TeklaShared/ToSpeckle/Raw/ArcToSpeckleConverter.cs b/Converters/Tekla/Speckle.Converters.TeklaShared/ToSpeckle/Raw/ArcToSpeckleConverter.cs index 89f4e2375..75b64c318 100644 --- a/Converters/Tekla/Speckle.Converters.TeklaShared/ToSpeckle/Raw/ArcToSpeckleConverter.cs +++ b/Converters/Tekla/Speckle.Converters.TeklaShared/ToSpeckle/Raw/ArcToSpeckleConverter.cs @@ -1,7 +1,7 @@ using Speckle.Converters.Common; using Speckle.Converters.Common.Objects; -namespace Speckle.Converter.Tekla2024.ToSpeckle.Raw; +namespace Speckle.Converters.TeklaShared.ToSpeckle.Raw; public class ArcToSpeckleConverter : ITypedConverter { diff --git a/Converters/Tekla/Speckle.Converters.TeklaShared/ToSpeckle/Raw/GridToSpeckleConverter.cs b/Converters/Tekla/Speckle.Converters.TeklaShared/ToSpeckle/Raw/GridToSpeckleConverter.cs index 36927b6db..497d9bef8 100644 --- a/Converters/Tekla/Speckle.Converters.TeklaShared/ToSpeckle/Raw/GridToSpeckleConverter.cs +++ b/Converters/Tekla/Speckle.Converters.TeklaShared/ToSpeckle/Raw/GridToSpeckleConverter.cs @@ -3,7 +3,7 @@ using Speckle.Converters.Common; using Speckle.Converters.Common.Objects; using Speckle.Sdk.Models; -namespace Speckle.Converter.Tekla2024.ToSpeckle.Raw; +namespace Speckle.Converters.TeklaShared.ToSpeckle.Raw; public class GridToSpeckleConverter : ITypedConverter> { diff --git a/Converters/Tekla/Speckle.Converters.TeklaShared/ToSpeckle/Raw/LineToSpeckleConverter.cs b/Converters/Tekla/Speckle.Converters.TeklaShared/ToSpeckle/Raw/LineToSpeckleConverter.cs index 00004a67c..3418c5f7c 100644 --- a/Converters/Tekla/Speckle.Converters.TeklaShared/ToSpeckle/Raw/LineToSpeckleConverter.cs +++ b/Converters/Tekla/Speckle.Converters.TeklaShared/ToSpeckle/Raw/LineToSpeckleConverter.cs @@ -1,7 +1,7 @@ using Speckle.Converters.Common; using Speckle.Converters.Common.Objects; -namespace Speckle.Converter.Tekla2024.ToSpeckle.Raw; +namespace Speckle.Converters.TeklaShared.ToSpeckle.Raw; public class TeklaLineConverter : ITypedConverter { diff --git a/Converters/Tekla/Speckle.Converters.TeklaShared/ToSpeckle/Raw/PointToSpeckleConverter.cs b/Converters/Tekla/Speckle.Converters.TeklaShared/ToSpeckle/Raw/PointToSpeckleConverter.cs index f0fad75c9..7758a77b0 100644 --- a/Converters/Tekla/Speckle.Converters.TeklaShared/ToSpeckle/Raw/PointToSpeckleConverter.cs +++ b/Converters/Tekla/Speckle.Converters.TeklaShared/ToSpeckle/Raw/PointToSpeckleConverter.cs @@ -1,7 +1,7 @@ using Speckle.Converters.Common; using Speckle.Converters.Common.Objects; -namespace Speckle.Converter.Tekla2024.ToSpeckle.Raw; +namespace Speckle.Converters.TeklaShared.ToSpeckle.Raw; public class TeklaPointConverter : ITypedConverter { diff --git a/Converters/Tekla/Speckle.Converters.TeklaShared/ToSpeckle/Raw/PolycurveToSpeckleConverter.cs b/Converters/Tekla/Speckle.Converters.TeklaShared/ToSpeckle/Raw/PolycurveToSpeckleConverter.cs index 33427499d..12ac3b8ec 100644 --- a/Converters/Tekla/Speckle.Converters.TeklaShared/ToSpeckle/Raw/PolycurveToSpeckleConverter.cs +++ b/Converters/Tekla/Speckle.Converters.TeklaShared/ToSpeckle/Raw/PolycurveToSpeckleConverter.cs @@ -1,7 +1,7 @@ using Speckle.Converters.Common; using Speckle.Converters.Common.Objects; -namespace Speckle.Converter.Tekla2024.ToSpeckle.Raw; +namespace Speckle.Converters.TeklaShared.ToSpeckle.Raw; public class PolycurveToSpeckleConverter : ITypedConverter { diff --git a/Converters/Tekla/Speckle.Converters.TeklaShared/ToSpeckle/Raw/SolidToSpeckleConverter.cs b/Converters/Tekla/Speckle.Converters.TeklaShared/ToSpeckle/Raw/SolidToSpeckleConverter.cs index 937f3fff8..60692cb11 100644 --- a/Converters/Tekla/Speckle.Converters.TeklaShared/ToSpeckle/Raw/SolidToSpeckleConverter.cs +++ b/Converters/Tekla/Speckle.Converters.TeklaShared/ToSpeckle/Raw/SolidToSpeckleConverter.cs @@ -1,7 +1,10 @@ +using Speckle.Common.MeshTriangulation; using Speckle.Converters.Common; using Speckle.Converters.Common.Objects; +using Speckle.DoubleNumerics; +using Tekla.Structures.Solid; -namespace Speckle.Converter.Tekla2024.ToSpeckle.Raw; +namespace Speckle.Converters.TeklaShared.ToSpeckle.Raw; public class SolidToSpeckleConverter : ITypedConverter { @@ -14,6 +17,21 @@ public class SolidToSpeckleConverter : ITypedConverter public SOG.Mesh Convert(TSM.Solid target) { + // early return extruded Mesh + // if there are exactly 2 opposite facing contours with holes (inner loops) + var facesAsPolygons = GetFacesAsPolygons(target); + var facesToExtrude = facesAsPolygons.Where(lst => lst.Count > 1).ToList(); + if (facesToExtrude.Count == 2) + { + var n0 = facesToExtrude[0][0].GetNormal(); + var n1 = facesToExtrude[1][0].GetNormal(); + + if ((n0 + n1).Length() < 0.001) + { + return ExtrudeFromPolygons(facesToExtrude[0], facesToExtrude[1]); + } + } + List vertices = new List(); List faces = new List(); int currentIndex = 0; @@ -74,4 +92,74 @@ public class SolidToSpeckleConverter : ITypedConverter units = _settingsStore.Current.SpeckleUnits }; } + + private SOG.Mesh ExtrudeFromPolygons(List face1, List face2) + { + var point = face2[0].Vertices[0]; + var generator = new MeshGenerator(new BaseTransformer(), new LibTessTriangulator()); + var mesh3 = generator.ExtrudeMesh(face1, point); + + return Mesh3ToSpeckleMesh(mesh3); + } + + private SOG.Mesh Mesh3ToSpeckleMesh(Mesh3 mesh3) + { + var vertices = new List(); + var faces = new List(); + + foreach (var v in mesh3.Vertices) + { + vertices.Add(v.X); + vertices.Add(v.Y); + vertices.Add(v.Z); + } + + for (int i = 0; i < mesh3.Triangles.Count; i += 3) + { + faces.Add(3); + faces.Add(mesh3.Triangles[i]); + faces.Add(mesh3.Triangles[i + 1]); + faces.Add(mesh3.Triangles[i + 2]); + } + + var mesh = new SOG.Mesh + { + vertices = vertices, + faces = faces, + units = _settingsStore.Current.SpeckleUnits + }; + + return mesh; + } + + private static List> GetFacesAsPolygons(TSM.Solid solid) + { + var polyFaces = new List>(); + + FaceEnumerator faceEnum = solid.GetFaceEnumerator(); + while (faceEnum.MoveNext()) + { + Face face = faceEnum.Current; + LoopEnumerator loopEnum = face.GetLoopEnumerator(); + + var polyFace = new List(); + while (loopEnum.MoveNext()) + { + Loop loop = loopEnum.Current; + VertexEnumerator vertexEnum = loop.GetVertexEnumerator(); + + var vertices = new List(); + while (vertexEnum.MoveNext()) + { + Tekla.Structures.Geometry3d.Point point = vertexEnum.Current; + vertices.Add(new Vector3(point.X, point.Y, point.Z)); + } + polyFace.Add(new Poly3(vertices)); + } + + polyFaces.Add(polyFace); + } + + return polyFaces; + } } diff --git a/Converters/Tekla/Speckle.Converters.TeklaShared/ToSpeckle/Raw/VectorToSpeckleConverter.cs b/Converters/Tekla/Speckle.Converters.TeklaShared/ToSpeckle/Raw/VectorToSpeckleConverter.cs index a0e1d0e5e..91e9a395b 100644 --- a/Converters/Tekla/Speckle.Converters.TeklaShared/ToSpeckle/Raw/VectorToSpeckleConverter.cs +++ b/Converters/Tekla/Speckle.Converters.TeklaShared/ToSpeckle/Raw/VectorToSpeckleConverter.cs @@ -1,7 +1,7 @@ using Speckle.Converters.Common; using Speckle.Converters.Common.Objects; -namespace Speckle.Converter.Tekla2024.ToSpeckle.Raw; +namespace Speckle.Converters.TeklaShared.ToSpeckle.Raw; public class VectorToSpeckleConverter : ITypedConverter { diff --git a/Converters/Tekla/Speckle.Converters.TeklaShared/ToSpeckle/TopLevel/ModelObjectToSpeckleConverter.cs b/Converters/Tekla/Speckle.Converters.TeklaShared/ToSpeckle/TopLevel/ModelObjectToSpeckleConverter.cs index c2529d860..f032d040f 100644 --- a/Converters/Tekla/Speckle.Converters.TeklaShared/ToSpeckle/TopLevel/ModelObjectToSpeckleConverter.cs +++ b/Converters/Tekla/Speckle.Converters.TeklaShared/ToSpeckle/TopLevel/ModelObjectToSpeckleConverter.cs @@ -1,104 +1,77 @@ -using Speckle.Converter.Tekla2024.Extensions; -using Speckle.Converter.Tekla2024.ToSpeckle.Helpers; using Speckle.Converters.Common; using Speckle.Converters.Common.Objects; +using Speckle.Converters.TeklaShared.Extensions; +using Speckle.Converters.TeklaShared.ToSpeckle.Helpers; +using Speckle.Objects.Data; using Speckle.Sdk.Models; -namespace Speckle.Converter.Tekla2024.ToSpeckle.TopLevel; +namespace Speckle.Converters.TeklaShared.ToSpeckle.TopLevel; -[NameAndRankValue(nameof(TSM.ModelObject), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +[NameAndRankValue(typeof(TSM.ModelObject), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] public class ModelObjectToSpeckleConverter : IToSpeckleTopLevelConverter { private readonly IConverterSettingsStore _settingsStore; private readonly DisplayValueExtractor _displayValueExtractor; - private readonly ClassPropertyExtractor _propertyExtractor; - private readonly ReportPropertyExtractor _reportPropertyExtractor; + private readonly PropertiesExtractor _propertiesExtractor; + private readonly ClassPropertyExtractor _classPropertyExtractor; public ModelObjectToSpeckleConverter( IConverterSettingsStore settingsStore, DisplayValueExtractor displayValueExtractor, - ClassPropertyExtractor propertyExtractor, - ReportPropertyExtractor reportPropertyExtractor + PropertiesExtractor propertiesExtractor, + ClassPropertyExtractor classPropertyExtractor ) { _settingsStore = settingsStore; _displayValueExtractor = displayValueExtractor; - _propertyExtractor = propertyExtractor; - _reportPropertyExtractor = reportPropertyExtractor; + _propertiesExtractor = propertiesExtractor; + _classPropertyExtractor = classPropertyExtractor; } - public Base Convert(object target) + public Base Convert(object target) => Convert((TSM.ModelObject)target); + + private TeklaObject Convert(TSM.ModelObject target) { - if (target is not TSM.ModelObject modelObject) - { - throw new ArgumentException($"Target object is not a ModelObject. It's a {target.GetType()}"); - } - - var result = new Base - { - ["type"] = modelObject.GetType().ToString().Split('.').Last(), - ["units"] = _settingsStore.Current.SpeckleUnits, - }; - - // get properties - var properties = _propertyExtractor.GetProperties(modelObject); - foreach (var prop in properties) - { - result[prop.Key] = prop.Value; - } - - // get report properties - var reportProperties = GetObjectReportProperties(modelObject); - if (reportProperties.Count > 0) - { - result["properties"] = reportProperties; - } - - // get display value - var displayValue = _displayValueExtractor.GetDisplayValue(modelObject).ToList(); - if (displayValue.Count > 0) - { - foreach (var displayValueObject in displayValue) - { - // NOTE: since we put ModelObject applicationIds into proxies, we should also mutate the same application id for its displayValue objects. - // otherwise we will have anonymous objects on receive for other host apps - displayValueObject.applicationId = modelObject.GetSpeckleApplicationId(); - } - result["displayValue"] = displayValue; - } - - // get report properties - Dictionary GetObjectReportProperties(TSM.ModelObject modelObject) - { - Dictionary properties = new(); - - // get report properties - var reportProperties = _reportPropertyExtractor.GetReportProperties(modelObject); - if (reportProperties.Count > 0) - { - properties["report"] = reportProperties; - } - - // POC: might add user defined properties here - - return properties; - } + string type = target.GetType().ToString().Split('.').Last(); // get children // POC: This logic should be same in the material unpacker in connector - List children = new(); - foreach (TSM.ModelObject childObject in modelObject.GetSupportedChildren()) + List children = new(); + foreach (TSM.ModelObject childObject in target.GetSupportedChildren()) { var child = Convert(childObject); child.applicationId = childObject.GetSpeckleApplicationId(); children.Add(child); } - if (children.Count > 0) + // get display value + IEnumerable displayValue = _displayValueExtractor.GetDisplayValue(target).ToList(); + + // get name + string name = type; + switch (target) { - result["elements"] = children; + case TSM.Part part: + name = part.Name; + break; + case TSM.Reinforcement reinforcement: + name = reinforcement.Name; + break; } + // get properties + var properties = _propertiesExtractor.GetProperties(target); + + var result = new TeklaObject() + { + name = name, + type = type, + elements = children, + properties = properties, + displayValue = displayValue.ToList(), + units = _settingsStore.Current.SpeckleUnits + }; + return result; } } diff --git a/DUI3/Speckle.Connectors.DUI.Tests/Bridge/IdleCallManagerTests.cs b/DUI3/Speckle.Connectors.DUI.Tests/Bridge/IdleCallManagerTests.cs index ecbb6ebaa..047ce27ea 100644 --- a/DUI3/Speckle.Connectors.DUI.Tests/Bridge/IdleCallManagerTests.cs +++ b/DUI3/Speckle.Connectors.DUI.Tests/Bridge/IdleCallManagerTests.cs @@ -15,12 +15,12 @@ public class IdleCallManagerTests : MoqTest var sut = new IdleCallManager(handler.Object); var action = Create(); var addEvent = Create(); - handler.Setup(x => x.CatchUnhandled(It.IsAny())); + handler.Setup(x => x.CatchUnhandled(It.IsAny())).Returns(new Result()); sut.SubscribeToIdle("id", action.Object, addEvent.Object); } [Test] - public void SubscribeInternalTest() + public void SubscribeToIdleTest_2() { var handler = Create(); var sut = new IdleCallManager(handler.Object); @@ -28,8 +28,11 @@ public class IdleCallManagerTests : MoqTest var addEvent = Create(); addEvent.Setup(x => x.Invoke()); + //add + handler.Setup(m => m.CatchUnhandled(It.IsAny())).Callback(a => a.Invoke()).Returns(new Result()); + sut.IdleSubscriptionCalled.Should().BeFalse(); - sut.SubscribeInternal("id", action.Object, addEvent.Object); + sut.SubscribeToIdle("id", action.Object, addEvent.Object); sut.Calls.Count.Should().Be(1); sut.Calls.Should().ContainKey("id"); sut.IdleSubscriptionCalled.Should().BeTrue(); @@ -53,15 +56,18 @@ public class IdleCallManagerTests : MoqTest var expectedAction = Create>(); expectedAction.Setup(x => x.Invoke()).Returns(Task.CompletedTask); + //add + handler.Setup(m => m.CatchUnhandled(It.IsAny())).Returns(new Result()); + //idle handler .Setup(m => m.CatchUnhandledAsync(It.IsAny>())) .Callback>(a => a.Invoke()) - .Returns(Task.CompletedTask); + .ReturnsAsync(new Result()); var removeEvent = Create(); removeEvent.Setup(x => x.Invoke()); - sut.SubscribeInternal("Test", expectedAction.Object, () => { }); + sut.SubscribeToIdle("Test", expectedAction.Object, () => { }); sut.IdleSubscriptionCalled.Should().BeTrue(); sut.Calls.Count.Should().Be(1); diff --git a/DUI3/Speckle.Connectors.DUI.Tests/Bridge/TopLevelExceptionHandlerTests.cs b/DUI3/Speckle.Connectors.DUI.Tests/Bridge/TopLevelExceptionHandlerTests.cs deleted file mode 100644 index 8ba16dc14..000000000 --- a/DUI3/Speckle.Connectors.DUI.Tests/Bridge/TopLevelExceptionHandlerTests.cs +++ /dev/null @@ -1,127 +0,0 @@ -using FluentAssertions; -using Microsoft.Extensions.Logging; -using Moq; -using NUnit.Framework; -using Speckle.Connectors.DUI.Bindings; -using Speckle.Connectors.DUI.Bridge; -using Speckle.Testing; - -namespace Speckle.Connectors.DUI.Tests.Bridge; - -public class TopLevelExceptionHandlerTests : MoqTest -{ - [Test] - public void CatchUnhandledAction_Happy() - { - var logger = Create>(MockBehavior.Loose); - var bridge = Create(); - var sut = new TopLevelExceptionHandler(logger.Object, bridge.Object); - - sut.CatchUnhandled(() => { }); - } - - [Test] - public void CatchUnhandledAction_Exception() - { - var logger = Create>(MockBehavior.Loose); - var bridge = Create(); - - bridge - .Setup(x => x.Send(BasicConnectorBindingCommands.SET_GLOBAL_NOTIFICATION, It.IsAny(), default)) - .Returns(Task.CompletedTask); - - var sut = new TopLevelExceptionHandler(logger.Object, bridge.Object); - - sut.CatchUnhandled(() => throw new InvalidOperationException()); - } - - [Test] - public void CatchUnhandledFunc_Happy() - { - var val = 2; - var logger = Create>(MockBehavior.Loose); - var bridge = Create(); - var sut = new TopLevelExceptionHandler(logger.Object, bridge.Object); - - var returnVal = sut.CatchUnhandled(() => val); - returnVal.Value.Should().Be(val); - returnVal.Exception.Should().BeNull(); - returnVal.IsSuccess.Should().BeTrue(); - } - - [Test] - public void CatchUnhandledFunc_Exception() - { - var logger = Create>(MockBehavior.Loose); - var bridge = Create(); - - bridge - .Setup(x => x.Send(BasicConnectorBindingCommands.SET_GLOBAL_NOTIFICATION, It.IsAny(), default)) - .Returns(Task.CompletedTask); - - var sut = new TopLevelExceptionHandler(logger.Object, bridge.Object); - - var returnVal = sut.CatchUnhandled((Func)(() => throw new InvalidOperationException())); - returnVal.Value.Should().BeNull(); - returnVal.Exception.Should().BeOfType(); - returnVal.IsSuccess.Should().BeFalse(); - } - - [Test] - public void CatchUnhandledFunc_Exception_Fatal() - { - var logger = Create>(MockBehavior.Loose); - var bridge = Create(); - var sut = new TopLevelExceptionHandler(logger.Object, bridge.Object); - - var exception = Assert.Throws( - () => sut.CatchUnhandled(new Func(() => throw new AppDomainUnloadedException())) - ); - exception.InnerExceptions.Single().Should().BeOfType(); - } - - [Test] - public async Task CatchUnhandledFuncAsync_Happy() - { - var val = 2; - var logger = Create>(MockBehavior.Loose); - var bridge = Create(); - var sut = new TopLevelExceptionHandler(logger.Object, bridge.Object); - - var returnVal = await sut.CatchUnhandledAsync(() => Task.FromResult(val)); - returnVal.Value.Should().Be(val); - returnVal.Exception.Should().BeNull(); - returnVal.IsSuccess.Should().BeTrue(); - } - - [Test] - public async Task CatchUnhandledFuncAsync_Exception() - { - var logger = Create>(MockBehavior.Loose); - var bridge = Create(); - - bridge - .Setup(x => x.Send(BasicConnectorBindingCommands.SET_GLOBAL_NOTIFICATION, It.IsAny(), default)) - .Returns(Task.CompletedTask); - - var sut = new TopLevelExceptionHandler(logger.Object, bridge.Object); - - var returnVal = await sut.CatchUnhandledAsync(new Func>(() => throw new InvalidOperationException())); - returnVal.Value.Should().BeNull(); - returnVal.Exception.Should().BeOfType(); - returnVal.IsSuccess.Should().BeFalse(); - } - - [Test] - public void CatchUnhandledFuncAsync_Exception_Fatal() - { - var logger = Create>(MockBehavior.Loose); - var bridge = Create(); - var sut = new TopLevelExceptionHandler(logger.Object, bridge.Object); - - var exception = Assert.ThrowsAsync( - async () => await sut.CatchUnhandledAsync(new Func>(() => throw new AppDomainUnloadedException())) - ); - exception.Should().BeOfType(); - } -} diff --git a/DUI3/Speckle.Connectors.DUI.Tests/Speckle.Connectors.DUI.Tests.csproj b/DUI3/Speckle.Connectors.DUI.Tests/Speckle.Connectors.DUI.Tests.csproj index 0b607cbe3..b5bbadceb 100644 --- a/DUI3/Speckle.Connectors.DUI.Tests/Speckle.Connectors.DUI.Tests.csproj +++ b/DUI3/Speckle.Connectors.DUI.Tests/Speckle.Connectors.DUI.Tests.csproj @@ -5,12 +5,6 @@ true Debug;Release;Local System.Runtime.CompilerServices.IsExternalInit;System.Runtime.CompilerServices.RequiresLocationAttribute - - - - CA2007; - $(NoWarn) - diff --git a/DUI3/Speckle.Connectors.DUI.Tests/packages.lock.json b/DUI3/Speckle.Connectors.DUI.Tests/packages.lock.json index 041c6a1ec..2bb0b70db 100644 --- a/DUI3/Speckle.Connectors.DUI.Tests/packages.lock.json +++ b/DUI3/Speckle.Connectors.DUI.Tests/packages.lock.json @@ -231,11 +231,6 @@ "resolved": "13.0.1", "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" }, - "Speckle.DoubleNumerics": { - "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w==" - }, "Speckle.Newtonsoft.Json": { "type": "Transitive", "resolved": "13.0.2", @@ -319,9 +314,9 @@ "dependencies": { "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", "Speckle.Connectors.Logging": "[1.0.0, )", - "Speckle.Objects": "[3.1.0-dev.191, )", - "Speckle.Sdk": "[3.1.0-dev.191, )", - "Speckle.Sdk.Dependencies": "[3.1.0-dev.191, )" + "Speckle.Objects": "[3.1.1, )", + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" } }, "speckle.connectors.dui": { @@ -329,9 +324,8 @@ "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", "Speckle.Connectors.Common": "[1.0.0, )", - "Speckle.Sdk": "[3.1.0-dev.191, )", - "Speckle.Sdk.Dependencies": "[3.1.0-dev.191, )", - "System.Threading.Tasks.Dataflow": "[6.0.0, )" + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" } }, "speckle.connectors.logging": { @@ -377,42 +371,42 @@ "resolved": "2.2.0", "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "+m7jRFm0ABbkcSz2UphdxAsislY10IpQ1u79c8a3aVvegLjnsVQZ1sXfRIRO1aFdulkhjYKXYpB3N9M8Z+epgQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.191" + "Speckle.Sdk": "3.1.1" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "VVT3LJiYlhqnggxxdeTt1QLrqfxDb044x0yX6kxS9b5MRzeDvK2Vz86pLDfuHs+SXvDimRVfYx1M42IW/aPcTQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.CSharp": "4.7.0", "Microsoft.Data.Sqlite": "7.0.5", "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", "Microsoft.Extensions.Logging": "2.2.0", - "Speckle.DoubleNumerics": "4.0.1", + "Speckle.DoubleNumerics": "4.1.0", "Speckle.Newtonsoft.Json": "13.0.2", - "Speckle.Sdk.Dependencies": "3.1.0-dev.191" + "Speckle.Sdk.Dependencies": "3.1.1" } }, "Speckle.Sdk.Dependencies": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "EmEOyjsGsNi56Z/ZoBOn8WirTmIT2yqWvlUeUh0BSPX2TDMZXHTKOM/kHmP6HSd10KVFn2Zo/ItY7/K9iRtL1Q==" - }, - "System.Threading.Tasks.Dataflow": { - "type": "CentralTransitive", - "requested": "[6.0.0, )", - "resolved": "6.0.0", - "contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA==" + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" } } } diff --git a/DUI3/Speckle.Connectors.DUI.WebView/DUI3ControlWebView.xaml.cs b/DUI3/Speckle.Connectors.DUI.WebView/DUI3ControlWebView.xaml.cs index 56ff2b85d..59307e4bb 100644 --- a/DUI3/Speckle.Connectors.DUI.WebView/DUI3ControlWebView.xaml.cs +++ b/DUI3/Speckle.Connectors.DUI.WebView/DUI3ControlWebView.xaml.cs @@ -26,57 +26,19 @@ public sealed partial class DUI3ControlWebView : UserControl, IBrowserScriptExec public object BrowserElement => Browser; - // { - // if (!Browser.IsInitialized) - // { - // throw new InvalidOperationException("Failed to execute script, Webview2 is not initialized yet."); - // } - // - // var t = Browser.Dispatcher.Invoke( - // async () => - // { - // var res = await Browser.ExecuteScriptAsync(script).ConfigureAwait(true); - // await Task.Delay(100).ConfigureAwait(true); - // return res; - // }, - // DispatcherPriority.Background - // ); - // - // _ = t.IsCompleted; - - // bool isAlreadyMainThread = Browser.Dispatcher.CheckAccess(); - // if (isAlreadyMainThread) - // { - // Browser.ExecuteScriptAsync(script); - // } - // else - // { - // Browser.Dispatcher.Invoke( - // () => - // { - // return Browser.ExecuteScriptAsync(script); - // }, - // DispatcherPriority.Background - // ); - // } - // } - - public async Task ExecuteScriptAsyncMethod(string script, CancellationToken cancellationToken) + public void ExecuteScript(string script) { if (!Browser.IsInitialized) { throw new InvalidOperationException("Failed to execute script, Webview2 is not initialized yet."); } - var callbackTask = await Browser - .Dispatcher.InvokeAsync( - async () => await Browser.ExecuteScriptAsync(script).ConfigureAwait(false), - DispatcherPriority.Background, - cancellationToken - ) - .Task.ConfigureAwait(false); - - _ = await callbackTask.ConfigureAwait(false); + //always invoke even on the main thread because it's better somehow + Browser.Dispatcher.Invoke( + //fire and forget + () => Browser.ExecuteScriptAsync(script), + DispatcherPriority.Background + ); } private void OnInitialized(object? sender, CoreWebView2InitializationCompletedEventArgs e) diff --git a/DUI3/Speckle.Connectors.DUI.WebView/Speckle.Connectors.DUI.WebView.csproj b/DUI3/Speckle.Connectors.DUI.WebView/Speckle.Connectors.DUI.WebView.csproj index fe8a9647e..6c6aa310d 100644 --- a/DUI3/Speckle.Connectors.DUI.WebView/Speckle.Connectors.DUI.WebView.csproj +++ b/DUI3/Speckle.Connectors.DUI.WebView/Speckle.Connectors.DUI.WebView.csproj @@ -1,7 +1,10 @@ - net48;net6.0-windows + net48;net6.0-windows;net8.0-windows + + + x64 true true Debug;Release;Local diff --git a/DUI3/Speckle.Connectors.DUI.WebView/packages.lock.json b/DUI3/Speckle.Connectors.DUI.WebView/packages.lock.json index 29ad831f0..100d87ef3 100644 --- a/DUI3/Speckle.Connectors.DUI.WebView/packages.lock.json +++ b/DUI3/Speckle.Connectors.DUI.WebView/packages.lock.json @@ -159,11 +159,6 @@ "resolved": "8.0.0", "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" }, - "Speckle.DoubleNumerics": { - "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w==" - }, "Speckle.Newtonsoft.Json": { "type": "Transitive", "resolved": "13.0.2", @@ -259,9 +254,9 @@ "dependencies": { "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", "Speckle.Connectors.Logging": "[1.0.0, )", - "Speckle.Objects": "[3.1.0-dev.191, )", - "Speckle.Sdk": "[3.1.0-dev.191, )", - "Speckle.Sdk.Dependencies": "[3.1.0-dev.191, )" + "Speckle.Objects": "[3.1.1, )", + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" } }, "speckle.connectors.dui": { @@ -269,9 +264,8 @@ "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", "Speckle.Connectors.Common": "[1.0.0, )", - "Speckle.Sdk": "[3.1.0-dev.191, )", - "Speckle.Sdk.Dependencies": "[3.1.0-dev.191, )", - "System.Threading.Tasks.Dataflow": "[6.0.0, )" + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" } }, "speckle.connectors.logging": { @@ -310,20 +304,26 @@ "resolved": "2.2.0", "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "+m7jRFm0ABbkcSz2UphdxAsislY10IpQ1u79c8a3aVvegLjnsVQZ1sXfRIRO1aFdulkhjYKXYpB3N9M8Z+epgQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.191" + "Speckle.Sdk": "3.1.1" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "VVT3LJiYlhqnggxxdeTt1QLrqfxDb044x0yX6kxS9b5MRzeDvK2Vz86pLDfuHs+SXvDimRVfYx1M42IW/aPcTQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.Bcl.AsyncInterfaces": "5.0.0", @@ -331,22 +331,16 @@ "Microsoft.Data.Sqlite": "7.0.5", "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", "Microsoft.Extensions.Logging": "2.2.0", - "Speckle.DoubleNumerics": "4.0.1", + "Speckle.DoubleNumerics": "4.1.0", "Speckle.Newtonsoft.Json": "13.0.2", - "Speckle.Sdk.Dependencies": "3.1.0-dev.191" + "Speckle.Sdk.Dependencies": "3.1.1" } }, "Speckle.Sdk.Dependencies": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "EmEOyjsGsNi56Z/ZoBOn8WirTmIT2yqWvlUeUh0BSPX2TDMZXHTKOM/kHmP6HSd10KVFn2Zo/ItY7/K9iRtL1Q==" - }, - "System.Threading.Tasks.Dataflow": { - "type": "CentralTransitive", - "requested": "[6.0.0, )", - "resolved": "6.0.0", - "contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA==" + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" } }, "net6.0-windows7.0": { @@ -503,10 +497,302 @@ "resolved": "8.0.0", "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" }, - "Speckle.DoubleNumerics": { + "Speckle.Newtonsoft.Json": { "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w==" + "resolved": "13.0.2", + "contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA==" + }, + "SQLitePCLRaw.bundle_e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "EWI1olKDjFEBMJu0+3wuxwziIAdWDVMYLhuZ3Qs84rrz+DHwD00RzWPZCa+bLnHCf3oJwuFZIRsHT5p236QXww==", + "dependencies": { + "SQLitePCLRaw.lib.e_sqlite3": "2.1.4", + "SQLitePCLRaw.provider.e_sqlite3": "2.1.4" + } + }, + "SQLitePCLRaw.core": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "inBjvSHo9UDKneGNzfUfDjK08JzlcIhn1+SP5Y3m6cgXpCxXKCJDy6Mka7LpgSV+UZmKSnC8rTwB0SQ0xKu5pA==", + "dependencies": { + "System.Memory": "4.5.3" + } + }, + "SQLitePCLRaw.lib.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "2C9Q9eX7CPLveJA0rIhf9RXAvu+7nWZu1A2MdG6SD/NOu26TakGgL1nsbc0JAspGijFOo3HoN79xrx8a368fBg==" + }, + "SQLitePCLRaw.provider.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "CSlb5dUp1FMIkez9Iv5EXzpeq7rHryVNqwJMWnpq87j9zWZexaEMdisDktMsnnrzKM6ahNrsTkjqNodTBPBxtQ==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "System.ComponentModel.Annotations": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "UxYQ3FGUOtzJ7LfSdnYSFd7+oEv6M8NgUatatIN2HxNtDdlcvFAf+VIq4Of9cDMJEJC0aSRv/x898RYhB4Yppg==" + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.3", + "contentHash": "3oDzvc/zzetpTKWMShs1AADwZjQ/36HnsufHRPcOjyRAAMLDlu2iD33MBI2opxnezcVUtXyqDXXjoFMOU9c7SA==" + }, + "System.Reactive": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==" + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Zh8t8oqolRaFa9vmOZfdQm/qKejdqz0J9kr7o2Fu0vPeoH3BL1EOXipKWwkWtLT1JPzjByrF19fGuFlNbmPpiw==" + }, + "speckle.connectors.common": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", + "Speckle.Connectors.Logging": "[1.0.0, )", + "Speckle.Objects": "[3.1.1, )", + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" + } + }, + "speckle.connectors.dui": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", + "Speckle.Connectors.Common": "[1.0.0, )", + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" + } + }, + "speckle.connectors.logging": { + "type": "Project" + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "MZtBIwfDFork5vfjpJdG5g8wuJFt7d/y3LOSVVtDK/76wlbtz6cjltfKHqLx2TKVqTj5/c41t77m1+h20zqtPA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0" + } + }, + "Microsoft.Extensions.Logging": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "Nxqhadc9FCmFHzU+fz3oc8sFlE6IadViYg8dfUdGzJZ2JUxnCsRghBhhOWdM4B2zSZqEc+0BjliBh/oNdRZuig==", + "dependencies": { + "Microsoft.Extensions.Configuration.Binder": "2.2.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging.Abstractions": "2.2.0", + "Microsoft.Extensions.Options": "2.2.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" + }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, + "Speckle.Objects": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", + "dependencies": { + "Speckle.Sdk": "3.1.1" + } + }, + "Speckle.Sdk": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", + "dependencies": { + "GraphQL.Client": "6.0.0", + "Microsoft.Bcl.AsyncInterfaces": "5.0.0", + "Microsoft.CSharp": "4.7.0", + "Microsoft.Data.Sqlite": "7.0.5", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging": "2.2.0", + "Speckle.DoubleNumerics": "4.1.0", + "Speckle.Newtonsoft.Json": "13.0.2", + "Speckle.Sdk.Dependencies": "3.1.1" + } + }, + "Speckle.Sdk.Dependencies": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" + } + }, + "net8.0-windows7.0": { + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net461": "1.0.3" + } + }, + "Microsoft.SourceLink.GitHub": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "dependencies": { + "Microsoft.Build.Tasks.Git": "8.0.0", + "Microsoft.SourceLink.Common": "8.0.0" + } + }, + "Microsoft.Web.WebView2": { + "type": "Direct", + "requested": "[1.0.1938.49, )", + "resolved": "1.0.1938.49", + "contentHash": "z8KnFnaTYzhA/ZnyRX0qGfS1NU5ZBJeClAH64F0fVDvdDJTvME7xl6zTJ0Jlfe1BtL3C0NH9xTy64shg2baKdw==" + }, + "PolySharp": { + "type": "Direct", + "requested": "[1.14.1, )", + "resolved": "1.14.1", + "contentHash": "mOOmFYwad3MIOL14VCjj02LljyF1GNw1wP0YVlxtcPvqdxjGGMNdNJJxHptlry3MOd8b40Flm8RPOM8JOlN2sQ==" + }, + "Speckle.InterfaceGenerator": { + "type": "Direct", + "requested": "[0.9.6, )", + "resolved": "0.9.6", + "contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w==" + }, + "GraphQL.Client": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "8yPNBbuVBpTptivyAlak4GZvbwbUcjeQTL4vN1HKHRuOykZ4r7l5fcLS6vpyPyLn0x8FsL31xbOIKyxbmR9rbA==", + "dependencies": { + "GraphQL.Client.Abstractions": "6.0.0", + "GraphQL.Client.Abstractions.Websocket": "6.0.0", + "System.Reactive": "5.0.0" + } + }, + "GraphQL.Client.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "h7uzWFORHZ+CCjwr/ThAyXMr0DPpzEANDa4Uo54wqCQ+j7qUKwqYTgOrb1W40sqbvNaZm9v/X7It31SUw0maHA==", + "dependencies": { + "GraphQL.Primitives": "6.0.0" + } + }, + "GraphQL.Client.Abstractions.Websocket": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "Nr9bPf8gIOvLuXpqEpqr9z9jslYFJOvd0feHth3/kPqeR3uMbjF5pjiwh4jxyMcxHdr8Pb6QiXkV3hsSyt0v7A==", + "dependencies": { + "GraphQL.Client.Abstractions": "6.0.0" + } + }, + "GraphQL.Primitives": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "yg72rrYDapfsIUrul7aF6wwNnTJBOFvuA9VdDTQpPa8AlAriHbufeXYLBcodKjfUdkCnaiggX1U/nEP08Zb5GA==" + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.CSharp": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==" + }, + "Microsoft.Data.Sqlite": { + "type": "Transitive", + "resolved": "7.0.5", + "contentHash": "KGxbPeWsQMnmQy43DSBxAFtHz3l2JX8EWBSGUCvT3CuZ8KsuzbkqMIJMDOxWtG8eZSoCDI04aiVQjWuuV8HmSw==", + "dependencies": { + "Microsoft.Data.Sqlite.Core": "7.0.5", + "SQLitePCLRaw.bundle_e_sqlite3": "2.1.4" + } + }, + "Microsoft.Data.Sqlite.Core": { + "type": "Transitive", + "resolved": "7.0.5", + "contentHash": "FTerRmQPqHrCrnoUzhBu+E+1DNGwyrAMLqHkAqOOOu5pGfyMOj8qQUBxI/gDtWtG11p49UxSfWmBzRNlwZqfUg==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "nOP8R1mVb/6mZtm2qgAJXn/LFm/2kMjHDAg/QJLFG6CuWYJtaD3p1BwQhufBVvRzL9ceJ/xF0SQ0qsI2GkDQAA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "65MrmXCziWaQFrI0UHkQbesrX5wTwf9XPjY5yFm/VkgJKFJ5gqvXRoXjIZcf2wLi5ZlwGz/oMYfyURVCWbM5iw==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "vJ9xvOZCnUAIHcGC3SU35r3HKmHTVIeHzo6u/qzlHAqD8m6xv92MLin4oJntTvkpKxVX3vI1GFFkIQtU3AdlsQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "2.2.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw==" + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "UpZLNLBpIZ0GTebShui7xXYh6DmBHjWM8NxGxZbdQh/bPZ5e6YswqI+bru6BnEL5eWiOdodsXtEz3FROcgi/qg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Primitives": "2.2.0", + "System.ComponentModel.Annotations": "4.5.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "azyQtqbm4fSaDzZHD/J+V6oWMFaf2tWP4WEGIYePLCMw3+b2RQdj9ybgbQyjCshcitQKQ4lEDOZjmSlTTrHxUg==", + "dependencies": { + "System.Memory": "4.5.1", + "System.Runtime.CompilerServices.Unsafe": "4.5.1" + } + }, + "Microsoft.NETFramework.ReferenceAssemblies.net461": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "AmOJZwCqnOCNp6PPcf9joyogScWLtwy0M1WkqfEQ0M9nYwyDD7EX9ZjscKS5iYnyvteX7kzSKFCKt9I9dXA6mA==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" }, "Speckle.Newtonsoft.Json": { "type": "Transitive", @@ -568,9 +854,9 @@ "dependencies": { "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", "Speckle.Connectors.Logging": "[1.0.0, )", - "Speckle.Objects": "[3.1.0-dev.191, )", - "Speckle.Sdk": "[3.1.0-dev.191, )", - "Speckle.Sdk.Dependencies": "[3.1.0-dev.191, )" + "Speckle.Objects": "[3.1.1, )", + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" } }, "speckle.connectors.dui": { @@ -578,9 +864,8 @@ "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", "Speckle.Connectors.Common": "[1.0.0, )", - "Speckle.Sdk": "[3.1.0-dev.191, )", - "Speckle.Sdk.Dependencies": "[3.1.0-dev.191, )", - "System.Threading.Tasks.Dataflow": "[6.0.0, )" + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" } }, "speckle.connectors.logging": { @@ -619,43 +904,42 @@ "resolved": "2.2.0", "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "+m7jRFm0ABbkcSz2UphdxAsislY10IpQ1u79c8a3aVvegLjnsVQZ1sXfRIRO1aFdulkhjYKXYpB3N9M8Z+epgQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.191" + "Speckle.Sdk": "3.1.1" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "VVT3LJiYlhqnggxxdeTt1QLrqfxDb044x0yX6kxS9b5MRzeDvK2Vz86pLDfuHs+SXvDimRVfYx1M42IW/aPcTQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", "dependencies": { "GraphQL.Client": "6.0.0", - "Microsoft.Bcl.AsyncInterfaces": "5.0.0", "Microsoft.CSharp": "4.7.0", "Microsoft.Data.Sqlite": "7.0.5", "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", "Microsoft.Extensions.Logging": "2.2.0", - "Speckle.DoubleNumerics": "4.0.1", + "Speckle.DoubleNumerics": "4.1.0", "Speckle.Newtonsoft.Json": "13.0.2", - "Speckle.Sdk.Dependencies": "3.1.0-dev.191" + "Speckle.Sdk.Dependencies": "3.1.1" } }, "Speckle.Sdk.Dependencies": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "EmEOyjsGsNi56Z/ZoBOn8WirTmIT2yqWvlUeUh0BSPX2TDMZXHTKOM/kHmP6HSd10KVFn2Zo/ItY7/K9iRtL1Q==" - }, - "System.Threading.Tasks.Dataflow": { - "type": "CentralTransitive", - "requested": "[6.0.0, )", - "resolved": "6.0.0", - "contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA==" + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" } } } diff --git a/DUI3/Speckle.Connectors.DUI/Bindings/ConfigBinding.cs b/DUI3/Speckle.Connectors.DUI/Bindings/ConfigBinding.cs index 2e5c5cad2..9a537b61d 100644 --- a/DUI3/Speckle.Connectors.DUI/Bindings/ConfigBinding.cs +++ b/DUI3/Speckle.Connectors.DUI/Bindings/ConfigBinding.cs @@ -2,7 +2,7 @@ using System.Runtime.Serialization; using Speckle.Connectors.DUI.Bridge; using Speckle.Connectors.DUI.Utils; using Speckle.Sdk; -using Speckle.Sdk.Transports; +using Speckle.Sdk.SQLite; namespace Speckle.Connectors.DUI.Bindings; @@ -17,14 +17,19 @@ public class ConfigBinding : IBinding { public string Name => "configBinding"; public IBrowserBridge Parent { get; } - private SQLiteTransport ConfigStorage { get; } + private readonly ISqLiteJsonCacheManager _jsonCacheManager; private readonly ISpeckleApplication _speckleApplication; private readonly IJsonSerializer _serializer; - public ConfigBinding(IJsonSerializer serializer, ISpeckleApplication speckleApplication, IBrowserBridge bridge) + public ConfigBinding( + IJsonSerializer serializer, + ISpeckleApplication speckleApplication, + IBrowserBridge bridge, + ISqLiteJsonCacheManagerFactory sqLiteJsonCacheManagerFactory + ) { Parent = bridge; - ConfigStorage = new SQLiteTransport(scope: "DUI3Config"); // POC: maybe inject? (if we ever want to use a different storage for configs later down the line) + _jsonCacheManager = sqLiteJsonCacheManagerFactory.CreateForUser("DUI3Config"); // POC: maybe inject? (if we ever want to use a different storage for configs later down the line) _speckleApplication = speckleApplication; _serializer = serializer; } @@ -40,9 +45,9 @@ public class ConfigBinding : IBinding #endif } - public async Task GetConfig() + public ConnectorConfig GetConfig() { - var rawConfig = await ConfigStorage.GetObject(_speckleApplication.HostApplication).ConfigureAwait(false); + var rawConfig = _jsonCacheManager.GetObject(_speckleApplication.HostApplication); if (rawConfig is null) { return SeedConfig(); @@ -74,18 +79,18 @@ public class ConfigBinding : IBinding public void UpdateConfig(ConnectorConfig config) { var str = _serializer.Serialize(config); - ConfigStorage.UpdateObject(_speckleApplication.HostApplication, str); + _jsonCacheManager.UpdateObject(_speckleApplication.HostApplication, str); } public void SetUserSelectedAccountId(string userSelectedAccountId) { var str = _serializer.Serialize(new AccountsConfig() { UserSelectedAccountId = userSelectedAccountId }); - ConfigStorage.UpdateObject("accounts", str); + _jsonCacheManager.UpdateObject("accounts", str); } - public async Task GetUserSelectedAccountId() + public AccountsConfig? GetUserSelectedAccountId() { - var rawConfig = await ConfigStorage.GetObject("accounts").ConfigureAwait(false); + var rawConfig = _jsonCacheManager.GetObject("accounts"); if (rawConfig is null) { return null; diff --git a/DUI3/Speckle.Connectors.DUI/Bindings/IBasicConnectorBinding.cs b/DUI3/Speckle.Connectors.DUI/Bindings/IBasicConnectorBinding.cs index 2677926d4..28a229d6d 100644 --- a/DUI3/Speckle.Connectors.DUI/Bindings/IBasicConnectorBinding.cs +++ b/DUI3/Speckle.Connectors.DUI/Bindings/IBasicConnectorBinding.cs @@ -53,8 +53,7 @@ public class BasicConnectorBindingCommands Bridge = bridge; } - public async Task NotifyDocumentChanged() => - await Bridge.Send(NOTIFY_DOCUMENT_CHANGED_EVENT_NAME).ConfigureAwait(false); + public async Task NotifyDocumentChanged() => await Bridge.Send(NOTIFY_DOCUMENT_CHANGED_EVENT_NAME); /// /// Use it whenever you want to send global toast notification to UI. @@ -69,21 +68,17 @@ public class BasicConnectorBindingCommands string message, bool autoClose = true ) => - await Bridge - .Send( - SET_GLOBAL_NOTIFICATION, - new - { - type, - title, - description = message, - autoClose - } - ) - .ConfigureAwait(false); + await Bridge.Send( + SET_GLOBAL_NOTIFICATION, + new + { + type, + title, + description = message, + autoClose + } + ); public async Task SetModelError(string modelCardId, Exception error) => - await Bridge - .Send(SET_MODEL_ERROR_UI_COMMAND_NAME, new { modelCardId, error = error.Message }) - .ConfigureAwait(false); + await Bridge.Send(SET_MODEL_ERROR_UI_COMMAND_NAME, new { modelCardId, error = error.Message }); } diff --git a/DUI3/Speckle.Connectors.DUI/Bindings/OperationProgressManager.cs b/DUI3/Speckle.Connectors.DUI/Bindings/OperationProgressManager.cs index 992aaf669..c3c621a81 100644 --- a/DUI3/Speckle.Connectors.DUI/Bindings/OperationProgressManager.cs +++ b/DUI3/Speckle.Connectors.DUI/Bindings/OperationProgressManager.cs @@ -1,4 +1,4 @@ -using System.Collections.Concurrent; +using System.Collections.Concurrent; using Speckle.Connectors.Common.Operations; using Speckle.Connectors.DUI.Bridge; using Speckle.Connectors.DUI.Models.Card; @@ -30,20 +30,19 @@ public class OperationProgressManager : IOperationProgressManager ) { var progress = new NonUIThreadProgress(args => - bridge.TopLevelExceptionHandler.FireAndForget( - () => - SetModelProgress( - bridge, - modelCardId, - new ModelCardProgress(modelCardId, args.Status, args.Progress), - cancellationToken - ) - ) - ); + { + Console.WriteLine($"Progress: {args.Status} - {args.Progress}"); + SetModelProgress( + bridge, + modelCardId, + new ModelCardProgress(modelCardId, args.Status, args.Progress), + cancellationToken + ); + }); return progress; } - public async Task SetModelProgress( + public void SetModelProgress( IBrowserBridge bridge, string modelCardId, ModelCardProgress progress, @@ -60,7 +59,7 @@ public class OperationProgressManager : IOperationProgressManager t.Item1 = DateTime.Now; s_lastProgressValues[modelCardId] = (t.Item1, progress.Status); // Since it's the first time we get a call for this model card, we should send it out - await SendProgress(bridge, modelCardId, progress).ConfigureAwait(false); + SendProgress(bridge, modelCardId, progress); return; } @@ -72,9 +71,9 @@ public class OperationProgressManager : IOperationProgressManager return; } s_lastProgressValues[modelCardId] = (currentTime, progress.Status); - await SendProgress(bridge, modelCardId, progress).ConfigureAwait(false); + SendProgress(bridge, modelCardId, progress); } - private static async Task SendProgress(IBrowserBridge bridge, string modelCardId, ModelCardProgress progress) => - await bridge.Send(SET_MODEL_PROGRESS_UI_COMMAND_NAME, new { modelCardId, progress }).ConfigureAwait(false); + private static void SendProgress(IBrowserBridge bridge, string modelCardId, ModelCardProgress progress) => + bridge.Send2(SET_MODEL_PROGRESS_UI_COMMAND_NAME, new { modelCardId, progress }); } diff --git a/DUI3/Speckle.Connectors.DUI/Bindings/ReceiveBindingUICommands.cs b/DUI3/Speckle.Connectors.DUI/Bindings/ReceiveBindingUICommands.cs index 0c7bd3428..2cabe20dd 100644 --- a/DUI3/Speckle.Connectors.DUI/Bindings/ReceiveBindingUICommands.cs +++ b/DUI3/Speckle.Connectors.DUI/Bindings/ReceiveBindingUICommands.cs @@ -17,16 +17,14 @@ public sealed class ReceiveBindingUICommands : BasicConnectorBindingCommands IEnumerable conversionResults ) { - await Bridge - .Send( - SET_MODEL_RECEIVE_RESULT_UI_COMMAND_NAME, - new - { - ModelCardId = modelCardId, - bakedObjectIds, - conversionResults - } - ) - .ConfigureAwait(false); + await Bridge.Send( + SET_MODEL_RECEIVE_RESULT_UI_COMMAND_NAME, + new + { + ModelCardId = modelCardId, + bakedObjectIds, + conversionResults + } + ); } } diff --git a/DUI3/Speckle.Connectors.DUI/Bindings/SendBindingUICommands.cs b/DUI3/Speckle.Connectors.DUI/Bindings/SendBindingUICommands.cs index 72569f5ac..45e3ff23f 100644 --- a/DUI3/Speckle.Connectors.DUI/Bindings/SendBindingUICommands.cs +++ b/DUI3/Speckle.Connectors.DUI/Bindings/SendBindingUICommands.cs @@ -12,43 +12,38 @@ public class SendBindingUICommands(IBrowserBridge bridge) : BasicConnectorBindin private const string SET_ID_MAP_COMMAND_NAME = "setIdMap"; // POC.. the only reasons this needs the bridge is to send? realtionship to these messages and the bridge is unclear - public async Task RefreshSendFilters() => - await Bridge.Send(REFRESH_SEND_FILTERS_UI_COMMAND_NAME).ConfigureAwait(false); + public async Task RefreshSendFilters() => await Bridge.Send(REFRESH_SEND_FILTERS_UI_COMMAND_NAME); public async Task SetModelsExpired(IEnumerable expiredModelIds) => - await Bridge.Send(SET_MODELS_EXPIRED_UI_COMMAND_NAME, expiredModelIds).ConfigureAwait(false); + await Bridge.Send(SET_MODELS_EXPIRED_UI_COMMAND_NAME, expiredModelIds); public async Task SetFilterObjectIds( string modelCardId, Dictionary idMap, List newSelectedObjectIds ) => - await Bridge - .Send( - SET_ID_MAP_COMMAND_NAME, - new - { - modelCardId, - idMap, - newSelectedObjectIds - } - ) - .ConfigureAwait(false); + await Bridge.Send( + SET_ID_MAP_COMMAND_NAME, + new + { + modelCardId, + idMap, + newSelectedObjectIds + } + ); public async Task SetModelSendResult( string modelCardId, string versionId, IEnumerable sendConversionResults ) => - await Bridge - .Send( - SET_MODEL_SEND_RESULT_UI_COMMAND_NAME, - new - { - modelCardId, - versionId, - sendConversionResults - } - ) - .ConfigureAwait(false); + await Bridge.Send( + SET_MODEL_SEND_RESULT_UI_COMMAND_NAME, + new + { + modelCardId, + versionId, + sendConversionResults + } + ); } diff --git a/DUI3/Speckle.Connectors.DUI/Bindings/TestBinding.cs b/DUI3/Speckle.Connectors.DUI/Bindings/TestBinding.cs index a5e095fbc..f43a0bd42 100644 --- a/DUI3/Speckle.Connectors.DUI/Bindings/TestBinding.cs +++ b/DUI3/Speckle.Connectors.DUI/Bindings/TestBinding.cs @@ -46,22 +46,20 @@ public class TestBinding : IBinding switch (eventName) { case "emptyTestEvent": - await Parent.Send("emptyTestEvent").ConfigureAwait(false); + await Parent.Send("emptyTestEvent"); break; case "testEvent": default: - await Parent - .Send( - "testEvent", - new - { - IsOk = true, - Name = "foo", - Count = 42 - } - ) - .ConfigureAwait(false); + await Parent.Send( + "testEvent", + new + { + IsOk = true, + Name = "foo", + Count = 42 + } + ); break; } } diff --git a/DUI3/Speckle.Connectors.DUI/Bindings/TopLevelExceptionHandlerBinding.cs b/DUI3/Speckle.Connectors.DUI/Bindings/TopLevelExceptionHandlerBinding.cs index 617722d69..2fec4a7ca 100644 --- a/DUI3/Speckle.Connectors.DUI/Bindings/TopLevelExceptionHandlerBinding.cs +++ b/DUI3/Speckle.Connectors.DUI/Bindings/TopLevelExceptionHandlerBinding.cs @@ -2,10 +2,6 @@ namespace Speckle.Connectors.DUI.Bindings; -/// -/// Simple binding that can be injected into non- services to get access to the -/// -/// public sealed class TopLevelExceptionHandlerBinding(IBrowserBridge parent) : IBinding { public string Name => "topLevelExceptionHandlerBinding"; diff --git a/DUI3/Speckle.Connectors.DUI/Bridge/BrowserBridge.cs b/DUI3/Speckle.Connectors.DUI/Bridge/BrowserBridge.cs index 252119ab2..228775334 100644 --- a/DUI3/Speckle.Connectors.DUI/Bridge/BrowserBridge.cs +++ b/DUI3/Speckle.Connectors.DUI/Bridge/BrowserBridge.cs @@ -3,8 +3,8 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Runtime.InteropServices; -using System.Threading.Tasks.Dataflow; using Microsoft.Extensions.Logging; +using Speckle.Connectors.Common.Threading; using Speckle.Connectors.DUI.Bindings; using Speckle.Connectors.DUI.Utils; using Speckle.Newtonsoft.Json; @@ -17,7 +17,9 @@ namespace Speckle.Connectors.DUI.Bridge; /// Wraps a binding class, and manages its calls from the Frontend to .NET, and sending events from .NET to the the Frontend. /// Initially inspired by: https://github.com/johot/WebView2-better-bridge /// +#pragma warning disable CS0618 // Type or member is obsolete [ClassInterface(ClassInterfaceType.AutoDual)] +#pragma warning restore CS0618 // Type or member is obsolete [ComVisible(true)] public sealed class BrowserBridge : IBrowserBridge { @@ -27,15 +29,14 @@ public sealed class BrowserBridge : IBrowserBridge /// private readonly ConcurrentDictionary _resultsStore = new(); - private readonly SynchronizationContext _mainThreadContext; - public ITopLevelExceptionHandler TopLevelExceptionHandler { get; } + + private readonly ITopLevelExceptionHandler _topLevelExceptionHandler; + private readonly IThreadContext _threadContext; private readonly IBrowserScriptExecutor _browserScriptExecutor; private readonly IJsonSerializer _jsonSerializer; private IReadOnlyDictionary _bindingMethodCache = new Dictionary(); - - private ActionBlock? _actionBlock; private IBinding? _binding; private Type? _bindingType; @@ -57,28 +58,34 @@ public sealed class BrowserBridge : IBrowserBridge } } - private struct RunMethodArgs - { - public string MethodName; - public string RequestId; - public string MethodArgs; - } - public BrowserBridge( + IThreadContext threadContext, IJsonSerializer jsonSerializer, ILogger logger, - ILogger topLogger, - IBrowserScriptExecutor browserScriptExecutor + IBrowserScriptExecutor browserScriptExecutor, + ITopLevelExceptionHandler topLevelExceptionHandler ) { + _threadContext = threadContext; _jsonSerializer = jsonSerializer; _logger = logger; - TopLevelExceptionHandler = new TopLevelExceptionHandler(topLogger, this); - // Capture the main thread's SynchronizationContext - _mainThreadContext = SynchronizationContext.Current.NotNull("No UI thread to capture?"); _browserScriptExecutor = browserScriptExecutor; + _topLevelExceptionHandler = topLevelExceptionHandler; } + private async Task OnExceptionEvent(Exception ex) => + await Send( + BasicConnectorBindingCommands.SET_GLOBAL_NOTIFICATION, + new + { + type = ToastNotificationType.DANGER, + title = "Unhandled Exception Occurred", + description = ex.ToFormattedString(), + autoClose = false + } + ) + .ConfigureAwait(false); + public void AssociateWithBinding(IBinding binding) { // set via binding property to ensure explosion if already bound @@ -95,30 +102,9 @@ public sealed class BrowserBridge : IBrowserBridge bindingMethodCache[m.Name] = m; } _bindingMethodCache = bindingMethodCache; - - // Whenever the ui will call run method inside .net, it will post a message to this action block. - // This conveniently executes the code outside the UI thread and does not block during long operations (such as sending). - _actionBlock = new ActionBlock( - OnActionBlock, - new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 1000 } - ); - _logger.LogInformation("Bridge bound to front end name {FrontEndName}", binding.Name); } - private async Task OnActionBlock(RunMethodArgs args) - { - Result result = await TopLevelExceptionHandler - .CatchUnhandledAsync(async () => await ExecuteMethod(args.MethodName, args.MethodArgs).ConfigureAwait(false)) - .ConfigureAwait(false); - - string resultJson = result.IsSuccess - ? _jsonSerializer.Serialize(result.Value) - : SerializeFormattedException(result.Exception); - - await NotifyUIMethodCallResultReady(args.RequestId, resultJson).ConfigureAwait(false); - } - /// /// Used by the Frontend bridge logic to understand which methods are available. /// @@ -130,81 +116,26 @@ public sealed class BrowserBridge : IBrowserBridge return bindingNames; } - /// - /// This method posts the requested call to our action block executor. - /// - /// - /// - /// - public void RunMethod(string methodName, string requestId, string args) - { - TopLevelExceptionHandler.CatchUnhandled(Post); - return; - - void Post() - { - bool wasAccepted = _actionBlock - .NotNull() - .Post( - new RunMethodArgs + //don't wait for browser runs on purpose + public void RunMethod(string methodName, string requestId, string methodArgs) => + _threadContext + .RunOnWorkerAsync(async () => + { + var task = await _topLevelExceptionHandler + .CatchUnhandledAsync(async () => { - MethodName = methodName, - RequestId = requestId, - MethodArgs = args - } - ); - if (!wasAccepted) - { - throw new InvalidOperationException($"Action block declined to Post ({methodName} {requestId} {args})"); - } - } - } - - public void RunOnMainThread(Action action) - { - _mainThreadContext.Post( - _ => - { - // Execute the action on the main thread - TopLevelExceptionHandler.CatchUnhandled(action); - }, - null - ); - } - - public async Task RunOnMainThreadAsync(Func action) - { - await RunOnMainThreadAsync(async () => - { - await action.Invoke().ConfigureAwait(false); - return null; + var result = await ExecuteMethod(methodName, methodArgs).ConfigureAwait(false); + string resultJson = _jsonSerializer.Serialize(result); + NotifyUIMethodCallResultReady(requestId, resultJson); + }) + .ConfigureAwait(false); + if (task.Exception is not null) + { + string resultJson = SerializeFormattedException(task.Exception); + NotifyUIMethodCallResultReady(requestId, resultJson); + } }) - .ConfigureAwait(false); - } - - [SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "TaskCompletionSource")] - public Task RunOnMainThreadAsync(Func> action) - { - TaskCompletionSource tcs = new(); - - _mainThreadContext.Post( - async _ => - { - try - { - T result = await action.Invoke().ConfigureAwait(false); - tcs.SetResult(result); - } - catch (Exception ex) - { - tcs.SetException(ex); - } - }, - null - ); - - return tcs.Task; - } + .FireAndForget(); /// /// Used by the action block to invoke the actual method called by the UI. @@ -222,7 +153,7 @@ public sealed class BrowserBridge : IBrowserBridge throw new InvalidOperationException("Bridge was not initialized with a binding"); } - if (!_bindingMethodCache.TryGetValue(methodName, out MethodInfo method)) + if (!_bindingMethodCache.TryGetValue(methodName, out MethodInfo? method)) { throw new ArgumentException( $"Cannot find method {methodName} in bindings class {_bindingType.NotNull().AssemblyQualifiedName}.", @@ -266,7 +197,7 @@ public sealed class BrowserBridge : IBrowserBridge } // It's an async call - await resultTypedTask.ConfigureAwait(false); + await resultTypedTask; // If has a "Result" property return the value otherwise null (Task etc) PropertyInfo? resultProperty = resultTypedTask.GetType().GetProperty(nameof(Task.Result)); @@ -296,16 +227,12 @@ public sealed class BrowserBridge : IBrowserBridge /// /// /// - /// - private async Task NotifyUIMethodCallResultReady( - string requestId, - string? serializedData = null, - CancellationToken cancellationToken = default - ) + /// + private void NotifyUIMethodCallResultReady(string requestId, string? serializedData = null) { _resultsStore[requestId] = serializedData; string script = $"{FrontendBoundName}.responseReady('{requestId}')"; - await _browserScriptExecutor.ExecuteScriptAsyncMethod(script, cancellationToken).ConfigureAwait(false); + _browserScriptExecutor.ExecuteScript(script); } /// @@ -339,7 +266,7 @@ public sealed class BrowserBridge : IBrowserBridge Process.Start(new ProcessStartInfo { FileName = url, UseShellExecute = true }); } - public async Task Send(string eventName, CancellationToken cancellationToken = default) + public Task Send(string eventName, CancellationToken cancellationToken = default) { if (_binding is null) { @@ -348,10 +275,11 @@ public sealed class BrowserBridge : IBrowserBridge var script = $"{FrontendBoundName}.emit('{eventName}')"; - await _browserScriptExecutor.ExecuteScriptAsyncMethod(script, cancellationToken).ConfigureAwait(false); + _browserScriptExecutor.ExecuteScript(script); + return Task.CompletedTask; } - public async Task Send(string eventName, T data, CancellationToken cancellationToken = default) + public Task Send(string eventName, T data, CancellationToken cancellationToken = default) where T : class { if (_binding is null) @@ -363,6 +291,22 @@ public sealed class BrowserBridge : IBrowserBridge string requestId = $"{Guid.NewGuid()}_{eventName}"; _resultsStore[requestId] = payload; var script = $"{FrontendBoundName}.emitResponseReady('{eventName}', '{requestId}')"; - await _browserScriptExecutor.ExecuteScriptAsyncMethod(script, cancellationToken).ConfigureAwait(false); + _browserScriptExecutor.ExecuteScript(script); + return Task.CompletedTask; + } + + public void Send2(string eventName, T data) + where T : class + { + if (_binding is null) + { + throw new InvalidOperationException("Bridge was not initialized with a binding"); + } + + string payload = _jsonSerializer.Serialize(data); + string requestId = $"{Guid.NewGuid()}_{eventName}"; + _resultsStore[requestId] = payload; + var script = $"{FrontendBoundName}.emitResponseReady('{eventName}', '{requestId}')"; + _browserScriptExecutor.ExecuteScript(script); } } diff --git a/DUI3/Speckle.Connectors.DUI/Bridge/IBrowserBridge.cs b/DUI3/Speckle.Connectors.DUI/Bridge/IBrowserBridge.cs index 7bd772978..20c85708f 100644 --- a/DUI3/Speckle.Connectors.DUI/Bridge/IBrowserBridge.cs +++ b/DUI3/Speckle.Connectors.DUI/Bridge/IBrowserBridge.cs @@ -29,22 +29,6 @@ public interface IBrowserBridge /// public void RunMethod(string methodName, string requestId, string args); - /// - /// Posts an onto the main thread - /// Some applications might need to run some operations on main thread as deferred actions. - /// - /// An awaitable - /// Action to run on the main thread - public Task RunOnMainThreadAsync(Func> action); - - /// - /// Posts an onto the main thread - /// Some applications might need to run some operations on main thread as deferred actions. - /// - /// An awaitable - /// Action to run on the main thread - public Task RunOnMainThreadAsync(Func action); - /// /// Bridge was not initialized with a binding public Task Send(string eventName, CancellationToken cancellationToken = default); @@ -56,5 +40,6 @@ public interface IBrowserBridge public Task Send(string eventName, T data, CancellationToken cancellationToken = default) where T : class; - public ITopLevelExceptionHandler TopLevelExceptionHandler { get; } + public void Send2(string eventName, T data) + where T : class; } diff --git a/DUI3/Speckle.Connectors.DUI/Bridge/IBrowserScriptExecutor.cs b/DUI3/Speckle.Connectors.DUI/Bridge/IBrowserScriptExecutor.cs index 6c3fb69f9..c0979f36c 100644 --- a/DUI3/Speckle.Connectors.DUI/Bridge/IBrowserScriptExecutor.cs +++ b/DUI3/Speckle.Connectors.DUI/Bridge/IBrowserScriptExecutor.cs @@ -4,7 +4,7 @@ public interface IBrowserScriptExecutor { /// thrown when is /// The (constant string) script to execute on the browser - public Task ExecuteScriptAsyncMethod(string script, CancellationToken cancellationToken); + public void ExecuteScript(string script); public bool IsBrowserInitialized { get; } diff --git a/DUI3/Speckle.Connectors.DUI/Bridge/IdleCallManager.cs b/DUI3/Speckle.Connectors.DUI/Bridge/IdleCallManager.cs index 0b3e83504..b0e285e26 100644 --- a/DUI3/Speckle.Connectors.DUI/Bridge/IdleCallManager.cs +++ b/DUI3/Speckle.Connectors.DUI/Bridge/IdleCallManager.cs @@ -1,31 +1,16 @@ using System.Collections.Concurrent; -using System.Diagnostics.CodeAnalysis; +using Speckle.InterfaceGenerator; namespace Speckle.Connectors.DUI.Bridge; -public interface IIdleCallManager -{ - void SubscribeToIdle(string id, Action action, Action addEvent); - void SubscribeToIdle(string id, Func asyncAction, Action addEvent); - void AppOnIdle(Action removeEvent); -} - //should be registered as singleton -[SuppressMessage("ReSharper", "InconsistentlySynchronizedField")] -public sealed class IdleCallManager : IIdleCallManager +[GenerateAutoInterface] +public sealed class IdleCallManager(ITopLevelExceptionHandler topLevelExceptionHandler) : IIdleCallManager { internal ConcurrentDictionary> Calls { get; } = new(); - private readonly object _lock = new(); public bool IdleSubscriptionCalled { get; private set; } - private readonly ITopLevelExceptionHandler _topLevelExceptionHandler; - - public IdleCallManager(ITopLevelExceptionHandler topLevelExceptionHandler) - { - _topLevelExceptionHandler = topLevelExceptionHandler; - } - public void SubscribeToIdle(string id, Action action, Action addEvent) => SubscribeToIdle( id, @@ -37,19 +22,20 @@ public sealed class IdleCallManager : IIdleCallManager addEvent ); - public void SubscribeToIdle(string id, Func asyncAction, Action addEvent) => - _topLevelExceptionHandler.CatchUnhandled(() => SubscribeInternal(id, asyncAction, addEvent)); - - internal void SubscribeInternal(string id, Func action, Action addEvent) + public void SubscribeToIdle(string id, Func action, Action addEvent) { - Calls.TryAdd(id, action); + if (!Calls.TryAdd(id, action)) + { + return; + } + if (!IdleSubscriptionCalled) { lock (_lock) { if (!IdleSubscriptionCalled) { - addEvent.Invoke(); + topLevelExceptionHandler.CatchUnhandled(addEvent.Invoke); IdleSubscriptionCalled = true; } } @@ -57,17 +43,16 @@ public sealed class IdleCallManager : IIdleCallManager } public void AppOnIdle(Action removeEvent) => - _topLevelExceptionHandler.FireAndForget(async () => await AppOnIdleInternal(removeEvent).ConfigureAwait(false)); + topLevelExceptionHandler.FireAndForget(async () => await AppOnIdleInternal(removeEvent)); internal async Task AppOnIdleInternal(Action removeEvent) { foreach (KeyValuePair> kvp in Calls) { - await _topLevelExceptionHandler.CatchUnhandledAsync(kvp.Value).ConfigureAwait(false); + await topLevelExceptionHandler.CatchUnhandledAsync(kvp.Value); } Calls.Clear(); - if (IdleSubscriptionCalled) { lock (_lock) diff --git a/DUI3/Speckle.Connectors.DUI/Bridge/SyncToUIThread.cs b/DUI3/Speckle.Connectors.DUI/Bridge/SyncToUIThread.cs deleted file mode 100644 index bb7bbe0c3..000000000 --- a/DUI3/Speckle.Connectors.DUI/Bridge/SyncToUIThread.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using Speckle.Connectors.Common.Operations; - -namespace Speckle.Connectors.DUI.Bridge; - -public class SyncToUIThread : ISyncToThread -{ - private readonly IBrowserBridge _bridge; - - public SyncToUIThread(IBrowserBridge bridge) - { - _bridge = bridge; - } - - [SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Task Completion Source")] - public async Task RunOnThread(Func> func) => - await _bridge.RunOnMainThreadAsync(func).ConfigureAwait(false); -} diff --git a/DUI3/Speckle.Connectors.DUI/Bridge/TopLevelExceptionHandler.cs b/DUI3/Speckle.Connectors.DUI/Bridge/TopLevelExceptionHandler.cs index a39ff0bc2..f8b440749 100644 --- a/DUI3/Speckle.Connectors.DUI/Bridge/TopLevelExceptionHandler.cs +++ b/DUI3/Speckle.Connectors.DUI/Bridge/TopLevelExceptionHandler.cs @@ -1,8 +1,6 @@ using Microsoft.Extensions.Logging; -using Speckle.Connectors.DUI.Bindings; using Speckle.InterfaceGenerator; using Speckle.Sdk; -using Speckle.Sdk.Models.Extensions; namespace Speckle.Connectors.DUI.Bridge; @@ -23,15 +21,13 @@ namespace Speckle.Connectors.DUI.Bridge; public sealed class TopLevelExceptionHandler : ITopLevelExceptionHandler { private readonly ILogger _logger; - public IBrowserBridge Parent { get; } public string Name => nameof(TopLevelExceptionHandler); private const string UNHANDLED_LOGGER_TEMPLATE = "An unhandled Exception occured"; - internal TopLevelExceptionHandler(ILogger logger, IBrowserBridge bridge) + public TopLevelExceptionHandler(ILogger logger) { _logger = logger; - Parent = bridge; } /// @@ -41,31 +37,56 @@ public sealed class TopLevelExceptionHandler : ITopLevelExceptionHandler /// The function to invoke and provide error handling for /// will be rethrown, these should be allowed to bubble up to the host app /// - public void CatchUnhandled(Action function) + public Result CatchUnhandled(Action function) { - _ = CatchUnhandled(() => + var r = CatchUnhandled(() => { function(); - return null; + return true; }); + if (r.IsSuccess) + { + return new Result(); + } + return new Result(r.Exception); } /// /// return type /// A result pattern struct (where exceptions have been handled) - public Result CatchUnhandled(Func function) => - CatchUnhandledAsync(() => Task.FromResult(function.Invoke())).Result; //Safe to do a .Result because this as an already completed and non-async Task from the Task.FromResult + public Result CatchUnhandled(Func function) + { + try + { + return new Result(function()); + } + catch (Exception ex) when (!ex.IsFatal()) + { + _logger.LogError(ex, UNHANDLED_LOGGER_TEMPLATE); + // _eventAggregator.GetEvent().PublishAsync(ex).Wait(); + return new(ex); + } + catch (Exception ex) + { + _logger.LogCritical(ex, UNHANDLED_LOGGER_TEMPLATE); + throw; + } + } /// /// A result pattern struct (where exceptions have been handled) - public async Task CatchUnhandledAsync(Func function) + public async Task CatchUnhandledAsync(Func function) { - _ = await CatchUnhandledAsync(async () => - { - await function().ConfigureAwait(false); - return null; - }) - .ConfigureAwait(false); + var r = await CatchUnhandledAsync(async () => + { + await function(); + return true; + }); + if (r.IsSuccess) + { + return new Result(); + } + return new Result(r.Exception); } /// @@ -75,18 +96,12 @@ public sealed class TopLevelExceptionHandler : ITopLevelExceptionHandler { try { - return new(await function.Invoke().ConfigureAwait(false)); + return new(await function.Invoke()); } catch (Exception ex) when (!ex.IsFatal()) { _logger.LogError(ex, UNHANDLED_LOGGER_TEMPLATE); - await SetGlobalNotification( - ToastNotificationType.DANGER, - "Unhandled Exception Occured", - ex.ToFormattedString(), - false - ) - .ConfigureAwait(false); + // await _eventAggregator.GetEvent().PublishAsync(ex); return new(ex); } } @@ -107,18 +122,4 @@ public sealed class TopLevelExceptionHandler : ITopLevelExceptionHandler /// /// public async void FireAndForget(Func function) => await CatchUnhandledAsync(function).ConfigureAwait(false); - - private async Task SetGlobalNotification(ToastNotificationType type, string title, string message, bool autoClose) => - await Parent - .Send( - BasicConnectorBindingCommands.SET_GLOBAL_NOTIFICATION, //TODO: We could move these constants into a DUI3 constants static class - new - { - type, - title, - description = message, - autoClose - } - ) - .ConfigureAwait(false); } diff --git a/DUI3/Speckle.Connectors.DUI/ContainerRegistration.cs b/DUI3/Speckle.Connectors.DUI/ContainerRegistration.cs index f9f76d551..15c90b734 100644 --- a/DUI3/Speckle.Connectors.DUI/ContainerRegistration.cs +++ b/DUI3/Speckle.Connectors.DUI/ContainerRegistration.cs @@ -1,50 +1,62 @@ using System.Reflection; using Microsoft.Extensions.DependencyInjection; -using Speckle.Connectors.Common.Operations; +using Microsoft.Extensions.Logging; +using Speckle.Connectors.Common.Threading; using Speckle.Connectors.DUI.Bindings; using Speckle.Connectors.DUI.Bridge; +using Speckle.Connectors.DUI.Models; using Speckle.Sdk; +using Speckle.Sdk.Common; using Speckle.Sdk.Transports; namespace Speckle.Connectors.DUI; public static class ContainerRegistration -{ /* - public static void AddDUI(this SpeckleContainerBuilder speckleContainerBuilder) +{ + public static void AddDUI(this IServiceCollection serviceCollection) + where TDocumentStore : DocumentModelStore + where TThreadContext : IThreadContext, new() { - // send operation and dependencies - speckleContainerBuilder.AddSingletonInstance(); - speckleContainerBuilder.AddSingleton(); - speckleContainerBuilder.AddTransient(); // POC: Each binding should have it's own bridge instance - speckleContainerBuilder.AddSingleton(GetJsonSerializerSettings()); - speckleContainerBuilder.ScanAssemblyOfType(); - speckleContainerBuilder.ScanAssemblyOfType(); - } -*/ - public static void AddDUI(this IServiceCollection serviceCollection) - { - // send operation and dependencies - serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(); + // context always newed up on host app's main/ui thread + serviceCollection.AddSingleton(new TThreadContext()); + serviceCollection.AddSingleton(); + serviceCollection.AddTransient(); // POC: Each binding should have it's own bridge instance - serviceCollection.AddMatchingInterfacesAsTransient(Assembly.GetAssembly(typeof(IdleCallManager))); - serviceCollection.AddMatchingInterfacesAsTransient(Assembly.GetAssembly(typeof(IServerTransportFactory))); - } + serviceCollection.AddMatchingInterfacesAsTransient(Assembly.GetAssembly(typeof(IdleCallManager)).NotNull()); + serviceCollection.AddMatchingInterfacesAsTransient(Assembly.GetAssembly(typeof(IServerTransportFactory)).NotNull()); - public static void UseDUI(this IServiceProvider serviceProvider) - { - serviceProvider.GetRequiredService(); - } - - public static void RegisterTopLevelExceptionHandler(this IServiceCollection serviceCollection) - { serviceCollection.AddSingleton(sp => sp.GetRequiredService() ); serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(c => - c.GetRequiredService().Parent.TopLevelExceptionHandler - ); + serviceCollection.AddSingleton(); + } + + public static void UseDUI(this IServiceProvider serviceProvider) + { + //observe the unobserved! + TaskScheduler.UnobservedTaskException += (_, args) => + { + try + { + serviceProvider + .GetRequiredService() + .CreateLogger("UnobservedTaskException") + .LogError(args.Exception, "Unobserved task exception"); + } +#pragma warning disable CA1031 + catch (Exception e) +#pragma warning restore CA1031 + { + Console.WriteLine("Error logging unobserved task exception"); + Console.WriteLine(args.Exception); + Console.WriteLine(e); + } + finally + { + args.SetObserved(); + } + }; } } diff --git a/DUI3/Speckle.Connectors.DUI/Models/Card/ModelCardProgress.cs b/DUI3/Speckle.Connectors.DUI/Models/Card/ModelCardProgress.cs index 1d2dbdeb7..aff002bd2 100644 --- a/DUI3/Speckle.Connectors.DUI/Models/Card/ModelCardProgress.cs +++ b/DUI3/Speckle.Connectors.DUI/Models/Card/ModelCardProgress.cs @@ -4,4 +4,7 @@ /// Progress value between 0 and 1 to calculate UI progress bar width. /// If it is null it will swooshing on UI. /// -public record ModelCardProgress(string ModelCardId, string Status, double? Progress); +public record ModelCardProgress(string ModelCardId, string Status, double? Progress) +{ + public override string ToString() => $"{ModelCardId} - {Status} - {Progress}"; +} diff --git a/DUI3/Speckle.Connectors.DUI/Models/Card/SendFilter/ISendFilterSelect.cs b/DUI3/Speckle.Connectors.DUI/Models/Card/SendFilter/ISendFilterSelect.cs new file mode 100644 index 000000000..d4dd8da6c --- /dev/null +++ b/DUI3/Speckle.Connectors.DUI/Models/Card/SendFilter/ISendFilterSelect.cs @@ -0,0 +1,13 @@ +namespace Speckle.Connectors.DUI.Models.Card.SendFilter; + +public record SendFilterSelectItem(string Id, string Name); + +/// +/// UI data type to make have FilterFormSelect component as send filter. +/// +public interface ISendFilterSelect : ISendFilter +{ + public bool IsMultiSelectable { get; set; } + public List SelectedItems { get; set; } + public List Items { get; set; } +} diff --git a/DUI3/Speckle.Connectors.DUI/Models/DocumentModelStore.cs b/DUI3/Speckle.Connectors.DUI/Models/DocumentModelStore.cs index 8f0a5f75e..dda4f8df4 100644 --- a/DUI3/Speckle.Connectors.DUI/Models/DocumentModelStore.cs +++ b/DUI3/Speckle.Connectors.DUI/Models/DocumentModelStore.cs @@ -1,6 +1,7 @@ -using System.Collections.ObjectModel; +using System.Diagnostics; using Speckle.Connectors.DUI.Models.Card; using Speckle.Connectors.DUI.Utils; +using Speckle.Sdk; using Speckle.Sdk.Common; namespace Speckle.Connectors.DUI.Models; @@ -8,42 +9,9 @@ namespace Speckle.Connectors.DUI.Models; /// /// Encapsulates the state Speckle needs to persist in the host app's document. /// -public abstract class DocumentModelStore +public abstract class DocumentModelStore(IJsonSerializer serializer) { - private ObservableCollection _models = new(); - - /// - /// Stores all the model cards in the current document/file. - /// - public ObservableCollection Models - { - get => _models; - protected set - { - _models = value; - RegisterWriteOnChangeEvent(); - } - } - - private readonly IJsonSerializer _serializer; - - private readonly bool _writeToFileOnChange; - - protected DocumentModelStore(IJsonSerializer jsonSerializer, bool writeToFileOnChange) - { - _serializer = jsonSerializer; - _writeToFileOnChange = writeToFileOnChange; - - RegisterWriteOnChangeEvent(); - } - - private void RegisterWriteOnChangeEvent() - { - if (_writeToFileOnChange) - { - _models.CollectionChanged += (_, _) => WriteToFile(); - } - } + private readonly List _models = new(); /// /// This event is triggered by each specific host app implementation of the document model store. @@ -51,6 +19,22 @@ public abstract class DocumentModelStore // POC: unsure about the PublicAPI annotation, unsure if this changed handle should live here on the store... :/ public event EventHandler? DocumentChanged; + //needed for javascript UI + public IReadOnlyList Models + { + get + { + lock (_models) + { + return _models.AsReadOnly(); + } + } + } + + protected void OnDocumentChanged() => DocumentChanged?.Invoke(this, EventArgs.Empty); + + public virtual Task OnDocumentStoreInitialized() => Task.CompletedTask; + public virtual bool IsDocumentInit { get; set; } // TODO: not sure about this, throwing an exception, needs some thought... @@ -58,43 +42,109 @@ public abstract class DocumentModelStore // In theory this should never really happen, but if it does public ModelCard GetModelById(string id) { - var model = Models.First(model => model.ModelCardId == id) ?? throw new ModelNotFoundException(); - return model; + lock (_models) + { + var model = _models.FirstOrDefault(model => model.ModelCardId == id) ?? throw new ModelNotFoundException(); + return model; + } + } + + public void AddModel(ModelCard model) + { + lock (_models) + { + _models.Add(model); + SaveState(); + } + } + + public void ClearAndSave() + { + lock (_models) + { + _models.Clear(); + SaveState(); + } } public void UpdateModel(ModelCard model) { - int idx = Models.ToList().FindIndex(m => model.ModelCardId == m.ModelCardId); - Models[idx] = model; + lock (_models) + { + var index = _models.FindIndex(m => m.ModelCardId == model.ModelCardId); + if (index == -1) + { + throw new ModelNotFoundException("Model card not found to update."); + } + _models[index] = model; + SaveState(); + } } public void RemoveModel(ModelCard model) { - int index = Models.ToList().FindIndex(m => m.ModelCardId == model.ModelCardId); - Models.RemoveAt(index); + lock (_models) + { + var index = _models.FindIndex(m => m.ModelCardId == model.ModelCardId); + if (index == -1) + { + throw new ModelNotFoundException("Model card not found to update."); + } + _models.RemoveAt(index); + SaveState(); + } } - protected void OnDocumentChanged() => DocumentChanged?.Invoke(this, EventArgs.Empty); + public IEnumerable GetSenders() + { + lock (_models) + { + return _models + .Where(model => model.TypeDiscriminator == nameof(SenderModelCard)) + .Cast() + .ToList(); + } + } - public IEnumerable GetSenders() => - Models.Where(model => model.TypeDiscriminator == nameof(SenderModelCard)).Cast(); - - public IEnumerable GetReceivers() => - Models.Where(model => model.TypeDiscriminator == nameof(ReceiverModelCard)).Cast(); - - protected string Serialize() => _serializer.Serialize(Models); + protected string Serialize() => serializer.Serialize(Models); // POC: this seemms more like a IModelsDeserializer?, seems disconnected from this class - protected ObservableCollection Deserialize(string models) => - _serializer.Deserialize>(models).NotNull(); + protected List Deserialize(string models) => serializer.Deserialize>(models).NotNull(); - /// - /// Implement this method according to the host app's specific ways of storing custom data in its file. - /// - public abstract void WriteToFile(); + protected void SaveState() + { + lock (_models) + { + var state = Serialize(); + HostAppSaveState(state); + } + } /// /// Implement this method according to the host app's specific ways of reading custom data from its file. /// - public abstract void ReadFromFile(); + protected abstract void HostAppSaveState(string modelCardState); + + protected abstract void LoadState(); + + protected void LoadFromString(string? models) + { + try + { + lock (_models) + { + _models.Clear(); + if (string.IsNullOrEmpty(models)) + { + return; + } + _models.AddRange(Deserialize(models.NotNull()).NotNull()); + } + } + catch (Exception ex) when (!ex.IsFatal()) + { + ClearAndSave(); + Debug.WriteLine(ex.Message); // POC: Log here error and notify UI that cards not read succesfully + } + } } diff --git a/DUI3/Speckle.Connectors.DUI/Speckle.Connectors.DUI.csproj b/DUI3/Speckle.Connectors.DUI/Speckle.Connectors.DUI.csproj index 8f3048e1a..447c8c052 100644 --- a/DUI3/Speckle.Connectors.DUI/Speckle.Connectors.DUI.csproj +++ b/DUI3/Speckle.Connectors.DUI/Speckle.Connectors.DUI.csproj @@ -1,9 +1,8 @@  - netstandard2.0 + net48;net6.0;net8.0 Debug;Release;Local - @@ -12,7 +11,6 @@ - diff --git a/DUI3/Speckle.Connectors.DUI/Utils/DiscriminatedObjectConverter.cs b/DUI3/Speckle.Connectors.DUI/Utils/DiscriminatedObjectConverter.cs index d3ec2e567..fb0314870 100644 --- a/DUI3/Speckle.Connectors.DUI/Utils/DiscriminatedObjectConverter.cs +++ b/DUI3/Speckle.Connectors.DUI/Utils/DiscriminatedObjectConverter.cs @@ -1,3 +1,4 @@ +using System.Collections.Concurrent; using System.Diagnostics; using System.Reflection; using Microsoft.Extensions.DependencyInjection; @@ -12,9 +13,12 @@ namespace Speckle.Connectors.DUI.Utils; /// This converter ensures we can do polymorphic deserialization to concrete types. This converter is intended /// for use only with UI bound types, not Speckle Bases. /// +//this is effectively a singleton because BrowserBridge and TopLevelExceptionHandler use it public class DiscriminatedObjectConverter(IServiceProvider serviceProvider) : JsonConverter { - private readonly Speckle.Newtonsoft.Json.JsonSerializer _localSerializer = + // POC: remove, replace with DI + private static readonly ConcurrentDictionary s_typeCache = new(); + private static readonly Speckle.Newtonsoft.Json.JsonSerializer s_localSerializer = new() { DefaultValueHandling = DefaultValueHandling.Ignore, @@ -32,7 +36,7 @@ public class DiscriminatedObjectConverter(IServiceProvider serviceProvider) : Js { return; } - var jo = JObject.FromObject(value, _localSerializer); + var jo = JObject.FromObject(value, s_localSerializer); jo.WriteTo(writer); } @@ -72,50 +76,44 @@ public class DiscriminatedObjectConverter(IServiceProvider serviceProvider) : Js return obj as DiscriminatedObject; } - // POC: remove, replace with DI - private readonly Dictionary _typeCache = new(); - - private Type? GetTypeByName(string name) - { - _typeCache.TryGetValue(name, out Type myType); - if (myType != null) - { - return myType; - } - - // POC: why does this exist like this? - // The assemblies within the CurrentDomain are not necessarily loaded - // probably we can leverage DI here so we already know the types, possibly DI plus an attribute - // then we can cache everything on startup - foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies().Reverse()) - { - try + private static Type? GetTypeByName(string typeName) => + s_typeCache.GetOrAdd( + typeName, + name => { - // POC: contains is weak - // working by accident, ModelCard is contained within SenderModelCard :O - // comparisons :D - var type = assembly.DefinedTypes.FirstOrDefault(t => !string.IsNullOrEmpty(t?.Name) && t?.Name == name); - if (type != null) + //POC: why does this exist like this? + // The assemblies within the CurrentDomain are not necessarily loaded + // probably we can leverage DI here so we already know the types, possibly DI plus an attribute + // then we can cache everything on startup + foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies().Reverse()) { - _typeCache[name] = type; - return type; + try + { + // POC: contains is weak + // working by accident, ModelCard is contained within SenderModelCard :O + // comparisons :D + var type = assembly.DefinedTypes.FirstOrDefault(t => !string.IsNullOrEmpty(t?.Name) && t?.Name == name); + if (type != null) + { + return type; + } + } + // POC: this Exception pattern is too broad and should be resticted but fixes the above issues + // the call above is causing load of all assemblies (which is also possibly not good) + // AND it explodes for me loading an exception, so at the last this should + // catch System.Reflection.ReflectionTypeLoadException (and anthing else DefinedTypes might throw) + // LATER COMMENT: Since discriminated object is only used in DUI3 models, we could restrict to only "this" assembly? + catch (ReflectionTypeLoadException ex) + { + // POC: logging + Debug.WriteLine("***" + ex.Message); + } } - } - // POC: this Exception pattern is too broad and should be resticted but fixes the above issues - // the call above is causing load of all assemblies (which is also possibly not good) - // AND it explodes for me loading an exception, so at the last this should - // catch System.Reflection.ReflectionTypeLoadException (and anthing else DefinedTypes might throw) - // LATER COMMENT: Since discriminated object is only used in DUI3 models, we could restrict to only "this" assembly? - catch (ReflectionTypeLoadException ex) - { - // POC: logging - Debug.WriteLine("***" + ex.Message); - } - } - // should this throw instead? :/ - return null; - } + // should this throw instead? :/ + return null; + } + ); } public class AbstractConverter : JsonConverter diff --git a/DUI3/Speckle.Connectors.DUI/packages.lock.json b/DUI3/Speckle.Connectors.DUI/packages.lock.json index d0fb5d70b..673ea02e5 100644 --- a/DUI3/Speckle.Connectors.DUI/packages.lock.json +++ b/DUI3/Speckle.Connectors.DUI/packages.lock.json @@ -1,7 +1,7 @@ { "version": 2, "dependencies": { - ".NETStandard,Version=v2.0": { + ".NETFramework,Version=v4.8": { "Microsoft.Extensions.Logging.Abstractions": { "type": "Direct", "requested": "[2.2.0, )", @@ -14,7 +14,7 @@ "resolved": "1.0.3", "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", "dependencies": { - "Microsoft.NETFramework.ReferenceAssemblies.net461": "1.0.3" + "Microsoft.NETFramework.ReferenceAssemblies.net48": "1.0.3" } }, "Microsoft.SourceLink.GitHub": { @@ -27,15 +27,6 @@ "Microsoft.SourceLink.Common": "8.0.0" } }, - "NETStandard.Library": { - "type": "Direct", - "requested": "[2.0.3, )", - "resolved": "2.0.3", - "contentHash": "st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0" - } - }, "PolySharp": { "type": "Direct", "requested": "[1.14.1, )", @@ -50,9 +41,9 @@ }, "Speckle.Sdk": { "type": "Direct", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "VVT3LJiYlhqnggxxdeTt1QLrqfxDb044x0yX6kxS9b5MRzeDvK2Vz86pLDfuHs+SXvDimRVfYx1M42IW/aPcTQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.Bcl.AsyncInterfaces": "5.0.0", @@ -60,22 +51,16 @@ "Microsoft.Data.Sqlite": "7.0.5", "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", "Microsoft.Extensions.Logging": "2.2.0", - "Speckle.DoubleNumerics": "4.0.1", + "Speckle.DoubleNumerics": "4.1.0", "Speckle.Newtonsoft.Json": "13.0.2", - "Speckle.Sdk.Dependencies": "3.1.0-dev.191" + "Speckle.Sdk.Dependencies": "3.1.1" } }, "Speckle.Sdk.Dependencies": { "type": "Direct", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "EmEOyjsGsNi56Z/ZoBOn8WirTmIT2yqWvlUeUh0BSPX2TDMZXHTKOM/kHmP6HSd10KVFn2Zo/ItY7/K9iRtL1Q==" - }, - "System.Threading.Tasks.Dataflow": { - "type": "Direct", - "requested": "[6.0.0, )", - "resolved": "6.0.0", - "contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA==" + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" }, "GraphQL.Client": { "type": "Transitive", @@ -84,6 +69,7 @@ "dependencies": { "GraphQL.Client.Abstractions": "6.0.0", "GraphQL.Client.Abstractions.Websocket": "6.0.0", + "System.Net.WebSockets.Client.Managed": "1.0.22", "System.Reactive": "5.0.0" } }, @@ -186,15 +172,327 @@ "System.Runtime.CompilerServices.Unsafe": "4.5.1" } }, - "Microsoft.NETCore.Platforms": { + "Microsoft.NETFramework.ReferenceAssemblies.net48": { "type": "Transitive", - "resolved": "1.1.0", - "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==" + "resolved": "1.0.3", + "contentHash": "zMk4D+9zyiEWByyQ7oPImPN/Jhpj166Ky0Nlla4eXlNL8hI/BtSJsgR8Inldd4NNpIAH3oh8yym0W2DrhXdSLQ==" }, - "Microsoft.NETCore.Targets": { + "Microsoft.SourceLink.Common": { "type": "Transitive", - "resolved": "1.1.0", - "contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==" + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "Speckle.Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.2", + "contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA==" + }, + "SQLitePCLRaw.bundle_e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "EWI1olKDjFEBMJu0+3wuxwziIAdWDVMYLhuZ3Qs84rrz+DHwD00RzWPZCa+bLnHCf3oJwuFZIRsHT5p236QXww==", + "dependencies": { + "SQLitePCLRaw.lib.e_sqlite3": "2.1.4", + "SQLitePCLRaw.provider.dynamic_cdecl": "2.1.4" + } + }, + "SQLitePCLRaw.core": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "inBjvSHo9UDKneGNzfUfDjK08JzlcIhn1+SP5Y3m6cgXpCxXKCJDy6Mka7LpgSV+UZmKSnC8rTwB0SQ0xKu5pA==", + "dependencies": { + "System.Memory": "4.5.3" + } + }, + "SQLitePCLRaw.lib.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "2C9Q9eX7CPLveJA0rIhf9RXAvu+7nWZu1A2MdG6SD/NOu26TakGgL1nsbc0JAspGijFOo3HoN79xrx8a368fBg==" + }, + "SQLitePCLRaw.provider.dynamic_cdecl": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "ZsaKKhgYF9B1fvcnOGKl3EycNAwd9CRWX7v0rEfuPWhQQ5Jjpvf2VEHahiLIGHio3hxi3EIKFJw9KvyowWOUAw==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "System.Buffers": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "AwarXzzoDwX6BgrhjoJsk6tUezZEozOT5Y9QKF94Gl4JK91I4PIIBkBco9068Y9/Dra8Dkbie99kXB8+1BaYKw==" + }, + "System.ComponentModel.Annotations": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "UxYQ3FGUOtzJ7LfSdnYSFd7+oEv6M8NgUatatIN2HxNtDdlcvFAf+VIq4Of9cDMJEJC0aSRv/x898RYhB4Yppg==" + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.3", + "contentHash": "3oDzvc/zzetpTKWMShs1AADwZjQ/36HnsufHRPcOjyRAAMLDlu2iD33MBI2opxnezcVUtXyqDXXjoFMOU9c7SA==", + "dependencies": { + "System.Buffers": "4.4.0", + "System.Numerics.Vectors": "4.4.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.2" + } + }, + "System.Net.WebSockets.Client.Managed": { + "type": "Transitive", + "resolved": "1.0.22", + "contentHash": "WqEOxPlXjuZrIjUtXNE9NxEfU/n5E35iV2PtoZdJSUC4tlrqwHnTee+wvMIM4OUaJWmwrymeqcgYrE0IkGAgLA==", + "dependencies": { + "System.Buffers": "4.4.0", + "System.Numerics.Vectors": "4.4.0" + } + }, + "System.Numerics.Vectors": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ==" + }, + "System.Reactive": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "4.5.3", + "contentHash": "3TIsJhD1EiiT0w2CcDMN/iSSwnNnsrnbzeVHSKkaEgV85txMprmuO+Yq2AdSbeVGcg28pdNDTPK87tJhX7VFHw==" + }, + "System.Threading.Tasks.Extensions": { + "type": "Transitive", + "resolved": "4.5.4", + "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + }, + "speckle.connectors.common": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", + "Speckle.Connectors.Logging": "[1.0.0, )", + "Speckle.Objects": "[3.1.1, )", + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" + } + }, + "speckle.connectors.logging": { + "type": "Project" + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "MZtBIwfDFork5vfjpJdG5g8wuJFt7d/y3LOSVVtDK/76wlbtz6cjltfKHqLx2TKVqTj5/c41t77m1+h20zqtPA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0" + } + }, + "Microsoft.Extensions.Logging": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "Nxqhadc9FCmFHzU+fz3oc8sFlE6IadViYg8dfUdGzJZ2JUxnCsRghBhhOWdM4B2zSZqEc+0BjliBh/oNdRZuig==", + "dependencies": { + "Microsoft.Extensions.Configuration.Binder": "2.2.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging.Abstractions": "2.2.0", + "Microsoft.Extensions.Options": "2.2.0" + } + }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, + "Speckle.Objects": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", + "dependencies": { + "Speckle.Sdk": "3.1.1" + } + } + }, + "net6.0": { + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Direct", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" + }, + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net461": "1.0.3" + } + }, + "Microsoft.SourceLink.GitHub": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "dependencies": { + "Microsoft.Build.Tasks.Git": "8.0.0", + "Microsoft.SourceLink.Common": "8.0.0" + } + }, + "PolySharp": { + "type": "Direct", + "requested": "[1.14.1, )", + "resolved": "1.14.1", + "contentHash": "mOOmFYwad3MIOL14VCjj02LljyF1GNw1wP0YVlxtcPvqdxjGGMNdNJJxHptlry3MOd8b40Flm8RPOM8JOlN2sQ==" + }, + "Speckle.InterfaceGenerator": { + "type": "Direct", + "requested": "[0.9.6, )", + "resolved": "0.9.6", + "contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w==" + }, + "Speckle.Sdk": { + "type": "Direct", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", + "dependencies": { + "GraphQL.Client": "6.0.0", + "Microsoft.Bcl.AsyncInterfaces": "5.0.0", + "Microsoft.CSharp": "4.7.0", + "Microsoft.Data.Sqlite": "7.0.5", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging": "2.2.0", + "Speckle.DoubleNumerics": "4.1.0", + "Speckle.Newtonsoft.Json": "13.0.2", + "Speckle.Sdk.Dependencies": "3.1.1" + } + }, + "Speckle.Sdk.Dependencies": { + "type": "Direct", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" + }, + "GraphQL.Client": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "8yPNBbuVBpTptivyAlak4GZvbwbUcjeQTL4vN1HKHRuOykZ4r7l5fcLS6vpyPyLn0x8FsL31xbOIKyxbmR9rbA==", + "dependencies": { + "GraphQL.Client.Abstractions": "6.0.0", + "GraphQL.Client.Abstractions.Websocket": "6.0.0", + "System.Reactive": "5.0.0" + } + }, + "GraphQL.Client.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "h7uzWFORHZ+CCjwr/ThAyXMr0DPpzEANDa4Uo54wqCQ+j7qUKwqYTgOrb1W40sqbvNaZm9v/X7It31SUw0maHA==", + "dependencies": { + "GraphQL.Primitives": "6.0.0" + } + }, + "GraphQL.Client.Abstractions.Websocket": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "Nr9bPf8gIOvLuXpqEpqr9z9jslYFJOvd0feHth3/kPqeR3uMbjF5pjiwh4jxyMcxHdr8Pb6QiXkV3hsSyt0v7A==", + "dependencies": { + "GraphQL.Client.Abstractions": "6.0.0" + } + }, + "GraphQL.Primitives": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "yg72rrYDapfsIUrul7aF6wwNnTJBOFvuA9VdDTQpPa8AlAriHbufeXYLBcodKjfUdkCnaiggX1U/nEP08Zb5GA==" + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "W8DPQjkMScOMTtJbPwmPyj9c3zYSFGawDW3jwlBOOsnY+EzZFLgNQ/UMkK35JmkNOVPdCyPr2Tw7Vv9N+KA3ZQ==" + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.CSharp": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==" + }, + "Microsoft.Data.Sqlite": { + "type": "Transitive", + "resolved": "7.0.5", + "contentHash": "KGxbPeWsQMnmQy43DSBxAFtHz3l2JX8EWBSGUCvT3CuZ8KsuzbkqMIJMDOxWtG8eZSoCDI04aiVQjWuuV8HmSw==", + "dependencies": { + "Microsoft.Data.Sqlite.Core": "7.0.5", + "SQLitePCLRaw.bundle_e_sqlite3": "2.1.4" + } + }, + "Microsoft.Data.Sqlite.Core": { + "type": "Transitive", + "resolved": "7.0.5", + "contentHash": "FTerRmQPqHrCrnoUzhBu+E+1DNGwyrAMLqHkAqOOOu5pGfyMOj8qQUBxI/gDtWtG11p49UxSfWmBzRNlwZqfUg==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "nOP8R1mVb/6mZtm2qgAJXn/LFm/2kMjHDAg/QJLFG6CuWYJtaD3p1BwQhufBVvRzL9ceJ/xF0SQ0qsI2GkDQAA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "65MrmXCziWaQFrI0UHkQbesrX5wTwf9XPjY5yFm/VkgJKFJ5gqvXRoXjIZcf2wLi5ZlwGz/oMYfyURVCWbM5iw==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "vJ9xvOZCnUAIHcGC3SU35r3HKmHTVIeHzo6u/qzlHAqD8m6xv92MLin4oJntTvkpKxVX3vI1GFFkIQtU3AdlsQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "2.2.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw==" + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "UpZLNLBpIZ0GTebShui7xXYh6DmBHjWM8NxGxZbdQh/bPZ5e6YswqI+bru6BnEL5eWiOdodsXtEz3FROcgi/qg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Primitives": "2.2.0", + "System.ComponentModel.Annotations": "4.5.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "azyQtqbm4fSaDzZHD/J+V6oWMFaf2tWP4WEGIYePLCMw3+b2RQdj9ybgbQyjCshcitQKQ4lEDOZjmSlTTrHxUg==", + "dependencies": { + "System.Memory": "4.5.1", + "System.Runtime.CompilerServices.Unsafe": "4.5.1" + } }, "Microsoft.NETFramework.ReferenceAssemblies.net461": { "type": "Transitive", @@ -206,11 +504,6 @@ "resolved": "8.0.0", "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" }, - "Speckle.DoubleNumerics": { - "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w==" - }, "Speckle.Newtonsoft.Json": { "type": "Transitive", "resolved": "13.0.2", @@ -246,11 +539,6 @@ "SQLitePCLRaw.core": "2.1.4" } }, - "System.Buffers": { - "type": "Transitive", - "resolved": "4.4.0", - "contentHash": "AwarXzzoDwX6BgrhjoJsk6tUezZEozOT5Y9QKF94Gl4JK91I4PIIBkBco9068Y9/Dra8Dkbie99kXB8+1BaYKw==" - }, "System.ComponentModel.Annotations": { "type": "Transitive", "resolved": "4.5.0", @@ -259,65 +547,26 @@ "System.Memory": { "type": "Transitive", "resolved": "4.5.3", - "contentHash": "3oDzvc/zzetpTKWMShs1AADwZjQ/36HnsufHRPcOjyRAAMLDlu2iD33MBI2opxnezcVUtXyqDXXjoFMOU9c7SA==", - "dependencies": { - "System.Buffers": "4.4.0", - "System.Numerics.Vectors": "4.4.0", - "System.Runtime.CompilerServices.Unsafe": "4.5.2" - } - }, - "System.Numerics.Vectors": { - "type": "Transitive", - "resolved": "4.4.0", - "contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ==" + "contentHash": "3oDzvc/zzetpTKWMShs1AADwZjQ/36HnsufHRPcOjyRAAMLDlu2iD33MBI2opxnezcVUtXyqDXXjoFMOU9c7SA==" }, "System.Reactive": { "type": "Transitive", "resolved": "5.0.0", - "contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==", - "dependencies": { - "System.Runtime.InteropServices.WindowsRuntime": "4.3.0", - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, - "System.Runtime": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - } + "contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==" }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", - "resolved": "4.5.3", - "contentHash": "3TIsJhD1EiiT0w2CcDMN/iSSwnNnsrnbzeVHSKkaEgV85txMprmuO+Yq2AdSbeVGcg28pdNDTPK87tJhX7VFHw==" - }, - "System.Runtime.InteropServices.WindowsRuntime": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "J4GUi3xZQLUBasNwZnjrffN8i5wpHrBtZoLG+OhRyGo/+YunMRWWtwoMDlUAIdmX0uRfpHIBDSV6zyr3yf00TA==", - "dependencies": { - "System.Runtime": "4.3.0" - } - }, - "System.Threading.Tasks.Extensions": { - "type": "Transitive", - "resolved": "4.5.4", - "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "4.5.3" - } + "resolved": "4.5.1", + "contentHash": "Zh8t8oqolRaFa9vmOZfdQm/qKejdqz0J9kr7o2Fu0vPeoH3BL1EOXipKWwkWtLT1JPzjByrF19fGuFlNbmPpiw==" }, "speckle.connectors.common": { "type": "Project", "dependencies": { "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", "Speckle.Connectors.Logging": "[1.0.0, )", - "Speckle.Objects": "[3.1.0-dev.191, )", - "Speckle.Sdk": "[3.1.0-dev.191, )", - "Speckle.Sdk.Dependencies": "[3.1.0-dev.191, )" + "Speckle.Objects": "[3.1.1, )", + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" } }, "speckle.connectors.logging": { @@ -350,13 +599,300 @@ "Microsoft.Extensions.Options": "2.2.0" } }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "+m7jRFm0ABbkcSz2UphdxAsislY10IpQ1u79c8a3aVvegLjnsVQZ1sXfRIRO1aFdulkhjYKXYpB3N9M8Z+epgQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.191" + "Speckle.Sdk": "3.1.1" + } + } + }, + "net8.0": { + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Direct", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" + }, + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net461": "1.0.3" + } + }, + "Microsoft.SourceLink.GitHub": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "dependencies": { + "Microsoft.Build.Tasks.Git": "8.0.0", + "Microsoft.SourceLink.Common": "8.0.0" + } + }, + "PolySharp": { + "type": "Direct", + "requested": "[1.14.1, )", + "resolved": "1.14.1", + "contentHash": "mOOmFYwad3MIOL14VCjj02LljyF1GNw1wP0YVlxtcPvqdxjGGMNdNJJxHptlry3MOd8b40Flm8RPOM8JOlN2sQ==" + }, + "Speckle.InterfaceGenerator": { + "type": "Direct", + "requested": "[0.9.6, )", + "resolved": "0.9.6", + "contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w==" + }, + "Speckle.Sdk": { + "type": "Direct", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", + "dependencies": { + "GraphQL.Client": "6.0.0", + "Microsoft.CSharp": "4.7.0", + "Microsoft.Data.Sqlite": "7.0.5", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging": "2.2.0", + "Speckle.DoubleNumerics": "4.1.0", + "Speckle.Newtonsoft.Json": "13.0.2", + "Speckle.Sdk.Dependencies": "3.1.1" + } + }, + "Speckle.Sdk.Dependencies": { + "type": "Direct", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" + }, + "GraphQL.Client": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "8yPNBbuVBpTptivyAlak4GZvbwbUcjeQTL4vN1HKHRuOykZ4r7l5fcLS6vpyPyLn0x8FsL31xbOIKyxbmR9rbA==", + "dependencies": { + "GraphQL.Client.Abstractions": "6.0.0", + "GraphQL.Client.Abstractions.Websocket": "6.0.0", + "System.Reactive": "5.0.0" + } + }, + "GraphQL.Client.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "h7uzWFORHZ+CCjwr/ThAyXMr0DPpzEANDa4Uo54wqCQ+j7qUKwqYTgOrb1W40sqbvNaZm9v/X7It31SUw0maHA==", + "dependencies": { + "GraphQL.Primitives": "6.0.0" + } + }, + "GraphQL.Client.Abstractions.Websocket": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "Nr9bPf8gIOvLuXpqEpqr9z9jslYFJOvd0feHth3/kPqeR3uMbjF5pjiwh4jxyMcxHdr8Pb6QiXkV3hsSyt0v7A==", + "dependencies": { + "GraphQL.Client.Abstractions": "6.0.0" + } + }, + "GraphQL.Primitives": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "yg72rrYDapfsIUrul7aF6wwNnTJBOFvuA9VdDTQpPa8AlAriHbufeXYLBcodKjfUdkCnaiggX1U/nEP08Zb5GA==" + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.CSharp": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==" + }, + "Microsoft.Data.Sqlite": { + "type": "Transitive", + "resolved": "7.0.5", + "contentHash": "KGxbPeWsQMnmQy43DSBxAFtHz3l2JX8EWBSGUCvT3CuZ8KsuzbkqMIJMDOxWtG8eZSoCDI04aiVQjWuuV8HmSw==", + "dependencies": { + "Microsoft.Data.Sqlite.Core": "7.0.5", + "SQLitePCLRaw.bundle_e_sqlite3": "2.1.4" + } + }, + "Microsoft.Data.Sqlite.Core": { + "type": "Transitive", + "resolved": "7.0.5", + "contentHash": "FTerRmQPqHrCrnoUzhBu+E+1DNGwyrAMLqHkAqOOOu5pGfyMOj8qQUBxI/gDtWtG11p49UxSfWmBzRNlwZqfUg==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "nOP8R1mVb/6mZtm2qgAJXn/LFm/2kMjHDAg/QJLFG6CuWYJtaD3p1BwQhufBVvRzL9ceJ/xF0SQ0qsI2GkDQAA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "65MrmXCziWaQFrI0UHkQbesrX5wTwf9XPjY5yFm/VkgJKFJ5gqvXRoXjIZcf2wLi5ZlwGz/oMYfyURVCWbM5iw==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "vJ9xvOZCnUAIHcGC3SU35r3HKmHTVIeHzo6u/qzlHAqD8m6xv92MLin4oJntTvkpKxVX3vI1GFFkIQtU3AdlsQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "2.2.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw==" + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "UpZLNLBpIZ0GTebShui7xXYh6DmBHjWM8NxGxZbdQh/bPZ5e6YswqI+bru6BnEL5eWiOdodsXtEz3FROcgi/qg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Primitives": "2.2.0", + "System.ComponentModel.Annotations": "4.5.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "azyQtqbm4fSaDzZHD/J+V6oWMFaf2tWP4WEGIYePLCMw3+b2RQdj9ybgbQyjCshcitQKQ4lEDOZjmSlTTrHxUg==", + "dependencies": { + "System.Memory": "4.5.1", + "System.Runtime.CompilerServices.Unsafe": "4.5.1" + } + }, + "Microsoft.NETFramework.ReferenceAssemblies.net461": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "AmOJZwCqnOCNp6PPcf9joyogScWLtwy0M1WkqfEQ0M9nYwyDD7EX9ZjscKS5iYnyvteX7kzSKFCKt9I9dXA6mA==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "Speckle.Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.2", + "contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA==" + }, + "SQLitePCLRaw.bundle_e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "EWI1olKDjFEBMJu0+3wuxwziIAdWDVMYLhuZ3Qs84rrz+DHwD00RzWPZCa+bLnHCf3oJwuFZIRsHT5p236QXww==", + "dependencies": { + "SQLitePCLRaw.lib.e_sqlite3": "2.1.4", + "SQLitePCLRaw.provider.e_sqlite3": "2.1.4" + } + }, + "SQLitePCLRaw.core": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "inBjvSHo9UDKneGNzfUfDjK08JzlcIhn1+SP5Y3m6cgXpCxXKCJDy6Mka7LpgSV+UZmKSnC8rTwB0SQ0xKu5pA==", + "dependencies": { + "System.Memory": "4.5.3" + } + }, + "SQLitePCLRaw.lib.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "2C9Q9eX7CPLveJA0rIhf9RXAvu+7nWZu1A2MdG6SD/NOu26TakGgL1nsbc0JAspGijFOo3HoN79xrx8a368fBg==" + }, + "SQLitePCLRaw.provider.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "CSlb5dUp1FMIkez9Iv5EXzpeq7rHryVNqwJMWnpq87j9zWZexaEMdisDktMsnnrzKM6ahNrsTkjqNodTBPBxtQ==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "System.ComponentModel.Annotations": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "UxYQ3FGUOtzJ7LfSdnYSFd7+oEv6M8NgUatatIN2HxNtDdlcvFAf+VIq4Of9cDMJEJC0aSRv/x898RYhB4Yppg==" + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.3", + "contentHash": "3oDzvc/zzetpTKWMShs1AADwZjQ/36HnsufHRPcOjyRAAMLDlu2iD33MBI2opxnezcVUtXyqDXXjoFMOU9c7SA==" + }, + "System.Reactive": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==" + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Zh8t8oqolRaFa9vmOZfdQm/qKejdqz0J9kr7o2Fu0vPeoH3BL1EOXipKWwkWtLT1JPzjByrF19fGuFlNbmPpiw==" + }, + "speckle.connectors.common": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", + "Speckle.Connectors.Logging": "[1.0.0, )", + "Speckle.Objects": "[3.1.1, )", + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" + } + }, + "speckle.connectors.logging": { + "type": "Project" + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "MZtBIwfDFork5vfjpJdG5g8wuJFt7d/y3LOSVVtDK/76wlbtz6cjltfKHqLx2TKVqTj5/c41t77m1+h20zqtPA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0" + } + }, + "Microsoft.Extensions.Logging": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "Nxqhadc9FCmFHzU+fz3oc8sFlE6IadViYg8dfUdGzJZ2JUxnCsRghBhhOWdM4B2zSZqEc+0BjliBh/oNdRZuig==", + "dependencies": { + "Microsoft.Extensions.Configuration.Binder": "2.2.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging.Abstractions": "2.2.0", + "Microsoft.Extensions.Options": "2.2.0" + } + }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, + "Speckle.Objects": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", + "dependencies": { + "Speckle.Sdk": "3.1.1" } } } diff --git a/Directory.Build.props b/Directory.Build.props index 95118439f..e3bbba7e2 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -11,7 +11,7 @@ true false - + true latest-AllEnabledByDefault @@ -19,7 +19,7 @@ true false true - + @@ -43,19 +43,19 @@ git $(MSBuildThisFileDirectory) - + - + - + true - + true $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb diff --git a/Directory.Build.targets b/Directory.Build.targets deleted file mode 100644 index cabf959a2..000000000 --- a/Directory.Build.targets +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Directory.Packages.props b/Directory.Packages.props index 38d1a9e07..8d31f6f34 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -1,5 +1,8 @@ + + + @@ -8,6 +11,8 @@ + + @@ -22,13 +27,15 @@ + + + - - - + + @@ -37,15 +44,15 @@ + - - - + + + - diff --git a/Importers/Ifc/Speckle.Importers.Ifc.Tester/DummySendCacheManager.cs b/Importers/Ifc/Speckle.Importers.Ifc.Tester/DummySendCacheManager.cs new file mode 100644 index 000000000..10f5d8454 --- /dev/null +++ b/Importers/Ifc/Speckle.Importers.Ifc.Tester/DummySendCacheManager.cs @@ -0,0 +1,28 @@ +using Speckle.Sdk.SQLite; + +namespace Speckle.Importers.Ifc.Tester; + +public sealed class DummySendCacheManager(Dictionary objects) : ISqLiteJsonCacheManager +{ + public void Dispose() { } + + public IReadOnlyCollection<(string, string)> GetAllObjects() => throw new NotImplementedException(); + + public void DeleteObject(string id) => throw new NotImplementedException(); + + public string? GetObject(string id) => null; + + public void SaveObject(string id, string json) => throw new NotImplementedException(); + + public bool HasObject(string objectId) => false; + + public void SaveObjects(IEnumerable<(string id, string json)> items) + { + foreach (var (id, json) in items) + { + objects[id] = json; + } + } + + public void UpdateObject(string id, string json) => throw new NotImplementedException(); +} diff --git a/Importers/Ifc/Speckle.Importers.Ifc.Tester/DummyServerObjectManager.cs b/Importers/Ifc/Speckle.Importers.Ifc.Tester/DummyServerObjectManager.cs new file mode 100644 index 000000000..0adb1a878 --- /dev/null +++ b/Importers/Ifc/Speckle.Importers.Ifc.Tester/DummyServerObjectManager.cs @@ -0,0 +1,43 @@ +using System.Text; +using Speckle.Sdk.Serialisation.V2; +using Speckle.Sdk.Serialisation.V2.Send; +using Speckle.Sdk.Transports; + +namespace Speckle.Importers.Ifc.Tester; + +public class DummyServerObjectManager : IServerObjectManager +{ + public IAsyncEnumerable<(string, string)> DownloadObjects( + IReadOnlyCollection objectIds, + IProgress? progress, + CancellationToken cancellationToken + ) => throw new NotImplementedException(); + + public Task DownloadSingleObject( + string objectId, + IProgress? progress, + CancellationToken cancellationToken + ) => throw new NotImplementedException(); + + public Task> HasObjects( + IReadOnlyCollection objectIds, + CancellationToken cancellationToken + ) => Task.FromResult(objectIds.ToDictionary(id => id, id => false)); + + public Task UploadObjects( + IReadOnlyList objects, + bool compressPayloads, + IProgress? progress, + CancellationToken cancellationToken + ) + { + long totalBytes = 0; + foreach (var item in objects) + { + totalBytes += Encoding.Default.GetByteCount(item.Json.Value); + } + + progress?.Report(new(ProgressEvent.UploadBytes, totalBytes, totalBytes)); + return Task.CompletedTask; + } +} diff --git a/Importers/Ifc/Speckle.Importers.Ifc.Tester/Program.cs b/Importers/Ifc/Speckle.Importers.Ifc.Tester/Program.cs new file mode 100644 index 000000000..953f0cce7 --- /dev/null +++ b/Importers/Ifc/Speckle.Importers.Ifc.Tester/Program.cs @@ -0,0 +1,68 @@ +#pragma warning disable CA1506 +using System.Diagnostics; +using Ara3D.Utils; +//using JetBrains.Profiler.SelfApi; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging.Abstractions; +using Speckle.Importers.Ifc; +using Speckle.Importers.Ifc.Ara3D.IfcParser; +using Speckle.Importers.Ifc.Converters; +using Speckle.Importers.Ifc.Tester; +using Speckle.Importers.Ifc.Types; +using Speckle.Sdk.Serialisation.V2.Send; +using Speckle.Sdk.SQLite; + +var serviceProvider = Import.GetServiceProvider(); + +//DotMemory.Init(); +var filePath = new FilePath( + //"C:\\Users\\adam\\Git\\speckle-server\\packages\\fileimport-service\\ifc-dotnet\\ifcs\\20210221PRIMARK.ifc" + //"C:\\Users\\adam\\Git\\speckle-server\\packages\\fileimport-service\\ifc-dotnet\\ifcs\\231110ADT-FZK-Haus-2005-2006.ifc" + //"C:\\Users\\adam\\Downloads\\T03PV06IMPMI01C.ifc" + "C:\\Users\\adam\\Downloads\\20231128_HW_Bouwkosten.ifc" +); + +var ifcFactory = serviceProvider.GetRequiredService(); +var stopwatch = Stopwatch.StartNew(); + +Console.WriteLine($"Opening with WebIFC: {filePath}"); +var model = ifcFactory.Open(filePath); +var ms = stopwatch.ElapsedMilliseconds; +Console.WriteLine($"Opened with WebIFC: {ms} ms"); + +var graph = IfcGraph.Load(new FilePath(filePath)); +var ms2 = stopwatch.ElapsedMilliseconds; +Console.WriteLine($"Loaded with StepParser: {ms2 - ms} ms"); + +var converter = serviceProvider.GetRequiredService(); +var b = converter.Convert(model, graph); +ms = ms2; +ms2 = stopwatch.ElapsedMilliseconds; +Console.WriteLine($"Converted to Speckle Bases: {ms2 - ms} ms"); + +var cache = $"C:\\Users\\adam\\Git\\temp\\{Guid.NewGuid()}.db"; +using var sqlite = new SqLiteJsonCacheManager($"Data Source={cache};", 2); +await using var process2 = new SerializeProcess( + new Progress(true), + sqlite, + new DummyServerObjectManager(), + new BaseChildFinder(new BasePropertyGatherer()), + new BaseSerializer(sqlite, new ObjectSerializerFactory(new BasePropertyGatherer())), + new NullLoggerFactory(), + default, + new SerializeProcessOptions(SkipServer: true) +); +Console.WriteLine($"Caching to Speckle: {cache}"); + +/*var config = new DotMemory.Config(); +config.OpenDotMemory(); +config.SaveToDir("C:\\Users\\adam\\dotTraceSnapshots"); +DotMemory.Attach(config); +DotMemory.GetSnapshot("Before");*/ +var (rootId, _) = await process2.Serialize(b).ConfigureAwait(false); +Console.WriteLine(rootId); +ms2 = stopwatch.ElapsedMilliseconds; +Console.WriteLine($"Converted to JSON: {ms2 - ms} ms"); +//DotMemory.GetSnapshot("After"); +//DotMemory.Detach(); +#pragma warning restore CA1506 diff --git a/Importers/Ifc/Speckle.Importers.Ifc.Tester/Progress.cs b/Importers/Ifc/Speckle.Importers.Ifc.Tester/Progress.cs new file mode 100644 index 000000000..0601c31f7 --- /dev/null +++ b/Importers/Ifc/Speckle.Importers.Ifc.Tester/Progress.cs @@ -0,0 +1,36 @@ +using Speckle.Sdk.Transports; + +namespace Speckle.Importers.Ifc.Tester; + +public class Progress(bool write) : IProgress +{ + private readonly TimeSpan _debounce = TimeSpan.FromMilliseconds(1000); + private DateTime _lastTime = DateTime.UtcNow; + + private long _totalBytes; + + public void Report(ProgressArgs value) + { + if (write) + { + if (value.ProgressEvent == ProgressEvent.DownloadBytes) + { + Interlocked.Add(ref _totalBytes, value.Count); + } + var now = DateTime.UtcNow; + if (now - _lastTime >= _debounce) + { + if (value.ProgressEvent == ProgressEvent.DownloadBytes) + { + Console.WriteLine(value.ProgressEvent + " t " + _totalBytes); + } + else + { + Console.WriteLine(value.ProgressEvent + " c " + value.Count + " t " + value.Total); + } + + _lastTime = now; + } + } + } +} diff --git a/Importers/Ifc/Speckle.Importers.Ifc.Tester/Speckle.Importers.Ifc.Tester.csproj b/Importers/Ifc/Speckle.Importers.Ifc.Tester/Speckle.Importers.Ifc.Tester.csproj new file mode 100644 index 000000000..17b459a6c --- /dev/null +++ b/Importers/Ifc/Speckle.Importers.Ifc.Tester/Speckle.Importers.Ifc.Tester.csproj @@ -0,0 +1,13 @@ + + + + Exe + net8.0 + Debug;Release;Local + + + + + + + diff --git a/Importers/Ifc/Speckle.Importers.Ifc.Tester/packages.lock.json b/Importers/Ifc/Speckle.Importers.Ifc.Tester/packages.lock.json new file mode 100644 index 000000000..95e5a1ef9 --- /dev/null +++ b/Importers/Ifc/Speckle.Importers.Ifc.Tester/packages.lock.json @@ -0,0 +1,308 @@ +{ + "version": 2, + "dependencies": { + "net8.0": { + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net461": "1.0.3" + } + }, + "Microsoft.SourceLink.GitHub": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "dependencies": { + "Microsoft.Build.Tasks.Git": "8.0.0", + "Microsoft.SourceLink.Common": "8.0.0" + } + }, + "PolySharp": { + "type": "Direct", + "requested": "[1.14.1, )", + "resolved": "1.14.1", + "contentHash": "mOOmFYwad3MIOL14VCjj02LljyF1GNw1wP0YVlxtcPvqdxjGGMNdNJJxHptlry3MOd8b40Flm8RPOM8JOlN2sQ==" + }, + "Speckle.InterfaceGenerator": { + "type": "Direct", + "requested": "[0.9.6, )", + "resolved": "0.9.6", + "contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w==" + }, + "GraphQL.Client": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "8yPNBbuVBpTptivyAlak4GZvbwbUcjeQTL4vN1HKHRuOykZ4r7l5fcLS6vpyPyLn0x8FsL31xbOIKyxbmR9rbA==", + "dependencies": { + "GraphQL.Client.Abstractions": "6.0.0", + "GraphQL.Client.Abstractions.Websocket": "6.0.0", + "System.Reactive": "5.0.0" + } + }, + "GraphQL.Client.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "h7uzWFORHZ+CCjwr/ThAyXMr0DPpzEANDa4Uo54wqCQ+j7qUKwqYTgOrb1W40sqbvNaZm9v/X7It31SUw0maHA==", + "dependencies": { + "GraphQL.Primitives": "6.0.0" + } + }, + "GraphQL.Client.Abstractions.Websocket": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "Nr9bPf8gIOvLuXpqEpqr9z9jslYFJOvd0feHth3/kPqeR3uMbjF5pjiwh4jxyMcxHdr8Pb6QiXkV3hsSyt0v7A==", + "dependencies": { + "GraphQL.Client.Abstractions": "6.0.0" + } + }, + "GraphQL.Primitives": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "yg72rrYDapfsIUrul7aF6wwNnTJBOFvuA9VdDTQpPa8AlAriHbufeXYLBcodKjfUdkCnaiggX1U/nEP08Zb5GA==" + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.CSharp": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==" + }, + "Microsoft.Data.Sqlite": { + "type": "Transitive", + "resolved": "7.0.5", + "contentHash": "KGxbPeWsQMnmQy43DSBxAFtHz3l2JX8EWBSGUCvT3CuZ8KsuzbkqMIJMDOxWtG8eZSoCDI04aiVQjWuuV8HmSw==", + "dependencies": { + "Microsoft.Data.Sqlite.Core": "7.0.5", + "SQLitePCLRaw.bundle_e_sqlite3": "2.1.4" + } + }, + "Microsoft.Data.Sqlite.Core": { + "type": "Transitive", + "resolved": "7.0.5", + "contentHash": "FTerRmQPqHrCrnoUzhBu+E+1DNGwyrAMLqHkAqOOOu5pGfyMOj8qQUBxI/gDtWtG11p49UxSfWmBzRNlwZqfUg==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "nOP8R1mVb/6mZtm2qgAJXn/LFm/2kMjHDAg/QJLFG6CuWYJtaD3p1BwQhufBVvRzL9ceJ/xF0SQ0qsI2GkDQAA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "65MrmXCziWaQFrI0UHkQbesrX5wTwf9XPjY5yFm/VkgJKFJ5gqvXRoXjIZcf2wLi5ZlwGz/oMYfyURVCWbM5iw==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "vJ9xvOZCnUAIHcGC3SU35r3HKmHTVIeHzo6u/qzlHAqD8m6xv92MLin4oJntTvkpKxVX3vI1GFFkIQtU3AdlsQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "2.2.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw==" + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "UpZLNLBpIZ0GTebShui7xXYh6DmBHjWM8NxGxZbdQh/bPZ5e6YswqI+bru6BnEL5eWiOdodsXtEz3FROcgi/qg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Primitives": "2.2.0", + "System.ComponentModel.Annotations": "4.5.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "azyQtqbm4fSaDzZHD/J+V6oWMFaf2tWP4WEGIYePLCMw3+b2RQdj9ybgbQyjCshcitQKQ4lEDOZjmSlTTrHxUg==", + "dependencies": { + "System.Memory": "4.5.1", + "System.Runtime.CompilerServices.Unsafe": "4.5.1" + } + }, + "Microsoft.NETFramework.ReferenceAssemblies.net461": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "AmOJZwCqnOCNp6PPcf9joyogScWLtwy0M1WkqfEQ0M9nYwyDD7EX9ZjscKS5iYnyvteX7kzSKFCKt9I9dXA6mA==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "Speckle.Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.2", + "contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA==" + }, + "SQLitePCLRaw.bundle_e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "EWI1olKDjFEBMJu0+3wuxwziIAdWDVMYLhuZ3Qs84rrz+DHwD00RzWPZCa+bLnHCf3oJwuFZIRsHT5p236QXww==", + "dependencies": { + "SQLitePCLRaw.lib.e_sqlite3": "2.1.4", + "SQLitePCLRaw.provider.e_sqlite3": "2.1.4" + } + }, + "SQLitePCLRaw.core": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "inBjvSHo9UDKneGNzfUfDjK08JzlcIhn1+SP5Y3m6cgXpCxXKCJDy6Mka7LpgSV+UZmKSnC8rTwB0SQ0xKu5pA==", + "dependencies": { + "System.Memory": "4.5.3" + } + }, + "SQLitePCLRaw.lib.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "2C9Q9eX7CPLveJA0rIhf9RXAvu+7nWZu1A2MdG6SD/NOu26TakGgL1nsbc0JAspGijFOo3HoN79xrx8a368fBg==" + }, + "SQLitePCLRaw.provider.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "CSlb5dUp1FMIkez9Iv5EXzpeq7rHryVNqwJMWnpq87j9zWZexaEMdisDktMsnnrzKM6ahNrsTkjqNodTBPBxtQ==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "System.ComponentModel.Annotations": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "UxYQ3FGUOtzJ7LfSdnYSFd7+oEv6M8NgUatatIN2HxNtDdlcvFAf+VIq4Of9cDMJEJC0aSRv/x898RYhB4Yppg==" + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==" + }, + "System.Reactive": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==" + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Zh8t8oqolRaFa9vmOZfdQm/qKejdqz0J9kr7o2Fu0vPeoH3BL1EOXipKWwkWtLT1JPzjByrF19fGuFlNbmPpiw==" + }, + "speckle.importers.ifc": { + "type": "Project", + "dependencies": { + "Ara3D.Buffers": "[1.4.5, )", + "Ara3D.Logging": "[1.4.5, )", + "Ara3D.Utils": "[1.4.5, )", + "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", + "Speckle.Objects": "[3.1.1, )", + "Speckle.Sdk": "[3.1.1, )" + } + }, + "Ara3D.Buffers": { + "type": "CentralTransitive", + "requested": "[1.4.5, )", + "resolved": "1.4.5", + "contentHash": "SKcQqgtXukyHTlTKFPCaUW4spSkue3XfBU/GmoA7KhH6H995v6TbJxtqjs0EfSgnXEkajL8U7X1NqktScRozXw==", + "dependencies": { + "System.Memory": "4.5.5" + } + }, + "Ara3D.Logging": { + "type": "CentralTransitive", + "requested": "[1.4.5, )", + "resolved": "1.4.5", + "contentHash": "7HPCe5Dq21JoOBF1iclk9H37XFCoB2ZzCPqTMNgdg4PWFvuRsofNbiuMdiE/HKgMHCVhy1C5opB2KwDKcO7Axw==", + "dependencies": { + "Ara3D.Utils": "1.4.5" + } + }, + "Ara3D.Utils": { + "type": "CentralTransitive", + "requested": "[1.4.5, )", + "resolved": "1.4.5", + "contentHash": "yba/E7PpbWP0+RDp+KbKw/vBXnXBSIheScdpVKuDnr8ytRg8pZ2Jd6nwKES+G0FcVEB9PeOVmEW7SGrFvAwRCg==" + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "MZtBIwfDFork5vfjpJdG5g8wuJFt7d/y3LOSVVtDK/76wlbtz6cjltfKHqLx2TKVqTj5/c41t77m1+h20zqtPA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0" + } + }, + "Microsoft.Extensions.Logging": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "Nxqhadc9FCmFHzU+fz3oc8sFlE6IadViYg8dfUdGzJZ2JUxnCsRghBhhOWdM4B2zSZqEc+0BjliBh/oNdRZuig==", + "dependencies": { + "Microsoft.Extensions.Configuration.Binder": "2.2.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging.Abstractions": "2.2.0", + "Microsoft.Extensions.Options": "2.2.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" + }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, + "Speckle.Objects": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", + "dependencies": { + "Speckle.Sdk": "3.1.1" + } + }, + "Speckle.Sdk": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", + "dependencies": { + "GraphQL.Client": "6.0.0", + "Microsoft.CSharp": "4.7.0", + "Microsoft.Data.Sqlite": "7.0.5", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging": "2.2.0", + "Speckle.DoubleNumerics": "4.1.0", + "Speckle.Newtonsoft.Json": "13.0.2", + "Speckle.Sdk.Dependencies": "3.1.1" + } + }, + "Speckle.Sdk.Dependencies": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" + } + } + } +} \ No newline at end of file diff --git a/Importers/Ifc/Speckle.Importers.Ifc.Tester2/Program.cs b/Importers/Ifc/Speckle.Importers.Ifc.Tester2/Program.cs new file mode 100644 index 000000000..a49b5d96a --- /dev/null +++ b/Importers/Ifc/Speckle.Importers.Ifc.Tester2/Program.cs @@ -0,0 +1,34 @@ +using Ara3D.Utils; +using Microsoft.Extensions.DependencyInjection; +using Speckle.Importers.Ifc; +using Speckle.Importers.Ifc.Ara3D.IfcParser; +using Speckle.Importers.Ifc.Converters; +using Speckle.Importers.Ifc.Tester2; +using Speckle.Importers.Ifc.Types; +using Speckle.Objects.Geometry; +using Speckle.Sdk.Host; +using Speckle.Sdk.Models; + +// Settings +// var filePath = new FilePath(@"C:\Users\Jedd\Desktop\GRAPHISOFT_Archicad_Sample_Project-S-Office_v1.0_AC25.ifc"); +var filePath = new FilePath(@"C:\Users\Jedd\Desktop\BIM_Projekt_Golden_Nugget-Architektur_und_Ingenieurbau.ifc"); +const string PROJECT_ID = "f3a42bdf24"; + +// Setup +TypeLoader.Initialize(typeof(Base).Assembly, typeof(Point).Assembly); + +var serviceCollection = new ServiceCollection(); +serviceCollection.AddIFCImporter(); +serviceCollection.AddSingleton(); +var serviceProvider = serviceCollection.BuildServiceProvider(); + +// Convert IFC to Speckle Objects +var ifcFactory = serviceProvider.GetRequiredService(); +var model = ifcFactory.Open(filePath); +var graph = IfcGraph.Load(new FilePath(filePath)); +var converter = serviceProvider.GetRequiredService(); +var b = converter.Convert(model, graph); + +//Send Speckle Objects to server +var sender = serviceProvider.GetRequiredService(); +await sender.Send(b, PROJECT_ID); diff --git a/Importers/Ifc/Speckle.Importers.Ifc.Tester2/Sender.cs b/Importers/Ifc/Speckle.Importers.Ifc.Tester2/Sender.cs new file mode 100644 index 000000000..62f83d600 --- /dev/null +++ b/Importers/Ifc/Speckle.Importers.Ifc.Tester2/Sender.cs @@ -0,0 +1,48 @@ +using Speckle.Sdk.Api; +using Speckle.Sdk.Api.GraphQL.Inputs; +using Speckle.Sdk.Api.GraphQL.Models; +using Speckle.Sdk.Common; +using Speckle.Sdk.Credentials; +using Speckle.Sdk.Models; +using Version = Speckle.Sdk.Api.GraphQL.Models.Version; + +namespace Speckle.Importers.Ifc.Tester2; + +public class Sender(IClientFactory clientFactory, IAccountManager accountManager, IOperations operations) +{ + public const string MODEL_NAME = "IFC Import"; + + public async Task Send(Base rootObject, string projectId, CancellationToken cancellationToken = default) + { + var account = accountManager.GetDefaultAccount().NotNull(); + using var client = clientFactory.Create(account); + + var res = await operations.Send2( + new(account.serverInfo.url), + projectId, + account.token, + rootObject, + null, + cancellationToken + ); + + var t = await GetOrCreateIfcModel(client, projectId); + + CreateVersionInput input = new(res.RootId, t.id, projectId); + return await client.Version.Create(input, cancellationToken); + } + + public async Task GetOrCreateIfcModel(Client client, string projectId) + { + ProjectModelsFilter filter = new(null, null, null, null, MODEL_NAME, null); + + var existing = await client.Model.GetModels(projectId, 1, modelsFilter: filter); + if (existing.items.Count != 0) + { + return existing.items[0]; + } + + CreateModelInput input = new(MODEL_NAME, null, projectId); + return await client.Model.Create(input); + } +} diff --git a/Importers/Ifc/Speckle.Importers.Ifc.Tester2/Speckle.Importers.Ifc.Tester2.csproj b/Importers/Ifc/Speckle.Importers.Ifc.Tester2/Speckle.Importers.Ifc.Tester2.csproj new file mode 100644 index 000000000..1cf67660b --- /dev/null +++ b/Importers/Ifc/Speckle.Importers.Ifc.Tester2/Speckle.Importers.Ifc.Tester2.csproj @@ -0,0 +1,11 @@ + + + + Exe + net8.0 + + + + + + diff --git a/Importers/Ifc/Speckle.Importers.Ifc.Tester2/packages.lock.json b/Importers/Ifc/Speckle.Importers.Ifc.Tester2/packages.lock.json new file mode 100644 index 000000000..95e5a1ef9 --- /dev/null +++ b/Importers/Ifc/Speckle.Importers.Ifc.Tester2/packages.lock.json @@ -0,0 +1,308 @@ +{ + "version": 2, + "dependencies": { + "net8.0": { + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net461": "1.0.3" + } + }, + "Microsoft.SourceLink.GitHub": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "dependencies": { + "Microsoft.Build.Tasks.Git": "8.0.0", + "Microsoft.SourceLink.Common": "8.0.0" + } + }, + "PolySharp": { + "type": "Direct", + "requested": "[1.14.1, )", + "resolved": "1.14.1", + "contentHash": "mOOmFYwad3MIOL14VCjj02LljyF1GNw1wP0YVlxtcPvqdxjGGMNdNJJxHptlry3MOd8b40Flm8RPOM8JOlN2sQ==" + }, + "Speckle.InterfaceGenerator": { + "type": "Direct", + "requested": "[0.9.6, )", + "resolved": "0.9.6", + "contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w==" + }, + "GraphQL.Client": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "8yPNBbuVBpTptivyAlak4GZvbwbUcjeQTL4vN1HKHRuOykZ4r7l5fcLS6vpyPyLn0x8FsL31xbOIKyxbmR9rbA==", + "dependencies": { + "GraphQL.Client.Abstractions": "6.0.0", + "GraphQL.Client.Abstractions.Websocket": "6.0.0", + "System.Reactive": "5.0.0" + } + }, + "GraphQL.Client.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "h7uzWFORHZ+CCjwr/ThAyXMr0DPpzEANDa4Uo54wqCQ+j7qUKwqYTgOrb1W40sqbvNaZm9v/X7It31SUw0maHA==", + "dependencies": { + "GraphQL.Primitives": "6.0.0" + } + }, + "GraphQL.Client.Abstractions.Websocket": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "Nr9bPf8gIOvLuXpqEpqr9z9jslYFJOvd0feHth3/kPqeR3uMbjF5pjiwh4jxyMcxHdr8Pb6QiXkV3hsSyt0v7A==", + "dependencies": { + "GraphQL.Client.Abstractions": "6.0.0" + } + }, + "GraphQL.Primitives": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "yg72rrYDapfsIUrul7aF6wwNnTJBOFvuA9VdDTQpPa8AlAriHbufeXYLBcodKjfUdkCnaiggX1U/nEP08Zb5GA==" + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.CSharp": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==" + }, + "Microsoft.Data.Sqlite": { + "type": "Transitive", + "resolved": "7.0.5", + "contentHash": "KGxbPeWsQMnmQy43DSBxAFtHz3l2JX8EWBSGUCvT3CuZ8KsuzbkqMIJMDOxWtG8eZSoCDI04aiVQjWuuV8HmSw==", + "dependencies": { + "Microsoft.Data.Sqlite.Core": "7.0.5", + "SQLitePCLRaw.bundle_e_sqlite3": "2.1.4" + } + }, + "Microsoft.Data.Sqlite.Core": { + "type": "Transitive", + "resolved": "7.0.5", + "contentHash": "FTerRmQPqHrCrnoUzhBu+E+1DNGwyrAMLqHkAqOOOu5pGfyMOj8qQUBxI/gDtWtG11p49UxSfWmBzRNlwZqfUg==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "nOP8R1mVb/6mZtm2qgAJXn/LFm/2kMjHDAg/QJLFG6CuWYJtaD3p1BwQhufBVvRzL9ceJ/xF0SQ0qsI2GkDQAA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "65MrmXCziWaQFrI0UHkQbesrX5wTwf9XPjY5yFm/VkgJKFJ5gqvXRoXjIZcf2wLi5ZlwGz/oMYfyURVCWbM5iw==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "vJ9xvOZCnUAIHcGC3SU35r3HKmHTVIeHzo6u/qzlHAqD8m6xv92MLin4oJntTvkpKxVX3vI1GFFkIQtU3AdlsQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "2.2.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw==" + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "UpZLNLBpIZ0GTebShui7xXYh6DmBHjWM8NxGxZbdQh/bPZ5e6YswqI+bru6BnEL5eWiOdodsXtEz3FROcgi/qg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Primitives": "2.2.0", + "System.ComponentModel.Annotations": "4.5.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "azyQtqbm4fSaDzZHD/J+V6oWMFaf2tWP4WEGIYePLCMw3+b2RQdj9ybgbQyjCshcitQKQ4lEDOZjmSlTTrHxUg==", + "dependencies": { + "System.Memory": "4.5.1", + "System.Runtime.CompilerServices.Unsafe": "4.5.1" + } + }, + "Microsoft.NETFramework.ReferenceAssemblies.net461": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "AmOJZwCqnOCNp6PPcf9joyogScWLtwy0M1WkqfEQ0M9nYwyDD7EX9ZjscKS5iYnyvteX7kzSKFCKt9I9dXA6mA==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "Speckle.Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.2", + "contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA==" + }, + "SQLitePCLRaw.bundle_e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "EWI1olKDjFEBMJu0+3wuxwziIAdWDVMYLhuZ3Qs84rrz+DHwD00RzWPZCa+bLnHCf3oJwuFZIRsHT5p236QXww==", + "dependencies": { + "SQLitePCLRaw.lib.e_sqlite3": "2.1.4", + "SQLitePCLRaw.provider.e_sqlite3": "2.1.4" + } + }, + "SQLitePCLRaw.core": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "inBjvSHo9UDKneGNzfUfDjK08JzlcIhn1+SP5Y3m6cgXpCxXKCJDy6Mka7LpgSV+UZmKSnC8rTwB0SQ0xKu5pA==", + "dependencies": { + "System.Memory": "4.5.3" + } + }, + "SQLitePCLRaw.lib.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "2C9Q9eX7CPLveJA0rIhf9RXAvu+7nWZu1A2MdG6SD/NOu26TakGgL1nsbc0JAspGijFOo3HoN79xrx8a368fBg==" + }, + "SQLitePCLRaw.provider.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "CSlb5dUp1FMIkez9Iv5EXzpeq7rHryVNqwJMWnpq87j9zWZexaEMdisDktMsnnrzKM6ahNrsTkjqNodTBPBxtQ==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "System.ComponentModel.Annotations": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "UxYQ3FGUOtzJ7LfSdnYSFd7+oEv6M8NgUatatIN2HxNtDdlcvFAf+VIq4Of9cDMJEJC0aSRv/x898RYhB4Yppg==" + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==" + }, + "System.Reactive": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==" + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Zh8t8oqolRaFa9vmOZfdQm/qKejdqz0J9kr7o2Fu0vPeoH3BL1EOXipKWwkWtLT1JPzjByrF19fGuFlNbmPpiw==" + }, + "speckle.importers.ifc": { + "type": "Project", + "dependencies": { + "Ara3D.Buffers": "[1.4.5, )", + "Ara3D.Logging": "[1.4.5, )", + "Ara3D.Utils": "[1.4.5, )", + "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", + "Speckle.Objects": "[3.1.1, )", + "Speckle.Sdk": "[3.1.1, )" + } + }, + "Ara3D.Buffers": { + "type": "CentralTransitive", + "requested": "[1.4.5, )", + "resolved": "1.4.5", + "contentHash": "SKcQqgtXukyHTlTKFPCaUW4spSkue3XfBU/GmoA7KhH6H995v6TbJxtqjs0EfSgnXEkajL8U7X1NqktScRozXw==", + "dependencies": { + "System.Memory": "4.5.5" + } + }, + "Ara3D.Logging": { + "type": "CentralTransitive", + "requested": "[1.4.5, )", + "resolved": "1.4.5", + "contentHash": "7HPCe5Dq21JoOBF1iclk9H37XFCoB2ZzCPqTMNgdg4PWFvuRsofNbiuMdiE/HKgMHCVhy1C5opB2KwDKcO7Axw==", + "dependencies": { + "Ara3D.Utils": "1.4.5" + } + }, + "Ara3D.Utils": { + "type": "CentralTransitive", + "requested": "[1.4.5, )", + "resolved": "1.4.5", + "contentHash": "yba/E7PpbWP0+RDp+KbKw/vBXnXBSIheScdpVKuDnr8ytRg8pZ2Jd6nwKES+G0FcVEB9PeOVmEW7SGrFvAwRCg==" + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "MZtBIwfDFork5vfjpJdG5g8wuJFt7d/y3LOSVVtDK/76wlbtz6cjltfKHqLx2TKVqTj5/c41t77m1+h20zqtPA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0" + } + }, + "Microsoft.Extensions.Logging": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "Nxqhadc9FCmFHzU+fz3oc8sFlE6IadViYg8dfUdGzJZ2JUxnCsRghBhhOWdM4B2zSZqEc+0BjliBh/oNdRZuig==", + "dependencies": { + "Microsoft.Extensions.Configuration.Binder": "2.2.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging.Abstractions": "2.2.0", + "Microsoft.Extensions.Options": "2.2.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" + }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, + "Speckle.Objects": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", + "dependencies": { + "Speckle.Sdk": "3.1.1" + } + }, + "Speckle.Sdk": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", + "dependencies": { + "GraphQL.Client": "6.0.0", + "Microsoft.CSharp": "4.7.0", + "Microsoft.Data.Sqlite": "7.0.5", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging": "2.2.0", + "Speckle.DoubleNumerics": "4.1.0", + "Speckle.Newtonsoft.Json": "13.0.2", + "Speckle.Sdk.Dependencies": "3.1.1" + } + }, + "Speckle.Sdk.Dependencies": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" + } + } + } +} \ No newline at end of file diff --git a/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.IfcParser/Extensions.cs b/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.IfcParser/Extensions.cs new file mode 100644 index 000000000..5a49f4328 --- /dev/null +++ b/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.IfcParser/Extensions.cs @@ -0,0 +1,190 @@ +using System.Text; +using Ara3D.Buffers; +using Speckle.Importers.Ifc.Ara3D.StepParser; + +namespace Speckle.Importers.Ifc.Ara3D.IfcParser; + +public static class IfcExtensions +{ + public static uint AsId(this StepValue value) => value is StepUnassigned ? 0u : ((StepId)value).Id; + + public static string AsString(this StepValue value) => + value is StepString ss + ? ss.AsString() + : value is StepNumber sn + ? sn.Value.ToString() + : value is StepId si + ? si.Id.ToString() + : value is StepSymbol ssm + ? ssm.Name.ToString() + : ""; + + public static double AsNumber(this StepValue value) => value is StepUnassigned ? 0 : ((StepNumber)value).Value; + + public static List AsList(this StepValue value) => + value is StepUnassigned ? new List() : ((StepList)value).Values; + + public static List AsIdList(this StepValue value) => + value is StepUnassigned ? new List() : value.AsList().Select(AsId).ToList(); + + // Uses Latin1 encoding (aka ISO-8859-1) + // Extended characters converted using an IFC specific system + public static string AsString(this ByteSpan span) => Encoding.Latin1.GetString(span.ToSpan()).IfcToUnicode(); + + // https://technical.buildingsmart.org/resources/ifcimplementationguidance/string-encoding/ + public static string IfcToUnicode(this string input) + { + if (!input.Contains('\\')) + return input; + + var output = new StringBuilder(); + var i = 0; + var length = input.Length; + while (i < length) + { + if (input[i] != '\\') + { + // Regular character, append to output + output.Append(input[i++]); + continue; + } + + i++; // Move past '\' + if (i >= length) + { + output.Append('\\'); + break; + } + + var escapeChar = input[i++]; + + if (escapeChar == 'S' && i < length && input[i] == '\\') + { + i++; // Move past '\' + if (i < length) + { + var c = input[i++]; + var code = c + 128; + output.Append((char)code); + } + else + { + output.Append("\\S\\"); + } + continue; + } + + if (escapeChar == 'X') + { + if (i < length && input[i] == '\\') + { + // Handle \X\XX escape sequence (8-bit character code) + i++; // Move past '\' + if (i + 1 < length) + { + var hex = input.Substring(i, 2); + i += 2; + var code = Convert.ToInt32(hex, 16); + output.Append((char)code); + } + else + { + output.Append("\\X\\"); + } + + continue; + } + + // Handle extended \Xn\...\X0\ escape sequence + // Skip 'n' until the next '\' + while (i < length && input[i] != '\\') + i++; + if (i < length) + i++; // Move past '\' + + // Collect hex digits until '\X0\' + var hexDigits = new StringBuilder(); + while (i + 3 <= length && input.Substring(i, 3) != "\\X0") + { + hexDigits.Append(input[i++]); + } + + if (i + 3 <= length && input.Substring(i, 3) == "\\X0") + { + i += 3; // Move past '\X0' + if (i < length && input[i] == '\\') + i++; // Move past '\' + + var hexStr = hexDigits.ToString(); + + // Process hex digits in chunks of 4 (representing Unicode code points) + for (var k = 0; k + 4 <= hexStr.Length; k += 4) + { + var codeHex = hexStr.Substring(k, 4); + var code = Convert.ToInt32(codeHex, 16); + output.Append(char.ConvertFromUtf32(code)); + } + continue; + } + + // Invalid format, append as is + output.Append("\\X"); + continue; + } + + // Unrecognized escape sequence, append as is + output.Append('\\').Append(escapeChar); + } + + return output.ToString(); + } + + public static string AsString(this StepString ss) => ss.Value.AsString(); + + public static object? ToJsonObject(this StepValue sv) + { + switch (sv) + { + case StepEntity stepEntity: + { + var attr = stepEntity.Attributes; + if (attr.Values.Count == 0) + return stepEntity.ToString(); + + if (attr.Values.Count == 1) + return attr.Values[0].ToJsonObject(); + + return attr.Values.Select(ToJsonObject).ToList(); + } + + case StepId stepId: + return stepId.Id; + + case StepList stepList: + return stepList.Values.Select(ToJsonObject).ToList(); + + case StepNumber stepNumber: + return stepNumber.AsNumber(); + + case StepRedeclared stepRedeclared: + return null; + + case StepString stepString: + return stepString.AsString(); + + case StepSymbol stepSymbol: + var tmp = stepSymbol.Name.AsString(); + if (tmp == "T") + return true; + if (tmp == "F") + return false; + return tmp; + + case StepUnassigned stepUnassigned: + return null; + + default: + throw new ArgumentOutOfRangeException(nameof(sv)); + } + } +} diff --git a/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.IfcParser/IfcEntity.cs b/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.IfcParser/IfcEntity.cs new file mode 100644 index 000000000..618e7726f --- /dev/null +++ b/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.IfcParser/IfcEntity.cs @@ -0,0 +1,68 @@ +using System.Diagnostics.CodeAnalysis; +using Speckle.Importers.Ifc.Ara3D.IfcParser.Schema; +using Speckle.Importers.Ifc.Ara3D.StepParser; + +namespace Speckle.Importers.Ifc.Ara3D.IfcParser; + +/// +/// It represents an entity definition. It is usually a single line in a STEP file. +/// Many entity definitions are derived from IfcRoot (including relations). +/// IfcRoot has a GUID, OwnerId, optional Name, and optional Description +/// https://iaiweb.lbl.gov/Resources/IFC_Releases/R2x3_final/ifckernel/lexical/ifcroot.htm +/// +public class IfcEntity +{ + public StepInstance LineData { get; } + public IfcGraph Graph { get; } + public uint Id => LineData.Id; + public string Type => LineData?.EntityType ?? ""; + + public IfcEntity(IfcGraph graph, StepInstance lineData) + { + Graph = graph; + LineData = lineData; + } + + public override bool Equals(object? obj) + { + if (obj is IfcEntity other) + return Id == other.Id; + return false; + } + + public override int GetHashCode() => (int)Id; + + public override string ToString() => $"{Type}#{Id}"; + + [MemberNotNullWhen(true, nameof(Guid))] + public bool IsIfcRoot => Count >= 4 && this[0] is StepString && (this[1] is StepId) || (this[1] is StepUnassigned); + + // Modern IFC files conform to this, but older ones have been observed to have different length IDs. + // Leaving as a comment for now. + //&& str.Value.Length == 22; + + public string? Guid => IsIfcRoot ? ((StepString)this[0]).Value.ToString() : null; + + public uint OwnerId => IsIfcRoot ? (this[1] as StepId)?.Id ?? 0 : 0; + + public string? Name => IsIfcRoot ? (this[2] as StepString)?.AsString() : null; + + public string? Description => IsIfcRoot ? (this[3] as StepString)?.AsString() : null; + + public int Count => LineData.Count; + + public StepValue this[int i] => LineData[i]; + + public IReadOnlyList GetOutgoingRelations() => Graph.GetRelationsFrom(Id); + + public IEnumerable GetAggregatedChildren() => + GetOutgoingRelations().OfType().SelectMany(r => r.GetRelatedNodes()); + + public IEnumerable GetSpatialChildren() => + GetOutgoingRelations().OfType().SelectMany(r => r.GetRelatedNodes()); + + public IEnumerable GetChildren() => GetAggregatedChildren().Concat(GetSpatialChildren()).Distinct(); + + public IReadOnlyList GetPropSets() => + Graph.PropertySetsByNode.TryGetValue(Id, out var list) ? list : Array.Empty(); +} diff --git a/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.IfcParser/IfcGraph.cs b/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.IfcParser/IfcGraph.cs new file mode 100644 index 000000000..98fc0e2ed --- /dev/null +++ b/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.IfcParser/IfcGraph.cs @@ -0,0 +1,260 @@ +using System.Diagnostics; +using Ara3D.Logging; +using Ara3D.Utils; +using Speckle.Importers.Ifc.Ara3D.IfcParser.Schema; +using Speckle.Importers.Ifc.Ara3D.StepParser; + +namespace Speckle.Importers.Ifc.Ara3D.IfcParser; + +/// +/// This is a high-level representation of an IFC model as a graph of nodes and relations. +/// It also contains the properties, and property sets. +/// +public sealed class IfcGraph +{ + public static IfcGraph Load(FilePath fp, ILogger? logger = null) => + new IfcGraph(new StepDocument(fp, logger), logger); + + public StepDocument Document { get; } + + public Dictionary Nodes { get; } = new Dictionary(); + public List Relations { get; } = new List(); + public Dictionary> RelationsByNode { get; } = new Dictionary>(); + public Dictionary> PropertySetsByNode { get; } = new Dictionary>(); + + public uint IfcProjectId { get; } + + public IfcNode AddNode(IfcNode n) => Nodes[n.Id] = n; + + public IfcRelation AddRelation(IfcRelation r) + { + Relations.Add(r); + var id = r.From.Id; + if (!RelationsByNode.ContainsKey(id)) + RelationsByNode[id] = new(); + RelationsByNode[id].Add(r); + return r; + } + + public IfcGraph(StepDocument d, ILogger? logger = null) + { + Document = d; + + uint ifcProjectId = 0; + logger?.Log("Computing entities"); + foreach (var inst in Document.RawInstances) + { + if (!inst.IsValid()) + continue; + + // Property Values + if (inst.Type.Equals("IFCPROPERTYSINGLEVALUE")) + { + var e = d.GetInstanceWithData(inst); + AddNode(new IfcProp(this, e, e[2])); + } + else if (inst.Type.Equals("IFCPROPERTYENUMERATEDVALUE")) + { + var e = d.GetInstanceWithData(inst); + AddNode(new IfcProp(this, e, e[2])); + } + else if (inst.Type.Equals("IFCPROPERTYREFERENCEVALUE")) + { + var e = d.GetInstanceWithData(inst); + AddNode(new IfcProp(this, e, e[3])); + } + else if (inst.Type.Equals("IFCPROPERTYLISTVALUE")) + { + var e = d.GetInstanceWithData(inst); + AddNode(new IfcProp(this, e, e[2])); + } + else if (inst.Type.Equals("IFCCOMPLEXPROPERTY")) + { + var e = d.GetInstanceWithData(inst); + AddNode(new IfcProp(this, e, e[3])); + } + // Quantities which are a treated as a kind of prop + // https://iaiweb.lbl.gov/Resources/IFC_Releases/R2x3_final/ifcquantityresource/lexical/ifcphysicalquantity.htm + else if (inst.Type.Equals("IFCQUANTITYLENGTH")) + { + // https://iaiweb.lbl.gov/Resources/IFC_Releases/R2x3_final/ifcquantityresource/lexical/ifcquantitylength.htm + var e = d.GetInstanceWithData(inst); + AddNode(new IfcProp(this, e, e[3])); + } + else if (inst.Type.Equals("IFCQUANTITYAREA")) + { + // https://iaiweb.lbl.gov/Resources/IFC_Releases/R2x3_final/ifcquantityresource/lexical/ifcquantityarea.htm + var e = d.GetInstanceWithData(inst); + AddNode(new IfcProp(this, e, e[3])); + } + else if (inst.Type.Equals("IFCQUANTITYVOLUME")) + { + // https://iaiweb.lbl.gov/Resources/IFC_Releases/R2x3_final/ifcquantityresource/lexical/ifcquantityvolume.htm + var e = d.GetInstanceWithData(inst); + AddNode(new IfcProp(this, e, e[3])); + } + else if (inst.Type.Equals("IFCQUANTITYCOUNT")) + { + // https://iaiweb.lbl.gov/Resources/IFC_Releases/R2x3_final/ifcquantityresource/lexical/ifcquantitycount.htm + var e = d.GetInstanceWithData(inst); + AddNode(new IfcProp(this, e, e[3])); + } + else if (inst.Type.Equals("IFCQUANTITYWEIGHT")) + { + // https://iaiweb.lbl.gov/Resources/IFC_Releases/R2x3_final/ifcquantityresource/lexical/ifcquantityweight.htm + var e = d.GetInstanceWithData(inst); + AddNode(new IfcProp(this, e, e[3])); + } + else if (inst.Type.Equals("IFCQUANTITYTIME")) + { + // https://iaiweb.lbl.gov/Resources/IFC_Releases/R2x3_final/ifcquantityresource/lexical/ifcquantitytime.htm + var e = d.GetInstanceWithData(inst); + AddNode(new IfcProp(this, e, e[3])); + } + else if (inst.Type.Equals("IFCPHYSICALCOMPLEXQUANTITY")) + { + //https://iaiweb.lbl.gov/Resources/IFC_Releases/R2x3_final/ifcquantityresource/lexical/ifcphysicalcomplexquantity.htm + var e = d.GetInstanceWithData(inst); + AddNode(new IfcProp(this, e, e[2])); + } + // Property Set (or element quantity) + else if (inst.Type.Equals("IFCPROPERTYSET")) + { + var e = d.GetInstanceWithData(inst); + AddNode(new IfcPropSet(this, e, (StepList)e[4])); + } + else if (inst.Type.Equals("IFCELEMENTQUANTITY")) + { + var e = d.GetInstanceWithData(inst); + AddNode(new IfcPropSet(this, e, (StepList)e[5])); + } + // Aggregate relation + else if (inst.Type.Equals("IFCRELAGGREGATES")) + { + var e = d.GetInstanceWithData(inst); + AddRelation(new IfcRelationAggregate(this, e, (StepId)e[4], (StepList)e[5])); + } + // Spatial relation + else if (inst.Type.Equals("IFCRELCONTAINEDINSPATIALSTRUCTURE")) + { + var e = d.GetInstanceWithData(inst); + AddRelation(new IfcRelationSpatial(this, e, (StepId)e[5], (StepList)e[4])); + } + // Property set relations + else if (inst.Type.Equals("IFCRELDEFINESBYPROPERTIES")) + { + var e = d.GetInstanceWithData(inst); + AddRelation(new IfcPropSetRelation(this, e, (StepId)e[5], (StepList)e[4])); + } + // Type relations + else if (inst.Type.Equals("IFCRELDEFINESBYTYPE")) + { + var e = d.GetInstanceWithData(inst); + AddRelation(new IfcRelationType(this, e, (StepId)e[5], (StepList)e[4])); + } + else if (inst.Type.Equals("IFCPROJECT")) + { + //Special case for IFC Projects, track them as a root node. + var e = d.GetInstanceWithData(inst); + ifcProjectId = inst.Id; + AddNode(new IfcProject(this, e)); + } + else if ( + inst.Type.Equals("IFCSITE") + || inst.Type.Equals("IFCBUILDING") + || inst.Type.Equals("IFCBUILDINGSTOREY") + || inst.Type.Equals("IFCFACILITY") + || inst.Type.Equals("IFCFACILITYPART") + || inst.Type.Equals("IFCBRIDGE") + || inst.Type.Equals("IFCROAD") + || inst.Type.Equals("IFCRAILWAY") + || inst.Type.Equals("IFCMARINEFACILITY") + ) + { + var e = d.GetInstanceWithData(inst); + AddNode(new IfcSpatialStructureElement(this, e)); + } + // Everything else + else + { + // Simple IFC node: without step entity data. + var e = d.GetInstanceWithData(inst); + AddNode(new IfcNode(this, e)); + } + } + + if (ifcProjectId <= 0) + throw new SpeckleIfcException("There was no IfcProject in the file"); + + IfcProjectId = ifcProjectId; + + logger?.Log("Creating lookup of property sets"); + + foreach (var psr in Relations.OfType()) + { + var ps = psr.PropSet; + foreach (var id in psr.GetRelatedIds()) + { + if (!PropertySetsByNode.ContainsKey(id)) + PropertySetsByNode[id] = []; + PropertySetsByNode[id].Add(ps); + } + } + + logger?.Log("Completed creating model graph"); + } + + public IEnumerable GetNodes() => Nodes.Values; + + public IEnumerable GetNodes(IEnumerable ids) => ids.Select(GetNode); + + public IfcNode GetOrCreateNode(StepInstance lineData, int arg) + { + if (arg < 0 || arg >= lineData.AttributeValues.Count) + throw new SpeckleIfcException("Argument index out of range"); + return GetOrCreateNode(lineData.AttributeValues[arg]); + } + + public IfcNode GetOrCreateNode(StepValue o) => + GetOrCreateNode(o is StepId id ? id.Id : throw new SpeckleIfcException($"Expected a StepId value, not {o}")); + + public IfcNode GetOrCreateNode(uint id) + { + var r = Nodes.TryGetValue(id, out var node) ? node : AddNode(new IfcNode(this, Document.GetInstanceWithData(id))); + Debug.Assert(r.Id == id); + return r; + } + + public List GetOrCreateNodes(List list) => list.Select(GetOrCreateNode).ToList(); + + public List GetOrCreateNodes(StepInstance line, int arg) + { + if (arg < 0 || arg >= line.AttributeValues.Count) + throw new SpeckleIfcException("Argument out of range"); + if (line.AttributeValues[arg] is not StepList agg) + throw new SpeckleIfcException("Expected a list"); + return GetOrCreateNodes(agg.Values); + } + + public IfcNode GetNode(StepId id) => GetNode(id.Id); + + public IfcNode GetNode(uint id) + { + var r = Nodes[id]; + Debug.Assert(r.Id == id); + return r; + } + + public IfcNode GetIfcProject() => GetNode(IfcProjectId); + + public IEnumerable GetPropSets() => GetNodes().OfType(); + + public IEnumerable GetProps() => GetNodes().OfType(); + + public IEnumerable GetSpatialRelations() => Relations.OfType(); + + public IEnumerable GetAggregateRelations() => Relations.OfType(); + + public IReadOnlyList GetRelationsFrom(uint id) => + RelationsByNode.TryGetValue(id, out var list) ? list : Array.Empty(); +} diff --git a/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.IfcParser/IfcPropSetRelation.cs b/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.IfcParser/IfcPropSetRelation.cs new file mode 100644 index 000000000..dc907f766 --- /dev/null +++ b/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.IfcParser/IfcPropSetRelation.cs @@ -0,0 +1,22 @@ +using Speckle.Importers.Ifc.Ara3D.IfcParser.Schema; +using Speckle.Importers.Ifc.Ara3D.StepParser; + +namespace Speckle.Importers.Ifc.Ara3D.IfcParser; + +// https://standards.buildingsmart.org/IFC/RELEASE/IFC2x3/TC1/HTML/ifckernel/lexical/ifcreldefinesbyproperties.htm +public class IfcPropSetRelation : IfcRelation +{ + public IfcPropSetRelation(IfcGraph graph, StepInstance lineData, StepId from, StepList to) + : base(graph, lineData, from, to) { } + + public IfcPropSet PropSet + { + get + { + var node = Graph.GetNode(From); + if (node is not IfcPropSet r) + throw new SpeckleIfcException($"Expected a property set not {node} from id {From}"); + return r; + } + } +} diff --git a/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.IfcParser/IfcRelation.cs b/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.IfcParser/IfcRelation.cs new file mode 100644 index 000000000..b50dac494 --- /dev/null +++ b/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.IfcParser/IfcRelation.cs @@ -0,0 +1,26 @@ +using Speckle.Importers.Ifc.Ara3D.IfcParser.Schema; +using Speckle.Importers.Ifc.Ara3D.StepParser; + +namespace Speckle.Importers.Ifc.Ara3D.IfcParser; + +/// +/// Always express a 1-to-many relation +/// +public class IfcRelation : IfcEntity +{ + public StepId From { get; } + public StepList To { get; } + + public IfcRelation(IfcGraph graph, StepInstance lineData, StepId from, StepList to) + : base(graph, lineData) + { + if (!IsIfcRoot) + throw new SpeckleIfcException("Expected relation to be an IFC root entity"); + From = from; + To = to; + } + + public IEnumerable GetRelatedIds() => To.Values.Select(v => v.AsId()); + + public IEnumerable GetRelatedNodes() => Graph.GetNodes(GetRelatedIds()); +} diff --git a/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.IfcParser/IfcRelationAggregate.cs b/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.IfcParser/IfcRelationAggregate.cs new file mode 100644 index 000000000..5b826a1a0 --- /dev/null +++ b/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.IfcParser/IfcRelationAggregate.cs @@ -0,0 +1,9 @@ +using Speckle.Importers.Ifc.Ara3D.StepParser; + +namespace Speckle.Importers.Ifc.Ara3D.IfcParser; + +public class IfcRelationAggregate : IfcRelation +{ + public IfcRelationAggregate(IfcGraph graph, StepInstance lineData, StepId from, StepList to) + : base(graph, lineData, from, to) { } +} diff --git a/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.IfcParser/IfcRelationSpatial.cs b/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.IfcParser/IfcRelationSpatial.cs new file mode 100644 index 000000000..1509d0ae0 --- /dev/null +++ b/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.IfcParser/IfcRelationSpatial.cs @@ -0,0 +1,9 @@ +using Speckle.Importers.Ifc.Ara3D.StepParser; + +namespace Speckle.Importers.Ifc.Ara3D.IfcParser; + +public class IfcRelationSpatial : IfcRelation +{ + public IfcRelationSpatial(IfcGraph graph, StepInstance lineData, StepId from, StepList to) + : base(graph, lineData, from, to) { } +} diff --git a/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.IfcParser/IfcRelationType.cs b/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.IfcParser/IfcRelationType.cs new file mode 100644 index 000000000..f049e1b55 --- /dev/null +++ b/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.IfcParser/IfcRelationType.cs @@ -0,0 +1,9 @@ +using Speckle.Importers.Ifc.Ara3D.StepParser; + +namespace Speckle.Importers.Ifc.Ara3D.IfcParser; + +public class IfcRelationType : IfcRelation +{ + public IfcRelationType(IfcGraph graph, StepInstance lineData, StepId from, StepList to) + : base(graph, lineData, from, to) { } +} diff --git a/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.IfcParser/Schema/IfcNode.cs b/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.IfcParser/Schema/IfcNode.cs new file mode 100644 index 000000000..83b5a99c3 --- /dev/null +++ b/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.IfcParser/Schema/IfcNode.cs @@ -0,0 +1,9 @@ +using Speckle.Importers.Ifc.Ara3D.StepParser; + +namespace Speckle.Importers.Ifc.Ara3D.IfcParser.Schema; + +public class IfcNode : IfcEntity +{ + public IfcNode(IfcGraph graph, StepInstance lineData) + : base(graph, lineData) { } +} diff --git a/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.IfcParser/Schema/IfcProject.cs b/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.IfcParser/Schema/IfcProject.cs new file mode 100644 index 000000000..73f508f4d --- /dev/null +++ b/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.IfcParser/Schema/IfcProject.cs @@ -0,0 +1,10 @@ +using Speckle.Importers.Ifc.Ara3D.StepParser; + +namespace Speckle.Importers.Ifc.Ara3D.IfcParser.Schema; + +public sealed class IfcProject(IfcGraph graph, StepInstance lineData) : IfcNode(graph, lineData) +{ + public string? ObjectType => (LineData[5] as StepString)?.AsString(); + public string? LongName => (LineData[6] as StepString)?.AsString(); + public string? Phase => (LineData[7] as StepString)?.AsString(); +} diff --git a/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.IfcParser/Schema/IfcProp.cs b/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.IfcParser/Schema/IfcProp.cs new file mode 100644 index 000000000..c47f108fb --- /dev/null +++ b/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.IfcParser/Schema/IfcProp.cs @@ -0,0 +1,21 @@ +using Speckle.Importers.Ifc.Ara3D.StepParser; + +namespace Speckle.Importers.Ifc.Ara3D.IfcParser.Schema; + +public class IfcProp : IfcNode +{ + public readonly StepValue Value; + + public new string Name => this[0].AsString(); + public new string Description => this[1].AsString(); + + public IfcProp(IfcGraph graph, StepInstance lineData, StepValue value) + : base(graph, lineData) + { + if (lineData.Count < 2) + throw new SpeckleIfcException("Expected at least two values in the line data"); + if (lineData[0] is not StepString) + throw new SpeckleIfcException("Expected the first value to be a string (Name)"); + Value = value; + } +} diff --git a/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.IfcParser/Schema/IfcPropSet.cs b/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.IfcParser/Schema/IfcPropSet.cs new file mode 100644 index 000000000..936a4aa9d --- /dev/null +++ b/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.IfcParser/Schema/IfcPropSet.cs @@ -0,0 +1,42 @@ +using System.Diagnostics; +using Speckle.Importers.Ifc.Ara3D.StepParser; + +namespace Speckle.Importers.Ifc.Ara3D.IfcParser.Schema; + +// This merges two separate entity types: IfcPropertySet and IfcElementQuantity. +// Both of which are derived from IfcPropertySetDefinition. +// This is something that can be referred to by a PropertySetRelation +// An IfcElementQuantity has an additional "method of measurement" property. +// https://standards.buildingsmart.org/IFC/RELEASE/IFC2x3/TC1/HTML/ifckernel/lexical/ifcpropertyset.htm +// https://standards.buildingsmart.org/IFC/RELEASE/IFC2x3/TC1/HTML/ifcproductextension/lexical/ifcelementquantity.htm +public class IfcPropSet : IfcNode +{ + public readonly StepList PropertyIdList; + + public IfcPropSet(IfcGraph graph, StepInstance lineData, StepList propertyIdList) + : base(graph, lineData) + { + Debug.Assert(IsIfcRoot); + Debug.Assert(lineData.AttributeValues.Count is 5 or 6); + Debug.Assert(Type is "IFCPROPERTYSET" or "IFCELEMENTQUANTITY"); + PropertyIdList = propertyIdList; + } + + public IEnumerable GetProperties() + { + for (var i = 0; i < NumProperties; ++i) + { + var id = PropertyId(i); + var node = Graph.GetNode(id); + if (node is not IfcProp prop) + throw new SpeckleIfcException($"Expected a property not {node} from id {id}"); + yield return prop; + } + } + + public bool IsQuantity => LineData.AttributeValues.Count == 6; + public string? MethodOfMeasurement => IsQuantity ? this[4]?.AsString() : null; + public int NumProperties => PropertyIdList.Values.Count; + + public uint PropertyId(int i) => PropertyIdList.Values[i].AsId(); +} diff --git a/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.IfcParser/Schema/IfcSpatialStructureElement.cs b/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.IfcParser/Schema/IfcSpatialStructureElement.cs new file mode 100644 index 000000000..c464c7fa3 --- /dev/null +++ b/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.IfcParser/Schema/IfcSpatialStructureElement.cs @@ -0,0 +1,15 @@ +using Speckle.Importers.Ifc.Ara3D.StepParser; + +namespace Speckle.Importers.Ifc.Ara3D.IfcParser.Schema; + +/// +/// Types like IfcSite, IfcBuilding, IfcBuildingStorey +/// +/// +/// +public class IfcSpatialStructureElement(IfcGraph graph, StepInstance lineData) : IfcNode(graph, lineData) +{ + public string? ObjectType => (LineData[5] as StepString)?.AsString(); + public string? LongName => (LineData[8] as StepString)?.AsString(); + public string? CompositionType => (LineData[9] as StepString)?.AsString(); +} diff --git a/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.StepParser/AlignedMemoryReader.cs b/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.StepParser/AlignedMemoryReader.cs new file mode 100644 index 000000000..2d61b9a9f --- /dev/null +++ b/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.StepParser/AlignedMemoryReader.cs @@ -0,0 +1,31 @@ +using System.Diagnostics; +using Ara3D.Buffers; + +namespace Speckle.Importers.Ifc.Ara3D.StepParser; + +public static class AlignedMemoryReader +{ + public static unsafe AlignedMemory ReadAllBytes(string path, int bufferSize = 1024 * 1024) + { + using var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize, false); + var fileLength = fs.Length; + if (fileLength > int.MaxValue) + throw new IOException("File too big: > 2GB"); + + var count = (int)fileLength; + var r = new AlignedMemory(count); + var pBytes = r.BytePtr; + while (count > 0) + { + var span = new Span(pBytes, count); + var n = fs.Read(span); + if (n == 0) + break; + pBytes += n; + count -= n; + } + + Debug.Assert(count == 0); + return r; + } +} diff --git a/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.StepParser/ByteSpanExtensions.cs b/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.StepParser/ByteSpanExtensions.cs new file mode 100644 index 000000000..60bccc81a --- /dev/null +++ b/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.StepParser/ByteSpanExtensions.cs @@ -0,0 +1,13 @@ +using System.Runtime.CompilerServices; +using Ara3D.Buffers; + +namespace Speckle.Importers.Ifc.Ara3D.StepParser; + +public static class ByteSpanExtensions +{ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double ToDouble(this ByteSpan self) => double.Parse(self.ToSpan()); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int ToInt(this ByteSpan self) => int.Parse(self.ToSpan()); +} diff --git a/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.StepParser/StepDocument.cs b/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.StepParser/StepDocument.cs new file mode 100644 index 000000000..e56a490d3 --- /dev/null +++ b/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.StepParser/StepDocument.cs @@ -0,0 +1,102 @@ +using System.Runtime.Intrinsics; +using Ara3D.Buffers; +using Ara3D.Logging; +using Ara3D.Utils; + +namespace Speckle.Importers.Ifc.Ara3D.StepParser; + +public sealed unsafe class StepDocument : IDisposable +{ + public readonly FilePath FilePath; + public readonly byte* DataStart; + public readonly byte* DataEnd; + public readonly AlignedMemory Data; + + /// + /// This is a list of raw step instance information. + /// Each one has only a type and an ID. + /// + public readonly StepRawInstance[] RawInstances; + + /// + /// The number of raw instance + /// + public readonly int NumRawInstances; + + /// + /// This gives us a fast way to look up a StepInstance by their ID + /// + public readonly Dictionary InstanceIdToIndex = new(); + + /// + /// This tells us the byte offset of the start of each line in the file + /// + public readonly List LineOffsets; + + public StepDocument(FilePath filePath, ILogger? logger = null) + { + FilePath = filePath; + logger ??= Logger.Null; + + logger.Log($"Loading {filePath.GetFileSizeAsString()} of data from {filePath.GetFileName()}"); + Data = AlignedMemoryReader.ReadAllBytes(filePath); + DataStart = Data.BytePtr; + DataEnd = DataStart + Data.NumBytes; + + logger.Log($"Computing the start of each line"); + // NOTE: this estimates that the average line length is at least 32 characters. + // This minimize the number of allocations that happen + var cap = Data.NumBytes / 32; + LineOffsets = new List(cap); + + // We are going to report the beginning of the lines, while the "ComputeLines" function + // will compute the ends of lines. + var currentLine = 1; + for (var i = 0; i < Data.NumVectors; i++) + { + StepLineParser.ComputeOffsets(((Vector256*)Data.BytePtr)[i], ref currentLine, LineOffsets); + } + + logger.Log($"Found {LineOffsets.Count} lines"); + + logger.Log($"Creating instance records"); + RawInstances = new StepRawInstance[LineOffsets.Count]; + + for (var i = 0; i < LineOffsets.Count - 1; i++) + { + var lineStart = LineOffsets[i]; + var lineEnd = LineOffsets[i + 1]; + var inst = StepLineParser.ParseLine(DataStart + lineStart, DataStart + lineEnd); + if (inst.IsValid()) + { + InstanceIdToIndex.Add(inst.Id, NumRawInstances); + RawInstances[NumRawInstances++] = inst; + } + } + + logger.Log($"Completed creation of STEP document from {filePath.GetFileName()}"); + } + + public void Dispose() => Data.Dispose(); + + public StepInstance GetInstanceWithData(uint id) => GetInstanceWithDataFromIndex(InstanceIdToIndex[id]); + + public StepInstance GetInstanceWithDataFromIndex(int index) => GetInstanceWithData(RawInstances[index]); + + public StepInstance GetInstanceWithData(StepRawInstance inst) + { + var attr = inst.GetAttributes(DataEnd); + var se = new StepEntity(inst.Type, attr); + return new StepInstance(inst.Id, se); + } + + public static StepDocument Create(FilePath fp) => new(fp); + + public IEnumerable GetRawInstances(string typeCode) => + RawInstances.Where(inst => inst.Type.Equals(typeCode)); + + public IEnumerable GetInstances() => RawInstances.Select(GetInstanceWithData); + + public IEnumerable GetInstances(string typeCode) => + GetRawInstances(typeCode).Select(GetInstanceWithData); +} diff --git a/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.StepParser/StepFactory.cs b/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.StepParser/StepFactory.cs new file mode 100644 index 000000000..e7cb5bbab --- /dev/null +++ b/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.StepParser/StepFactory.cs @@ -0,0 +1,90 @@ +namespace Speckle.Importers.Ifc.Ara3D.StepParser; + +public static unsafe class StepFactory +{ + public static StepList GetAttributes(this StepRawInstance inst, byte* lineEnd) + { + if (!inst.IsValid()) + return StepList.CreateDefault(); + var ptr = inst.Type.End(); + var token = StepTokenizer.ParseToken(ptr, lineEnd); + // TODO: there is a potential bug here when the line is split across multiple line + return CreateAggregate(ref token, lineEnd); + } + + public static StepValue Create(ref StepToken token, byte* end) + { + switch (token.Type) + { + case StepTokenType.String: + return StepString.Create(token); + + case StepTokenType.Symbol: + return StepSymbol.Create(token); + + case StepTokenType.Id: + return StepId.Create(token); + + case StepTokenType.Redeclared: + return StepRedeclared.Create(token); + + case StepTokenType.Unassigned: + return StepUnassigned.Create(token); + + case StepTokenType.Number: + return StepNumber.Create(token); + + case StepTokenType.Ident: + var span = token.Span; + StepTokenizer.ParseNextToken(ref token, end); + var attr = CreateAggregate(ref token, end); + return new StepEntity(span, attr); + + case StepTokenType.BeginGroup: + return CreateAggregate(ref token, end); + + case StepTokenType.None: + case StepTokenType.Whitespace: + case StepTokenType.Comment: + case StepTokenType.Unknown: + case StepTokenType.LineBreak: + case StepTokenType.EndOfLine: + case StepTokenType.Definition: + case StepTokenType.Separator: + case StepTokenType.EndGroup: + default: + throw new SpeckleIfcException($"Cannot convert token type {token.Type} to a StepValue"); + } + } + + public static StepList CreateAggregate(ref StepToken token, byte* end) + { + var values = new List(); + StepTokenizer.EatWSpace(ref token, end); + if (token.Type != StepTokenType.BeginGroup) + throw new SpeckleIfcException("Expected '('"); + + while (StepTokenizer.ParseNextToken(ref token, end)) + { + switch (token.Type) + { + // Advance past comments, whitespace, and commas + case StepTokenType.Comment: + case StepTokenType.Whitespace: + case StepTokenType.LineBreak: + case StepTokenType.Separator: + case StepTokenType.None: + continue; + + // Expected end of group + case StepTokenType.EndGroup: + return new StepList(values); + } + + var curValue = Create(ref token, end); + values.Add(curValue); + } + + throw new SpeckleIfcException("Unexpected end of input"); + } +} diff --git a/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.StepParser/StepGraph.cs b/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.StepParser/StepGraph.cs new file mode 100644 index 000000000..dd45cba98 --- /dev/null +++ b/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.StepParser/StepGraph.cs @@ -0,0 +1,59 @@ +using Ara3D.Utils; +using Speckle.Sdk.Common; + +namespace Speckle.Importers.Ifc.Ara3D.StepParser; + +public class StepGraph +{ + public StepDocument Document { get; } + + public readonly Dictionary Lookup = new(); + + public StepNode GetNode(uint id) => Lookup[id]; + + public IEnumerable Nodes => Lookup.Values; + + public StepGraph(StepDocument doc) + { + Document = doc; + + foreach (var e in doc.GetInstances()) + { + var node = new StepNode(this, e); + Lookup.Add(node.Entity.Id, node); + } + + foreach (var n in Nodes) + n.Init(); + } + + public static StepGraph Create(StepDocument doc) => new(doc); + + public string ToValString(StepNode node, int depth) => ToValString(node.Entity.Entity, depth - 1); + + public string ToValString(StepValue value, int depth) + { + if (value == null) + return ""; + + switch (value) + { + case StepList stepAggregate: + return $"({stepAggregate.Values.Select(v => ToValString(v, depth)).JoinStringsWithComma()})"; + + case StepEntity stepEntity: + return $"{stepEntity.EntityType}{ToValString(stepEntity.Attributes, depth)}"; + + case StepId stepId: + return depth <= 0 ? "#" : ToValString(GetNode(stepId.Id), depth - 1); + + case StepNumber stepNumber: + case StepRedeclared stepRedeclared: + case StepString stepString: + case StepSymbol stepSymbol: + case StepUnassigned stepUnassigned: + default: + return value.ToString().NotNull(); + } + } +} diff --git a/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.StepParser/StepInstance.cs b/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.StepParser/StepInstance.cs new file mode 100644 index 000000000..8d405acc5 --- /dev/null +++ b/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.StepParser/StepInstance.cs @@ -0,0 +1,25 @@ +namespace Speckle.Importers.Ifc.Ara3D.StepParser; + +public class StepInstance +{ + public readonly StepEntity Entity; + public readonly uint Id; + + public List AttributeValues => Entity.Attributes.Values; + + public string EntityType => Entity?.EntityType.ToString() ?? ""; + + public StepInstance(uint id, StepEntity entity) + { + Id = id; + Entity = entity; + } + + public bool IsEntityType(string str) => EntityType == str; + + public override string ToString() => $"#{Id}={Entity};"; + + public int Count => AttributeValues.Count; + + public StepValue this[int i] => AttributeValues[i]; +} diff --git a/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.StepParser/StepLineParser.cs b/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.StepParser/StepLineParser.cs new file mode 100644 index 000000000..48316abfe --- /dev/null +++ b/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.StepParser/StepLineParser.cs @@ -0,0 +1,151 @@ +using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; +using Ara3D.Buffers; + +namespace Speckle.Importers.Ifc.Ara3D.StepParser; + +public static class StepLineParser +{ + public static readonly Vector256 Comma = Vector256.Create((byte)','); + public static readonly Vector256 NewLine = Vector256.Create((byte)'\n'); + public static readonly Vector256 StartGroup = Vector256.Create((byte)'('); + public static readonly Vector256 EndGroup = Vector256.Create((byte)')'); + public static readonly Vector256 Definition = Vector256.Create((byte)'='); + public static readonly Vector256 Quote = Vector256.Create((byte)'\''); + public static readonly Vector256 Id = Vector256.Create((byte)'#'); + public static readonly Vector256 SemiColon = Vector256.Create((byte)';'); + public static readonly Vector256 Unassigned = Vector256.Create((byte)'*'); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ComputeOffsets(in Vector256 v, ref int index, List offsets) + { + var r = Avx2.CompareEqual(v, NewLine); + var mask = (uint)Avx2.MoveMask(r); + if (mask == 0) + { + index += 32; + return; + } + + // Fully unrolled handling of each bit + if ((mask & 0x00000001) != 0) + offsets.Add(index); + if ((mask & 0x00000002) != 0) + offsets.Add(index + 1); + if ((mask & 0x00000004) != 0) + offsets.Add(index + 2); + if ((mask & 0x00000008) != 0) + offsets.Add(index + 3); + if ((mask & 0x00000010) != 0) + offsets.Add(index + 4); + if ((mask & 0x00000020) != 0) + offsets.Add(index + 5); + if ((mask & 0x00000040) != 0) + offsets.Add(index + 6); + if ((mask & 0x00000080) != 0) + offsets.Add(index + 7); + if ((mask & 0x00000100) != 0) + offsets.Add(index + 8); + if ((mask & 0x00000200) != 0) + offsets.Add(index + 9); + if ((mask & 0x00000400) != 0) + offsets.Add(index + 10); + if ((mask & 0x00000800) != 0) + offsets.Add(index + 11); + if ((mask & 0x00001000) != 0) + offsets.Add(index + 12); + if ((mask & 0x00002000) != 0) + offsets.Add(index + 13); + if ((mask & 0x00004000) != 0) + offsets.Add(index + 14); + if ((mask & 0x00008000) != 0) + offsets.Add(index + 15); + if ((mask & 0x00010000) != 0) + offsets.Add(index + 16); + if ((mask & 0x00020000) != 0) + offsets.Add(index + 17); + if ((mask & 0x00040000) != 0) + offsets.Add(index + 18); + if ((mask & 0x00080000) != 0) + offsets.Add(index + 19); + if ((mask & 0x00100000) != 0) + offsets.Add(index + 20); + if ((mask & 0x00200000) != 0) + offsets.Add(index + 21); + if ((mask & 0x00400000) != 0) + offsets.Add(index + 22); + if ((mask & 0x00800000) != 0) + offsets.Add(index + 23); + if ((mask & 0x01000000) != 0) + offsets.Add(index + 24); + if ((mask & 0x02000000) != 0) + offsets.Add(index + 25); + if ((mask & 0x04000000) != 0) + offsets.Add(index + 26); + if ((mask & 0x08000000) != 0) + offsets.Add(index + 27); + if ((mask & 0x10000000) != 0) + offsets.Add(index + 28); + if ((mask & 0x20000000) != 0) + offsets.Add(index + 29); + if ((mask & 0x40000000) != 0) + offsets.Add(index + 30); + if ((mask & 0x80000000) != 0) + offsets.Add(index + 31); + + // Update lineIndex to the next starting position + index += 32; + } + + public static unsafe StepRawInstance ParseLine(byte* ptr, byte* end) + { + var start = ptr; + var cnt = end - ptr; + const int MIN_LINE_LENGTH = 5; + if (cnt < MIN_LINE_LENGTH) + return default; + + // Parse the ID + if (*ptr++ != '#') + return default; + + var id = 0u; + while (ptr < end) + { + if (*ptr < '0' || *ptr > '9') + break; + id = id * 10 + *ptr - '0'; + ptr++; + } + + var foundEquals = false; + while (ptr < end) + { + if (*ptr == '=') + foundEquals = true; + + if (*ptr != (byte)' ' && *ptr != (byte)'=') + break; + + ptr++; + } + + if (!foundEquals) + return default; + + // Parse the entity type name + var entityStart = ptr; + while (ptr < end) + { + if (!StepTokenizer.IsIdentLookup[*ptr]) + break; + ptr++; + } + if (ptr == entityStart) + return default; + + var entityType = new ByteSpan(entityStart, ptr); + return new(id, entityType, start); + } +} diff --git a/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.StepParser/StepNode.cs b/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.StepParser/StepNode.cs new file mode 100644 index 000000000..c23188c7a --- /dev/null +++ b/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.StepParser/StepNode.cs @@ -0,0 +1,52 @@ +using Ara3D.Utils; + +namespace Speckle.Importers.Ifc.Ara3D.StepParser; + +public class StepNode +{ + public readonly StepGraph Graph; + public readonly StepInstance Entity; + + public StepNode(StepGraph g, StepInstance e) + { + Graph = g; + Entity = e; + } + + public List Nodes { get; } = new(); + + private void AddNodes(StepValue value) + { + if (value is StepId id) + { + var n = Graph.GetNode(id.Id); + Nodes.Add(n); + } + else if (value is StepList agg) + { + foreach (var v in agg.Values) + AddNodes(v); + } + } + + public void Init() + { + foreach (var a in Entity.AttributeValues) + AddNodes(a); + } + + public override string ToString() => Entity.ToString(); + + public string ToGraph(HashSet? prev = null) + { + prev ??= new HashSet(); + if (prev.Contains(this)) + return "_"; + var nodeStr = Nodes.Select(n => n.ToGraph(prev)).JoinStringsWithComma(); + return $"{EntityType}({nodeStr})"; + } + + public string EntityType => Entity.EntityType; + + public string QuickHash() => $"{EntityType}:{Nodes.Count}"; +} diff --git a/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.StepParser/StepRawInstance.cs b/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.StepParser/StepRawInstance.cs new file mode 100644 index 000000000..ee15dd920 --- /dev/null +++ b/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.StepParser/StepRawInstance.cs @@ -0,0 +1,20 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using Ara3D.Buffers; + +namespace Speckle.Importers.Ifc.Ara3D.StepParser; + +/// +/// Contains information about where an instance is within a file. +/// +[StructLayout(LayoutKind.Sequential, Pack = 1)] +[method: MethodImpl(MethodImplOptions.AggressiveInlining)] +public readonly unsafe struct StepRawInstance(uint id, ByteSpan type, byte* ptr) +{ + public readonly uint Id = id; + public readonly ByteSpan Type = type; + public readonly byte* Ptr = ptr; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool IsValid() => Id > 0; +} diff --git a/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.StepParser/StepToken.cs b/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.StepParser/StepToken.cs new file mode 100644 index 000000000..19fc352ee --- /dev/null +++ b/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.StepParser/StepToken.cs @@ -0,0 +1,17 @@ +using System.Diagnostics; +using Ara3D.Buffers; + +namespace Speckle.Importers.Ifc.Ara3D.StepParser; + +public readonly struct StepToken +{ + public readonly ByteSpan Span; + public readonly StepTokenType Type; + + public StepToken(ByteSpan span, StepTokenType type) + { + Span = span; + Debug.Assert(span.Length > 0); + Type = type; + } +} diff --git a/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.StepParser/StepTokenType.cs b/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.StepParser/StepTokenType.cs new file mode 100644 index 000000000..f42e11b6b --- /dev/null +++ b/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.StepParser/StepTokenType.cs @@ -0,0 +1,22 @@ +namespace Speckle.Importers.Ifc.Ara3D.StepParser; + +public enum StepTokenType : byte +{ + None, + Ident, + String, + Whitespace, + Number, + Symbol, + Id, + Separator, + Unassigned, + Redeclared, + Comment, + Unknown, + BeginGroup, + EndGroup, + LineBreak, + EndOfLine, + Definition, +} diff --git a/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.StepParser/StepTokenizer.cs b/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.StepParser/StepTokenizer.cs new file mode 100644 index 000000000..a23a2e949 --- /dev/null +++ b/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.StepParser/StepTokenizer.cs @@ -0,0 +1,306 @@ +using System.Diagnostics; +using System.Runtime.CompilerServices; +using Ara3D.Buffers; + +namespace Speckle.Importers.Ifc.Ara3D.StepParser; + +public static class StepTokenizer +{ + public static readonly StepTokenType[] TokenLookup = CreateTokenLookup(); + + public static readonly bool[] IsNumberLookup = CreateNumberLookup(); + + public static readonly bool[] IsIdentLookup = CreateIdentLookup(); + + public static StepTokenType[] CreateTokenLookup() + { + var r = new StepTokenType[256]; + for (var i = 0; i < 256; i++) + r[i] = GetTokenType((byte)i); + return r; + } + + public static bool[] CreateNumberLookup() + { + var r = new bool[256]; + for (var i = 0; i < 256; i++) + r[i] = IsNumberChar((byte)i); + return r; + } + + public static bool[] CreateIdentLookup() + { + var r = new bool[256]; + for (var i = 0; i < 256; i++) + r[i] = IsIdentOrDigitChar((byte)i); + return r; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static StepTokenType LookupToken(byte b) => TokenLookup[b]; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsNumberChar(byte b) + { + switch (b) + { + case (byte)'0': + case (byte)'1': + case (byte)'2': + case (byte)'3': + case (byte)'4': + case (byte)'5': + case (byte)'6': + case (byte)'7': + case (byte)'8': + case (byte)'9': + case (byte)'E': + case (byte)'e': + case (byte)'+': + case (byte)'-': + case (byte)'.': + return true; + } + + return false; + } + + public static StepTokenType GetTokenType(byte b) + { + switch (b) + { + case (byte)'0': + case (byte)'1': + case (byte)'2': + case (byte)'3': + case (byte)'4': + case (byte)'5': + case (byte)'6': + case (byte)'7': + case (byte)'8': + case (byte)'9': + case (byte)'+': + case (byte)'-': + return StepTokenType.Number; + + case (byte)' ': + case (byte)'\t': + return StepTokenType.Whitespace; + + case (byte)'\n': + case (byte)'\r': + return StepTokenType.LineBreak; + + case (byte)'\'': + case (byte)'"': + return StepTokenType.String; + + case (byte)'.': + return StepTokenType.Symbol; + + case (byte)'#': + return StepTokenType.Id; + + case (byte)';': + return StepTokenType.EndOfLine; + + case (byte)'(': + return StepTokenType.BeginGroup; + + case (byte)'=': + return StepTokenType.Definition; + + case (byte)')': + return StepTokenType.EndGroup; + + case (byte)',': + return StepTokenType.Separator; + + case (byte)'$': + return StepTokenType.Unassigned; + + case (byte)'*': + return StepTokenType.Redeclared; + + case (byte)'/': + return StepTokenType.Comment; + + case (byte)'a': + case (byte)'b': + case (byte)'c': + case (byte)'d': + case (byte)'e': + case (byte)'f': + case (byte)'g': + case (byte)'h': + case (byte)'i': + case (byte)'j': + case (byte)'k': + case (byte)'l': + case (byte)'m': + case (byte)'n': + case (byte)'o': + case (byte)'p': + case (byte)'q': + case (byte)'r': + case (byte)'s': + case (byte)'t': + case (byte)'u': + case (byte)'v': + case (byte)'w': + case (byte)'x': + case (byte)'y': + case (byte)'z': + case (byte)'A': + case (byte)'B': + case (byte)'C': + case (byte)'D': + case (byte)'E': + case (byte)'F': + case (byte)'G': + case (byte)'H': + case (byte)'I': + case (byte)'J': + case (byte)'K': + case (byte)'L': + case (byte)'M': + case (byte)'N': + case (byte)'O': + case (byte)'P': + case (byte)'Q': + case (byte)'R': + case (byte)'S': + case (byte)'T': + case (byte)'U': + case (byte)'V': + case (byte)'W': + case (byte)'X': + case (byte)'Y': + case (byte)'Z': + case (byte)'_': + return StepTokenType.Ident; + + default: + return StepTokenType.Unknown; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsWhiteSpace(byte b) => b == ' ' || b == '\t'; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsLineBreak(byte b) => b == '\n' || b == '\r'; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsIdent(byte b) => b >= 'A' && b <= 'Z' || b >= 'a' && b <= 'z' || b == '_'; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsDigit(byte b) => b >= '0' && b <= '9'; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsIdentOrDigitChar(byte b) => IsIdent(b) || IsDigit(b); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe byte* AdvancePast(byte* begin, byte* end, string s) + { + if (end - begin < s.Length) + return null; + foreach (var c in s) + if (*begin++ != (byte)c) + return null; + return begin; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe StepToken ParseToken(byte* begin, byte* end) + { + var cur = begin; + var tt = InternalParseToken(ref cur, end); + Debug.Assert(cur < end); + var span = new ByteSpan(begin, cur); + return new StepToken(span, tt); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe bool EatWSpace(ref StepToken cur, byte* end) + { + while ( + cur.Type == StepTokenType.Comment || cur.Type == StepTokenType.Whitespace || cur.Type == StepTokenType.LineBreak + ) + { + if (!ParseNextToken(ref cur, end)) + return false; + } + return true; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe bool ParseNextToken(ref StepToken prev, byte* end) + { + var cur = prev.Span.End(); + if (cur >= end) + return false; + prev = ParseToken(cur, end); + return true; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe StepTokenType InternalParseToken(ref byte* cur, byte* end) + { + var type = TokenLookup[*cur++]; + + switch (type) + { + case StepTokenType.Ident: + while (IsIdentLookup[*cur]) + cur++; + break; + + case StepTokenType.String: + // usually it is as single quote, + // but in rare cases it could be a double quote + var quoteChar = *(cur - 1); + while (cur < end) + { + if (*cur++ == quoteChar) + { + if (*cur != quoteChar) + break; + else + cur++; + } + } + + break; + + case StepTokenType.LineBreak: + while (IsLineBreak(*cur)) + cur++; + break; + + case StepTokenType.Number: + while (IsNumberLookup[*cur]) + cur++; + break; + + case StepTokenType.Symbol: + while (*cur++ != '.') { } + + break; + + case StepTokenType.Id: + while (IsNumberLookup[*cur]) + cur++; + break; + + case StepTokenType.Comment: + var prev = *cur++; + while (cur < end && (prev != '*' || *cur != '/')) + prev = *cur++; + cur++; + break; + } + + return type; + } +} diff --git a/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.StepParser/StepValue.cs b/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.StepParser/StepValue.cs new file mode 100644 index 000000000..6b1e93bf9 --- /dev/null +++ b/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.StepParser/StepValue.cs @@ -0,0 +1,154 @@ +using System.Diagnostics; +using Ara3D.Buffers; +using Ara3D.Utils; + +namespace Speckle.Importers.Ifc.Ara3D.StepParser; + +/// +/// The base class of the different type of value items that can be found in a STEP file. +/// * Entity +/// * List +/// * String +/// * Symbol +/// * Unassigned token +/// * Redeclared token +/// * Number +/// +public class StepValue; + +public class StepEntity : StepValue +{ + public readonly ByteSpan EntityType; + public readonly StepList Attributes; + + public StepEntity(ByteSpan entityType, StepList attributes) + { + Debug.Assert(!entityType.IsNull()); + EntityType = entityType; + Attributes = attributes; + } + + public override string ToString() => $"{EntityType}{Attributes}"; +} + +public class StepList : StepValue +{ + public readonly List Values; + + public StepList(List values) => Values = values; + + public override string ToString() => $"({Values.JoinStringsWithComma()})"; + + public static StepList CreateDefault() => new(new List()); +} + +public class StepString : StepValue +{ + public readonly ByteSpan Value; + + public static StepString Create(StepToken token) + { + var span = token.Span; + Debug.Assert(token.Type == StepTokenType.String); + Debug.Assert(span.Length >= 2); + Debug.Assert(span.First() == '\'' || span.First() == '"'); + Debug.Assert(span.Last() == '\'' || span.Last() == '"'); + return new StepString(span.Trim(1, 1)); + } + + public StepString(ByteSpan value) => Value = value; + + public override string ToString() => $"'{Value}'"; +} + +public class StepSymbol : StepValue +{ + public readonly ByteSpan Name; + + public StepSymbol(ByteSpan name) => Name = name; + + public override string ToString() => $".{Name}."; + + public static StepSymbol Create(StepToken token) + { + Debug.Assert(token.Type == StepTokenType.Symbol); + var span = token.Span; + Debug.Assert(span.Length >= 2); + Debug.Assert(span.First() == '.'); + Debug.Assert(span.Last() == '.'); + return new StepSymbol(span.Trim(1, 1)); + } +} + +public class StepNumber : StepValue +{ + public readonly ByteSpan Span; + public double Value => Span.ToDouble(); + + public StepNumber(ByteSpan span) => Span = span; + + public override string ToString() => $"{Value}"; + + public static StepNumber Create(StepToken token) + { + Debug.Assert(token.Type == StepTokenType.Number); + var span = token.Span; + return new(span); + } +} + +public class StepId : StepValue +{ + public readonly uint Id; + + public StepId(uint id) => Id = id; + + public override string ToString() => $"#{Id}"; + + public static unsafe StepId Create(StepToken token) + { + Debug.Assert(token.Type == StepTokenType.Id); + var span = token.Span; + Debug.Assert(span.Length >= 2); + Debug.Assert(span.First() == '#'); + var id = 0u; + for (var i = 1; i < span.Length; ++i) + { + Debug.Assert(span.Ptr[i] >= '0' && span.Ptr[i] <= '9'); + id = id * 10 + span.Ptr[i] - '0'; + } + return new StepId(id); + } +} + +public class StepUnassigned : StepValue +{ + public static readonly StepUnassigned Default = new(); + + public override string ToString() => "$"; + + public static StepUnassigned Create(StepToken token) + { + Debug.Assert(token.Type == StepTokenType.Unassigned); + var span = token.Span; + Debug.Assert(span.Length == 1); + Debug.Assert(span.First() == '$'); + return Default; + } +} + +public class StepRedeclared : StepValue +{ + public static readonly StepRedeclared Default = new(); + + public override string ToString() => "*"; + + public static StepRedeclared Create(StepToken token) + { + Debug.Assert(token.Type == StepTokenType.Redeclared); + var span = token.Span; + Debug.Assert(span.Length == 1); + Debug.Assert(span.First() == '*'); + return Default; + } +} diff --git a/Importers/Ifc/Speckle.Importers.Ifc/Converters/DataObjectConverter.cs b/Importers/Ifc/Speckle.Importers.Ifc/Converters/DataObjectConverter.cs new file mode 100644 index 000000000..1e5552520 --- /dev/null +++ b/Importers/Ifc/Speckle.Importers.Ifc/Converters/DataObjectConverter.cs @@ -0,0 +1,34 @@ +using Speckle.Importers.Ifc.Ara3D.IfcParser.Schema; +using Speckle.Importers.Ifc.Types; +using Speckle.InterfaceGenerator; +using Speckle.Objects.Data; +using Speckle.Sdk.Models; + +namespace Speckle.Importers.Ifc.Converters; + +[GenerateAutoInterface] +public sealed class DataObjectConverter(IGeometryConverter geometryConverter) : IDataObjectConverter +{ + public DataObject Convert(IfcModel model, IfcNode node, INodeConverter childrenConverter) + { + if (!node.IsIfcRoot) + throw new ArgumentException("Expected to be an IfcRoot", paramName: nameof(node)); + + // Even if there is no geometry, this will return an empty collection. + var geo = model.GetGeometry(node.Id); + List displayValue = geo != null ? geometryConverter.Convert(geo) : new(); + + return new DataObject() + { + applicationId = node.Guid, // Guid is null for property values, and other Ifc entities not derived from IfcRoot + properties = node.ConvertPropertySets(), + name = node.Name ?? node.Guid, + displayValue = displayValue, + ["@elements"] = childrenConverter.ConvertChildren(model, node), + ["ifcType"] = node.Type, + ["expressID"] = node.Id, + ["ownerId"] = node.OwnerId, + ["description"] = node.Description, + }; + } +} diff --git a/Importers/Ifc/Speckle.Importers.Ifc/Converters/GeometryConverter.cs b/Importers/Ifc/Speckle.Importers.Ifc/Converters/GeometryConverter.cs new file mode 100644 index 000000000..6920c5327 --- /dev/null +++ b/Importers/Ifc/Speckle.Importers.Ifc/Converters/GeometryConverter.cs @@ -0,0 +1,20 @@ +using Speckle.Importers.Ifc.Types; +using Speckle.InterfaceGenerator; +using Speckle.Sdk.Models; + +namespace Speckle.Importers.Ifc.Converters; + +[GenerateAutoInterface] +public sealed class GeometryConverter(IMeshConverter meshConverter) : IGeometryConverter +{ + public List Convert(IfcGeometry geometry) + { + List ret = new(); + foreach (var mesh in geometry.GetMeshes()) + { + ret.Add(meshConverter.Convert(mesh)); + } + + return ret; + } +} diff --git a/Importers/Ifc/Speckle.Importers.Ifc/Converters/GraphConverter.cs b/Importers/Ifc/Speckle.Importers.Ifc/Converters/GraphConverter.cs new file mode 100644 index 000000000..d0b1a7dce --- /dev/null +++ b/Importers/Ifc/Speckle.Importers.Ifc/Converters/GraphConverter.cs @@ -0,0 +1,22 @@ +using Speckle.Importers.Ifc.Ara3D.IfcParser; +using Speckle.Importers.Ifc.Services; +using Speckle.Importers.Ifc.Types; +using Speckle.InterfaceGenerator; +using Speckle.Sdk.Models; + +namespace Speckle.Importers.Ifc.Converters; + +[GenerateAutoInterface] +public sealed class GraphConverter(INodeConverter nodeConverter, IRenderMaterialProxyManager proxyManager) + : IGraphConverter +{ + public Base Convert(IfcModel model, IfcGraph graph) + { + Base rootCollection = nodeConverter.Convert(model, graph.GetIfcProject()); + + //Grabing materials from ProxyManager + rootCollection["renderMaterialProxies"] = proxyManager.RenderMaterialProxies.Values.ToList(); + + return rootCollection; + } +} diff --git a/Importers/Ifc/Speckle.Importers.Ifc/Converters/MeshConverter.cs b/Importers/Ifc/Speckle.Importers.Ifc/Converters/MeshConverter.cs new file mode 100644 index 000000000..4395a3330 --- /dev/null +++ b/Importers/Ifc/Speckle.Importers.Ifc/Converters/MeshConverter.cs @@ -0,0 +1,74 @@ +using System.Drawing; +using Speckle.Importers.Ifc.Services; +using Speckle.Importers.Ifc.Types; +using Speckle.InterfaceGenerator; +using Speckle.Objects.Geometry; +using Speckle.Objects.Other; + +namespace Speckle.Importers.Ifc.Converters; + +[GenerateAutoInterface] +public sealed class MeshConverter(IRenderMaterialProxyManager renderMaterialManager) : IMeshConverter +{ + public Mesh Convert(IfcMesh mesh) + { + var m = mesh.Transform; + var vp = mesh.Vertices; + var ip = mesh.Indices; + + var vertices = new List(vp.Length * 3); + foreach (var vertex in vp) + { + var x = vertex.PX; + var y = vertex.PY; + var z = vertex.PZ; + + vertices.Add(m[0] * x + m[4] * y + m[8] * z + m[12]); + vertices.Add(-(m[2] * x + m[6] * y + m[10] * z + m[14])); + vertices.Add(m[1] * x + m[5] * y + m[9] * z + m[13]); + } + + var faces = new List(ip.Length * 4); + for (var i = 0; i < ip.Length; i += 3) + { + var a = ip[i]; + var b = ip[i + 1]; + var c = ip[i + 2]; + faces.Add(3); + faces.Add(a); + faces.Add(b); + faces.Add(c); + } + + RenderMaterial renderMaterial = ConvertRenderMaterial(mesh); + Mesh converted = + new() + { + applicationId = Guid.NewGuid().ToString(), + vertices = vertices, + faces = faces, + units = "m", + }; + + renderMaterialManager.AddMeshMapping(renderMaterial, converted); + + return converted; + } + + private static RenderMaterial ConvertRenderMaterial(IfcMesh mesh) + { + var color = mesh.Color; + var diffuse = Color.FromArgb(1, To8BitValue(color.R), To8BitValue(color.G), To8BitValue(color.B)); + + var name = $"IFC_MATERIAL:{(color.A, color.R, color.G, color.B).GetHashCode()}"; + + return new RenderMaterial() + { + applicationId = name, + name = name, + diffuse = diffuse.ToArgb(), + opacity = color.A + }; + static int To8BitValue(double value) => (int)(value * 255); + } +} diff --git a/Importers/Ifc/Speckle.Importers.Ifc/Converters/NodeConverter.cs b/Importers/Ifc/Speckle.Importers.Ifc/Converters/NodeConverter.cs new file mode 100644 index 000000000..83f9dfa07 --- /dev/null +++ b/Importers/Ifc/Speckle.Importers.Ifc/Converters/NodeConverter.cs @@ -0,0 +1,44 @@ +using Speckle.Importers.Ifc.Ara3D.IfcParser.Schema; +using Speckle.Importers.Ifc.Types; +using Speckle.InterfaceGenerator; +using Speckle.Sdk.Models; + +namespace Speckle.Importers.Ifc.Converters; + +/// +/// This is the main "recursive" converter for converting all IfcTypes to Speckle +/// +[GenerateAutoInterface] +public sealed class NodeConverter( + IDataObjectConverter dataObjectConverter, + IIfcSpatialStructureElementConverter spatialStructureConverter, + IProjectConverter projectConverter +) : INodeConverter +{ + /// + /// Converts Ifc nodes that inherits IfcRoot class To Speckle) + /// + /// + /// + /// + public Base Convert(IfcModel model, IfcNode node) + { + if (!node.IsIfcRoot) + throw new ArgumentException("Expected to be an IfcRoot", paramName: nameof(node)); + + return node switch + { + IfcProject project => projectConverter.Convert(model, project, this), + //Note: we're only expecting IfcSite, IfcBuilding, and IfcBuildingStory's here... + //but I cba to add full classes + inheritance, so IfcSpatialStructureElements is the closest common class + IfcSpatialStructureElement structure => spatialStructureConverter.Convert(model, structure, this), + IfcPropSet => throw new NotImplementedException("We didn't expect IfcPropSets here!"), + _ => dataObjectConverter.Convert(model, node, this) + }; + } + + public List ConvertChildren(IfcModel model, IfcNode node) + { + return node.GetChildren().Where(x => x.IsIfcRoot).Select(x => Convert(model, x)).ToList(); + } +} diff --git a/Importers/Ifc/Speckle.Importers.Ifc/Converters/NodeExtensions.cs b/Importers/Ifc/Speckle.Importers.Ifc/Converters/NodeExtensions.cs new file mode 100644 index 000000000..039fc4822 --- /dev/null +++ b/Importers/Ifc/Speckle.Importers.Ifc/Converters/NodeExtensions.cs @@ -0,0 +1,32 @@ +using Speckle.Importers.Ifc.Ara3D.IfcParser; +using Speckle.Importers.Ifc.Ara3D.IfcParser.Schema; + +namespace Speckle.Importers.Ifc.Converters; + +public static class NodeExtensions +{ + public static Dictionary ConvertPropertySets(this IfcNode node) + { + var result = new Dictionary(); + foreach (var p in node.GetPropSets()) + { + if (p.NumProperties <= 0) + continue; + + var name = p.Name; + if (string.IsNullOrWhiteSpace(name)) + name = $"#{p.Id}"; + result[name] = ToSpeckleDictionary(p); + } + + return result; + } + + public static Dictionary ToSpeckleDictionary(this IfcPropSet ps) + { + var d = new Dictionary(); + foreach (var p in ps.GetProperties()) + d[p.Name] = p.Value.ToJsonObject(); + return d; + } +} diff --git a/Importers/Ifc/Speckle.Importers.Ifc/Converters/ProjectConverter.cs b/Importers/Ifc/Speckle.Importers.Ifc/Converters/ProjectConverter.cs new file mode 100644 index 000000000..7d0bf6628 --- /dev/null +++ b/Importers/Ifc/Speckle.Importers.Ifc/Converters/ProjectConverter.cs @@ -0,0 +1,31 @@ +using Speckle.Importers.Ifc.Ara3D.IfcParser.Schema; +using Speckle.Importers.Ifc.Types; +using Speckle.InterfaceGenerator; +using Speckle.Sdk.Models.Collections; + +namespace Speckle.Importers.Ifc.Converters; + +[GenerateAutoInterface] +public sealed class ProjectConverter : IProjectConverter +{ + public Collection Convert(IfcModel model, IfcProject node, INodeConverter childrenConverter) + { + if (!node.IsIfcRoot) //I'd really rather have a class for this (IfcRoot : IfcNode) + throw new ArgumentException("Expected to be an IfcRoot", paramName: nameof(node)); + + return new Collection + { + name = node.Name ?? node.Guid, + applicationId = node.Guid, + elements = childrenConverter.ConvertChildren(model, node), + ["expressID"] = node.Id, + ["ownerId"] = node.OwnerId, + ["ifcType"] = node.Type, + ["description"] = node.Description, + ["objectType"] = node.ObjectType, + ["longName"] = node.LongName, + ["phase"] = node.Phase, + ["properties"] = node.ConvertPropertySets(), + }; + } +} diff --git a/Importers/Ifc/Speckle.Importers.Ifc/Converters/SpatialStructureConverter.cs b/Importers/Ifc/Speckle.Importers.Ifc/Converters/SpatialStructureConverter.cs new file mode 100644 index 000000000..242df450c --- /dev/null +++ b/Importers/Ifc/Speckle.Importers.Ifc/Converters/SpatialStructureConverter.cs @@ -0,0 +1,31 @@ +using Speckle.Importers.Ifc.Ara3D.IfcParser.Schema; +using Speckle.Importers.Ifc.Types; +using Speckle.InterfaceGenerator; +using Speckle.Sdk.Models.Collections; + +namespace Speckle.Importers.Ifc.Converters; + +[GenerateAutoInterface] +public sealed class IfcSpatialStructureElementConverter : IIfcSpatialStructureElementConverter +{ + public Collection Convert(IfcModel model, IfcSpatialStructureElement node, INodeConverter childrenConverter) + { + if (!node.IsIfcRoot) //I'd really rather have a class for this (IfcRoot : IfcNode) + throw new ArgumentException("Expected to be an IfcRoot", paramName: nameof(node)); + + return new Collection + { + name = node.Name ?? node.Guid, + applicationId = node.Guid, + elements = childrenConverter.ConvertChildren(model, node), + ["expressID"] = node.Id, + ["ownerId"] = node.OwnerId, + ["ifcType"] = node.Type, + ["description"] = node.Description, + ["objectType"] = node.ObjectType, + ["compositionType"] = node.CompositionType, + ["longName"] = node.LongName, + ["properties"] = node.ConvertPropertySets(), + }; + } +} diff --git a/Importers/Ifc/Speckle.Importers.Ifc/Import.cs b/Importers/Ifc/Speckle.Importers.Ifc/Import.cs new file mode 100644 index 000000000..0acb11518 --- /dev/null +++ b/Importers/Ifc/Speckle.Importers.Ifc/Import.cs @@ -0,0 +1,113 @@ +using System.Diagnostics; +using System.Reflection; +using Ara3D.Utils; +using Microsoft.Extensions.DependencyInjection; +using Speckle.Importers.Ifc.Ara3D.IfcParser; +using Speckle.Importers.Ifc.Converters; +using Speckle.Importers.Ifc.Types; +using Speckle.Objects.Geometry; +using Speckle.Sdk; +using Speckle.Sdk.Api; +using Speckle.Sdk.Api.GraphQL.Inputs; +using Speckle.Sdk.Api.GraphQL.Models; +using Speckle.Sdk.Credentials; +using Speckle.Sdk.Host; +using Speckle.Sdk.Models; +using Speckle.Sdk.Serialisation.V2; +using Speckle.Sdk.Serialisation.V2.Send; +using Speckle.Sdk.Transports; + +namespace Speckle.Importers.Ifc; + +public static class Import +{ + public static async Task Ifc( + string url, + string filePath, + string streamId, + string modelId, + string commitMessage, + string token, + IProgress? progress = null + ) + { + var serviceProvider = GetServiceProvider(); + return await Ifc(serviceProvider, url, filePath, streamId, modelId, commitMessage, token, progress); + } + + public static ServiceProvider GetServiceProvider() + { + TypeLoader.Initialize(typeof(Base).Assembly, typeof(Point).Assembly); + var serviceCollection = new ServiceCollection(); + serviceCollection.AddIFCImporter(); + return serviceCollection.BuildServiceProvider(); + } + + public static void AddIFCImporter(this ServiceCollection serviceCollection) + { + serviceCollection.AddSpeckleSdk(new("IFC", "ifc"), HostAppVersion.v2024, "IFC-Importer"); + serviceCollection.AddSpeckleWebIfc(); + serviceCollection.AddMatchingInterfacesAsTransient(Assembly.GetExecutingAssembly()); + } + + public static async Task Ifc( + IServiceProvider serviceProvider, + string url, + string filePath, + string streamId, + string modelId, + string commitMessage, + string token, + IProgress? progress = null + ) + { + var ifcFactory = serviceProvider.GetRequiredService(); + var clientFactory = serviceProvider.GetRequiredService(); + var baseUri = new Uri(url); + var stopwatch = Stopwatch.StartNew(); + + var model = ifcFactory.Open(filePath); + var ms = stopwatch.ElapsedMilliseconds; + Console.WriteLine($"Opened with WebIFC: {ms} ms"); + + var graph = IfcGraph.Load(new FilePath(filePath)); + var ms2 = stopwatch.ElapsedMilliseconds; + Console.WriteLine($"Loaded with StepParser: {ms2 - ms} ms"); + + var converter = serviceProvider.GetRequiredService(); + var b = converter.Convert(model, graph); + ms = ms2; + ms2 = stopwatch.ElapsedMilliseconds; + Console.WriteLine($"Converted to Speckle Bases: {ms2 - ms} ms"); + + var serializeProcessFactory = serviceProvider.GetRequiredService(); + var process = serializeProcessFactory.CreateSerializeProcess( + baseUri, + streamId, + token, + progress, + default, + new SerializeProcessOptions(true, true, false, progress is null) + ); + var (rootId, _) = await process.Serialize(b).ConfigureAwait(false); + Account account = + new() + { + token = token, + serverInfo = new ServerInfo { url = baseUri.ToString() }, + }; + ms = ms2; + ms2 = stopwatch.ElapsedMilliseconds; + Console.WriteLine($"Uploaded to Speckle: {ms2 - ms} ms. Root id: {rootId}"); + + // 8 - Create the version (commit) + using var apiClient = clientFactory.Create(account); + var commit = await apiClient.Version.Create( + new CreateVersionInput(rootId, modelId, streamId, message: commitMessage, sourceApplication: "IFC") + ); + ms = ms2; + ms2 = stopwatch.ElapsedMilliseconds; + Console.WriteLine($"Committed to Speckle: {ms2 - ms} ms"); + return commit.id; + } +} diff --git a/Importers/Ifc/Speckle.Importers.Ifc/Native/WebIfc.cs b/Importers/Ifc/Speckle.Importers.Ifc/Native/WebIfc.cs new file mode 100644 index 000000000..0775d7605 --- /dev/null +++ b/Importers/Ifc/Speckle.Importers.Ifc/Native/WebIfc.cs @@ -0,0 +1,107 @@ +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; + +namespace Speckle.Importers.Ifc.Native; + +[SuppressMessage("Globalization", "CA2101:Specify marshaling for P/Invoke string arguments")] +[SuppressMessage("Security", "CA5393:Do not use unsafe DllImportSearchPath value")] +internal static class WebIfc +{ +#if WINDOWS + private const string DllName = "Native/web-ifc.dll"; + private const CharSet Set = CharSet.Ansi; +#else + private const string DllName = "libweb-ifc.so"; + private const CharSet Set = CharSet.Auto; +#endif + + private const DllImportSearchPath ImportSearchPath = DllImportSearchPath.AssemblyDirectory; + + [DllImport(DllName)] + [DefaultDllImportSearchPaths(ImportSearchPath)] + public static extern IntPtr InitializeApi(); + + [DllImport(DllName)] + [DefaultDllImportSearchPaths(ImportSearchPath)] + public static extern void FinalizeApi(IntPtr api); + + [DllImport(DllName, CharSet = Set)] + [DefaultDllImportSearchPaths(ImportSearchPath)] + public static extern IntPtr LoadModel(IntPtr api, string fileName); + + [DllImport(DllName, CharSet = Set)] + [DefaultDllImportSearchPaths(ImportSearchPath)] + public static extern string GetVersion(); + + [DllImport(DllName)] + [DefaultDllImportSearchPaths(ImportSearchPath)] + public static extern IntPtr GetMesh(IntPtr geometry, int index); + + [DllImport(DllName)] + [DefaultDllImportSearchPaths(ImportSearchPath)] + public static extern int GetNumMeshes(IntPtr geometry); + + [DllImport(DllName)] + [DefaultDllImportSearchPaths(ImportSearchPath)] + public static extern uint GetGeometryType(IntPtr geometry); + + [DllImport(DllName)] + [DefaultDllImportSearchPaths(ImportSearchPath)] + public static extern uint GetGeometryId(IntPtr geometry); + + [DllImport(DllName)] + [DefaultDllImportSearchPaths(ImportSearchPath)] + public static extern uint GetLineId(IntPtr line); + + [DllImport(DllName)] + [DefaultDllImportSearchPaths(ImportSearchPath)] + public static extern uint GetLineType(IntPtr line); + + [DllImport(DllName)] + [DefaultDllImportSearchPaths(ImportSearchPath)] + public static extern string GetLineArguments(IntPtr line); + + [DllImport(DllName)] + [DefaultDllImportSearchPaths(ImportSearchPath)] + public static extern int GetNumVertices(IntPtr mesh); + + [DllImport(DllName)] + [DefaultDllImportSearchPaths(ImportSearchPath)] + public static extern IntPtr GetVertices(IntPtr mesh); + + [DllImport(DllName)] + [DefaultDllImportSearchPaths(ImportSearchPath)] + public static extern IntPtr GetTransform(IntPtr mesh); + + [DllImport(DllName)] + [DefaultDllImportSearchPaths(ImportSearchPath)] + public static extern int GetNumIndices(IntPtr mesh); + + [DllImport(DllName)] + [DefaultDllImportSearchPaths(ImportSearchPath)] + public static extern IntPtr GetIndices(IntPtr mesh); + + [DllImport(DllName)] + [DefaultDllImportSearchPaths(ImportSearchPath)] + public static extern IntPtr GetColor(IntPtr mesh); + + [DllImport(DllName)] + [DefaultDllImportSearchPaths(ImportSearchPath)] + public static extern IntPtr GetGeometryFromId(IntPtr model, uint id); + + [DllImport(DllName)] + [DefaultDllImportSearchPaths(ImportSearchPath)] + public static extern int GetNumGeometries(IntPtr model); + + [DllImport(DllName)] + [DefaultDllImportSearchPaths(ImportSearchPath)] + public static extern IntPtr GetGeometryFromIndex(IntPtr model, int index); + + [DllImport(DllName)] + [DefaultDllImportSearchPaths(ImportSearchPath)] + public static extern uint GetMaxId(IntPtr model); + + [DllImport(DllName)] + [DefaultDllImportSearchPaths(ImportSearchPath)] + public static extern IntPtr GetLineFromModel(IntPtr model, uint id); +} diff --git a/Importers/Ifc/Speckle.Importers.Ifc/Native/libweb-ifc.so b/Importers/Ifc/Speckle.Importers.Ifc/Native/libweb-ifc.so new file mode 100644 index 000000000..b303c198d Binary files /dev/null and b/Importers/Ifc/Speckle.Importers.Ifc/Native/libweb-ifc.so differ diff --git a/Importers/Ifc/Speckle.Importers.Ifc/Native/web-ifc.dll b/Importers/Ifc/Speckle.Importers.Ifc/Native/web-ifc.dll new file mode 100644 index 000000000..35808817c Binary files /dev/null and b/Importers/Ifc/Speckle.Importers.Ifc/Native/web-ifc.dll differ diff --git a/Importers/Ifc/Speckle.Importers.Ifc/ServiceRegistration.cs b/Importers/Ifc/Speckle.Importers.Ifc/ServiceRegistration.cs new file mode 100644 index 000000000..9b8c9b4a0 --- /dev/null +++ b/Importers/Ifc/Speckle.Importers.Ifc/ServiceRegistration.cs @@ -0,0 +1,33 @@ +using System.Reflection; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Speckle.Importers.Ifc.Services; +using Speckle.Importers.Ifc.Types; +using Speckle.Sdk; + +namespace Speckle.Importers.Ifc; + +public static class ServiceRegistration +{ + public static void AddSpeckleWebIfc(this IServiceCollection services) + { + services.AddSingleton(); + services.AddSingleton(); + } + + public static IServiceCollection AddMatchingInterfacesAsTransient( + this IServiceCollection serviceCollection, + Assembly assembly + ) + { + foreach (var type in assembly.ExportedTypes.Where(t => t.IsNonAbstractClass())) + { + foreach (var matchingInterface in type.FindMatchingInterface()) + { + serviceCollection.TryAddTransient(matchingInterface, type); + } + } + + return serviceCollection; + } +} diff --git a/Importers/Ifc/Speckle.Importers.Ifc/Services/RenderMaterialProxyManager.cs b/Importers/Ifc/Speckle.Importers.Ifc/Services/RenderMaterialProxyManager.cs new file mode 100644 index 000000000..643401f43 --- /dev/null +++ b/Importers/Ifc/Speckle.Importers.Ifc/Services/RenderMaterialProxyManager.cs @@ -0,0 +1,27 @@ +using Speckle.InterfaceGenerator; +using Speckle.Objects.Geometry; +using Speckle.Objects.Other; +using Speckle.Sdk.Common; + +namespace Speckle.Importers.Ifc.Services; + +[GenerateAutoInterface] +public sealed class RenderMaterialProxyManager : IRenderMaterialProxyManager +{ + public Dictionary RenderMaterialProxies { get; } = new(); + + public void AddMeshMapping(RenderMaterial renderMaterial, Mesh mesh) + { + string materialId = renderMaterial.applicationId.NotNull(); + string meshId = mesh.applicationId.NotNull(); + + if (RenderMaterialProxies.TryGetValue(materialId, out RenderMaterialProxy? proxy)) + { + proxy.objects.Add(meshId); + } + else + { + RenderMaterialProxies.Add(materialId, new() { objects = [meshId], value = renderMaterial, }); + } + } +} diff --git a/Importers/Ifc/Speckle.Importers.Ifc/Speckle.Importers.Ifc.csproj b/Importers/Ifc/Speckle.Importers.Ifc/Speckle.Importers.Ifc.csproj new file mode 100644 index 000000000..b2b80aa0f --- /dev/null +++ b/Importers/Ifc/Speckle.Importers.Ifc/Speckle.Importers.Ifc.csproj @@ -0,0 +1,43 @@ + + + + net8.0 + true + Debug;Release;Local + WINDOWS + LINUX + + + + true + true + + + + + IDE1006;IDE0130;IDE0011;CA1051;CA1720;CA1002;CA1054;CA1028;CA1721;CA1502;CA1065;NU5104; + $(NoWarn) + + + + + + + + + + + + + + + + PreserveNewest + true + + + PreserveNewest + true + + + diff --git a/Importers/Ifc/Speckle.Importers.Ifc/SpeckleIfcException.cs b/Importers/Ifc/Speckle.Importers.Ifc/SpeckleIfcException.cs new file mode 100644 index 000000000..4ab39d11c --- /dev/null +++ b/Importers/Ifc/Speckle.Importers.Ifc/SpeckleIfcException.cs @@ -0,0 +1,14 @@ +using Speckle.Sdk; + +namespace Speckle.Importers.Ifc; + +public class SpeckleIfcException : SpeckleException +{ + public SpeckleIfcException() { } + + public SpeckleIfcException(string? message) + : base(message) { } + + public SpeckleIfcException(string? message, Exception? inner = null) + : base(message, inner) { } +} diff --git a/Importers/Ifc/Speckle.Importers.Ifc/Types/IfcColor.cs b/Importers/Ifc/Speckle.Importers.Ifc/Types/IfcColor.cs new file mode 100644 index 000000000..9a99790b2 --- /dev/null +++ b/Importers/Ifc/Speckle.Importers.Ifc/Types/IfcColor.cs @@ -0,0 +1,12 @@ +using System.Runtime.InteropServices; + +namespace Speckle.Importers.Ifc.Types; + +[StructLayout(LayoutKind.Sequential, Pack = 1)] +public readonly struct IfcColor +{ + public readonly double R, + G, + B, + A; +} diff --git a/Importers/Ifc/Speckle.Importers.Ifc/Types/IfcFactory.cs b/Importers/Ifc/Speckle.Importers.Ifc/Types/IfcFactory.cs new file mode 100644 index 000000000..3689ff4ed --- /dev/null +++ b/Importers/Ifc/Speckle.Importers.Ifc/Types/IfcFactory.cs @@ -0,0 +1,21 @@ +using Speckle.InterfaceGenerator; + +namespace Speckle.Importers.Ifc.Types; + +[GenerateAutoInterface] +public sealed class IfcFactory : IIfcFactory +{ + //probably never disposing this + private static readonly IntPtr _ptr = Importers.Ifc.Native.WebIfc.InitializeApi(); + + public IfcModel Open(string fullPath) + { + if (!File.Exists(fullPath)) + { + throw new ArgumentException($"File does not exist: {fullPath}"); + } + return new(Importers.Ifc.Native.WebIfc.LoadModel(_ptr, fullPath)); + } + + public string Version => Importers.Ifc.Native.WebIfc.GetVersion(); +} diff --git a/Importers/Ifc/Speckle.Importers.Ifc/Types/IfcGeometry.cs b/Importers/Ifc/Speckle.Importers.Ifc/Types/IfcGeometry.cs new file mode 100644 index 000000000..8cc5b48dd --- /dev/null +++ b/Importers/Ifc/Speckle.Importers.Ifc/Types/IfcGeometry.cs @@ -0,0 +1,18 @@ +namespace Speckle.Importers.Ifc.Types; + +public sealed class IfcGeometry(IntPtr geometry) +{ + public IfcMesh GetMesh(int i) => new(Importers.Ifc.Native.WebIfc.GetMesh(geometry, i)); + + public int MeshCount => Importers.Ifc.Native.WebIfc.GetNumMeshes(geometry); + + public IfcSchemaType Type => (IfcSchemaType)Importers.Ifc.Native.WebIfc.GetGeometryType(geometry); + + public IEnumerable GetMeshes() + { + for (int i = 0; i < MeshCount; ++i) + { + yield return GetMesh(i); + } + } +} diff --git a/Importers/Ifc/Speckle.Importers.Ifc/Types/IfcLine.cs b/Importers/Ifc/Speckle.Importers.Ifc/Types/IfcLine.cs new file mode 100644 index 000000000..12ddb010c --- /dev/null +++ b/Importers/Ifc/Speckle.Importers.Ifc/Types/IfcLine.cs @@ -0,0 +1,9 @@ +namespace Speckle.Importers.Ifc.Types; + +public sealed class IfcLine(IntPtr line) +{ + public uint Id => Importers.Ifc.Native.WebIfc.GetLineId(line); + public IfcSchemaType Type => (IfcSchemaType)Importers.Ifc.Native.WebIfc.GetLineType(line); + + public string Arguments() => Importers.Ifc.Native.WebIfc.GetLineArguments(line); +} diff --git a/Importers/Ifc/Speckle.Importers.Ifc/Types/IfcMesh.cs b/Importers/Ifc/Speckle.Importers.Ifc/Types/IfcMesh.cs new file mode 100644 index 000000000..00543cc0f --- /dev/null +++ b/Importers/Ifc/Speckle.Importers.Ifc/Types/IfcMesh.cs @@ -0,0 +1,40 @@ +using System.Runtime.InteropServices; +using Speckle.Importers.Ifc.Native; + +namespace Speckle.Importers.Ifc.Types; + +public sealed class IfcMesh(IntPtr mesh) +{ + public int VerticesCount => WebIfc.GetNumVertices(mesh); + + public unsafe ReadOnlySpan Vertices + { + get + { + IfcVertex* ptr = (IfcVertex*)WebIfc.GetVertices(mesh); + return new ReadOnlySpan(ptr, VerticesCount); + } + } + + public unsafe ReadOnlySpan Transform + { + get + { + double* ptr = (double*)WebIfc.GetTransform(mesh); + return new ReadOnlySpan(ptr, 16); + } + } + + public int IndicesCount => WebIfc.GetNumIndices(mesh); + + public unsafe ReadOnlySpan Indices + { + get + { + var ptr = (int*)WebIfc.GetIndices(mesh); + return new ReadOnlySpan(ptr, IndicesCount); + } + } + + public IfcColor Color => Marshal.PtrToStructure(WebIfc.GetColor(mesh)); +} diff --git a/Importers/Ifc/Speckle.Importers.Ifc/Types/IfcModel.cs b/Importers/Ifc/Speckle.Importers.Ifc/Types/IfcModel.cs new file mode 100644 index 000000000..06184a04d --- /dev/null +++ b/Importers/Ifc/Speckle.Importers.Ifc/Types/IfcModel.cs @@ -0,0 +1,33 @@ +namespace Speckle.Importers.Ifc.Types; + +public sealed class IfcModel(IntPtr model) +{ + public int GetNumGeometries() => Importers.Ifc.Native.WebIfc.GetNumGeometries(model); + + public IfcGeometry? GetGeometry(uint id) + { + var geometry = Importers.Ifc.Native.WebIfc.GetGeometryFromId(model, id); + return geometry == IntPtr.Zero ? null : new IfcGeometry(geometry); + } + + public IEnumerable GetGeometries() + { + var numGeometries = Importers.Ifc.Native.WebIfc.GetNumGeometries(model); + for (int i = 0; i < numGeometries; ++i) + { + var gPtr = Importers.Ifc.Native.WebIfc.GetGeometryFromIndex(model, i); + if (gPtr != IntPtr.Zero) + { + yield return new IfcGeometry(gPtr); + } + } + } + + public uint GetMaxId() => Importers.Ifc.Native.WebIfc.GetMaxId(model); + + public IfcLine? GetLine(uint id) + { + var line = Importers.Ifc.Native.WebIfc.GetLineFromModel(model, id); + return line == IntPtr.Zero ? null : new IfcLine(line); + } +} diff --git a/Importers/Ifc/Speckle.Importers.Ifc/Types/IfcSchemaType.cs b/Importers/Ifc/Speckle.Importers.Ifc/Types/IfcSchemaType.cs new file mode 100644 index 000000000..6c25971d8 --- /dev/null +++ b/Importers/Ifc/Speckle.Importers.Ifc/Types/IfcSchemaType.cs @@ -0,0 +1,1149 @@ +namespace Speckle.Importers.Ifc.Types; + +#pragma warning disable CA1028 +#pragma warning disable CA1008 +public enum IfcSchemaType : uint +{ +#pragma warning restore CA1008 +#pragma warning restore CA1028 + FILE_SCHEMA = 1109904537, + FILE_NAME = 1390159747, + FILE_DESCRIPTION = 599546466, + IFCACTORROLE = 3630933823, + IFCADDRESS = 618182010, + IFCAPPLICATION = 639542469, + IFCAPPLIEDVALUE = 411424972, + IFCAPPLIEDVALUERELATIONSHIP = 1110488051, + IFCAPPROVAL = 130549933, + IFCAPPROVALACTORRELATIONSHIP = 2080292479, + IFCAPPROVALPROPERTYRELATIONSHIP = 390851274, + IFCAPPROVALRELATIONSHIP = 3869604511, + IFCBOUNDARYCONDITION = 4037036970, + IFCBOUNDARYEDGECONDITION = 1560379544, + IFCBOUNDARYFACECONDITION = 3367102660, + IFCBOUNDARYNODECONDITION = 1387855156, + IFCBOUNDARYNODECONDITIONWARPING = 2069777674, + IFCCALENDARDATE = 622194075, + IFCCLASSIFICATION = 747523909, + IFCCLASSIFICATIONITEM = 1767535486, + IFCCLASSIFICATIONITEMRELATIONSHIP = 1098599126, + IFCCLASSIFICATIONNOTATION = 938368621, + IFCCLASSIFICATIONNOTATIONFACET = 3639012971, + IFCCOLOURSPECIFICATION = 3264961684, + IFCCONNECTIONGEOMETRY = 2859738748, + IFCCONNECTIONPOINTGEOMETRY = 2614616156, + IFCCONNECTIONPORTGEOMETRY = 4257277454, + IFCCONNECTIONSURFACEGEOMETRY = 2732653382, + IFCCONSTRAINT = 1959218052, + IFCCONSTRAINTAGGREGATIONRELATIONSHIP = 1658513725, + IFCCONSTRAINTCLASSIFICATIONRELATIONSHIP = 613356794, + IFCCONSTRAINTRELATIONSHIP = 347226245, + IFCCOORDINATEDUNIVERSALTIMEOFFSET = 1065062679, + IFCCOSTVALUE = 602808272, + IFCCURRENCYRELATIONSHIP = 539742890, + IFCCURVESTYLEFONT = 1105321065, + IFCCURVESTYLEFONTANDSCALING = 2367409068, + IFCCURVESTYLEFONTPATTERN = 3510044353, + IFCDATEANDTIME = 1072939445, + IFCDERIVEDUNIT = 1765591967, + IFCDERIVEDUNITELEMENT = 1045800335, + IFCDIMENSIONALEXPONENTS = 2949456006, + IFCDOCUMENTELECTRONICFORMAT = 1376555844, + IFCDOCUMENTINFORMATION = 1154170062, + IFCDOCUMENTINFORMATIONRELATIONSHIP = 770865208, + IFCDRAUGHTINGCALLOUTRELATIONSHIP = 3796139169, + IFCENVIRONMENTALIMPACTVALUE = 1648886627, + IFCEXTERNALREFERENCE = 3200245327, + IFCEXTERNALLYDEFINEDHATCHSTYLE = 2242383968, + IFCEXTERNALLYDEFINEDSURFACESTYLE = 1040185647, + IFCEXTERNALLYDEFINEDSYMBOL = 3207319532, + IFCEXTERNALLYDEFINEDTEXTFONT = 3548104201, + IFCGRIDAXIS = 852622518, + IFCIRREGULARTIMESERIESVALUE = 3020489413, + IFCLIBRARYINFORMATION = 2655187982, + IFCLIBRARYREFERENCE = 3452421091, + IFCLIGHTDISTRIBUTIONDATA = 4162380809, + IFCLIGHTINTENSITYDISTRIBUTION = 1566485204, + IFCLOCALTIME = 30780891, + IFCMATERIAL = 1838606355, + IFCMATERIALCLASSIFICATIONRELATIONSHIP = 1847130766, + IFCMATERIALLAYER = 248100487, + IFCMATERIALLAYERSET = 3303938423, + IFCMATERIALLAYERSETUSAGE = 1303795690, + IFCMATERIALLIST = 2199411900, + IFCMATERIALPROPERTIES = 3265635763, + IFCMEASUREWITHUNIT = 2597039031, + IFCMECHANICALMATERIALPROPERTIES = 4256014907, + IFCMECHANICALSTEELMATERIALPROPERTIES = 677618848, + IFCMETRIC = 3368373690, + IFCMONETARYUNIT = 2706619895, + IFCNAMEDUNIT = 1918398963, + IFCOBJECTPLACEMENT = 3701648758, + IFCOBJECTIVE = 2251480897, + IFCOPTICALMATERIALPROPERTIES = 1227763645, + IFCORGANIZATION = 4251960020, + IFCORGANIZATIONRELATIONSHIP = 1411181986, + IFCOWNERHISTORY = 1207048766, + IFCPERSON = 2077209135, + IFCPERSONANDORGANIZATION = 101040310, + IFCPHYSICALQUANTITY = 2483315170, + IFCPHYSICALSIMPLEQUANTITY = 2226359599, + IFCPOSTALADDRESS = 3355820592, + IFCPREDEFINEDITEM = 3727388367, + IFCPREDEFINEDSYMBOL = 990879717, + IFCPREDEFINEDTERMINATORSYMBOL = 3213052703, + IFCPREDEFINEDTEXTFONT = 1775413392, + IFCPRESENTATIONLAYERASSIGNMENT = 2022622350, + IFCPRESENTATIONLAYERWITHSTYLE = 1304840413, + IFCPRESENTATIONSTYLE = 3119450353, + IFCPRESENTATIONSTYLEASSIGNMENT = 2417041796, + IFCPRODUCTREPRESENTATION = 2095639259, + IFCPRODUCTSOFCOMBUSTIONPROPERTIES = 2267347899, + IFCPROFILEDEF = 3958567839, + IFCPROFILEPROPERTIES = 2802850158, + IFCPROPERTY = 2598011224, + IFCPROPERTYCONSTRAINTRELATIONSHIP = 3896028662, + IFCPROPERTYDEPENDENCYRELATIONSHIP = 148025276, + IFCPROPERTYENUMERATION = 3710013099, + IFCQUANTITYAREA = 2044713172, + IFCQUANTITYCOUNT = 2093928680, + IFCQUANTITYLENGTH = 931644368, + IFCQUANTITYTIME = 3252649465, + IFCQUANTITYVOLUME = 2405470396, + IFCQUANTITYWEIGHT = 825690147, + IFCREFERENCESVALUEDOCUMENT = 2692823254, + IFCREINFORCEMENTBARPROPERTIES = 1580146022, + IFCRELAXATION = 1222501353, + IFCREPRESENTATION = 1076942058, + IFCREPRESENTATIONCONTEXT = 3377609919, + IFCREPRESENTATIONITEM = 3008791417, + IFCREPRESENTATIONMAP = 1660063152, + IFCRIBPLATEPROFILEPROPERTIES = 3679540991, + IFCROOT = 2341007311, + IFCSIUNIT = 448429030, + IFCSECTIONPROPERTIES = 2042790032, + IFCSECTIONREINFORCEMENTPROPERTIES = 4165799628, + IFCSHAPEASPECT = 867548509, + IFCSHAPEMODEL = 3982875396, + IFCSHAPEREPRESENTATION = 4240577450, + IFCSIMPLEPROPERTY = 3692461612, + IFCSTRUCTURALCONNECTIONCONDITION = 2273995522, + IFCSTRUCTURALLOAD = 2162789131, + IFCSTRUCTURALLOADSTATIC = 2525727697, + IFCSTRUCTURALLOADTEMPERATURE = 3408363356, + IFCSTYLEMODEL = 2830218821, + IFCSTYLEDITEM = 3958052878, + IFCSTYLEDREPRESENTATION = 3049322572, + IFCSURFACESTYLE = 1300840506, + IFCSURFACESTYLELIGHTING = 3303107099, + IFCSURFACESTYLEREFRACTION = 1607154358, + IFCSURFACESTYLESHADING = 846575682, + IFCSURFACESTYLEWITHTEXTURES = 1351298697, + IFCSURFACETEXTURE = 626085974, + IFCSYMBOLSTYLE = 1290481447, + IFCTABLE = 985171141, + IFCTABLEROW = 531007025, + IFCTELECOMADDRESS = 912023232, + IFCTEXTSTYLE = 1447204868, + IFCTEXTSTYLEFONTMODEL = 1983826977, + IFCTEXTSTYLEFORDEFINEDFONT = 2636378356, + IFCTEXTSTYLETEXTMODEL = 1640371178, + IFCTEXTSTYLEWITHBOXCHARACTERISTICS = 1484833681, + IFCTEXTURECOORDINATE = 280115917, + IFCTEXTURECOORDINATEGENERATOR = 1742049831, + IFCTEXTUREMAP = 2552916305, + IFCTEXTUREVERTEX = 1210645708, + IFCTHERMALMATERIALPROPERTIES = 3317419933, + IFCTIMESERIES = 3101149627, + IFCTIMESERIESREFERENCERELATIONSHIP = 1718945513, + IFCTIMESERIESVALUE = 581633288, + IFCTOPOLOGICALREPRESENTATIONITEM = 1377556343, + IFCTOPOLOGYREPRESENTATION = 1735638870, + IFCUNITASSIGNMENT = 180925521, + IFCVERTEX = 2799835756, + IFCVERTEXBASEDTEXTUREMAP = 3304826586, + IFCVERTEXPOINT = 1907098498, + IFCVIRTUALGRIDINTERSECTION = 891718957, + IFCWATERPROPERTIES = 1065908215, + IFCANNOTATIONOCCURRENCE = 2442683028, + IFCANNOTATIONSURFACEOCCURRENCE = 962685235, + IFCANNOTATIONSYMBOLOCCURRENCE = 3612888222, + IFCANNOTATIONTEXTOCCURRENCE = 2297822566, + IFCARBITRARYCLOSEDPROFILEDEF = 3798115385, + IFCARBITRARYOPENPROFILEDEF = 1310608509, + IFCARBITRARYPROFILEDEFWITHVOIDS = 2705031697, + IFCBLOBTEXTURE = 616511568, + IFCCENTERLINEPROFILEDEF = 3150382593, + IFCCLASSIFICATIONREFERENCE = 647927063, + IFCCOLOURRGB = 776857604, + IFCCOMPLEXPROPERTY = 2542286263, + IFCCOMPOSITEPROFILEDEF = 1485152156, + IFCCONNECTEDFACESET = 370225590, + IFCCONNECTIONCURVEGEOMETRY = 1981873012, + IFCCONNECTIONPOINTECCENTRICITY = 45288368, + IFCCONTEXTDEPENDENTUNIT = 3050246964, + IFCCONVERSIONBASEDUNIT = 2889183280, + IFCCURVESTYLE = 3800577675, + IFCDERIVEDPROFILEDEF = 3632507154, + IFCDIMENSIONCALLOUTRELATIONSHIP = 2273265877, + IFCDIMENSIONPAIR = 1694125774, + IFCDOCUMENTREFERENCE = 3732053477, + IFCDRAUGHTINGPREDEFINEDTEXTFONT = 4170525392, + IFCEDGE = 3900360178, + IFCEDGECURVE = 476780140, + IFCEXTENDEDMATERIALPROPERTIES = 1860660968, + IFCFACE = 2556980723, + IFCFACEBOUND = 1809719519, + IFCFACEOUTERBOUND = 803316827, + IFCFACESURFACE = 3008276851, + IFCFAILURECONNECTIONCONDITION = 4219587988, + IFCFILLAREASTYLE = 738692330, + IFCFUELPROPERTIES = 3857492461, + IFCGENERALMATERIALPROPERTIES = 803998398, + IFCGENERALPROFILEPROPERTIES = 1446786286, + IFCGEOMETRICREPRESENTATIONCONTEXT = 3448662350, + IFCGEOMETRICREPRESENTATIONITEM = 2453401579, + IFCGEOMETRICREPRESENTATIONSUBCONTEXT = 4142052618, + IFCGEOMETRICSET = 3590301190, + IFCGRIDPLACEMENT = 178086475, + IFCHALFSPACESOLID = 812098782, + IFCHYGROSCOPICMATERIALPROPERTIES = 2445078500, + IFCIMAGETEXTURE = 3905492369, + IFCIRREGULARTIMESERIES = 3741457305, + IFCLIGHTSOURCE = 1402838566, + IFCLIGHTSOURCEAMBIENT = 125510826, + IFCLIGHTSOURCEDIRECTIONAL = 2604431987, + IFCLIGHTSOURCEGONIOMETRIC = 4266656042, + IFCLIGHTSOURCEPOSITIONAL = 1520743889, + IFCLIGHTSOURCESPOT = 3422422726, + IFCLOCALPLACEMENT = 2624227202, + IFCLOOP = 1008929658, + IFCMAPPEDITEM = 2347385850, + IFCMATERIALDEFINITIONREPRESENTATION = 2022407955, + IFCMECHANICALCONCRETEMATERIALPROPERTIES = 1430189142, + IFCOBJECTDEFINITION = 219451334, + IFCONEDIRECTIONREPEATFACTOR = 2833995503, + IFCOPENSHELL = 2665983363, + IFCORIENTEDEDGE = 1029017970, + IFCPARAMETERIZEDPROFILEDEF = 2529465313, + IFCPATH = 2519244187, + IFCPHYSICALCOMPLEXQUANTITY = 3021840470, + IFCPIXELTEXTURE = 597895409, + IFCPLACEMENT = 2004835150, + IFCPLANAREXTENT = 1663979128, + IFCPOINT = 2067069095, + IFCPOINTONCURVE = 4022376103, + IFCPOINTONSURFACE = 1423911732, + IFCPOLYLOOP = 2924175390, + IFCPOLYGONALBOUNDEDHALFSPACE = 2775532180, + IFCPREDEFINEDCOLOUR = 759155922, + IFCPREDEFINEDCURVEFONT = 2559016684, + IFCPREDEFINEDDIMENSIONSYMBOL = 433424934, + IFCPREDEFINEDPOINTMARKERSYMBOL = 179317114, + IFCPRODUCTDEFINITIONSHAPE = 673634403, + IFCPROPERTYBOUNDEDVALUE = 871118103, + IFCPROPERTYDEFINITION = 1680319473, + IFCPROPERTYENUMERATEDVALUE = 4166981789, + IFCPROPERTYLISTVALUE = 2752243245, + IFCPROPERTYREFERENCEVALUE = 941946838, + IFCPROPERTYSETDEFINITION = 3357820518, + IFCPROPERTYSINGLEVALUE = 3650150729, + IFCPROPERTYTABLEVALUE = 110355661, + IFCRECTANGLEPROFILEDEF = 3615266464, + IFCREGULARTIMESERIES = 3413951693, + IFCREINFORCEMENTDEFINITIONPROPERTIES = 3765753017, + IFCRELATIONSHIP = 478536968, + IFCROUNDEDRECTANGLEPROFILEDEF = 2778083089, + IFCSECTIONEDSPINE = 1509187699, + IFCSERVICELIFEFACTOR = 2411513650, + IFCSHELLBASEDSURFACEMODEL = 4124623270, + IFCSLIPPAGECONNECTIONCONDITION = 2609359061, + IFCSOLIDMODEL = 723233188, + IFCSOUNDPROPERTIES = 2485662743, + IFCSOUNDVALUE = 1202362311, + IFCSPACETHERMALLOADPROPERTIES = 390701378, + IFCSTRUCTURALLOADLINEARFORCE = 1595516126, + IFCSTRUCTURALLOADPLANARFORCE = 2668620305, + IFCSTRUCTURALLOADSINGLEDISPLACEMENT = 2473145415, + IFCSTRUCTURALLOADSINGLEDISPLACEMENTDISTORTION = 1973038258, + IFCSTRUCTURALLOADSINGLEFORCE = 1597423693, + IFCSTRUCTURALLOADSINGLEFORCEWARPING = 1190533807, + IFCSTRUCTURALPROFILEPROPERTIES = 3843319758, + IFCSTRUCTURALSTEELPROFILEPROPERTIES = 3653947884, + IFCSUBEDGE = 2233826070, + IFCSURFACE = 2513912981, + IFCSURFACESTYLERENDERING = 1878645084, + IFCSWEPTAREASOLID = 2247615214, + IFCSWEPTDISKSOLID = 1260650574, + IFCSWEPTSURFACE = 230924584, + IFCTSHAPEPROFILEDEF = 3071757647, + IFCTERMINATORSYMBOL = 3028897424, + IFCTEXTLITERAL = 4282788508, + IFCTEXTLITERALWITHEXTENT = 3124975700, + IFCTRAPEZIUMPROFILEDEF = 2715220739, + IFCTWODIRECTIONREPEATFACTOR = 1345879162, + IFCTYPEOBJECT = 1628702193, + IFCTYPEPRODUCT = 2347495698, + IFCUSHAPEPROFILEDEF = 427810014, + IFCVECTOR = 1417489154, + IFCVERTEXLOOP = 2759199220, + IFCWINDOWLININGPROPERTIES = 336235671, + IFCWINDOWPANELPROPERTIES = 512836454, + IFCWINDOWSTYLE = 1299126871, + IFCZSHAPEPROFILEDEF = 2543172580, + IFCANNOTATIONCURVEOCCURRENCE = 3288037868, + IFCANNOTATIONFILLAREA = 669184980, + IFCANNOTATIONFILLAREAOCCURRENCE = 2265737646, + IFCANNOTATIONSURFACE = 1302238472, + IFCAXIS1PLACEMENT = 4261334040, + IFCAXIS2PLACEMENT2D = 3125803723, + IFCAXIS2PLACEMENT3D = 2740243338, + IFCBOOLEANRESULT = 2736907675, + IFCBOUNDEDSURFACE = 4182860854, + IFCBOUNDINGBOX = 2581212453, + IFCBOXEDHALFSPACE = 2713105998, + IFCCSHAPEPROFILEDEF = 2898889636, + IFCCARTESIANPOINT = 1123145078, + IFCCARTESIANTRANSFORMATIONOPERATOR = 59481748, + IFCCARTESIANTRANSFORMATIONOPERATOR2D = 3749851601, + IFCCARTESIANTRANSFORMATIONOPERATOR2DNONUNIFORM = 3486308946, + IFCCARTESIANTRANSFORMATIONOPERATOR3D = 3331915920, + IFCCARTESIANTRANSFORMATIONOPERATOR3DNONUNIFORM = 1416205885, + IFCCIRCLEPROFILEDEF = 1383045692, + IFCCLOSEDSHELL = 2205249479, + IFCCOMPOSITECURVESEGMENT = 2485617015, + IFCCRANERAILASHAPEPROFILEDEF = 4133800736, + IFCCRANERAILFSHAPEPROFILEDEF = 194851669, + IFCCSGPRIMITIVE3D = 2506170314, + IFCCSGSOLID = 2147822146, + IFCCURVE = 2601014836, + IFCCURVEBOUNDEDPLANE = 2827736869, + IFCDEFINEDSYMBOL = 693772133, + IFCDIMENSIONCURVE = 606661476, + IFCDIMENSIONCURVETERMINATOR = 4054601972, + IFCDIRECTION = 32440307, + IFCDOORLININGPROPERTIES = 2963535650, + IFCDOORPANELPROPERTIES = 1714330368, + IFCDOORSTYLE = 526551008, + IFCDRAUGHTINGCALLOUT = 3073041342, + IFCDRAUGHTINGPREDEFINEDCOLOUR = 445594917, + IFCDRAUGHTINGPREDEFINEDCURVEFONT = 4006246654, + IFCEDGELOOP = 1472233963, + IFCELEMENTQUANTITY = 1883228015, + IFCELEMENTTYPE = 339256511, + IFCELEMENTARYSURFACE = 2777663545, + IFCELLIPSEPROFILEDEF = 2835456948, + IFCENERGYPROPERTIES = 80994333, + IFCEXTRUDEDAREASOLID = 477187591, + IFCFACEBASEDSURFACEMODEL = 2047409740, + IFCFILLAREASTYLEHATCHING = 374418227, + IFCFILLAREASTYLETILESYMBOLWITHSTYLE = 4203026998, + IFCFILLAREASTYLETILES = 315944413, + IFCFLUIDFLOWPROPERTIES = 3455213021, + IFCFURNISHINGELEMENTTYPE = 4238390223, + IFCFURNITURETYPE = 1268542332, + IFCGEOMETRICCURVESET = 987898635, + IFCISHAPEPROFILEDEF = 1484403080, + IFCLSHAPEPROFILEDEF = 572779678, + IFCLINE = 1281925730, + IFCMANIFOLDSOLIDBREP = 1425443689, + IFCOBJECT = 3888040117, + IFCOFFSETCURVE2D = 3388369263, + IFCOFFSETCURVE3D = 3505215534, + IFCPERMEABLECOVERINGPROPERTIES = 3566463478, + IFCPLANARBOX = 603570806, + IFCPLANE = 220341763, + IFCPROCESS = 2945172077, + IFCPRODUCT = 4208778838, + IFCPROJECT = 103090709, + IFCPROJECTIONCURVE = 4194566429, + IFCPROPERTYSET = 1451395588, + IFCPROXY = 3219374653, + IFCRECTANGLEHOLLOWPROFILEDEF = 2770003689, + IFCRECTANGULARPYRAMID = 2798486643, + IFCRECTANGULARTRIMMEDSURFACE = 3454111270, + IFCRELASSIGNS = 3939117080, + IFCRELASSIGNSTOACTOR = 1683148259, + IFCRELASSIGNSTOCONTROL = 2495723537, + IFCRELASSIGNSTOGROUP = 1307041759, + IFCRELASSIGNSTOPROCESS = 4278684876, + IFCRELASSIGNSTOPRODUCT = 2857406711, + IFCRELASSIGNSTOPROJECTORDER = 3372526763, + IFCRELASSIGNSTORESOURCE = 205026976, + IFCRELASSOCIATES = 1865459582, + IFCRELASSOCIATESAPPLIEDVALUE = 1327628568, + IFCRELASSOCIATESAPPROVAL = 4095574036, + IFCRELASSOCIATESCLASSIFICATION = 919958153, + IFCRELASSOCIATESCONSTRAINT = 2728634034, + IFCRELASSOCIATESDOCUMENT = 982818633, + IFCRELASSOCIATESLIBRARY = 3840914261, + IFCRELASSOCIATESMATERIAL = 2655215786, + IFCRELASSOCIATESPROFILEPROPERTIES = 2851387026, + IFCRELCONNECTS = 826625072, + IFCRELCONNECTSELEMENTS = 1204542856, + IFCRELCONNECTSPATHELEMENTS = 3945020480, + IFCRELCONNECTSPORTTOELEMENT = 4201705270, + IFCRELCONNECTSPORTS = 3190031847, + IFCRELCONNECTSSTRUCTURALACTIVITY = 2127690289, + IFCRELCONNECTSSTRUCTURALELEMENT = 3912681535, + IFCRELCONNECTSSTRUCTURALMEMBER = 1638771189, + IFCRELCONNECTSWITHECCENTRICITY = 504942748, + IFCRELCONNECTSWITHREALIZINGELEMENTS = 3678494232, + IFCRELCONTAINEDINSPATIALSTRUCTURE = 3242617779, + IFCRELCOVERSBLDGELEMENTS = 886880790, + IFCRELCOVERSSPACES = 2802773753, + IFCRELDECOMPOSES = 2551354335, + IFCRELDEFINES = 693640335, + IFCRELDEFINESBYPROPERTIES = 4186316022, + IFCRELDEFINESBYTYPE = 781010003, + IFCRELFILLSELEMENT = 3940055652, + IFCRELFLOWCONTROLELEMENTS = 279856033, + IFCRELINTERACTIONREQUIREMENTS = 4189434867, + IFCRELNESTS = 3268803585, + IFCRELOCCUPIESSPACES = 2051452291, + IFCRELOVERRIDESPROPERTIES = 202636808, + IFCRELPROJECTSELEMENT = 750771296, + IFCRELREFERENCEDINSPATIALSTRUCTURE = 1245217292, + IFCRELSCHEDULESCOSTITEMS = 1058617721, + IFCRELSEQUENCE = 4122056220, + IFCRELSERVICESBUILDINGS = 366585022, + IFCRELSPACEBOUNDARY = 3451746338, + IFCRELVOIDSELEMENT = 1401173127, + IFCRESOURCE = 2914609552, + IFCREVOLVEDAREASOLID = 1856042241, + IFCRIGHTCIRCULARCONE = 4158566097, + IFCRIGHTCIRCULARCYLINDER = 3626867408, + IFCSPATIALSTRUCTUREELEMENT = 2706606064, + IFCSPATIALSTRUCTUREELEMENTTYPE = 3893378262, + IFCSPHERE = 451544542, + IFCSTRUCTURALACTIVITY = 3544373492, + IFCSTRUCTURALITEM = 3136571912, + IFCSTRUCTURALMEMBER = 530289379, + IFCSTRUCTURALREACTION = 3689010777, + IFCSTRUCTURALSURFACEMEMBER = 3979015343, + IFCSTRUCTURALSURFACEMEMBERVARYING = 2218152070, + IFCSTRUCTUREDDIMENSIONCALLOUT = 4070609034, + IFCSURFACECURVESWEPTAREASOLID = 2028607225, + IFCSURFACEOFLINEAREXTRUSION = 2809605785, + IFCSURFACEOFREVOLUTION = 4124788165, + IFCSYSTEMFURNITUREELEMENTTYPE = 1580310250, + IFCTASK = 3473067441, + IFCTRANSPORTELEMENTTYPE = 2097647324, + IFCACTOR = 2296667514, + IFCANNOTATION = 1674181508, + IFCASYMMETRICISHAPEPROFILEDEF = 3207858831, + IFCBLOCK = 1334484129, + IFCBOOLEANCLIPPINGRESULT = 3649129432, + IFCBOUNDEDCURVE = 1260505505, + IFCBUILDING = 4031249490, + IFCBUILDINGELEMENTTYPE = 1950629157, + IFCBUILDINGSTOREY = 3124254112, + IFCCIRCLEHOLLOWPROFILEDEF = 2937912522, + IFCCOLUMNTYPE = 300633059, + IFCCOMPOSITECURVE = 3732776249, + IFCCONIC = 2510884976, + IFCCONSTRUCTIONRESOURCE = 2559216714, + IFCCONTROL = 3293443760, + IFCCOSTITEM = 3895139033, + IFCCOSTSCHEDULE = 1419761937, + IFCCOVERINGTYPE = 1916426348, + IFCCREWRESOURCE = 3295246426, + IFCCURTAINWALLTYPE = 1457835157, + IFCDIMENSIONCURVEDIRECTEDCALLOUT = 681481545, + IFCDISTRIBUTIONELEMENTTYPE = 3256556792, + IFCDISTRIBUTIONFLOWELEMENTTYPE = 3849074793, + IFCELECTRICALBASEPROPERTIES = 360485395, + IFCELEMENT = 1758889154, + IFCELEMENTASSEMBLY = 4123344466, + IFCELEMENTCOMPONENT = 1623761950, + IFCELEMENTCOMPONENTTYPE = 2590856083, + IFCELLIPSE = 1704287377, + IFCENERGYCONVERSIONDEVICETYPE = 2107101300, + IFCEQUIPMENTELEMENT = 1962604670, + IFCEQUIPMENTSTANDARD = 3272907226, + IFCEVAPORATIVECOOLERTYPE = 3174744832, + IFCEVAPORATORTYPE = 3390157468, + IFCFACETEDBREP = 807026263, + IFCFACETEDBREPWITHVOIDS = 3737207727, + IFCFASTENER = 647756555, + IFCFASTENERTYPE = 2489546625, + IFCFEATUREELEMENT = 2827207264, + IFCFEATUREELEMENTADDITION = 2143335405, + IFCFEATUREELEMENTSUBTRACTION = 1287392070, + IFCFLOWCONTROLLERTYPE = 3907093117, + IFCFLOWFITTINGTYPE = 3198132628, + IFCFLOWMETERTYPE = 3815607619, + IFCFLOWMOVINGDEVICETYPE = 1482959167, + IFCFLOWSEGMENTTYPE = 1834744321, + IFCFLOWSTORAGEDEVICETYPE = 1339347760, + IFCFLOWTERMINALTYPE = 2297155007, + IFCFLOWTREATMENTDEVICETYPE = 3009222698, + IFCFURNISHINGELEMENT = 263784265, + IFCFURNITURESTANDARD = 814719939, + IFCGASTERMINALTYPE = 200128114, + IFCGRID = 3009204131, + IFCGROUP = 2706460486, + IFCHEATEXCHANGERTYPE = 1251058090, + IFCHUMIDIFIERTYPE = 1806887404, + IFCINVENTORY = 2391368822, + IFCJUNCTIONBOXTYPE = 4288270099, + IFCLABORRESOURCE = 3827777499, + IFCLAMPTYPE = 1051575348, + IFCLIGHTFIXTURETYPE = 1161773419, + IFCLINEARDIMENSION = 2506943328, + IFCMECHANICALFASTENER = 377706215, + IFCMECHANICALFASTENERTYPE = 2108223431, + IFCMEMBERTYPE = 3181161470, + IFCMOTORCONNECTIONTYPE = 977012517, + IFCMOVE = 1916936684, + IFCOCCUPANT = 4143007308, + IFCOPENINGELEMENT = 3588315303, + IFCORDERACTION = 3425660407, + IFCOUTLETTYPE = 2837617999, + IFCPERFORMANCEHISTORY = 2382730787, + IFCPERMIT = 3327091369, + IFCPIPEFITTINGTYPE = 804291784, + IFCPIPESEGMENTTYPE = 4231323485, + IFCPLATETYPE = 4017108033, + IFCPOLYLINE = 3724593414, + IFCPORT = 3740093272, + IFCPROCEDURE = 2744685151, + IFCPROJECTORDER = 2904328755, + IFCPROJECTORDERRECORD = 3642467123, + IFCPROJECTIONELEMENT = 3651124850, + IFCPROTECTIVEDEVICETYPE = 1842657554, + IFCPUMPTYPE = 2250791053, + IFCRADIUSDIMENSION = 3248260540, + IFCRAILINGTYPE = 2893384427, + IFCRAMPFLIGHTTYPE = 2324767716, + IFCRELAGGREGATES = 160246688, + IFCRELASSIGNSTASKS = 2863920197, + IFCSANITARYTERMINALTYPE = 1768891740, + IFCSCHEDULETIMECONTROL = 3517283431, + IFCSERVICELIFE = 4105383287, + IFCSITE = 4097777520, + IFCSLABTYPE = 2533589738, + IFCSPACE = 3856911033, + IFCSPACEHEATERTYPE = 1305183839, + IFCSPACEPROGRAM = 652456506, + IFCSPACETYPE = 3812236995, + IFCSTACKTERMINALTYPE = 3112655638, + IFCSTAIRFLIGHTTYPE = 1039846685, + IFCSTRUCTURALACTION = 682877961, + IFCSTRUCTURALCONNECTION = 1179482911, + IFCSTRUCTURALCURVECONNECTION = 4243806635, + IFCSTRUCTURALCURVEMEMBER = 214636428, + IFCSTRUCTURALCURVEMEMBERVARYING = 2445595289, + IFCSTRUCTURALLINEARACTION = 1807405624, + IFCSTRUCTURALLINEARACTIONVARYING = 1721250024, + IFCSTRUCTURALLOADGROUP = 1252848954, + IFCSTRUCTURALPLANARACTION = 1621171031, + IFCSTRUCTURALPLANARACTIONVARYING = 3987759626, + IFCSTRUCTURALPOINTACTION = 2082059205, + IFCSTRUCTURALPOINTCONNECTION = 734778138, + IFCSTRUCTURALPOINTREACTION = 1235345126, + IFCSTRUCTURALRESULTGROUP = 2986769608, + IFCSTRUCTURALSURFACECONNECTION = 1975003073, + IFCSUBCONTRACTRESOURCE = 148013059, + IFCSWITCHINGDEVICETYPE = 2315554128, + IFCSYSTEM = 2254336722, + IFCTANKTYPE = 5716631, + IFCTIMESERIESSCHEDULE = 1637806684, + IFCTRANSFORMERTYPE = 1692211062, + IFCTRANSPORTELEMENT = 1620046519, + IFCTRIMMEDCURVE = 3593883385, + IFCTUBEBUNDLETYPE = 1600972822, + IFCUNITARYEQUIPMENTTYPE = 1911125066, + IFCVALVETYPE = 728799441, + IFCVIRTUALELEMENT = 2769231204, + IFCWALLTYPE = 1898987631, + IFCWASTETERMINALTYPE = 1133259667, + IFCWORKCONTROL = 1028945134, + IFCWORKPLAN = 4218914973, + IFCWORKSCHEDULE = 3342526732, + IFCZONE = 1033361043, + IFC2DCOMPOSITECURVE = 1213861670, + IFCACTIONREQUEST = 3821786052, + IFCAIRTERMINALBOXTYPE = 1411407467, + IFCAIRTERMINALTYPE = 3352864051, + IFCAIRTOAIRHEATRECOVERYTYPE = 1871374353, + IFCANGULARDIMENSION = 2470393545, + IFCASSET = 3460190687, + IFCBSPLINECURVE = 1967976161, + IFCBEAMTYPE = 819618141, + IFCBEZIERCURVE = 1916977116, + IFCBOILERTYPE = 231477066, + IFCBUILDINGELEMENT = 3299480353, + IFCBUILDINGELEMENTCOMPONENT = 52481810, + IFCBUILDINGELEMENTPART = 2979338954, + IFCBUILDINGELEMENTPROXY = 1095909175, + IFCBUILDINGELEMENTPROXYTYPE = 1909888760, + IFCCABLECARRIERFITTINGTYPE = 395041908, + IFCCABLECARRIERSEGMENTTYPE = 3293546465, + IFCCABLESEGMENTTYPE = 1285652485, + IFCCHILLERTYPE = 2951183804, + IFCCIRCLE = 2611217952, + IFCCOILTYPE = 2301859152, + IFCCOLUMN = 843113511, + IFCCOMPRESSORTYPE = 3850581409, + IFCCONDENSERTYPE = 2816379211, + IFCCONDITION = 2188551683, + IFCCONDITIONCRITERION = 1163958913, + IFCCONSTRUCTIONEQUIPMENTRESOURCE = 3898045240, + IFCCONSTRUCTIONMATERIALRESOURCE = 1060000209, + IFCCONSTRUCTIONPRODUCTRESOURCE = 488727124, + IFCCOOLEDBEAMTYPE = 335055490, + IFCCOOLINGTOWERTYPE = 2954562838, + IFCCOVERING = 1973544240, + IFCCURTAINWALL = 3495092785, + IFCDAMPERTYPE = 3961806047, + IFCDIAMETERDIMENSION = 4147604152, + IFCDISCRETEACCESSORY = 1335981549, + IFCDISCRETEACCESSORYTYPE = 2635815018, + IFCDISTRIBUTIONCHAMBERELEMENTTYPE = 1599208980, + IFCDISTRIBUTIONCONTROLELEMENTTYPE = 2063403501, + IFCDISTRIBUTIONELEMENT = 1945004755, + IFCDISTRIBUTIONFLOWELEMENT = 3040386961, + IFCDISTRIBUTIONPORT = 3041715199, + IFCDOOR = 395920057, + IFCDUCTFITTINGTYPE = 869906466, + IFCDUCTSEGMENTTYPE = 3760055223, + IFCDUCTSILENCERTYPE = 2030761528, + IFCEDGEFEATURE = 855621170, + IFCELECTRICAPPLIANCETYPE = 663422040, + IFCELECTRICFLOWSTORAGEDEVICETYPE = 3277789161, + IFCELECTRICGENERATORTYPE = 1534661035, + IFCELECTRICHEATERTYPE = 1365060375, + IFCELECTRICMOTORTYPE = 1217240411, + IFCELECTRICTIMECONTROLTYPE = 712377611, + IFCELECTRICALCIRCUIT = 1634875225, + IFCELECTRICALELEMENT = 857184966, + IFCENERGYCONVERSIONDEVICE = 1658829314, + IFCFANTYPE = 346874300, + IFCFILTERTYPE = 1810631287, + IFCFIRESUPPRESSIONTERMINALTYPE = 4222183408, + IFCFLOWCONTROLLER = 2058353004, + IFCFLOWFITTING = 4278956645, + IFCFLOWINSTRUMENTTYPE = 4037862832, + IFCFLOWMOVINGDEVICE = 3132237377, + IFCFLOWSEGMENT = 987401354, + IFCFLOWSTORAGEDEVICE = 707683696, + IFCFLOWTERMINAL = 2223149337, + IFCFLOWTREATMENTDEVICE = 3508470533, + IFCFOOTING = 900683007, + IFCMEMBER = 1073191201, + IFCPILE = 1687234759, + IFCPLATE = 3171933400, + IFCRAILING = 2262370178, + IFCRAMP = 3024970846, + IFCRAMPFLIGHT = 3283111854, + IFCRATIONALBEZIERCURVE = 3055160366, + IFCREINFORCINGELEMENT = 3027567501, + IFCREINFORCINGMESH = 2320036040, + IFCROOF = 2016517767, + IFCROUNDEDEDGEFEATURE = 1376911519, + IFCSENSORTYPE = 1783015770, + IFCSLAB = 1529196076, + IFCSTAIR = 331165859, + IFCSTAIRFLIGHT = 4252922144, + IFCSTRUCTURALANALYSISMODEL = 2515109513, + IFCTENDON = 3824725483, + IFCTENDONANCHOR = 2347447852, + IFCVIBRATIONISOLATORTYPE = 3313531582, + IFCWALL = 2391406946, + IFCWALLSTANDARDCASE = 3512223829, + IFCWINDOW = 3304561284, + IFCACTUATORTYPE = 2874132201, + IFCALARMTYPE = 3001207471, + IFCBEAM = 753842376, + IFCCHAMFEREDGEFEATURE = 2454782716, + IFCCONTROLLERTYPE = 578613899, + IFCDISTRIBUTIONCHAMBERELEMENT = 1052013943, + IFCDISTRIBUTIONCONTROLELEMENT = 1062813311, + IFCELECTRICDISTRIBUTIONPOINT = 3700593921, + IFCREINFORCINGBAR = 979691226, + IFCCONNECTIONVOLUMEGEOMETRY = 775493141, + IFCCOORDINATEOPERATION = 1785450214, + IFCCOORDINATEREFERENCESYSTEM = 1466758467, + IFCEXTERNALINFORMATION = 4294318154, + IFCMAPCONVERSION = 3057273783, + IFCMATERIALDEFINITION = 760658860, + IFCMATERIALLAYERWITHOFFSETS = 1847252529, + IFCMATERIALPROFILE = 2235152071, + IFCMATERIALPROFILESET = 164193824, + IFCMATERIALPROFILEWITHOFFSETS = 552965576, + IFCMATERIALUSAGEDEFINITION = 1507914824, + IFCPRESENTATIONITEM = 677532197, + IFCPROJECTEDCRS = 3843373140, + IFCPROPERTYABSTRACTION = 986844984, + IFCRECURRENCEPATTERN = 3915482550, + IFCREFERENCE = 2433181523, + IFCRESOURCELEVELRELATIONSHIP = 2439245199, + IFCSCHEDULINGTIME = 1054537805, + IFCSTRUCTURALLOADCONFIGURATION = 3478079324, + IFCSTRUCTURALLOADORRESULT = 609421318, + IFCSURFACEREINFORCEMENTAREA = 2934153892, + IFCTABLECOLUMN = 2043862942, + IFCTASKTIME = 1549132990, + IFCTASKTIMERECURRING = 2771591690, + IFCTEXTUREVERTEXLIST = 3611470254, + IFCTIMEPERIOD = 1199560280, + IFCWORKTIME = 1236880293, + IFCCOLOURRGBLIST = 3285139300, + IFCCONVERSIONBASEDUNITWITHOFFSET = 2713554722, + IFCEVENTTIME = 211053100, + IFCEXTENDEDPROPERTIES = 297599258, + IFCEXTERNALREFERENCERELATIONSHIP = 1437805879, + IFCINDEXEDCOLOURMAP = 3570813810, + IFCINDEXEDTEXTUREMAP = 1437953363, + IFCINDEXEDTRIANGLETEXTUREMAP = 2133299955, + IFCLAGTIME = 1585845231, + IFCMATERIALCONSTITUENT = 3708119000, + IFCMATERIALCONSTITUENTSET = 2852063980, + IFCMATERIALPROFILESETUSAGE = 3079605661, + IFCMATERIALPROFILESETUSAGETAPERING = 3404854881, + IFCMATERIALRELATIONSHIP = 853536259, + IFCMIRROREDPROFILEDEF = 2998442950, + IFCPREDEFINEDPROPERTIES = 3778827333, + IFCPROPERTYTEMPLATEDEFINITION = 1482703590, + IFCQUANTITYSET = 2090586900, + IFCRESOURCEAPPROVALRELATIONSHIP = 2943643501, + IFCRESOURCECONSTRAINTRELATIONSHIP = 1608871552, + IFCRESOURCETIME = 1042787934, + IFCSWEPTDISKSOLIDPOLYGONAL = 1096409881, + IFCTESSELLATEDITEM = 901063453, + IFCTYPEPROCESS = 3736923433, + IFCTYPERESOURCE = 3698973494, + IFCADVANCEDFACE = 3406155212, + IFCCARTESIANPOINTLIST = 574549367, + IFCCARTESIANPOINTLIST2D = 1675464909, + IFCCARTESIANPOINTLIST3D = 2059837836, + IFCCONSTRUCTIONRESOURCETYPE = 2574617495, + IFCCONTEXT = 3419103109, + IFCCREWRESOURCETYPE = 1815067380, + IFCCURVEBOUNDEDSURFACE = 2629017746, + IFCEVENTTYPE = 4024345920, + IFCEXTRUDEDAREASOLIDTAPERED = 2804161546, + IFCFIXEDREFERENCESWEPTAREASOLID = 2652556860, + IFCGEOGRAPHICELEMENTTYPE = 4095422895, + IFCINDEXEDPOLYGONALFACE = 178912537, + IFCINDEXEDPOLYGONALFACEWITHVOIDS = 2294589976, + IFCLABORRESOURCETYPE = 428585644, + IFCPCURVE = 1682466193, + IFCPREDEFINEDPROPERTYSET = 3967405729, + IFCPROCEDURETYPE = 569719735, + IFCPROJECTLIBRARY = 653396225, + IFCPROPERTYSETTEMPLATE = 492091185, + IFCPROPERTYTEMPLATE = 3521284610, + IFCRELASSIGNSTOGROUPBYFACTOR = 1027710054, + IFCRELDECLARES = 2565941209, + IFCRELDEFINESBYOBJECT = 1462361463, + IFCRELDEFINESBYTEMPLATE = 307848117, + IFCRELINTERFERESELEMENTS = 427948657, + IFCRELSPACEBOUNDARY1STLEVEL = 3523091289, + IFCRELSPACEBOUNDARY2NDLEVEL = 1521410863, + IFCREPARAMETRISEDCOMPOSITECURVESEGMENT = 816062949, + IFCREVOLVEDAREASOLIDTAPERED = 3243963512, + IFCSIMPLEPROPERTYTEMPLATE = 3663146110, + IFCSPATIALELEMENT = 1412071761, + IFCSPATIALELEMENTTYPE = 710998568, + IFCSPATIALZONE = 463610769, + IFCSPATIALZONETYPE = 2481509218, + IFCSPHERICALSURFACE = 4015995234, + IFCSTRUCTURALSURFACEREACTION = 603775116, + IFCSUBCONTRACTRESOURCETYPE = 4095615324, + IFCSURFACECURVE = 699246055, + IFCTASKTYPE = 3206491090, + IFCTESSELLATEDFACESET = 2387106220, + IFCTOROIDALSURFACE = 1935646853, + IFCTRIANGULATEDFACESET = 2916149573, + IFCADVANCEDBREP = 1635779807, + IFCADVANCEDBREPWITHVOIDS = 2603310189, + IFCBSPLINESURFACE = 2887950389, + IFCBSPLINESURFACEWITHKNOTS = 167062518, + IFCCHIMNEYTYPE = 2197970202, + IFCCIVILELEMENTTYPE = 3893394355, + IFCCOMPLEXPROPERTYTEMPLATE = 3875453745, + IFCCOMPOSITECURVEONSURFACE = 15328376, + IFCCONSTRUCTIONEQUIPMENTRESOURCETYPE = 2185764099, + IFCCONSTRUCTIONMATERIALRESOURCETYPE = 4105962743, + IFCCONSTRUCTIONPRODUCTRESOURCETYPE = 1525564444, + IFCCYLINDRICALSURFACE = 1213902940, + IFCDOORTYPE = 2323601079, + IFCELEMENTASSEMBLYTYPE = 2397081782, + IFCENGINETYPE = 132023988, + IFCEVENT = 4148101412, + IFCEXTERNALSPATIALSTRUCTUREELEMENT = 2853485674, + IFCFOOTINGTYPE = 1893162501, + IFCFURNITURE = 1509553395, + IFCGEOGRAPHICELEMENT = 3493046030, + IFCINDEXEDPOLYCURVE = 2571569899, + IFCINTERCEPTORTYPE = 3946677679, + IFCINTERSECTIONCURVE = 3113134337, + IFCMEDICALDEVICETYPE = 1114901282, + IFCOPENINGSTANDARDCASE = 3079942009, + IFCPILETYPE = 1158309216, + IFCPOLYGONALFACESET = 2839578677, + IFCRAMPTYPE = 1469900589, + IFCRATIONALBSPLINESURFACEWITHKNOTS = 683857671, + IFCREINFORCINGELEMENTTYPE = 964333572, + IFCREINFORCINGMESHTYPE = 2310774935, + IFCROOFTYPE = 2781568857, + IFCSEAMCURVE = 2157484638, + IFCSHADINGDEVICETYPE = 4074543187, + IFCSOLARDEVICETYPE = 1072016465, + IFCSTAIRTYPE = 338393293, + IFCSTRUCTURALCURVEACTION = 1004757350, + IFCSTRUCTURALCURVEREACTION = 2757150158, + IFCSTRUCTURALSURFACEACTION = 3657597509, + IFCSURFACEFEATURE = 3101698114, + IFCSYSTEMFURNITUREELEMENT = 413509423, + IFCTENDONANCHORTYPE = 3081323446, + IFCTENDONTYPE = 2415094496, + IFCVIBRATIONISOLATOR = 2391383451, + IFCVOIDINGFEATURE = 926996030, + IFCWINDOWTYPE = 4009809668, + IFCWORKCALENDAR = 4088093105, + IFCAUDIOVISUALAPPLIANCETYPE = 1532957894, + IFCBSPLINECURVEWITHKNOTS = 2461110595, + IFCBOUNDARYCURVE = 1136057603, + IFCBUILDINGELEMENTPARTTYPE = 39481116, + IFCBUILDINGSYSTEM = 1177604601, + IFCBURNERTYPE = 2188180465, + IFCCABLEFITTINGTYPE = 2674252688, + IFCCHIMNEY = 3296154744, + IFCCIVILELEMENT = 1677625105, + IFCCOLUMNSTANDARDCASE = 905975707, + IFCCOMMUNICATIONSAPPLIANCETYPE = 400855858, + IFCDISTRIBUTIONSYSTEM = 3205830791, + IFCDOORSTANDARDCASE = 3242481149, + IFCELECTRICDISTRIBUTIONBOARDTYPE = 2417008758, + IFCENGINE = 2814081492, + IFCEVAPORATIVECOOLER = 3747195512, + IFCEVAPORATOR = 484807127, + IFCEXTERNALSPATIALELEMENT = 1209101575, + IFCFLOWMETER = 2188021234, + IFCHEATEXCHANGER = 3319311131, + IFCHUMIDIFIER = 2068733104, + IFCINTERCEPTOR = 4175244083, + IFCJUNCTIONBOX = 2176052936, + IFCLAMP = 76236018, + IFCLIGHTFIXTURE = 629592764, + IFCMEDICALDEVICE = 1437502449, + IFCMEMBERSTANDARDCASE = 1911478936, + IFCMOTORCONNECTION = 2474470126, + IFCOUTERBOUNDARYCURVE = 144952367, + IFCOUTLET = 3694346114, + IFCPIPEFITTING = 310824031, + IFCPIPESEGMENT = 3612865200, + IFCPLATESTANDARDCASE = 1156407060, + IFCPROTECTIVEDEVICE = 738039164, + IFCPROTECTIVEDEVICETRIPPINGUNITTYPE = 655969474, + IFCPUMP = 90941305, + IFCRATIONALBSPLINECURVEWITHKNOTS = 1232101972, + IFCREINFORCINGBARTYPE = 2572171363, + IFCSANITARYTERMINAL = 3053780830, + IFCSHADINGDEVICE = 1329646415, + IFCSLABELEMENTEDCASE = 3127900445, + IFCSLABSTANDARDCASE = 3027962421, + IFCSOLARDEVICE = 3420628829, + IFCSPACEHEATER = 1999602285, + IFCSTACKTERMINAL = 1404847402, + IFCSTRUCTURALLOADCASE = 385403989, + IFCSWITCHINGDEVICE = 1162798199, + IFCTANK = 812556717, + IFCTRANSFORMER = 3825984169, + IFCTUBEBUNDLE = 3026737570, + IFCUNITARYCONTROLELEMENTTYPE = 3179687236, + IFCUNITARYEQUIPMENT = 4292641817, + IFCVALVE = 4207607924, + IFCWALLELEMENTEDCASE = 4156078855, + IFCWASTETERMINAL = 4237592921, + IFCWINDOWSTANDARDCASE = 486154966, + IFCAIRTERMINAL = 1634111441, + IFCAIRTERMINALBOX = 177149247, + IFCAIRTOAIRHEATRECOVERY = 2056796094, + IFCAUDIOVISUALAPPLIANCE = 277319702, + IFCBEAMSTANDARDCASE = 2906023776, + IFCBOILER = 32344328, + IFCBURNER = 2938176219, + IFCCABLECARRIERFITTING = 635142910, + IFCCABLECARRIERSEGMENT = 3758799889, + IFCCABLEFITTING = 1051757585, + IFCCABLESEGMENT = 4217484030, + IFCCHILLER = 3902619387, + IFCCOIL = 639361253, + IFCCOMMUNICATIONSAPPLIANCE = 3221913625, + IFCCOMPRESSOR = 3571504051, + IFCCONDENSER = 2272882330, + IFCCOOLEDBEAM = 4136498852, + IFCCOOLINGTOWER = 3640358203, + IFCDAMPER = 4074379575, + IFCDISTRIBUTIONCIRCUIT = 562808652, + IFCDUCTFITTING = 342316401, + IFCDUCTSEGMENT = 3518393246, + IFCDUCTSILENCER = 1360408905, + IFCELECTRICAPPLIANCE = 1904799276, + IFCELECTRICDISTRIBUTIONBOARD = 862014818, + IFCELECTRICFLOWSTORAGEDEVICE = 3310460725, + IFCELECTRICGENERATOR = 264262732, + IFCELECTRICMOTOR = 402227799, + IFCELECTRICTIMECONTROL = 1003880860, + IFCFAN = 3415622556, + IFCFILTER = 819412036, + IFCFIRESUPPRESSIONTERMINAL = 1426591983, + IFCFLOWINSTRUMENT = 182646315, + IFCPROTECTIVEDEVICETRIPPINGUNIT = 2295281155, + IFCSENSOR = 4086658281, + IFCUNITARYCONTROLELEMENT = 630975310, + IFCACTUATOR = 4288193352, + IFCALARM = 3087945054, + IFCCONTROLLER = 25142252, + IFCALIGNMENTPARAMETERSEGMENT = 2879124712, + IFCALIGNMENTVERTICALSEGMENT = 3633395639, + IFCQUANTITYNUMBER = 2691318326, + IFCTEXTURECOORDINATEINDICES = 222769930, + IFCTEXTURECOORDINATEINDICESWITHVOIDS = 1010789467, + IFCALIGNMENTCANTSEGMENT = 3752311538, + IFCALIGNMENTHORIZONTALSEGMENT = 536804194, + IFCLINEARPLACEMENT = 388784114, + IFCOPENCROSSPROFILEDEF = 182550632, + IFCPOINTBYDISTANCEEXPRESSION = 2165702409, + IFCSEGMENT = 823603102, + IFCAXIS2PLACEMENTLINEAR = 3425423356, + IFCCURVESEGMENT = 4212018352, + IFCDIRECTRIXCURVESWEPTAREASOLID = 593015953, + IFCINDEXEDPOLYGONALTEXTUREMAP = 3465909080, + IFCOFFSETCURVE = 590820931, + IFCOFFSETCURVEBYDISTANCES = 2485787929, + IFCPOLYNOMIALCURVE = 3381221214, + IFCRELASSOCIATESPROFILEDEF = 1033248425, + IFCRELPOSITIONS = 1441486842, + IFCSECTIONEDSOLID = 1862484736, + IFCSECTIONEDSOLIDHORIZONTAL = 1290935644, + IFCSECTIONEDSURFACE = 1356537516, + IFCSPIRAL = 2735484536, + IFCTHIRDORDERPOLYNOMIALSPIRAL = 782932809, + IFCTRANSPORTATIONDEVICETYPE = 3665877780, + IFCTRIANGULATEDIRREGULARNETWORK = 1229763772, + IFCVEHICLETYPE = 3651464721, + IFCBUILTELEMENTTYPE = 1626504194, + IFCCLOTHOID = 3497074424, + IFCCOSINESPIRAL = 2000195564, + IFCCOURSETYPE = 4189326743, + IFCDEEPFOUNDATIONTYPE = 1306400036, + IFCDIRECTRIXDERIVEDREFERENCESWEPTAREASOLID = 4234616927, + IFCFACILITY = 24185140, + IFCFACILITYPART = 1310830890, + IFCFACILITYPARTCOMMON = 4228831410, + IFCGEOTECHNICALELEMENT = 4230923436, + IFCGEOTECHNICALSTRATUM = 1594536857, + IFCGRADIENTCURVE = 2898700619, + IFCIMPACTPROTECTIONDEVICE = 2568555532, + IFCIMPACTPROTECTIONDEVICETYPE = 3948183225, + IFCKERBTYPE = 679976338, + IFCLINEARELEMENT = 2176059722, + IFCLIQUIDTERMINALTYPE = 1770583370, + IFCMARINEFACILITY = 525669439, + IFCMARINEPART = 976884017, + IFCMOBILETELECOMMUNICATIONSAPPLIANCETYPE = 1950438474, + IFCMOORINGDEVICETYPE = 710110818, + IFCNAVIGATIONELEMENTTYPE = 506776471, + IFCPAVEMENTTYPE = 514975943, + IFCPOSITIONINGELEMENT = 1946335990, + IFCRAILTYPE = 1763565496, + IFCRAILWAY = 3992365140, + IFCRAILWAYPART = 1891881377, + IFCREFERENT = 4021432810, + IFCRELADHERESTOELEMENT = 3818125796, + IFCROAD = 146592293, + IFCROADPART = 550521510, + IFCSECONDORDERPOLYNOMIALSPIRAL = 3649235739, + IFCSEGMENTEDREFERENCECURVE = 544395925, + IFCSEVENTHORDERPOLYNOMIALSPIRAL = 1027922057, + IFCSIGN = 33720170, + IFCSIGNTYPE = 3599934289, + IFCSIGNALTYPE = 1894708472, + IFCSINESPIRAL = 42703149, + IFCTENDONCONDUIT = 3663046924, + IFCTENDONCONDUITTYPE = 2281632017, + IFCTRACKELEMENTTYPE = 618700268, + IFCTRANSPORTATIONDEVICE = 1953115116, + IFCVEHICLE = 840318589, + IFCVIBRATIONDAMPER = 1530820697, + IFCVIBRATIONDAMPERTYPE = 3956297820, + IFCALIGNMENTCANT = 4266260250, + IFCALIGNMENTHORIZONTAL = 1545765605, + IFCALIGNMENTSEGMENT = 317615605, + IFCALIGNMENTVERTICAL = 1662888072, + IFCBEARINGTYPE = 3649138523, + IFCBRIDGE = 644574406, + IFCBRIDGEPART = 963979645, + IFCBUILTELEMENT = 1876633798, + IFCBUILTSYSTEM = 3862327254, + IFCCAISSONFOUNDATIONTYPE = 3203706013, + IFCCONVEYORSEGMENTTYPE = 2940368186, + IFCCOURSE = 1502416096, + IFCDEEPFOUNDATION = 3426335179, + IFCDISTRIBUTIONBOARDTYPE = 479945903, + IFCEARTHWORKSCUT = 3071239417, + IFCEARTHWORKSELEMENT = 1077100507, + IFCEARTHWORKSFILL = 3376911765, + IFCELECTRICFLOWTREATMENTDEVICETYPE = 2142170206, + IFCGEOTECHNICALASSEMBLY = 2713699986, + IFCKERB = 2696325953, + IFCLINEARPOSITIONINGELEMENT = 1154579445, + IFCLIQUIDTERMINAL = 1638804497, + IFCMOBILETELECOMMUNICATIONSAPPLIANCE = 2078563270, + IFCMOORINGDEVICE = 234836483, + IFCNAVIGATIONELEMENT = 2182337498, + IFCPAVEMENT = 1383356374, + IFCRAIL = 3290496277, + IFCREINFORCEDSOIL = 3798194928, + IFCSIGNAL = 991950508, + IFCTRACKELEMENT = 3425753595, + IFCALIGNMENT = 325726236, + IFCBEARING = 4196446775, + IFCBOREHOLE = 3314249567, + IFCCAISSONFOUNDATION = 3999819293, + IFCCONVEYORSEGMENT = 3460952963, + IFCDISTRIBUTIONBOARD = 3693000487, + IFCELECTRICFLOWTREATMENTDEVICE = 24726584, + IFCGEOMODEL = 2680139844, + IFCGEOSLICE = 1971632696, + IFCABSORBEDDOSEMEASURE = 3699917729, + IFCACCELERATIONMEASURE = 4182062534, + IFCAMOUNTOFSUBSTANCEMEASURE = 360377573, + IFCANGULARVELOCITYMEASURE = 632304761, + IFCAREAMEASURE = 2650437152, + IFCBOOLEAN = 2735952531, + IFCBOXALIGNMENT = 1867003952, + IFCCOMPLEXNUMBER = 2991860651, + IFCCOMPOUNDPLANEANGLEMEASURE = 3812528620, + IFCCONTEXTDEPENDENTMEASURE = 3238673880, + IFCCOUNTMEASURE = 1778710042, + IFCCURVATUREMEASURE = 94842927, + IFCDAYINMONTHNUMBER = 86635668, + IFCDAYLIGHTSAVINGHOUR = 300323983, + IFCDESCRIPTIVEMEASURE = 1514641115, + IFCDIMENSIONCOUNT = 4134073009, + IFCDOSEEQUIVALENTMEASURE = 524656162, + IFCDYNAMICVISCOSITYMEASURE = 69416015, + IFCELECTRICCAPACITANCEMEASURE = 1827137117, + IFCELECTRICCHARGEMEASURE = 3818826038, + IFCELECTRICCONDUCTANCEMEASURE = 2093906313, + IFCELECTRICCURRENTMEASURE = 3790457270, + IFCELECTRICRESISTANCEMEASURE = 2951915441, + IFCELECTRICVOLTAGEMEASURE = 2506197118, + IFCENERGYMEASURE = 2078135608, + IFCFONTSTYLE = 1102727119, + IFCFONTVARIANT = 2715512545, + IFCFONTWEIGHT = 2590844177, + IFCFORCEMEASURE = 1361398929, + IFCFREQUENCYMEASURE = 3044325142, + IFCGLOBALLYUNIQUEID = 3064340077, + IFCHEATFLUXDENSITYMEASURE = 3113092358, + IFCHEATINGVALUEMEASURE = 1158859006, + IFCHOURINDAY = 2589826445, + IFCIDENTIFIER = 983778844, + IFCILLUMINANCEMEASURE = 3358199106, + IFCINDUCTANCEMEASURE = 2679005408, + IFCINTEGER = 1939436016, + IFCINTEGERCOUNTRATEMEASURE = 3809634241, + IFCIONCONCENTRATIONMEASURE = 3686016028, + IFCISOTHERMALMOISTURECAPACITYMEASURE = 3192672207, + IFCKINEMATICVISCOSITYMEASURE = 2054016361, + IFCLABEL = 3258342251, + IFCLENGTHMEASURE = 1243674935, + IFCLINEARFORCEMEASURE = 191860431, + IFCLINEARMOMENTMEASURE = 2128979029, + IFCLINEARSTIFFNESSMEASURE = 1307019551, + IFCLINEARVELOCITYMEASURE = 3086160713, + IFCLOGICAL = 503418787, + IFCLUMINOUSFLUXMEASURE = 2095003142, + IFCLUMINOUSINTENSITYDISTRIBUTIONMEASURE = 2755797622, + IFCLUMINOUSINTENSITYMEASURE = 151039812, + IFCMAGNETICFLUXDENSITYMEASURE = 286949696, + IFCMAGNETICFLUXMEASURE = 2486716878, + IFCMASSDENSITYMEASURE = 1477762836, + IFCMASSFLOWRATEMEASURE = 4017473158, + IFCMASSMEASURE = 3124614049, + IFCMASSPERLENGTHMEASURE = 3531705166, + IFCMINUTEINHOUR = 102610177, + IFCMODULUSOFELASTICITYMEASURE = 3341486342, + IFCMODULUSOFLINEARSUBGRADEREACTIONMEASURE = 2173214787, + IFCMODULUSOFROTATIONALSUBGRADEREACTIONMEASURE = 1052454078, + IFCMODULUSOFSUBGRADEREACTIONMEASURE = 1753493141, + IFCMOISTUREDIFFUSIVITYMEASURE = 3177669450, + IFCMOLECULARWEIGHTMEASURE = 1648970520, + IFCMOMENTOFINERTIAMEASURE = 3114022597, + IFCMONETARYMEASURE = 2615040989, + IFCMONTHINYEARNUMBER = 765770214, + IFCNORMALISEDRATIOMEASURE = 2095195183, + IFCNUMERICMEASURE = 2395907400, + IFCPHMEASURE = 929793134, + IFCPARAMETERVALUE = 2260317790, + IFCPLANARFORCEMEASURE = 2642773653, + IFCPLANEANGLEMEASURE = 4042175685, + IFCPOSITIVELENGTHMEASURE = 2815919920, + IFCPOSITIVEPLANEANGLEMEASURE = 3054510233, + IFCPOSITIVERATIOMEASURE = 1245737093, + IFCPOWERMEASURE = 1364037233, + IFCPRESENTABLETEXT = 2169031380, + IFCPRESSUREMEASURE = 3665567075, + IFCRADIOACTIVITYMEASURE = 3972513137, + IFCRATIOMEASURE = 96294661, + IFCREAL = 200335297, + IFCROTATIONALFREQUENCYMEASURE = 2133746277, + IFCROTATIONALMASSMEASURE = 1755127002, + IFCROTATIONALSTIFFNESSMEASURE = 3211557302, + IFCSECONDINMINUTE = 2766185779, + IFCSECTIONMODULUSMEASURE = 3467162246, + IFCSECTIONALAREAINTEGRALMEASURE = 2190458107, + IFCSHEARMODULUSMEASURE = 408310005, + IFCSOLIDANGLEMEASURE = 3471399674, + IFCSOUNDPOWERMEASURE = 846465480, + IFCSOUNDPRESSUREMEASURE = 993287707, + IFCSPECIFICHEATCAPACITYMEASURE = 3477203348, + IFCSPECULAREXPONENT = 2757832317, + IFCSPECULARROUGHNESS = 361837227, + IFCTEMPERATUREGRADIENTMEASURE = 58845555, + IFCTEXT = 2801250643, + IFCTEXTALIGNMENT = 1460886941, + IFCTEXTDECORATION = 3490877962, + IFCTEXTFONTNAME = 603696268, + IFCTEXTTRANSFORMATION = 296282323, + IFCTHERMALADMITTANCEMEASURE = 232962298, + IFCTHERMALCONDUCTIVITYMEASURE = 2645777649, + IFCTHERMALEXPANSIONCOEFFICIENTMEASURE = 2281867870, + IFCTHERMALRESISTANCEMEASURE = 857959152, + IFCTHERMALTRANSMITTANCEMEASURE = 2016195849, + IFCTHERMODYNAMICTEMPERATUREMEASURE = 743184107, + IFCTIMEMEASURE = 2726807636, + IFCTIMESTAMP = 2591213694, + IFCTORQUEMEASURE = 1278329552, + IFCVAPORPERMEABILITYMEASURE = 3345633955, + IFCVOLUMEMEASURE = 3458127941, + IFCVOLUMETRICFLOWRATEMEASURE = 2593997549, + IFCWARPINGCONSTANTMEASURE = 51269191, + IFCWARPINGMOMENTMEASURE = 1718600412, + IFCYEARNUMBER = 4065007721, + IFCARCINDEX = 3683503648, + IFCAREADENSITYMEASURE = 1500781891, + IFCBINARY = 2314439260, + IFCCARDINALPOINTREFERENCE = 1683019596, + IFCDATE = 937566702, + IFCDATETIME = 2195413836, + IFCDAYINWEEKNUMBER = 3701338814, + IFCDURATION = 2541165894, + IFCLANGUAGEID = 1275358634, + IFCLINEINDEX = 1774176899, + IFCNONNEGATIVELENGTHMEASURE = 525895558, + IFCPOSITIVEINTEGER = 1790229001, + IFCPROPERTYSETDEFINITIONSET = 2798247006, + IFCSOUNDPOWERLEVELMEASURE = 4157543285, + IFCSOUNDPRESSURELEVELMEASURE = 3457685358, + IFCTEMPERATURERATEOFCHANGEMEASURE = 1209108979, + IFCTIME = 4075327185, + IFCURIREFERENCE = 950732822, +} diff --git a/Importers/Ifc/Speckle.Importers.Ifc/Types/IfcVertex.cs b/Importers/Ifc/Speckle.Importers.Ifc/Types/IfcVertex.cs new file mode 100644 index 000000000..b227f1f52 --- /dev/null +++ b/Importers/Ifc/Speckle.Importers.Ifc/Types/IfcVertex.cs @@ -0,0 +1,14 @@ +using System.Runtime.InteropServices; + +namespace Speckle.Importers.Ifc.Types; + +[StructLayout(LayoutKind.Sequential, Pack = 1)] +public readonly struct IfcVertex +{ + public readonly double PX, + PY, + PZ; + public readonly double NX, + NY, + NZ; +} diff --git a/Importers/Ifc/Speckle.Importers.Ifc/packages.lock.json b/Importers/Ifc/Speckle.Importers.Ifc/packages.lock.json new file mode 100644 index 000000000..8cc40e421 --- /dev/null +++ b/Importers/Ifc/Speckle.Importers.Ifc/packages.lock.json @@ -0,0 +1,297 @@ +{ + "version": 2, + "dependencies": { + "net8.0": { + "Ara3D.Buffers": { + "type": "Direct", + "requested": "[1.4.5, )", + "resolved": "1.4.5", + "contentHash": "SKcQqgtXukyHTlTKFPCaUW4spSkue3XfBU/GmoA7KhH6H995v6TbJxtqjs0EfSgnXEkajL8U7X1NqktScRozXw==", + "dependencies": { + "System.Memory": "4.5.5" + } + }, + "Ara3D.Logging": { + "type": "Direct", + "requested": "[1.4.5, )", + "resolved": "1.4.5", + "contentHash": "7HPCe5Dq21JoOBF1iclk9H37XFCoB2ZzCPqTMNgdg4PWFvuRsofNbiuMdiE/HKgMHCVhy1C5opB2KwDKcO7Axw==", + "dependencies": { + "Ara3D.Utils": "1.4.5" + } + }, + "Ara3D.Utils": { + "type": "Direct", + "requested": "[1.4.5, )", + "resolved": "1.4.5", + "contentHash": "yba/E7PpbWP0+RDp+KbKw/vBXnXBSIheScdpVKuDnr8ytRg8pZ2Jd6nwKES+G0FcVEB9PeOVmEW7SGrFvAwRCg==" + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "Direct", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "MZtBIwfDFork5vfjpJdG5g8wuJFt7d/y3LOSVVtDK/76wlbtz6cjltfKHqLx2TKVqTj5/c41t77m1+h20zqtPA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0" + } + }, + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net461": "1.0.3" + } + }, + "Microsoft.SourceLink.GitHub": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "dependencies": { + "Microsoft.Build.Tasks.Git": "8.0.0", + "Microsoft.SourceLink.Common": "8.0.0" + } + }, + "PolySharp": { + "type": "Direct", + "requested": "[1.14.1, )", + "resolved": "1.14.1", + "contentHash": "mOOmFYwad3MIOL14VCjj02LljyF1GNw1wP0YVlxtcPvqdxjGGMNdNJJxHptlry3MOd8b40Flm8RPOM8JOlN2sQ==" + }, + "Speckle.InterfaceGenerator": { + "type": "Direct", + "requested": "[0.9.6, )", + "resolved": "0.9.6", + "contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w==" + }, + "Speckle.Objects": { + "type": "Direct", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", + "dependencies": { + "Speckle.Sdk": "3.1.1" + } + }, + "Speckle.Sdk": { + "type": "Direct", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", + "dependencies": { + "GraphQL.Client": "6.0.0", + "Microsoft.CSharp": "4.7.0", + "Microsoft.Data.Sqlite": "7.0.5", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging": "2.2.0", + "Speckle.DoubleNumerics": "4.1.0", + "Speckle.Newtonsoft.Json": "13.0.2", + "Speckle.Sdk.Dependencies": "3.1.1" + } + }, + "GraphQL.Client": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "8yPNBbuVBpTptivyAlak4GZvbwbUcjeQTL4vN1HKHRuOykZ4r7l5fcLS6vpyPyLn0x8FsL31xbOIKyxbmR9rbA==", + "dependencies": { + "GraphQL.Client.Abstractions": "6.0.0", + "GraphQL.Client.Abstractions.Websocket": "6.0.0", + "System.Reactive": "5.0.0" + } + }, + "GraphQL.Client.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "h7uzWFORHZ+CCjwr/ThAyXMr0DPpzEANDa4Uo54wqCQ+j7qUKwqYTgOrb1W40sqbvNaZm9v/X7It31SUw0maHA==", + "dependencies": { + "GraphQL.Primitives": "6.0.0" + } + }, + "GraphQL.Client.Abstractions.Websocket": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "Nr9bPf8gIOvLuXpqEpqr9z9jslYFJOvd0feHth3/kPqeR3uMbjF5pjiwh4jxyMcxHdr8Pb6QiXkV3hsSyt0v7A==", + "dependencies": { + "GraphQL.Client.Abstractions": "6.0.0" + } + }, + "GraphQL.Primitives": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "yg72rrYDapfsIUrul7aF6wwNnTJBOFvuA9VdDTQpPa8AlAriHbufeXYLBcodKjfUdkCnaiggX1U/nEP08Zb5GA==" + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.CSharp": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==" + }, + "Microsoft.Data.Sqlite": { + "type": "Transitive", + "resolved": "7.0.5", + "contentHash": "KGxbPeWsQMnmQy43DSBxAFtHz3l2JX8EWBSGUCvT3CuZ8KsuzbkqMIJMDOxWtG8eZSoCDI04aiVQjWuuV8HmSw==", + "dependencies": { + "Microsoft.Data.Sqlite.Core": "7.0.5", + "SQLitePCLRaw.bundle_e_sqlite3": "2.1.4" + } + }, + "Microsoft.Data.Sqlite.Core": { + "type": "Transitive", + "resolved": "7.0.5", + "contentHash": "FTerRmQPqHrCrnoUzhBu+E+1DNGwyrAMLqHkAqOOOu5pGfyMOj8qQUBxI/gDtWtG11p49UxSfWmBzRNlwZqfUg==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "nOP8R1mVb/6mZtm2qgAJXn/LFm/2kMjHDAg/QJLFG6CuWYJtaD3p1BwQhufBVvRzL9ceJ/xF0SQ0qsI2GkDQAA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "65MrmXCziWaQFrI0UHkQbesrX5wTwf9XPjY5yFm/VkgJKFJ5gqvXRoXjIZcf2wLi5ZlwGz/oMYfyURVCWbM5iw==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "vJ9xvOZCnUAIHcGC3SU35r3HKmHTVIeHzo6u/qzlHAqD8m6xv92MLin4oJntTvkpKxVX3vI1GFFkIQtU3AdlsQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "2.2.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw==" + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "UpZLNLBpIZ0GTebShui7xXYh6DmBHjWM8NxGxZbdQh/bPZ5e6YswqI+bru6BnEL5eWiOdodsXtEz3FROcgi/qg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Primitives": "2.2.0", + "System.ComponentModel.Annotations": "4.5.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "azyQtqbm4fSaDzZHD/J+V6oWMFaf2tWP4WEGIYePLCMw3+b2RQdj9ybgbQyjCshcitQKQ4lEDOZjmSlTTrHxUg==", + "dependencies": { + "System.Memory": "4.5.1", + "System.Runtime.CompilerServices.Unsafe": "4.5.1" + } + }, + "Microsoft.NETFramework.ReferenceAssemblies.net461": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "AmOJZwCqnOCNp6PPcf9joyogScWLtwy0M1WkqfEQ0M9nYwyDD7EX9ZjscKS5iYnyvteX7kzSKFCKt9I9dXA6mA==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "Speckle.Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.2", + "contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA==" + }, + "SQLitePCLRaw.bundle_e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "EWI1olKDjFEBMJu0+3wuxwziIAdWDVMYLhuZ3Qs84rrz+DHwD00RzWPZCa+bLnHCf3oJwuFZIRsHT5p236QXww==", + "dependencies": { + "SQLitePCLRaw.lib.e_sqlite3": "2.1.4", + "SQLitePCLRaw.provider.e_sqlite3": "2.1.4" + } + }, + "SQLitePCLRaw.core": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "inBjvSHo9UDKneGNzfUfDjK08JzlcIhn1+SP5Y3m6cgXpCxXKCJDy6Mka7LpgSV+UZmKSnC8rTwB0SQ0xKu5pA==", + "dependencies": { + "System.Memory": "4.5.3" + } + }, + "SQLitePCLRaw.lib.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "2C9Q9eX7CPLveJA0rIhf9RXAvu+7nWZu1A2MdG6SD/NOu26TakGgL1nsbc0JAspGijFOo3HoN79xrx8a368fBg==" + }, + "SQLitePCLRaw.provider.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "CSlb5dUp1FMIkez9Iv5EXzpeq7rHryVNqwJMWnpq87j9zWZexaEMdisDktMsnnrzKM6ahNrsTkjqNodTBPBxtQ==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "System.ComponentModel.Annotations": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "UxYQ3FGUOtzJ7LfSdnYSFd7+oEv6M8NgUatatIN2HxNtDdlcvFAf+VIq4Of9cDMJEJC0aSRv/x898RYhB4Yppg==" + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==" + }, + "System.Reactive": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==" + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Zh8t8oqolRaFa9vmOZfdQm/qKejdqz0J9kr7o2Fu0vPeoH3BL1EOXipKWwkWtLT1JPzjByrF19fGuFlNbmPpiw==" + }, + "Microsoft.Extensions.Logging": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "Nxqhadc9FCmFHzU+fz3oc8sFlE6IadViYg8dfUdGzJZ2JUxnCsRghBhhOWdM4B2zSZqEc+0BjliBh/oNdRZuig==", + "dependencies": { + "Microsoft.Extensions.Configuration.Binder": "2.2.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging.Abstractions": "2.2.0", + "Microsoft.Extensions.Options": "2.2.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" + }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, + "Speckle.Sdk.Dependencies": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" + } + } + } +} \ No newline at end of file diff --git a/Local.sln b/Local.sln index 738e2b396..c9330fe8e 100644 --- a/Local.sln +++ b/Local.sln @@ -8,7 +8,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Config", "Config", "{85A13E .editorconfig = .editorconfig codecov.yml = codecov.yml Directory.Build.props = Directory.Build.props - Directory.Build.targets = Directory.Build.targets Directory.Packages.props = Directory.Packages.props global.json = global.json README.md = README.md @@ -62,8 +61,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "HostApps", "HostApps", "{42 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{59E8E8F3-4E42-4E92-83B3-B1C2AB901D18}" ProjectSection(SolutionItems) = preProject - .github\workflows\ci.yml = .github\workflows\ci.yml - .github\workflows\main.yml = .github\workflows\main.yml + .github\workflows\pr.yml = .github\workflows\pr.yml + .github\workflows\release.yml = .github\workflows\release.yml EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Connectors.Civil3d2024", "Connectors\Autocad\Speckle.Connectors.Civil3d2024\Speckle.Connectors.Civil3d2024.csproj", "{CA8EAE01-AB9F-4EC1-B6F3-73721487E9E1}" @@ -174,7 +173,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.Civil3d2 EndProject Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Speckle.Connectors.Civil3dShared", "Connectors\Autocad\Speckle.Connectors.Civil3dShared\Speckle.Connectors.Civil3dShared.shproj", "{EFD01520-93E8-4CCA-8E03-9CDC635F55F4}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Sdk.Dependencies", "..\speckle-sharp-sdk\src\Speckle.Sdk.Dependencies\Speckle.Sdk.Dependencies.csproj", "{B4679D71-F2F5-4759-8FDD-E305C1370306}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Sdk.Dependencies", "..\speckle-sharp-sdk\src\Speckle.Sdk.Dependencies\Speckle.Sdk.Dependencies.csproj", "{B4679D71-F2F5-4759-8FDD-E305C1370306}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "2024", "2024", "{474752C4-3D2A-4A7D-B8E0-B7A43FC3694C}" EndProject @@ -182,18 +181,102 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "2023", "2023", "{AF303676-6 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{0B47C151-3B4E-4EB5-AB21-02FD096A2024}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Connector.Tekla2023", "Connectors\Tekla\Speckle.Connector.Tekla2023\Speckle.Connector.Tekla2023.csproj", "{A277CDF6-7C8F-4870-8D15-AF35F4749CAA}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Connector.Tekla2023", "Connectors\Tekla\Speckle.Connector.Tekla2023\Speckle.Connector.Tekla2023.csproj", "{A277CDF6-7C8F-4870-8D15-AF35F4749CAA}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Converter.Tekla2023", "Converters\Tekla\Speckle.Converter.Tekla2023\Speckle.Converter.Tekla2023.csproj", "{B236750B-CFE5-4640-9073-77A27A7257F6}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converter.Tekla2023", "Converters\Tekla\Speckle.Converter.Tekla2023\Speckle.Converter.Tekla2023.csproj", "{B236750B-CFE5-4640-9073-77A27A7257F6}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Connector.Tekla2024", "Connectors\Tekla\Speckle.Connector.Tekla2024\Speckle.Connector.Tekla2024.csproj", "{2CBC9A3D-8AC6-4127-ADA5-C6D7F07A35E3}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Connector.Tekla2024", "Connectors\Tekla\Speckle.Connector.Tekla2024\Speckle.Connector.Tekla2024.csproj", "{2CBC9A3D-8AC6-4127-ADA5-C6D7F07A35E3}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Converter.Tekla2024", "Converters\Tekla\Speckle.Converter.Tekla2024\Speckle.Converter.Tekla2024.csproj", "{4C373FD0-E3F3-478B-AD32-CDBF340D21A9}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converter.Tekla2024", "Converters\Tekla\Speckle.Converter.Tekla2024\Speckle.Converter.Tekla2024.csproj", "{4C373FD0-E3F3-478B-AD32-CDBF340D21A9}" EndProject Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Speckle.Connectors.TeklaShared", "Connectors\Tekla\Speckle.Connector.TeklaShared\Speckle.Connectors.TeklaShared.shproj", "{3AB9028B-B2D2-464B-9BA3-39C192441E50}" EndProject Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Speckle.Converters.TeklaShared", "Converters\Tekla\Speckle.Converters.TeklaShared\Speckle.Converters.TeklaShared.shproj", "{52666479-5401-47D6-B7BA-D554784439EA}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "CSi", "CSi", "{1F11635B-410A-4B16-A909-99CE3CCEF52E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Connectors.ETABS22", "Connectors\CSi\Speckle.Connectors.ETABS22\Speckle.Connectors.ETABS22.csproj", "{AFB47B71-4185-4135-98BB-EEDFFA8AA194}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ETABS22", "ETABS22", "{F2A1E5FC-CFEF-4590-BF0D-BE7B9F74E567}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{D2638AC8-28B2-4667-A47B-3FAB9F900E6F}" +EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Speckle.Connectors.CSiShared", "Connectors\CSi\Speckle.Connectors.CSiShared\Speckle.Connectors.CSiShared.shproj", "{A8E949B8-AA55-4909-99F0-8B551791A1F8}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ETABS21", "ETABS21", "{FCCE9A47-05D8-41CB-A8EE-586FC5B69545}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Connectors.ETABS21", "Connectors\CSi\Speckle.Connectors.ETABS21\Speckle.Connectors.ETABS21.csproj", "{1696F113-7F6B-49D6-A4EF-15751C7600F9}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Converters.ETABS21", "Converters\CSi\Speckle.Converters.ETABS21\Speckle.Converters.ETABS21.csproj", "{3B81B220-92E3-43FC-86C6-1E6DBEEB1917}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Converters.ETABS22", "Converters\CSi\Speckle.Converters.ETABS22\Speckle.Converters.ETABS22.csproj", "{5E924B13-B3E8-4724-9BA7-CE82E39866EB}" +EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Speckle.Converters.CSiShared", "Converters\CSi\Speckle.Converters.CSiShared\Speckle.Converters.CSiShared.shproj", "{1B5C5FB2-3B22-4371-9AA5-3EDF3B4D62DE}" +EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Speckle.Connectors.ETABSShared", "Connectors\CSi\Speckle.Connectors.ETABSShared\Speckle.Connectors.ETABSShared.shproj", "{5D1E0B0D-56A7-4E13-B9A9-8633E02B8F17}" +EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Speckle.Converters.ETABSShared", "Converters\CSi\Speckle.Converters.ETABSShared\Speckle.Converters.ETABSShared.shproj", "{36377858-D696-4567-AB05-637F4EC841F5}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Navisworks", "Navisworks", "{95AD741F-44B5-4E01-AF9C-FF9256E26D4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "2024", "2024", "{AE58C92C-DBF3-4248-8614-8F283B8CB5F8}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{46D4410B-009A-43BD-AA32-BDB8B00D9E93}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Connectors.Navisworks2024", "Connectors\Navisworks\Speckle.Connectors.Navisworks2024\Speckle.Connectors.Navisworks2024.csproj", "{CE4D238F-66C2-46B1-BD25-9AC22D7CCB1A}" +EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Speckle.Connectors.NavisworksShared", "Connectors\Navisworks\Speckle.Connectors.NavisworksShared\Speckle.Connectors.NavisworksShared.shproj", "{62813838-52F7-43CB-9062-BB2611C00C79}" +EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Speckle.Converters.NavisworksShared", "Converters\Navisworks\Speckle.Converters.NavisworksShared\Speckle.Converters.NavisworksShared.shproj", "{23830613-87F8-4655-B523-72189A597962}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "2020", "2020", "{EAFCD59F-3650-497C-94ED-AEEFC3131B18}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Connectors.Navisworks2020", "Connectors\Navisworks\Speckle.Connectors.Navisworks2020\Speckle.Connectors.Navisworks2020.csproj", "{B6985672-4704-4F86-A3E0-2310BF8E6D73}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Converters.Navisworks2020", "Converters\Navisworks\Speckle.Converters.Navisworks2020\Speckle.Converters.Navisworks2020.csproj", "{56680EA7-3599-4D88-83A5-B43BA93AC046}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "2021", "2021", "{78DE77B0-7954-41FF-B374-ABE705881CEC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Converters.Navisworks2021", "Converters\Navisworks\Speckle.Converters.Navisworks2021\Speckle.Converters.Navisworks2021.csproj", "{6101B44D-3805-4AA1-B8D0-E06C461ED202}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "2022", "2022", "{19F15419-F493-4D53-83EA-F90869D97D6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Converters.Navisworks2022", "Converters\Navisworks\Speckle.Converters.Navisworks2022\Speckle.Converters.Navisworks2022.csproj", "{A675E34C-667E-4069-A8FA-92F2C2DB8A27}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "2023", "2023", "{91DCAFB0-283B-4B07-9F6F-7335DECEEB08}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Converters.Navisworks2023", "Converters\Navisworks\Speckle.Converters.Navisworks2023\Speckle.Converters.Navisworks2023.csproj", "{57AFB8CB-B310-49E4-9C53-621A614063CF}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "2025", "2025", "{A88CFA1F-B2D5-4DBE-8496-68D0AFA46F2D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Converters.Navisworks2025", "Converters\Navisworks\Speckle.Converters.Navisworks2025\Speckle.Converters.Navisworks2025.csproj", "{0B5AB325-3791-4A81-B0EF-BCA040381BE2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Connectors.Navisworks2021", "Connectors\Navisworks\Speckle.Connectors.Navisworks2021\Speckle.Connectors.Navisworks2021.csproj", "{C635619C-2938-4E6F-9E25-56CE1632A7EC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Connectors.Navisworks2022", "Connectors\Navisworks\Speckle.Connectors.Navisworks2022\Speckle.Connectors.Navisworks2022.csproj", "{04FC86A3-2E25-41A1-98C5-10898616CD78}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Connectors.Navisworks2023", "Connectors\Navisworks\Speckle.Connectors.Navisworks2023\Speckle.Connectors.Navisworks2023.csproj", "{FD44E1F0-D20A-49B6-ADC9-482D911A74FB}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Connectors.Navisworks2025", "Connectors\Navisworks\Speckle.Connectors.Navisworks2025\Speckle.Connectors.Navisworks2025.csproj", "{7791806E-7531-41D8-9C9D-4A1249D9F99C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Converters.Navisworks2024", "Converters\Navisworks\Speckle.Converters.Navisworks2024\Speckle.Converters.Navisworks2024.csproj", "{52D789C2-5B3C-4225-9F84-C54B3AB4F1B5}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Importers", "Importers", "{24750B06-C90D-43B1-B2E4-088D531D6748}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Ifc", "Ifc", "{0EF6C4D6-AC76-408F-8D32-2F9DA8006077}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Importers.Ifc", "Importers\Ifc\Speckle.Importers.Ifc\Speckle.Importers.Ifc.csproj", "{9FD99F9B-8D50-4C7A-B3A6-22E28AB5F26B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Importers.Ifc.Tester", "Importers\Ifc\Speckle.Importers.Ifc.Tester\Speckle.Importers.Ifc.Tester.csproj", "{B3B126CA-A419-48D1-B117-6DEE1DE1AFAD}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Importers.Ifc.Tester2", "Importers\Ifc\Speckle.Importers.Ifc.Tester2\Speckle.Importers.Ifc.Tester2.csproj", "{867DC1A5-A886-4F49-8665-793EB9832F9E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Connectors.Common.Tests", "Sdk\Speckle.Connectors.Common.Tests\Speckle.Connectors.Common.Tests.csproj", "{C19C3EDF-D95D-4D22-9949-D99F91447AE7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Common.MeshTriangulation", "Sdk\Speckle.Common.MeshTriangulation\Speckle.Common.MeshTriangulation.csproj", "{20BB9687-2C92-44E9-9E2F-9D2F45641354}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Performance", "Sdk\Speckle.Performance\Speckle.Performance.csproj", "{54C21396-EFCA-446B-BFBA-2139B0D29ED8}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -401,8 +484,8 @@ Global {62F50A22-8BBF-497A-B599-F858DFF1D31C}.Release|Any CPU.Build.0 = Release|Any CPU {1A2B994C-A36D-4827-8237-0817F56B37E1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1A2B994C-A36D-4827-8237-0817F56B37E1}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1A2B994C-A36D-4827-8237-0817F56B37E1}.Local|Any CPU.ActiveCfg = Debug|Any CPU - {1A2B994C-A36D-4827-8237-0817F56B37E1}.Local|Any CPU.Build.0 = Debug|Any CPU + {1A2B994C-A36D-4827-8237-0817F56B37E1}.Local|Any CPU.ActiveCfg = Local|Any CPU + {1A2B994C-A36D-4827-8237-0817F56B37E1}.Local|Any CPU.Build.0 = Local|Any CPU {1A2B994C-A36D-4827-8237-0817F56B37E1}.Release|Any CPU.ActiveCfg = Release|Any CPU {1A2B994C-A36D-4827-8237-0817F56B37E1}.Release|Any CPU.Build.0 = Release|Any CPU {27AFBC57-F493-4B51-BEE0-0C364B6A6A13}.Debug|Any CPU.ActiveCfg = Debug|Any CPU @@ -425,8 +508,8 @@ Global {C34D0246-6C16-4E81-AE57-2CE70F81484B}.Release|Any CPU.Build.0 = Release|Any CPU {38497468-6B28-4F8F-A9E0-3167E90DDD89}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {38497468-6B28-4F8F-A9E0-3167E90DDD89}.Debug|Any CPU.Build.0 = Debug|Any CPU - {38497468-6B28-4F8F-A9E0-3167E90DDD89}.Local|Any CPU.ActiveCfg = Debug|Any CPU - {38497468-6B28-4F8F-A9E0-3167E90DDD89}.Local|Any CPU.Build.0 = Debug|Any CPU + {38497468-6B28-4F8F-A9E0-3167E90DDD89}.Local|Any CPU.ActiveCfg = Local|Any CPU + {38497468-6B28-4F8F-A9E0-3167E90DDD89}.Local|Any CPU.Build.0 = Local|Any CPU {38497468-6B28-4F8F-A9E0-3167E90DDD89}.Release|Any CPU.ActiveCfg = Release|Any CPU {38497468-6B28-4F8F-A9E0-3167E90DDD89}.Release|Any CPU.Build.0 = Release|Any CPU {3747C01B-343E-4425-A973-4D9D181BE468}.Debug|Any CPU.ActiveCfg = Debug|Any CPU @@ -485,8 +568,8 @@ Global {B4679D71-F2F5-4759-8FDD-E305C1370306}.Release|Any CPU.Build.0 = Release|Any CPU {A277CDF6-7C8F-4870-8D15-AF35F4749CAA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A277CDF6-7C8F-4870-8D15-AF35F4749CAA}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A277CDF6-7C8F-4870-8D15-AF35F4749CAA}.Local|Any CPU.ActiveCfg = Debug|Any CPU - {A277CDF6-7C8F-4870-8D15-AF35F4749CAA}.Local|Any CPU.Build.0 = Debug|Any CPU + {A277CDF6-7C8F-4870-8D15-AF35F4749CAA}.Local|Any CPU.ActiveCfg = Local|Any CPU + {A277CDF6-7C8F-4870-8D15-AF35F4749CAA}.Local|Any CPU.Build.0 = Local|Any CPU {A277CDF6-7C8F-4870-8D15-AF35F4749CAA}.Release|Any CPU.ActiveCfg = Release|Any CPU {A277CDF6-7C8F-4870-8D15-AF35F4749CAA}.Release|Any CPU.Build.0 = Release|Any CPU {B236750B-CFE5-4640-9073-77A27A7257F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU @@ -497,8 +580,8 @@ Global {B236750B-CFE5-4640-9073-77A27A7257F6}.Release|Any CPU.Build.0 = Release|Any CPU {2CBC9A3D-8AC6-4127-ADA5-C6D7F07A35E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2CBC9A3D-8AC6-4127-ADA5-C6D7F07A35E3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2CBC9A3D-8AC6-4127-ADA5-C6D7F07A35E3}.Local|Any CPU.ActiveCfg = Debug|Any CPU - {2CBC9A3D-8AC6-4127-ADA5-C6D7F07A35E3}.Local|Any CPU.Build.0 = Debug|Any CPU + {2CBC9A3D-8AC6-4127-ADA5-C6D7F07A35E3}.Local|Any CPU.ActiveCfg = Local|Any CPU + {2CBC9A3D-8AC6-4127-ADA5-C6D7F07A35E3}.Local|Any CPU.Build.0 = Local|Any CPU {2CBC9A3D-8AC6-4127-ADA5-C6D7F07A35E3}.Release|Any CPU.ActiveCfg = Release|Any CPU {2CBC9A3D-8AC6-4127-ADA5-C6D7F07A35E3}.Release|Any CPU.Build.0 = Release|Any CPU {4C373FD0-E3F3-478B-AD32-CDBF340D21A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU @@ -507,6 +590,138 @@ Global {4C373FD0-E3F3-478B-AD32-CDBF340D21A9}.Local|Any CPU.Build.0 = Local|Any CPU {4C373FD0-E3F3-478B-AD32-CDBF340D21A9}.Release|Any CPU.ActiveCfg = Release|Any CPU {4C373FD0-E3F3-478B-AD32-CDBF340D21A9}.Release|Any CPU.Build.0 = Release|Any CPU + {AFB47B71-4185-4135-98BB-EEDFFA8AA194}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AFB47B71-4185-4135-98BB-EEDFFA8AA194}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AFB47B71-4185-4135-98BB-EEDFFA8AA194}.Local|Any CPU.ActiveCfg = Local|Any CPU + {AFB47B71-4185-4135-98BB-EEDFFA8AA194}.Local|Any CPU.Build.0 = Local|Any CPU + {AFB47B71-4185-4135-98BB-EEDFFA8AA194}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AFB47B71-4185-4135-98BB-EEDFFA8AA194}.Release|Any CPU.Build.0 = Release|Any CPU + {1696F113-7F6B-49D6-A4EF-15751C7600F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1696F113-7F6B-49D6-A4EF-15751C7600F9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1696F113-7F6B-49D6-A4EF-15751C7600F9}.Local|Any CPU.ActiveCfg = Local|Any CPU + {1696F113-7F6B-49D6-A4EF-15751C7600F9}.Local|Any CPU.Build.0 = Local|Any CPU + {1696F113-7F6B-49D6-A4EF-15751C7600F9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1696F113-7F6B-49D6-A4EF-15751C7600F9}.Release|Any CPU.Build.0 = Release|Any CPU + {3B81B220-92E3-43FC-86C6-1E6DBEEB1917}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3B81B220-92E3-43FC-86C6-1E6DBEEB1917}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3B81B220-92E3-43FC-86C6-1E6DBEEB1917}.Local|Any CPU.ActiveCfg = Local|Any CPU + {3B81B220-92E3-43FC-86C6-1E6DBEEB1917}.Local|Any CPU.Build.0 = Local|Any CPU + {3B81B220-92E3-43FC-86C6-1E6DBEEB1917}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3B81B220-92E3-43FC-86C6-1E6DBEEB1917}.Release|Any CPU.Build.0 = Release|Any CPU + {5E924B13-B3E8-4724-9BA7-CE82E39866EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5E924B13-B3E8-4724-9BA7-CE82E39866EB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5E924B13-B3E8-4724-9BA7-CE82E39866EB}.Local|Any CPU.ActiveCfg = Local|Any CPU + {5E924B13-B3E8-4724-9BA7-CE82E39866EB}.Local|Any CPU.Build.0 = Local|Any CPU + {5E924B13-B3E8-4724-9BA7-CE82E39866EB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5E924B13-B3E8-4724-9BA7-CE82E39866EB}.Release|Any CPU.Build.0 = Release|Any CPU + {CE4D238F-66C2-46B1-BD25-9AC22D7CCB1A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CE4D238F-66C2-46B1-BD25-9AC22D7CCB1A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CE4D238F-66C2-46B1-BD25-9AC22D7CCB1A}.Local|Any CPU.ActiveCfg = Local|Any CPU + {CE4D238F-66C2-46B1-BD25-9AC22D7CCB1A}.Local|Any CPU.Build.0 = Local|Any CPU + {CE4D238F-66C2-46B1-BD25-9AC22D7CCB1A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CE4D238F-66C2-46B1-BD25-9AC22D7CCB1A}.Release|Any CPU.Build.0 = Release|Any CPU + {B6985672-4704-4F86-A3E0-2310BF8E6D73}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B6985672-4704-4F86-A3E0-2310BF8E6D73}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B6985672-4704-4F86-A3E0-2310BF8E6D73}.Local|Any CPU.ActiveCfg = Local|Any CPU + {B6985672-4704-4F86-A3E0-2310BF8E6D73}.Local|Any CPU.Build.0 = Local|Any CPU + {B6985672-4704-4F86-A3E0-2310BF8E6D73}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B6985672-4704-4F86-A3E0-2310BF8E6D73}.Release|Any CPU.Build.0 = Release|Any CPU + {56680EA7-3599-4D88-83A5-B43BA93AC046}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {56680EA7-3599-4D88-83A5-B43BA93AC046}.Debug|Any CPU.Build.0 = Debug|Any CPU + {56680EA7-3599-4D88-83A5-B43BA93AC046}.Local|Any CPU.ActiveCfg = Local|Any CPU + {56680EA7-3599-4D88-83A5-B43BA93AC046}.Local|Any CPU.Build.0 = Local|Any CPU + {56680EA7-3599-4D88-83A5-B43BA93AC046}.Release|Any CPU.ActiveCfg = Release|Any CPU + {56680EA7-3599-4D88-83A5-B43BA93AC046}.Release|Any CPU.Build.0 = Release|Any CPU + {6101B44D-3805-4AA1-B8D0-E06C461ED202}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6101B44D-3805-4AA1-B8D0-E06C461ED202}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6101B44D-3805-4AA1-B8D0-E06C461ED202}.Local|Any CPU.ActiveCfg = Local|Any CPU + {6101B44D-3805-4AA1-B8D0-E06C461ED202}.Local|Any CPU.Build.0 = Local|Any CPU + {6101B44D-3805-4AA1-B8D0-E06C461ED202}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6101B44D-3805-4AA1-B8D0-E06C461ED202}.Release|Any CPU.Build.0 = Release|Any CPU + {A675E34C-667E-4069-A8FA-92F2C2DB8A27}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A675E34C-667E-4069-A8FA-92F2C2DB8A27}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A675E34C-667E-4069-A8FA-92F2C2DB8A27}.Local|Any CPU.ActiveCfg = Local|Any CPU + {A675E34C-667E-4069-A8FA-92F2C2DB8A27}.Local|Any CPU.Build.0 = Local|Any CPU + {A675E34C-667E-4069-A8FA-92F2C2DB8A27}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A675E34C-667E-4069-A8FA-92F2C2DB8A27}.Release|Any CPU.Build.0 = Release|Any CPU + {57AFB8CB-B310-49E4-9C53-621A614063CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {57AFB8CB-B310-49E4-9C53-621A614063CF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {57AFB8CB-B310-49E4-9C53-621A614063CF}.Local|Any CPU.ActiveCfg = Local|Any CPU + {57AFB8CB-B310-49E4-9C53-621A614063CF}.Local|Any CPU.Build.0 = Local|Any CPU + {57AFB8CB-B310-49E4-9C53-621A614063CF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {57AFB8CB-B310-49E4-9C53-621A614063CF}.Release|Any CPU.Build.0 = Release|Any CPU + {0B5AB325-3791-4A81-B0EF-BCA040381BE2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0B5AB325-3791-4A81-B0EF-BCA040381BE2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0B5AB325-3791-4A81-B0EF-BCA040381BE2}.Local|Any CPU.ActiveCfg = Local|Any CPU + {0B5AB325-3791-4A81-B0EF-BCA040381BE2}.Local|Any CPU.Build.0 = Local|Any CPU + {0B5AB325-3791-4A81-B0EF-BCA040381BE2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0B5AB325-3791-4A81-B0EF-BCA040381BE2}.Release|Any CPU.Build.0 = Release|Any CPU + {C635619C-2938-4E6F-9E25-56CE1632A7EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C635619C-2938-4E6F-9E25-56CE1632A7EC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C635619C-2938-4E6F-9E25-56CE1632A7EC}.Local|Any CPU.ActiveCfg = Local|Any CPU + {C635619C-2938-4E6F-9E25-56CE1632A7EC}.Local|Any CPU.Build.0 = Local|Any CPU + {C635619C-2938-4E6F-9E25-56CE1632A7EC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C635619C-2938-4E6F-9E25-56CE1632A7EC}.Release|Any CPU.Build.0 = Release|Any CPU + {04FC86A3-2E25-41A1-98C5-10898616CD78}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {04FC86A3-2E25-41A1-98C5-10898616CD78}.Debug|Any CPU.Build.0 = Debug|Any CPU + {04FC86A3-2E25-41A1-98C5-10898616CD78}.Local|Any CPU.ActiveCfg = Local|Any CPU + {04FC86A3-2E25-41A1-98C5-10898616CD78}.Local|Any CPU.Build.0 = Local|Any CPU + {04FC86A3-2E25-41A1-98C5-10898616CD78}.Release|Any CPU.ActiveCfg = Release|Any CPU + {04FC86A3-2E25-41A1-98C5-10898616CD78}.Release|Any CPU.Build.0 = Release|Any CPU + {FD44E1F0-D20A-49B6-ADC9-482D911A74FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FD44E1F0-D20A-49B6-ADC9-482D911A74FB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FD44E1F0-D20A-49B6-ADC9-482D911A74FB}.Local|Any CPU.ActiveCfg = Local|Any CPU + {FD44E1F0-D20A-49B6-ADC9-482D911A74FB}.Local|Any CPU.Build.0 = Local|Any CPU + {FD44E1F0-D20A-49B6-ADC9-482D911A74FB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FD44E1F0-D20A-49B6-ADC9-482D911A74FB}.Release|Any CPU.Build.0 = Release|Any CPU + {7791806E-7531-41D8-9C9D-4A1249D9F99C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7791806E-7531-41D8-9C9D-4A1249D9F99C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7791806E-7531-41D8-9C9D-4A1249D9F99C}.Local|Any CPU.ActiveCfg = Local|Any CPU + {7791806E-7531-41D8-9C9D-4A1249D9F99C}.Local|Any CPU.Build.0 = Local|Any CPU + {7791806E-7531-41D8-9C9D-4A1249D9F99C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7791806E-7531-41D8-9C9D-4A1249D9F99C}.Release|Any CPU.Build.0 = Release|Any CPU + {52D789C2-5B3C-4225-9F84-C54B3AB4F1B5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {52D789C2-5B3C-4225-9F84-C54B3AB4F1B5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {52D789C2-5B3C-4225-9F84-C54B3AB4F1B5}.Local|Any CPU.ActiveCfg = Local|Any CPU + {52D789C2-5B3C-4225-9F84-C54B3AB4F1B5}.Local|Any CPU.Build.0 = Local|Any CPU + {52D789C2-5B3C-4225-9F84-C54B3AB4F1B5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {52D789C2-5B3C-4225-9F84-C54B3AB4F1B5}.Release|Any CPU.Build.0 = Release|Any CPU + {9FD99F9B-8D50-4C7A-B3A6-22E28AB5F26B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9FD99F9B-8D50-4C7A-B3A6-22E28AB5F26B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9FD99F9B-8D50-4C7A-B3A6-22E28AB5F26B}.Local|Any CPU.ActiveCfg = Local|Any CPU + {9FD99F9B-8D50-4C7A-B3A6-22E28AB5F26B}.Local|Any CPU.Build.0 = Local|Any CPU + {9FD99F9B-8D50-4C7A-B3A6-22E28AB5F26B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9FD99F9B-8D50-4C7A-B3A6-22E28AB5F26B}.Release|Any CPU.Build.0 = Release|Any CPU + {B3B126CA-A419-48D1-B117-6DEE1DE1AFAD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B3B126CA-A419-48D1-B117-6DEE1DE1AFAD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B3B126CA-A419-48D1-B117-6DEE1DE1AFAD}.Local|Any CPU.ActiveCfg = Local|Any CPU + {B3B126CA-A419-48D1-B117-6DEE1DE1AFAD}.Local|Any CPU.Build.0 = Local|Any CPU + {B3B126CA-A419-48D1-B117-6DEE1DE1AFAD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B3B126CA-A419-48D1-B117-6DEE1DE1AFAD}.Release|Any CPU.Build.0 = Release|Any CPU + {867DC1A5-A886-4F49-8665-793EB9832F9E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {867DC1A5-A886-4F49-8665-793EB9832F9E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {867DC1A5-A886-4F49-8665-793EB9832F9E}.Local|Any CPU.ActiveCfg = Debug|Any CPU + {867DC1A5-A886-4F49-8665-793EB9832F9E}.Local|Any CPU.Build.0 = Debug|Any CPU + {867DC1A5-A886-4F49-8665-793EB9832F9E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {867DC1A5-A886-4F49-8665-793EB9832F9E}.Release|Any CPU.Build.0 = Release|Any CPU + {C19C3EDF-D95D-4D22-9949-D99F91447AE7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C19C3EDF-D95D-4D22-9949-D99F91447AE7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C19C3EDF-D95D-4D22-9949-D99F91447AE7}.Local|Any CPU.ActiveCfg = Local|Any CPU + {C19C3EDF-D95D-4D22-9949-D99F91447AE7}.Local|Any CPU.Build.0 = Local|Any CPU + {C19C3EDF-D95D-4D22-9949-D99F91447AE7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C19C3EDF-D95D-4D22-9949-D99F91447AE7}.Release|Any CPU.Build.0 = Release|Any CPU + {20BB9687-2C92-44E9-9E2F-9D2F45641354}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {20BB9687-2C92-44E9-9E2F-9D2F45641354}.Debug|Any CPU.Build.0 = Debug|Any CPU + {20BB9687-2C92-44E9-9E2F-9D2F45641354}.Local|Any CPU.ActiveCfg = Debug|Any CPU + {20BB9687-2C92-44E9-9E2F-9D2F45641354}.Local|Any CPU.Build.0 = Debug|Any CPU + {20BB9687-2C92-44E9-9E2F-9D2F45641354}.Release|Any CPU.ActiveCfg = Release|Any CPU + {20BB9687-2C92-44E9-9E2F-9D2F45641354}.Release|Any CPU.Build.0 = Release|Any CPU + {54C21396-EFCA-446B-BFBA-2139B0D29ED8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {54C21396-EFCA-446B-BFBA-2139B0D29ED8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {54C21396-EFCA-446B-BFBA-2139B0D29ED8}.Local|Any CPU.ActiveCfg = Debug|Any CPU + {54C21396-EFCA-446B-BFBA-2139B0D29ED8}.Local|Any CPU.Build.0 = Debug|Any CPU + {54C21396-EFCA-446B-BFBA-2139B0D29ED8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {54C21396-EFCA-446B-BFBA-2139B0D29ED8}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -593,6 +808,47 @@ Global {4C373FD0-E3F3-478B-AD32-CDBF340D21A9} = {474752C4-3D2A-4A7D-B8E0-B7A43FC3694C} {3AB9028B-B2D2-464B-9BA3-39C192441E50} = {0B47C151-3B4E-4EB5-AB21-02FD096A2024} {52666479-5401-47D6-B7BA-D554784439EA} = {0B47C151-3B4E-4EB5-AB21-02FD096A2024} + {1F11635B-410A-4B16-A909-99CE3CCEF52E} = {42826721-9A18-4762-8BA9-F1429DD5C5B1} + {AFB47B71-4185-4135-98BB-EEDFFA8AA194} = {F2A1E5FC-CFEF-4590-BF0D-BE7B9F74E567} + {F2A1E5FC-CFEF-4590-BF0D-BE7B9F74E567} = {1F11635B-410A-4B16-A909-99CE3CCEF52E} + {D2638AC8-28B2-4667-A47B-3FAB9F900E6F} = {1F11635B-410A-4B16-A909-99CE3CCEF52E} + {A8E949B8-AA55-4909-99F0-8B551791A1F8} = {D2638AC8-28B2-4667-A47B-3FAB9F900E6F} + {FCCE9A47-05D8-41CB-A8EE-586FC5B69545} = {1F11635B-410A-4B16-A909-99CE3CCEF52E} + {1696F113-7F6B-49D6-A4EF-15751C7600F9} = {FCCE9A47-05D8-41CB-A8EE-586FC5B69545} + {3B81B220-92E3-43FC-86C6-1E6DBEEB1917} = {FCCE9A47-05D8-41CB-A8EE-586FC5B69545} + {5E924B13-B3E8-4724-9BA7-CE82E39866EB} = {F2A1E5FC-CFEF-4590-BF0D-BE7B9F74E567} + {1B5C5FB2-3B22-4371-9AA5-3EDF3B4D62DE} = {D2638AC8-28B2-4667-A47B-3FAB9F900E6F} + {5D1E0B0D-56A7-4E13-B9A9-8633E02B8F17} = {D2638AC8-28B2-4667-A47B-3FAB9F900E6F} + {36377858-D696-4567-AB05-637F4EC841F5} = {D2638AC8-28B2-4667-A47B-3FAB9F900E6F} + {95AD741F-44B5-4E01-AF9C-FF9256E26D4B} = {42826721-9A18-4762-8BA9-F1429DD5C5B1} + {AE58C92C-DBF3-4248-8614-8F283B8CB5F8} = {95AD741F-44B5-4E01-AF9C-FF9256E26D4B} + {46D4410B-009A-43BD-AA32-BDB8B00D9E93} = {95AD741F-44B5-4E01-AF9C-FF9256E26D4B} + {CE4D238F-66C2-46B1-BD25-9AC22D7CCB1A} = {AE58C92C-DBF3-4248-8614-8F283B8CB5F8} + {62813838-52F7-43CB-9062-BB2611C00C79} = {46D4410B-009A-43BD-AA32-BDB8B00D9E93} + {23830613-87F8-4655-B523-72189A597962} = {46D4410B-009A-43BD-AA32-BDB8B00D9E93} + {EAFCD59F-3650-497C-94ED-AEEFC3131B18} = {95AD741F-44B5-4E01-AF9C-FF9256E26D4B} + {B6985672-4704-4F86-A3E0-2310BF8E6D73} = {EAFCD59F-3650-497C-94ED-AEEFC3131B18} + {56680EA7-3599-4D88-83A5-B43BA93AC046} = {EAFCD59F-3650-497C-94ED-AEEFC3131B18} + {78DE77B0-7954-41FF-B374-ABE705881CEC} = {95AD741F-44B5-4E01-AF9C-FF9256E26D4B} + {6101B44D-3805-4AA1-B8D0-E06C461ED202} = {78DE77B0-7954-41FF-B374-ABE705881CEC} + {19F15419-F493-4D53-83EA-F90869D97D6E} = {95AD741F-44B5-4E01-AF9C-FF9256E26D4B} + {A675E34C-667E-4069-A8FA-92F2C2DB8A27} = {19F15419-F493-4D53-83EA-F90869D97D6E} + {91DCAFB0-283B-4B07-9F6F-7335DECEEB08} = {95AD741F-44B5-4E01-AF9C-FF9256E26D4B} + {57AFB8CB-B310-49E4-9C53-621A614063CF} = {91DCAFB0-283B-4B07-9F6F-7335DECEEB08} + {A88CFA1F-B2D5-4DBE-8496-68D0AFA46F2D} = {95AD741F-44B5-4E01-AF9C-FF9256E26D4B} + {0B5AB325-3791-4A81-B0EF-BCA040381BE2} = {A88CFA1F-B2D5-4DBE-8496-68D0AFA46F2D} + {C635619C-2938-4E6F-9E25-56CE1632A7EC} = {78DE77B0-7954-41FF-B374-ABE705881CEC} + {04FC86A3-2E25-41A1-98C5-10898616CD78} = {19F15419-F493-4D53-83EA-F90869D97D6E} + {FD44E1F0-D20A-49B6-ADC9-482D911A74FB} = {91DCAFB0-283B-4B07-9F6F-7335DECEEB08} + {7791806E-7531-41D8-9C9D-4A1249D9F99C} = {A88CFA1F-B2D5-4DBE-8496-68D0AFA46F2D} + {52D789C2-5B3C-4225-9F84-C54B3AB4F1B5} = {AE58C92C-DBF3-4248-8614-8F283B8CB5F8} + {0EF6C4D6-AC76-408F-8D32-2F9DA8006077} = {24750B06-C90D-43B1-B2E4-088D531D6748} + {9FD99F9B-8D50-4C7A-B3A6-22E28AB5F26B} = {0EF6C4D6-AC76-408F-8D32-2F9DA8006077} + {B3B126CA-A419-48D1-B117-6DEE1DE1AFAD} = {0EF6C4D6-AC76-408F-8D32-2F9DA8006077} + {867DC1A5-A886-4F49-8665-793EB9832F9E} = {0EF6C4D6-AC76-408F-8D32-2F9DA8006077} + {C19C3EDF-D95D-4D22-9949-D99F91447AE7} = {2E00592E-558D-492D-88F9-3ECEE4C0C7DA} + {20BB9687-2C92-44E9-9E2F-9D2F45641354} = {2E00592E-558D-492D-88F9-3ECEE4C0C7DA} + {54C21396-EFCA-446B-BFBA-2139B0D29ED8} = {2E00592E-558D-492D-88F9-3ECEE4C0C7DA} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {EE253116-7070-4E9A-BCE8-2911C251B8C8} @@ -600,22 +856,43 @@ Global GlobalSection(SharedMSBuildProjectFiles) = preSolution Connectors\Revit\Speckle.Connectors.RevitShared.Cef\Speckle.Connectors.RevitShared.Cef.projitems*{01f98733-7352-47ad-a594-537d979de3de}*SharedItemsImports = 5 Connectors\Revit\Speckle.Connectors.RevitShared\Speckle.Connectors.RevitShared.projitems*{01f98733-7352-47ad-a594-537d979de3de}*SharedItemsImports = 5 + Connectors\Navisworks\Speckle.Connectors.NavisworksShared\Speckle.Connectors.NavisworksShared.projitems*{04fc86a3-2e25-41a1-98c5-10898616cd78}*SharedItemsImports = 5 + Converters\Navisworks\Speckle.Converters.NavisworksShared\Speckle.Converters.NavisworksShared.projitems*{0b5ab325-3791-4a81-b0ef-bca040381be2}*SharedItemsImports = 5 + Connectors\CSi\Speckle.Connectors.CSiShared\Speckle.Connectors.CSiShared.projitems*{1696f113-7f6b-49d6-a4ef-15751c7600f9}*SharedItemsImports = 5 + Connectors\CSi\Speckle.Connectors.ETABSShared\Speckle.Connectors.ETABSShared.projitems*{1696f113-7f6b-49d6-a4ef-15751c7600f9}*SharedItemsImports = 5 Converters\Revit\Speckle.Converters.RevitShared\Speckle.Converters.RevitShared.projitems*{19424b55-058c-4e9c-b86f-700aef9eaec3}*SharedItemsImports = 5 Connectors\Autocad\Speckle.Connectors.AutocadShared\Speckle.Connectors.AutocadShared.projitems*{1b1f674c-cfd5-4eac-adfd-f29a70b9d229}*SharedItemsImports = 5 + Converters\CSi\Speckle.Converters.CSiShared\Speckle.Converters.CSiShared.projitems*{1b5c5fb2-3b22-4371-9aa5-3edf3b4d62de}*SharedItemsImports = 13 Connectors\Rhino\Speckle.Connectors.RhinoShared\Speckle.Connectors.RhinoShared.projitems*{1e2644a9-6b31-4350-8772-ceaad6ee0b21}*SharedItemsImports = 5 Converters\Autocad\Speckle.Converters.AutocadShared\Speckle.Converters.AutocadShared.projitems*{1f93164a-42ee-4d72-b7cd-9ca831f60e09}*SharedItemsImports = 5 Converters\Civil3d\Speckle.Converters.Civil3dShared\Speckle.Converters.Civil3dShared.projitems*{1f93164a-42ee-4d72-b7cd-9ca831f60e09}*SharedItemsImports = 5 + Converters\Navisworks\Speckle.Converters.NavisworksShared\Speckle.Converters.NavisworksShared.projitems*{23830613-87f8-4655-b523-72189a597962}*SharedItemsImports = 13 Converters\Autocad\Speckle.Converters.AutocadShared\Speckle.Converters.AutocadShared.projitems*{25172c49-7aa4-4739-bb07-69785094c379}*SharedItemsImports = 5 Converters\Civil3d\Speckle.Converters.Civil3dShared\Speckle.Converters.Civil3dShared.projitems*{25172c49-7aa4-4739-bb07-69785094c379}*SharedItemsImports = 5 Converters\Revit\Speckle.Converters.RevitShared\Speckle.Converters.RevitShared.projitems*{26391930-f86f-47e0-a5f6-b89919e38ce1}*SharedItemsImports = 5 Connectors\Autocad\Speckle.Connectors.AutocadShared\Speckle.Connectors.AutocadShared.projitems*{27afbc57-f493-4b51-bee0-0c364b6a6a13}*SharedItemsImports = 5 + Connectors\Tekla\Speckle.Connector.TeklaShared\Speckle.Connectors.TeklaShared.projitems*{2cbc9a3d-8ac6-4127-ada5-c6d7f07a35e3}*SharedItemsImports = 5 Converters\Autocad\Speckle.Converters.AutocadShared\Speckle.Converters.AutocadShared.projitems*{2f04e109-2b13-4dfc-961d-b7eea94299bd}*SharedItemsImports = 5 Converters\Civil3d\Speckle.Converters.Civil3dShared\Speckle.Converters.Civil3dShared.projitems*{35175682-da83-4c0a-a49d-b191f5885d8e}*SharedItemsImports = 13 + Converters\CSi\Speckle.Converters.ETABSShared\Speckle.Converters.ETABSShared.projitems*{36377858-d696-4567-ab05-637f4ec841f5}*SharedItemsImports = 13 + Connectors\Tekla\Speckle.Connector.TeklaShared\Speckle.Connectors.TeklaShared.projitems*{3ab9028b-b2d2-464b-9ba3-39c192441e50}*SharedItemsImports = 13 + Converters\CSi\Speckle.Converters.CSiShared\Speckle.Converters.CSiShared.projitems*{3b81b220-92e3-43fc-86c6-1e6dbeeb1917}*SharedItemsImports = 5 + Converters\CSi\Speckle.Converters.ETABSShared\Speckle.Converters.ETABSShared.projitems*{3b81b220-92e3-43fc-86c6-1e6dbeeb1917}*SharedItemsImports = 5 Connectors\Autocad\Speckle.Connectors.AutocadShared\Speckle.Connectors.AutocadShared.projitems*{41bc679f-887f-44cf-971d-a5502ee87db0}*SharedItemsImports = 13 + Converters\Tekla\Speckle.Converters.TeklaShared\Speckle.Converters.TeklaShared.projitems*{4c373fd0-e3f3-478b-ad32-cdbf340d21a9}*SharedItemsImports = 5 Converters\Revit\Speckle.Converters.RevitShared\Speckle.Converters.RevitShared.projitems*{4d40a101-07e6-4ff2-8934-83434932591d}*SharedItemsImports = 5 + Converters\Tekla\Speckle.Converters.TeklaShared\Speckle.Converters.TeklaShared.projitems*{52666479-5401-47d6-b7ba-d554784439ea}*SharedItemsImports = 13 + Converters\Navisworks\Speckle.Converters.NavisworksShared\Speckle.Converters.NavisworksShared.projitems*{52d789c2-5b3c-4225-9f84-c54b3ab4f1b5}*SharedItemsImports = 5 + Converters\Navisworks\Speckle.Converters.NavisworksShared\Speckle.Converters.NavisworksShared.projitems*{56680ea7-3599-4d88-83a5-b43ba93ac046}*SharedItemsImports = 5 + Converters\Navisworks\Speckle.Converters.NavisworksShared\Speckle.Converters.NavisworksShared.projitems*{57afb8cb-b310-49e4-9c53-621a614063cf}*SharedItemsImports = 5 Converters\Rhino\Speckle.Converters.RhinoShared\Speckle.Converters.RhinoShared.projitems*{57c1aa51-5be2-40f3-8cb2-8b7d2af0ff5c}*SharedItemsImports = 5 + Connectors\CSi\Speckle.Connectors.ETABSShared\Speckle.Connectors.ETABSShared.projitems*{5d1e0b0d-56a7-4e13-b9a9-8633e02b8f17}*SharedItemsImports = 13 + Converters\CSi\Speckle.Converters.CSiShared\Speckle.Converters.CSiShared.projitems*{5e924b13-b3e8-4724-9ba7-ce82e39866eb}*SharedItemsImports = 5 + Converters\CSi\Speckle.Converters.ETABSShared\Speckle.Converters.ETABSShared.projitems*{5e924b13-b3e8-4724-9ba7-ce82e39866eb}*SharedItemsImports = 5 + Converters\Navisworks\Speckle.Converters.NavisworksShared\Speckle.Converters.NavisworksShared.projitems*{6101b44d-3805-4aa1-b8d0-e06c461ed202}*SharedItemsImports = 5 Connectors\Revit\Speckle.Connectors.RevitShared.Cef\Speckle.Connectors.RevitShared.Cef.projitems*{617bd3c7-87d9-4d28-8ac9-4910945bb9fc}*SharedItemsImports = 5 Connectors\Revit\Speckle.Connectors.RevitShared\Speckle.Connectors.RevitShared.projitems*{617bd3c7-87d9-4d28-8ac9-4910945bb9fc}*SharedItemsImports = 5 + Connectors\Navisworks\Speckle.Connectors.NavisworksShared\Speckle.Connectors.NavisworksShared.projitems*{62813838-52f7-43cb-9062-bb2611c00c79}*SharedItemsImports = 13 Converters\Autocad\Speckle.Converters.AutocadShared\Speckle.Converters.AutocadShared.projitems*{62f50a22-8bbf-497a-b599-f858dff1d31c}*SharedItemsImports = 5 Converters\Autocad\Speckle.Converters.AutocadShared\Speckle.Converters.AutocadShared.projitems*{631c295a-7ccf-4b42-8686-7034e31469e7}*SharedItemsImports = 5 Converters\Rhino\Speckle.Converters.RhinoShared\Speckle.Converters.RhinoShared.projitems*{65a2f556-f14a-49f3-8a92-7f2e1e7ed3b5}*SharedItemsImports = 5 @@ -624,16 +901,24 @@ Global Converters\Revit\Speckle.Converters.RevitShared\Speckle.Converters.RevitShared.projitems*{68cf9bdf-94ac-4d2d-a7bd-d1c064f97051}*SharedItemsImports = 5 Connectors\Revit\Speckle.Connectors.RevitShared.Cef\Speckle.Connectors.RevitShared.Cef.projitems*{6a40cbe4-ecab-4ced-9917-5c64cbf75da6}*SharedItemsImports = 13 Connectors\Rhino\Speckle.Connectors.RhinoShared\Speckle.Connectors.RhinoShared.projitems*{6f3fd892-6315-4f75-bfbd-843a489f8b94}*SharedItemsImports = 5 + Connectors\Navisworks\Speckle.Connectors.NavisworksShared\Speckle.Connectors.NavisworksShared.projitems*{7791806e-7531-41d8-9c9d-4a1249d9f99c}*SharedItemsImports = 5 Connectors\Revit\Speckle.Connectors.RevitShared.Cef\Speckle.Connectors.RevitShared.Cef.projitems*{7f1fdcf2-0ce8-4119-b3c1-f2cc6d7e1c36}*SharedItemsImports = 5 Connectors\Revit\Speckle.Connectors.RevitShared\Speckle.Connectors.RevitShared.projitems*{7f1fdcf2-0ce8-4119-b3c1-f2cc6d7e1c36}*SharedItemsImports = 5 Connectors\Autocad\Speckle.Connectors.AutocadShared\Speckle.Connectors.AutocadShared.projitems*{89c4cbc7-1606-40de-b6da-fbe3aac98395}*SharedItemsImports = 5 Converters\Autocad\Speckle.Converters.AutocadShared\Speckle.Converters.AutocadShared.projitems*{9add1b7a-6401-4202-8613-f668e2fbc0a4}*SharedItemsImports = 13 + Connectors\Tekla\Speckle.Connector.TeklaShared\Speckle.Connectors.TeklaShared.projitems*{a277cdf6-7c8f-4870-8d15-af35f4749caa}*SharedItemsImports = 5 + Converters\Navisworks\Speckle.Converters.NavisworksShared\Speckle.Converters.NavisworksShared.projitems*{a675e34c-667e-4069-a8fa-92f2c2db8a27}*SharedItemsImports = 5 Connectors\Revit\Speckle.Connectors.RevitShared.Cef\Speckle.Connectors.RevitShared.Cef.projitems*{a6de3da0-b242-4f49-aef0-4e26af92d16c}*SharedItemsImports = 5 Connectors\Revit\Speckle.Connectors.RevitShared\Speckle.Connectors.RevitShared.projitems*{a6de3da0-b242-4f49-aef0-4e26af92d16c}*SharedItemsImports = 5 + Connectors\CSi\Speckle.Connectors.CSiShared\Speckle.Connectors.CSiShared.projitems*{a8e949b8-aa55-4909-99f0-8b551791a1f8}*SharedItemsImports = 13 Converters\Rhino\Speckle.Converters.RhinoShared\Speckle.Converters.RhinoShared.projitems*{ac2db416-f05c-4296-9040-56d6ad4fcd27}*SharedItemsImports = 5 Converters\Autocad\Speckle.Converters.AutocadShared\Speckle.Converters.AutocadShared.projitems*{ae75b68f-3594-403b-9719-9c36da0e3f8a}*SharedItemsImports = 5 Converters\Civil3d\Speckle.Converters.Civil3dShared\Speckle.Converters.Civil3dShared.projitems*{ae75b68f-3594-403b-9719-9c36da0e3f8a}*SharedItemsImports = 5 + Connectors\CSi\Speckle.Connectors.CSiShared\Speckle.Connectors.CSiShared.projitems*{afb47b71-4185-4135-98bb-eedffa8aa194}*SharedItemsImports = 5 + Connectors\CSi\Speckle.Connectors.ETABSShared\Speckle.Connectors.ETABSShared.projitems*{afb47b71-4185-4135-98bb-eedffa8aa194}*SharedItemsImports = 5 + Converters\Tekla\Speckle.Converters.TeklaShared\Speckle.Converters.TeklaShared.projitems*{b236750b-cfe5-4640-9073-77a27a7257f6}*SharedItemsImports = 5 Connectors\Rhino\Speckle.Connectors.RhinoShared\Speckle.Connectors.RhinoShared.projitems*{b37d4b9a-8d3f-4fa5-b9c8-e6c5f8a0c1e2}*SharedItemsImports = 13 + Connectors\Navisworks\Speckle.Connectors.NavisworksShared\Speckle.Connectors.NavisworksShared.projitems*{b6985672-4704-4f86-a3e0-2310bf8e6d73}*SharedItemsImports = 5 Converters\Autocad\Speckle.Converters.AutocadShared\Speckle.Converters.AutocadShared.projitems*{b6c04ec4-ed81-4e61-b4b2-4902015808d7}*SharedItemsImports = 5 Converters\Civil3d\Speckle.Converters.Civil3dShared\Speckle.Converters.Civil3dShared.projitems*{b6c04ec4-ed81-4e61-b4b2-4902015808d7}*SharedItemsImports = 5 Converters\Autocad\Speckle.Converters.AutocadShared\Speckle.Converters.AutocadShared.projitems*{c2de264a-aa87-4012-b954-17e3f403a237}*SharedItemsImports = 5 @@ -643,8 +928,10 @@ Global Converters\Revit\Speckle.Converters.RevitShared\Speckle.Converters.RevitShared.projitems*{c34d0246-6c16-4e81-ae57-2ce70f81484b}*SharedItemsImports = 5 Connectors\Autocad\Speckle.Connectors.AutocadShared\Speckle.Connectors.AutocadShared.projitems*{c3879337-cd00-4011-89c5-585dd2cc3ed5}*SharedItemsImports = 5 Connectors\Autocad\Speckle.Connectors.Civil3dShared\Speckle.Connectors.Civil3dShared.projitems*{c3879337-cd00-4011-89c5-585dd2cc3ed5}*SharedItemsImports = 5 + Connectors\Navisworks\Speckle.Connectors.NavisworksShared\Speckle.Connectors.NavisworksShared.projitems*{c635619c-2938-4e6f-9e25-56ce1632a7ec}*SharedItemsImports = 5 Connectors\Autocad\Speckle.Connectors.AutocadShared\Speckle.Connectors.AutocadShared.projitems*{ca8eae01-ab9f-4ec1-b6f3-73721487e9e1}*SharedItemsImports = 5 Connectors\Autocad\Speckle.Connectors.Civil3dShared\Speckle.Connectors.Civil3dShared.projitems*{ca8eae01-ab9f-4ec1-b6f3-73721487e9e1}*SharedItemsImports = 5 + Connectors\Navisworks\Speckle.Connectors.NavisworksShared\Speckle.Connectors.NavisworksShared.projitems*{ce4d238f-66c2-46b1-bd25-9ac22d7ccb1a}*SharedItemsImports = 5 Connectors\Autocad\Speckle.Connectors.AutocadShared\Speckle.Connectors.AutocadShared.projitems*{d4566bf1-56cf-477a-8769-63299a9f45a3}*SharedItemsImports = 5 Connectors\Autocad\Speckle.Connectors.Civil3dShared\Speckle.Connectors.Civil3dShared.projitems*{d4566bf1-56cf-477a-8769-63299a9f45a3}*SharedItemsImports = 5 Connectors\Revit\Speckle.Connectors.RevitShared\Speckle.Connectors.RevitShared.projitems*{dc570fff-6fe5-47bd-8bc1-b471a6067786}*SharedItemsImports = 13 @@ -653,6 +940,7 @@ Global Converters\Revit\Speckle.Converters.RevitShared.Tests\Speckle.Converters.RevitShared.Tests.projitems*{e1c43415-3202-45f4-8bf9-a4dd7d7f2ed6}*SharedItemsImports = 13 Connectors\Autocad\Speckle.Connectors.AutocadShared\Speckle.Connectors.AutocadShared.projitems*{eaad080d-49af-49bf-b8bd-a18ceb210734}*SharedItemsImports = 5 Connectors\Autocad\Speckle.Connectors.Civil3dShared\Speckle.Connectors.Civil3dShared.projitems*{efd01520-93e8-4cca-8e03-9cdc635f55f4}*SharedItemsImports = 13 + Connectors\Navisworks\Speckle.Connectors.NavisworksShared\Speckle.Connectors.NavisworksShared.projitems*{fd44e1f0-d20a-49b6-adc9-482d911a74fb}*SharedItemsImports = 5 Connectors\Autocad\Speckle.Connectors.AutocadShared\Speckle.Connectors.AutocadShared.projitems*{fe4cb79d-4e59-4070-871c-108545537e6c}*SharedItemsImports = 5 Connectors\Autocad\Speckle.Connectors.Civil3dShared\Speckle.Connectors.Civil3dShared.projitems*{fe4cb79d-4e59-4070-871c-108545537e6c}*SharedItemsImports = 5 EndGlobalSection diff --git a/README.md b/README.md index 886d761aa..e530d9505 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ We're using [CSharpier](https://github.com/belav/csharpier) to format our code. For best DX, we recommend turning on CSharpier's `reformat on save` setting if you've installed it in your IDE. ### Clean Locks -We're using npm package locks to store exact and versioned dependency trees. Occasionally you will need to clean your local package-lock files, eg when switching between `Speckle.Connectors.sln` and `local.sln`. +We're using npm package locks to store exact and versioned dependency trees. Occasionally you will need to clean your local package-lock files, eg when switching between `Speckle.Connectors.sln` and `Local.sln`. Run this command in CLI to delete all package.lock.json files before a restore: ``` .\build.ps1 clean-locks @@ -85,6 +85,14 @@ To make sure your local environment is ready for a clean build, run this command ``` .\build.ps1 deep-clean ``` +### Deep Clean Local + +This is for users of the `Local.sln` solution: + +To make sure your local environment is ready for a clean build, run this command to delete all `bin` and `obj` directories and restore all projects: +``` +.\build.ps1 deep-clean-local +``` ## Local development with SDK changes If you'd like to make changes to the [`speckle-sharp-sdk`](https://github.com/specklesystems/speckle-sharp-sdk) side-by-side with changes to this repo's projects, use `**Local.sln**`.
diff --git a/Sdk/Speckle.Common.MeshTriangulation/BaseTransformer.cs b/Sdk/Speckle.Common.MeshTriangulation/BaseTransformer.cs new file mode 100644 index 000000000..accc077cb --- /dev/null +++ b/Sdk/Speckle.Common.MeshTriangulation/BaseTransformer.cs @@ -0,0 +1,120 @@ +using Speckle.DoubleNumerics; + +namespace Speckle.Common.MeshTriangulation; + +public class BaseTransformer : IBaseTransformer +{ + private Matrix4x4 _2DTo3DMatrix; + private Matrix4x4 _3DTo2DMatrix; + private double _zOffset; + + // creates a target coordinate system, where the Z axis is the plane normal + public void SetTargetPlane(Poly3 plane) + { + Vector3 normal = plane.GetNormal(); + var (xBasis, yBasis) = ComputeBasisVectors(normal); + + _2DTo3DMatrix = GetTransformationMatrix(xBasis, yBasis, normal); + Matrix4x4.Invert(_2DTo3DMatrix, out _3DTo2DMatrix); + + var transformedVertex = Vector3.Transform(plane.Vertices[0], _3DTo2DMatrix); + _zOffset = transformedVertex.Z; + } + + public Vector3 Vec2ToVec3(Vector2 vec2) + { + return Vector3.Transform(new Vector3(vec2.X, vec2.Y, _zOffset), _2DTo3DMatrix); + } + + public Vector2 Vec3ToVec2(Vector3 vec3) + { + var transformedVertex = Vector3.Transform(vec3, _3DTo2DMatrix); + return new Vector2(transformedVertex.X, transformedVertex.Y); + } + + public Poly3 Poly2ToPoly3(Poly2 poly2) + { + var poly3 = new Poly3(); + + foreach (var vertex in poly2.Vertices) + { + poly3.Vertices.Add(Vec2ToVec3(vertex)); + } + + return poly3; + } + + public Poly2 Poly3ToPoly2(Poly3 poly3) + { + var poly2 = new Poly2(); + + foreach (var vertex in poly3.Vertices) + { + poly2.Vertices.Add(Vec3ToVec2(vertex)); + } + + return poly2; + } + + public Mesh3 Mesh2ToMesh3(Mesh2 mesh2) + { + var vertices = new List(); + foreach (var vertex in mesh2.Vertices) + { + vertices.Add(Vec2ToVec3(vertex)); + } + + return new Mesh3(vertices, new List(mesh2.Triangles)); + } + + public Mesh2 Mesh3ToMesh2(Mesh3 mesh3) + { + var vertices = new List(); + foreach (var vertex in mesh3.Vertices) + { + vertices.Add(Vec3ToVec2(vertex)); + } + + return new Mesh2(vertices, new List(mesh3.Triangles)); + } + + private (Vector3 xBasis, Vector3 yBasis) ComputeBasisVectors(Vector3 normal) + { + // We need two arbitrary orthogonal vectors in the polygon plane + // Find an arbitrary vector not parallel to the normal + Vector3 arbitrary = Vector3.UnitZ; + var angle = Vector3.Dot(normal, arbitrary); + if (Math.Abs(angle) > 0.99f) + { + arbitrary = Vector3.UnitY; + } + + // Compute the X and Y basis vectors + Vector3 xBasis = Vector3.Normalize(Vector3.Cross(normal, arbitrary)); + Vector3 yBasis = Vector3.Normalize(Vector3.Cross(normal, xBasis)); + + return (xBasis, yBasis); + } + + private Matrix4x4 GetTransformationMatrix(Vector3 xBasis, Vector3 yBasis, Vector3 normal) + { + return new Matrix4x4( + xBasis.X, + xBasis.Y, + xBasis.Z, + 0, + yBasis.X, + yBasis.Y, + yBasis.Z, + 0, + normal.X, + normal.Y, + normal.Z, + 0, + 0, + 0, + 0, + 1 + ); + } +} diff --git a/Sdk/Speckle.Common.MeshTriangulation/IBaseTransformer.cs b/Sdk/Speckle.Common.MeshTriangulation/IBaseTransformer.cs new file mode 100644 index 000000000..7ec380536 --- /dev/null +++ b/Sdk/Speckle.Common.MeshTriangulation/IBaseTransformer.cs @@ -0,0 +1,14 @@ +using Speckle.DoubleNumerics; + +namespace Speckle.Common.MeshTriangulation; + +public interface IBaseTransformer +{ + void SetTargetPlane(Poly3 plane); + Vector2 Vec3ToVec2(Vector3 vec3); + Vector3 Vec2ToVec3(Vector2 vec2); + Poly2 Poly3ToPoly2(Poly3 poly3); + Poly3 Poly2ToPoly3(Poly2 poly2); + Mesh2 Mesh3ToMesh2(Mesh3 mesh3); + Mesh3 Mesh2ToMesh3(Mesh2 mesh2); +} diff --git a/Sdk/Speckle.Common.MeshTriangulation/ITriangulator.cs b/Sdk/Speckle.Common.MeshTriangulation/ITriangulator.cs new file mode 100644 index 000000000..7c8be5f04 --- /dev/null +++ b/Sdk/Speckle.Common.MeshTriangulation/ITriangulator.cs @@ -0,0 +1,6 @@ +namespace Speckle.Common.MeshTriangulation; + +public interface ITriangulator +{ + Mesh2 Triangulate(IReadOnlyList polygons); +} diff --git a/Sdk/Speckle.Common.MeshTriangulation/LibTessTriangulator.cs b/Sdk/Speckle.Common.MeshTriangulation/LibTessTriangulator.cs new file mode 100644 index 000000000..cf3def845 --- /dev/null +++ b/Sdk/Speckle.Common.MeshTriangulation/LibTessTriangulator.cs @@ -0,0 +1,53 @@ +using Speckle.DoubleNumerics; + +namespace Speckle.Common.MeshTriangulation; + +public class LibTessTriangulator : ITriangulator +{ + public Mesh2 Triangulate(IReadOnlyList polygons) + { + var tess = new LibTessDotNet.Tess(); + + for (int polygonIndex = 0; polygonIndex < polygons.Count; polygonIndex++) + { + var numPoints = polygons[polygonIndex].Vertices.Count; + var contour = new LibTessDotNet.ContourVertex[numPoints]; + for (int vertexIndex = 0; vertexIndex < numPoints; vertexIndex++) + { + var v = polygons[polygonIndex].Vertices[vertexIndex]; + contour[vertexIndex].Position = new LibTessDotNet.Vec3((float)v.X, (float)v.Y, 0); + } + + // the outer contour has to be in clockwise order + var orientation = + polygonIndex == 0 + ? LibTessDotNet.ContourOrientation.Clockwise + : LibTessDotNet.ContourOrientation.CounterClockwise; + tess.AddContour(contour, orientation); + } + + tess.Tessellate(); + + var vertices = new List(); + var triangles = new List(); + int tc = 0; + + int numTriangles = tess.ElementCount; + for (int i = 0; i < numTriangles; i++) + { + var v1 = tess.Vertices[tess.Elements[i * 3]].Position; + var v2 = tess.Vertices[tess.Elements[i * 3 + 1]].Position; + var v3 = tess.Vertices[tess.Elements[i * 3 + 2]].Position; + + vertices.Add(new Vector2(v1.X, v1.Y)); + vertices.Add(new Vector2(v2.X, v2.Y)); + vertices.Add(new Vector2(v3.X, v3.Y)); + + triangles.Add(tc++); + triangles.Add(tc++); + triangles.Add(tc++); + } + + return new Mesh2(vertices, triangles); + } +} diff --git a/Sdk/Speckle.Common.MeshTriangulation/Mesh2.cs b/Sdk/Speckle.Common.MeshTriangulation/Mesh2.cs new file mode 100644 index 000000000..489074b60 --- /dev/null +++ b/Sdk/Speckle.Common.MeshTriangulation/Mesh2.cs @@ -0,0 +1,20 @@ +using Speckle.DoubleNumerics; + +namespace Speckle.Common.MeshTriangulation; + +public readonly struct Mesh2 +{ + public List Vertices { get; } + public List Triangles { get; } + + public Mesh2(List vertices, List triangles) + { + Vertices = vertices ?? throw new ArgumentNullException(nameof(vertices)); + Triangles = triangles ?? throw new ArgumentNullException(nameof(triangles)); + + if (triangles.Count % 3 != 0) + { + throw new ArgumentException("Triangles list must be divisible by 3, as each triangle requires 3 indices."); + } + } +} diff --git a/Sdk/Speckle.Common.MeshTriangulation/Mesh3.cs b/Sdk/Speckle.Common.MeshTriangulation/Mesh3.cs new file mode 100644 index 000000000..59dfe7bdb --- /dev/null +++ b/Sdk/Speckle.Common.MeshTriangulation/Mesh3.cs @@ -0,0 +1,20 @@ +using Speckle.DoubleNumerics; + +namespace Speckle.Common.MeshTriangulation; + +public readonly struct Mesh3 +{ + public List Vertices { get; } + public List Triangles { get; } + + public Mesh3(List vertices, List triangles) + { + Vertices = vertices ?? throw new ArgumentNullException(nameof(vertices)); + Triangles = triangles ?? throw new ArgumentNullException(nameof(triangles)); + + if (triangles.Count % 3 != 0) + { + throw new ArgumentException("Triangles list must be divisible by 3, as each triangle requires 3 indices."); + } + } +} diff --git a/Sdk/Speckle.Common.MeshTriangulation/MeshGenerator.cs b/Sdk/Speckle.Common.MeshTriangulation/MeshGenerator.cs new file mode 100644 index 000000000..9f1667777 --- /dev/null +++ b/Sdk/Speckle.Common.MeshTriangulation/MeshGenerator.cs @@ -0,0 +1,143 @@ +using Speckle.DoubleNumerics; + +namespace Speckle.Common.MeshTriangulation; + +public sealed class MeshGenerator +{ + private readonly IBaseTransformer _baseTransformer; + private readonly ITriangulator _triangulator; + + public MeshGenerator(IBaseTransformer baseTransformer, ITriangulator triangulator) + { + _baseTransformer = baseTransformer; + _triangulator = triangulator; + } + + // creates a triangulated surface mesh from the given polygons + // each polygon has to contain at least 3 points + // the first polygon is the contour of the mesh, it has to be clockwise + // the rest of the polygons define the holes, these have to be counterclockwise + public Mesh3 TriangulateSurface(IReadOnlyList polygons) + { + _baseTransformer.SetTargetPlane(polygons[0]); + var polygons2D = new List(); + foreach (var polygon in polygons) + { + polygons2D.Add(_baseTransformer.Poly3ToPoly2(polygon)); + } + var mesh2 = _triangulator.Triangulate(polygons2D); + + return _baseTransformer.Mesh2ToMesh3(mesh2); + } + + // creates an extruded triangle mesh from the given polygons + // each polygon has to contain at least 3 points + // the first polygon is the contour of the mesh, it has to be clockwise + // the rest of the polygons define the holes, these have to be counterclockwise + // the mesh will be extruded in the normal direction by the given height + public Mesh3 ExtrudeMesh(IReadOnlyList polygons, double extrusionHeight) + { + if (polygons.Count < 1) + { + throw new ArgumentException("No polygon was provided for extrusion"); + } + + var vertices = new List(); + var triangles = new List(); + var tc = 0; + + var normal = polygons[0].GetNormal(); + var offset = normal * extrusionHeight; + + // the contour has to be clockwise and the holes have to be counterclockwise + // if any of the holes is clockwise then it has to be reversed + for (var i = 1; i < polygons.Count; i++) + { + var polyNormal = polygons[i].GetNormal(); + if ((polyNormal + normal).Length() > 0.01f) + { + polygons[i].Reverse(); + } + } + + foreach (var polygon in polygons) + { + for (var curr = 0; curr < polygon.Vertices.Count; curr++) + { + var next = curr + 1; + if (next >= polygon.Vertices.Count) + { + next = 0; + } + + var p1 = polygon.Vertices[curr]; + var p2 = polygon.Vertices[next]; + var p3 = polygon.Vertices[curr] + offset; + + var p4 = polygon.Vertices[next]; + var p5 = polygon.Vertices[next] + offset; + var p6 = polygon.Vertices[curr] + offset; + + vertices.Add(p1); + vertices.Add(p2); + vertices.Add(p3); + vertices.Add(p4); + vertices.Add(p5); + vertices.Add(p6); + + triangles.Add(tc++); + triangles.Add(tc++); + triangles.Add(tc++); + triangles.Add(tc++); + triangles.Add(tc++); + triangles.Add(tc++); + } + } + + var cap = TriangulateSurface(polygons); + + // Top cap triangles have to be in reverse order + for (var i = 0; i < cap.Vertices.Count; i += 3) + { + vertices.Add(cap.Vertices[i + 2]); + vertices.Add(cap.Vertices[i + 1]); + vertices.Add(cap.Vertices[i]); + + triangles.Add(tc++); + triangles.Add(tc++); + triangles.Add(tc++); + } + + // Bottom cap + foreach (var vertex in cap.Vertices) + { + var op = vertex + offset; + vertices.Add(op); + triangles.Add(tc++); + } + + return new Mesh3(vertices, triangles); + } + + public Mesh3 ExtrudeMesh(IReadOnlyList polygons, Vector3 point) + { + var distance = GetDistanceToPlane(polygons[0], point); + return ExtrudeMesh(polygons, distance); + } + + public static double GetDistanceToPlane(Poly3 plane, Vector3 point) + { + var p1 = plane.Vertices[0]; + var p2 = plane.Vertices[1]; + var p3 = plane.Vertices[2]; + + Vector3 v1 = p2 - p1; + Vector3 v2 = p3 - p1; + Vector3 normal = Vector3.Normalize(Vector3.Cross(v1, v2)); + + var dot = -Vector3.Dot(normal, p1); + var distance = Math.Abs(Vector3.Dot(normal, point) + dot); + + return distance; + } +} diff --git a/Sdk/Speckle.Common.MeshTriangulation/Poly2.cs b/Sdk/Speckle.Common.MeshTriangulation/Poly2.cs new file mode 100644 index 000000000..cc943fb1a --- /dev/null +++ b/Sdk/Speckle.Common.MeshTriangulation/Poly2.cs @@ -0,0 +1,18 @@ +using Speckle.DoubleNumerics; + +namespace Speckle.Common.MeshTriangulation; + +public readonly struct Poly2 +{ + public List Vertices { get; } + + public Poly2() + { + Vertices = new List(); + } + + public Poly2(List vertices) + { + Vertices = vertices ?? throw new ArgumentNullException(nameof(vertices)); + } +} diff --git a/Sdk/Speckle.Common.MeshTriangulation/Poly3.cs b/Sdk/Speckle.Common.MeshTriangulation/Poly3.cs new file mode 100644 index 000000000..6475d2a93 --- /dev/null +++ b/Sdk/Speckle.Common.MeshTriangulation/Poly3.cs @@ -0,0 +1,36 @@ +using Speckle.DoubleNumerics; + +namespace Speckle.Common.MeshTriangulation; + +public readonly struct Poly3 +{ + public List Vertices { get; } + + public Poly3() + { + Vertices = new List(); + } + + public Poly3(List vertices) + { + Vertices = vertices ?? throw new ArgumentNullException(nameof(vertices)); + } + + // helper function, only works on polygons with at least 3 vertices + public Vector3 GetNormal() + { + if (Vertices.Count < 3) + { + throw new InvalidOperationException("Polygon must have at least 3 points to calculate a normal."); + } + + var v1 = Vertices[1] - Vertices[0]; + var v2 = Vertices[2] - Vertices[0]; + return Vector3.Normalize(Vector3.Cross(v1, v2)); + } + + public void Reverse() + { + Vertices.Reverse(); + } +} diff --git a/Sdk/Speckle.Common.MeshTriangulation/Speckle.Common.MeshTriangulation.csproj b/Sdk/Speckle.Common.MeshTriangulation/Speckle.Common.MeshTriangulation.csproj new file mode 100644 index 000000000..f63d63341 --- /dev/null +++ b/Sdk/Speckle.Common.MeshTriangulation/Speckle.Common.MeshTriangulation.csproj @@ -0,0 +1,12 @@ + + + + net48;net6.0;net8.0 + + + + + + + + diff --git a/Sdk/Speckle.Common.MeshTriangulation/packages.lock.json b/Sdk/Speckle.Common.MeshTriangulation/packages.lock.json new file mode 100644 index 000000000..b4abae136 --- /dev/null +++ b/Sdk/Speckle.Common.MeshTriangulation/packages.lock.json @@ -0,0 +1,185 @@ +{ + "version": 2, + "dependencies": { + ".NETFramework,Version=v4.8": { + "LibTessDotNet": { + "type": "Direct", + "requested": "[1.1.15, )", + "resolved": "1.1.15", + "contentHash": "KuA7N3Nv/lIeawJdQBQJR6oqWD9KETHLbWzBqapwFs+Tby+R5I4crkKujKMm5bXcSuFZ8LNtflFQVadsWCbBjg==" + }, + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net48": "1.0.3" + } + }, + "Microsoft.SourceLink.GitHub": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "dependencies": { + "Microsoft.Build.Tasks.Git": "8.0.0", + "Microsoft.SourceLink.Common": "8.0.0" + } + }, + "PolySharp": { + "type": "Direct", + "requested": "[1.14.1, )", + "resolved": "1.14.1", + "contentHash": "mOOmFYwad3MIOL14VCjj02LljyF1GNw1wP0YVlxtcPvqdxjGGMNdNJJxHptlry3MOd8b40Flm8RPOM8JOlN2sQ==" + }, + "Speckle.DoubleNumerics": { + "type": "Direct", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, + "Speckle.InterfaceGenerator": { + "type": "Direct", + "requested": "[0.9.6, )", + "resolved": "0.9.6", + "contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w==" + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.NETFramework.ReferenceAssemblies.net48": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "zMk4D+9zyiEWByyQ7oPImPN/Jhpj166Ky0Nlla4eXlNL8hI/BtSJsgR8Inldd4NNpIAH3oh8yym0W2DrhXdSLQ==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + } + }, + "net6.0": { + "LibTessDotNet": { + "type": "Direct", + "requested": "[1.1.15, )", + "resolved": "1.1.15", + "contentHash": "KuA7N3Nv/lIeawJdQBQJR6oqWD9KETHLbWzBqapwFs+Tby+R5I4crkKujKMm5bXcSuFZ8LNtflFQVadsWCbBjg==" + }, + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net461": "1.0.3" + } + }, + "Microsoft.SourceLink.GitHub": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "dependencies": { + "Microsoft.Build.Tasks.Git": "8.0.0", + "Microsoft.SourceLink.Common": "8.0.0" + } + }, + "PolySharp": { + "type": "Direct", + "requested": "[1.14.1, )", + "resolved": "1.14.1", + "contentHash": "mOOmFYwad3MIOL14VCjj02LljyF1GNw1wP0YVlxtcPvqdxjGGMNdNJJxHptlry3MOd8b40Flm8RPOM8JOlN2sQ==" + }, + "Speckle.DoubleNumerics": { + "type": "Direct", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, + "Speckle.InterfaceGenerator": { + "type": "Direct", + "requested": "[0.9.6, )", + "resolved": "0.9.6", + "contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w==" + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.NETFramework.ReferenceAssemblies.net461": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "AmOJZwCqnOCNp6PPcf9joyogScWLtwy0M1WkqfEQ0M9nYwyDD7EX9ZjscKS5iYnyvteX7kzSKFCKt9I9dXA6mA==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + } + }, + "net8.0": { + "LibTessDotNet": { + "type": "Direct", + "requested": "[1.1.15, )", + "resolved": "1.1.15", + "contentHash": "KuA7N3Nv/lIeawJdQBQJR6oqWD9KETHLbWzBqapwFs+Tby+R5I4crkKujKMm5bXcSuFZ8LNtflFQVadsWCbBjg==" + }, + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net461": "1.0.3" + } + }, + "Microsoft.SourceLink.GitHub": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "dependencies": { + "Microsoft.Build.Tasks.Git": "8.0.0", + "Microsoft.SourceLink.Common": "8.0.0" + } + }, + "PolySharp": { + "type": "Direct", + "requested": "[1.14.1, )", + "resolved": "1.14.1", + "contentHash": "mOOmFYwad3MIOL14VCjj02LljyF1GNw1wP0YVlxtcPvqdxjGGMNdNJJxHptlry3MOd8b40Flm8RPOM8JOlN2sQ==" + }, + "Speckle.DoubleNumerics": { + "type": "Direct", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, + "Speckle.InterfaceGenerator": { + "type": "Direct", + "requested": "[0.9.6, )", + "resolved": "0.9.6", + "contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w==" + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.NETFramework.ReferenceAssemblies.net461": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "AmOJZwCqnOCNp6PPcf9joyogScWLtwy0M1WkqfEQ0M9nYwyDD7EX9ZjscKS5iYnyvteX7kzSKFCKt9I9dXA6mA==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + } + } + } +} \ No newline at end of file diff --git a/Sdk/Speckle.Connectors.Common.Tests/Cancellation/CancellationManagerTests.cs b/Sdk/Speckle.Connectors.Common.Tests/Cancellation/CancellationManagerTests.cs new file mode 100644 index 000000000..034d57093 --- /dev/null +++ b/Sdk/Speckle.Connectors.Common.Tests/Cancellation/CancellationManagerTests.cs @@ -0,0 +1,109 @@ +using FluentAssertions; +using NUnit.Framework; +using Speckle.Connectors.Common.Cancellation; + +namespace Speckle.Connectors.Common.Tests.Cancellation; + +public class CancellationManagerTests +{ + [Test] + public void CancelOne() + { + var manager = new CancellationManager(); + manager.NumberOfOperations.Should().Be(0); + + var id = Guid.NewGuid().ToString(); + var item = manager.GetCancellationItem(id); + + manager.NumberOfOperations.Should().Be(1); + manager.IsExist(id).Should().BeTrue(); + manager.CancelOperation(id); + item.Token.IsCancellationRequested.Should().BeTrue(); + manager.IsCancellationRequested(id).Should().BeTrue(); + manager.IsExist(id).Should().BeTrue(); + item.Dispose(); + + manager.IsExist(id).Should().BeFalse(); + item.Token.IsCancellationRequested.Should().BeTrue(); + + manager.IsCancellationRequested(id).Should().BeTrue(); + } + + [Test] + public void CancelTwo() + { + var manager = new CancellationManager(); + manager.NumberOfOperations.Should().Be(0); + + var id1 = Guid.NewGuid().ToString(); + var id2 = Guid.NewGuid().ToString(); + var item1 = manager.GetCancellationItem(id1); + var item2 = manager.GetCancellationItem(id2); + + manager.NumberOfOperations.Should().Be(2); + manager.IsExist(id1).Should().BeTrue(); + manager.CancelOperation(id1); + item1.Token.IsCancellationRequested.Should().BeTrue(); + manager.IsCancellationRequested(id1).Should().BeTrue(); + manager.IsExist(id1).Should().BeTrue(); + item1.Dispose(); + + manager.IsExist(id1).Should().BeFalse(); + item1.Token.IsCancellationRequested.Should().BeTrue(); + + manager.IsCancellationRequested(id1).Should().BeTrue(); + + manager.NumberOfOperations.Should().Be(1); + manager.IsExist(id2).Should().BeTrue(); + manager.CancelOperation(id2); + item2.Token.IsCancellationRequested.Should().BeTrue(); + manager.IsCancellationRequested(id2).Should().BeTrue(); + manager.IsExist(id2).Should().BeTrue(); + item2.Dispose(); + + manager.IsExist(id2).Should().BeFalse(); + item2.Token.IsCancellationRequested.Should().BeTrue(); + + manager.IsCancellationRequested(id2).Should().BeTrue(); + } + + [Test] + public void CancelAll() + { + var manager = new CancellationManager(); + manager.NumberOfOperations.Should().Be(0); + + var id1 = Guid.NewGuid().ToString(); + var id2 = Guid.NewGuid().ToString(); + var item1 = manager.GetCancellationItem(id1); + var item2 = manager.GetCancellationItem(id2); + + manager.NumberOfOperations.Should().Be(2); + manager.IsExist(id1).Should().BeTrue(); + manager.CancelAllOperations(); + item1.Token.IsCancellationRequested.Should().BeTrue(); + item2.Token.IsCancellationRequested.Should().BeTrue(); + manager.IsCancellationRequested(id1).Should().BeTrue(); + manager.IsCancellationRequested(id2).Should().BeTrue(); + manager.IsExist(id1).Should().BeTrue(); + manager.IsExist(id2).Should().BeTrue(); + item1.Dispose(); + item2.Dispose(); + + manager.IsExist(id1).Should().BeFalse(); + manager.IsExist(id2).Should().BeFalse(); + + item1.Token.IsCancellationRequested.Should().BeTrue(); + item2.Token.IsCancellationRequested.Should().BeTrue(); + } + + [Test] + public void Cancel_No_Existing() + { + var manager = new CancellationManager(); + manager.NumberOfOperations.Should().Be(0); + var x = Guid.NewGuid().ToString(); + manager.IsCancellationRequested(x).Should().BeTrue(); + manager.IsExist(x).Should().BeFalse(); + } +} diff --git a/Sdk/Speckle.Connectors.Common.Tests/Operations/SendOperationTests.cs b/Sdk/Speckle.Connectors.Common.Tests/Operations/SendOperationTests.cs new file mode 100644 index 000000000..4d6abd3f8 --- /dev/null +++ b/Sdk/Speckle.Connectors.Common.Tests/Operations/SendOperationTests.cs @@ -0,0 +1,131 @@ +using System.Reflection; +using FluentAssertions; +using Moq; +using NUnit.Framework; +using Speckle.Connectors.Common.Builders; +using Speckle.Connectors.Common.Caching; +using Speckle.Connectors.Common.Conversion; +using Speckle.Connectors.Common.Operations; +using Speckle.Connectors.Common.Threading; +using Speckle.Sdk.Api; +using Speckle.Sdk.Credentials; +using Speckle.Sdk.Host; +using Speckle.Sdk.Logging; +using Speckle.Sdk.Models; +using Speckle.Sdk.Serialisation; +using Speckle.Sdk.Serialisation.V2.Send; +using Speckle.Testing; + +namespace Speckle.Connectors.Common.Tests.Operations; + +public class SendOperationTests : MoqTest +{ +#pragma warning disable CA1034 + [SpeckleType("TestBase")] + public class TestBase : Base; +#pragma warning restore CA1034 + [Test] +#pragma warning disable CA1506 + public async Task Execute() +#pragma warning restore CA1506 + { + TypeLoader.Reset(); + TypeLoader.Initialize(Assembly.GetExecutingAssembly()); + var rootObjectBuilder = Create>(); + var sendConversionCache = Create(); + var accountService = Create(); + var sendProgress = Create(); + var operations = Create(); + var sendOperationVersionRecorder = Create(); + var activityFactory = Create(); + var threadContext = Create(); + + var ct = new CancellationToken(); + var objects = new List(); + var sendInfo = new SendInfo(string.Empty, new Uri("https://localhost"), string.Empty, string.Empty, string.Empty); + var progress = Create>(); + + var conversionResults = new List(); + var rootResult = new RootObjectBuilderResult(new TestBase(), conversionResults); + rootObjectBuilder.Setup(x => x.Build(objects, sendInfo, progress.Object, ct)).ReturnsAsync(rootResult); + + var rootId = "rootId"; + var refs = new Dictionary(); + var serializeProcessResults = new SerializeProcessResults(rootId, refs); + threadContext + .Setup(x => x.RunOnThreadAsync(It.IsAny>>(), false)) + .ReturnsAsync(serializeProcessResults); + + var sendOperation = new SendOperation( + rootObjectBuilder.Object, + sendConversionCache.Object, + accountService.Object, + sendProgress.Object, + operations.Object, + sendOperationVersionRecorder.Object, + activityFactory.Object, + threadContext.Object + ); + var result = await sendOperation.Execute(objects, sendInfo, progress.Object, ct); + result.Should().NotBeNull(); + rootResult.RootObject["version"].Should().Be(3); + result.RootObjId.Should().Be(rootId); + result.ConvertedReferences.Should().BeSameAs(refs); + result.ConversionResults.Should().BeSameAs(conversionResults); + } + + [Test] + public async Task Send() + { + TypeLoader.Reset(); + TypeLoader.Initialize(Assembly.GetExecutingAssembly()); + var rootObjectBuilder = Create>(); + var sendConversionCache = Create(); + var accountService = Create(); + var sendProgress = Create(); + var operations = Create(); + var sendOperationVersionRecorder = Create(); + var activityFactory = Create(); + var threadContext = Create(); + + var commitObject = new TestBase(); + var projectId = "projectId"; + var modelId = "modelId"; + var accountId = "accountId"; + var url = new Uri("https://localhost"); + var sendInfo = new SendInfo(accountId, url, projectId, modelId, string.Empty); + var progress = Create>(MockBehavior.Loose); + + var ct = new CancellationToken(); + + var token = "token"; + var account = new Account() { token = token }; + var rootId = "rootId"; + var refs = new Dictionary(); + var serializeProcessResults = new SerializeProcessResults(rootId, refs); + accountService.Setup(x => x.GetAccountWithServerUrlFallback(accountId, url)).Returns(account); + activityFactory.Setup(x => x.Start("SendOperation", "Send")).Returns((ISdkActivity?)null); + + operations + .Setup(x => x.Send2(url, projectId, token, commitObject, It.IsAny(), ct)) + .ReturnsAsync(serializeProcessResults); + + sendConversionCache.Setup(x => x.StoreSendResult(projectId, refs)); + sendProgress.Setup(x => x.Begin()); + + sendOperationVersionRecorder.Setup(x => x.RecordVersion(rootId, sendInfo, account, ct)).Returns(Task.CompletedTask); + + var sendOperation = new SendOperation( + rootObjectBuilder.Object, + sendConversionCache.Object, + accountService.Object, + sendProgress.Object, + operations.Object, + sendOperationVersionRecorder.Object, + activityFactory.Object, + threadContext.Object + ); + var result = await sendOperation.Send(commitObject, sendInfo, progress.Object, ct); + result.Should().Be(serializeProcessResults); + } +} diff --git a/Sdk/Speckle.Connectors.Common.Tests/Speckle.Connectors.Common.Tests.csproj b/Sdk/Speckle.Connectors.Common.Tests/Speckle.Connectors.Common.Tests.csproj new file mode 100644 index 000000000..965d2164b --- /dev/null +++ b/Sdk/Speckle.Connectors.Common.Tests/Speckle.Connectors.Common.Tests.csproj @@ -0,0 +1,22 @@ + + + + net8.0 + false + true + Debug;Release;Local + + + + + + + + + + + + + + + diff --git a/Sdk/Speckle.Connectors.Common.Tests/Threading/Funcs.cs b/Sdk/Speckle.Connectors.Common.Tests/Threading/Funcs.cs new file mode 100644 index 000000000..58c0b41a9 --- /dev/null +++ b/Sdk/Speckle.Connectors.Common.Tests/Threading/Funcs.cs @@ -0,0 +1,18 @@ +using System.Diagnostics.CodeAnalysis; + +namespace Speckle.Connectors.Common.Tests.Threading; + +[SuppressMessage("Design", "CA1008:Enums should have zero value")] +public enum Funcs +{ + RunMain, + RunWorker, + RunMainAsync, + RunWorkerAsync, + WorkerToMainAsync, + MainToWorker, + WorkerToMain, + MainToWorkerAsync, + RunMainAsync_T, + RunWorkerAsync_T +} diff --git a/Sdk/Speckle.Connectors.Common.Tests/Threading/TestThreadContext.cs b/Sdk/Speckle.Connectors.Common.Tests/Threading/TestThreadContext.cs new file mode 100644 index 000000000..d3d39ebe7 --- /dev/null +++ b/Sdk/Speckle.Connectors.Common.Tests/Threading/TestThreadContext.cs @@ -0,0 +1,103 @@ +using FluentAssertions; +using Speckle.Connectors.Common.Threading; + +namespace Speckle.Connectors.Common.Tests.Threading; + +public class TestThreadContext(bool isMain) : ThreadContext +{ + public override bool IsMainThread => isMain; + + public Funcs? Func { get; set; } + + protected override void RunMain(Action action) + { + action(); + Func.Should().BeNull(); + Func = Funcs.RunMain; + } + + protected override void RunWorker(Action action) + { + action(); + Func.Should().BeNull(); + Func = Funcs.RunWorker; + } + + protected override async Task WorkerToMainAsync(Func> action) + { + var x = await action(); + Func.Should().BeNull(); + Func = Funcs.WorkerToMainAsync; + return x; + } + + protected override async Task MainToWorkerAsync(Func> action) + { + var x = await action(); + Func.Should().BeNull(); + Func = Funcs.MainToWorkerAsync; + return x; + } + + protected override Task WorkerToMain(Func action) + { + var x = action(); + Func.Should().BeNull(); + Func = Funcs.WorkerToMain; + return Task.FromResult(x); + } + + protected override Task MainToWorker(Func action) + { + var x = action(); + Func.Should().BeNull(); + Func = Funcs.MainToWorker; + return Task.FromResult(x); + } + + protected override Task RunMainAsync(Func action) + { + var x = action(); + Func.Should().BeNull(); + Func = Funcs.RunMainAsync_T; + return Task.FromResult(x); + } + + protected override Task RunWorkerAsync(Func action) + { + var x = action(); + Func.Should().BeNull(); + Func = Funcs.RunWorkerAsync_T; + return Task.FromResult(x); + } + + protected override async Task RunMainAsync(Func action) + { + await action(); + Func.Should().BeNull(); + Func = Funcs.RunMainAsync; + } + + protected override async Task RunWorkerAsync(Func action) + { + await action(); + Func.Should().BeNull(); + Func = Funcs.RunWorkerAsync; + } + + protected override async Task RunMainAsync(Func> action) + { + var x = await action(); + Func.Should().BeNull(); + Func = Funcs.RunMainAsync_T; + return x; + } + + protected override async Task RunWorkerAsync(Func> action) + { + var x = await action(); + Func.Should().BeNull(); + Func = Funcs.RunWorkerAsync_T; + return x; + } +} diff --git a/Sdk/Speckle.Connectors.Common.Tests/Threading/ThreadContextExtensionTests.cs b/Sdk/Speckle.Connectors.Common.Tests/Threading/ThreadContextExtensionTests.cs new file mode 100644 index 000000000..3ca61ec49 --- /dev/null +++ b/Sdk/Speckle.Connectors.Common.Tests/Threading/ThreadContextExtensionTests.cs @@ -0,0 +1,93 @@ +using FluentAssertions; +using Moq; +using NUnit.Framework; +using Speckle.Connectors.Common.Threading; +using Speckle.Testing; + +namespace Speckle.Connectors.Common.Tests.Threading; + +public class ThreadContextExtensionTests : MoqTest +{ + [Test] + public async Task RunOnMain() + { + Action a = () => { }; + var tc = Create(); + tc.Setup(x => x.RunOnThread(a, true)).Returns(Task.CompletedTask); + await tc.Object.RunOnMain(a); + } + + [Test] + public async Task RunOnWorker() + { + Action a = () => { }; + var tc = Create(); + tc.Setup(x => x.RunOnThread(a, false)).Returns(Task.CompletedTask); + await tc.Object.RunOnWorker(a); + } + + [Test] + public async Task RunOnMain_T() + { + Func a = () => true; + var tc = Create(); + tc.Setup(x => x.RunOnThread(a, true)).ReturnsAsync(true); + (await tc.Object.RunOnMain(a)).Should().BeTrue(); + } + + [Test] + public async Task RunOnWorker_T() + { + Func a = () => true; + var tc = Create(); + tc.Setup(x => x.RunOnThread(a, false)).ReturnsAsync(true); + (await tc.Object.RunOnWorker(a)).Should().BeTrue(); + } + + [Test] + public async Task RunOnMainAsync() + { + Func a = () => Task.CompletedTask; + var tc = Create(); + tc.Setup(x => x.RunOnThreadAsync(a, true)).Returns(Task.CompletedTask); + await tc.Object.RunOnMainAsync(a); + } + + [Test] + public async Task RunOnWorkerAsync() + { + Func a = () => Task.CompletedTask; + var tc = Create(); + tc.Setup(x => x.RunOnThreadAsync(a, false)).Returns(Task.CompletedTask); + await tc.Object.RunOnWorkerAsync(a); + } + + [Test] + public async Task RunOnMainAsync_T() + { + Func> a = () => Task.FromResult(true); + var tc = Create(); + tc.Setup(x => x.RunOnThreadAsync(a, true)).ReturnsAsync(true); + (await tc.Object.RunOnMainAsync(a)).Should().BeTrue(); + } + + [Test] + public async Task RunOnWorkerAsync_T() + { + Func> a = () => Task.FromResult(true); + var tc = Create(); + tc.Setup(x => x.RunOnThreadAsync(a, false)).ReturnsAsync(true); + await tc.Object.RunOnWorkerAsync(a); + (await tc.Object.RunOnWorkerAsync(a)).Should().BeTrue(); + } + + [Test] +#pragma warning disable CA1030 + public async Task FireAndForget() +#pragma warning restore CA1030 + { + //kind of does nothing, just making sure there's no error + Task.CompletedTask.FireAndForget(); + await Task.Delay(500); + } +} diff --git a/Sdk/Speckle.Connectors.Common.Tests/Threading/ThreadContextTests.cs b/Sdk/Speckle.Connectors.Common.Tests/Threading/ThreadContextTests.cs new file mode 100644 index 000000000..48b2a69e3 --- /dev/null +++ b/Sdk/Speckle.Connectors.Common.Tests/Threading/ThreadContextTests.cs @@ -0,0 +1,93 @@ +using FluentAssertions; +using NUnit.Framework; +using Speckle.Testing; + +namespace Speckle.Connectors.Common.Tests.Threading; + +public class ThreadContextTests : MoqTest +{ + [Test] + [TestCase(true, true, Funcs.RunMain)] + [TestCase(true, false, Funcs.MainToWorker)] + [TestCase(false, true, Funcs.WorkerToMain)] + [TestCase(false, false, Funcs.RunWorker)] + public async Task RunOnThread(bool isMain, bool useMain, Funcs? result) + { + var tc = new TestThreadContext(isMain); + bool resultRan = false; + await tc.RunOnThread( + () => + { + resultRan = true; + }, + useMain + ); + resultRan.Should().BeTrue(); + tc.Func.Should().Be(result); + } + + [Test] + [TestCase(true, true, Funcs.RunMainAsync_T)] + [TestCase(true, false, Funcs.MainToWorker)] + [TestCase(false, true, Funcs.WorkerToMain)] + [TestCase(false, false, Funcs.RunWorkerAsync_T)] + public async Task RunOnThread_T(bool isMain, bool useMain, Funcs? result) + { + var tc = new TestThreadContext(isMain); + bool resultRan = false; + var x = await tc.RunOnThread( + () => + { + resultRan = true; + return false; + }, + useMain + ); + resultRan.Should().BeTrue(); + x.Should().BeFalse(); + tc.Func.Should().Be(result); + } + + [Test] + [TestCase(true, true, Funcs.RunMainAsync)] + [TestCase(true, false, Funcs.MainToWorkerAsync)] + [TestCase(false, true, Funcs.WorkerToMainAsync)] + [TestCase(false, false, Funcs.RunWorkerAsync)] + public async Task RunOnThreadAsync(bool isMain, bool useMain, Funcs? result) + { + var tc = new TestThreadContext(isMain); + bool resultRan = false; + await tc.RunOnThreadAsync( + () => + { + resultRan = true; + return Task.CompletedTask; + }, + useMain + ); + resultRan.Should().BeTrue(); + tc.Func.Should().Be(result); + } + + [Test] + [TestCase(true, true, Funcs.RunMainAsync_T)] + [TestCase(true, false, Funcs.MainToWorkerAsync)] + [TestCase(false, true, Funcs.WorkerToMainAsync)] + [TestCase(false, false, Funcs.RunWorkerAsync_T)] + public async Task RunOnThreadAsync_T(bool isMain, bool useMain, Funcs? result) + { + var tc = new TestThreadContext(isMain); + bool resultRan = false; + var x = await tc.RunOnThreadAsync( + () => + { + resultRan = true; + return Task.FromResult(false); + }, + useMain + ); + resultRan.Should().BeTrue(); + x.Should().BeFalse(); + tc.Func.Should().Be(result); + } +} diff --git a/Sdk/Speckle.Connectors.Common.Tests/packages.lock.json b/Sdk/Speckle.Connectors.Common.Tests/packages.lock.json new file mode 100644 index 000000000..fd265a9bb --- /dev/null +++ b/Sdk/Speckle.Connectors.Common.Tests/packages.lock.json @@ -0,0 +1,403 @@ +{ + "version": 2, + "dependencies": { + "net8.0": { + "altcover": { + "type": "Direct", + "requested": "[8.9.3, )", + "resolved": "8.9.3", + "contentHash": "auKC+pDCkLjfhFkSRaAUBu25BOmlLSqucR7YBs/Lkbdc0XRuJoklWafs1KKp+M+VoJ1f0TeMS6B/FO5IeIcu7w==" + }, + "FluentAssertions": { + "type": "Direct", + "requested": "[6.12.1, )", + "resolved": "6.12.1", + "contentHash": "hciWwryyLw3eonfqhFpOMTXyM1/auJChYslEBA+iGJyuBs5O3t/kA8YaeH4iRo/2Fe3ElSYL86C0miivtZ0f3g==", + "dependencies": { + "System.Configuration.ConfigurationManager": "4.4.0" + } + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "Direct", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "MZtBIwfDFork5vfjpJdG5g8wuJFt7d/y3LOSVVtDK/76wlbtz6cjltfKHqLx2TKVqTj5/c41t77m1+h20zqtPA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0" + } + }, + "Microsoft.NET.Test.Sdk": { + "type": "Direct", + "requested": "[17.11.1, )", + "resolved": "17.11.1", + "contentHash": "U3Ty4BaGoEu+T2bwSko9tWqWUOU16WzSFkq6U8zve75oRBMSLTBdMAZrVNNz1Tq12aCdDom9fcOcM9QZaFHqFg==", + "dependencies": { + "Microsoft.CodeCoverage": "17.11.1", + "Microsoft.TestPlatform.TestHost": "17.11.1" + } + }, + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net461": "1.0.3" + } + }, + "Microsoft.SourceLink.GitHub": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "dependencies": { + "Microsoft.Build.Tasks.Git": "8.0.0", + "Microsoft.SourceLink.Common": "8.0.0" + } + }, + "NUnit.Analyzers": { + "type": "Direct", + "requested": "[4.2.0, )", + "resolved": "4.2.0", + "contentHash": "4fJojPkzdoa4nB2+p6U+fITvPnVvwWSnsmiJ/Dl30xqiL3oxNbYvfeSLVd91hOmEjoUqSwN3Z7j1aFedjqWbUA==" + }, + "NUnit3TestAdapter": { + "type": "Direct", + "requested": "[4.6.0, )", + "resolved": "4.6.0", + "contentHash": "R7e1+a4vuV/YS+ItfL7f//rG+JBvVeVLX4mHzFEZo4W1qEKl8Zz27AqvQSAqo+BtIzUCo4aAJMYa56VXS4hudw==" + }, + "PolySharp": { + "type": "Direct", + "requested": "[1.14.1, )", + "resolved": "1.14.1", + "contentHash": "mOOmFYwad3MIOL14VCjj02LljyF1GNw1wP0YVlxtcPvqdxjGGMNdNJJxHptlry3MOd8b40Flm8RPOM8JOlN2sQ==" + }, + "Speckle.InterfaceGenerator": { + "type": "Direct", + "requested": "[0.9.6, )", + "resolved": "0.9.6", + "contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w==" + }, + "Castle.Core": { + "type": "Transitive", + "resolved": "5.1.1", + "contentHash": "rpYtIczkzGpf+EkZgDr9CClTdemhsrwA/W5hMoPjLkRFnXzH44zDLoovXeKtmxb1ykXK9aJVODSpiJml8CTw2g==", + "dependencies": { + "System.Diagnostics.EventLog": "6.0.0" + } + }, + "GraphQL.Client": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "8yPNBbuVBpTptivyAlak4GZvbwbUcjeQTL4vN1HKHRuOykZ4r7l5fcLS6vpyPyLn0x8FsL31xbOIKyxbmR9rbA==", + "dependencies": { + "GraphQL.Client.Abstractions": "6.0.0", + "GraphQL.Client.Abstractions.Websocket": "6.0.0", + "System.Reactive": "5.0.0" + } + }, + "GraphQL.Client.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "h7uzWFORHZ+CCjwr/ThAyXMr0DPpzEANDa4Uo54wqCQ+j7qUKwqYTgOrb1W40sqbvNaZm9v/X7It31SUw0maHA==", + "dependencies": { + "GraphQL.Primitives": "6.0.0" + } + }, + "GraphQL.Client.Abstractions.Websocket": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "Nr9bPf8gIOvLuXpqEpqr9z9jslYFJOvd0feHth3/kPqeR3uMbjF5pjiwh4jxyMcxHdr8Pb6QiXkV3hsSyt0v7A==", + "dependencies": { + "GraphQL.Client.Abstractions": "6.0.0" + } + }, + "GraphQL.Primitives": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "yg72rrYDapfsIUrul7aF6wwNnTJBOFvuA9VdDTQpPa8AlAriHbufeXYLBcodKjfUdkCnaiggX1U/nEP08Zb5GA==" + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.CodeCoverage": { + "type": "Transitive", + "resolved": "17.11.1", + "contentHash": "nPJqrcA5iX+Y0kqoT3a+pD/8lrW/V7ayqnEJQsTonSoPz59J8bmoQhcSN4G8+UJ64Hkuf0zuxnfuj2lkHOq4cA==" + }, + "Microsoft.CSharp": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==" + }, + "Microsoft.Data.Sqlite": { + "type": "Transitive", + "resolved": "7.0.5", + "contentHash": "KGxbPeWsQMnmQy43DSBxAFtHz3l2JX8EWBSGUCvT3CuZ8KsuzbkqMIJMDOxWtG8eZSoCDI04aiVQjWuuV8HmSw==", + "dependencies": { + "Microsoft.Data.Sqlite.Core": "7.0.5", + "SQLitePCLRaw.bundle_e_sqlite3": "2.1.4" + } + }, + "Microsoft.Data.Sqlite.Core": { + "type": "Transitive", + "resolved": "7.0.5", + "contentHash": "FTerRmQPqHrCrnoUzhBu+E+1DNGwyrAMLqHkAqOOOu5pGfyMOj8qQUBxI/gDtWtG11p49UxSfWmBzRNlwZqfUg==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "nOP8R1mVb/6mZtm2qgAJXn/LFm/2kMjHDAg/QJLFG6CuWYJtaD3p1BwQhufBVvRzL9ceJ/xF0SQ0qsI2GkDQAA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "65MrmXCziWaQFrI0UHkQbesrX5wTwf9XPjY5yFm/VkgJKFJ5gqvXRoXjIZcf2wLi5ZlwGz/oMYfyURVCWbM5iw==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "vJ9xvOZCnUAIHcGC3SU35r3HKmHTVIeHzo6u/qzlHAqD8m6xv92MLin4oJntTvkpKxVX3vI1GFFkIQtU3AdlsQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "2.2.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw==" + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "UpZLNLBpIZ0GTebShui7xXYh6DmBHjWM8NxGxZbdQh/bPZ5e6YswqI+bru6BnEL5eWiOdodsXtEz3FROcgi/qg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Primitives": "2.2.0", + "System.ComponentModel.Annotations": "4.5.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "azyQtqbm4fSaDzZHD/J+V6oWMFaf2tWP4WEGIYePLCMw3+b2RQdj9ybgbQyjCshcitQKQ4lEDOZjmSlTTrHxUg==", + "dependencies": { + "System.Memory": "4.5.1", + "System.Runtime.CompilerServices.Unsafe": "4.5.1" + } + }, + "Microsoft.NETFramework.ReferenceAssemblies.net461": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "AmOJZwCqnOCNp6PPcf9joyogScWLtwy0M1WkqfEQ0M9nYwyDD7EX9ZjscKS5iYnyvteX7kzSKFCKt9I9dXA6mA==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "Microsoft.TestPlatform.ObjectModel": { + "type": "Transitive", + "resolved": "17.11.1", + "contentHash": "E2jZqAU6JeWEVsyOEOrSW1o1bpHLgb25ypvKNB/moBXPVsFYBPd/Jwi7OrYahG50J83LfHzezYI+GaEkpAotiA==", + "dependencies": { + "System.Reflection.Metadata": "1.6.0" + } + }, + "Microsoft.TestPlatform.TestHost": { + "type": "Transitive", + "resolved": "17.11.1", + "contentHash": "DnG+GOqJXO/CkoqlJWeDFTgPhqD/V6VqUIL3vINizCWZ3X+HshCtbbyDdSHQQEjrc2Sl/K3yaxX6s+5LFEdYuw==", + "dependencies": { + "Microsoft.TestPlatform.ObjectModel": "17.11.1", + "Newtonsoft.Json": "13.0.1" + } + }, + "Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.1", + "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" + }, + "Speckle.Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.2", + "contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA==" + }, + "SQLitePCLRaw.bundle_e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "EWI1olKDjFEBMJu0+3wuxwziIAdWDVMYLhuZ3Qs84rrz+DHwD00RzWPZCa+bLnHCf3oJwuFZIRsHT5p236QXww==", + "dependencies": { + "SQLitePCLRaw.lib.e_sqlite3": "2.1.4", + "SQLitePCLRaw.provider.e_sqlite3": "2.1.4" + } + }, + "SQLitePCLRaw.core": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "inBjvSHo9UDKneGNzfUfDjK08JzlcIhn1+SP5Y3m6cgXpCxXKCJDy6Mka7LpgSV+UZmKSnC8rTwB0SQ0xKu5pA==", + "dependencies": { + "System.Memory": "4.5.3" + } + }, + "SQLitePCLRaw.lib.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "2C9Q9eX7CPLveJA0rIhf9RXAvu+7nWZu1A2MdG6SD/NOu26TakGgL1nsbc0JAspGijFOo3HoN79xrx8a368fBg==" + }, + "SQLitePCLRaw.provider.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "CSlb5dUp1FMIkez9Iv5EXzpeq7rHryVNqwJMWnpq87j9zWZexaEMdisDktMsnnrzKM6ahNrsTkjqNodTBPBxtQ==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "System.ComponentModel.Annotations": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "UxYQ3FGUOtzJ7LfSdnYSFd7+oEv6M8NgUatatIN2HxNtDdlcvFAf+VIq4Of9cDMJEJC0aSRv/x898RYhB4Yppg==" + }, + "System.Configuration.ConfigurationManager": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "gWwQv/Ug1qWJmHCmN17nAbxJYmQBM/E94QxKLksvUiiKB1Ld3Sc/eK1lgmbSjDFxkQhVuayI/cGFZhpBSodLrg==", + "dependencies": { + "System.Security.Cryptography.ProtectedData": "4.4.0" + } + }, + "System.Diagnostics.EventLog": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "lcyUiXTsETK2ALsZrX+nWuHSIQeazhqPphLfaRxzdGaG93+0kELqpgEHtwWOlQe7+jSFnKwaCAgL4kjeZCQJnw==" + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.3", + "contentHash": "3oDzvc/zzetpTKWMShs1AADwZjQ/36HnsufHRPcOjyRAAMLDlu2iD33MBI2opxnezcVUtXyqDXXjoFMOU9c7SA==" + }, + "System.Reactive": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==" + }, + "System.Reflection.Metadata": { + "type": "Transitive", + "resolved": "1.6.0", + "contentHash": "COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ==" + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Zh8t8oqolRaFa9vmOZfdQm/qKejdqz0J9kr7o2Fu0vPeoH3BL1EOXipKWwkWtLT1JPzjByrF19fGuFlNbmPpiw==" + }, + "System.Security.Cryptography.ProtectedData": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "cJV7ScGW7EhatRsjehfvvYVBvtiSMKgN8bOVI0bQhnF5bU7vnHVIsH49Kva7i7GWaWYvmEzkYVk1TC+gZYBEog==" + }, + "speckle.connectors.common": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", + "Speckle.Connectors.Logging": "[1.0.0, )", + "Speckle.Objects": "[3.1.1, )", + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" + } + }, + "speckle.connectors.logging": { + "type": "Project" + }, + "speckle.testing": { + "type": "Project", + "dependencies": { + "Moq": "[4.20.70, )", + "NUnit": "[4.1.0, )" + } + }, + "Microsoft.Extensions.Logging": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "Nxqhadc9FCmFHzU+fz3oc8sFlE6IadViYg8dfUdGzJZ2JUxnCsRghBhhOWdM4B2zSZqEc+0BjliBh/oNdRZuig==", + "dependencies": { + "Microsoft.Extensions.Configuration.Binder": "2.2.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging.Abstractions": "2.2.0", + "Microsoft.Extensions.Options": "2.2.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" + }, + "Moq": { + "type": "CentralTransitive", + "requested": "[4.20.70, )", + "resolved": "4.20.70", + "contentHash": "4rNnAwdpXJBuxqrOCzCyICXHSImOTRktCgCWXWykuF1qwoIsVvEnR7PjbMk/eLOxWvhmj5Kwt+kDV3RGUYcNwg==", + "dependencies": { + "Castle.Core": "5.1.1" + } + }, + "NUnit": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "MT/DpAhjtiytzhTgTqIhBuWx4y26PKfDepYUHUM+5uv4TsryHC2jwFo5e6NhWkApCm/G6kZ80dRjdJFuAxq3rg==" + }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, + "Speckle.Objects": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", + "dependencies": { + "Speckle.Sdk": "3.1.1" + } + }, + "Speckle.Sdk": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", + "dependencies": { + "GraphQL.Client": "6.0.0", + "Microsoft.CSharp": "4.7.0", + "Microsoft.Data.Sqlite": "7.0.5", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging": "2.2.0", + "Speckle.DoubleNumerics": "4.1.0", + "Speckle.Newtonsoft.Json": "13.0.2", + "Speckle.Sdk.Dependencies": "3.1.1" + } + }, + "Speckle.Sdk.Dependencies": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" + } + } + } +} \ No newline at end of file diff --git a/Sdk/Speckle.Connectors.Common/Builders/IHostObjectBuilder.cs b/Sdk/Speckle.Connectors.Common/Builders/IHostObjectBuilder.cs index 7d9e150dc..aa19cc90f 100644 --- a/Sdk/Speckle.Connectors.Common/Builders/IHostObjectBuilder.cs +++ b/Sdk/Speckle.Connectors.Common/Builders/IHostObjectBuilder.cs @@ -14,7 +14,6 @@ public interface IHostObjectBuilder /// Project of the model. /// Name of the model. /// Action to update UI progress bar. - /// Cancellation token that passed from top -> ReceiveBinding. /// List of application ids. // POC: Where we will return these ids will matter later when we target to also cache received application ids. /// Project and model name are needed for now to construct host app objects into related layers or filters. /// POC: we might consider later to have HostObjectBuilderContext? that might hold all possible data we will need. diff --git a/Sdk/Speckle.Connectors.Common/Builders/IRootObjectBuilder.cs b/Sdk/Speckle.Connectors.Common/Builders/IRootObjectBuilder.cs index f3584c726..dc4e95af9 100644 --- a/Sdk/Speckle.Connectors.Common/Builders/IRootObjectBuilder.cs +++ b/Sdk/Speckle.Connectors.Common/Builders/IRootObjectBuilder.cs @@ -1,4 +1,4 @@ -using Speckle.Connectors.Common.Conversion; +using Speckle.Connectors.Common.Conversion; using Speckle.Connectors.Common.Operations; using Speckle.Sdk.Models; @@ -10,8 +10,25 @@ public interface IRootObjectBuilder IReadOnlyList objects, SendInfo sendInfo, IProgress onOperationProgressed, - CancellationToken ct = default + CancellationToken cancellationToken ); } +// public abstract class RootObjectBuilderBase : IRootObjectBuilder +// { +// public Task BuildAsync( +// IReadOnlyList objects, +// SendInfo sendInfo, +// IProgress onOperationProgressed, +// CancellationToken cancellationToken +// ) => Task.FromResult(Build(objects, sendInfo, onOperationProgressed, cancellationToken)); +// +// public abstract RootObjectBuilderResult Build( +// IReadOnlyList objects, +// SendInfo sendInfo, +// IProgress onOperationProgressed, +// CancellationToken cancellationToken +// ); +// } + public record RootObjectBuilderResult(Base RootObject, IEnumerable ConversionResults); diff --git a/Sdk/Speckle.Connectors.Common/Cancellation/CancellationManager.cs b/Sdk/Speckle.Connectors.Common/Cancellation/CancellationManager.cs index 7e7c85891..79f1d0fbc 100644 --- a/Sdk/Speckle.Connectors.Common/Cancellation/CancellationManager.cs +++ b/Sdk/Speckle.Connectors.Common/Cancellation/CancellationManager.cs @@ -1,10 +1,25 @@ +using Speckle.InterfaceGenerator; + namespace Speckle.Connectors.Common.Cancellation; +public interface ICancellationItem : IDisposable +{ + CancellationToken Token { get; } +} + /// /// Util class to manage cancellations. /// -public class CancellationManager +[GenerateAutoInterface] +public class CancellationManager : ICancellationManager { + private sealed class CancellationItem(CancellationManager manager, string id) : ICancellationItem + { + public void Dispose() => manager.DisposeOperation(id); + + public CancellationToken Token => manager.GetToken(id); + } + /// /// Dictionary to relate with registered id. /// @@ -12,34 +27,18 @@ public class CancellationManager public int NumberOfOperations => _operationsInProgress.Count; - /// - /// Get token with registered id. - /// - /// Id of the operation. - /// CancellationToken that belongs to operation. - public CancellationToken GetToken(string id) - { - return _operationsInProgress[id].Token; - } + //if we can't find it then it must be cancelled + private CancellationToken GetToken(string id) => + _operationsInProgress.TryGetValue(id, out var source) ? source.Token : new CancellationToken(true); - /// - /// Whether given id registered or not. - /// - /// Id to check registration. - /// Whether given id registered or not. - public bool IsExist(string id) - { - return _operationsInProgress.ContainsKey(id); - } + public bool IsExist(string id) => _operationsInProgress.ContainsKey(id); public void CancelAllOperations() { foreach (var operation in _operationsInProgress) { operation.Value.Cancel(); - operation.Value.Dispose(); } - _operationsInProgress.Clear(); } /// @@ -48,16 +47,13 @@ public class CancellationManager /// /// Id to register token. /// Initialized cancellation token source. - public CancellationToken InitCancellationTokenSource(string id) + public ICancellationItem GetCancellationItem(string id) { - if (IsExist(id)) - { - CancelOperation(id); - } + DisposeOperation(id); var cts = new CancellationTokenSource(); _operationsInProgress[id] = cts; - return cts.Token; + return new CancellationItem(this, id); } /// @@ -66,7 +62,15 @@ public class CancellationManager /// Id to cancel operation. public void CancelOperation(string id) { - if (_operationsInProgress.TryGetValue(id, out CancellationTokenSource cts)) + if (_operationsInProgress.TryGetValue(id, out CancellationTokenSource? cts)) + { + cts.Cancel(); + } + } + + private void DisposeOperation(string id) + { + if (_operationsInProgress.TryGetValue(id, out CancellationTokenSource? cts)) { cts.Cancel(); cts.Dispose(); @@ -79,8 +83,5 @@ public class CancellationManager /// /// Id to check cancellation requested already or not. /// - public bool IsCancellationRequested(string id) - { - return _operationsInProgress[id].IsCancellationRequested; - } + public bool IsCancellationRequested(string id) => GetToken(id).IsCancellationRequested; } diff --git a/Sdk/Speckle.Connectors.Common/Cancellation/ICancelable.cs b/Sdk/Speckle.Connectors.Common/Cancellation/ICancelable.cs deleted file mode 100644 index c0db8880e..000000000 --- a/Sdk/Speckle.Connectors.Common/Cancellation/ICancelable.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Speckle.Connectors.Common.Cancellation; - -/// -/// Provides a mechanism for cancelling operations. -/// -public interface ICancelable -{ - public CancellationManager CancellationManager { get; } -} diff --git a/Sdk/Speckle.Connectors.Common/ContainerRegistration.cs b/Sdk/Speckle.Connectors.Common/ContainerRegistration.cs index 84ee2251d..62c03a0c3 100644 --- a/Sdk/Speckle.Connectors.Common/ContainerRegistration.cs +++ b/Sdk/Speckle.Connectors.Common/ContainerRegistration.cs @@ -9,23 +9,11 @@ using Speckle.Sdk; namespace Speckle.Connectors.Common; public static class ContainerRegistration -{ /* - public static void AddConnectorUtils(this SpeckleContainerBuilder builder) - { - // send operation and dependencies - builder.AddSingleton(); - builder.AddScoped(); - builder.AddScoped(); - builder.AddSingleton(); - builder.ScanAssembly(Assembly.GetExecutingAssembly()); - - builder.ContainerBuilder.RegisterGeneric(typeof(Logger<>)).As(typeof(ILogger<>)).SingleInstance(); - } -*/ +{ public static void AddConnectorUtils(this IServiceCollection serviceCollection) { // send operation and dependencies - serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); serviceCollection.AddScoped(); serviceCollection.AddScoped(); serviceCollection.AddSingleton(); diff --git a/Sdk/Speckle.Connectors.Common/Conversion/ReportResult.cs b/Sdk/Speckle.Connectors.Common/Conversion/ReportResult.cs index 64d421bbc..c27e3bba8 100644 --- a/Sdk/Speckle.Connectors.Common/Conversion/ReportResult.cs +++ b/Sdk/Speckle.Connectors.Common/Conversion/ReportResult.cs @@ -1,3 +1,4 @@ +using Speckle.Sdk.Common; using Speckle.Sdk.Models; namespace Speckle.Connectors.Common.Conversion; @@ -49,7 +50,7 @@ public class ReceiveConversionResult : ConversionResult ) { Status = status; - SourceId = source.id; + SourceId = source.id.NotNull(); SourceType = source.speckle_type; // Note: we'll parse it nicely in FE ResultId = resultId; ResultType = resultType; diff --git a/Sdk/Speckle.Connectors.Common/Extensions/CollectionExtensions.cs b/Sdk/Speckle.Connectors.Common/Extensions/CollectionExtensions.cs new file mode 100644 index 000000000..cc4883a8d --- /dev/null +++ b/Sdk/Speckle.Connectors.Common/Extensions/CollectionExtensions.cs @@ -0,0 +1,20 @@ +namespace Speckle.Connectors.Common.Extensions; + +public static class CollectionExtensions +{ + public static void AddRange(this ICollection collection, IEnumerable items) + { + foreach (var item in items) + { + collection.Add(item); + } + } + +#if NETSTANDARD2_0 + public static HashSet ToHashSet(this IEnumerable items) + { + var set = new HashSet(items); + return set; + } +#endif +} diff --git a/Sdk/Speckle.Connectors.Common/Instances/BakeResult.cs b/Sdk/Speckle.Connectors.Common/Instances/BakeResult.cs index b3866ae7e..447b8cee9 100644 --- a/Sdk/Speckle.Connectors.Common/Instances/BakeResult.cs +++ b/Sdk/Speckle.Connectors.Common/Instances/BakeResult.cs @@ -3,7 +3,7 @@ namespace Speckle.Connectors.Common.Instances; public record BakeResult( - List CreatedInstanceIds, - List ConsumedObjectIds, - List InstanceConversionResults + IReadOnlyCollection CreatedInstanceIds, + IReadOnlyCollection ConsumedObjectIds, + IReadOnlyCollection InstanceConversionResults ); diff --git a/Sdk/Speckle.Connectors.Common/Instances/IInstanceBaker.cs b/Sdk/Speckle.Connectors.Common/Instances/IInstanceBaker.cs index fe6fa4af7..a2a8e559f 100644 --- a/Sdk/Speckle.Connectors.Common/Instances/IInstanceBaker.cs +++ b/Sdk/Speckle.Connectors.Common/Instances/IInstanceBaker.cs @@ -14,8 +14,8 @@ public interface IInstanceBaker /// /// /// - Task BakeInstances( - IReadOnlyCollection<(Collection[] collectionPath, IInstanceComponent obj)> instanceComponents, + BakeResult BakeInstances( + ICollection<(Collection[] collectionPath, IInstanceComponent obj)> instanceComponents, Dictionary applicationIdMap, string baseLayerName, IProgress onOperationProgressed diff --git a/Sdk/Speckle.Connectors.Common/Instances/InstanceObjectsManager.cs b/Sdk/Speckle.Connectors.Common/Instances/InstanceObjectsManager.cs index 306b69cdf..2af8cb20e 100644 --- a/Sdk/Speckle.Connectors.Common/Instances/InstanceObjectsManager.cs +++ b/Sdk/Speckle.Connectors.Common/Instances/InstanceObjectsManager.cs @@ -30,7 +30,7 @@ public class InstanceObjectsManager [NotNullWhen(true)] out List? instanceProxiesWithSameDefinition ) { - if (_instanceProxiesByDefinitionId.TryGetValue(definitionId, out List value)) + if (_instanceProxiesByDefinitionId.TryGetValue(definitionId, out List? value)) { instanceProxiesWithSameDefinition = value; return true; @@ -44,7 +44,7 @@ public class InstanceObjectsManager [NotNullWhen(true)] out InstanceDefinitionProxy? instanceDefinitionProxy ) { - if (_definitionProxies.TryGetValue(definitionId, out InstanceDefinitionProxy value)) + if (_definitionProxies.TryGetValue(definitionId, out InstanceDefinitionProxy? value)) { instanceDefinitionProxy = value; return true; diff --git a/Sdk/Speckle.Connectors.Common/Instances/LocalToGlobalMap.cs b/Sdk/Speckle.Connectors.Common/Instances/LocalToGlobalMap.cs index 9e20fc111..24f836ebd 100644 --- a/Sdk/Speckle.Connectors.Common/Instances/LocalToGlobalMap.cs +++ b/Sdk/Speckle.Connectors.Common/Instances/LocalToGlobalMap.cs @@ -9,7 +9,7 @@ namespace Speckle.Connectors.Common.Instances; public class LocalToGlobalMap { - public LocalToGlobalMap(TraversalContext traversalContext, Base atomicObject, List matrix) + public LocalToGlobalMap(TraversalContext traversalContext, Base atomicObject, IReadOnlyCollection matrix) { TraversalContext = traversalContext; AtomicObject = atomicObject; @@ -18,5 +18,5 @@ public class LocalToGlobalMap public TraversalContext TraversalContext { get; set; } public Base AtomicObject { get; set; } - public List Matrix { get; set; } + public IReadOnlyCollection Matrix { get; set; } } diff --git a/Sdk/Speckle.Connectors.Common/Instances/LocalToGlobalUnpacker.cs b/Sdk/Speckle.Connectors.Common/Instances/LocalToGlobalUnpacker.cs index 3a13c6cc7..1451a9a89 100644 --- a/Sdk/Speckle.Connectors.Common/Instances/LocalToGlobalUnpacker.cs +++ b/Sdk/Speckle.Connectors.Common/Instances/LocalToGlobalUnpacker.cs @@ -1,5 +1,6 @@ using Speckle.DoubleNumerics; using Speckle.InterfaceGenerator; +using Speckle.Sdk.Dependencies; using Speckle.Sdk.Models; using Speckle.Sdk.Models.GraphTraversal; using Speckle.Sdk.Models.Instances; @@ -14,15 +15,15 @@ namespace Speckle.Connectors.Common.Instances; [GenerateAutoInterface] public class LocalToGlobalUnpacker : ILocalToGlobalUnpacker { - public List Unpack( - List? instanceDefinitionProxies, - List objectsToUnpack + public IReadOnlyCollection Unpack( + IReadOnlyCollection? instanceDefinitionProxies, + IReadOnlyCollection objectsToUnpack ) { - var localToGlobalMaps = new List(); + var localToGlobalMaps = new HashSet(); - var instanceProxies = new List<(TraversalContext tc, InstanceProxy obj)>(); - var atomicObjects = new List<(TraversalContext tc, Base obj)>(); + var instanceProxies = new HashSet<(TraversalContext tc, InstanceProxy obj)>(); + var atomicObjects = new HashSet<(TraversalContext tc, Base obj)>(); // 1. Split up the instances from the non-instances foreach (TraversalContext objectToUnpack in objectsToUnpack) @@ -37,8 +38,8 @@ public class LocalToGlobalUnpacker : ILocalToGlobalUnpacker } } - var objectsAtAbsolute = new List<(TraversalContext tc, Base obj)>(); - var objectsAtRelative = new List<(TraversalContext tc, Base obj)>(); + var objectsAtAbsolute = new HashSet<(TraversalContext tc, Base obj)>(); + var objectsAtRelative = new HashSet<(TraversalContext tc, Base obj)>(); // 2. Split atomic objects that in absolute or relative coordinates. foreach ((TraversalContext tc, Base atomicObject) in atomicObjects) @@ -61,7 +62,7 @@ public class LocalToGlobalUnpacker : ILocalToGlobalUnpacker // 3. Add atomic objects that on absolute coordinates that doesn't need a transformation. foreach ((TraversalContext tc, Base objectAtAbsolute) in objectsAtAbsolute) { - localToGlobalMaps.Add(new LocalToGlobalMap(tc, objectAtAbsolute, new List())); + localToGlobalMaps.Add(new LocalToGlobalMap(tc, objectAtAbsolute, new HashSet())); } // 4. Return if no logic around instancing. @@ -78,28 +79,28 @@ public class LocalToGlobalUnpacker : ILocalToGlobalUnpacker instanceProxies, objectAtRelative, objectAtRelative, - new List(), + new HashSet(), localToGlobalMaps ); } - return localToGlobalMaps.Where(ltgm => ltgm.AtomicObject is not InstanceProxy).ToList(); + return localToGlobalMaps.Where(ltgm => ltgm.AtomicObject is not InstanceProxy).Freeze(); } private void UnpackMatrix( - List instanceDefinitionProxies, - List<(TraversalContext tc, InstanceProxy instanceProxy)> instanceProxies, + IReadOnlyCollection instanceDefinitionProxies, + HashSet<(TraversalContext tc, InstanceProxy instanceProxy)> instanceProxies, (TraversalContext tc, Base obj) objectAtRelative, (TraversalContext tc, Base obj) searchForDefinition, - List matrices, - List localToGlobalMaps + HashSet matrices, + HashSet localToGlobalMaps ) { if (searchForDefinition.obj.applicationId is null) { return; } - InstanceDefinitionProxy? definitionProxy = instanceDefinitionProxies.Find(idp => + InstanceDefinitionProxy? definitionProxy = instanceDefinitionProxies.FirstOrDefault(idp => idp.objects.Contains(searchForDefinition.obj.applicationId) ); if (definitionProxy is null) @@ -116,7 +117,7 @@ public class LocalToGlobalUnpacker : ILocalToGlobalUnpacker var instances = instanceProxies.Where(ic => ic.instanceProxy.definitionId == definitionProxy.applicationId); foreach (var instance in instances) { - List newMatrices = [.. matrices, instance.instanceProxy.transform]; // Do not mutate the list! + HashSet newMatrices = [.. matrices, instance.instanceProxy.transform]; // Do not mutate the list! UnpackMatrix( instanceDefinitionProxies, instanceProxies, diff --git a/Sdk/Speckle.Connectors.Common/Operations/AccountService.cs b/Sdk/Speckle.Connectors.Common/Operations/AccountService.cs index 665d24c2d..6e1986143 100644 --- a/Sdk/Speckle.Connectors.Common/Operations/AccountService.cs +++ b/Sdk/Speckle.Connectors.Common/Operations/AccountService.cs @@ -1,4 +1,5 @@ -using Speckle.Sdk.Credentials; +using Speckle.InterfaceGenerator; +using Speckle.Sdk.Credentials; namespace Speckle.Connectors.Common.Operations; @@ -7,7 +8,8 @@ namespace Speckle.Connectors.Common.Operations; /// Note: Be sure it is registered on refactorings. Otherwise, we won't be able to do any send/receive ops. /// This can safely be registered as singleton. /// -public class AccountService(IAccountManager accountManager) +[GenerateAutoInterface] +public class AccountService(IAccountManager accountManager) : IAccountService { /// /// Account to retrieve with its id, if not exist try to retrieve from matching serverUrl. diff --git a/Sdk/Speckle.Connectors.Common/Operations/ISyncToThread.cs b/Sdk/Speckle.Connectors.Common/Operations/ISyncToThread.cs deleted file mode 100644 index 2440e642f..000000000 --- a/Sdk/Speckle.Connectors.Common/Operations/ISyncToThread.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Speckle.Connectors.Common.Operations; - -public interface ISyncToThread -{ - public Task RunOnThread(Func> func); -} diff --git a/Sdk/Speckle.Connectors.Common/Operations/PassthroughProgress.cs b/Sdk/Speckle.Connectors.Common/Operations/PassthroughProgress.cs index 265c9554d..9c639e996 100644 --- a/Sdk/Speckle.Connectors.Common/Operations/PassthroughProgress.cs +++ b/Sdk/Speckle.Connectors.Common/Operations/PassthroughProgress.cs @@ -2,6 +2,7 @@ namespace Speckle.Connectors.Common.Operations; +//this aggregates speed across all SDK uploads and passes it to the main thread public class PassthroughProgress : IProgress { private readonly Action _progressCallback; @@ -18,7 +19,7 @@ public class PassthroughProgress : IProgress public void Report(ProgressArgs value) { - if (value.ProgressEvent == ProgressEvent.DownloadBytes) + if (value.ProgressEvent == ProgressEvent.DownloadBytes || value.ProgressEvent == ProgressEvent.UploadBytes) { long totalBytes; lock (_totals) diff --git a/Sdk/Speckle.Connectors.Common/Operations/ProxyKeys.cs b/Sdk/Speckle.Connectors.Common/Operations/ProxyKeys.cs index a0abff694..d82612751 100644 --- a/Sdk/Speckle.Connectors.Common/Operations/ProxyKeys.cs +++ b/Sdk/Speckle.Connectors.Common/Operations/ProxyKeys.cs @@ -8,4 +8,6 @@ public static class ProxyKeys public const string GROUP = "groupProxies"; public const string PARAMETER_DEFINITIONS = "parameterDefinitions"; public const string PROPERTYSET_DEFINITIONS = "propertySetDefinitions"; + public const string MATERIAL = "materialProxies"; + public const string SECTION = "sectionProxies"; } diff --git a/Sdk/Speckle.Connectors.Common/Operations/Receive/RootObjectUnpacker.cs b/Sdk/Speckle.Connectors.Common/Operations/Receive/RootObjectUnpacker.cs index 870d5f392..be23d2f42 100644 --- a/Sdk/Speckle.Connectors.Common/Operations/Receive/RootObjectUnpacker.cs +++ b/Sdk/Speckle.Connectors.Common/Operations/Receive/RootObjectUnpacker.cs @@ -1,4 +1,5 @@ using Speckle.Objects.Other; +using Speckle.Sdk.Dependencies; using Speckle.Sdk.Models; using Speckle.Sdk.Models.Collections; using Speckle.Sdk.Models.GraphTraversal; @@ -28,26 +29,28 @@ public class RootObjectUnpacker TryGetColorProxies(root) ); - public IEnumerable GetObjectsToConvert(Base root) => - _traverseFunction.Traverse(root).Where(obj => obj.Current is not Collection); + public IReadOnlyCollection GetObjectsToConvert(Base root) => + _traverseFunction.Traverse(root).Where(obj => obj.Current is not Collection).Freeze(); - public List? TryGetColorProxies(Base root) => TryGetProxies(root, ProxyKeys.COLOR); + public IReadOnlyCollection? TryGetColorProxies(Base root) => + TryGetProxies(root, ProxyKeys.COLOR); - public List? TryGetRenderMaterialProxies(Base root) => + public IReadOnlyCollection? TryGetRenderMaterialProxies(Base root) => TryGetProxies(root, ProxyKeys.RENDER_MATERIAL); - public List? TryGetInstanceDefinitionProxies(Base root) => + public IReadOnlyCollection? TryGetInstanceDefinitionProxies(Base root) => TryGetProxies(root, ProxyKeys.INSTANCE_DEFINITION); - public List? TryGetGroupProxies(Base root) => TryGetProxies(root, ProxyKeys.GROUP); + public IReadOnlyCollection? TryGetGroupProxies(Base root) => + TryGetProxies(root, ProxyKeys.GROUP); public ( - List atomicObjects, - List instanceComponents + IReadOnlyCollection atomicObjects, + IReadOnlyCollection instanceComponents ) SplitAtomicObjectsAndInstances(IEnumerable objectsToSplit) { - List atomicObjects = new(); - List instanceComponents = new(); + HashSet atomicObjects = new(); + HashSet instanceComponents = new(); foreach (TraversalContext tc in objectsToSplit) { if (tc.Current is IInstanceComponent) @@ -59,8 +62,9 @@ public class RootObjectUnpacker atomicObjects.Add(tc); } } - return (atomicObjects, instanceComponents); + return (atomicObjects.Freeze(), instanceComponents.Freeze()); } - private List? TryGetProxies(Base root, string key) => (root[key] as List)?.Cast().ToList(); + private IReadOnlyCollection? TryGetProxies(Base root, string key) => + (root[key] as List)?.Cast().ToList(); } diff --git a/Sdk/Speckle.Connectors.Common/Operations/Receive/RootObjectUnpackerResult.cs b/Sdk/Speckle.Connectors.Common/Operations/Receive/RootObjectUnpackerResult.cs index 283bb2826..48a860f7f 100644 --- a/Sdk/Speckle.Connectors.Common/Operations/Receive/RootObjectUnpackerResult.cs +++ b/Sdk/Speckle.Connectors.Common/Operations/Receive/RootObjectUnpackerResult.cs @@ -6,9 +6,9 @@ using Speckle.Sdk.Models.Proxies; namespace Speckle.Connectors.Common.Operations.Receive; public record RootObjectUnpackerResult( - IEnumerable ObjectsToConvert, - List? DefinitionProxies, - List? GroupProxies, - List? RenderMaterialProxies, - List? ColorProxies + IReadOnlyCollection ObjectsToConvert, + IReadOnlyCollection? DefinitionProxies, + IReadOnlyCollection? GroupProxies, + IReadOnlyCollection? RenderMaterialProxies, + IReadOnlyCollection? ColorProxies ); diff --git a/Sdk/Speckle.Connectors.Common/Operations/Receive/TraversalContextUnpacker.cs b/Sdk/Speckle.Connectors.Common/Operations/Receive/TraversalContextUnpacker.cs index a60a5d985..aa29c0afb 100644 --- a/Sdk/Speckle.Connectors.Common/Operations/Receive/TraversalContextUnpacker.cs +++ b/Sdk/Speckle.Connectors.Common/Operations/Receive/TraversalContextUnpacker.cs @@ -1,4 +1,4 @@ -using Speckle.Sdk.Models; +using Speckle.Sdk.Models; using Speckle.Sdk.Models.Collections; using Speckle.Sdk.Models.GraphTraversal; using Speckle.Sdk.Models.Instances; @@ -10,11 +10,11 @@ namespace Speckle.Connectors.Common.Operations.Receive; /// public class TraversalContextUnpacker { - public List<(Collection[] path, Base current)> GetAtomicObjectsWithPath( + public IReadOnlyCollection<(Collection[] path, Base current)> GetAtomicObjectsWithPath( IEnumerable atomicObjects ) => atomicObjects.Select(o => (GetCollectionPath(o), o.Current)).ToList(); - public List<(Collection[] path, IInstanceComponent instance)> GetInstanceComponentsWithPath( + public ICollection<(Collection[] path, IInstanceComponent instance)> GetInstanceComponentsWithPath( IEnumerable instanceComponents ) => instanceComponents.Select(o => (GetCollectionPath(o), (o.Current as IInstanceComponent)!)).ToList(); diff --git a/Sdk/Speckle.Connectors.Common/Operations/ReceiveOperation.cs b/Sdk/Speckle.Connectors.Common/Operations/ReceiveOperation.cs index 84e6fee41..fe7a3cd03 100644 --- a/Sdk/Speckle.Connectors.Common/Operations/ReceiveOperation.cs +++ b/Sdk/Speckle.Connectors.Common/Operations/ReceiveOperation.cs @@ -1,105 +1,47 @@ using Speckle.Connectors.Common.Builders; +using Speckle.Connectors.Common.Threading; using Speckle.Connectors.Logging; using Speckle.Sdk.Api; using Speckle.Sdk.Credentials; using Speckle.Sdk.Logging; using Speckle.Sdk.Models; using Speckle.Sdk.Models.Extensions; -using Speckle.Sdk.Transports; namespace Speckle.Connectors.Common.Operations; -public sealed class ReceiveOperation +public sealed class ReceiveOperation( + IHostObjectBuilder hostObjectBuilder, + AccountService accountService, + IReceiveProgress receiveProgress, + ISdkActivityFactory activityFactory, + IOperations operations, + IClientFactory clientFactory, + IThreadContext threadContext +) { - private readonly IHostObjectBuilder _hostObjectBuilder; - private readonly AccountService _accountService; - private readonly IServerTransportFactory _serverTransportFactory; - private readonly IProgressDisplayManager _progressDisplayManager; - private readonly ISdkActivityFactory _activityFactory; - private readonly IOperations _operations; - private readonly IClientFactory _clientFactory; - - public ReceiveOperation( - IHostObjectBuilder hostObjectBuilder, - AccountService accountService, - IServerTransportFactory serverTransportFactory, - IProgressDisplayManager progressDisplayManager, - ISdkActivityFactory activityFactory, - IOperations operations, - IClientFactory clientFactory - ) - { - _hostObjectBuilder = hostObjectBuilder; - _accountService = accountService; - _serverTransportFactory = serverTransportFactory; - _progressDisplayManager = progressDisplayManager; - _activityFactory = activityFactory; - _operations = operations; - _clientFactory = clientFactory; - } - public async Task Execute( ReceiveInfo receiveInfo, IProgress onOperationProgressed, CancellationToken cancellationToken ) { - using var execute = _activityFactory.Start("Receive Operation"); + using var execute = activityFactory.Start("Receive Operation"); + cancellationToken.ThrowIfCancellationRequested(); execute?.SetTag("receiveInfo", receiveInfo); // 2 - Check account exist - Account account = _accountService.GetAccountWithServerUrlFallback(receiveInfo.AccountId, receiveInfo.ServerUrl); - using Client apiClient = _clientFactory.Create(account); + Account account = accountService.GetAccountWithServerUrlFallback(receiveInfo.AccountId, receiveInfo.ServerUrl); + using Client apiClient = clientFactory.Create(account); using var userScope = ActivityScope.SetTag(Consts.USER_ID, account.GetHashedEmail()); - var version = await apiClient - .Version.Get(receiveInfo.SelectedVersionId, receiveInfo.ProjectId, cancellationToken) - .ConfigureAwait(false); - - using var transport = _serverTransportFactory.Create(account, receiveInfo.ProjectId); - - double? previousPercentage = null; - _progressDisplayManager.Begin(); - Base? commitObject = await _operations - .Receive2( - new Uri(account.serverInfo.url), - receiveInfo.ProjectId, - version.referencedObject, - account.token, - onProgressAction: new PassthroughProgress(args => - { - if (args.ProgressEvent == ProgressEvent.CacheCheck || args.ProgressEvent == ProgressEvent.DownloadBytes) - { - switch (args.ProgressEvent) - { - case ProgressEvent.CacheCheck: - previousPercentage = _progressDisplayManager.CalculatePercentage(args); - break; - } - } - if (!_progressDisplayManager.ShouldUpdate()) - { - return; - } - - switch (args.ProgressEvent) - { - case ProgressEvent.CacheCheck: - case ProgressEvent.DownloadBytes: - onOperationProgressed.Report(new("Checking and Downloading... ", previousPercentage)); - break; - case ProgressEvent.DeserializeObject: - onOperationProgressed.Report(new("Deserializing ...", _progressDisplayManager.CalculatePercentage(args))); - break; - } - }), - cancellationToken: cancellationToken - ) - .ConfigureAwait(false); + var version = await apiClient.Version.Get(receiveInfo.SelectedVersionId, receiveInfo.ProjectId, cancellationToken); cancellationToken.ThrowIfCancellationRequested(); + var commitObject = await threadContext.RunOnWorkerAsync( + () => ReceiveData(account, version, receiveInfo, onOperationProgressed, cancellationToken) + ); // 4 - Convert objects - HostObjectBuilderResult? res = await ConvertObjects( + HostObjectBuilderResult res = await ConvertObjects( commitObject, receiveInfo, onOperationProgressed, @@ -107,13 +49,37 @@ public sealed class ReceiveOperation ) .ConfigureAwait(false); - await apiClient - .Version.Received(new(version.id, receiveInfo.ProjectId, receiveInfo.SourceApplication), cancellationToken) - .ConfigureAwait(false); + cancellationToken.ThrowIfCancellationRequested(); + await apiClient.Version.Received( + new(version.id, receiveInfo.ProjectId, receiveInfo.SourceApplication), + cancellationToken + ); return res; } + private async Task ReceiveData( + Account account, + Speckle.Sdk.Api.GraphQL.Models.Version version, + ReceiveInfo receiveInfo, + IProgress onOperationProgressed, + CancellationToken cancellationToken + ) + { + receiveProgress.Begin(); + Base commitObject = await operations.Receive2( + new Uri(account.serverInfo.url), + receiveInfo.ProjectId, + version.referencedObject, + account.token, + onProgressAction: new PassthroughProgress(args => receiveProgress.Report(onOperationProgressed, args)), + cancellationToken: cancellationToken + ); + + cancellationToken.ThrowIfCancellationRequested(); + return commitObject; + } + private async Task ConvertObjects( Base commitObject, ReceiveInfo receiveInfo, @@ -121,7 +87,7 @@ public sealed class ReceiveOperation CancellationToken cancellationToken ) { - using var conversionActivity = _activityFactory.Start("ReceiveOperation.ConvertObjects"); + using var conversionActivity = activityFactory.Start("ReceiveOperation.ConvertObjects"); conversionActivity?.SetTag("smellsLikeV2Data", commitObject.SmellsLikeV2Data()); conversionActivity?.SetTag("receiveInfo.serverUrl", receiveInfo.ServerUrl); conversionActivity?.SetTag("receiveInfo.projectId", receiveInfo.ProjectId); @@ -131,7 +97,7 @@ public sealed class ReceiveOperation try { - HostObjectBuilderResult res = await _hostObjectBuilder + HostObjectBuilderResult res = await hostObjectBuilder .Build(commitObject, receiveInfo.ProjectName, receiveInfo.ModelName, onOperationProgressed, cancellationToken) .ConfigureAwait(false); conversionActivity?.SetStatus(SdkActivityStatusCode.Ok); diff --git a/Sdk/Speckle.Connectors.Common/Operations/ReceiveProgress.cs b/Sdk/Speckle.Connectors.Common/Operations/ReceiveProgress.cs new file mode 100644 index 000000000..3e0cf800d --- /dev/null +++ b/Sdk/Speckle.Connectors.Common/Operations/ReceiveProgress.cs @@ -0,0 +1,54 @@ +using Speckle.InterfaceGenerator; +using Speckle.Sdk.Transports; + +namespace Speckle.Connectors.Common.Operations; + +[GenerateAutoInterface] +public sealed class ReceiveProgress(IProgressDisplayManager progressDisplayManager) : IReceiveProgress +{ + private double? _previousPercentage; + private string? _downloadSpeed; + private double? _downloadPercentage; + + public void Begin() => progressDisplayManager.Begin(); + + public void Report(IProgress onOperationProgressed, ProgressArgs args) + { + switch (args.ProgressEvent) + { + case ProgressEvent.CacheCheck: + _previousPercentage = progressDisplayManager.CalculatePercentage(args); + break; + case ProgressEvent.DownloadBytes: + _downloadSpeed = progressDisplayManager.CalculateSpeed(args); + break; + case ProgressEvent.DownloadObjects: + _downloadPercentage = progressDisplayManager.CalculatePercentage(args); + break; + } + + if (!progressDisplayManager.ShouldUpdate()) + { + return; + } + + switch (args.ProgressEvent) + { + case ProgressEvent.CacheCheck: + onOperationProgressed.Report(new("Checking cache... ", _previousPercentage)); + break; + case ProgressEvent.DownloadBytes: + case ProgressEvent.DownloadObjects: + onOperationProgressed.Report(new($"Downloading... ({_downloadSpeed})", _downloadPercentage)); + break; + case ProgressEvent.DeserializeObject: + onOperationProgressed.Report( + new( + $"Deserializing ... ({args.Count} / {args.Total} objects)", + progressDisplayManager.CalculatePercentage(args) + ) + ); + break; + } + } +} diff --git a/Sdk/Speckle.Connectors.Common/Operations/RootObjectSender.cs b/Sdk/Speckle.Connectors.Common/Operations/RootObjectSender.cs deleted file mode 100644 index bf896b6b7..000000000 --- a/Sdk/Speckle.Connectors.Common/Operations/RootObjectSender.cs +++ /dev/null @@ -1,135 +0,0 @@ -using Speckle.Connectors.Common.Caching; -using Speckle.Connectors.Logging; -using Speckle.InterfaceGenerator; -using Speckle.Sdk.Api; -using Speckle.Sdk.Api.GraphQL.Inputs; -using Speckle.Sdk.Credentials; -using Speckle.Sdk.Logging; -using Speckle.Sdk.Models; -using Speckle.Sdk.Serialisation.V2.Send; -using Speckle.Sdk.Transports; - -namespace Speckle.Connectors.Common.Operations; - -/// -/// Default implementation of the which takes a and sends -/// it to a server described by the parameters in the method -/// -/// POC: we have a generic RootObjectSender but we're not using it everywhere. It also appears to need some specialisation or at least -/// a way to get the application name, so RevitContext is being used in the revit version but we could probably inject that as a IHostAppContext maybe? -[GenerateAutoInterface] -public sealed class RootObjectSender : IRootObjectSender -{ - private readonly ISendConversionCache _sendConversionCache; - private readonly AccountService _accountService; - private readonly IProgressDisplayManager _progressDisplayManager; - private readonly IOperations _operations; - private readonly IClientFactory _clientFactory; - private readonly ISdkActivityFactory _activityFactory; - - public RootObjectSender( - ISendConversionCache sendConversionCache, - AccountService accountService, - IProgressDisplayManager progressDisplayManager, - IOperations operations, - IClientFactory clientFactory, - ISdkActivityFactory activityFactory - ) - { - _sendConversionCache = sendConversionCache; - _accountService = accountService; - _progressDisplayManager = progressDisplayManager; - _operations = operations; - _clientFactory = clientFactory; - _activityFactory = activityFactory; - } - - /// - /// Contract for the send operation that handles an assembled object. - /// In production, this will send to a server. - /// In testing, this could send to a sqlite db or just save to a dictionary. - /// - public async Task Send( - Base commitObject, - SendInfo sendInfo, - IProgress onOperationProgressed, - CancellationToken ct = default - ) - { - ct.ThrowIfCancellationRequested(); - - onOperationProgressed.Report(new("Uploading...", null)); - - Account account = _accountService.GetAccountWithServerUrlFallback(sendInfo.AccountId, sendInfo.ServerUrl); - using var userScope = ActivityScope.SetTag(Consts.USER_ID, account.GetHashedEmail()); - using var activity = _activityFactory.Start("SendOperation"); - - string previousSpeed = string.Empty; - _progressDisplayManager.Begin(); - var sendResult = await _operations - .Send2( - sendInfo.ServerUrl, - sendInfo.ProjectId, - account.token, - commitObject, - onProgressAction: new PassthroughProgress(args => - { - if (args.ProgressEvent == ProgressEvent.UploadBytes) - { - switch (args.ProgressEvent) - { - case ProgressEvent.UploadBytes: - previousSpeed = _progressDisplayManager.CalculateSpeed(args); - break; - } - } - if (!_progressDisplayManager.ShouldUpdate()) - { - return; - } - - switch (args.ProgressEvent) - { - case ProgressEvent.CachedToLocal: - onOperationProgressed.Report(new($"Caching... ({args.Count})", null)); - break; - case ProgressEvent.UploadBytes: - onOperationProgressed.Report(new($"Uploading... ({previousSpeed}) {args.Count}", null)); - break; - case ProgressEvent.FromCacheOrSerialized: - onOperationProgressed.Report( - new( - $"Loading cache and Serializing... ({_progressDisplayManager.CalculateSpeed(args)})", - _progressDisplayManager.CalculatePercentage(args) - ) - ); - break; - } - }), - ct - ) - .ConfigureAwait(false); - - _sendConversionCache.StoreSendResult(sendInfo.ProjectId, sendResult.ConvertedReferences); - - ct.ThrowIfCancellationRequested(); - - onOperationProgressed.Report(new("Linking version to model...", null)); - - // 8 - Create the version (commit) - using var apiClient = _clientFactory.Create(account); - _ = await apiClient - .Version.Create( - new CreateVersionInput( - sendResult.RootId, - sendInfo.ModelId, - sendInfo.ProjectId, - sourceApplication: sendInfo.SourceApplication - ), - ct - ) - .ConfigureAwait(true); - - return sendResult; - } -} diff --git a/Sdk/Speckle.Connectors.Common/Operations/SendOperation.cs b/Sdk/Speckle.Connectors.Common/Operations/SendOperation.cs index 665e1910c..d1baf6fae 100644 --- a/Sdk/Speckle.Connectors.Common/Operations/SendOperation.cs +++ b/Sdk/Speckle.Connectors.Common/Operations/SendOperation.cs @@ -1,21 +1,28 @@ using Speckle.Connectors.Common.Builders; +using Speckle.Connectors.Common.Caching; using Speckle.Connectors.Common.Conversion; +using Speckle.Connectors.Common.Threading; +using Speckle.Connectors.Logging; +using Speckle.Sdk.Api; +using Speckle.Sdk.Credentials; +using Speckle.Sdk.Logging; using Speckle.Sdk.Models; using Speckle.Sdk.Serialisation; +using Speckle.Sdk.Serialisation.V2.Send; namespace Speckle.Connectors.Common.Operations; -public sealed class SendOperation +public sealed class SendOperation( + IRootObjectBuilder rootObjectBuilder, + ISendConversionCache sendConversionCache, + IAccountService accountService, + ISendProgress sendProgress, + IOperations operations, + ISendOperationVersionRecorder sendOperationVersionRecorder, + ISdkActivityFactory activityFactory, + IThreadContext threadContext +) { - private readonly IRootObjectBuilder _rootObjectBuilder; - private readonly IRootObjectSender _baseObjectSender; - - public SendOperation(IRootObjectBuilder rootObjectBuilder, IRootObjectSender baseObjectSender) - { - _rootObjectBuilder = rootObjectBuilder; - _baseObjectSender = baseObjectSender; - } - public async Task Execute( IReadOnlyList objects, SendInfo sendInfo, @@ -23,22 +30,60 @@ public sealed class SendOperation CancellationToken ct = default ) { - var buildResult = await _rootObjectBuilder - .Build(objects, sendInfo, onOperationProgressed, ct) - .ConfigureAwait(false); + ct.ThrowIfCancellationRequested(); + var buildResult = await rootObjectBuilder.Build(objects, sendInfo, onOperationProgressed, ct); + ct.ThrowIfCancellationRequested(); // POC: Jonathon asks on behalf of willow twin - let's explore how this can work // buildResult.RootObject["@report"] = new Report { ConversionResults = buildResult.ConversionResults }; buildResult.RootObject["version"] = 3; // base object handler is separated, so we can do some testing on non-production databases // exact interface may want to be tweaked when we implement this - var (rootObjId, convertedReferences) = await _baseObjectSender - .Send(buildResult.RootObject, sendInfo, onOperationProgressed, ct) - .ConfigureAwait(false); + var (rootObjId, convertedReferences) = await threadContext.RunOnWorkerAsync( + () => Send(buildResult.RootObject, sendInfo, onOperationProgressed, ct) + ); + ct.ThrowIfCancellationRequested(); return new(rootObjId, convertedReferences, buildResult.ConversionResults); } + + public async Task Send( + Base commitObject, + SendInfo sendInfo, + IProgress onOperationProgressed, + CancellationToken ct = default + ) + { + ct.ThrowIfCancellationRequested(); + + onOperationProgressed.Report(new("Uploading...", null)); + + Account account = accountService.GetAccountWithServerUrlFallback(sendInfo.AccountId, sendInfo.ServerUrl); + using var userScope = ActivityScope.SetTag(Consts.USER_ID, account.GetHashedEmail()); + using var activity = activityFactory.Start("SendOperation"); + + sendProgress.Begin(); + var sendResult = await operations.Send2( + sendInfo.ServerUrl, + sendInfo.ProjectId, + account.token, + commitObject, + onProgressAction: new PassthroughProgress(args => sendProgress.Report(onOperationProgressed, args)), + ct + ); + + sendConversionCache.StoreSendResult(sendInfo.ProjectId, sendResult.ConvertedReferences); + + ct.ThrowIfCancellationRequested(); + + onOperationProgressed.Report(new("Linking version to model...", null)); + + // 8 - Create the version (commit) + await sendOperationVersionRecorder.RecordVersion(sendResult.RootId, sendInfo, account, ct); + + return sendResult; + } } public record SendOperationResult( diff --git a/Sdk/Speckle.Connectors.Common/Operations/SendOperationVersionRecorder.cs b/Sdk/Speckle.Connectors.Common/Operations/SendOperationVersionRecorder.cs new file mode 100644 index 000000000..946ad9f31 --- /dev/null +++ b/Sdk/Speckle.Connectors.Common/Operations/SendOperationVersionRecorder.cs @@ -0,0 +1,27 @@ +using Speckle.InterfaceGenerator; +using Speckle.Sdk.Api; +using Speckle.Sdk.Api.GraphQL.Inputs; +using Speckle.Sdk.Credentials; + +namespace Speckle.Connectors.Common.Operations; + +[GenerateAutoInterface] +//this is unnecessary if IClientFactory.Create returned an interface +public class SendOperationVersionRecorder(IClientFactory clientFactory) : ISendOperationVersionRecorder +{ + public async Task RecordVersion(string rootId, SendInfo sendInfo, Account account, CancellationToken ct) + { + using var apiClient = clientFactory.Create(account); + _ = await apiClient + .Version.Create( + new CreateVersionInput( + rootId, + sendInfo.ModelId, + sendInfo.ProjectId, + sourceApplication: sendInfo.SourceApplication + ), + ct + ) + .ConfigureAwait(true); + } +} diff --git a/Sdk/Speckle.Connectors.Common/Operations/SendProgress.cs b/Sdk/Speckle.Connectors.Common/Operations/SendProgress.cs new file mode 100644 index 000000000..018db248e --- /dev/null +++ b/Sdk/Speckle.Connectors.Common/Operations/SendProgress.cs @@ -0,0 +1,65 @@ +using Speckle.InterfaceGenerator; +using Speckle.Sdk.Transports; + +namespace Speckle.Connectors.Common.Operations; + +[GenerateAutoInterface] +public class SendProgress(IProgressDisplayManager progressDisplayManager) : ISendProgress +{ + private string? _previousSpeed; + private bool _serializeIsDone; + private long _serialized; + private long _total; + + public void Begin() => progressDisplayManager.Begin(); + + public void Report(IProgress onOperationProgressed, ProgressArgs args) + { + if (args.ProgressEvent == ProgressEvent.FromCacheOrSerialized) + { + _serialized = args.Count; + _serializeIsDone = args.Count >= args.Total; + } + else if (args.ProgressEvent == ProgressEvent.FindingChildren) + { + _total = args.Count; + } + else if (args.ProgressEvent == ProgressEvent.UploadBytes) + { + switch (args.ProgressEvent) + { + case ProgressEvent.UploadBytes: + _previousSpeed = progressDisplayManager.CalculateSpeed(args); + break; + } + } + if (!progressDisplayManager.ShouldUpdate()) + { + return; + } + + switch (args.ProgressEvent) + { + case ProgressEvent.CachedToLocal: + if (!_serializeIsDone) + { + return; + } + onOperationProgressed.Report( + new($"Caching... ({args.Count} objects)", progressDisplayManager.CalculatePercentage(args)) + ); + break; + case ProgressEvent.UploadBytes: + if (!_serializeIsDone) + { + return; + } + onOperationProgressed.Report(new($"Uploading... ({_previousSpeed})", null)); + break; + case ProgressEvent.FromCacheOrSerialized: + var message = $"Serializing... ({_serialized} / {_total} found objects)"; + onOperationProgressed.Report(new(message, progressDisplayManager.CalculatePercentage(args))); + break; + } + } +} diff --git a/Sdk/Speckle.Connectors.Common/Speckle.Connectors.Common.csproj b/Sdk/Speckle.Connectors.Common/Speckle.Connectors.Common.csproj index e521cbc3d..77d58c6e5 100644 --- a/Sdk/Speckle.Connectors.Common/Speckle.Connectors.Common.csproj +++ b/Sdk/Speckle.Connectors.Common/Speckle.Connectors.Common.csproj @@ -1,7 +1,7 @@ - netstandard2.0 + net48;net6.0;net8.0 Debug;Release;Local diff --git a/Sdk/Speckle.Connectors.Common/Threading/DefaultThreadContext.cs b/Sdk/Speckle.Connectors.Common/Threading/DefaultThreadContext.cs new file mode 100644 index 000000000..92a6dc8a3 --- /dev/null +++ b/Sdk/Speckle.Connectors.Common/Threading/DefaultThreadContext.cs @@ -0,0 +1,39 @@ +using System.Diagnostics.CodeAnalysis; + +namespace Speckle.Connectors.Common.Threading; + +[ExcludeFromCodeCoverage] +public class DefaultThreadContext : ThreadContext +{ + //should be always newed up on the host app's main thread + private readonly TaskScheduler _uiTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext(); + + protected override Task WorkerToMainAsync(Func> action) + { + var t = Task.Factory.StartNew(action, default, TaskCreationOptions.AttachedToParent, _uiTaskScheduler); + return t.Unwrap(); + } + + protected override Task MainToWorkerAsync(Func> action) + { + Task> f = Task.Factory.StartNew( + action, + default, + TaskCreationOptions.AttachedToParent, + TaskScheduler.Default + ); + return f.Unwrap(); + } + + protected override Task WorkerToMain(Func action) + { + var t = Task.Factory.StartNew(action, default, TaskCreationOptions.AttachedToParent, _uiTaskScheduler); + return t; + } + + protected override Task MainToWorker(Func action) + { + Task f = Task.Factory.StartNew(action, default, TaskCreationOptions.LongRunning, TaskScheduler.Default); + return f; + } +} diff --git a/Sdk/Speckle.Connectors.Common/Threading/TaskExtensions.cs b/Sdk/Speckle.Connectors.Common/Threading/TaskExtensions.cs new file mode 100644 index 000000000..3ebc31b8c --- /dev/null +++ b/Sdk/Speckle.Connectors.Common/Threading/TaskExtensions.cs @@ -0,0 +1,8 @@ +namespace Speckle.Connectors.Common.Threading; + +public static class TaskExtensions +{ +#pragma warning disable CA1030 + public static async void FireAndForget(this Task valueTask) => await valueTask; +#pragma warning restore CA1030 +} diff --git a/Sdk/Speckle.Connectors.Common/Threading/ThreadContext.cs b/Sdk/Speckle.Connectors.Common/Threading/ThreadContext.cs new file mode 100644 index 000000000..644568f67 --- /dev/null +++ b/Sdk/Speckle.Connectors.Common/Threading/ThreadContext.cs @@ -0,0 +1,145 @@ +using Speckle.InterfaceGenerator; + +namespace Speckle.Connectors.Common.Threading; + +[GenerateAutoInterface] +public abstract class ThreadContext : IThreadContext +{ + private static readonly Task s_empty = Task.FromResult(null); + public virtual bool IsMainThread => Environment.CurrentManagedThreadId == 1; + + public async Task RunOnThread(Action action, bool useMain) + { + if (useMain) + { + if (IsMainThread) + { + RunMain(action); + } + else + { + await WorkerToMain(() => + { + action(); + return s_empty; + }); + } + } + else + { + if (IsMainThread) + { + await MainToWorker(() => + { + action(); + return s_empty; + }); + } + else + { + RunWorker(action); + } + } + } + + public Task RunOnThread(Func action, bool useMain) + { + if (useMain) + { + if (IsMainThread) + { + return RunMainAsync(action); + } + + return WorkerToMain(action); + } + if (IsMainThread) + { + return MainToWorker(action); + } + + return RunWorkerAsync(action); + } + + public async Task RunOnThreadAsync(Func action, bool useMain) + { + if (useMain) + { + if (IsMainThread) + { + await RunMainAsync(action); + } + else + { + await WorkerToMainAsync(async () => + { + await action(); + return s_empty; + }); + } + } + else + { + if (IsMainThread) + { + await MainToWorkerAsync(async () => + { + await action(); + return s_empty; + }); + } + else + { + if (useMain) + { + await RunMainAsync(action); + } + else + { + await RunWorkerAsync(action); + } + } + } + } + + public Task RunOnThreadAsync(Func> action, bool useMain) + { + if (useMain) + { + if (IsMainThread) + { + return RunMainAsync(action); + } + + return WorkerToMainAsync(action); + } + if (IsMainThread) + { + return MainToWorkerAsync(action); + } + return RunWorkerAsync(action); + } + + protected abstract Task WorkerToMainAsync(Func> action); + + protected abstract Task MainToWorkerAsync(Func> action); + protected abstract Task WorkerToMain(Func action); + + protected abstract Task MainToWorker(Func action); + + protected virtual void RunMain(Action action) => action(); + + protected virtual void RunWorker(Action action) => action(); + + protected virtual Task RunMainAsync(Func action) => Task.FromResult(action()); + + protected virtual Task RunWorkerAsync(Func action) => Task.FromResult(action()); + + protected virtual Task RunMainAsync(Func action) => Task.FromResult(action()); + + protected virtual Task RunWorkerAsync(Func action) => Task.FromResult(action()); + + protected virtual Task RunMainAsync(Func> action) => action(); + + protected virtual Task RunWorkerAsync(Func> action) => action(); +} diff --git a/Sdk/Speckle.Connectors.Common/Threading/ThreadContextExtensions.cs b/Sdk/Speckle.Connectors.Common/Threading/ThreadContextExtensions.cs new file mode 100644 index 000000000..d0cb56029 --- /dev/null +++ b/Sdk/Speckle.Connectors.Common/Threading/ThreadContextExtensions.cs @@ -0,0 +1,28 @@ +namespace Speckle.Connectors.Common.Threading; + +public static class ThreadContextExtensions +{ + public static Task RunOnMain(this IThreadContext threadContext, Action action) => + threadContext.RunOnThread(action, true); + + public static Task RunOnWorker(this IThreadContext threadContext, Action action) => + threadContext.RunOnThread(action, false); + + public static Task RunOnMain(this IThreadContext threadContext, Func action) => + threadContext.RunOnThread(action, true); + + public static Task RunOnWorker(this IThreadContext threadContext, Func action) => + threadContext.RunOnThread(action, false); + + public static Task RunOnMainAsync(this IThreadContext threadContext, Func action) => + threadContext.RunOnThreadAsync(action, true); + + public static Task RunOnWorkerAsync(this IThreadContext threadContext, Func action) => + threadContext.RunOnThreadAsync(action, false); + + public static Task RunOnMainAsync(this IThreadContext threadContext, Func> action) => + threadContext.RunOnThreadAsync(action, true); + + public static Task RunOnWorkerAsync(this IThreadContext threadContext, Func> action) => + threadContext.RunOnThreadAsync(action, false); +} diff --git a/Sdk/Speckle.Connectors.Common/packages.lock.json b/Sdk/Speckle.Connectors.Common/packages.lock.json index 871b0e4fd..0218123f5 100644 --- a/Sdk/Speckle.Connectors.Common/packages.lock.json +++ b/Sdk/Speckle.Connectors.Common/packages.lock.json @@ -1,7 +1,7 @@ { "version": 2, "dependencies": { - ".NETStandard,Version=v2.0": { + ".NETFramework,Version=v4.8": { "Microsoft.Extensions.DependencyInjection": { "type": "Direct", "requested": "[2.2.0, )", @@ -17,7 +17,7 @@ "resolved": "1.0.3", "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", "dependencies": { - "Microsoft.NETFramework.ReferenceAssemblies.net461": "1.0.3" + "Microsoft.NETFramework.ReferenceAssemblies.net48": "1.0.3" } }, "Microsoft.SourceLink.GitHub": { @@ -30,15 +30,6 @@ "Microsoft.SourceLink.Common": "8.0.0" } }, - "NETStandard.Library": { - "type": "Direct", - "requested": "[2.0.3, )", - "resolved": "2.0.3", - "contentHash": "st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0" - } - }, "PolySharp": { "type": "Direct", "requested": "[1.14.1, )", @@ -53,18 +44,18 @@ }, "Speckle.Objects": { "type": "Direct", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "+m7jRFm0ABbkcSz2UphdxAsislY10IpQ1u79c8a3aVvegLjnsVQZ1sXfRIRO1aFdulkhjYKXYpB3N9M8Z+epgQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.191" + "Speckle.Sdk": "3.1.1" } }, "Speckle.Sdk": { "type": "Direct", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "VVT3LJiYlhqnggxxdeTt1QLrqfxDb044x0yX6kxS9b5MRzeDvK2Vz86pLDfuHs+SXvDimRVfYx1M42IW/aPcTQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.Bcl.AsyncInterfaces": "5.0.0", @@ -72,16 +63,16 @@ "Microsoft.Data.Sqlite": "7.0.5", "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", "Microsoft.Extensions.Logging": "2.2.0", - "Speckle.DoubleNumerics": "4.0.1", + "Speckle.DoubleNumerics": "4.1.0", "Speckle.Newtonsoft.Json": "13.0.2", - "Speckle.Sdk.Dependencies": "3.1.0-dev.191" + "Speckle.Sdk.Dependencies": "3.1.1" } }, "Speckle.Sdk.Dependencies": { "type": "Direct", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "EmEOyjsGsNi56Z/ZoBOn8WirTmIT2yqWvlUeUh0BSPX2TDMZXHTKOM/kHmP6HSd10KVFn2Zo/ItY7/K9iRtL1Q==" + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" }, "GraphQL.Client": { "type": "Transitive", @@ -90,6 +81,7 @@ "dependencies": { "GraphQL.Client.Abstractions": "6.0.0", "GraphQL.Client.Abstractions.Websocket": "6.0.0", + "System.Net.WebSockets.Client.Managed": "1.0.22", "System.Reactive": "5.0.0" } }, @@ -192,15 +184,317 @@ "System.Runtime.CompilerServices.Unsafe": "4.5.1" } }, - "Microsoft.NETCore.Platforms": { + "Microsoft.NETFramework.ReferenceAssemblies.net48": { "type": "Transitive", - "resolved": "1.1.0", - "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==" + "resolved": "1.0.3", + "contentHash": "zMk4D+9zyiEWByyQ7oPImPN/Jhpj166Ky0Nlla4eXlNL8hI/BtSJsgR8Inldd4NNpIAH3oh8yym0W2DrhXdSLQ==" }, - "Microsoft.NETCore.Targets": { + "Microsoft.SourceLink.Common": { "type": "Transitive", - "resolved": "1.1.0", - "contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==" + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "Speckle.Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.2", + "contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA==" + }, + "SQLitePCLRaw.bundle_e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "EWI1olKDjFEBMJu0+3wuxwziIAdWDVMYLhuZ3Qs84rrz+DHwD00RzWPZCa+bLnHCf3oJwuFZIRsHT5p236QXww==", + "dependencies": { + "SQLitePCLRaw.lib.e_sqlite3": "2.1.4", + "SQLitePCLRaw.provider.dynamic_cdecl": "2.1.4" + } + }, + "SQLitePCLRaw.core": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "inBjvSHo9UDKneGNzfUfDjK08JzlcIhn1+SP5Y3m6cgXpCxXKCJDy6Mka7LpgSV+UZmKSnC8rTwB0SQ0xKu5pA==", + "dependencies": { + "System.Memory": "4.5.3" + } + }, + "SQLitePCLRaw.lib.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "2C9Q9eX7CPLveJA0rIhf9RXAvu+7nWZu1A2MdG6SD/NOu26TakGgL1nsbc0JAspGijFOo3HoN79xrx8a368fBg==" + }, + "SQLitePCLRaw.provider.dynamic_cdecl": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "ZsaKKhgYF9B1fvcnOGKl3EycNAwd9CRWX7v0rEfuPWhQQ5Jjpvf2VEHahiLIGHio3hxi3EIKFJw9KvyowWOUAw==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "System.Buffers": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "AwarXzzoDwX6BgrhjoJsk6tUezZEozOT5Y9QKF94Gl4JK91I4PIIBkBco9068Y9/Dra8Dkbie99kXB8+1BaYKw==" + }, + "System.ComponentModel.Annotations": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "UxYQ3FGUOtzJ7LfSdnYSFd7+oEv6M8NgUatatIN2HxNtDdlcvFAf+VIq4Of9cDMJEJC0aSRv/x898RYhB4Yppg==" + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.3", + "contentHash": "3oDzvc/zzetpTKWMShs1AADwZjQ/36HnsufHRPcOjyRAAMLDlu2iD33MBI2opxnezcVUtXyqDXXjoFMOU9c7SA==", + "dependencies": { + "System.Buffers": "4.4.0", + "System.Numerics.Vectors": "4.4.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.2" + } + }, + "System.Net.WebSockets.Client.Managed": { + "type": "Transitive", + "resolved": "1.0.22", + "contentHash": "WqEOxPlXjuZrIjUtXNE9NxEfU/n5E35iV2PtoZdJSUC4tlrqwHnTee+wvMIM4OUaJWmwrymeqcgYrE0IkGAgLA==", + "dependencies": { + "System.Buffers": "4.4.0", + "System.Numerics.Vectors": "4.4.0" + } + }, + "System.Numerics.Vectors": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ==" + }, + "System.Reactive": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "4.5.3", + "contentHash": "3TIsJhD1EiiT0w2CcDMN/iSSwnNnsrnbzeVHSKkaEgV85txMprmuO+Yq2AdSbeVGcg28pdNDTPK87tJhX7VFHw==" + }, + "System.Threading.Tasks.Extensions": { + "type": "Transitive", + "resolved": "4.5.4", + "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + }, + "speckle.connectors.logging": { + "type": "Project" + }, + "Microsoft.Extensions.Logging": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "Nxqhadc9FCmFHzU+fz3oc8sFlE6IadViYg8dfUdGzJZ2JUxnCsRghBhhOWdM4B2zSZqEc+0BjliBh/oNdRZuig==", + "dependencies": { + "Microsoft.Extensions.Configuration.Binder": "2.2.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging.Abstractions": "2.2.0", + "Microsoft.Extensions.Options": "2.2.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" + }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + } + }, + "net6.0": { + "Microsoft.Extensions.DependencyInjection": { + "type": "Direct", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "MZtBIwfDFork5vfjpJdG5g8wuJFt7d/y3LOSVVtDK/76wlbtz6cjltfKHqLx2TKVqTj5/c41t77m1+h20zqtPA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0" + } + }, + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net461": "1.0.3" + } + }, + "Microsoft.SourceLink.GitHub": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "dependencies": { + "Microsoft.Build.Tasks.Git": "8.0.0", + "Microsoft.SourceLink.Common": "8.0.0" + } + }, + "PolySharp": { + "type": "Direct", + "requested": "[1.14.1, )", + "resolved": "1.14.1", + "contentHash": "mOOmFYwad3MIOL14VCjj02LljyF1GNw1wP0YVlxtcPvqdxjGGMNdNJJxHptlry3MOd8b40Flm8RPOM8JOlN2sQ==" + }, + "Speckle.InterfaceGenerator": { + "type": "Direct", + "requested": "[0.9.6, )", + "resolved": "0.9.6", + "contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w==" + }, + "Speckle.Objects": { + "type": "Direct", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", + "dependencies": { + "Speckle.Sdk": "3.1.1" + } + }, + "Speckle.Sdk": { + "type": "Direct", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", + "dependencies": { + "GraphQL.Client": "6.0.0", + "Microsoft.Bcl.AsyncInterfaces": "5.0.0", + "Microsoft.CSharp": "4.7.0", + "Microsoft.Data.Sqlite": "7.0.5", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging": "2.2.0", + "Speckle.DoubleNumerics": "4.1.0", + "Speckle.Newtonsoft.Json": "13.0.2", + "Speckle.Sdk.Dependencies": "3.1.1" + } + }, + "Speckle.Sdk.Dependencies": { + "type": "Direct", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" + }, + "GraphQL.Client": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "8yPNBbuVBpTptivyAlak4GZvbwbUcjeQTL4vN1HKHRuOykZ4r7l5fcLS6vpyPyLn0x8FsL31xbOIKyxbmR9rbA==", + "dependencies": { + "GraphQL.Client.Abstractions": "6.0.0", + "GraphQL.Client.Abstractions.Websocket": "6.0.0", + "System.Reactive": "5.0.0" + } + }, + "GraphQL.Client.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "h7uzWFORHZ+CCjwr/ThAyXMr0DPpzEANDa4Uo54wqCQ+j7qUKwqYTgOrb1W40sqbvNaZm9v/X7It31SUw0maHA==", + "dependencies": { + "GraphQL.Primitives": "6.0.0" + } + }, + "GraphQL.Client.Abstractions.Websocket": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "Nr9bPf8gIOvLuXpqEpqr9z9jslYFJOvd0feHth3/kPqeR3uMbjF5pjiwh4jxyMcxHdr8Pb6QiXkV3hsSyt0v7A==", + "dependencies": { + "GraphQL.Client.Abstractions": "6.0.0" + } + }, + "GraphQL.Primitives": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "yg72rrYDapfsIUrul7aF6wwNnTJBOFvuA9VdDTQpPa8AlAriHbufeXYLBcodKjfUdkCnaiggX1U/nEP08Zb5GA==" + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "W8DPQjkMScOMTtJbPwmPyj9c3zYSFGawDW3jwlBOOsnY+EzZFLgNQ/UMkK35JmkNOVPdCyPr2Tw7Vv9N+KA3ZQ==" + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.CSharp": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==" + }, + "Microsoft.Data.Sqlite": { + "type": "Transitive", + "resolved": "7.0.5", + "contentHash": "KGxbPeWsQMnmQy43DSBxAFtHz3l2JX8EWBSGUCvT3CuZ8KsuzbkqMIJMDOxWtG8eZSoCDI04aiVQjWuuV8HmSw==", + "dependencies": { + "Microsoft.Data.Sqlite.Core": "7.0.5", + "SQLitePCLRaw.bundle_e_sqlite3": "2.1.4" + } + }, + "Microsoft.Data.Sqlite.Core": { + "type": "Transitive", + "resolved": "7.0.5", + "contentHash": "FTerRmQPqHrCrnoUzhBu+E+1DNGwyrAMLqHkAqOOOu5pGfyMOj8qQUBxI/gDtWtG11p49UxSfWmBzRNlwZqfUg==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "nOP8R1mVb/6mZtm2qgAJXn/LFm/2kMjHDAg/QJLFG6CuWYJtaD3p1BwQhufBVvRzL9ceJ/xF0SQ0qsI2GkDQAA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "65MrmXCziWaQFrI0UHkQbesrX5wTwf9XPjY5yFm/VkgJKFJ5gqvXRoXjIZcf2wLi5ZlwGz/oMYfyURVCWbM5iw==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "vJ9xvOZCnUAIHcGC3SU35r3HKmHTVIeHzo6u/qzlHAqD8m6xv92MLin4oJntTvkpKxVX3vI1GFFkIQtU3AdlsQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "2.2.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw==" + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "UpZLNLBpIZ0GTebShui7xXYh6DmBHjWM8NxGxZbdQh/bPZ5e6YswqI+bru6BnEL5eWiOdodsXtEz3FROcgi/qg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Primitives": "2.2.0", + "System.ComponentModel.Annotations": "4.5.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "azyQtqbm4fSaDzZHD/J+V6oWMFaf2tWP4WEGIYePLCMw3+b2RQdj9ybgbQyjCshcitQKQ4lEDOZjmSlTTrHxUg==", + "dependencies": { + "System.Memory": "4.5.1", + "System.Runtime.CompilerServices.Unsafe": "4.5.1" + } }, "Microsoft.NETFramework.ReferenceAssemblies.net461": { "type": "Transitive", @@ -212,11 +506,6 @@ "resolved": "8.0.0", "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" }, - "Speckle.DoubleNumerics": { - "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w==" - }, "Speckle.Newtonsoft.Json": { "type": "Transitive", "resolved": "13.0.2", @@ -252,11 +541,6 @@ "SQLitePCLRaw.core": "2.1.4" } }, - "System.Buffers": { - "type": "Transitive", - "resolved": "4.4.0", - "contentHash": "AwarXzzoDwX6BgrhjoJsk6tUezZEozOT5Y9QKF94Gl4JK91I4PIIBkBco9068Y9/Dra8Dkbie99kXB8+1BaYKw==" - }, "System.ComponentModel.Annotations": { "type": "Transitive", "resolved": "4.5.0", @@ -265,56 +549,17 @@ "System.Memory": { "type": "Transitive", "resolved": "4.5.3", - "contentHash": "3oDzvc/zzetpTKWMShs1AADwZjQ/36HnsufHRPcOjyRAAMLDlu2iD33MBI2opxnezcVUtXyqDXXjoFMOU9c7SA==", - "dependencies": { - "System.Buffers": "4.4.0", - "System.Numerics.Vectors": "4.4.0", - "System.Runtime.CompilerServices.Unsafe": "4.5.2" - } - }, - "System.Numerics.Vectors": { - "type": "Transitive", - "resolved": "4.4.0", - "contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ==" + "contentHash": "3oDzvc/zzetpTKWMShs1AADwZjQ/36HnsufHRPcOjyRAAMLDlu2iD33MBI2opxnezcVUtXyqDXXjoFMOU9c7SA==" }, "System.Reactive": { "type": "Transitive", "resolved": "5.0.0", - "contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==", - "dependencies": { - "System.Runtime.InteropServices.WindowsRuntime": "4.3.0", - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, - "System.Runtime": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - } + "contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==" }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", - "resolved": "4.5.3", - "contentHash": "3TIsJhD1EiiT0w2CcDMN/iSSwnNnsrnbzeVHSKkaEgV85txMprmuO+Yq2AdSbeVGcg28pdNDTPK87tJhX7VFHw==" - }, - "System.Runtime.InteropServices.WindowsRuntime": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "J4GUi3xZQLUBasNwZnjrffN8i5wpHrBtZoLG+OhRyGo/+YunMRWWtwoMDlUAIdmX0uRfpHIBDSV6zyr3yf00TA==", - "dependencies": { - "System.Runtime": "4.3.0" - } - }, - "System.Threading.Tasks.Extensions": { - "type": "Transitive", - "resolved": "4.5.4", - "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "4.5.3" - } + "resolved": "4.5.1", + "contentHash": "Zh8t8oqolRaFa9vmOZfdQm/qKejdqz0J9kr7o2Fu0vPeoH3BL1EOXipKWwkWtLT1JPzjByrF19fGuFlNbmPpiw==" }, "speckle.connectors.logging": { "type": "Project" @@ -342,6 +587,283 @@ "requested": "[2.2.0, )", "resolved": "2.2.0", "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" + }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + } + }, + "net8.0": { + "Microsoft.Extensions.DependencyInjection": { + "type": "Direct", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "MZtBIwfDFork5vfjpJdG5g8wuJFt7d/y3LOSVVtDK/76wlbtz6cjltfKHqLx2TKVqTj5/c41t77m1+h20zqtPA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0" + } + }, + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net461": "1.0.3" + } + }, + "Microsoft.SourceLink.GitHub": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "dependencies": { + "Microsoft.Build.Tasks.Git": "8.0.0", + "Microsoft.SourceLink.Common": "8.0.0" + } + }, + "PolySharp": { + "type": "Direct", + "requested": "[1.14.1, )", + "resolved": "1.14.1", + "contentHash": "mOOmFYwad3MIOL14VCjj02LljyF1GNw1wP0YVlxtcPvqdxjGGMNdNJJxHptlry3MOd8b40Flm8RPOM8JOlN2sQ==" + }, + "Speckle.InterfaceGenerator": { + "type": "Direct", + "requested": "[0.9.6, )", + "resolved": "0.9.6", + "contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w==" + }, + "Speckle.Objects": { + "type": "Direct", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", + "dependencies": { + "Speckle.Sdk": "3.1.1" + } + }, + "Speckle.Sdk": { + "type": "Direct", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", + "dependencies": { + "GraphQL.Client": "6.0.0", + "Microsoft.CSharp": "4.7.0", + "Microsoft.Data.Sqlite": "7.0.5", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging": "2.2.0", + "Speckle.DoubleNumerics": "4.1.0", + "Speckle.Newtonsoft.Json": "13.0.2", + "Speckle.Sdk.Dependencies": "3.1.1" + } + }, + "Speckle.Sdk.Dependencies": { + "type": "Direct", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" + }, + "GraphQL.Client": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "8yPNBbuVBpTptivyAlak4GZvbwbUcjeQTL4vN1HKHRuOykZ4r7l5fcLS6vpyPyLn0x8FsL31xbOIKyxbmR9rbA==", + "dependencies": { + "GraphQL.Client.Abstractions": "6.0.0", + "GraphQL.Client.Abstractions.Websocket": "6.0.0", + "System.Reactive": "5.0.0" + } + }, + "GraphQL.Client.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "h7uzWFORHZ+CCjwr/ThAyXMr0DPpzEANDa4Uo54wqCQ+j7qUKwqYTgOrb1W40sqbvNaZm9v/X7It31SUw0maHA==", + "dependencies": { + "GraphQL.Primitives": "6.0.0" + } + }, + "GraphQL.Client.Abstractions.Websocket": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "Nr9bPf8gIOvLuXpqEpqr9z9jslYFJOvd0feHth3/kPqeR3uMbjF5pjiwh4jxyMcxHdr8Pb6QiXkV3hsSyt0v7A==", + "dependencies": { + "GraphQL.Client.Abstractions": "6.0.0" + } + }, + "GraphQL.Primitives": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "yg72rrYDapfsIUrul7aF6wwNnTJBOFvuA9VdDTQpPa8AlAriHbufeXYLBcodKjfUdkCnaiggX1U/nEP08Zb5GA==" + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.CSharp": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==" + }, + "Microsoft.Data.Sqlite": { + "type": "Transitive", + "resolved": "7.0.5", + "contentHash": "KGxbPeWsQMnmQy43DSBxAFtHz3l2JX8EWBSGUCvT3CuZ8KsuzbkqMIJMDOxWtG8eZSoCDI04aiVQjWuuV8HmSw==", + "dependencies": { + "Microsoft.Data.Sqlite.Core": "7.0.5", + "SQLitePCLRaw.bundle_e_sqlite3": "2.1.4" + } + }, + "Microsoft.Data.Sqlite.Core": { + "type": "Transitive", + "resolved": "7.0.5", + "contentHash": "FTerRmQPqHrCrnoUzhBu+E+1DNGwyrAMLqHkAqOOOu5pGfyMOj8qQUBxI/gDtWtG11p49UxSfWmBzRNlwZqfUg==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "nOP8R1mVb/6mZtm2qgAJXn/LFm/2kMjHDAg/QJLFG6CuWYJtaD3p1BwQhufBVvRzL9ceJ/xF0SQ0qsI2GkDQAA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "65MrmXCziWaQFrI0UHkQbesrX5wTwf9XPjY5yFm/VkgJKFJ5gqvXRoXjIZcf2wLi5ZlwGz/oMYfyURVCWbM5iw==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "vJ9xvOZCnUAIHcGC3SU35r3HKmHTVIeHzo6u/qzlHAqD8m6xv92MLin4oJntTvkpKxVX3vI1GFFkIQtU3AdlsQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "2.2.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw==" + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "UpZLNLBpIZ0GTebShui7xXYh6DmBHjWM8NxGxZbdQh/bPZ5e6YswqI+bru6BnEL5eWiOdodsXtEz3FROcgi/qg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Primitives": "2.2.0", + "System.ComponentModel.Annotations": "4.5.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "azyQtqbm4fSaDzZHD/J+V6oWMFaf2tWP4WEGIYePLCMw3+b2RQdj9ybgbQyjCshcitQKQ4lEDOZjmSlTTrHxUg==", + "dependencies": { + "System.Memory": "4.5.1", + "System.Runtime.CompilerServices.Unsafe": "4.5.1" + } + }, + "Microsoft.NETFramework.ReferenceAssemblies.net461": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "AmOJZwCqnOCNp6PPcf9joyogScWLtwy0M1WkqfEQ0M9nYwyDD7EX9ZjscKS5iYnyvteX7kzSKFCKt9I9dXA6mA==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "Speckle.Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.2", + "contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA==" + }, + "SQLitePCLRaw.bundle_e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "EWI1olKDjFEBMJu0+3wuxwziIAdWDVMYLhuZ3Qs84rrz+DHwD00RzWPZCa+bLnHCf3oJwuFZIRsHT5p236QXww==", + "dependencies": { + "SQLitePCLRaw.lib.e_sqlite3": "2.1.4", + "SQLitePCLRaw.provider.e_sqlite3": "2.1.4" + } + }, + "SQLitePCLRaw.core": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "inBjvSHo9UDKneGNzfUfDjK08JzlcIhn1+SP5Y3m6cgXpCxXKCJDy6Mka7LpgSV+UZmKSnC8rTwB0SQ0xKu5pA==", + "dependencies": { + "System.Memory": "4.5.3" + } + }, + "SQLitePCLRaw.lib.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "2C9Q9eX7CPLveJA0rIhf9RXAvu+7nWZu1A2MdG6SD/NOu26TakGgL1nsbc0JAspGijFOo3HoN79xrx8a368fBg==" + }, + "SQLitePCLRaw.provider.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "CSlb5dUp1FMIkez9Iv5EXzpeq7rHryVNqwJMWnpq87j9zWZexaEMdisDktMsnnrzKM6ahNrsTkjqNodTBPBxtQ==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "System.ComponentModel.Annotations": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "UxYQ3FGUOtzJ7LfSdnYSFd7+oEv6M8NgUatatIN2HxNtDdlcvFAf+VIq4Of9cDMJEJC0aSRv/x898RYhB4Yppg==" + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.3", + "contentHash": "3oDzvc/zzetpTKWMShs1AADwZjQ/36HnsufHRPcOjyRAAMLDlu2iD33MBI2opxnezcVUtXyqDXXjoFMOU9c7SA==" + }, + "System.Reactive": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==" + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Zh8t8oqolRaFa9vmOZfdQm/qKejdqz0J9kr7o2Fu0vPeoH3BL1EOXipKWwkWtLT1JPzjByrF19fGuFlNbmPpiw==" + }, + "speckle.connectors.logging": { + "type": "Project" + }, + "Microsoft.Extensions.Logging": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "Nxqhadc9FCmFHzU+fz3oc8sFlE6IadViYg8dfUdGzJZ2JUxnCsRghBhhOWdM4B2zSZqEc+0BjliBh/oNdRZuig==", + "dependencies": { + "Microsoft.Extensions.Configuration.Binder": "2.2.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging.Abstractions": "2.2.0", + "Microsoft.Extensions.Options": "2.2.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" + }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" } } } diff --git a/Sdk/Speckle.Connectors.Logging/ActivityScopeExtensions.cs b/Sdk/Speckle.Connectors.Logging/ActivityScopeExtensions.cs index 2cb549fef..01b809a43 100644 --- a/Sdk/Speckle.Connectors.Logging/ActivityScopeExtensions.cs +++ b/Sdk/Speckle.Connectors.Logging/ActivityScopeExtensions.cs @@ -5,7 +5,7 @@ public static class ActivityScope private static readonly AsyncLocal> s_tags = new() { Value = new() }; public static IReadOnlyDictionary Tags => s_tags.Value ?? []; public static IReadOnlyList> TagsList { get; } = - new List>(s_tags.Value); + new List>(s_tags.Value ?? []); public static IDisposable SetTag(string key, string value) { @@ -16,6 +16,6 @@ public static class ActivityScope private sealed class TagScope(string key) : IDisposable { - public void Dispose() => s_tags.Value.Remove(key); + public void Dispose() => s_tags.Value?.Remove(key); } } diff --git a/Sdk/Speckle.Connectors.Logging/Consts.cs b/Sdk/Speckle.Connectors.Logging/Consts.cs index c8d5a24cc..e4ec80d53 100644 --- a/Sdk/Speckle.Connectors.Logging/Consts.cs +++ b/Sdk/Speckle.Connectors.Logging/Consts.cs @@ -32,6 +32,8 @@ public static class Consts } var indexOfPlusSign = informationalVersion.IndexOf('+'); +#pragma warning disable IDE0057 return indexOfPlusSign > 0 ? informationalVersion.Substring(0, indexOfPlusSign) : informationalVersion; +#pragma warning restore IDE0057 } } diff --git a/Sdk/Speckle.Connectors.Logging/Internal/LogBuilder.cs b/Sdk/Speckle.Connectors.Logging/Internal/LogBuilder.cs index cb7fd96fd..cc79ef1a4 100644 --- a/Sdk/Speckle.Connectors.Logging/Internal/LogBuilder.cs +++ b/Sdk/Speckle.Connectors.Logging/Internal/LogBuilder.cs @@ -78,7 +78,9 @@ internal static class LogBuilder x.AddOtlpExporter(y => { y.Protocol = OtlpExportProtocol.HttpProtobuf; - y.Endpoint = new Uri(speckleOtelLogging.Endpoint); + y.Endpoint = speckleOtelLogging.Endpoint is null + ? throw new InvalidOperationException("Need a logging endpoint") + : new Uri(speckleOtelLogging.Endpoint); var sb = new StringBuilder(); bool appendSemicolon = false; foreach (var kvp in speckleOtelLogging.Headers ?? []) diff --git a/Sdk/Speckle.Connectors.Logging/LoggingActivity.cs b/Sdk/Speckle.Connectors.Logging/LoggingActivity.cs index d8e6073bf..b3dacc691 100644 --- a/Sdk/Speckle.Connectors.Logging/LoggingActivity.cs +++ b/Sdk/Speckle.Connectors.Logging/LoggingActivity.cs @@ -1,5 +1,4 @@ using System.Diagnostics; -using OpenTelemetry.Trace; namespace Speckle.Connectors.Logging; @@ -16,7 +15,7 @@ public readonly struct LoggingActivity public void SetTag(string key, object? value) => _activity.SetTag(key, value); - public void RecordException(Exception e) => _activity.RecordException(e); + public void RecordException(Exception e) => _activity.AddException(e); public string TraceId => _activity.TraceId.ToString(); diff --git a/Sdk/Speckle.Connectors.Logging/Speckle.Connectors.Logging.csproj b/Sdk/Speckle.Connectors.Logging/Speckle.Connectors.Logging.csproj index c197c7503..5b0676a70 100644 --- a/Sdk/Speckle.Connectors.Logging/Speckle.Connectors.Logging.csproj +++ b/Sdk/Speckle.Connectors.Logging/Speckle.Connectors.Logging.csproj @@ -1,7 +1,7 @@  - netstandard2.0 + netstandard2.0;net8.0 Debug;Release;Local diff --git a/Sdk/Speckle.Connectors.Tests/ActivityScopeTests.cs b/Sdk/Speckle.Connectors.Tests/ActivityScopeTests.cs index 3305d84f4..e92d64788 100644 --- a/Sdk/Speckle.Connectors.Tests/ActivityScopeTests.cs +++ b/Sdk/Speckle.Connectors.Tests/ActivityScopeTests.cs @@ -9,7 +9,7 @@ public class ActivityScopeTests public async Task TestAsyncLocal() { Logging.ActivityScope.SetTag("test", "me"); - await Task.Delay(10).ConfigureAwait(false); + await Task.Delay(10); Logging.ActivityScope.Tags.ContainsKey("test").Should().BeTrue(); Logging.ActivityScope.Tags["test"].Should().Be("me"); } diff --git a/Sdk/Speckle.Connectors.Tests/packages.lock.json b/Sdk/Speckle.Connectors.Tests/packages.lock.json index f35a3d76d..e8cb18daa 100644 --- a/Sdk/Speckle.Connectors.Tests/packages.lock.json +++ b/Sdk/Speckle.Connectors.Tests/packages.lock.json @@ -231,11 +231,6 @@ "resolved": "13.0.1", "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" }, - "Speckle.DoubleNumerics": { - "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w==" - }, "Speckle.Newtonsoft.Json": { "type": "Transitive", "resolved": "13.0.2", @@ -319,9 +314,9 @@ "dependencies": { "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", "Speckle.Connectors.Logging": "[1.0.0, )", - "Speckle.Objects": "[3.1.0-dev.191, )", - "Speckle.Sdk": "[3.1.0-dev.191, )", - "Speckle.Sdk.Dependencies": "[3.1.0-dev.191, )" + "Speckle.Objects": "[3.1.1, )", + "Speckle.Sdk": "[3.1.1, )", + "Speckle.Sdk.Dependencies": "[3.1.1, )" } }, "speckle.connectors.logging": { @@ -367,36 +362,42 @@ "resolved": "2.2.0", "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "+m7jRFm0ABbkcSz2UphdxAsislY10IpQ1u79c8a3aVvegLjnsVQZ1sXfRIRO1aFdulkhjYKXYpB3N9M8Z+epgQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.191" + "Speckle.Sdk": "3.1.1" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "VVT3LJiYlhqnggxxdeTt1QLrqfxDb044x0yX6kxS9b5MRzeDvK2Vz86pLDfuHs+SXvDimRVfYx1M42IW/aPcTQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.CSharp": "4.7.0", "Microsoft.Data.Sqlite": "7.0.5", "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", "Microsoft.Extensions.Logging": "2.2.0", - "Speckle.DoubleNumerics": "4.0.1", + "Speckle.DoubleNumerics": "4.1.0", "Speckle.Newtonsoft.Json": "13.0.2", - "Speckle.Sdk.Dependencies": "3.1.0-dev.191" + "Speckle.Sdk.Dependencies": "3.1.1" } }, "Speckle.Sdk.Dependencies": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "EmEOyjsGsNi56Z/ZoBOn8WirTmIT2yqWvlUeUh0BSPX2TDMZXHTKOM/kHmP6HSd10KVFn2Zo/ItY7/K9iRtL1Q==" + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" } } } diff --git a/Sdk/Speckle.Converters.Common.Tests/ConverterManagerTests.cs b/Sdk/Speckle.Converters.Common.Tests/ConverterManagerTests.cs index fedef0ee7..6cda24beb 100644 --- a/Sdk/Speckle.Converters.Common.Tests/ConverterManagerTests.cs +++ b/Sdk/Speckle.Converters.Common.Tests/ConverterManagerTests.cs @@ -36,7 +36,7 @@ public class ConverterManagerTests [Test] public void Test_NoFallback() { - var sut = SetupManager("String", typeof(TestConverter)); + var sut = SetupManager("System.String", typeof(TestConverter)); var converter = sut.ResolveConverter(typeof(string), false); converter.Should().NotBeNull(); } @@ -44,7 +44,7 @@ public class ConverterManagerTests [Test] public void Test_Fallback() { - var sut = SetupManager("Object", typeof(TestConverter)); + var sut = SetupManager("System.Object", typeof(TestConverter)); var converter = sut.ResolveConverter(typeof(string), true); converter.Should().NotBeNull(); } diff --git a/Sdk/Speckle.Converters.Common.Tests/packages.lock.json b/Sdk/Speckle.Converters.Common.Tests/packages.lock.json index a1433754b..ec12f7122 100644 --- a/Sdk/Speckle.Converters.Common.Tests/packages.lock.json +++ b/Sdk/Speckle.Converters.Common.Tests/packages.lock.json @@ -240,11 +240,6 @@ "resolved": "13.0.1", "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" }, - "Speckle.DoubleNumerics": { - "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w==" - }, "Speckle.Newtonsoft.Json": { "type": "Transitive", "resolved": "13.0.2", @@ -327,7 +322,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.191, )" + "Speckle.Objects": "[3.1.1, )" } }, "speckle.testing": { @@ -361,36 +356,42 @@ "resolved": "2.2.0", "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "+m7jRFm0ABbkcSz2UphdxAsislY10IpQ1u79c8a3aVvegLjnsVQZ1sXfRIRO1aFdulkhjYKXYpB3N9M8Z+epgQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.191" + "Speckle.Sdk": "3.1.1" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "VVT3LJiYlhqnggxxdeTt1QLrqfxDb044x0yX6kxS9b5MRzeDvK2Vz86pLDfuHs+SXvDimRVfYx1M42IW/aPcTQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.CSharp": "4.7.0", "Microsoft.Data.Sqlite": "7.0.5", "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", "Microsoft.Extensions.Logging": "2.2.0", - "Speckle.DoubleNumerics": "4.0.1", + "Speckle.DoubleNumerics": "4.1.0", "Speckle.Newtonsoft.Json": "13.0.2", - "Speckle.Sdk.Dependencies": "3.1.0-dev.191" + "Speckle.Sdk.Dependencies": "3.1.1" } }, "Speckle.Sdk.Dependencies": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "EmEOyjsGsNi56Z/ZoBOn8WirTmIT2yqWvlUeUh0BSPX2TDMZXHTKOM/kHmP6HSd10KVFn2Zo/ItY7/K9iRtL1Q==" + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" } } } diff --git a/Sdk/Speckle.Converters.Common/LocalToGlobalConverterUtils.cs b/Sdk/Speckle.Converters.Common/LocalToGlobalConverterUtils.cs index d3788fc68..bed59a604 100644 --- a/Sdk/Speckle.Converters.Common/LocalToGlobalConverterUtils.cs +++ b/Sdk/Speckle.Converters.Common/LocalToGlobalConverterUtils.cs @@ -20,7 +20,7 @@ public class LocalToGlobalConverterUtils } // POC: This could move to converters instead handling all cases like this. - public Base TransformObjects(Base atomicObject, List matrix) + public Base TransformObjects(Base atomicObject, IReadOnlyCollection matrix) { if (matrix.Count == 0) { @@ -33,10 +33,13 @@ public class LocalToGlobalConverterUtils if (atomicObject is ITransformable c) { + //TODO TransformTo will be deprecated as it's dangerous and requires ID transposing which is wrong! + //ID needs to be copied to the new instance + string id = atomicObject.id.NotNull(); foreach (var transform in transforms) { c.TransformTo(transform, out ITransformable newObj); - c = newObj; + c = newObj; // we need to keep the reference to the new object, as we're going to use it in the cache } if (c is not Base) @@ -46,8 +49,7 @@ public class LocalToGlobalConverterUtils ); } - string id = atomicObject.id; - atomicObject = (Base)c; + atomicObject = (Base)c; // restore the id, as it's used in the cache atomicObject.id = id; // .TransformTo only transfers typed properties, we need to add back the dynamic ones: diff --git a/Sdk/Speckle.Converters.Common/NameAndRankValueAttribute.cs b/Sdk/Speckle.Converters.Common/NameAndRankValueAttribute.cs index 54f380eeb..cea30ccac 100644 --- a/Sdk/Speckle.Converters.Common/NameAndRankValueAttribute.cs +++ b/Sdk/Speckle.Converters.Common/NameAndRankValueAttribute.cs @@ -2,17 +2,11 @@ // POC: maybe better to put in utils/reflection [AttributeUsage(AttributeTargets.Class)] -public sealed class NameAndRankValueAttribute : Attribute +public sealed class NameAndRankValueAttribute(Type type, int rank) : Attribute { // DO NOT CHANGE! This is the base, lowest rank for a conversion public const int SPECKLE_DEFAULT_RANK = 0; - public string Name { get; private set; } - public int Rank { get; private set; } - - public NameAndRankValueAttribute(string name, int rank) - { - Name = name; - Rank = rank; - } + public Type Type { get; private set; } = type; + public int Rank { get; private set; } = rank; } diff --git a/Sdk/Speckle.Converters.Common/Registration/ConverterManager.cs b/Sdk/Speckle.Converters.Common/Registration/ConverterManager.cs index 49a1558fd..ad90785ec 100644 --- a/Sdk/Speckle.Converters.Common/Registration/ConverterManager.cs +++ b/Sdk/Speckle.Converters.Common/Registration/ConverterManager.cs @@ -1,5 +1,6 @@ using System.Collections.Concurrent; using Microsoft.Extensions.DependencyInjection; +using Speckle.Sdk.Common; using Speckle.Sdk.Common.Exceptions; namespace Speckle.Converters.Common.Registration; @@ -14,7 +15,7 @@ public class ConverterManager(ConcurrentDictionary converterTyp var currentType = type; while (true) { - var typeName = currentType.Name; + var typeName = currentType.FullName.NotNull(); var converter = GetConverterByType(typeName); if (converter is null && recursive) { @@ -37,9 +38,9 @@ public class ConverterManager(ConcurrentDictionary converterTyp } } - private T? GetConverterByType(string typeName) + private T? GetConverterByType(string fullName) { - if (converterTypes.TryGetValue(typeName, out var converter)) + if (converterTypes.TryGetValue(fullName, out var converter)) { return (T)ActivatorUtilities.CreateInstance(serviceProvider, converter); } diff --git a/Sdk/Speckle.Converters.Common/Registration/ServiceRegistration.cs b/Sdk/Speckle.Converters.Common/Registration/ServiceRegistration.cs index 9a8aba66e..254bb2004 100644 --- a/Sdk/Speckle.Converters.Common/Registration/ServiceRegistration.cs +++ b/Sdk/Speckle.Converters.Common/Registration/ServiceRegistration.cs @@ -3,6 +3,7 @@ using System.Reflection; using Microsoft.Extensions.DependencyInjection; using Speckle.Converters.Common.Objects; using Speckle.Converters.Common.ToHost; +using Speckle.Sdk.Common; namespace Speckle.Converters.Common.Registration; @@ -43,41 +44,41 @@ public static class ServiceRegistration public static void AddConverters(this IServiceCollection serviceCollection, Assembly converterAssembly) { ConcurrentDictionary converterTypes = new(); - var types = converterAssembly.ExportedTypes.Where(x => x.GetInterfaces().Contains(typeof(T))); + var exportedTypes = converterAssembly.ExportedTypes.Where(x => x.GetInterfaces().Contains(typeof(T))); // we only care about named types - var byName = types + var byName = exportedTypes .Where(x => x.GetCustomAttribute() != null) .Select(x => { - var nameAndRank = x.GetCustomAttribute(); + var nameAndRank = x.GetCustomAttribute().NotNull(); - return (name: nameAndRank.Name, rank: nameAndRank.Rank, type: x); + return (Type: nameAndRank.Type, Rank: nameAndRank.Rank, Converter: x); }) .ToList(); // we'll register the types accordingly - var names = byName.Select(x => x.name).Distinct(); - foreach (string name in names) + var types = byName.Select(x => x.Type).Distinct(); + foreach (Type type in types) { - var namedTypes = byName.Where(x => x.name == name).OrderByDescending(y => y.rank).ToList(); + var namedTypes = byName.Where(x => x.Type == type).OrderByDescending(y => y.Rank).ToList(); // first type found var first = namedTypes[0]; // POC: may need to be instance per lifecycle scope - converterTypes.TryAdd(first.name, first.type); + converterTypes.TryAdd(first.Type.FullName.NotNull(), first.Converter); // POC: not sure yet if... // * This should be an array of types // * Whether the scope should be modified or modifiable // * Whether this is in the write project... hmmm // POC: IsAssignableFrom() - var secondaryType = first.type.GetInterface(typeof(ITypedConverter<,>).Name); + var secondaryType = first.Type.GetInterface(typeof(ITypedConverter<,>).Name); // POC: should we explode if no found? if (secondaryType != null) { - converterTypes.TryAdd(first.name, secondaryType); + converterTypes.TryAdd(first.Type.FullName, secondaryType); } // register subsequent types with rank diff --git a/Sdk/Speckle.Converters.Common/Speckle.Converters.Common.csproj b/Sdk/Speckle.Converters.Common/Speckle.Converters.Common.csproj index d91cb9c1b..5a56a836d 100644 --- a/Sdk/Speckle.Converters.Common/Speckle.Converters.Common.csproj +++ b/Sdk/Speckle.Converters.Common/Speckle.Converters.Common.csproj @@ -1,7 +1,7 @@ - netstandard2.0 + net48;net6.0;net8.0 Debug;Release;Local diff --git a/Sdk/Speckle.Converters.Common/ToHost/ConverterWithFallback.cs b/Sdk/Speckle.Converters.Common/ToHost/ConverterWithFallback.cs index 8e304e6c0..35feb7750 100644 --- a/Sdk/Speckle.Converters.Common/ToHost/ConverterWithFallback.cs +++ b/Sdk/Speckle.Converters.Common/ToHost/ConverterWithFallback.cs @@ -49,9 +49,12 @@ public sealed class ConverterWithFallback : IRootToHostConverter } catch (ConversionNotSupportedException e) { + // NOTE: `ConversionNotSupportedException` should not be an exception - we know what we can convert and what we can't; this is doubly true + // on hot paths where we *know* that we will fallback to a display value anyway. _logger.LogInformation(e, "Attempt to find conversion for type {type} failed", type); } + // NOTE: this section becomes a bit obsolete now, as we're implementing direct converters for data objects. // Fallback to display value if it exists. var displayValue = target.TryGetDisplayValue(); @@ -74,7 +77,9 @@ public sealed class ConverterWithFallback : IRootToHostConverter // if the host app returns a list of objects as the result of the fallback conversion, we zip them together with the original base display value objects that generated them. if (conversionResult is IEnumerable result) { - return result.Zip(displayValue, (a, b) => (a, b)).ToList(); + // Further note: this madness can now go away, as the actual direct converter for the host app can control what + // is being returned. In rhino, this map is done in the actual data object converter. + return result.Zip(displayValue, (a, b) => (a, b)); } // if not, and the host app "merges" together somehow multiple display values into one entity, we return that. diff --git a/Sdk/Speckle.Converters.Common/packages.lock.json b/Sdk/Speckle.Converters.Common/packages.lock.json index f7ea974c0..59cfddb05 100644 --- a/Sdk/Speckle.Converters.Common/packages.lock.json +++ b/Sdk/Speckle.Converters.Common/packages.lock.json @@ -1,7 +1,7 @@ { "version": 2, "dependencies": { - ".NETStandard,Version=v2.0": { + ".NETFramework,Version=v4.8": { "Microsoft.Extensions.Logging.Abstractions": { "type": "Direct", "requested": "[2.2.0, )", @@ -14,7 +14,7 @@ "resolved": "1.0.3", "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", "dependencies": { - "Microsoft.NETFramework.ReferenceAssemblies.net461": "1.0.3" + "Microsoft.NETFramework.ReferenceAssemblies.net48": "1.0.3" } }, "Microsoft.SourceLink.GitHub": { @@ -27,15 +27,6 @@ "Microsoft.SourceLink.Common": "8.0.0" } }, - "NETStandard.Library": { - "type": "Direct", - "requested": "[2.0.3, )", - "resolved": "2.0.3", - "contentHash": "st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0" - } - }, "PolySharp": { "type": "Direct", "requested": "[1.14.1, )", @@ -50,11 +41,11 @@ }, "Speckle.Objects": { "type": "Direct", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "+m7jRFm0ABbkcSz2UphdxAsislY10IpQ1u79c8a3aVvegLjnsVQZ1sXfRIRO1aFdulkhjYKXYpB3N9M8Z+epgQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.191" + "Speckle.Sdk": "3.1.1" } }, "GraphQL.Client": { @@ -64,6 +55,7 @@ "dependencies": { "GraphQL.Client.Abstractions": "6.0.0", "GraphQL.Client.Abstractions.Websocket": "6.0.0", + "System.Net.WebSockets.Client.Managed": "1.0.22", "System.Reactive": "5.0.0" } }, @@ -166,15 +158,305 @@ "System.Runtime.CompilerServices.Unsafe": "4.5.1" } }, - "Microsoft.NETCore.Platforms": { + "Microsoft.NETFramework.ReferenceAssemblies.net48": { "type": "Transitive", - "resolved": "1.1.0", - "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==" + "resolved": "1.0.3", + "contentHash": "zMk4D+9zyiEWByyQ7oPImPN/Jhpj166Ky0Nlla4eXlNL8hI/BtSJsgR8Inldd4NNpIAH3oh8yym0W2DrhXdSLQ==" }, - "Microsoft.NETCore.Targets": { + "Microsoft.SourceLink.Common": { "type": "Transitive", - "resolved": "1.1.0", - "contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==" + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "Speckle.Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.2", + "contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA==" + }, + "SQLitePCLRaw.bundle_e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "EWI1olKDjFEBMJu0+3wuxwziIAdWDVMYLhuZ3Qs84rrz+DHwD00RzWPZCa+bLnHCf3oJwuFZIRsHT5p236QXww==", + "dependencies": { + "SQLitePCLRaw.lib.e_sqlite3": "2.1.4", + "SQLitePCLRaw.provider.dynamic_cdecl": "2.1.4" + } + }, + "SQLitePCLRaw.core": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "inBjvSHo9UDKneGNzfUfDjK08JzlcIhn1+SP5Y3m6cgXpCxXKCJDy6Mka7LpgSV+UZmKSnC8rTwB0SQ0xKu5pA==", + "dependencies": { + "System.Memory": "4.5.3" + } + }, + "SQLitePCLRaw.lib.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "2C9Q9eX7CPLveJA0rIhf9RXAvu+7nWZu1A2MdG6SD/NOu26TakGgL1nsbc0JAspGijFOo3HoN79xrx8a368fBg==" + }, + "SQLitePCLRaw.provider.dynamic_cdecl": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "ZsaKKhgYF9B1fvcnOGKl3EycNAwd9CRWX7v0rEfuPWhQQ5Jjpvf2VEHahiLIGHio3hxi3EIKFJw9KvyowWOUAw==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "System.Buffers": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "AwarXzzoDwX6BgrhjoJsk6tUezZEozOT5Y9QKF94Gl4JK91I4PIIBkBco9068Y9/Dra8Dkbie99kXB8+1BaYKw==" + }, + "System.ComponentModel.Annotations": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "UxYQ3FGUOtzJ7LfSdnYSFd7+oEv6M8NgUatatIN2HxNtDdlcvFAf+VIq4Of9cDMJEJC0aSRv/x898RYhB4Yppg==" + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.3", + "contentHash": "3oDzvc/zzetpTKWMShs1AADwZjQ/36HnsufHRPcOjyRAAMLDlu2iD33MBI2opxnezcVUtXyqDXXjoFMOU9c7SA==", + "dependencies": { + "System.Buffers": "4.4.0", + "System.Numerics.Vectors": "4.4.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.2" + } + }, + "System.Net.WebSockets.Client.Managed": { + "type": "Transitive", + "resolved": "1.0.22", + "contentHash": "WqEOxPlXjuZrIjUtXNE9NxEfU/n5E35iV2PtoZdJSUC4tlrqwHnTee+wvMIM4OUaJWmwrymeqcgYrE0IkGAgLA==", + "dependencies": { + "System.Buffers": "4.4.0", + "System.Numerics.Vectors": "4.4.0" + } + }, + "System.Numerics.Vectors": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ==" + }, + "System.Reactive": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "4.5.3", + "contentHash": "3TIsJhD1EiiT0w2CcDMN/iSSwnNnsrnbzeVHSKkaEgV85txMprmuO+Yq2AdSbeVGcg28pdNDTPK87tJhX7VFHw==" + }, + "System.Threading.Tasks.Extensions": { + "type": "Transitive", + "resolved": "4.5.4", + "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + }, + "Microsoft.Extensions.Logging": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "Nxqhadc9FCmFHzU+fz3oc8sFlE6IadViYg8dfUdGzJZ2JUxnCsRghBhhOWdM4B2zSZqEc+0BjliBh/oNdRZuig==", + "dependencies": { + "Microsoft.Extensions.Configuration.Binder": "2.2.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging.Abstractions": "2.2.0", + "Microsoft.Extensions.Options": "2.2.0" + } + }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, + "Speckle.Sdk": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", + "dependencies": { + "GraphQL.Client": "6.0.0", + "Microsoft.Bcl.AsyncInterfaces": "5.0.0", + "Microsoft.CSharp": "4.7.0", + "Microsoft.Data.Sqlite": "7.0.5", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging": "2.2.0", + "Speckle.DoubleNumerics": "4.1.0", + "Speckle.Newtonsoft.Json": "13.0.2", + "Speckle.Sdk.Dependencies": "3.1.1" + } + }, + "Speckle.Sdk.Dependencies": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" + } + }, + "net6.0": { + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Direct", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" + }, + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net461": "1.0.3" + } + }, + "Microsoft.SourceLink.GitHub": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "dependencies": { + "Microsoft.Build.Tasks.Git": "8.0.0", + "Microsoft.SourceLink.Common": "8.0.0" + } + }, + "PolySharp": { + "type": "Direct", + "requested": "[1.14.1, )", + "resolved": "1.14.1", + "contentHash": "mOOmFYwad3MIOL14VCjj02LljyF1GNw1wP0YVlxtcPvqdxjGGMNdNJJxHptlry3MOd8b40Flm8RPOM8JOlN2sQ==" + }, + "Speckle.InterfaceGenerator": { + "type": "Direct", + "requested": "[0.9.6, )", + "resolved": "0.9.6", + "contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w==" + }, + "Speckle.Objects": { + "type": "Direct", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", + "dependencies": { + "Speckle.Sdk": "3.1.1" + } + }, + "GraphQL.Client": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "8yPNBbuVBpTptivyAlak4GZvbwbUcjeQTL4vN1HKHRuOykZ4r7l5fcLS6vpyPyLn0x8FsL31xbOIKyxbmR9rbA==", + "dependencies": { + "GraphQL.Client.Abstractions": "6.0.0", + "GraphQL.Client.Abstractions.Websocket": "6.0.0", + "System.Reactive": "5.0.0" + } + }, + "GraphQL.Client.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "h7uzWFORHZ+CCjwr/ThAyXMr0DPpzEANDa4Uo54wqCQ+j7qUKwqYTgOrb1W40sqbvNaZm9v/X7It31SUw0maHA==", + "dependencies": { + "GraphQL.Primitives": "6.0.0" + } + }, + "GraphQL.Client.Abstractions.Websocket": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "Nr9bPf8gIOvLuXpqEpqr9z9jslYFJOvd0feHth3/kPqeR3uMbjF5pjiwh4jxyMcxHdr8Pb6QiXkV3hsSyt0v7A==", + "dependencies": { + "GraphQL.Client.Abstractions": "6.0.0" + } + }, + "GraphQL.Primitives": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "yg72rrYDapfsIUrul7aF6wwNnTJBOFvuA9VdDTQpPa8AlAriHbufeXYLBcodKjfUdkCnaiggX1U/nEP08Zb5GA==" + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "W8DPQjkMScOMTtJbPwmPyj9c3zYSFGawDW3jwlBOOsnY+EzZFLgNQ/UMkK35JmkNOVPdCyPr2Tw7Vv9N+KA3ZQ==" + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.CSharp": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==" + }, + "Microsoft.Data.Sqlite": { + "type": "Transitive", + "resolved": "7.0.5", + "contentHash": "KGxbPeWsQMnmQy43DSBxAFtHz3l2JX8EWBSGUCvT3CuZ8KsuzbkqMIJMDOxWtG8eZSoCDI04aiVQjWuuV8HmSw==", + "dependencies": { + "Microsoft.Data.Sqlite.Core": "7.0.5", + "SQLitePCLRaw.bundle_e_sqlite3": "2.1.4" + } + }, + "Microsoft.Data.Sqlite.Core": { + "type": "Transitive", + "resolved": "7.0.5", + "contentHash": "FTerRmQPqHrCrnoUzhBu+E+1DNGwyrAMLqHkAqOOOu5pGfyMOj8qQUBxI/gDtWtG11p49UxSfWmBzRNlwZqfUg==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "nOP8R1mVb/6mZtm2qgAJXn/LFm/2kMjHDAg/QJLFG6CuWYJtaD3p1BwQhufBVvRzL9ceJ/xF0SQ0qsI2GkDQAA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "65MrmXCziWaQFrI0UHkQbesrX5wTwf9XPjY5yFm/VkgJKFJ5gqvXRoXjIZcf2wLi5ZlwGz/oMYfyURVCWbM5iw==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "vJ9xvOZCnUAIHcGC3SU35r3HKmHTVIeHzo6u/qzlHAqD8m6xv92MLin4oJntTvkpKxVX3vI1GFFkIQtU3AdlsQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "2.2.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw==" + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "UpZLNLBpIZ0GTebShui7xXYh6DmBHjWM8NxGxZbdQh/bPZ5e6YswqI+bru6BnEL5eWiOdodsXtEz3FROcgi/qg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Primitives": "2.2.0", + "System.ComponentModel.Annotations": "4.5.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "azyQtqbm4fSaDzZHD/J+V6oWMFaf2tWP4WEGIYePLCMw3+b2RQdj9ybgbQyjCshcitQKQ4lEDOZjmSlTTrHxUg==", + "dependencies": { + "System.Memory": "4.5.1", + "System.Runtime.CompilerServices.Unsafe": "4.5.1" + } }, "Microsoft.NETFramework.ReferenceAssemblies.net461": { "type": "Transitive", @@ -186,11 +468,6 @@ "resolved": "8.0.0", "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" }, - "Speckle.DoubleNumerics": { - "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w==" - }, "Speckle.Newtonsoft.Json": { "type": "Transitive", "resolved": "13.0.2", @@ -226,11 +503,6 @@ "SQLitePCLRaw.core": "2.1.4" } }, - "System.Buffers": { - "type": "Transitive", - "resolved": "4.4.0", - "contentHash": "AwarXzzoDwX6BgrhjoJsk6tUezZEozOT5Y9QKF94Gl4JK91I4PIIBkBco9068Y9/Dra8Dkbie99kXB8+1BaYKw==" - }, "System.ComponentModel.Annotations": { "type": "Transitive", "resolved": "4.5.0", @@ -239,56 +511,17 @@ "System.Memory": { "type": "Transitive", "resolved": "4.5.3", - "contentHash": "3oDzvc/zzetpTKWMShs1AADwZjQ/36HnsufHRPcOjyRAAMLDlu2iD33MBI2opxnezcVUtXyqDXXjoFMOU9c7SA==", - "dependencies": { - "System.Buffers": "4.4.0", - "System.Numerics.Vectors": "4.4.0", - "System.Runtime.CompilerServices.Unsafe": "4.5.2" - } - }, - "System.Numerics.Vectors": { - "type": "Transitive", - "resolved": "4.4.0", - "contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ==" + "contentHash": "3oDzvc/zzetpTKWMShs1AADwZjQ/36HnsufHRPcOjyRAAMLDlu2iD33MBI2opxnezcVUtXyqDXXjoFMOU9c7SA==" }, "System.Reactive": { "type": "Transitive", "resolved": "5.0.0", - "contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==", - "dependencies": { - "System.Runtime.InteropServices.WindowsRuntime": "4.3.0", - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, - "System.Runtime": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - } + "contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==" }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", - "resolved": "4.5.3", - "contentHash": "3TIsJhD1EiiT0w2CcDMN/iSSwnNnsrnbzeVHSKkaEgV85txMprmuO+Yq2AdSbeVGcg28pdNDTPK87tJhX7VFHw==" - }, - "System.Runtime.InteropServices.WindowsRuntime": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "J4GUi3xZQLUBasNwZnjrffN8i5wpHrBtZoLG+OhRyGo/+YunMRWWtwoMDlUAIdmX0uRfpHIBDSV6zyr3yf00TA==", - "dependencies": { - "System.Runtime": "4.3.0" - } - }, - "System.Threading.Tasks.Extensions": { - "type": "Transitive", - "resolved": "4.5.4", - "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "4.5.3" - } + "resolved": "4.5.1", + "contentHash": "Zh8t8oqolRaFa9vmOZfdQm/qKejdqz0J9kr7o2Fu0vPeoH3BL1EOXipKWwkWtLT1JPzjByrF19fGuFlNbmPpiw==" }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "CentralTransitive", @@ -308,11 +541,17 @@ "Microsoft.Extensions.Options": "2.2.0" } }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "VVT3LJiYlhqnggxxdeTt1QLrqfxDb044x0yX6kxS9b5MRzeDvK2Vz86pLDfuHs+SXvDimRVfYx1M42IW/aPcTQ==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.Bcl.AsyncInterfaces": "5.0.0", @@ -320,16 +559,275 @@ "Microsoft.Data.Sqlite": "7.0.5", "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", "Microsoft.Extensions.Logging": "2.2.0", - "Speckle.DoubleNumerics": "4.0.1", + "Speckle.DoubleNumerics": "4.1.0", "Speckle.Newtonsoft.Json": "13.0.2", - "Speckle.Sdk.Dependencies": "3.1.0-dev.191" + "Speckle.Sdk.Dependencies": "3.1.1" } }, "Speckle.Sdk.Dependencies": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.191, )", - "resolved": "3.1.0-dev.191", - "contentHash": "EmEOyjsGsNi56Z/ZoBOn8WirTmIT2yqWvlUeUh0BSPX2TDMZXHTKOM/kHmP6HSd10KVFn2Zo/ItY7/K9iRtL1Q==" + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" + } + }, + "net8.0": { + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Direct", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" + }, + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net461": "1.0.3" + } + }, + "Microsoft.SourceLink.GitHub": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "dependencies": { + "Microsoft.Build.Tasks.Git": "8.0.0", + "Microsoft.SourceLink.Common": "8.0.0" + } + }, + "PolySharp": { + "type": "Direct", + "requested": "[1.14.1, )", + "resolved": "1.14.1", + "contentHash": "mOOmFYwad3MIOL14VCjj02LljyF1GNw1wP0YVlxtcPvqdxjGGMNdNJJxHptlry3MOd8b40Flm8RPOM8JOlN2sQ==" + }, + "Speckle.InterfaceGenerator": { + "type": "Direct", + "requested": "[0.9.6, )", + "resolved": "0.9.6", + "contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w==" + }, + "Speckle.Objects": { + "type": "Direct", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "eap7OfzVjaC7ls47uNDSJm4I/oOxBn7OzN9GrZn/HEg7lahAUsASnijHcD7aDmq/j+EeyoyBwQOKLLlJ9G/2sw==", + "dependencies": { + "Speckle.Sdk": "3.1.1" + } + }, + "GraphQL.Client": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "8yPNBbuVBpTptivyAlak4GZvbwbUcjeQTL4vN1HKHRuOykZ4r7l5fcLS6vpyPyLn0x8FsL31xbOIKyxbmR9rbA==", + "dependencies": { + "GraphQL.Client.Abstractions": "6.0.0", + "GraphQL.Client.Abstractions.Websocket": "6.0.0", + "System.Reactive": "5.0.0" + } + }, + "GraphQL.Client.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "h7uzWFORHZ+CCjwr/ThAyXMr0DPpzEANDa4Uo54wqCQ+j7qUKwqYTgOrb1W40sqbvNaZm9v/X7It31SUw0maHA==", + "dependencies": { + "GraphQL.Primitives": "6.0.0" + } + }, + "GraphQL.Client.Abstractions.Websocket": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "Nr9bPf8gIOvLuXpqEpqr9z9jslYFJOvd0feHth3/kPqeR3uMbjF5pjiwh4jxyMcxHdr8Pb6QiXkV3hsSyt0v7A==", + "dependencies": { + "GraphQL.Client.Abstractions": "6.0.0" + } + }, + "GraphQL.Primitives": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "yg72rrYDapfsIUrul7aF6wwNnTJBOFvuA9VdDTQpPa8AlAriHbufeXYLBcodKjfUdkCnaiggX1U/nEP08Zb5GA==" + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.CSharp": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==" + }, + "Microsoft.Data.Sqlite": { + "type": "Transitive", + "resolved": "7.0.5", + "contentHash": "KGxbPeWsQMnmQy43DSBxAFtHz3l2JX8EWBSGUCvT3CuZ8KsuzbkqMIJMDOxWtG8eZSoCDI04aiVQjWuuV8HmSw==", + "dependencies": { + "Microsoft.Data.Sqlite.Core": "7.0.5", + "SQLitePCLRaw.bundle_e_sqlite3": "2.1.4" + } + }, + "Microsoft.Data.Sqlite.Core": { + "type": "Transitive", + "resolved": "7.0.5", + "contentHash": "FTerRmQPqHrCrnoUzhBu+E+1DNGwyrAMLqHkAqOOOu5pGfyMOj8qQUBxI/gDtWtG11p49UxSfWmBzRNlwZqfUg==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "nOP8R1mVb/6mZtm2qgAJXn/LFm/2kMjHDAg/QJLFG6CuWYJtaD3p1BwQhufBVvRzL9ceJ/xF0SQ0qsI2GkDQAA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "65MrmXCziWaQFrI0UHkQbesrX5wTwf9XPjY5yFm/VkgJKFJ5gqvXRoXjIZcf2wLi5ZlwGz/oMYfyURVCWbM5iw==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "vJ9xvOZCnUAIHcGC3SU35r3HKmHTVIeHzo6u/qzlHAqD8m6xv92MLin4oJntTvkpKxVX3vI1GFFkIQtU3AdlsQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "2.2.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw==" + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "UpZLNLBpIZ0GTebShui7xXYh6DmBHjWM8NxGxZbdQh/bPZ5e6YswqI+bru6BnEL5eWiOdodsXtEz3FROcgi/qg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Primitives": "2.2.0", + "System.ComponentModel.Annotations": "4.5.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "azyQtqbm4fSaDzZHD/J+V6oWMFaf2tWP4WEGIYePLCMw3+b2RQdj9ybgbQyjCshcitQKQ4lEDOZjmSlTTrHxUg==", + "dependencies": { + "System.Memory": "4.5.1", + "System.Runtime.CompilerServices.Unsafe": "4.5.1" + } + }, + "Microsoft.NETFramework.ReferenceAssemblies.net461": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "AmOJZwCqnOCNp6PPcf9joyogScWLtwy0M1WkqfEQ0M9nYwyDD7EX9ZjscKS5iYnyvteX7kzSKFCKt9I9dXA6mA==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "Speckle.Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.2", + "contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA==" + }, + "SQLitePCLRaw.bundle_e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "EWI1olKDjFEBMJu0+3wuxwziIAdWDVMYLhuZ3Qs84rrz+DHwD00RzWPZCa+bLnHCf3oJwuFZIRsHT5p236QXww==", + "dependencies": { + "SQLitePCLRaw.lib.e_sqlite3": "2.1.4", + "SQLitePCLRaw.provider.e_sqlite3": "2.1.4" + } + }, + "SQLitePCLRaw.core": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "inBjvSHo9UDKneGNzfUfDjK08JzlcIhn1+SP5Y3m6cgXpCxXKCJDy6Mka7LpgSV+UZmKSnC8rTwB0SQ0xKu5pA==", + "dependencies": { + "System.Memory": "4.5.3" + } + }, + "SQLitePCLRaw.lib.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "2C9Q9eX7CPLveJA0rIhf9RXAvu+7nWZu1A2MdG6SD/NOu26TakGgL1nsbc0JAspGijFOo3HoN79xrx8a368fBg==" + }, + "SQLitePCLRaw.provider.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "CSlb5dUp1FMIkez9Iv5EXzpeq7rHryVNqwJMWnpq87j9zWZexaEMdisDktMsnnrzKM6ahNrsTkjqNodTBPBxtQ==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "System.ComponentModel.Annotations": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "UxYQ3FGUOtzJ7LfSdnYSFd7+oEv6M8NgUatatIN2HxNtDdlcvFAf+VIq4Of9cDMJEJC0aSRv/x898RYhB4Yppg==" + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.3", + "contentHash": "3oDzvc/zzetpTKWMShs1AADwZjQ/36HnsufHRPcOjyRAAMLDlu2iD33MBI2opxnezcVUtXyqDXXjoFMOU9c7SA==" + }, + "System.Reactive": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==" + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Zh8t8oqolRaFa9vmOZfdQm/qKejdqz0J9kr7o2Fu0vPeoH3BL1EOXipKWwkWtLT1JPzjByrF19fGuFlNbmPpiw==" + }, + "Microsoft.Extensions.Logging": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "Nxqhadc9FCmFHzU+fz3oc8sFlE6IadViYg8dfUdGzJZ2JUxnCsRghBhhOWdM4B2zSZqEc+0BjliBh/oNdRZuig==", + "dependencies": { + "Microsoft.Extensions.Configuration.Binder": "2.2.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging.Abstractions": "2.2.0", + "Microsoft.Extensions.Options": "2.2.0" + } + }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, + "Speckle.Sdk": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "4oVDt8u9ifHDiK+gOW+YKSmA6QtHI1+FHlgM+ezG7z1Zgm1tLnu/zIo8x7F8G3sQiw8yBx7Nli6mn1Y5YuIorg==", + "dependencies": { + "GraphQL.Client": "6.0.0", + "Microsoft.CSharp": "4.7.0", + "Microsoft.Data.Sqlite": "7.0.5", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging": "2.2.0", + "Speckle.DoubleNumerics": "4.1.0", + "Speckle.Newtonsoft.Json": "13.0.2", + "Speckle.Sdk.Dependencies": "3.1.1" + } + }, + "Speckle.Sdk.Dependencies": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "7RuD2i35uRbvgNR3vY8m5CdqnYJMdI2JWlrJ8kDw+wjKQWZ9JfpjAt0fd+jSijLO0nERNl172EfAOvcPvdGCgQ==" } } } diff --git a/Sdk/Speckle.Performance/JetbrainsProfiler.cs b/Sdk/Speckle.Performance/JetbrainsProfiler.cs new file mode 100644 index 000000000..207e4b2d3 --- /dev/null +++ b/Sdk/Speckle.Performance/JetbrainsProfiler.cs @@ -0,0 +1,48 @@ +using JetBrains.Profiler.SelfApi; + +namespace Speckle.Performance; + +public static class JetbrainsProfiler +{ + private sealed class CpuClass : IDisposable + { + public CpuClass(string snapshotPath) + { + DotTrace.Init(); + var config2 = new DotTrace.Config(); + config2.SaveToDir(snapshotPath); + DotTrace.Attach(config2); + DotTrace.StartCollectingData(); + } + + public void Dispose() + { + DotTrace.StopCollectingData(); + DotTrace.SaveData(); + DotTrace.Detach(); + } + } + + private sealed class MemoryClass : IDisposable + { + public MemoryClass(string snapshotPath) + { + DotMemory.Init(); + var config = new DotMemory.Config(); + config.OpenDotMemory(); + config.SaveToDir(snapshotPath); + DotMemory.Attach(config); + DotMemory.GetSnapshot("Before"); + } + + public void Dispose() + { + DotMemory.GetSnapshot("After"); + DotMemory.Detach(); + } + } + + public static IDisposable Cpu(string snapshotPath) => new CpuClass(snapshotPath); + + public static IDisposable Memory(string snapshotPath) => new MemoryClass(snapshotPath); +} diff --git a/Sdk/Speckle.Performance/Speckle.Performance.csproj b/Sdk/Speckle.Performance/Speckle.Performance.csproj new file mode 100644 index 000000000..84d1253ce --- /dev/null +++ b/Sdk/Speckle.Performance/Speckle.Performance.csproj @@ -0,0 +1,10 @@ + + + + net48;net6.0;net8.0 + + + + + + diff --git a/Sdk/Speckle.Performance/packages.lock.json b/Sdk/Speckle.Performance/packages.lock.json new file mode 100644 index 000000000..283b1ef5d --- /dev/null +++ b/Sdk/Speckle.Performance/packages.lock.json @@ -0,0 +1,242 @@ +{ + "version": 2, + "dependencies": { + ".NETFramework,Version=v4.8": { + "JetBrains.Profiler.SelfApi": { + "type": "Direct", + "requested": "[2.5.12, )", + "resolved": "2.5.12", + "contentHash": "m0MGIX6Eut9Z5hzqF4gNVn8oK2jeYDe7kihZTtD2/Iy9P7zE+zemO9VisiMD/1I1V8Es7lHOI49HNq9VGut96A==", + "dependencies": { + "JetBrains.HabitatDetector": "1.4.3", + "JetBrains.Profiler.Api": "1.4.8" + } + }, + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net48": "1.0.3" + } + }, + "Microsoft.SourceLink.GitHub": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "dependencies": { + "Microsoft.Build.Tasks.Git": "8.0.0", + "Microsoft.SourceLink.Common": "8.0.0" + } + }, + "PolySharp": { + "type": "Direct", + "requested": "[1.14.1, )", + "resolved": "1.14.1", + "contentHash": "mOOmFYwad3MIOL14VCjj02LljyF1GNw1wP0YVlxtcPvqdxjGGMNdNJJxHptlry3MOd8b40Flm8RPOM8JOlN2sQ==" + }, + "Speckle.InterfaceGenerator": { + "type": "Direct", + "requested": "[0.9.6, )", + "resolved": "0.9.6", + "contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w==" + }, + "JetBrains.FormatRipper": { + "type": "Transitive", + "resolved": "2.2.2", + "contentHash": "UvbPJM1ryALWSrnMX428ZxLujOrSAxdCgSwr1/7A0mFFxM8Rxku0OjN+zZENmZQ6Hirm7StkC176qy0m30UgjQ==" + }, + "JetBrains.HabitatDetector": { + "type": "Transitive", + "resolved": "1.4.3", + "contentHash": "4VC4sP/T+16MwsfUjwbKVYwKEOEu9cdQxLvN9uUiIm17m2y1SlV4Aj29HKsFbFyCbTePOGQMw3ccoPuym6d+EQ==", + "dependencies": { + "JetBrains.FormatRipper": "2.2.2" + } + }, + "JetBrains.Profiler.Api": { + "type": "Transitive", + "resolved": "1.4.8", + "contentHash": "hH0Hej/hjwTn14yhhzEcGaB3HJ40C/CwW2E8RrhsqCcBg5bs50LQFOx2a5dFXjWDMzspAso5xADtQmBI6lXKTw==", + "dependencies": { + "JetBrains.HabitatDetector": "1.4.3" + } + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.NETFramework.ReferenceAssemblies.net48": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "zMk4D+9zyiEWByyQ7oPImPN/Jhpj166Ky0Nlla4eXlNL8hI/BtSJsgR8Inldd4NNpIAH3oh8yym0W2DrhXdSLQ==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + } + }, + "net6.0": { + "JetBrains.Profiler.SelfApi": { + "type": "Direct", + "requested": "[2.5.12, )", + "resolved": "2.5.12", + "contentHash": "m0MGIX6Eut9Z5hzqF4gNVn8oK2jeYDe7kihZTtD2/Iy9P7zE+zemO9VisiMD/1I1V8Es7lHOI49HNq9VGut96A==", + "dependencies": { + "JetBrains.HabitatDetector": "1.4.3", + "JetBrains.Profiler.Api": "1.4.8" + } + }, + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net461": "1.0.3" + } + }, + "Microsoft.SourceLink.GitHub": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "dependencies": { + "Microsoft.Build.Tasks.Git": "8.0.0", + "Microsoft.SourceLink.Common": "8.0.0" + } + }, + "PolySharp": { + "type": "Direct", + "requested": "[1.14.1, )", + "resolved": "1.14.1", + "contentHash": "mOOmFYwad3MIOL14VCjj02LljyF1GNw1wP0YVlxtcPvqdxjGGMNdNJJxHptlry3MOd8b40Flm8RPOM8JOlN2sQ==" + }, + "Speckle.InterfaceGenerator": { + "type": "Direct", + "requested": "[0.9.6, )", + "resolved": "0.9.6", + "contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w==" + }, + "JetBrains.FormatRipper": { + "type": "Transitive", + "resolved": "2.2.2", + "contentHash": "UvbPJM1ryALWSrnMX428ZxLujOrSAxdCgSwr1/7A0mFFxM8Rxku0OjN+zZENmZQ6Hirm7StkC176qy0m30UgjQ==" + }, + "JetBrains.HabitatDetector": { + "type": "Transitive", + "resolved": "1.4.3", + "contentHash": "4VC4sP/T+16MwsfUjwbKVYwKEOEu9cdQxLvN9uUiIm17m2y1SlV4Aj29HKsFbFyCbTePOGQMw3ccoPuym6d+EQ==", + "dependencies": { + "JetBrains.FormatRipper": "2.2.2" + } + }, + "JetBrains.Profiler.Api": { + "type": "Transitive", + "resolved": "1.4.8", + "contentHash": "hH0Hej/hjwTn14yhhzEcGaB3HJ40C/CwW2E8RrhsqCcBg5bs50LQFOx2a5dFXjWDMzspAso5xADtQmBI6lXKTw==", + "dependencies": { + "JetBrains.HabitatDetector": "1.4.3" + } + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.NETFramework.ReferenceAssemblies.net461": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "AmOJZwCqnOCNp6PPcf9joyogScWLtwy0M1WkqfEQ0M9nYwyDD7EX9ZjscKS5iYnyvteX7kzSKFCKt9I9dXA6mA==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + } + }, + "net8.0": { + "JetBrains.Profiler.SelfApi": { + "type": "Direct", + "requested": "[2.5.12, )", + "resolved": "2.5.12", + "contentHash": "m0MGIX6Eut9Z5hzqF4gNVn8oK2jeYDe7kihZTtD2/Iy9P7zE+zemO9VisiMD/1I1V8Es7lHOI49HNq9VGut96A==", + "dependencies": { + "JetBrains.HabitatDetector": "1.4.3", + "JetBrains.Profiler.Api": "1.4.8" + } + }, + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net461": "1.0.3" + } + }, + "Microsoft.SourceLink.GitHub": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "dependencies": { + "Microsoft.Build.Tasks.Git": "8.0.0", + "Microsoft.SourceLink.Common": "8.0.0" + } + }, + "PolySharp": { + "type": "Direct", + "requested": "[1.14.1, )", + "resolved": "1.14.1", + "contentHash": "mOOmFYwad3MIOL14VCjj02LljyF1GNw1wP0YVlxtcPvqdxjGGMNdNJJxHptlry3MOd8b40Flm8RPOM8JOlN2sQ==" + }, + "Speckle.InterfaceGenerator": { + "type": "Direct", + "requested": "[0.9.6, )", + "resolved": "0.9.6", + "contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w==" + }, + "JetBrains.FormatRipper": { + "type": "Transitive", + "resolved": "2.2.2", + "contentHash": "UvbPJM1ryALWSrnMX428ZxLujOrSAxdCgSwr1/7A0mFFxM8Rxku0OjN+zZENmZQ6Hirm7StkC176qy0m30UgjQ==" + }, + "JetBrains.HabitatDetector": { + "type": "Transitive", + "resolved": "1.4.3", + "contentHash": "4VC4sP/T+16MwsfUjwbKVYwKEOEu9cdQxLvN9uUiIm17m2y1SlV4Aj29HKsFbFyCbTePOGQMw3ccoPuym6d+EQ==", + "dependencies": { + "JetBrains.FormatRipper": "2.2.2" + } + }, + "JetBrains.Profiler.Api": { + "type": "Transitive", + "resolved": "1.4.8", + "contentHash": "hH0Hej/hjwTn14yhhzEcGaB3HJ40C/CwW2E8RrhsqCcBg5bs50LQFOx2a5dFXjWDMzspAso5xADtQmBI6lXKTw==", + "dependencies": { + "JetBrains.HabitatDetector": "1.4.3" + } + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.NETFramework.ReferenceAssemblies.net461": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "AmOJZwCqnOCNp6PPcf9joyogScWLtwy0M1WkqfEQ0M9nYwyDD7EX9ZjscKS5iYnyvteX7kzSKFCKt9I9dXA6mA==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + } + } + } +} \ No newline at end of file diff --git a/Speckle.Connectors.sln b/Speckle.Connectors.sln index 50b6e43e1..586e6aee2 100644 --- a/Speckle.Connectors.sln +++ b/Speckle.Connectors.sln @@ -9,15 +9,15 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Config", "Config", "{85A13E codecov.yml = codecov.yml CodeMetricsConfig.txt = CodeMetricsConfig.txt Directory.Build.props = Directory.Build.props - Directory.Build.targets = Directory.Build.targets Directory.Packages.props = Directory.Packages.props global.json = global.json README.md = README.md EndProjectSection EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DUI", "DUI", "{FD4D6594-D81E-456F-8F2E-35B09E04A755}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Revit", "Revit", "{D92751C8-1039-4005-90B2-913E55E0B8BD}" + ProjectSection(SolutionItems) = preProject + Connectors\Revit\Directory.Build.targets = Connectors\Revit\Directory.Build.targets + EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sdk", "Sdk", "{2E00592E-558D-492D-88F9-3ECEE4C0C7DA}" EndProject @@ -36,6 +36,9 @@ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.Common", "Sdk\Speckle.Converters.Common\Speckle.Converters.Common.csproj", "{8AEF06C0-CA5C-4460-BC2D-ADE5F35D0434}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Rhino", "Rhino", "{9584AEE5-CD59-46E6-93E6-2DC2B5285B75}" + ProjectSection(SolutionItems) = preProject + Connectors\Rhino\Directory.Build.targets = Connectors\Rhino\Directory.Build.targets + EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Connectors.Rhino7", "Connectors\Rhino\Speckle.Connectors.Rhino7\Speckle.Connectors.Rhino7.csproj", "{1E2644A9-6B31-4350-8772-CEAAD6EE0B21}" EndProject @@ -52,6 +55,9 @@ EndProject Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Speckle.Connectors.AutocadShared", "Connectors\Autocad\Speckle.Connectors.AutocadShared\Speckle.Connectors.AutocadShared.shproj", "{41BC679F-887F-44CF-971D-A5502EE87DB0}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Autocad", "Autocad", "{804E065F-914C-414A-AF84-009312C3CFF6}" + ProjectSection(SolutionItems) = preProject + Connectors\Autocad\Directory.Build.targets = Connectors\Autocad\Directory.Build.targets + EndProjectSection EndProject Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Speckle.Converters.AutocadShared", "Converters\Autocad\Speckle.Converters.AutocadShared\Speckle.Converters.AutocadShared.shproj", "{9ADD1B7A-6401-4202-8613-F668E2FBC0A4}" EndProject @@ -61,12 +67,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Connectors.DUI.WebV EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Build", "Build\Build.csproj", "{C50AA3E3-8C31-4131-9DEC-1D8B377D5A89}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "HostApps", "HostApps", "{42826721-9A18-4762-8BA9-F1429DD5C5B1}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Connectors", "Connectors", "{42826721-9A18-4762-8BA9-F1429DD5C5B1}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{59E8E8F3-4E42-4E92-83B3-B1C2AB901D18}" ProjectSection(SolutionItems) = preProject - .github\workflows\ci.yml = .github\workflows\ci.yml - .github\workflows\main.yml = .github\workflows\main.yml + .github\workflows\pr.yml = .github\workflows\pr.yml + .github\workflows\release.yml = .github\workflows\release.yml EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Connectors.Civil3d2024", "Connectors\Autocad\Speckle.Connectors.Civil3d2024\Speckle.Connectors.Civil3d2024.csproj", "{CA8EAE01-AB9F-4EC1-B6F3-73721487E9E1}" @@ -154,6 +160,9 @@ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Connectors.Logging", "Sdk\Speckle.Connectors.Logging\Speckle.Connectors.Logging.csproj", "{8098BAFC-DF1C-4AFA-A93E-08121E6D09D4}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tekla", "Tekla", "{696086E4-D8CC-4FE0-A9B3-5F10B9089B55}" + ProjectSection(SolutionItems) = preProject + Connectors\Tekla\Directory.Build.targets = Connectors\Tekla\Directory.Build.targets + EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Connector.Tekla2024", "Connectors\Tekla\Speckle.Connector.Tekla2024\Speckle.Connector.Tekla2024.csproj", "{2319C00F-B268-4E4C-9F88-6B379E2BBD22}" EndProject @@ -190,6 +199,92 @@ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converter.Tekla2023", "Converters\Tekla\Speckle.Converter.Tekla2023\Speckle.Converter.Tekla2023.csproj", "{8F9181C2-1808-44C0-A33A-5BAE40C49E63}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Connectors.Grasshopper8", "Connectors\Rhino\Speckle.Connectors.Grasshopper8\Speckle.Connectors.Grasshopper8.csproj", "{8F6AD59B-FE43-41AE-8F47-7B9752BA0085}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "CSi", "CSi", "{073F40A8-6C95-41C1-A2F3-369FFFCB9520}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Connectors.ETABS22", "Connectors\CSi\Speckle.Connectors.ETABS22\Speckle.Connectors.ETABS22.csproj", "{7C49337A-6F7B-47AB-B549-42E799E89CF2}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ETABS22", "ETABS22", "{C6CD9332-874A-49DA-BEB6-3FAA5A700793}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{181F0468-B7A7-4CD7-ABD1-7F32B3ABB991}" +EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Speckle.Connectors.CSiShared", "Connectors\CSi\Speckle.Connectors.CSiShared\Speckle.Connectors.CSiShared.shproj", "{A8E949B8-AA55-4909-99F0-8B551791A1F8}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ETABS21", "ETABS21", "{DA6A607B-C267-4B2E-9C8A-F50B2F1BBFE0}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Connectors.ETABS21", "Connectors\CSi\Speckle.Connectors.ETABS21\Speckle.Connectors.ETABS21.csproj", "{115D6106-1801-484A-B4E5-BCC94B6E5C7F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.ETABS21", "Converters\CSi\Speckle.Converters.ETABS21\Speckle.Converters.ETABS21.csproj", "{791E3288-8001-4D54-8EAB-03D1D7F51044}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.ETABS22", "Converters\CSi\Speckle.Converters.ETABS22\Speckle.Converters.ETABS22.csproj", "{D61ECD90-3D17-4AF0-8B1A-0E0AD302DFF9}" +EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Speckle.Converters.CSiShared", "Converters\CSi\Speckle.Converters.CSiShared\Speckle.Converters.CSiShared.shproj", "{1B5C5FB2-3B22-4371-9AA5-3EDF3B4D62DE}" +EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Speckle.Connectors.ETABSShared", "Connectors\CSi\Speckle.Connectors.ETABSShared\Speckle.Connectors.ETABSShared.shproj", "{5D1E0B0D-56A7-4E13-B9A9-8633E02B8F17}" +EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Speckle.Converters.ETABSShared", "Converters\CSi\Speckle.Converters.ETABSShared\Speckle.Converters.ETABSShared.shproj", "{36377858-D696-4567-AB05-637F4EC841F5}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Navisworks", "Navisworks", "{7B95C512-C1CD-4110-A9A9-CECCD9630353}" + ProjectSection(SolutionItems) = preProject + Connectors\Navisworks\Directory.Build.targets = Connectors\Navisworks\Directory.Build.targets + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{3C54FE49-F14E-43A2-B1DF-85D5C7638117}" +EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Speckle.Connectors.NavisworksShared", "Connectors\Navisworks\Speckle.Connectors.NavisworksShared\Speckle.Connectors.NavisworksShared.shproj", "{62813838-52F7-43CB-9062-BB2611C00C79}" +EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Speckle.Converters.NavisworksShared", "Converters\Navisworks\Speckle.Converters.NavisworksShared\Speckle.Converters.NavisworksShared.shproj", "{23830613-87F8-4655-B523-72189A597962}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "2024", "2024", "{96FA29FE-766A-4500-853A-0A4D1BD0A78F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Connectors.Navisworks2024", "Connectors\Navisworks\Speckle.Connectors.Navisworks2024\Speckle.Connectors.Navisworks2024.csproj", "{8C14D930-03D2-4E2F-9D8D-A6B57F57A659}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Converters.Navisworks2024", "Converters\Navisworks\Speckle.Converters.Navisworks2024\Speckle.Converters.Navisworks2024.csproj", "{B12D6A2E-51FD-4197-B442-46C7D8F5E4E4}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "2020", "2020", "{EAFCD59F-3650-497C-94ED-AEEFC3131B18}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Connectors.Navisworks2020", "Connectors\Navisworks\Speckle.Connectors.Navisworks2020\Speckle.Connectors.Navisworks2020.csproj", "{B6985672-4704-4F86-A3E0-2310BF8E6D73}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Converters.Navisworks2020", "Converters\Navisworks\Speckle.Converters.Navisworks2020\Speckle.Converters.Navisworks2020.csproj", "{56680EA7-3599-4D88-83A5-B43BA93AC046}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "2021", "2021", "{78DE77B0-7954-41FF-B374-ABE705881CEC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Converters.Navisworks2021", "Converters\Navisworks\Speckle.Converters.Navisworks2021\Speckle.Converters.Navisworks2021.csproj", "{6101B44D-3805-4AA1-B8D0-E06C461ED202}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "2022", "2022", "{19F15419-F493-4D53-83EA-F90869D97D6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Converters.Navisworks2022", "Converters\Navisworks\Speckle.Converters.Navisworks2022\Speckle.Converters.Navisworks2022.csproj", "{A675E34C-667E-4069-A8FA-92F2C2DB8A27}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "2023", "2023", "{91DCAFB0-283B-4B07-9F6F-7335DECEEB08}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Converters.Navisworks2023", "Converters\Navisworks\Speckle.Converters.Navisworks2023\Speckle.Converters.Navisworks2023.csproj", "{57AFB8CB-B310-49E4-9C53-621A614063CF}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "2025", "2025", "{A88CFA1F-B2D5-4DBE-8496-68D0AFA46F2D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Converters.Navisworks2025", "Converters\Navisworks\Speckle.Converters.Navisworks2025\Speckle.Converters.Navisworks2025.csproj", "{0B5AB325-3791-4A81-B0EF-BCA040381BE2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Connectors.Navisworks2021", "Connectors\Navisworks\Speckle.Connectors.Navisworks2021\Speckle.Connectors.Navisworks2021.csproj", "{C635619C-2938-4E6F-9E25-56CE1632A7EC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Connectors.Navisworks2022", "Connectors\Navisworks\Speckle.Connectors.Navisworks2022\Speckle.Connectors.Navisworks2022.csproj", "{04FC86A3-2E25-41A1-98C5-10898616CD78}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Connectors.Navisworks2023", "Connectors\Navisworks\Speckle.Connectors.Navisworks2023\Speckle.Connectors.Navisworks2023.csproj", "{FD44E1F0-D20A-49B6-ADC9-482D911A74FB}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Connectors.Navisworks2025", "Connectors\Navisworks\Speckle.Connectors.Navisworks2025\Speckle.Connectors.Navisworks2025.csproj", "{7791806E-7531-41D8-9C9D-4A1249D9F99C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Importers.Ifc", "Importers\Ifc\Speckle.Importers.Ifc\Speckle.Importers.Ifc.csproj", "{E6B7A640-F85C-41C9-8226-B5310A98822D}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Ifc", "Ifc", "{F93052A6-6937-443F-8F1F-4A967A8A2BEF}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Importers", "Importers", "{336F0341-5C39-40F7-9377-122FED4E4549}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Importers.Ifc.Tester", "Importers\Ifc\Speckle.Importers.Ifc.Tester\Speckle.Importers.Ifc.Tester.csproj", "{FCD6CB79-6B41-4448-99E1-787408AD24B0}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Importers.Ifc.Tester2", "Importers\Ifc\Speckle.Importers.Ifc.Tester2\Speckle.Importers.Ifc.Tester2.csproj", "{17FB6920-DF63-4D94-86A4-F1619D501C6D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Connectors.Common.Tests", "Sdk\Speckle.Connectors.Common.Tests\Speckle.Connectors.Common.Tests.csproj", "{F86DFA8A-E2E0-4EBE-9BAF-72AE2698EDC6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Common.MeshTriangulation", "Sdk\Speckle.Common.MeshTriangulation\Speckle.Common.MeshTriangulation.csproj", "{B740A025-1035-4A75-865B-7825857D610C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Performance", "Sdk\Speckle.Performance\Speckle.Performance.csproj", "{65230E97-8EBA-4594-8A17-2847C5E2B459}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -492,6 +587,138 @@ Global {8F6AD59B-FE43-41AE-8F47-7B9752BA0085}.Local|Any CPU.Build.0 = Debug|Any CPU {8F6AD59B-FE43-41AE-8F47-7B9752BA0085}.Release|Any CPU.ActiveCfg = Release|Any CPU {8F6AD59B-FE43-41AE-8F47-7B9752BA0085}.Release|Any CPU.Build.0 = Release|Any CPU + {7C49337A-6F7B-47AB-B549-42E799E89CF2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7C49337A-6F7B-47AB-B549-42E799E89CF2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7C49337A-6F7B-47AB-B549-42E799E89CF2}.Local|Any CPU.ActiveCfg = Debug|Any CPU + {7C49337A-6F7B-47AB-B549-42E799E89CF2}.Local|Any CPU.Build.0 = Debug|Any CPU + {7C49337A-6F7B-47AB-B549-42E799E89CF2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7C49337A-6F7B-47AB-B549-42E799E89CF2}.Release|Any CPU.Build.0 = Release|Any CPU + {115D6106-1801-484A-B4E5-BCC94B6E5C7F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {115D6106-1801-484A-B4E5-BCC94B6E5C7F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {115D6106-1801-484A-B4E5-BCC94B6E5C7F}.Local|Any CPU.ActiveCfg = Debug|Any CPU + {115D6106-1801-484A-B4E5-BCC94B6E5C7F}.Local|Any CPU.Build.0 = Debug|Any CPU + {115D6106-1801-484A-B4E5-BCC94B6E5C7F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {115D6106-1801-484A-B4E5-BCC94B6E5C7F}.Release|Any CPU.Build.0 = Release|Any CPU + {791E3288-8001-4D54-8EAB-03D1D7F51044}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {791E3288-8001-4D54-8EAB-03D1D7F51044}.Debug|Any CPU.Build.0 = Debug|Any CPU + {791E3288-8001-4D54-8EAB-03D1D7F51044}.Local|Any CPU.ActiveCfg = Debug|Any CPU + {791E3288-8001-4D54-8EAB-03D1D7F51044}.Local|Any CPU.Build.0 = Debug|Any CPU + {791E3288-8001-4D54-8EAB-03D1D7F51044}.Release|Any CPU.ActiveCfg = Release|Any CPU + {791E3288-8001-4D54-8EAB-03D1D7F51044}.Release|Any CPU.Build.0 = Release|Any CPU + {D61ECD90-3D17-4AF0-8B1A-0E0AD302DFF9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D61ECD90-3D17-4AF0-8B1A-0E0AD302DFF9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D61ECD90-3D17-4AF0-8B1A-0E0AD302DFF9}.Local|Any CPU.ActiveCfg = Debug|Any CPU + {D61ECD90-3D17-4AF0-8B1A-0E0AD302DFF9}.Local|Any CPU.Build.0 = Debug|Any CPU + {D61ECD90-3D17-4AF0-8B1A-0E0AD302DFF9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D61ECD90-3D17-4AF0-8B1A-0E0AD302DFF9}.Release|Any CPU.Build.0 = Release|Any CPU + {8C14D930-03D2-4E2F-9D8D-A6B57F57A659}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8C14D930-03D2-4E2F-9D8D-A6B57F57A659}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8C14D930-03D2-4E2F-9D8D-A6B57F57A659}.Local|Any CPU.ActiveCfg = Debug|Any CPU + {8C14D930-03D2-4E2F-9D8D-A6B57F57A659}.Local|Any CPU.Build.0 = Debug|Any CPU + {8C14D930-03D2-4E2F-9D8D-A6B57F57A659}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8C14D930-03D2-4E2F-9D8D-A6B57F57A659}.Release|Any CPU.Build.0 = Release|Any CPU + {B12D6A2E-51FD-4197-B442-46C7D8F5E4E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B12D6A2E-51FD-4197-B442-46C7D8F5E4E4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B12D6A2E-51FD-4197-B442-46C7D8F5E4E4}.Local|Any CPU.ActiveCfg = Local|Any CPU + {B12D6A2E-51FD-4197-B442-46C7D8F5E4E4}.Local|Any CPU.Build.0 = Local|Any CPU + {B12D6A2E-51FD-4197-B442-46C7D8F5E4E4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B12D6A2E-51FD-4197-B442-46C7D8F5E4E4}.Release|Any CPU.Build.0 = Release|Any CPU + {B6985672-4704-4F86-A3E0-2310BF8E6D73}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B6985672-4704-4F86-A3E0-2310BF8E6D73}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B6985672-4704-4F86-A3E0-2310BF8E6D73}.Local|Any CPU.ActiveCfg = Debug|Any CPU + {B6985672-4704-4F86-A3E0-2310BF8E6D73}.Local|Any CPU.Build.0 = Debug|Any CPU + {B6985672-4704-4F86-A3E0-2310BF8E6D73}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B6985672-4704-4F86-A3E0-2310BF8E6D73}.Release|Any CPU.Build.0 = Release|Any CPU + {56680EA7-3599-4D88-83A5-B43BA93AC046}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {56680EA7-3599-4D88-83A5-B43BA93AC046}.Debug|Any CPU.Build.0 = Debug|Any CPU + {56680EA7-3599-4D88-83A5-B43BA93AC046}.Local|Any CPU.ActiveCfg = Local|Any CPU + {56680EA7-3599-4D88-83A5-B43BA93AC046}.Local|Any CPU.Build.0 = Local|Any CPU + {56680EA7-3599-4D88-83A5-B43BA93AC046}.Release|Any CPU.ActiveCfg = Release|Any CPU + {56680EA7-3599-4D88-83A5-B43BA93AC046}.Release|Any CPU.Build.0 = Release|Any CPU + {6101B44D-3805-4AA1-B8D0-E06C461ED202}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6101B44D-3805-4AA1-B8D0-E06C461ED202}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6101B44D-3805-4AA1-B8D0-E06C461ED202}.Local|Any CPU.ActiveCfg = Local|Any CPU + {6101B44D-3805-4AA1-B8D0-E06C461ED202}.Local|Any CPU.Build.0 = Local|Any CPU + {6101B44D-3805-4AA1-B8D0-E06C461ED202}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6101B44D-3805-4AA1-B8D0-E06C461ED202}.Release|Any CPU.Build.0 = Release|Any CPU + {A675E34C-667E-4069-A8FA-92F2C2DB8A27}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A675E34C-667E-4069-A8FA-92F2C2DB8A27}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A675E34C-667E-4069-A8FA-92F2C2DB8A27}.Local|Any CPU.ActiveCfg = Local|Any CPU + {A675E34C-667E-4069-A8FA-92F2C2DB8A27}.Local|Any CPU.Build.0 = Local|Any CPU + {A675E34C-667E-4069-A8FA-92F2C2DB8A27}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A675E34C-667E-4069-A8FA-92F2C2DB8A27}.Release|Any CPU.Build.0 = Release|Any CPU + {57AFB8CB-B310-49E4-9C53-621A614063CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {57AFB8CB-B310-49E4-9C53-621A614063CF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {57AFB8CB-B310-49E4-9C53-621A614063CF}.Local|Any CPU.ActiveCfg = Local|Any CPU + {57AFB8CB-B310-49E4-9C53-621A614063CF}.Local|Any CPU.Build.0 = Local|Any CPU + {57AFB8CB-B310-49E4-9C53-621A614063CF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {57AFB8CB-B310-49E4-9C53-621A614063CF}.Release|Any CPU.Build.0 = Release|Any CPU + {0B5AB325-3791-4A81-B0EF-BCA040381BE2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0B5AB325-3791-4A81-B0EF-BCA040381BE2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0B5AB325-3791-4A81-B0EF-BCA040381BE2}.Local|Any CPU.ActiveCfg = Local|Any CPU + {0B5AB325-3791-4A81-B0EF-BCA040381BE2}.Local|Any CPU.Build.0 = Local|Any CPU + {0B5AB325-3791-4A81-B0EF-BCA040381BE2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0B5AB325-3791-4A81-B0EF-BCA040381BE2}.Release|Any CPU.Build.0 = Release|Any CPU + {C635619C-2938-4E6F-9E25-56CE1632A7EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C635619C-2938-4E6F-9E25-56CE1632A7EC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C635619C-2938-4E6F-9E25-56CE1632A7EC}.Local|Any CPU.ActiveCfg = Debug|Any CPU + {C635619C-2938-4E6F-9E25-56CE1632A7EC}.Local|Any CPU.Build.0 = Debug|Any CPU + {C635619C-2938-4E6F-9E25-56CE1632A7EC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C635619C-2938-4E6F-9E25-56CE1632A7EC}.Release|Any CPU.Build.0 = Release|Any CPU + {04FC86A3-2E25-41A1-98C5-10898616CD78}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {04FC86A3-2E25-41A1-98C5-10898616CD78}.Debug|Any CPU.Build.0 = Debug|Any CPU + {04FC86A3-2E25-41A1-98C5-10898616CD78}.Local|Any CPU.ActiveCfg = Debug|Any CPU + {04FC86A3-2E25-41A1-98C5-10898616CD78}.Local|Any CPU.Build.0 = Debug|Any CPU + {04FC86A3-2E25-41A1-98C5-10898616CD78}.Release|Any CPU.ActiveCfg = Release|Any CPU + {04FC86A3-2E25-41A1-98C5-10898616CD78}.Release|Any CPU.Build.0 = Release|Any CPU + {FD44E1F0-D20A-49B6-ADC9-482D911A74FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FD44E1F0-D20A-49B6-ADC9-482D911A74FB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FD44E1F0-D20A-49B6-ADC9-482D911A74FB}.Local|Any CPU.ActiveCfg = Debug|Any CPU + {FD44E1F0-D20A-49B6-ADC9-482D911A74FB}.Local|Any CPU.Build.0 = Debug|Any CPU + {FD44E1F0-D20A-49B6-ADC9-482D911A74FB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FD44E1F0-D20A-49B6-ADC9-482D911A74FB}.Release|Any CPU.Build.0 = Release|Any CPU + {7791806E-7531-41D8-9C9D-4A1249D9F99C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7791806E-7531-41D8-9C9D-4A1249D9F99C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7791806E-7531-41D8-9C9D-4A1249D9F99C}.Local|Any CPU.ActiveCfg = Debug|Any CPU + {7791806E-7531-41D8-9C9D-4A1249D9F99C}.Local|Any CPU.Build.0 = Debug|Any CPU + {7791806E-7531-41D8-9C9D-4A1249D9F99C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7791806E-7531-41D8-9C9D-4A1249D9F99C}.Release|Any CPU.Build.0 = Release|Any CPU + {E6B7A640-F85C-41C9-8226-B5310A98822D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E6B7A640-F85C-41C9-8226-B5310A98822D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E6B7A640-F85C-41C9-8226-B5310A98822D}.Local|Any CPU.ActiveCfg = Debug|Any CPU + {E6B7A640-F85C-41C9-8226-B5310A98822D}.Local|Any CPU.Build.0 = Debug|Any CPU + {E6B7A640-F85C-41C9-8226-B5310A98822D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E6B7A640-F85C-41C9-8226-B5310A98822D}.Release|Any CPU.Build.0 = Release|Any CPU + {FCD6CB79-6B41-4448-99E1-787408AD24B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FCD6CB79-6B41-4448-99E1-787408AD24B0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FCD6CB79-6B41-4448-99E1-787408AD24B0}.Local|Any CPU.ActiveCfg = Debug|Any CPU + {FCD6CB79-6B41-4448-99E1-787408AD24B0}.Local|Any CPU.Build.0 = Debug|Any CPU + {FCD6CB79-6B41-4448-99E1-787408AD24B0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FCD6CB79-6B41-4448-99E1-787408AD24B0}.Release|Any CPU.Build.0 = Release|Any CPU + {17FB6920-DF63-4D94-86A4-F1619D501C6D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {17FB6920-DF63-4D94-86A4-F1619D501C6D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {17FB6920-DF63-4D94-86A4-F1619D501C6D}.Local|Any CPU.ActiveCfg = Debug|Any CPU + {17FB6920-DF63-4D94-86A4-F1619D501C6D}.Local|Any CPU.Build.0 = Debug|Any CPU + {17FB6920-DF63-4D94-86A4-F1619D501C6D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {17FB6920-DF63-4D94-86A4-F1619D501C6D}.Release|Any CPU.Build.0 = Release|Any CPU + {F86DFA8A-E2E0-4EBE-9BAF-72AE2698EDC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F86DFA8A-E2E0-4EBE-9BAF-72AE2698EDC6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F86DFA8A-E2E0-4EBE-9BAF-72AE2698EDC6}.Local|Any CPU.ActiveCfg = Debug|Any CPU + {F86DFA8A-E2E0-4EBE-9BAF-72AE2698EDC6}.Local|Any CPU.Build.0 = Debug|Any CPU + {F86DFA8A-E2E0-4EBE-9BAF-72AE2698EDC6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F86DFA8A-E2E0-4EBE-9BAF-72AE2698EDC6}.Release|Any CPU.Build.0 = Release|Any CPU + {B740A025-1035-4A75-865B-7825857D610C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B740A025-1035-4A75-865B-7825857D610C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B740A025-1035-4A75-865B-7825857D610C}.Local|Any CPU.ActiveCfg = Debug|Any CPU + {B740A025-1035-4A75-865B-7825857D610C}.Local|Any CPU.Build.0 = Debug|Any CPU + {B740A025-1035-4A75-865B-7825857D610C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B740A025-1035-4A75-865B-7825857D610C}.Release|Any CPU.Build.0 = Release|Any CPU + {65230E97-8EBA-4594-8A17-2847C5E2B459}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {65230E97-8EBA-4594-8A17-2847C5E2B459}.Debug|Any CPU.Build.0 = Debug|Any CPU + {65230E97-8EBA-4594-8A17-2847C5E2B459}.Local|Any CPU.ActiveCfg = Debug|Any CPU + {65230E97-8EBA-4594-8A17-2847C5E2B459}.Local|Any CPU.Build.0 = Debug|Any CPU + {65230E97-8EBA-4594-8A17-2847C5E2B459}.Release|Any CPU.ActiveCfg = Release|Any CPU + {65230E97-8EBA-4594-8A17-2847C5E2B459}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -502,7 +729,7 @@ Global {DC570FFF-6FE5-47BD-8BC1-B471A6067786} = {FC224610-32D3-454E-9BC1-1219FE8ACD5F} {E1C43415-3200-45F4-8BF9-A4DD7D7F2ED6} = {FC224610-32D3-454E-9BC1-1219FE8ACD5F} {26391930-F86F-47E0-A5F6-B89919E38CE1} = {E9DEBA00-50A4-485D-BA65-D8AB3E3467AB} - {D81C0B87-F0C1-4297-A147-02F001FB7E1E} = {FD4D6594-D81E-456F-8F2E-35B09E04A755} + {D81C0B87-F0C1-4297-A147-02F001FB7E1E} = {2E00592E-558D-492D-88F9-3ECEE4C0C7DA} {7291B93C-615D-42DE-B8C1-3F9DF643E0FC} = {2E00592E-558D-492D-88F9-3ECEE4C0C7DA} {8AEF06C0-CA5C-4460-BC2D-ADE5F35D0434} = {2E00592E-558D-492D-88F9-3ECEE4C0C7DA} {9584AEE5-CD59-46E6-93E6-2DC2B5285B75} = {42826721-9A18-4762-8BA9-F1429DD5C5B1} @@ -516,7 +743,7 @@ Global {804E065F-914C-414A-AF84-009312C3CFF6} = {42826721-9A18-4762-8BA9-F1429DD5C5B1} {9ADD1B7A-6401-4202-8613-F668E2FBC0A4} = {4721AA15-AF6E-4A62-A2C3-65564DC563E6} {631C295A-7CCF-4B42-8686-7034E31469E7} = {2D5AE63D-85C0-43D1-84BF-04418ED93F63} - {7420652C-3046-4F38-BE64-9B9E69D76FA2} = {FD4D6594-D81E-456F-8F2E-35B09E04A755} + {7420652C-3046-4F38-BE64-9B9E69D76FA2} = {2E00592E-558D-492D-88F9-3ECEE4C0C7DA} {C50AA3E3-8C31-4131-9DEC-1D8B377D5A89} = {59E8E8F3-4E42-4E92-83B3-B1C2AB901D18} {CA8EAE01-AB9F-4EC1-B6F3-73721487E9E1} = {2F45036E-D817-41E9-B82F-DBE013EC95D0} {35175682-DA83-4C0A-A49D-B191F5885D8E} = {4721AA15-AF6E-4A62-A2C3-65564DC563E6} @@ -543,7 +770,7 @@ Global {7F1FDCF2-0CE8-4119-B3C1-F2CC6D7E1C36} = {0AF38BA3-65A0-481B-8CBB-B82E406E1575} {19424B55-058C-4E9C-B86F-700AEF9EAEC3} = {0AF38BA3-65A0-481B-8CBB-B82E406E1575} {0AF38BA3-65A0-481B-8CBB-B82E406E1575} = {D92751C8-1039-4005-90B2-913E55E0B8BD} - {EB83A3A3-F9B6-4281-8EBF-F7289FB5D885} = {FD4D6594-D81E-456F-8F2E-35B09E04A755} + {EB83A3A3-F9B6-4281-8EBF-F7289FB5D885} = {2E00592E-558D-492D-88F9-3ECEE4C0C7DA} {D8069A23-AD2E-4C9E-8574-7E8C45296A46} = {0AF38BA3-65A0-481B-8CBB-B82E406E1575} {2D5AE63D-85C0-43D1-84BF-04418ED93F63} = {804E065F-914C-414A-AF84-009312C3CFF6} {2F45036E-D817-41E9-B82F-DBE013EC95D0} = {804E065F-914C-414A-AF84-009312C3CFF6} @@ -579,6 +806,47 @@ Global {025C85F8-F741-4600-BC46-5FEAD754B65D} = {C552C165-5023-47AB-9EE1-1EA61819D2B1} {8F9181C2-1808-44C0-A33A-5BAE40C49E63} = {C552C165-5023-47AB-9EE1-1EA61819D2B1} {8F6AD59B-FE43-41AE-8F47-7B9752BA0085} = {5929C9C7-F971-449E-BC5B-4486016BD11F} + {073F40A8-6C95-41C1-A2F3-369FFFCB9520} = {42826721-9A18-4762-8BA9-F1429DD5C5B1} + {7C49337A-6F7B-47AB-B549-42E799E89CF2} = {C6CD9332-874A-49DA-BEB6-3FAA5A700793} + {C6CD9332-874A-49DA-BEB6-3FAA5A700793} = {073F40A8-6C95-41C1-A2F3-369FFFCB9520} + {181F0468-B7A7-4CD7-ABD1-7F32B3ABB991} = {073F40A8-6C95-41C1-A2F3-369FFFCB9520} + {A8E949B8-AA55-4909-99F0-8B551791A1F8} = {181F0468-B7A7-4CD7-ABD1-7F32B3ABB991} + {DA6A607B-C267-4B2E-9C8A-F50B2F1BBFE0} = {073F40A8-6C95-41C1-A2F3-369FFFCB9520} + {115D6106-1801-484A-B4E5-BCC94B6E5C7F} = {DA6A607B-C267-4B2E-9C8A-F50B2F1BBFE0} + {791E3288-8001-4D54-8EAB-03D1D7F51044} = {DA6A607B-C267-4B2E-9C8A-F50B2F1BBFE0} + {D61ECD90-3D17-4AF0-8B1A-0E0AD302DFF9} = {C6CD9332-874A-49DA-BEB6-3FAA5A700793} + {1B5C5FB2-3B22-4371-9AA5-3EDF3B4D62DE} = {181F0468-B7A7-4CD7-ABD1-7F32B3ABB991} + {5D1E0B0D-56A7-4E13-B9A9-8633E02B8F17} = {181F0468-B7A7-4CD7-ABD1-7F32B3ABB991} + {36377858-D696-4567-AB05-637F4EC841F5} = {181F0468-B7A7-4CD7-ABD1-7F32B3ABB991} + {7B95C512-C1CD-4110-A9A9-CECCD9630353} = {42826721-9A18-4762-8BA9-F1429DD5C5B1} + {3C54FE49-F14E-43A2-B1DF-85D5C7638117} = {7B95C512-C1CD-4110-A9A9-CECCD9630353} + {62813838-52F7-43CB-9062-BB2611C00C79} = {3C54FE49-F14E-43A2-B1DF-85D5C7638117} + {23830613-87F8-4655-B523-72189A597962} = {3C54FE49-F14E-43A2-B1DF-85D5C7638117} + {96FA29FE-766A-4500-853A-0A4D1BD0A78F} = {7B95C512-C1CD-4110-A9A9-CECCD9630353} + {8C14D930-03D2-4E2F-9D8D-A6B57F57A659} = {96FA29FE-766A-4500-853A-0A4D1BD0A78F} + {B12D6A2E-51FD-4197-B442-46C7D8F5E4E4} = {96FA29FE-766A-4500-853A-0A4D1BD0A78F} + {EAFCD59F-3650-497C-94ED-AEEFC3131B18} = {7B95C512-C1CD-4110-A9A9-CECCD9630353} + {B6985672-4704-4F86-A3E0-2310BF8E6D73} = {EAFCD59F-3650-497C-94ED-AEEFC3131B18} + {56680EA7-3599-4D88-83A5-B43BA93AC046} = {EAFCD59F-3650-497C-94ED-AEEFC3131B18} + {78DE77B0-7954-41FF-B374-ABE705881CEC} = {7B95C512-C1CD-4110-A9A9-CECCD9630353} + {6101B44D-3805-4AA1-B8D0-E06C461ED202} = {78DE77B0-7954-41FF-B374-ABE705881CEC} + {19F15419-F493-4D53-83EA-F90869D97D6E} = {7B95C512-C1CD-4110-A9A9-CECCD9630353} + {A675E34C-667E-4069-A8FA-92F2C2DB8A27} = {19F15419-F493-4D53-83EA-F90869D97D6E} + {91DCAFB0-283B-4B07-9F6F-7335DECEEB08} = {7B95C512-C1CD-4110-A9A9-CECCD9630353} + {57AFB8CB-B310-49E4-9C53-621A614063CF} = {91DCAFB0-283B-4B07-9F6F-7335DECEEB08} + {A88CFA1F-B2D5-4DBE-8496-68D0AFA46F2D} = {7B95C512-C1CD-4110-A9A9-CECCD9630353} + {0B5AB325-3791-4A81-B0EF-BCA040381BE2} = {A88CFA1F-B2D5-4DBE-8496-68D0AFA46F2D} + {C635619C-2938-4E6F-9E25-56CE1632A7EC} = {78DE77B0-7954-41FF-B374-ABE705881CEC} + {04FC86A3-2E25-41A1-98C5-10898616CD78} = {19F15419-F493-4D53-83EA-F90869D97D6E} + {FD44E1F0-D20A-49B6-ADC9-482D911A74FB} = {91DCAFB0-283B-4B07-9F6F-7335DECEEB08} + {7791806E-7531-41D8-9C9D-4A1249D9F99C} = {A88CFA1F-B2D5-4DBE-8496-68D0AFA46F2D} + {E6B7A640-F85C-41C9-8226-B5310A98822D} = {F93052A6-6937-443F-8F1F-4A967A8A2BEF} + {F93052A6-6937-443F-8F1F-4A967A8A2BEF} = {336F0341-5C39-40F7-9377-122FED4E4549} + {FCD6CB79-6B41-4448-99E1-787408AD24B0} = {F93052A6-6937-443F-8F1F-4A967A8A2BEF} + {17FB6920-DF63-4D94-86A4-F1619D501C6D} = {F93052A6-6937-443F-8F1F-4A967A8A2BEF} + {F86DFA8A-E2E0-4EBE-9BAF-72AE2698EDC6} = {2E00592E-558D-492D-88F9-3ECEE4C0C7DA} + {B740A025-1035-4A75-865B-7825857D610C} = {2E00592E-558D-492D-88F9-3ECEE4C0C7DA} + {65230E97-8EBA-4594-8A17-2847C5E2B459} = {2E00592E-558D-492D-88F9-3ECEE4C0C7DA} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {EE253116-7070-4E9A-BCE8-2911C251B8C8} @@ -587,13 +855,20 @@ Global Connectors\Revit\Speckle.Connectors.RevitShared.Cef\Speckle.Connectors.RevitShared.Cef.projitems*{01f98733-7352-47ad-a594-537d979de3de}*SharedItemsImports = 5 Connectors\Revit\Speckle.Connectors.RevitShared\Speckle.Connectors.RevitShared.projitems*{01f98733-7352-47ad-a594-537d979de3de}*SharedItemsImports = 5 Connectors\Tekla\Speckle.Connector.TeklaShared\Speckle.Connectors.TeklaShared.projitems*{025c85f8-f741-4600-bc46-5fead754b65d}*SharedItemsImports = 5 + Connectors\Navisworks\Speckle.Connectors.NavisworksShared\Speckle.Connectors.NavisworksShared.projitems*{04fc86a3-2e25-41a1-98c5-10898616cd78}*SharedItemsImports = 5 + Converters\Navisworks\Speckle.Converters.NavisworksShared\Speckle.Converters.NavisworksShared.projitems*{0b5ab325-3791-4a81-b0ef-bca040381be2}*SharedItemsImports = 5 + Connectors\CSi\Speckle.Connectors.CsiShared\Speckle.Connectors.CsiShared.projitems*{115d6106-1801-484a-b4e5-bcc94b6e5c7f}*SharedItemsImports = 5 + Connectors\CSi\Speckle.Connectors.ETABSShared\Speckle.Connectors.ETABSShared.projitems*{115d6106-1801-484a-b4e5-bcc94b6e5c7f}*SharedItemsImports = 5 Converters\Revit\Speckle.Converters.RevitShared\Speckle.Converters.RevitShared.projitems*{19424b55-058c-4e9c-b86f-700aef9eaec3}*SharedItemsImports = 5 + Converters\CSi\Speckle.Converters.CSiShared\Speckle.Converters.CSiShared.projitems*{1b5c5fb2-3b22-4371-9aa5-3edf3b4d62de}*SharedItemsImports = 13 Connectors\Rhino\Speckle.Connectors.RhinoShared\Speckle.Connectors.RhinoShared.projitems*{1e2644a9-6b31-4350-8772-ceaad6ee0b21}*SharedItemsImports = 5 Connectors\Tekla\Speckle.Connector.TeklaShared\Speckle.Connectors.TeklaShared.projitems*{2319c00f-b268-4e4c-9f88-6b379e2bbd22}*SharedItemsImports = 5 + Converters\Navisworks\Speckle.Converters.NavisworksShared\Speckle.Converters.NavisworksShared.projitems*{23830613-87f8-4655-b523-72189a597962}*SharedItemsImports = 13 Converters\Autocad\Speckle.Converters.AutocadShared\Speckle.Converters.AutocadShared.projitems*{25172c49-7aa4-4739-bb07-69785094c379}*SharedItemsImports = 5 Converters\Civil3d\Speckle.Converters.Civil3dShared\Speckle.Converters.Civil3dShared.projitems*{25172c49-7aa4-4739-bb07-69785094c379}*SharedItemsImports = 5 Converters\Revit\Speckle.Converters.RevitShared\Speckle.Converters.RevitShared.projitems*{26391930-f86f-47e0-a5f6-b89919e38ce1}*SharedItemsImports = 5 Converters\Civil3d\Speckle.Converters.Civil3dShared\Speckle.Converters.Civil3dShared.projitems*{35175682-da83-4c0a-a49d-b191f5885d8e}*SharedItemsImports = 13 + Converters\CSi\Speckle.Converters.ETABSShared\Speckle.Converters.ETABSShared.projitems*{36377858-d696-4567-ab05-637f4ec841f5}*SharedItemsImports = 13 Connectors\Tekla\Speckle.Connector.TeklaShared\Speckle.Connectors.TeklaShared.projitems*{3ab9028b-b2d2-464b-9ba3-39c192441e50}*SharedItemsImports = 13 Connectors\Autocad\Speckle.Connectors.AutocadShared\Speckle.Connectors.AutocadShared.projitems*{41bc679f-887f-44cf-971d-a5502ee87db0}*SharedItemsImports = 13 Connectors\Autocad\Speckle.Connectors.AutocadShared\Speckle.Connectors.AutocadShared.projitems*{4459f2b1-a340-488e-a856-eb2ae9c72ad4}*SharedItemsImports = 5 @@ -601,17 +876,27 @@ Global Converters\Revit\Speckle.Converters.RevitShared\Speckle.Converters.RevitShared.projitems*{4d40a101-07e6-4ff2-8934-83434932591d}*SharedItemsImports = 5 Converters\Tekla\Speckle.Converters.TeklaShared\Speckle.Converters.TeklaShared.projitems*{52666479-5401-47d6-b7ba-d554784439ea}*SharedItemsImports = 13 Converters\Autocad\Speckle.Converters.AutocadShared\Speckle.Converters.AutocadShared.projitems*{5505b953-d434-49ce-8ebd-efd7b3c378f7}*SharedItemsImports = 5 + Converters\Navisworks\Speckle.Converters.NavisworksShared\Speckle.Converters.NavisworksShared.projitems*{56680ea7-3599-4d88-83a5-b43ba93ac046}*SharedItemsImports = 5 Converters\Rhino\Speckle.Converters.RhinoShared\Speckle.Converters.RhinoShared.projitems*{56a909ae-6e99-4d4d-a22e-38bdc5528b8e}*SharedItemsImports = 5 + Converters\Navisworks\Speckle.Converters.NavisworksShared\Speckle.Converters.NavisworksShared.projitems*{57afb8cb-b310-49e4-9c53-621a614063cf}*SharedItemsImports = 5 Converters\Autocad\Speckle.Converters.AutocadShared\Speckle.Converters.AutocadShared.projitems*{5cdec958-708e-4d19-a79e-0c1db23a6039}*SharedItemsImports = 5 Converters\Civil3d\Speckle.Converters.Civil3dShared\Speckle.Converters.Civil3dShared.projitems*{5cdec958-708e-4d19-a79e-0c1db23a6039}*SharedItemsImports = 5 + Connectors\CSi\Speckle.Connectors.ETABSShared\Speckle.Connectors.ETABSShared.projitems*{5d1e0b0d-56a7-4e13-b9a9-8633e02b8f17}*SharedItemsImports = 13 + Converters\Navisworks\Speckle.Converters.NavisworksShared\Speckle.Converters.NavisworksShared.projitems*{6101b44d-3805-4aa1-b8d0-e06c461ed202}*SharedItemsImports = 5 Connectors\Revit\Speckle.Connectors.RevitShared.Cef\Speckle.Connectors.RevitShared.Cef.projitems*{617bd3c7-87d9-4d28-8ac9-4910945bb9fc}*SharedItemsImports = 5 Connectors\Revit\Speckle.Connectors.RevitShared\Speckle.Connectors.RevitShared.projitems*{617bd3c7-87d9-4d28-8ac9-4910945bb9fc}*SharedItemsImports = 5 + Connectors\Navisworks\Speckle.Connectors.NavisworksShared\Speckle.Connectors.NavisworksShared.projitems*{62813838-52f7-43cb-9062-bb2611c00c79}*SharedItemsImports = 13 Converters\Autocad\Speckle.Converters.AutocadShared\Speckle.Converters.AutocadShared.projitems*{631c295a-7ccf-4b42-8686-7034e31469e7}*SharedItemsImports = 5 Converters\Rhino\Speckle.Converters.RhinoShared\Speckle.Converters.RhinoShared.projitems*{65a2f556-f14a-49f3-8a92-7f2e1e7ed3b5}*SharedItemsImports = 5 Converters\Revit\Speckle.Converters.RevitShared\Speckle.Converters.RevitShared.projitems*{67b888d9-c6c4-49f1-883c-5b964151d889}*SharedItemsImports = 5 Converters\Revit\Speckle.Converters.RevitShared.Tests\Speckle.Converters.RevitShared.Tests.projitems*{68cf9bdf-94ac-4d2d-a7bd-d1c064f97051}*SharedItemsImports = 5 Converters\Revit\Speckle.Converters.RevitShared\Speckle.Converters.RevitShared.projitems*{68cf9bdf-94ac-4d2d-a7bd-d1c064f97051}*SharedItemsImports = 5 Connectors\Revit\Speckle.Connectors.RevitShared.Cef\Speckle.Connectors.RevitShared.Cef.projitems*{6a40cbe4-ecab-4ced-9917-5c64cbf75da6}*SharedItemsImports = 13 + Connectors\Navisworks\Speckle.Connectors.NavisworksShared\Speckle.Connectors.NavisworksShared.projitems*{7791806e-7531-41d8-9c9d-4a1249d9f99c}*SharedItemsImports = 5 + Converters\CSi\Speckle.Converters.CSiShared\Speckle.Converters.CSiShared.projitems*{791e3288-8001-4d54-8eab-03d1d7f51044}*SharedItemsImports = 5 + Converters\CSi\Speckle.Converters.ETABSShared\Speckle.Converters.ETABSShared.projitems*{791e3288-8001-4d54-8eab-03d1d7f51044}*SharedItemsImports = 5 + Connectors\CSi\Speckle.Connectors.CsiShared\Speckle.Connectors.CsiShared.projitems*{7c49337a-6f7b-47ab-b549-42e799e89cf2}*SharedItemsImports = 5 + Connectors\CSi\Speckle.Connectors.ETABSShared\Speckle.Connectors.ETABSShared.projitems*{7c49337a-6f7b-47ab-b549-42e799e89cf2}*SharedItemsImports = 5 Connectors\Revit\Speckle.Connectors.RevitShared.Cef\Speckle.Connectors.RevitShared.Cef.projitems*{7f1fdcf2-0ce8-4119-b3c1-f2cc6d7e1c36}*SharedItemsImports = 5 Connectors\Revit\Speckle.Connectors.RevitShared\Speckle.Connectors.RevitShared.projitems*{7f1fdcf2-0ce8-4119-b3c1-f2cc6d7e1c36}*SharedItemsImports = 5 Connectors\Autocad\Speckle.Connectors.AutocadShared\Speckle.Connectors.AutocadShared.projitems*{81fcee13-feac-475d-9ef9-71132ef26909}*SharedItemsImports = 5 @@ -619,26 +904,34 @@ Global Converters\Autocad\Speckle.Converters.AutocadShared\Speckle.Converters.AutocadShared.projitems*{842f4bfd-3997-485d-bab5-9419c1d982f2}*SharedItemsImports = 5 Converters\Civil3d\Speckle.Converters.Civil3dShared\Speckle.Converters.Civil3dShared.projitems*{842f4bfd-3997-485d-bab5-9419c1d982f2}*SharedItemsImports = 5 Connectors\Autocad\Speckle.Connectors.AutocadShared\Speckle.Connectors.AutocadShared.projitems*{89c4cbc7-1606-40de-b6da-fbe3aac98395}*SharedItemsImports = 5 + Connectors\Navisworks\Speckle.Connectors.NavisworksShared\Speckle.Connectors.NavisworksShared.projitems*{8c14d930-03d2-4e2f-9d8d-a6b57f57a659}*SharedItemsImports = 5 Converters\Tekla\Speckle.Converters.TeklaShared\Speckle.Converters.TeklaShared.projitems*{8f9181c2-1808-44c0-a33a-5bae40c49e63}*SharedItemsImports = 5 Connectors\Autocad\Speckle.Connectors.AutocadShared\Speckle.Connectors.AutocadShared.projitems*{9166cc10-12e1-4a0f-916b-61f6f2004f5a}*SharedItemsImports = 5 Converters\Autocad\Speckle.Converters.AutocadShared\Speckle.Converters.AutocadShared.projitems*{9add1b7a-6401-4202-8613-f668e2fbc0a4}*SharedItemsImports = 13 Converters\Autocad\Speckle.Converters.AutocadShared\Speckle.Converters.AutocadShared.projitems*{9d66ede4-afc2-4f00-b40c-a7e878a2972f}*SharedItemsImports = 5 Connectors\Autocad\Speckle.Connectors.Civil3dShared\Speckle.Connectors.Civil3dShared.projitems*{a1b72ec1-5bc4-41fd-850f-5da87df49616}*SharedItemsImports = 13 + Converters\Navisworks\Speckle.Converters.NavisworksShared\Speckle.Converters.NavisworksShared.projitems*{a675e34c-667e-4069-a8fa-92f2c2db8a27}*SharedItemsImports = 5 Connectors\Revit\Speckle.Connectors.RevitShared.Cef\Speckle.Connectors.RevitShared.Cef.projitems*{a6de3da0-b242-4f49-aef0-4e26af92d16c}*SharedItemsImports = 5 Connectors\Revit\Speckle.Connectors.RevitShared\Speckle.Connectors.RevitShared.projitems*{a6de3da0-b242-4f49-aef0-4e26af92d16c}*SharedItemsImports = 5 Connectors\Rhino\Speckle.Connectors.RhinoShared\Speckle.Connectors.RhinoShared.projitems*{a6e3a82f-4696-4d92-aba1-38aa80752067}*SharedItemsImports = 5 + Connectors\CSi\Speckle.Connectors.CsiShared\Speckle.Connectors.CsiShared.projitems*{a8e949b8-aa55-4909-99f0-8b551791a1f8}*SharedItemsImports = 13 Converters\Rhino\Speckle.Converters.RhinoShared\Speckle.Converters.RhinoShared.projitems*{ac2db416-f05c-4296-9040-56d6ad4fcd27}*SharedItemsImports = 5 Converters\Tekla\Speckle.Converters.TeklaShared\Speckle.Converters.TeklaShared.projitems*{acf75860-7fce-4ae9-8c45-68ad1043550b}*SharedItemsImports = 5 Connectors\Autocad\Speckle.Connectors.AutocadShared\Speckle.Connectors.AutocadShared.projitems*{afab80bd-a4dd-4cad-9937-acbfed668a48}*SharedItemsImports = 5 Connectors\Autocad\Speckle.Connectors.Civil3dShared\Speckle.Connectors.Civil3dShared.projitems*{afab80bd-a4dd-4cad-9937-acbfed668a48}*SharedItemsImports = 5 + Converters\Navisworks\Speckle.Converters.NavisworksShared\Speckle.Converters.NavisworksShared.projitems*{b12d6a2e-51fd-4197-b442-46c7d8f5e4e4}*SharedItemsImports = 5 Connectors\Rhino\Speckle.Connectors.RhinoShared\Speckle.Connectors.RhinoShared.projitems*{b37d4b9a-8d3f-4fa5-b9c8-e6c5f8a0c1e2}*SharedItemsImports = 13 + Connectors\Navisworks\Speckle.Connectors.NavisworksShared\Speckle.Connectors.NavisworksShared.projitems*{b6985672-4704-4f86-a3e0-2310bf8e6d73}*SharedItemsImports = 5 Connectors\Autocad\Speckle.Connectors.AutocadShared\Speckle.Connectors.AutocadShared.projitems*{bbf2c19e-221e-4aa0-8521-fe75d8f4a2b0}*SharedItemsImports = 5 Converters\Autocad\Speckle.Converters.AutocadShared\Speckle.Converters.AutocadShared.projitems*{c2de264a-aa87-4012-b954-17e3f403a237}*SharedItemsImports = 5 Converters\Revit\Speckle.Converters.RevitShared.Tests\Speckle.Converters.RevitShared.Tests.projitems*{c32274d9-1b66-4d5c-82f9-eb3f10f46752}*SharedItemsImports = 5 Converters\Revit\Speckle.Converters.RevitShared\Speckle.Converters.RevitShared.projitems*{c32274d9-1b66-4d5c-82f9-eb3f10f46752}*SharedItemsImports = 5 + Connectors\Navisworks\Speckle.Connectors.NavisworksShared\Speckle.Connectors.NavisworksShared.projitems*{c635619c-2938-4e6f-9e25-56ce1632a7ec}*SharedItemsImports = 5 Connectors\Autocad\Speckle.Connectors.AutocadShared\Speckle.Connectors.AutocadShared.projitems*{c70ebb84-ba5b-4f2f-819e-25e0985ba13c}*SharedItemsImports = 5 Connectors\Autocad\Speckle.Connectors.AutocadShared\Speckle.Connectors.AutocadShared.projitems*{ca8eae01-ab9f-4ec1-b6f3-73721487e9e1}*SharedItemsImports = 5 Connectors\Autocad\Speckle.Connectors.Civil3dShared\Speckle.Connectors.Civil3dShared.projitems*{ca8eae01-ab9f-4ec1-b6f3-73721487e9e1}*SharedItemsImports = 5 + Converters\CSi\Speckle.Converters.CSiShared\Speckle.Converters.CSiShared.projitems*{d61ecd90-3d17-4af0-8b1a-0e0ad302dff9}*SharedItemsImports = 5 + Converters\CSi\Speckle.Converters.ETABSShared\Speckle.Converters.ETABSShared.projitems*{d61ecd90-3d17-4af0-8b1a-0e0ad302dff9}*SharedItemsImports = 5 Converters\Revit\Speckle.Converters.RevitShared.Tests\Speckle.Converters.RevitShared.Tests.projitems*{d8069a23-ad2e-4c9e-8574-7e8c45296a46}*SharedItemsImports = 5 Converters\Revit\Speckle.Converters.RevitShared\Speckle.Converters.RevitShared.projitems*{d8069a23-ad2e-4c9e-8574-7e8c45296a46}*SharedItemsImports = 5 Converters\Autocad\Speckle.Converters.AutocadShared\Speckle.Converters.AutocadShared.projitems*{db31e57b-60fc-49be-91e0-1374290bcf03}*SharedItemsImports = 5 @@ -647,5 +940,6 @@ Global Converters\Revit\Speckle.Converters.RevitShared\Speckle.Converters.RevitShared.projitems*{e1c43415-3200-45f4-8bf9-a4dd7d7f2ed6}*SharedItemsImports = 13 Converters\Rhino\Speckle.Converters.RhinoShared\Speckle.Converters.RhinoShared.projitems*{e1c43415-3200-45f4-8bf9-a4dd7d7f2ed9}*SharedItemsImports = 13 Converters\Revit\Speckle.Converters.RevitShared.Tests\Speckle.Converters.RevitShared.Tests.projitems*{e1c43415-3202-45f4-8bf9-a4dd7d7f2ed6}*SharedItemsImports = 13 + Connectors\Navisworks\Speckle.Connectors.NavisworksShared\Speckle.Connectors.NavisworksShared.projitems*{fd44e1f0-d20a-49b6-adc9-482d911a74fb}*SharedItemsImports = 5 EndGlobalSection EndGlobal diff --git a/Speckle.Connectors.sln.DotSettings b/Speckle.Connectors.sln.DotSettings index 3af02c0f3..71bf7f852 100644 --- a/Speckle.Connectors.sln.DotSettings +++ b/Speckle.Connectors.sln.DotSettings @@ -1,4 +1,5 @@  DUI + IFC UI URI \ No newline at end of file