diff --git a/.circleci/scripts/config-template.yml b/.circleci/scripts/config-template.yml index ff92a40566..115a0539ee 100644 --- a/.circleci/scripts/config-template.yml +++ b/.circleci/scripts/config-template.yml @@ -433,7 +433,7 @@ jobs: # Each project will have individual jobs for each specific task it has to build-connector-dotnet-mac: docker: - - image: mcr.microsoft.com/dotnet/sdk:7.0 + - image: mcr.microsoft.com/dotnet/sdk:8.0.204 parameters: slnname: type: string diff --git a/.circleci/scripts/parameters.json b/.circleci/scripts/parameters.json index 3530ba7244..f5529641b7 100644 --- a/.circleci/scripts/parameters.json +++ b/.circleci/scripts/parameters.json @@ -1,12 +1,12 @@ { - "core": true, - "rhino": true, - "revit": true, - "dynamo": true, - "csi": true, - "autocadcivil": true, - "bentley": true, - "archicad": true, - "teklastructures": true, - "navisworks": true -} + "core": true, + "rhino": true, + "revit": true, + "dynamo": true, + "csi": true, + "autocadcivil": true, + "bentley": true, + "archicad": true, + "teklastructures": true, + "navisworks": true + } \ No newline at end of file diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 3b0ca23774..47a1501435 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -4,27 +4,19 @@ "tools": { "dotnet-t4": { "version": "2.2.1", - "commands": [ - "t4" - ] - }, - "jetbrains.resharper.globaltools": { - "version": "2023.1.0", - "commands": [ - "jb" - ] + "commands": ["t4"] }, "csharpier": { "version": "0.23.0", - "commands": [ - "dotnet-csharpier" - ] + "commands": ["dotnet-csharpier"] }, "husky": { "version": "0.5.4", - "commands": [ - "husky" - ] + "commands": ["husky"] + }, + "gitversion.tool": { + "version": "5.12.0", + "commands": ["dotnet-gitversion"] } } -} \ No newline at end of file +} diff --git a/.editorconfig b/.editorconfig index a226d40036..0485923f3a 100644 --- a/.editorconfig +++ b/.editorconfig @@ -219,6 +219,8 @@ dotnet_diagnostic.ide0078.severity = suggestion # Use pattern matching: Subjecti dotnet_diagnostic.ide0260.severity = suggestion # Use pattern matching: Subjective dotnet_diagnostic.ide0022.severity = suggestion # Use expression body for method: Subjective dotnet_diagnostic.ide0061.severity = suggestion # Use expression body for local functions: Subjective +dotnet_diagnostic.ide0063.severity = suggestion # Using directive can be simplified +dotnet_diagnostic.ide0066.severity = suggestion # Use switch expression: Subjective dotnet_diagnostic.ide0029.severity = suggestion # Null check can be simplified: Subjective dotnet_diagnostic.ide0030.severity = suggestion # Null check can be simplified: Subjective dotnet_diagnostic.ide0270.severity = suggestion # Null check can be simplified: Subjective @@ -229,6 +231,7 @@ dotnet_diagnostic.ide0030.severity = suggestion # Null check can be simplified: dotnet_diagnostic.ide0270.severity = suggestion # Null check can be simplified: Subjective dotnet_diagnostic.ide0042.severity = suggestion # Deconstruct variable declaration: Subjective dotnet_diagnostic.ide0028.severity = suggestion # Use collection initializers: Subjective +dotnet_diagnostic.ide0072.severity = suggestion # Populate switch statement: Subjective dotnet_diagnostic.ide0074.severity = suggestion # Use compound assignment: Subjective # Maintainability rules @@ -239,6 +242,8 @@ dotnet_diagnostic.ca1506.severity = warning # Avoid excessive class coupling dotnet_diagnostic.ca1507.severity = warning # Use nameof in place of string dotnet_diagnostic.ca1508.severity = warning # Avoid dead conditional code dotnet_diagnostic.ca1509.severity = warning # Invalid entry in code metrics configuration file +dotnet_diagnostic.CA2254.severity = none # Template should be a static expression + # Performance rules diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000000..3413224bf0 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" # search for actions - there are other options available + directory: "/" # search in .github/workflows under root `/` + schedule: + interval: "weekly" # check for action update every week \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000000..9444fbb667 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,52 @@ +name: .NET Build + +on: + pull_request: # Run build on every pull request that is not to main/dev + branches-ignore: + - main + - dev + workflow_call: + outputs: + version: + value: ${{ jobs.build.outputs.version }} + +jobs: + build: + runs-on: windows-latest + outputs: + version: ${{ steps.set-version.outputs.version }} + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: 8.0.2xx # Align with global.json (including roll forward rules) + + - name: Cache Nuget + uses: actions/cache@v4 + with: + path: ~/.nuget/packages + key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }} + restore-keys: | + ${{ runner.os }}-nuget- + + - name: ⚒️ Run GitVersion + run: ./build.ps1 build-server-version + + - name: ⚒️ Run build + run: ./build.ps1 + + - name: ⬆️ Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: output-${{ env.GitVersion_FullSemVer }} + path: output/*.* + compression-level: 0 # no compression + + - id: set-version + name: Set version to output + run: echo "version=${{ env.GitVersion_FullSemVer }}" >> "$GITHUB_OUTPUT" diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000000..b83fa759fc --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,30 @@ +name: .NET Build and Publish + +on: + push: + branches: ["main", "dev", "dui3/alpha"] # Continuous delivery on every long-lived branch + tags: ["3.*"] # Manual delivery on every 3.x tag + pull_request: + branches: ["main", "dev"] # Releases on every PR that targets main or dev + +jobs: + build: + uses: ./.github/workflows/ci.yml + + # deploy-installers: + # runs-on: ubuntu-latest + # needs: build + # steps: + # - name: 🔫 Trigger Build Installers + # uses: ALEEF02/workflow-dispatch@v3.0.0 + # with: + # workflow: Build Installers + # repo: specklesystems/connector-installers + # token: ${{ secrets.CONNECTORS_GH_TOKEN }} + # inputs: '{ "run_id": "${{ github.run_id }}", "version": "${{ needs.build.outputs.version }}" }' + # ref: main + # wait-for-completion: true + # wait-for-completion-interval: 10s + # wait-for-completion-timeout: 10m + # display-workflow-run-url: true + # display-workflow-run-url-interval: 10s diff --git a/.gitignore b/.gitignore index 86ceee4974..f7b3cd5d9a 100644 --- a/.gitignore +++ b/.gitignore @@ -341,6 +341,9 @@ ASALocalRun/ # MSBuild Binary and Structured Log *.binlog +# random logs +log*.txt + # NVidia Nsight GPU debugger configuration file *.nvuser @@ -377,3 +380,7 @@ ConnectorNavisworks/ConnectorNavisworks/Sample Models ConnectorArchicad/AddOn/Build* **/dist/ + +**/yarn.lock + +output/ diff --git a/All.sln b/All.sln index df383c2f22..4cca95fc50 100644 --- a/All.sln +++ b/All.sln @@ -2,7 +2,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.1.32210.238 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Connectors", "Connectors", "{DD7DA7E3-FBB7-4216-852B-A4A5BF3AB9AB}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "LegacyConnectors", "LegacyConnectors", "{DD7DA7E3-FBB7-4216-852B-A4A5BF3AB9AB}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Dynamo", "Dynamo", "{CB6F8F77-4487-469B-896A-1EEEC4451A04}" EndProject @@ -24,7 +24,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Transports", "Transports", EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Objects", "Objects", "{E3916A0F-68D5-4C84-ACAE-41547F75E454}" ProjectSection(SolutionItems) = preProject - Objects\Directory.Build.Props = Objects\Directory.Build.Props + Objects\Directory.Build.props = Objects\Directory.Build.props EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Converters", "Converters", "{4DE5ED81-2A55-4C23-A05F-3C9B9B06F85D}" @@ -172,8 +172,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "CSI", "CSI", "{11C013F3-258 EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConnectorCSIBridge", "ConnectorCSI\ConnectorCSIBridge\ConnectorCSIBridge.csproj", "{23BE6E54-96C1-4373-89F3-E18A1C9807FD}" ProjectSection(ProjectDependencies) = postProject - {60BE029E-1F31-4473-8B68-A745A43AF179} = {60BE029E-1F31-4473-8B68-A745A43AF179} {21223BA5-C6E8-405D-B581-106C4726EDC0} = {21223BA5-C6E8-405D-B581-106C4726EDC0} + {60BE029E-1F31-4473-8B68-A745A43AF179} = {60BE029E-1F31-4473-8B68-A745A43AF179} EndProjectSection EndProject Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "ConnectorCSIShared", "ConnectorCSI\ConnectorCSIShared\ConnectorCSIShared.shproj", "{61374CD0-E774-4DCD-BFAB-6356B0931283}" @@ -186,8 +186,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConnectorETABS", "Connector EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConnectorSAFE", "ConnectorCSI\ConnectorSAFE\ConnectorSAFE.csproj", "{9D188843-8841-4A76-A844-EFBE8E32EE05}" ProjectSection(ProjectDependencies) = postProject - {60BE029E-1F31-4473-8B68-A745A43AF179} = {60BE029E-1F31-4473-8B68-A745A43AF179} {442116F3-0F4A-4136-894E-FF5F4295500B} = {442116F3-0F4A-4136-894E-FF5F4295500B} + {60BE029E-1F31-4473-8B68-A745A43AF179} = {60BE029E-1F31-4473-8B68-A745A43AF179} EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConnectorSAP2000", "ConnectorCSI\ConnectorSAP2000\ConnectorSAP2000.csproj", "{31E0C098-6813-4571-AB96-A245E0FC1C23}" @@ -347,6 +347,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution CodeMetricsConfig.txt = CodeMetricsConfig.txt Directory.Build.props = Directory.Build.props Directory.Build.targets = Directory.Build.targets + global.json = global.json EndProjectSection EndProject Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "ConnectorAutocadCivilShared", "ConnectorAutocadCivil\ConnectorAutocadCivil\ConnectorAutocadCivilShared.shproj", "{B47492D9-2EDA-4016-A930-7FA708C85C3D}" @@ -455,6 +456,86 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConnectorNavisworks2025", " EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DllConflictManagement", "ConnectorCore\DllConflictManagement\DllConflictManagement.csproj", "{0D23858F-4CC1-4DCA-9207-5EDB8B6CE9DD}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DUI3-DX", "DUI3-DX", "{9DB74760-01DE-4AC1-A81B-BC7784351D22}" + ProjectSection(SolutionItems) = preProject + DUI3-DX\Directory.Build.props = DUI3-DX\Directory.Build.props + DUI3-DX\Directory.Packages.props = DUI3-DX\Directory.Packages.props + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Revit", "Revit", "{4838C66E-8677-4FBD-9609-25376042E981}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Connectors", "Connectors", "{33D19E88-F3AE-4D28-B588-D91CCF9E3BA8}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Converters", "Converters", "{1FE3C60E-7865-40A5-9794-55ECB64F6489}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DUI3", "DUI3", "{FD4D6594-D81E-456F-8F2E-35B09E04A755}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Revit", "Revit", "{D92751C8-1039-4005-90B2-913E55E0B8BD}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sdk", "Sdk", "{2E00592E-558D-492D-88F9-3ECEE4C0C7DA}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Connectors.Revit2023", "DUI3-DX\Connectors\Revit\Speckle.Connectors.Revit2023\Speckle.Connectors.Revit2023.csproj", "{01F98733-7352-47AD-A594-537D979DE3DE}" +EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Speckle.Connectors.RevitShared", "DUI3-DX\Connectors\Revit\Speckle.Connectors.RevitShared\Speckle.Connectors.RevitShared.shproj", "{DC570FFF-6FE5-47BD-8BC1-B471A6067786}" +EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Speckle.Converters.RevitShared", "DUI3-DX\Converters\Revit\Speckle.Converters.RevitShared\Speckle.Converters.RevitShared.shproj", "{E1C43415-3200-45F4-8BF9-A4DD7D7F2ED6}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.Revit2023.DependencyInjection", "DUI3-DX\Converters\Revit\Speckle.Converters.Revit2023.DependencyInjection\Speckle.Converters.Revit2023.DependencyInjection.csproj", "{83EAD6F0-3CB3-456A-AD81-072127D0DE0E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.Revit2023", "DUI3-DX\Converters\Revit\Speckle.Converters.Revit2023\Speckle.Converters.Revit2023.csproj", "{26391930-F86F-47E0-A5F6-B89919E38CE1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Connectors.DUI", "DUI3-DX\DUI3\Speckle.Connectors.DUI\Speckle.Connectors.DUI.csproj", "{D81C0B87-F0C1-4297-A147-02F001FB7E1E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Autofac", "DUI3-DX\Sdk\Speckle.Autofac\Speckle.Autofac.csproj", "{C9D4CA21-182B-4ED2-81BB-280A6FD713F6}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Connectors.Utils", "DUI3-DX\Sdk\Speckle.Connectors.Utils\Speckle.Connectors.Utils.csproj", "{7291B93C-615D-42DE-B8C1-3F9DF643E0FC}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.Common", "DUI3-DX\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}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Connectors.Rhino7", "DUI3-DX\Connectors\Rhino\Speckle.Connectors.Rhino7\Speckle.Connectors.Rhino7.csproj", "{1E2644A9-6B31-4350-8772-CEAAD6EE0B21}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Rhino", "Rhino", "{34C2C062-E43F-4FB5-B839-64BC044CCEF3}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.Rhino7", "DUI3-DX\Converters\Rhino\Speckle.Converters.Rhino7\Speckle.Converters.Rhino7.csproj", "{65A2F556-F14A-49F3-8A92-7F2E1E7ED3B5}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.Rhino7.DependencyInjection", "DUI3-DX\Converters\Rhino\Speckle.Converters.Rhino7.DependencyInjection\Speckle.Converters.Rhino7.DependencyInjection.csproj", "{9C1ECA1D-DE98-4FB7-92D0-FC45BB308E5D}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Connectors.ArcGIS3", "DUI3-DX\Connectors\ArcGIS\Speckle.Connectors.ArcGIS3\Speckle.Connectors.ArcGIS3.csproj", "{A97F7177-86C7-4B38-A6ED-DA51BF762471}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ArcGIS", "ArcGIS", "{CE4B899D-9C0A-4B5D-B91A-CE62D2327695}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.ArcGIS3", "DUI3-DX\Converters\ArcGIS\Speckle.Converters.ArcGIS3\Speckle.Converters.ArcGIS3.csproj", "{139F7A79-69E4-4B8A-B2A5-6A30A66C495C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.ArcGIS3.DependencyInjection", "DUI3-DX\Converters\ArcGIS\Speckle.Converters.ArcGIS3.DependencyInjection\Speckle.Converters.ArcGIS3.DependencyInjection.csproj", "{7DFF1591-237D-499E-A767-EE37B93FB958}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ArcGIS", "ArcGIS", "{CCF48B65-33D1-4E8B-A57B-E03394730B21}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Autocad", "Autocad", "{743489BF-1941-43D5-8AF9-35C56D0DCC34}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Connectors.Autocad2023", "DUI3-DX\Connectors\Autocad\Speckle.Connectors.Autocad2023\Speckle.Connectors.Autocad2023.csproj", "{89C4CBC7-1606-40DE-B6DA-FBE3AAC98395}" +EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Speckle.Connectors.AutocadShared", "DUI3-DX\Connectors\Autocad\Speckle.Connectors.AutocadShared\Speckle.Connectors.AutocadShared.shproj", "{41BC679F-887F-44CF-971D-A5502EE87DB0}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.Common.DependencyInjection", "DUI3-DX\Sdk\Speckle.Converters.Common.DependencyInjection\Speckle.Converters.Common.DependencyInjection.csproj", "{11F7D41B-AFCA-4D29-BC08-285A14BF3A3B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Autocad", "Autocad", "{804E065F-914C-414A-AF84-009312C3CFF6}" +EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Speckle.Converters.AutocadShared", "DUI3-DX\Converters\Autocad\Speckle.Converters.AutocadShared\Speckle.Converters.AutocadShared.shproj", "{9ADD1B7A-6401-4202-8613-F668E2FBC0A4}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Connectors.DUI.WebView", "DUI3-DX\DUI3\Speckle.Connectors.DUI.WebView\Speckle.Connectors.DUI.WebView.csproj", "{7420652C-3046-4F38-BE64-9B9E69D76FA2}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{326ECEE0-D009-4A65-B24C-00FA343D8B99}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Build", "Build\Build.csproj", "{3973D572-5E24-476F-B058-8022D826B793}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Workflows", "Workflows", "{2AB6B848-E8F6-4BB7-AE8F-9913C74C893F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\ci.yml = .github\workflows\ci.yml + .github\workflows\main.yml = .github\workflows\main.yml + EndProjectSection +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RevitSharedResources2025", "ConnectorRevit\RevitSharedResources2025\RevitSharedResources2025.csproj", "{7B02BACC-D9B6-4FFE-A450-7ECB5F71F209}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConnectorRevit2025", "ConnectorRevit\ConnectorRevit2025\ConnectorRevit2025.csproj", "{D607BD0A-9F7F-4C3A-9B9C-FEAD6BA49C7C}" @@ -469,6 +550,26 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConnectorCivil2025", "Conne EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConverterCivil2025", "Objects\Converters\ConverterAutocadCivil\ConverterCivil2025\ConverterCivil2025.csproj", "{F06E4C37-4076-4272-9CA6-FB505E02CD31}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{B1324D25-C601-40F2-8AE2-6131F492B911}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Connectors.Civil3d2024", "DUI3-DX\Connectors\Autocad\Speckle.Connectors.Civil3d2024\Speckle.Connectors.Civil3d2024.csproj", "{DDBBA313-69A6-40DE-AB3A-79EE5BF32A7E}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Civil3d", "Civil3d", "{34A6BB15-A030-4C5B-94B2-1A1DFE49334A}" +EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Speckle.Converters.Civil3dShared", "DUI3-DX\Converters\Civil3d\Speckle.Converters.Civil3dShared\Speckle.Converters.Civil3dShared.shproj", "{35175682-DA83-4C0A-A49D-B191F5885D8E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.Civil3d2024", "DUI3-DX\Converters\Civil3d\Speckle.Converters.Civil3d2024\Speckle.Converters.Civil3d2024.csproj", "{E7FA6A25-A224-4207-846B-75CE8236228D}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.Civil3d2024.DependencyInjection", "DUI3-DX\Converters\Civil3d\Speckle.Converters.Civil3d2024.DependencyInjection\Speckle.Converters.Civil3d2024.DependencyInjection.csproj", "{9BCE88BA-1E01-44EE-BF9D-2CB906E63E34}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.Autocad2024.DependencyInjection", "DUI3-DX\Converters\Autocad\Speckle.Converters.Autocad2024.DependencyInjection\Speckle.Converters.Autocad2024.DependencyInjection.csproj", "{2C587020-5F9F-40E1-8AF8-772FE3FC256A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.Autocad2023", "DUI3-DX\Converters\Autocad\Speckle.Converters.Autocad2023\Speckle.Converters.Autocad2023.csproj", "{C9F084AF-FB70-4F5F-ACEB-DB6818BBECAF}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.Autocad2023.DependencyInjection", "DUI3-DX\Converters\Autocad\Speckle.Converters.Autocad2023.DependencyInjection\Speckle.Converters.Autocad2023.DependencyInjection.csproj", "{4E83BA93-4EC4-4A6E-AD27-8CD9A409FD23}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.Autocad2024", "DUI3-DX\Converters\Autocad\Speckle.Converters.Autocad2024\Speckle.Converters.Autocad2024.csproj", "{A6BAA749-0DBE-49A9-9A9D-FCA0F06EE096}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug Mac|Any CPU = Debug Mac|Any CPU @@ -1350,8 +1451,8 @@ Global {74E39841-B2FA-494D-AC40-A6E505DE6B33}.Debug Mac|x64.Build.0 = Debug|x64 {74E39841-B2FA-494D-AC40-A6E505DE6B33}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {74E39841-B2FA-494D-AC40-A6E505DE6B33}.Debug|Any CPU.Build.0 = Debug|Any CPU - {74E39841-B2FA-494D-AC40-A6E505DE6B33}.Debug|x64.ActiveCfg = Debug|x64 - {74E39841-B2FA-494D-AC40-A6E505DE6B33}.Debug|x64.Build.0 = Debug|x64 + {74E39841-B2FA-494D-AC40-A6E505DE6B33}.Debug|x64.ActiveCfg = Debug|Any CPU + {74E39841-B2FA-494D-AC40-A6E505DE6B33}.Debug|x64.Build.0 = Debug|Any CPU {74E39841-B2FA-494D-AC40-A6E505DE6B33}.Release Mac|Any CPU.ActiveCfg = Release|Any CPU {74E39841-B2FA-494D-AC40-A6E505DE6B33}.Release Mac|Any CPU.Build.0 = Release|Any CPU {74E39841-B2FA-494D-AC40-A6E505DE6B33}.Release Mac|x64.ActiveCfg = Release|x64 @@ -1365,8 +1466,8 @@ Global {77D4F346-ACA5-42C8-8522-5EF176F3ADF1}.Debug Mac|x64.Build.0 = Debug|x64 {77D4F346-ACA5-42C8-8522-5EF176F3ADF1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {77D4F346-ACA5-42C8-8522-5EF176F3ADF1}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77D4F346-ACA5-42C8-8522-5EF176F3ADF1}.Debug|x64.ActiveCfg = Debug|x64 - {77D4F346-ACA5-42C8-8522-5EF176F3ADF1}.Debug|x64.Build.0 = Debug|x64 + {77D4F346-ACA5-42C8-8522-5EF176F3ADF1}.Debug|x64.ActiveCfg = Debug|Any CPU + {77D4F346-ACA5-42C8-8522-5EF176F3ADF1}.Debug|x64.Build.0 = Debug|Any CPU {77D4F346-ACA5-42C8-8522-5EF176F3ADF1}.Release Mac|Any CPU.ActiveCfg = Release|Any CPU {77D4F346-ACA5-42C8-8522-5EF176F3ADF1}.Release Mac|Any CPU.Build.0 = Release|Any CPU {77D4F346-ACA5-42C8-8522-5EF176F3ADF1}.Release Mac|x64.ActiveCfg = Release|x64 @@ -1380,8 +1481,8 @@ Global {DEBC2174-5E31-4B6E-8680-690D75E50E2D}.Debug Mac|x64.Build.0 = Debug|x64 {DEBC2174-5E31-4B6E-8680-690D75E50E2D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DEBC2174-5E31-4B6E-8680-690D75E50E2D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {DEBC2174-5E31-4B6E-8680-690D75E50E2D}.Debug|x64.ActiveCfg = Debug|x64 - {DEBC2174-5E31-4B6E-8680-690D75E50E2D}.Debug|x64.Build.0 = Debug|x64 + {DEBC2174-5E31-4B6E-8680-690D75E50E2D}.Debug|x64.ActiveCfg = Debug|Any CPU + {DEBC2174-5E31-4B6E-8680-690D75E50E2D}.Debug|x64.Build.0 = Debug|Any CPU {DEBC2174-5E31-4B6E-8680-690D75E50E2D}.Release Mac|Any CPU.ActiveCfg = Release|Any CPU {DEBC2174-5E31-4B6E-8680-690D75E50E2D}.Release Mac|Any CPU.Build.0 = Release|Any CPU {DEBC2174-5E31-4B6E-8680-690D75E50E2D}.Release Mac|x64.ActiveCfg = Release|x64 @@ -1395,8 +1496,8 @@ Global {9A7D7F9A-4FE1-4053-950B-50B43BC81087}.Debug Mac|x64.Build.0 = Debug|x64 {9A7D7F9A-4FE1-4053-950B-50B43BC81087}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9A7D7F9A-4FE1-4053-950B-50B43BC81087}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9A7D7F9A-4FE1-4053-950B-50B43BC81087}.Debug|x64.ActiveCfg = Debug|x64 - {9A7D7F9A-4FE1-4053-950B-50B43BC81087}.Debug|x64.Build.0 = Debug|x64 + {9A7D7F9A-4FE1-4053-950B-50B43BC81087}.Debug|x64.ActiveCfg = Debug|Any CPU + {9A7D7F9A-4FE1-4053-950B-50B43BC81087}.Debug|x64.Build.0 = Debug|Any CPU {9A7D7F9A-4FE1-4053-950B-50B43BC81087}.Release Mac|Any CPU.ActiveCfg = Release|Any CPU {9A7D7F9A-4FE1-4053-950B-50B43BC81087}.Release Mac|Any CPU.Build.0 = Release|Any CPU {9A7D7F9A-4FE1-4053-950B-50B43BC81087}.Release Mac|x64.ActiveCfg = Release|x64 @@ -1742,8 +1843,8 @@ Global {5F8E5DD7-386E-46A6-85E4-1318CBCC4BA1}.Debug Mac|x64.Build.0 = Debug|x64 {5F8E5DD7-386E-46A6-85E4-1318CBCC4BA1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5F8E5DD7-386E-46A6-85E4-1318CBCC4BA1}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5F8E5DD7-386E-46A6-85E4-1318CBCC4BA1}.Debug|x64.ActiveCfg = Debug|x64 - {5F8E5DD7-386E-46A6-85E4-1318CBCC4BA1}.Debug|x64.Build.0 = Debug|x64 + {5F8E5DD7-386E-46A6-85E4-1318CBCC4BA1}.Debug|x64.ActiveCfg = Debug|Any CPU + {5F8E5DD7-386E-46A6-85E4-1318CBCC4BA1}.Debug|x64.Build.0 = Debug|Any CPU {5F8E5DD7-386E-46A6-85E4-1318CBCC4BA1}.Release Mac|Any CPU.ActiveCfg = Release|Any CPU {5F8E5DD7-386E-46A6-85E4-1318CBCC4BA1}.Release Mac|Any CPU.Build.0 = Release|Any CPU {5F8E5DD7-386E-46A6-85E4-1318CBCC4BA1}.Release Mac|x64.ActiveCfg = Release|x64 @@ -1757,8 +1858,8 @@ Global {2568500E-F1BC-440E-9150-DB4820B3FAD6}.Debug Mac|x64.Build.0 = Debug|x64 {2568500E-F1BC-440E-9150-DB4820B3FAD6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2568500E-F1BC-440E-9150-DB4820B3FAD6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2568500E-F1BC-440E-9150-DB4820B3FAD6}.Debug|x64.ActiveCfg = Debug|x64 - {2568500E-F1BC-440E-9150-DB4820B3FAD6}.Debug|x64.Build.0 = Debug|x64 + {2568500E-F1BC-440E-9150-DB4820B3FAD6}.Debug|x64.ActiveCfg = Debug|Any CPU + {2568500E-F1BC-440E-9150-DB4820B3FAD6}.Debug|x64.Build.0 = Debug|Any CPU {2568500E-F1BC-440E-9150-DB4820B3FAD6}.Release Mac|Any CPU.ActiveCfg = Release|Any CPU {2568500E-F1BC-440E-9150-DB4820B3FAD6}.Release Mac|Any CPU.Build.0 = Release|Any CPU {2568500E-F1BC-440E-9150-DB4820B3FAD6}.Release Mac|x64.ActiveCfg = Release|x64 @@ -2093,8 +2194,8 @@ Global {AF51DD10-C0D5-4209-AF55-8F6476EA8A99}.Debug Mac|x64.Build.0 = Debug|Any CPU {AF51DD10-C0D5-4209-AF55-8F6476EA8A99}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {AF51DD10-C0D5-4209-AF55-8F6476EA8A99}.Debug|Any CPU.Build.0 = Debug|Any CPU - {AF51DD10-C0D5-4209-AF55-8F6476EA8A99}.Debug|x64.ActiveCfg = Debug|Any CPU - {AF51DD10-C0D5-4209-AF55-8F6476EA8A99}.Debug|x64.Build.0 = Debug|Any CPU + {AF51DD10-C0D5-4209-AF55-8F6476EA8A99}.Debug|x64.ActiveCfg = Debug|x64 + {AF51DD10-C0D5-4209-AF55-8F6476EA8A99}.Debug|x64.Build.0 = Debug|x64 {AF51DD10-C0D5-4209-AF55-8F6476EA8A99}.Release Mac|Any CPU.ActiveCfg = Debug|Any CPU {AF51DD10-C0D5-4209-AF55-8F6476EA8A99}.Release Mac|Any CPU.Build.0 = Debug|Any CPU {AF51DD10-C0D5-4209-AF55-8F6476EA8A99}.Release Mac|x64.ActiveCfg = Debug|Any CPU @@ -2109,8 +2210,8 @@ Global {A0C9EBE0-A56A-4D07-B6EF-2EEAEC45D6C4}.Debug Mac|x64.Build.0 = Debug|Any CPU {A0C9EBE0-A56A-4D07-B6EF-2EEAEC45D6C4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A0C9EBE0-A56A-4D07-B6EF-2EEAEC45D6C4}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A0C9EBE0-A56A-4D07-B6EF-2EEAEC45D6C4}.Debug|x64.ActiveCfg = Debug|Any CPU - {A0C9EBE0-A56A-4D07-B6EF-2EEAEC45D6C4}.Debug|x64.Build.0 = Debug|Any CPU + {A0C9EBE0-A56A-4D07-B6EF-2EEAEC45D6C4}.Debug|x64.ActiveCfg = Debug|x64 + {A0C9EBE0-A56A-4D07-B6EF-2EEAEC45D6C4}.Debug|x64.Build.0 = Debug|x64 {A0C9EBE0-A56A-4D07-B6EF-2EEAEC45D6C4}.Release Mac|Any CPU.ActiveCfg = Debug|Any CPU {A0C9EBE0-A56A-4D07-B6EF-2EEAEC45D6C4}.Release Mac|Any CPU.Build.0 = Debug|Any CPU {A0C9EBE0-A56A-4D07-B6EF-2EEAEC45D6C4}.Release Mac|x64.ActiveCfg = Debug|Any CPU @@ -2287,6 +2388,278 @@ Global {0D23858F-4CC1-4DCA-9207-5EDB8B6CE9DD}.Release|Any CPU.Build.0 = Release|Any CPU {0D23858F-4CC1-4DCA-9207-5EDB8B6CE9DD}.Release|x64.ActiveCfg = Release|Any CPU {0D23858F-4CC1-4DCA-9207-5EDB8B6CE9DD}.Release|x64.Build.0 = Release|Any CPU + {01F98733-7352-47AD-A594-537D979DE3DE}.Debug Mac|Any CPU.ActiveCfg = Debug|Any CPU + {01F98733-7352-47AD-A594-537D979DE3DE}.Debug Mac|Any CPU.Build.0 = Debug|Any CPU + {01F98733-7352-47AD-A594-537D979DE3DE}.Debug Mac|x64.ActiveCfg = Debug|Any CPU + {01F98733-7352-47AD-A594-537D979DE3DE}.Debug Mac|x64.Build.0 = Debug|Any CPU + {01F98733-7352-47AD-A594-537D979DE3DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {01F98733-7352-47AD-A594-537D979DE3DE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {01F98733-7352-47AD-A594-537D979DE3DE}.Debug|x64.ActiveCfg = Debug|Any CPU + {01F98733-7352-47AD-A594-537D979DE3DE}.Debug|x64.Build.0 = Debug|Any CPU + {01F98733-7352-47AD-A594-537D979DE3DE}.Release Mac|Any CPU.ActiveCfg = Release|Any CPU + {01F98733-7352-47AD-A594-537D979DE3DE}.Release Mac|Any CPU.Build.0 = Release|Any CPU + {01F98733-7352-47AD-A594-537D979DE3DE}.Release Mac|x64.ActiveCfg = Release|Any CPU + {01F98733-7352-47AD-A594-537D979DE3DE}.Release Mac|x64.Build.0 = Release|Any CPU + {01F98733-7352-47AD-A594-537D979DE3DE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {01F98733-7352-47AD-A594-537D979DE3DE}.Release|Any CPU.Build.0 = Release|Any CPU + {01F98733-7352-47AD-A594-537D979DE3DE}.Release|x64.ActiveCfg = Release|Any CPU + {01F98733-7352-47AD-A594-537D979DE3DE}.Release|x64.Build.0 = Release|Any CPU + {83EAD6F0-3CB3-456A-AD81-072127D0DE0E}.Debug Mac|Any CPU.ActiveCfg = Debug|Any CPU + {83EAD6F0-3CB3-456A-AD81-072127D0DE0E}.Debug Mac|Any CPU.Build.0 = Debug|Any CPU + {83EAD6F0-3CB3-456A-AD81-072127D0DE0E}.Debug Mac|x64.ActiveCfg = Debug|Any CPU + {83EAD6F0-3CB3-456A-AD81-072127D0DE0E}.Debug Mac|x64.Build.0 = Debug|Any CPU + {83EAD6F0-3CB3-456A-AD81-072127D0DE0E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {83EAD6F0-3CB3-456A-AD81-072127D0DE0E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {83EAD6F0-3CB3-456A-AD81-072127D0DE0E}.Debug|x64.ActiveCfg = Debug|Any CPU + {83EAD6F0-3CB3-456A-AD81-072127D0DE0E}.Debug|x64.Build.0 = Debug|Any CPU + {83EAD6F0-3CB3-456A-AD81-072127D0DE0E}.Release Mac|Any CPU.ActiveCfg = Release|Any CPU + {83EAD6F0-3CB3-456A-AD81-072127D0DE0E}.Release Mac|Any CPU.Build.0 = Release|Any CPU + {83EAD6F0-3CB3-456A-AD81-072127D0DE0E}.Release Mac|x64.ActiveCfg = Release|Any CPU + {83EAD6F0-3CB3-456A-AD81-072127D0DE0E}.Release Mac|x64.Build.0 = Release|Any CPU + {83EAD6F0-3CB3-456A-AD81-072127D0DE0E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {83EAD6F0-3CB3-456A-AD81-072127D0DE0E}.Release|Any CPU.Build.0 = Release|Any CPU + {83EAD6F0-3CB3-456A-AD81-072127D0DE0E}.Release|x64.ActiveCfg = Release|Any CPU + {83EAD6F0-3CB3-456A-AD81-072127D0DE0E}.Release|x64.Build.0 = Release|Any CPU + {26391930-F86F-47E0-A5F6-B89919E38CE1}.Debug Mac|Any CPU.ActiveCfg = Debug|Any CPU + {26391930-F86F-47E0-A5F6-B89919E38CE1}.Debug Mac|Any CPU.Build.0 = Debug|Any CPU + {26391930-F86F-47E0-A5F6-B89919E38CE1}.Debug Mac|x64.ActiveCfg = Debug|Any CPU + {26391930-F86F-47E0-A5F6-B89919E38CE1}.Debug Mac|x64.Build.0 = Debug|Any CPU + {26391930-F86F-47E0-A5F6-B89919E38CE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {26391930-F86F-47E0-A5F6-B89919E38CE1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {26391930-F86F-47E0-A5F6-B89919E38CE1}.Debug|x64.ActiveCfg = Debug|Any CPU + {26391930-F86F-47E0-A5F6-B89919E38CE1}.Debug|x64.Build.0 = Debug|Any CPU + {26391930-F86F-47E0-A5F6-B89919E38CE1}.Release Mac|Any CPU.ActiveCfg = Release|Any CPU + {26391930-F86F-47E0-A5F6-B89919E38CE1}.Release Mac|Any CPU.Build.0 = Release|Any CPU + {26391930-F86F-47E0-A5F6-B89919E38CE1}.Release Mac|x64.ActiveCfg = Release|Any CPU + {26391930-F86F-47E0-A5F6-B89919E38CE1}.Release Mac|x64.Build.0 = Release|Any CPU + {26391930-F86F-47E0-A5F6-B89919E38CE1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {26391930-F86F-47E0-A5F6-B89919E38CE1}.Release|Any CPU.Build.0 = Release|Any CPU + {26391930-F86F-47E0-A5F6-B89919E38CE1}.Release|x64.ActiveCfg = Release|Any CPU + {26391930-F86F-47E0-A5F6-B89919E38CE1}.Release|x64.Build.0 = Release|Any CPU + {D81C0B87-F0C1-4297-A147-02F001FB7E1E}.Debug Mac|Any CPU.ActiveCfg = Debug|Any CPU + {D81C0B87-F0C1-4297-A147-02F001FB7E1E}.Debug Mac|Any CPU.Build.0 = Debug|Any CPU + {D81C0B87-F0C1-4297-A147-02F001FB7E1E}.Debug Mac|x64.ActiveCfg = Debug|Any CPU + {D81C0B87-F0C1-4297-A147-02F001FB7E1E}.Debug Mac|x64.Build.0 = Debug|Any CPU + {D81C0B87-F0C1-4297-A147-02F001FB7E1E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D81C0B87-F0C1-4297-A147-02F001FB7E1E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D81C0B87-F0C1-4297-A147-02F001FB7E1E}.Debug|x64.ActiveCfg = Debug|Any CPU + {D81C0B87-F0C1-4297-A147-02F001FB7E1E}.Debug|x64.Build.0 = Debug|Any CPU + {D81C0B87-F0C1-4297-A147-02F001FB7E1E}.Release Mac|Any CPU.ActiveCfg = Release|Any CPU + {D81C0B87-F0C1-4297-A147-02F001FB7E1E}.Release Mac|Any CPU.Build.0 = Release|Any CPU + {D81C0B87-F0C1-4297-A147-02F001FB7E1E}.Release Mac|x64.ActiveCfg = Release|Any CPU + {D81C0B87-F0C1-4297-A147-02F001FB7E1E}.Release Mac|x64.Build.0 = Release|Any CPU + {D81C0B87-F0C1-4297-A147-02F001FB7E1E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D81C0B87-F0C1-4297-A147-02F001FB7E1E}.Release|Any CPU.Build.0 = Release|Any CPU + {D81C0B87-F0C1-4297-A147-02F001FB7E1E}.Release|x64.ActiveCfg = Release|Any CPU + {D81C0B87-F0C1-4297-A147-02F001FB7E1E}.Release|x64.Build.0 = Release|Any CPU + {C9D4CA21-182B-4ED2-81BB-280A6FD713F6}.Debug Mac|Any CPU.ActiveCfg = Debug|Any CPU + {C9D4CA21-182B-4ED2-81BB-280A6FD713F6}.Debug Mac|Any CPU.Build.0 = Debug|Any CPU + {C9D4CA21-182B-4ED2-81BB-280A6FD713F6}.Debug Mac|x64.ActiveCfg = Debug|Any CPU + {C9D4CA21-182B-4ED2-81BB-280A6FD713F6}.Debug Mac|x64.Build.0 = Debug|Any CPU + {C9D4CA21-182B-4ED2-81BB-280A6FD713F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C9D4CA21-182B-4ED2-81BB-280A6FD713F6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C9D4CA21-182B-4ED2-81BB-280A6FD713F6}.Debug|x64.ActiveCfg = Debug|Any CPU + {C9D4CA21-182B-4ED2-81BB-280A6FD713F6}.Debug|x64.Build.0 = Debug|Any CPU + {C9D4CA21-182B-4ED2-81BB-280A6FD713F6}.Release Mac|Any CPU.ActiveCfg = Release|Any CPU + {C9D4CA21-182B-4ED2-81BB-280A6FD713F6}.Release Mac|Any CPU.Build.0 = Release|Any CPU + {C9D4CA21-182B-4ED2-81BB-280A6FD713F6}.Release Mac|x64.ActiveCfg = Release|Any CPU + {C9D4CA21-182B-4ED2-81BB-280A6FD713F6}.Release Mac|x64.Build.0 = Release|Any CPU + {C9D4CA21-182B-4ED2-81BB-280A6FD713F6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C9D4CA21-182B-4ED2-81BB-280A6FD713F6}.Release|Any CPU.Build.0 = Release|Any CPU + {C9D4CA21-182B-4ED2-81BB-280A6FD713F6}.Release|x64.ActiveCfg = Release|Any CPU + {C9D4CA21-182B-4ED2-81BB-280A6FD713F6}.Release|x64.Build.0 = Release|Any CPU + {7291B93C-615D-42DE-B8C1-3F9DF643E0FC}.Debug Mac|Any CPU.ActiveCfg = Debug|Any CPU + {7291B93C-615D-42DE-B8C1-3F9DF643E0FC}.Debug Mac|Any CPU.Build.0 = Debug|Any CPU + {7291B93C-615D-42DE-B8C1-3F9DF643E0FC}.Debug Mac|x64.ActiveCfg = Debug|Any CPU + {7291B93C-615D-42DE-B8C1-3F9DF643E0FC}.Debug Mac|x64.Build.0 = Debug|Any CPU + {7291B93C-615D-42DE-B8C1-3F9DF643E0FC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7291B93C-615D-42DE-B8C1-3F9DF643E0FC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7291B93C-615D-42DE-B8C1-3F9DF643E0FC}.Debug|x64.ActiveCfg = Debug|Any CPU + {7291B93C-615D-42DE-B8C1-3F9DF643E0FC}.Debug|x64.Build.0 = Debug|Any CPU + {7291B93C-615D-42DE-B8C1-3F9DF643E0FC}.Release Mac|Any CPU.ActiveCfg = Release|Any CPU + {7291B93C-615D-42DE-B8C1-3F9DF643E0FC}.Release Mac|Any CPU.Build.0 = Release|Any CPU + {7291B93C-615D-42DE-B8C1-3F9DF643E0FC}.Release Mac|x64.ActiveCfg = Release|Any CPU + {7291B93C-615D-42DE-B8C1-3F9DF643E0FC}.Release Mac|x64.Build.0 = Release|Any CPU + {7291B93C-615D-42DE-B8C1-3F9DF643E0FC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7291B93C-615D-42DE-B8C1-3F9DF643E0FC}.Release|Any CPU.Build.0 = Release|Any CPU + {7291B93C-615D-42DE-B8C1-3F9DF643E0FC}.Release|x64.ActiveCfg = Release|Any CPU + {7291B93C-615D-42DE-B8C1-3F9DF643E0FC}.Release|x64.Build.0 = Release|Any CPU + {8AEF06C0-CA5C-4460-BC2D-ADE5F35D0434}.Debug Mac|Any CPU.ActiveCfg = Debug|Any CPU + {8AEF06C0-CA5C-4460-BC2D-ADE5F35D0434}.Debug Mac|Any CPU.Build.0 = Debug|Any CPU + {8AEF06C0-CA5C-4460-BC2D-ADE5F35D0434}.Debug Mac|x64.ActiveCfg = Debug|Any CPU + {8AEF06C0-CA5C-4460-BC2D-ADE5F35D0434}.Debug Mac|x64.Build.0 = Debug|Any CPU + {8AEF06C0-CA5C-4460-BC2D-ADE5F35D0434}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8AEF06C0-CA5C-4460-BC2D-ADE5F35D0434}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8AEF06C0-CA5C-4460-BC2D-ADE5F35D0434}.Debug|x64.ActiveCfg = Debug|Any CPU + {8AEF06C0-CA5C-4460-BC2D-ADE5F35D0434}.Debug|x64.Build.0 = Debug|Any CPU + {8AEF06C0-CA5C-4460-BC2D-ADE5F35D0434}.Release Mac|Any CPU.ActiveCfg = Release|Any CPU + {8AEF06C0-CA5C-4460-BC2D-ADE5F35D0434}.Release Mac|Any CPU.Build.0 = Release|Any CPU + {8AEF06C0-CA5C-4460-BC2D-ADE5F35D0434}.Release Mac|x64.ActiveCfg = Release|Any CPU + {8AEF06C0-CA5C-4460-BC2D-ADE5F35D0434}.Release Mac|x64.Build.0 = Release|Any CPU + {8AEF06C0-CA5C-4460-BC2D-ADE5F35D0434}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8AEF06C0-CA5C-4460-BC2D-ADE5F35D0434}.Release|Any CPU.Build.0 = Release|Any CPU + {8AEF06C0-CA5C-4460-BC2D-ADE5F35D0434}.Release|x64.ActiveCfg = Release|Any CPU + {8AEF06C0-CA5C-4460-BC2D-ADE5F35D0434}.Release|x64.Build.0 = Release|Any CPU + {1E2644A9-6B31-4350-8772-CEAAD6EE0B21}.Debug Mac|Any CPU.ActiveCfg = Debug|Any CPU + {1E2644A9-6B31-4350-8772-CEAAD6EE0B21}.Debug Mac|Any CPU.Build.0 = Debug|Any CPU + {1E2644A9-6B31-4350-8772-CEAAD6EE0B21}.Debug Mac|x64.ActiveCfg = Debug|Any CPU + {1E2644A9-6B31-4350-8772-CEAAD6EE0B21}.Debug Mac|x64.Build.0 = Debug|Any CPU + {1E2644A9-6B31-4350-8772-CEAAD6EE0B21}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1E2644A9-6B31-4350-8772-CEAAD6EE0B21}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1E2644A9-6B31-4350-8772-CEAAD6EE0B21}.Debug|x64.ActiveCfg = Debug|Any CPU + {1E2644A9-6B31-4350-8772-CEAAD6EE0B21}.Debug|x64.Build.0 = Debug|Any CPU + {1E2644A9-6B31-4350-8772-CEAAD6EE0B21}.Release Mac|Any CPU.ActiveCfg = Debug|Any CPU + {1E2644A9-6B31-4350-8772-CEAAD6EE0B21}.Release Mac|Any CPU.Build.0 = Debug|Any CPU + {1E2644A9-6B31-4350-8772-CEAAD6EE0B21}.Release Mac|x64.ActiveCfg = Debug|Any CPU + {1E2644A9-6B31-4350-8772-CEAAD6EE0B21}.Release Mac|x64.Build.0 = Debug|Any CPU + {1E2644A9-6B31-4350-8772-CEAAD6EE0B21}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1E2644A9-6B31-4350-8772-CEAAD6EE0B21}.Release|Any CPU.Build.0 = Release|Any CPU + {1E2644A9-6B31-4350-8772-CEAAD6EE0B21}.Release|x64.ActiveCfg = Release|Any CPU + {1E2644A9-6B31-4350-8772-CEAAD6EE0B21}.Release|x64.Build.0 = Release|Any CPU + {65A2F556-F14A-49F3-8A92-7F2E1E7ED3B5}.Debug Mac|Any CPU.ActiveCfg = Debug|Any CPU + {65A2F556-F14A-49F3-8A92-7F2E1E7ED3B5}.Debug Mac|Any CPU.Build.0 = Debug|Any CPU + {65A2F556-F14A-49F3-8A92-7F2E1E7ED3B5}.Debug Mac|x64.ActiveCfg = Debug|Any CPU + {65A2F556-F14A-49F3-8A92-7F2E1E7ED3B5}.Debug Mac|x64.Build.0 = Debug|Any CPU + {65A2F556-F14A-49F3-8A92-7F2E1E7ED3B5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {65A2F556-F14A-49F3-8A92-7F2E1E7ED3B5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {65A2F556-F14A-49F3-8A92-7F2E1E7ED3B5}.Debug|x64.ActiveCfg = Debug|Any CPU + {65A2F556-F14A-49F3-8A92-7F2E1E7ED3B5}.Debug|x64.Build.0 = Debug|Any CPU + {65A2F556-F14A-49F3-8A92-7F2E1E7ED3B5}.Release Mac|Any CPU.ActiveCfg = Debug|Any CPU + {65A2F556-F14A-49F3-8A92-7F2E1E7ED3B5}.Release Mac|Any CPU.Build.0 = Debug|Any CPU + {65A2F556-F14A-49F3-8A92-7F2E1E7ED3B5}.Release Mac|x64.ActiveCfg = Debug|Any CPU + {65A2F556-F14A-49F3-8A92-7F2E1E7ED3B5}.Release Mac|x64.Build.0 = Debug|Any CPU + {65A2F556-F14A-49F3-8A92-7F2E1E7ED3B5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {65A2F556-F14A-49F3-8A92-7F2E1E7ED3B5}.Release|Any CPU.Build.0 = Release|Any CPU + {65A2F556-F14A-49F3-8A92-7F2E1E7ED3B5}.Release|x64.ActiveCfg = Release|Any CPU + {65A2F556-F14A-49F3-8A92-7F2E1E7ED3B5}.Release|x64.Build.0 = Release|Any CPU + {9C1ECA1D-DE98-4FB7-92D0-FC45BB308E5D}.Debug Mac|Any CPU.ActiveCfg = Debug|Any CPU + {9C1ECA1D-DE98-4FB7-92D0-FC45BB308E5D}.Debug Mac|Any CPU.Build.0 = Debug|Any CPU + {9C1ECA1D-DE98-4FB7-92D0-FC45BB308E5D}.Debug Mac|x64.ActiveCfg = Debug|Any CPU + {9C1ECA1D-DE98-4FB7-92D0-FC45BB308E5D}.Debug Mac|x64.Build.0 = Debug|Any CPU + {9C1ECA1D-DE98-4FB7-92D0-FC45BB308E5D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9C1ECA1D-DE98-4FB7-92D0-FC45BB308E5D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9C1ECA1D-DE98-4FB7-92D0-FC45BB308E5D}.Debug|x64.ActiveCfg = Debug|Any CPU + {9C1ECA1D-DE98-4FB7-92D0-FC45BB308E5D}.Debug|x64.Build.0 = Debug|Any CPU + {9C1ECA1D-DE98-4FB7-92D0-FC45BB308E5D}.Release Mac|Any CPU.ActiveCfg = Debug|Any CPU + {9C1ECA1D-DE98-4FB7-92D0-FC45BB308E5D}.Release Mac|Any CPU.Build.0 = Debug|Any CPU + {9C1ECA1D-DE98-4FB7-92D0-FC45BB308E5D}.Release Mac|x64.ActiveCfg = Debug|Any CPU + {9C1ECA1D-DE98-4FB7-92D0-FC45BB308E5D}.Release Mac|x64.Build.0 = Debug|Any CPU + {9C1ECA1D-DE98-4FB7-92D0-FC45BB308E5D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9C1ECA1D-DE98-4FB7-92D0-FC45BB308E5D}.Release|Any CPU.Build.0 = Release|Any CPU + {9C1ECA1D-DE98-4FB7-92D0-FC45BB308E5D}.Release|x64.ActiveCfg = Release|Any CPU + {9C1ECA1D-DE98-4FB7-92D0-FC45BB308E5D}.Release|x64.Build.0 = Release|Any CPU + {A97F7177-86C7-4B38-A6ED-DA51BF762471}.Debug Mac|Any CPU.ActiveCfg = Debug|Any CPU + {A97F7177-86C7-4B38-A6ED-DA51BF762471}.Debug Mac|Any CPU.Build.0 = Debug|Any CPU + {A97F7177-86C7-4B38-A6ED-DA51BF762471}.Debug Mac|x64.ActiveCfg = Debug|Any CPU + {A97F7177-86C7-4B38-A6ED-DA51BF762471}.Debug Mac|x64.Build.0 = Debug|Any CPU + {A97F7177-86C7-4B38-A6ED-DA51BF762471}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A97F7177-86C7-4B38-A6ED-DA51BF762471}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A97F7177-86C7-4B38-A6ED-DA51BF762471}.Debug|x64.ActiveCfg = Debug|Any CPU + {A97F7177-86C7-4B38-A6ED-DA51BF762471}.Debug|x64.Build.0 = Debug|Any CPU + {A97F7177-86C7-4B38-A6ED-DA51BF762471}.Release Mac|Any CPU.ActiveCfg = Release|Any CPU + {A97F7177-86C7-4B38-A6ED-DA51BF762471}.Release Mac|Any CPU.Build.0 = Release|Any CPU + {A97F7177-86C7-4B38-A6ED-DA51BF762471}.Release Mac|x64.ActiveCfg = Release|Any CPU + {A97F7177-86C7-4B38-A6ED-DA51BF762471}.Release Mac|x64.Build.0 = Release|Any CPU + {A97F7177-86C7-4B38-A6ED-DA51BF762471}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A97F7177-86C7-4B38-A6ED-DA51BF762471}.Release|Any CPU.Build.0 = Release|Any CPU + {A97F7177-86C7-4B38-A6ED-DA51BF762471}.Release|x64.ActiveCfg = Release|Any CPU + {A97F7177-86C7-4B38-A6ED-DA51BF762471}.Release|x64.Build.0 = Release|Any CPU + {139F7A79-69E4-4B8A-B2A5-6A30A66C495C}.Debug Mac|Any CPU.ActiveCfg = Debug|Any CPU + {139F7A79-69E4-4B8A-B2A5-6A30A66C495C}.Debug Mac|Any CPU.Build.0 = Debug|Any CPU + {139F7A79-69E4-4B8A-B2A5-6A30A66C495C}.Debug Mac|x64.ActiveCfg = Debug|Any CPU + {139F7A79-69E4-4B8A-B2A5-6A30A66C495C}.Debug Mac|x64.Build.0 = Debug|Any CPU + {139F7A79-69E4-4B8A-B2A5-6A30A66C495C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {139F7A79-69E4-4B8A-B2A5-6A30A66C495C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {139F7A79-69E4-4B8A-B2A5-6A30A66C495C}.Debug|x64.ActiveCfg = Debug|Any CPU + {139F7A79-69E4-4B8A-B2A5-6A30A66C495C}.Debug|x64.Build.0 = Debug|Any CPU + {139F7A79-69E4-4B8A-B2A5-6A30A66C495C}.Release Mac|Any CPU.ActiveCfg = Release|Any CPU + {139F7A79-69E4-4B8A-B2A5-6A30A66C495C}.Release Mac|Any CPU.Build.0 = Release|Any CPU + {139F7A79-69E4-4B8A-B2A5-6A30A66C495C}.Release Mac|x64.ActiveCfg = Release|Any CPU + {139F7A79-69E4-4B8A-B2A5-6A30A66C495C}.Release Mac|x64.Build.0 = Release|Any CPU + {139F7A79-69E4-4B8A-B2A5-6A30A66C495C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {139F7A79-69E4-4B8A-B2A5-6A30A66C495C}.Release|Any CPU.Build.0 = Release|Any CPU + {139F7A79-69E4-4B8A-B2A5-6A30A66C495C}.Release|x64.ActiveCfg = Release|Any CPU + {139F7A79-69E4-4B8A-B2A5-6A30A66C495C}.Release|x64.Build.0 = Release|Any CPU + {7DFF1591-237D-499E-A767-EE37B93FB958}.Debug Mac|Any CPU.ActiveCfg = Debug|Any CPU + {7DFF1591-237D-499E-A767-EE37B93FB958}.Debug Mac|Any CPU.Build.0 = Debug|Any CPU + {7DFF1591-237D-499E-A767-EE37B93FB958}.Debug Mac|x64.ActiveCfg = Debug|Any CPU + {7DFF1591-237D-499E-A767-EE37B93FB958}.Debug Mac|x64.Build.0 = Debug|Any CPU + {7DFF1591-237D-499E-A767-EE37B93FB958}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7DFF1591-237D-499E-A767-EE37B93FB958}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7DFF1591-237D-499E-A767-EE37B93FB958}.Debug|x64.ActiveCfg = Debug|Any CPU + {7DFF1591-237D-499E-A767-EE37B93FB958}.Debug|x64.Build.0 = Debug|Any CPU + {7DFF1591-237D-499E-A767-EE37B93FB958}.Release Mac|Any CPU.ActiveCfg = Release|Any CPU + {7DFF1591-237D-499E-A767-EE37B93FB958}.Release Mac|Any CPU.Build.0 = Release|Any CPU + {7DFF1591-237D-499E-A767-EE37B93FB958}.Release Mac|x64.ActiveCfg = Release|Any CPU + {7DFF1591-237D-499E-A767-EE37B93FB958}.Release Mac|x64.Build.0 = Release|Any CPU + {7DFF1591-237D-499E-A767-EE37B93FB958}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7DFF1591-237D-499E-A767-EE37B93FB958}.Release|Any CPU.Build.0 = Release|Any CPU + {7DFF1591-237D-499E-A767-EE37B93FB958}.Release|x64.ActiveCfg = Release|Any CPU + {7DFF1591-237D-499E-A767-EE37B93FB958}.Release|x64.Build.0 = Release|Any CPU + {89C4CBC7-1606-40DE-B6DA-FBE3AAC98395}.Debug Mac|Any CPU.ActiveCfg = Debug|Any CPU + {89C4CBC7-1606-40DE-B6DA-FBE3AAC98395}.Debug Mac|Any CPU.Build.0 = Debug|Any CPU + {89C4CBC7-1606-40DE-B6DA-FBE3AAC98395}.Debug Mac|x64.ActiveCfg = Debug|Any CPU + {89C4CBC7-1606-40DE-B6DA-FBE3AAC98395}.Debug Mac|x64.Build.0 = Debug|Any CPU + {89C4CBC7-1606-40DE-B6DA-FBE3AAC98395}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {89C4CBC7-1606-40DE-B6DA-FBE3AAC98395}.Debug|Any CPU.Build.0 = Debug|Any CPU + {89C4CBC7-1606-40DE-B6DA-FBE3AAC98395}.Debug|x64.ActiveCfg = Debug|Any CPU + {89C4CBC7-1606-40DE-B6DA-FBE3AAC98395}.Debug|x64.Build.0 = Debug|Any CPU + {89C4CBC7-1606-40DE-B6DA-FBE3AAC98395}.Release Mac|Any CPU.ActiveCfg = Debug|Any CPU + {89C4CBC7-1606-40DE-B6DA-FBE3AAC98395}.Release Mac|Any CPU.Build.0 = Debug|Any CPU + {89C4CBC7-1606-40DE-B6DA-FBE3AAC98395}.Release Mac|x64.ActiveCfg = Debug|Any CPU + {89C4CBC7-1606-40DE-B6DA-FBE3AAC98395}.Release Mac|x64.Build.0 = Debug|Any CPU + {89C4CBC7-1606-40DE-B6DA-FBE3AAC98395}.Release|Any CPU.ActiveCfg = Release|Any CPU + {89C4CBC7-1606-40DE-B6DA-FBE3AAC98395}.Release|Any CPU.Build.0 = Release|Any CPU + {89C4CBC7-1606-40DE-B6DA-FBE3AAC98395}.Release|x64.ActiveCfg = Release|Any CPU + {89C4CBC7-1606-40DE-B6DA-FBE3AAC98395}.Release|x64.Build.0 = Release|Any CPU + {11F7D41B-AFCA-4D29-BC08-285A14BF3A3B}.Debug Mac|Any CPU.ActiveCfg = Debug|Any CPU + {11F7D41B-AFCA-4D29-BC08-285A14BF3A3B}.Debug Mac|Any CPU.Build.0 = Debug|Any CPU + {11F7D41B-AFCA-4D29-BC08-285A14BF3A3B}.Debug Mac|x64.ActiveCfg = Debug|Any CPU + {11F7D41B-AFCA-4D29-BC08-285A14BF3A3B}.Debug Mac|x64.Build.0 = Debug|Any CPU + {11F7D41B-AFCA-4D29-BC08-285A14BF3A3B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {11F7D41B-AFCA-4D29-BC08-285A14BF3A3B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {11F7D41B-AFCA-4D29-BC08-285A14BF3A3B}.Debug|x64.ActiveCfg = Debug|Any CPU + {11F7D41B-AFCA-4D29-BC08-285A14BF3A3B}.Debug|x64.Build.0 = Debug|Any CPU + {11F7D41B-AFCA-4D29-BC08-285A14BF3A3B}.Release Mac|Any CPU.ActiveCfg = Debug|Any CPU + {11F7D41B-AFCA-4D29-BC08-285A14BF3A3B}.Release Mac|Any CPU.Build.0 = Debug|Any CPU + {11F7D41B-AFCA-4D29-BC08-285A14BF3A3B}.Release Mac|x64.ActiveCfg = Debug|Any CPU + {11F7D41B-AFCA-4D29-BC08-285A14BF3A3B}.Release Mac|x64.Build.0 = Debug|Any CPU + {11F7D41B-AFCA-4D29-BC08-285A14BF3A3B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {11F7D41B-AFCA-4D29-BC08-285A14BF3A3B}.Release|Any CPU.Build.0 = Release|Any CPU + {11F7D41B-AFCA-4D29-BC08-285A14BF3A3B}.Release|x64.ActiveCfg = Release|Any CPU + {11F7D41B-AFCA-4D29-BC08-285A14BF3A3B}.Release|x64.Build.0 = Release|Any CPU + {7420652C-3046-4F38-BE64-9B9E69D76FA2}.Debug Mac|Any CPU.ActiveCfg = Debug|Any CPU + {7420652C-3046-4F38-BE64-9B9E69D76FA2}.Debug Mac|Any CPU.Build.0 = Debug|Any CPU + {7420652C-3046-4F38-BE64-9B9E69D76FA2}.Debug Mac|x64.ActiveCfg = Debug|Any CPU + {7420652C-3046-4F38-BE64-9B9E69D76FA2}.Debug Mac|x64.Build.0 = Debug|Any CPU + {7420652C-3046-4F38-BE64-9B9E69D76FA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7420652C-3046-4F38-BE64-9B9E69D76FA2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7420652C-3046-4F38-BE64-9B9E69D76FA2}.Debug|x64.ActiveCfg = Debug|Any CPU + {7420652C-3046-4F38-BE64-9B9E69D76FA2}.Debug|x64.Build.0 = Debug|Any CPU + {7420652C-3046-4F38-BE64-9B9E69D76FA2}.Release Mac|Any CPU.ActiveCfg = Debug|Any CPU + {7420652C-3046-4F38-BE64-9B9E69D76FA2}.Release Mac|Any CPU.Build.0 = Debug|Any CPU + {7420652C-3046-4F38-BE64-9B9E69D76FA2}.Release Mac|x64.ActiveCfg = Debug|Any CPU + {7420652C-3046-4F38-BE64-9B9E69D76FA2}.Release Mac|x64.Build.0 = Debug|Any CPU + {7420652C-3046-4F38-BE64-9B9E69D76FA2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7420652C-3046-4F38-BE64-9B9E69D76FA2}.Release|Any CPU.Build.0 = Release|Any CPU + {7420652C-3046-4F38-BE64-9B9E69D76FA2}.Release|x64.ActiveCfg = Release|Any CPU + {7420652C-3046-4F38-BE64-9B9E69D76FA2}.Release|x64.Build.0 = Release|Any CPU + {3973D572-5E24-476F-B058-8022D826B793}.Debug Mac|Any CPU.ActiveCfg = Debug|Any CPU + {3973D572-5E24-476F-B058-8022D826B793}.Debug Mac|Any CPU.Build.0 = Debug|Any CPU + {3973D572-5E24-476F-B058-8022D826B793}.Debug Mac|x64.ActiveCfg = Debug|Any CPU + {3973D572-5E24-476F-B058-8022D826B793}.Debug Mac|x64.Build.0 = Debug|Any CPU + {3973D572-5E24-476F-B058-8022D826B793}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3973D572-5E24-476F-B058-8022D826B793}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3973D572-5E24-476F-B058-8022D826B793}.Debug|x64.ActiveCfg = Debug|Any CPU + {3973D572-5E24-476F-B058-8022D826B793}.Debug|x64.Build.0 = Debug|Any CPU + {3973D572-5E24-476F-B058-8022D826B793}.Release Mac|Any CPU.ActiveCfg = Debug|Any CPU + {3973D572-5E24-476F-B058-8022D826B793}.Release Mac|Any CPU.Build.0 = Debug|Any CPU + {3973D572-5E24-476F-B058-8022D826B793}.Release Mac|x64.ActiveCfg = Debug|Any CPU + {3973D572-5E24-476F-B058-8022D826B793}.Release Mac|x64.Build.0 = Debug|Any CPU + {3973D572-5E24-476F-B058-8022D826B793}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3973D572-5E24-476F-B058-8022D826B793}.Release|Any CPU.Build.0 = Release|Any CPU + {3973D572-5E24-476F-B058-8022D826B793}.Release|x64.ActiveCfg = Release|Any CPU + {3973D572-5E24-476F-B058-8022D826B793}.Release|x64.Build.0 = Release|Any CPU {7B02BACC-D9B6-4FFE-A450-7ECB5F71F209}.Debug Mac|Any CPU.ActiveCfg = Debug|Any CPU {7B02BACC-D9B6-4FFE-A450-7ECB5F71F209}.Debug Mac|Any CPU.Build.0 = Debug|Any CPU {7B02BACC-D9B6-4FFE-A450-7ECB5F71F209}.Debug Mac|x64.ActiveCfg = Debug|Any CPU @@ -2399,6 +2772,118 @@ Global {F06E4C37-4076-4272-9CA6-FB505E02CD31}.Release|Any CPU.Build.0 = Release|Any CPU {F06E4C37-4076-4272-9CA6-FB505E02CD31}.Release|x64.ActiveCfg = Release|Any CPU {F06E4C37-4076-4272-9CA6-FB505E02CD31}.Release|x64.Build.0 = Release|Any CPU + {DDBBA313-69A6-40DE-AB3A-79EE5BF32A7E}.Debug Mac|Any CPU.ActiveCfg = Debug|Any CPU + {DDBBA313-69A6-40DE-AB3A-79EE5BF32A7E}.Debug Mac|Any CPU.Build.0 = Debug|Any CPU + {DDBBA313-69A6-40DE-AB3A-79EE5BF32A7E}.Debug Mac|x64.ActiveCfg = Debug|Any CPU + {DDBBA313-69A6-40DE-AB3A-79EE5BF32A7E}.Debug Mac|x64.Build.0 = Debug|Any CPU + {DDBBA313-69A6-40DE-AB3A-79EE5BF32A7E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DDBBA313-69A6-40DE-AB3A-79EE5BF32A7E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DDBBA313-69A6-40DE-AB3A-79EE5BF32A7E}.Debug|x64.ActiveCfg = Debug|Any CPU + {DDBBA313-69A6-40DE-AB3A-79EE5BF32A7E}.Debug|x64.Build.0 = Debug|Any CPU + {DDBBA313-69A6-40DE-AB3A-79EE5BF32A7E}.Release Mac|Any CPU.ActiveCfg = Release|Any CPU + {DDBBA313-69A6-40DE-AB3A-79EE5BF32A7E}.Release Mac|Any CPU.Build.0 = Release|Any CPU + {DDBBA313-69A6-40DE-AB3A-79EE5BF32A7E}.Release Mac|x64.ActiveCfg = Release|Any CPU + {DDBBA313-69A6-40DE-AB3A-79EE5BF32A7E}.Release Mac|x64.Build.0 = Release|Any CPU + {DDBBA313-69A6-40DE-AB3A-79EE5BF32A7E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DDBBA313-69A6-40DE-AB3A-79EE5BF32A7E}.Release|Any CPU.Build.0 = Release|Any CPU + {DDBBA313-69A6-40DE-AB3A-79EE5BF32A7E}.Release|x64.ActiveCfg = Release|Any CPU + {DDBBA313-69A6-40DE-AB3A-79EE5BF32A7E}.Release|x64.Build.0 = Release|Any CPU + {E7FA6A25-A224-4207-846B-75CE8236228D}.Debug Mac|Any CPU.ActiveCfg = Debug|Any CPU + {E7FA6A25-A224-4207-846B-75CE8236228D}.Debug Mac|Any CPU.Build.0 = Debug|Any CPU + {E7FA6A25-A224-4207-846B-75CE8236228D}.Debug Mac|x64.ActiveCfg = Debug|Any CPU + {E7FA6A25-A224-4207-846B-75CE8236228D}.Debug Mac|x64.Build.0 = Debug|Any CPU + {E7FA6A25-A224-4207-846B-75CE8236228D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E7FA6A25-A224-4207-846B-75CE8236228D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E7FA6A25-A224-4207-846B-75CE8236228D}.Debug|x64.ActiveCfg = Debug|Any CPU + {E7FA6A25-A224-4207-846B-75CE8236228D}.Debug|x64.Build.0 = Debug|Any CPU + {E7FA6A25-A224-4207-846B-75CE8236228D}.Release Mac|Any CPU.ActiveCfg = Release|Any CPU + {E7FA6A25-A224-4207-846B-75CE8236228D}.Release Mac|Any CPU.Build.0 = Release|Any CPU + {E7FA6A25-A224-4207-846B-75CE8236228D}.Release Mac|x64.ActiveCfg = Release|Any CPU + {E7FA6A25-A224-4207-846B-75CE8236228D}.Release Mac|x64.Build.0 = Release|Any CPU + {E7FA6A25-A224-4207-846B-75CE8236228D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E7FA6A25-A224-4207-846B-75CE8236228D}.Release|Any CPU.Build.0 = Release|Any CPU + {E7FA6A25-A224-4207-846B-75CE8236228D}.Release|x64.ActiveCfg = Release|Any CPU + {E7FA6A25-A224-4207-846B-75CE8236228D}.Release|x64.Build.0 = Release|Any CPU + {9BCE88BA-1E01-44EE-BF9D-2CB906E63E34}.Debug Mac|Any CPU.ActiveCfg = Debug|Any CPU + {9BCE88BA-1E01-44EE-BF9D-2CB906E63E34}.Debug Mac|Any CPU.Build.0 = Debug|Any CPU + {9BCE88BA-1E01-44EE-BF9D-2CB906E63E34}.Debug Mac|x64.ActiveCfg = Debug|Any CPU + {9BCE88BA-1E01-44EE-BF9D-2CB906E63E34}.Debug Mac|x64.Build.0 = Debug|Any CPU + {9BCE88BA-1E01-44EE-BF9D-2CB906E63E34}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9BCE88BA-1E01-44EE-BF9D-2CB906E63E34}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9BCE88BA-1E01-44EE-BF9D-2CB906E63E34}.Debug|x64.ActiveCfg = Debug|Any CPU + {9BCE88BA-1E01-44EE-BF9D-2CB906E63E34}.Debug|x64.Build.0 = Debug|Any CPU + {9BCE88BA-1E01-44EE-BF9D-2CB906E63E34}.Release Mac|Any CPU.ActiveCfg = Release|Any CPU + {9BCE88BA-1E01-44EE-BF9D-2CB906E63E34}.Release Mac|Any CPU.Build.0 = Release|Any CPU + {9BCE88BA-1E01-44EE-BF9D-2CB906E63E34}.Release Mac|x64.ActiveCfg = Release|Any CPU + {9BCE88BA-1E01-44EE-BF9D-2CB906E63E34}.Release Mac|x64.Build.0 = Release|Any CPU + {9BCE88BA-1E01-44EE-BF9D-2CB906E63E34}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9BCE88BA-1E01-44EE-BF9D-2CB906E63E34}.Release|Any CPU.Build.0 = Release|Any CPU + {9BCE88BA-1E01-44EE-BF9D-2CB906E63E34}.Release|x64.ActiveCfg = Release|Any CPU + {9BCE88BA-1E01-44EE-BF9D-2CB906E63E34}.Release|x64.Build.0 = Release|Any CPU + {2C587020-5F9F-40E1-8AF8-772FE3FC256A}.Debug Mac|Any CPU.ActiveCfg = Debug|Any CPU + {2C587020-5F9F-40E1-8AF8-772FE3FC256A}.Debug Mac|Any CPU.Build.0 = Debug|Any CPU + {2C587020-5F9F-40E1-8AF8-772FE3FC256A}.Debug Mac|x64.ActiveCfg = Debug|Any CPU + {2C587020-5F9F-40E1-8AF8-772FE3FC256A}.Debug Mac|x64.Build.0 = Debug|Any CPU + {2C587020-5F9F-40E1-8AF8-772FE3FC256A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2C587020-5F9F-40E1-8AF8-772FE3FC256A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2C587020-5F9F-40E1-8AF8-772FE3FC256A}.Debug|x64.ActiveCfg = Debug|Any CPU + {2C587020-5F9F-40E1-8AF8-772FE3FC256A}.Debug|x64.Build.0 = Debug|Any CPU + {2C587020-5F9F-40E1-8AF8-772FE3FC256A}.Release Mac|Any CPU.ActiveCfg = Release|Any CPU + {2C587020-5F9F-40E1-8AF8-772FE3FC256A}.Release Mac|Any CPU.Build.0 = Release|Any CPU + {2C587020-5F9F-40E1-8AF8-772FE3FC256A}.Release Mac|x64.ActiveCfg = Release|Any CPU + {2C587020-5F9F-40E1-8AF8-772FE3FC256A}.Release Mac|x64.Build.0 = Release|Any CPU + {2C587020-5F9F-40E1-8AF8-772FE3FC256A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2C587020-5F9F-40E1-8AF8-772FE3FC256A}.Release|Any CPU.Build.0 = Release|Any CPU + {2C587020-5F9F-40E1-8AF8-772FE3FC256A}.Release|x64.ActiveCfg = Release|Any CPU + {2C587020-5F9F-40E1-8AF8-772FE3FC256A}.Release|x64.Build.0 = Release|Any CPU + {C9F084AF-FB70-4F5F-ACEB-DB6818BBECAF}.Debug Mac|Any CPU.ActiveCfg = Debug|Any CPU + {C9F084AF-FB70-4F5F-ACEB-DB6818BBECAF}.Debug Mac|Any CPU.Build.0 = Debug|Any CPU + {C9F084AF-FB70-4F5F-ACEB-DB6818BBECAF}.Debug Mac|x64.ActiveCfg = Debug|Any CPU + {C9F084AF-FB70-4F5F-ACEB-DB6818BBECAF}.Debug Mac|x64.Build.0 = Debug|Any CPU + {C9F084AF-FB70-4F5F-ACEB-DB6818BBECAF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C9F084AF-FB70-4F5F-ACEB-DB6818BBECAF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C9F084AF-FB70-4F5F-ACEB-DB6818BBECAF}.Debug|x64.ActiveCfg = Debug|Any CPU + {C9F084AF-FB70-4F5F-ACEB-DB6818BBECAF}.Debug|x64.Build.0 = Debug|Any CPU + {C9F084AF-FB70-4F5F-ACEB-DB6818BBECAF}.Release Mac|Any CPU.ActiveCfg = Release|Any CPU + {C9F084AF-FB70-4F5F-ACEB-DB6818BBECAF}.Release Mac|Any CPU.Build.0 = Release|Any CPU + {C9F084AF-FB70-4F5F-ACEB-DB6818BBECAF}.Release Mac|x64.ActiveCfg = Release|Any CPU + {C9F084AF-FB70-4F5F-ACEB-DB6818BBECAF}.Release Mac|x64.Build.0 = Release|Any CPU + {C9F084AF-FB70-4F5F-ACEB-DB6818BBECAF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C9F084AF-FB70-4F5F-ACEB-DB6818BBECAF}.Release|Any CPU.Build.0 = Release|Any CPU + {C9F084AF-FB70-4F5F-ACEB-DB6818BBECAF}.Release|x64.ActiveCfg = Release|Any CPU + {C9F084AF-FB70-4F5F-ACEB-DB6818BBECAF}.Release|x64.Build.0 = Release|Any CPU + {4E83BA93-4EC4-4A6E-AD27-8CD9A409FD23}.Debug Mac|Any CPU.ActiveCfg = Debug|Any CPU + {4E83BA93-4EC4-4A6E-AD27-8CD9A409FD23}.Debug Mac|Any CPU.Build.0 = Debug|Any CPU + {4E83BA93-4EC4-4A6E-AD27-8CD9A409FD23}.Debug Mac|x64.ActiveCfg = Debug|Any CPU + {4E83BA93-4EC4-4A6E-AD27-8CD9A409FD23}.Debug Mac|x64.Build.0 = Debug|Any CPU + {4E83BA93-4EC4-4A6E-AD27-8CD9A409FD23}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4E83BA93-4EC4-4A6E-AD27-8CD9A409FD23}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4E83BA93-4EC4-4A6E-AD27-8CD9A409FD23}.Debug|x64.ActiveCfg = Debug|Any CPU + {4E83BA93-4EC4-4A6E-AD27-8CD9A409FD23}.Debug|x64.Build.0 = Debug|Any CPU + {4E83BA93-4EC4-4A6E-AD27-8CD9A409FD23}.Release Mac|Any CPU.ActiveCfg = Release|Any CPU + {4E83BA93-4EC4-4A6E-AD27-8CD9A409FD23}.Release Mac|Any CPU.Build.0 = Release|Any CPU + {4E83BA93-4EC4-4A6E-AD27-8CD9A409FD23}.Release Mac|x64.ActiveCfg = Release|Any CPU + {4E83BA93-4EC4-4A6E-AD27-8CD9A409FD23}.Release Mac|x64.Build.0 = Release|Any CPU + {4E83BA93-4EC4-4A6E-AD27-8CD9A409FD23}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4E83BA93-4EC4-4A6E-AD27-8CD9A409FD23}.Release|Any CPU.Build.0 = Release|Any CPU + {4E83BA93-4EC4-4A6E-AD27-8CD9A409FD23}.Release|x64.ActiveCfg = Release|Any CPU + {4E83BA93-4EC4-4A6E-AD27-8CD9A409FD23}.Release|x64.Build.0 = Release|Any CPU + {A6BAA749-0DBE-49A9-9A9D-FCA0F06EE096}.Debug Mac|Any CPU.ActiveCfg = Debug|Any CPU + {A6BAA749-0DBE-49A9-9A9D-FCA0F06EE096}.Debug Mac|Any CPU.Build.0 = Debug|Any CPU + {A6BAA749-0DBE-49A9-9A9D-FCA0F06EE096}.Debug Mac|x64.ActiveCfg = Debug|Any CPU + {A6BAA749-0DBE-49A9-9A9D-FCA0F06EE096}.Debug Mac|x64.Build.0 = Debug|Any CPU + {A6BAA749-0DBE-49A9-9A9D-FCA0F06EE096}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A6BAA749-0DBE-49A9-9A9D-FCA0F06EE096}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A6BAA749-0DBE-49A9-9A9D-FCA0F06EE096}.Debug|x64.ActiveCfg = Debug|Any CPU + {A6BAA749-0DBE-49A9-9A9D-FCA0F06EE096}.Debug|x64.Build.0 = Debug|Any CPU + {A6BAA749-0DBE-49A9-9A9D-FCA0F06EE096}.Release Mac|Any CPU.ActiveCfg = Release|Any CPU + {A6BAA749-0DBE-49A9-9A9D-FCA0F06EE096}.Release Mac|Any CPU.Build.0 = Release|Any CPU + {A6BAA749-0DBE-49A9-9A9D-FCA0F06EE096}.Release Mac|x64.ActiveCfg = Release|Any CPU + {A6BAA749-0DBE-49A9-9A9D-FCA0F06EE096}.Release Mac|x64.Build.0 = Release|Any CPU + {A6BAA749-0DBE-49A9-9A9D-FCA0F06EE096}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A6BAA749-0DBE-49A9-9A9D-FCA0F06EE096}.Release|Any CPU.Build.0 = Release|Any CPU + {A6BAA749-0DBE-49A9-9A9D-FCA0F06EE096}.Release|x64.ActiveCfg = Release|Any CPU + {A6BAA749-0DBE-49A9-9A9D-FCA0F06EE096}.Release|x64.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -2568,6 +3053,39 @@ Global {0B6B5C52-54EC-461F-8729-6244ACA63646} = {B6C38DB9-7B20-4B7E-BC90-6A8CAFC16807} {2568500E-F1BC-440E-9150-DD4820B3FAD6} = {B6887DDC-B9B9-4B00-95DC-1DD930A1E901} {0D23858F-4CC1-4DCA-9207-5EDB8B6CE9DD} = {DA9DFC36-C53F-4B19-8911-BF7605230BA7} + {4838C66E-8677-4FBD-9609-25376042E981} = {33D19E88-F3AE-4D28-B588-D91CCF9E3BA8} + {33D19E88-F3AE-4D28-B588-D91CCF9E3BA8} = {9DB74760-01DE-4AC1-A81B-BC7784351D22} + {1FE3C60E-7865-40A5-9794-55ECB64F6489} = {9DB74760-01DE-4AC1-A81B-BC7784351D22} + {FD4D6594-D81E-456F-8F2E-35B09E04A755} = {9DB74760-01DE-4AC1-A81B-BC7784351D22} + {D92751C8-1039-4005-90B2-913E55E0B8BD} = {1FE3C60E-7865-40A5-9794-55ECB64F6489} + {2E00592E-558D-492D-88F9-3ECEE4C0C7DA} = {9DB74760-01DE-4AC1-A81B-BC7784351D22} + {01F98733-7352-47AD-A594-537D979DE3DE} = {4838C66E-8677-4FBD-9609-25376042E981} + {DC570FFF-6FE5-47BD-8BC1-B471A6067786} = {4838C66E-8677-4FBD-9609-25376042E981} + {E1C43415-3200-45F4-8BF9-A4DD7D7F2ED6} = {D92751C8-1039-4005-90B2-913E55E0B8BD} + {83EAD6F0-3CB3-456A-AD81-072127D0DE0E} = {D92751C8-1039-4005-90B2-913E55E0B8BD} + {26391930-F86F-47E0-A5F6-B89919E38CE1} = {D92751C8-1039-4005-90B2-913E55E0B8BD} + {D81C0B87-F0C1-4297-A147-02F001FB7E1E} = {FD4D6594-D81E-456F-8F2E-35B09E04A755} + {C9D4CA21-182B-4ED2-81BB-280A6FD713F6} = {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} = {33D19E88-F3AE-4D28-B588-D91CCF9E3BA8} + {1E2644A9-6B31-4350-8772-CEAAD6EE0B21} = {9584AEE5-CD59-46E6-93E6-2DC2B5285B75} + {34C2C062-E43F-4FB5-B839-64BC044CCEF3} = {1FE3C60E-7865-40A5-9794-55ECB64F6489} + {65A2F556-F14A-49F3-8A92-7F2E1E7ED3B5} = {34C2C062-E43F-4FB5-B839-64BC044CCEF3} + {9C1ECA1D-DE98-4FB7-92D0-FC45BB308E5D} = {34C2C062-E43F-4FB5-B839-64BC044CCEF3} + {A97F7177-86C7-4B38-A6ED-DA51BF762471} = {CE4B899D-9C0A-4B5D-B91A-CE62D2327695} + {CE4B899D-9C0A-4B5D-B91A-CE62D2327695} = {33D19E88-F3AE-4D28-B588-D91CCF9E3BA8} + {139F7A79-69E4-4B8A-B2A5-6A30A66C495C} = {CCF48B65-33D1-4E8B-A57B-E03394730B21} + {7DFF1591-237D-499E-A767-EE37B93FB958} = {CCF48B65-33D1-4E8B-A57B-E03394730B21} + {CCF48B65-33D1-4E8B-A57B-E03394730B21} = {1FE3C60E-7865-40A5-9794-55ECB64F6489} + {743489BF-1941-43D5-8AF9-35C56D0DCC34} = {33D19E88-F3AE-4D28-B588-D91CCF9E3BA8} + {89C4CBC7-1606-40DE-B6DA-FBE3AAC98395} = {743489BF-1941-43D5-8AF9-35C56D0DCC34} + {41BC679F-887F-44CF-971D-A5502EE87DB0} = {743489BF-1941-43D5-8AF9-35C56D0DCC34} + {11F7D41B-AFCA-4D29-BC08-285A14BF3A3B} = {2E00592E-558D-492D-88F9-3ECEE4C0C7DA} + {804E065F-914C-414A-AF84-009312C3CFF6} = {1FE3C60E-7865-40A5-9794-55ECB64F6489} + {9ADD1B7A-6401-4202-8613-F668E2FBC0A4} = {804E065F-914C-414A-AF84-009312C3CFF6} + {7420652C-3046-4F38-BE64-9B9E69D76FA2} = {FD4D6594-D81E-456F-8F2E-35B09E04A755} + {3973D572-5E24-476F-B058-8022D826B793} = {326ECEE0-D009-4A65-B24C-00FA343D8B99} {7B02BACC-D9B6-4FFE-A450-7ECB5F71F209} = {C73C19B5-72A3-4C63-8D56-0A7E7DB46CA5} {D607BD0A-9F7F-4C3A-9B9C-FEAD6BA49C7C} = {42A86931-7497-4A34-B2FD-060231CD0A8F} {C0295BF9-9A40-4FCD-BE39-E943985CA3F8} = {925C0BF6-A0B1-4699-9C4B-078E01D652CC} @@ -2575,11 +3093,21 @@ Global {829688CD-CECE-4F6C-A5A0-032BB39CD9E0} = {BE521908-7944-46F3-98BF-B47D34509934} {70DEAA13-6DC8-44A0-B287-9E806A8054F1} = {890F3257-FCC2-4ED8-9180-22B3641B494C} {F06E4C37-4076-4272-9CA6-FB505E02CD31} = {BE521908-7944-46F3-98BF-B47D34509934} + {DDBBA313-69A6-40DE-AB3A-79EE5BF32A7E} = {743489BF-1941-43D5-8AF9-35C56D0DCC34} + {34A6BB15-A030-4C5B-94B2-1A1DFE49334A} = {1FE3C60E-7865-40A5-9794-55ECB64F6489} + {35175682-DA83-4C0A-A49D-B191F5885D8E} = {34A6BB15-A030-4C5B-94B2-1A1DFE49334A} + {E7FA6A25-A224-4207-846B-75CE8236228D} = {34A6BB15-A030-4C5B-94B2-1A1DFE49334A} + {9BCE88BA-1E01-44EE-BF9D-2CB906E63E34} = {34A6BB15-A030-4C5B-94B2-1A1DFE49334A} + {2C587020-5F9F-40E1-8AF8-772FE3FC256A} = {804E065F-914C-414A-AF84-009312C3CFF6} + {C9F084AF-FB70-4F5F-ACEB-DB6818BBECAF} = {804E065F-914C-414A-AF84-009312C3CFF6} + {4E83BA93-4EC4-4A6E-AD27-8CD9A409FD23} = {804E065F-914C-414A-AF84-009312C3CFF6} + {A6BAA749-0DBE-49A9-9A9D-FCA0F06EE096} = {804E065F-914C-414A-AF84-009312C3CFF6} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {1D43D91B-4F01-4A78-8250-CC6F9BD93A14} EndGlobalSection GlobalSection(SharedMSBuildProjectFiles) = preSolution + DUI3-DX\Connectors\Revit\Speckle.Connectors.RevitShared\Speckle.Connectors.RevitShared.projitems*{01f98733-7352-47ad-a594-537d979de3de}*SharedItemsImports = 5 Objects\Converters\ConverterTeklaStructures\ConverterTeklaStructuresShared\ConverterTeklaStructuresShared.projitems*{02a24dd8-e0ca-4657-baa7-b033a4563709}*SharedItemsImports = 5 ConnectorBentley\ConnectorBentleyShared\ConnectorBentleyShared.projitems*{0420d74a-2997-4b92-844b-c3769deaa1ad}*SharedItemsImports = 5 Objects\Converters\ConverterTeklaStructures\ConverterTeklaStructuresShared\ConverterTeklaStructuresShared.projitems*{05f993a6-8651-4801-a732-9a30d1472eef}*SharedItemsImports = 5 @@ -2593,18 +3121,21 @@ Global Objects\Converters\ConverterCSI\ConverterCSIShared\ConverterCSIShared.projitems*{21223ba5-c6e8-405d-b581-106c4726edc0}*SharedItemsImports = 5 ConnectorNavisworks\ConnectorNavisworks\ConnectorNavisworks.Shared.projitems*{2568500e-f1bc-440e-9150-db4820b3fad6}*SharedItemsImports = 5 ConnectorNavisworks\ConnectorNavisworks\ConnectorNavisworks.Shared.projitems*{2568500e-f1bc-440e-9150-dd4820b3fad6}*SharedItemsImports = 5 + DUI3-DX\Converters\Revit\Speckle.Converters.RevitShared\Speckle.Converters.RevitShared.projitems*{26391930-f86f-47e0-a5f6-b89919e38ce1}*SharedItemsImports = 5 ConnectorAutocadCivil\ConnectorAutocadCivil\ConnectorAutocadCivilShared.projitems*{2639e37d-80d3-415a-b4d1-20d7f321f27f}*SharedItemsImports = 5 Objects\Converters\ConverterRhinoGh\ConverterRhinoGhShared\ConverterRhinoGhShared.projitems*{26eca1be-f5b2-4a41-9658-46a4a917bfe6}*SharedItemsImports = 5 ConnectorRevit\ConnectorRevit\ConnectorRevit.projitems*{27a79aca-7ea8-4406-8bb8-216578cc3ab7}*SharedItemsImports = 5 ConnectorTeklaStructures\ConnectorTeklaStructuresShared\ConnectorTeklaStructuresShared.projitems*{28e2ea7f-ffd1-4e13-9165-0243b5ac82f5}*SharedItemsImports = 13 Objects\Converters\ConverterAutocadCivil\ConverterAutocadCivilShared\ConverterAutocadCivilShared.projitems*{2d0f9f8a-2e89-4780-978a-cd92d6d7b843}*SharedItemsImports = 13 Objects\Converters\ConverterRevit\ConverterRevitShared\ConverterRevitShared.projitems*{2dcd648d-dca5-4d2a-8b14-ad2cb85d24b0}*SharedItemsImports = 13 + DUI3-DX\Converters\Civil3d\Speckle.Converters.Civil3dShared\Speckle.Converters.Civil3dShared.projitems*{35175682-da83-4c0a-a49d-b191f5885d8e}*SharedItemsImports = 13 ConnectorBentley\ConnectorBentleyShared\ConnectorBentleyShared.projitems*{372d9f0f-ede9-4050-bf8c-758911c5c2e0}*SharedItemsImports = 13 ConnectorTeklaStructures\ConnectorTeklaStructuresShared\ConnectorTeklaStructuresShared.projitems*{3af1ef30-0906-4926-a02c-4e3ad666352a}*SharedItemsImports = 5 ConnectorAutocadCivil\ConnectorAutocadCivil\ConnectorAutocadCivilShared.projitems*{3b9189b9-e485-448a-8793-9b9587a36791}*SharedItemsImports = 5 Objects\Converters\ConverterRhinoGh\ConverterRhinoGhShared\ConverterRhinoGhShared.projitems*{3cdef4cc-2cfa-4939-8427-3ed00fa9db55}*SharedItemsImports = 5 Objects\Converters\ConverterDynamo\ConverterDynamoShared\ConverterDynamoShared.projitems*{3df12639-78b6-41b3-a046-a675035369be}*SharedItemsImports = 5 ConnectorAutocadCivil\ConnectorAutocadCivil\ConnectorAutocadCivilShared.projitems*{3e30d170-3cb4-4728-97d5-887c5019da9b}*SharedItemsImports = 5 + DUI3-DX\Connectors\Autocad\Speckle.Connectors.AutocadShared\Speckle.Connectors.AutocadShared.projitems*{41bc679f-887f-44cf-971d-a5502ee87db0}*SharedItemsImports = 13 Objects\Converters\ConverterBentley\ConverterBentleyShared\ConverterBentleyShared.projitems*{425f0d00-6608-4bd2-a1e0-2730c9f2bfd3}*SharedItemsImports = 13 ConnectorAutocadCivil\ConnectorAutocadCivil\ConnectorAutocadCivilShared.projitems*{42fe69bf-c821-43e8-8eae-8f342749ef7a}*SharedItemsImports = 5 Objects\Converters\ConverterCSI\ConverterCSIShared\ConverterCSIShared.projitems*{442116f3-0f4a-4136-894e-ff5f4295500b}*SharedItemsImports = 5 @@ -2646,6 +3177,7 @@ Global ConnectorGrasshopper\ConnectorGrasshopperShared\ConnectorGrasshopperShared.projitems*{86920221-416e-4a66-a601-3418207e2401}*SharedItemsImports = 5 Objects\Converters\ConverterAutocadCivil\ConverterAutocadCivilShared\ConverterAutocadCivilShared.projitems*{88a24c40-74c8-4e20-9051-6be9e6adecfd}*SharedItemsImports = 5 Objects\Converters\ConverterRhinoGh\ConverterRhinoGhShared\ConverterRhinoGhShared.projitems*{89996067-3233-410a-a6a1-39e2f11f0626}*SharedItemsImports = 5 + DUI3-DX\Connectors\Autocad\Speckle.Connectors.AutocadShared\Speckle.Connectors.AutocadShared.projitems*{89c4cbc7-1606-40de-b6da-fbe3aac98395}*SharedItemsImports = 5 ConnectorRevit\ConnectorRevit\ConnectorRevit.projitems*{8a53b084-20d8-48f6-9591-9d53cfa74130}*SharedItemsImports = 5 ConnectorRevit\RevitSharedResources\RevitSharedResources.projitems*{8ad2ea4f-14fb-4bb6-94cd-932630dfed9c}*SharedItemsImports = 5 Objects\Converters\ConverterNavisworks\ConverterNavisworks\ConverterNavisworksShared.projitems*{8b467ff4-6a6c-4071-87a7-0dd7b9822251}*SharedItemsImports = 5 @@ -2654,10 +3186,12 @@ Global Objects\Converters\ConverterBentley\ConverterBentleyShared\ConverterBentleyShared.projitems*{931fc9a8-18b4-4ac5-81d9-14c48499bfb5}*SharedItemsImports = 5 ConnectorRevit\ConnectorRevit\ConnectorRevit.projitems*{9a1e899a-f821-4519-aad1-0789a4e9ccb3}*SharedItemsImports = 5 ConnectorNavisworks\ConnectorNavisworks\ConnectorNavisworks.Shared.projitems*{9a7d7f9a-4fe1-4053-950b-50b43bc81087}*SharedItemsImports = 5 + DUI3-DX\Converters\Autocad\Speckle.Converters.AutocadShared\Speckle.Converters.AutocadShared.projitems*{9add1b7a-6401-4202-8613-f668e2fbc0a4}*SharedItemsImports = 13 ConnectorCSI\ConnectorCSIShared\ConnectorCSIShared.projitems*{9d188843-8841-4a76-a844-efbe8e32ee05}*SharedItemsImports = 5 ConnectorBentley\ConnectorBentleyShared\ConnectorBentleyShared.projitems*{a3a0ee09-6055-4009-ab8e-13fbc1a403a9}*SharedItemsImports = 5 ConnectorNavisworks\ConnectorNavisworks\ConnectorNavisworks.Shared.projitems*{a517a609-cab1-4b33-b83c-1b13b34e4560}*SharedItemsImports = 13 ConnectorRhino\ConnectorRhino\ConnectorRhinoShared\ConnectorRhinoShared.projitems*{a64acbf9-db82-4839-af99-57ed2e7989f4}*SharedItemsImports = 5 + DUI3-DX\Converters\Autocad\Speckle.Converters.AutocadShared\Speckle.Converters.AutocadShared.projitems*{a6baa749-0dbe-49a9-9a9d-fca0f06ee096}*SharedItemsImports = 5 Objects\Converters\ConverterRhinoGh\ConverterRhinoGhShared\ConverterRhinoGhShared.projitems*{a8607330-1b23-43cc-8b9b-25818d9c1d64}*SharedItemsImports = 5 Objects\Converters\ConverterDynamo\ConverterDynamoShared\ConverterDynamoShared.projitems*{aaa05c3d-856d-4f22-971e-5e3f066d0eae}*SharedItemsImports = 5 Objects\Converters\ConverterRevit\ConverterRevitShared\ConverterRevitShared.projitems*{aaa05c3d-856d-4f22-971e-5e3f066d0eae}*SharedItemsImports = 5 @@ -2676,6 +3210,7 @@ Global Objects\Converters\ConverterRevit\ConverterRevitShared\ConverterRevitShared.projitems*{c21a6553-b4ec-4ec3-b82a-c7a83cffb809}*SharedItemsImports = 5 ConnectorRevit\RevitSharedResources\RevitSharedResources.projitems*{c2ba8b6b-72bd-4dab-865f-90c66083bdb2}*SharedItemsImports = 5 Objects\Converters\ConverterNavisworks\ConverterNavisworks\ConverterNavisworksShared.projitems*{c3232ef3-2000-44c6-a330-b94531c9cc83}*SharedItemsImports = 13 + DUI3-DX\Converters\Autocad\Speckle.Converters.AutocadShared\Speckle.Converters.AutocadShared.projitems*{c9f084af-fb70-4f5f-aceb-db6818bbecaf}*SharedItemsImports = 5 Objects\Converters\ConverterNavisworks\ConverterNavisworks\ConverterNavisworksShared.projitems*{cafd4eac-75a8-4fc8-94e5-91cadc39f5b3}*SharedItemsImports = 5 Objects\Converters\ConverterRevit\ConverterRevitShared\ConverterRevitShared.projitems*{cc790553-8088-41a9-83cd-b29f7141f408}*SharedItemsImports = 5 Objects\Converters\ConverterNavisworks\ConverterNavisworks\ConverterNavisworksShared.projitems*{cd334556-ba2b-4272-a1eb-628e8152204a}*SharedItemsImports = 5 @@ -2686,8 +3221,13 @@ Global ConnectorRevit\ConnectorRevit\ConnectorRevit.projitems*{d607bd0a-9f7f-4c3a-9b9c-fead6ba49c7c}*SharedItemsImports = 5 ConnectorRhino\ConnectorRhino\ConnectorRhinoShared\ConnectorRhinoShared.projitems*{d648bb69-b992-4d34-906e-7a547374b86c}*SharedItemsImports = 5 Objects\Converters\ConverterAutocadCivil\ConverterAutocadCivilShared\ConverterAutocadCivilShared.projitems*{d9f443b5-c55b-4ad8-9c70-bc3d2be781be}*SharedItemsImports = 5 + DUI3-DX\Connectors\Revit\Speckle.Connectors.RevitShared\Speckle.Connectors.RevitShared.projitems*{dc570fff-6fe5-47bd-8bc1-b471a6067786}*SharedItemsImports = 13 + DUI3-DX\Connectors\Autocad\Speckle.Connectors.AutocadShared\Speckle.Connectors.AutocadShared.projitems*{ddbba313-69a6-40de-ab3a-79ee5bf32a7e}*SharedItemsImports = 5 ConnectorNavisworks\ConnectorNavisworks\ConnectorNavisworks.Shared.projitems*{debc2174-5e31-4b6e-8680-690d75e50e2d}*SharedItemsImports = 5 ConnectorRevit\ConnectorRevit\ConnectorRevit.projitems*{dfdfdbb8-018b-4dcb-a012-54227abf53a7}*SharedItemsImports = 5 + DUI3-DX\Converters\Revit\Speckle.Converters.RevitShared\Speckle.Converters.RevitShared.projitems*{e1c43415-3200-45f4-8bf9-a4dd7d7f2ed6}*SharedItemsImports = 13 + DUI3-DX\Converters\Autocad\Speckle.Converters.AutocadShared\Speckle.Converters.AutocadShared.projitems*{e7fa6a25-a224-4207-846b-75ce8236228d}*SharedItemsImports = 5 + DUI3-DX\Converters\Civil3d\Speckle.Converters.Civil3dShared\Speckle.Converters.Civil3dShared.projitems*{e7fa6a25-a224-4207-846b-75ce8236228d}*SharedItemsImports = 5 ConnectorRevit\RevitSharedResources\RevitSharedResources.projitems*{ea34ac83-5825-4473-a572-d5127fd33b1b}*SharedItemsImports = 5 Objects\Converters\ConverterRhinoGh\ConverterRhinoGhShared\ConverterRhinoGhShared.projitems*{ea81f83c-1485-49c8-ab05-9df2798d70ec}*SharedItemsImports = 5 Objects\Converters\ConverterTeklaStructures\ConverterTeklaStructuresShared\ConverterTeklaStructuresShared.projitems*{eb52e451-9ed8-460e-9ee4-6717bfb12eab}*SharedItemsImports = 5 diff --git a/All.sln.DotSettings b/All.sln.DotSettings index 1170e1aa0a..c5930b608d 100644 --- a/All.sln.DotSettings +++ b/All.sln.DotSettings @@ -1,4 +1,5 @@  + False SUGGESTION SUGGESTION SUGGESTION @@ -10,7 +11,9 @@ SUGGESTION SUGGESTION HINT + HINT HINT + HINT DO_NOT_SHOW HINT @@ -614,6 +617,7 @@ Speckle:Cleanup CCW + CRS CSI DUI GQL @@ -621,6 +625,7 @@ QL SQ UI + URI True ExternalToolData|CSharpier|csharpier||csharpier|$FILE$ CamelCase diff --git a/Automate/Tests/Speckle.Automate.Sdk.Tests.Integration/Speckle.Automate.Sdk.Tests.Integration.csproj b/Automate/Tests/Speckle.Automate.Sdk.Tests.Integration/Speckle.Automate.Sdk.Tests.Integration.csproj index 0d5b566f3c..53a7ff66c0 100644 --- a/Automate/Tests/Speckle.Automate.Sdk.Tests.Integration/Speckle.Automate.Sdk.Tests.Integration.csproj +++ b/Automate/Tests/Speckle.Automate.Sdk.Tests.Integration/Speckle.Automate.Sdk.Tests.Integration.csproj @@ -3,8 +3,7 @@ net7.0 enable - enable - + disable false true @@ -14,8 +13,8 @@ - - + + diff --git a/Automate/Tests/Speckle.Automate.Sdk.Tests.Integration/SpeckleAutomate.cs b/Automate/Tests/Speckle.Automate.Sdk.Tests.Integration/SpeckleAutomate.cs index 1c663e6ecb..7fc0517136 100644 --- a/Automate/Tests/Speckle.Automate.Sdk.Tests.Integration/SpeckleAutomate.cs +++ b/Automate/Tests/Speckle.Automate.Sdk.Tests.Integration/SpeckleAutomate.cs @@ -115,7 +115,7 @@ public void TestParseInputData() FunctionRunData functionRunData = new() { FunctionInputs = testFunctionInputs }; string serializedFunctionRunData = JsonConvert.SerializeObject(functionRunData); File.WriteAllText("./inputData.json", serializedFunctionRunData); - FunctionRunData? data = FunctionRunDataParser.FromPath("./inputData.json"); + FunctionRunData data = FunctionRunDataParser.FromPath("./inputData.json"); Assert.AreEqual("Base", data.FunctionInputs.ForbiddenSpeckleType); } diff --git a/Build/Build.csproj b/Build/Build.csproj new file mode 100644 index 0000000000..74408729a7 --- /dev/null +++ b/Build/Build.csproj @@ -0,0 +1,13 @@ + + + + Exe + net7.0 + enable + + + + + + + diff --git a/Build/Consts.cs b/Build/Consts.cs new file mode 100644 index 0000000000..5d3f91de54 --- /dev/null +++ b/Build/Consts.cs @@ -0,0 +1,46 @@ +using System.Collections.Generic; + +namespace Build; + +public static class Consts +{ + public static readonly string[] Solutions = { "DUI3-DX.slnf" }; + public static readonly string[] TestProjects = System.Array.Empty(); + + public static readonly InstallerProject[] InstallerManifests = + { + new( + "arcgis", + new InstallerAsset[] { new("DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3", "net6.0-windows") } + ), + new("rhino", new InstallerAsset[] { new("DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7", "net48") }), + new("revit", new InstallerAsset[] { new("DUI3-DX/Connectors/Revit/Speckle.Connectors.Revit2023", "net48") }), + new("autocad", new InstallerAsset[] { new("DUI3-DX/Connectors/Autocad/Speckle.Connectors.Autocad2023", "net48") }) + }; +} + +public readonly struct InstallerProject +{ + public string HostAppSlug { get; init; } + public IReadOnlyList Projects { get; init; } + + public InstallerProject(string hostAppSlug, IReadOnlyList projects) + { + HostAppSlug = hostAppSlug; + Projects = projects; + } + + public override string ToString() => $"{HostAppSlug}"; +} + +public readonly struct InstallerAsset +{ + public InstallerAsset(string projectPath, string targetName) + { + ProjectPath = projectPath; + TargetName = targetName; + } + + public string ProjectPath { get; init; } + public string TargetName { get; init; } +} diff --git a/Build/Github.cs b/Build/Github.cs new file mode 100644 index 0000000000..5d3267937e --- /dev/null +++ b/Build/Github.cs @@ -0,0 +1,42 @@ +using System; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Net.Mime; +using System.Text.Json; +using System.Threading.Tasks; + +namespace Build; + +public static class Github +{ + public static async Task BuildInstallers(string token, string runId, string version) + { + using var client = new HttpClient(); + var payload = new { event_type = "build-installers", client_payload = new { run_id = runId, version } }; + var content = new StringContent( + JsonSerializer.Serialize(payload), + new MediaTypeHeaderValue(MediaTypeNames.Application.Json) + ); + + var request = new HttpRequestMessage() + { + Method = HttpMethod.Post, + RequestUri = new Uri("https://api.github.com/repos/specklesystems/connector-installers/dispatches"), + Headers = + { + Accept = { new MediaTypeWithQualityHeaderValue("application/vnd.github+json") }, + Authorization = new AuthenticationHeaderValue("Bearer", token), + UserAgent = { new ProductInfoHeaderValue("Speckle.build", "3.0.0") } + }, + Content = content + }; + request.Headers.Add("X-GitHub-Api-Version", "2022-11-28"); + var response = await client.SendAsync(request).ConfigureAwait(false); + if (!response.IsSuccessStatusCode) + { + throw new InvalidOperationException( + $"{response.StatusCode} {response.ReasonPhrase} {await response.Content.ReadAsStringAsync().ConfigureAwait(false)}" + ); + } + } +} diff --git a/Build/Program.cs b/Build/Program.cs new file mode 100644 index 0000000000..610115c621 --- /dev/null +++ b/Build/Program.cs @@ -0,0 +1,181 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using Build; +using GlobExpressions; +using static Bullseye.Targets; +using static SimpleExec.Command; + +const string CLEAN = "clean"; +const string RESTORE = "restore"; +const string BUILD = "build"; +const string TEST = "test"; +const string FORMAT = "format"; +const string ZIP = "zip"; +const string VERSION = "version"; +const string RESTORE_TOOLS = "restore-tools"; +const string BUILD_SERVER_VERSION = "build-server-version"; + +//need to pass arguments +/*var arguments = new List(); +if (args.Length > 1) +{ + arguments = args.ToList(); + args = new[] { arguments.First() }; + //arguments = arguments.Skip(1).ToList(); +}*/ + +Target( + CLEAN, + ForEach("**/output"), + dir => + { + IEnumerable GetDirectories(string d) + { + return Glob.Directories(".", d); + } + + void RemoveDirectory(string d) + { + if (Directory.Exists(d)) + { + Console.WriteLine(d); + Directory.Delete(d, true); + } + } + + foreach (var d in GetDirectories(dir)) + { + RemoveDirectory(d); + } + } +); + +Target( + VERSION, + async () => + { + var (output, _) = await ReadAsync("dotnet", "minver -v w").ConfigureAwait(false); + output = output.Trim(); + Console.WriteLine($"Version: {output}"); + Run("echo", $"\"version={output}\" >> $GITHUB_OUTPUT"); + } +); + +Target( + RESTORE_TOOLS, + () => + { + Run("dotnet", "tool restore"); + } +); + +Target( + FORMAT, + DependsOn(RESTORE_TOOLS), + () => + { + Run("dotnet", "csharpier --check ."); + } +); + +Target( + RESTORE, + Consts.Solutions, + s => + { + Run("dotnet", $"restore {s} --locked-mode"); + } +); + +Target( + BUILD_SERVER_VERSION, + DependsOn(RESTORE_TOOLS), + () => + { + Run("dotnet", "tool run dotnet-gitversion /output json /output buildserver"); + } +); + +Target( + BUILD, + DependsOn(RESTORE), + Consts.Solutions, + s => + { + 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 {s} -c Release --no-restore -p:IsDesktopBuild=false -p:Version={version} -p:FileVersion={fileVersion} -v:m" + ); + } +); + +Target( + TEST, + DependsOn(BUILD), + Consts.TestProjects, + t => + { + IEnumerable GetFiles(string d) + { + return Glob.Files(".", d); + } + + foreach (var file in GetFiles($"**/{t}.csproj")) + { + Run("dotnet", $"test {file} -c Release --no-build --no-restore --verbosity=normal"); + } + } +); + +Target( + ZIP, + DependsOn(TEST), + Consts.InstallerManifests, + x => + { + var outputDir = Path.Combine(".", "output"); + var slugDir = Path.Combine(outputDir, x.HostAppSlug); + + Directory.CreateDirectory(outputDir); + Directory.CreateDirectory(slugDir); + + foreach (var asset in x.Projects) + { + var fullPath = Path.Combine(".", asset.ProjectPath, "bin", "Release", asset.TargetName); + if (!Directory.Exists(fullPath)) + { + throw new InvalidOperationException("Could not find: " + fullPath); + } + + var assetName = Path.GetFileName(asset.ProjectPath); + var connectorDir = Path.Combine(slugDir, assetName); + + Directory.CreateDirectory(connectorDir); + foreach (var directory in Directory.EnumerateDirectories(fullPath, "*", SearchOption.AllDirectories)) + { + Directory.CreateDirectory(directory.Replace(fullPath, connectorDir)); + } + + foreach (var file in Directory.EnumerateFiles(fullPath, "*", SearchOption.AllDirectories)) + { + Console.WriteLine(file); + File.Copy(file, file.Replace(fullPath, connectorDir), true); + } + } + + var outputPath = Path.Combine(outputDir, $"{x.HostAppSlug}.zip"); + File.Delete(outputPath); + Console.WriteLine($"Zipping: '{slugDir}' to '{outputPath}'"); + ZipFile.CreateFromDirectory(slugDir, outputPath); + // Directory.Delete(slugDir, true); + } +); + +Target("default", DependsOn(FORMAT, ZIP), () => Console.WriteLine("Done!")); + +await RunTargetsAndExitAsync(args).ConfigureAwait(true); diff --git a/ConnectorAutocadCivil/ConnectorAutocadCivil.slnf b/ConnectorAutocadCivil/ConnectorAutocadCivil.slnf index e645e74cae..961482fe14 100644 --- a/ConnectorAutocadCivil/ConnectorAutocadCivil.slnf +++ b/ConnectorAutocadCivil/ConnectorAutocadCivil.slnf @@ -2,6 +2,9 @@ "solution": { "path": "..\\All.sln", "projects": [ + "ConnectorAutocadCivil\\AutocadCivilDUI3\\Autocad2022\\ConnectorAutocad2022DUI3.csproj", + "ConnectorAutocadCivil\\AutocadCivilDUI3\\Autocad2023\\ConnectorAutocad2023DUI3.csproj", + "ConnectorAutocadCivil\\AutocadCivilDUI3\\AutocadCivilDUI3Shared\\AutocadCivilDUI3Shared.shproj", "ConnectorAutocadCivil\\ConnectorAutocad2021\\ConnectorAutocad2021.csproj", "ConnectorAutocadCivil\\ConnectorAutocad2022\\ConnectorAutocad2022.csproj", "ConnectorAutocadCivil\\ConnectorAutocad2023\\ConnectorAutocad2023.csproj", @@ -18,6 +21,7 @@ "Core\\Transports\\DiskTransport\\DiskTransport.csproj", "DesktopUI2\\AvaloniaHwndHost\\AvaloniaHwndHost.csproj", "DesktopUI2\\DesktopUI2\\DesktopUI2.csproj", + "DesktopUI3\\DUI3\\DUI3.csproj", "Objects\\Converters\\ConverterAutocadCivil\\ConverterAutocad2021\\ConverterAutocad2021.csproj", "Objects\\Converters\\ConverterAutocadCivil\\ConverterAutocad2022\\ConverterAutocad2022.csproj", "Objects\\Converters\\ConverterAutocadCivil\\ConverterAutocad2023\\ConverterAutocad2023.csproj", diff --git a/ConnectorNavisworks/ConnectorNavisworks2020/ConnectorNavisworks2020.csproj b/ConnectorNavisworks/ConnectorNavisworks2020/ConnectorNavisworks2020.csproj index c3bc46cd33..b39324da4a 100644 --- a/ConnectorNavisworks/ConnectorNavisworks2020/ConnectorNavisworks2020.csproj +++ b/ConnectorNavisworks/ConnectorNavisworks2020/ConnectorNavisworks2020.csproj @@ -29,7 +29,6 @@ 2020 $(AppData)\Autodesk\ApplicationPlugins\Speckle.ConnectorNavisworks.bundle $(AppData)\Autodesk\ApplicationPlugins\Speckle.ConnectorNavisworks.bundle\Contents\$(NavisworksBuildNumber) - AnyCPU;x64 true diff --git a/ConnectorNavisworks/ConnectorNavisworks2021/ConnectorNavisworks2021.csproj b/ConnectorNavisworks/ConnectorNavisworks2021/ConnectorNavisworks2021.csproj index 213c001c05..83a33c0d95 100644 --- a/ConnectorNavisworks/ConnectorNavisworks2021/ConnectorNavisworks2021.csproj +++ b/ConnectorNavisworks/ConnectorNavisworks2021/ConnectorNavisworks2021.csproj @@ -29,7 +29,6 @@ 2021 $(AppData)\Autodesk\ApplicationPlugins\Speckle.ConnectorNavisworks.bundle $(AppData)\Autodesk\ApplicationPlugins\Speckle.ConnectorNavisworks.bundle\Contents\$(NavisworksBuildNumber) - AnyCPU;x64 true diff --git a/ConnectorNavisworks/ConnectorNavisworks2022/ConnectorNavisworks2022.csproj b/ConnectorNavisworks/ConnectorNavisworks2022/ConnectorNavisworks2022.csproj index 72ece355a2..d238fd1fa3 100644 --- a/ConnectorNavisworks/ConnectorNavisworks2022/ConnectorNavisworks2022.csproj +++ b/ConnectorNavisworks/ConnectorNavisworks2022/ConnectorNavisworks2022.csproj @@ -29,7 +29,6 @@ 2022 $(AppData)\Autodesk\ApplicationPlugins\Speckle.ConnectorNavisworks.bundle $(AppData)\Autodesk\ApplicationPlugins\Speckle.ConnectorNavisworks.bundle\Contents\$(NavisworksBuildNumber) - AnyCPU;x64 true diff --git a/ConnectorNavisworks/ConnectorNavisworks2023/ConnectorNavisworks2023.csproj b/ConnectorNavisworks/ConnectorNavisworks2023/ConnectorNavisworks2023.csproj index f85901641d..e34e605375 100644 --- a/ConnectorNavisworks/ConnectorNavisworks2023/ConnectorNavisworks2023.csproj +++ b/ConnectorNavisworks/ConnectorNavisworks2023/ConnectorNavisworks2023.csproj @@ -29,7 +29,6 @@ 2023 $(AppData)\Autodesk\ApplicationPlugins\Speckle.ConnectorNavisworks.bundle $(AppData)\Autodesk\ApplicationPlugins\Speckle.ConnectorNavisworks.bundle\Contents\$(NavisworksBuildNumber) - AnyCPU;x64 true diff --git a/ConnectorNavisworks/ConnectorNavisworks2024/ConnectorNavisworks2024.csproj b/ConnectorNavisworks/ConnectorNavisworks2024/ConnectorNavisworks2024.csproj index f46df0cfd2..f84dec5a2b 100644 --- a/ConnectorNavisworks/ConnectorNavisworks2024/ConnectorNavisworks2024.csproj +++ b/ConnectorNavisworks/ConnectorNavisworks2024/ConnectorNavisworks2024.csproj @@ -29,7 +29,6 @@ 2024 $(AppData)\Autodesk\ApplicationPlugins\Speckle.ConnectorNavisworks.bundle $(AppData)\Autodesk\ApplicationPlugins\Speckle.ConnectorNavisworks.bundle\Contents\$(NavisworksBuildNumber) - AnyCPU;x64 true diff --git a/ConnectorRhino/ConnectorRhino.slnf b/ConnectorRhino/ConnectorRhino.slnf index 40d01b1231..2b79cebc61 100644 --- a/ConnectorRhino/ConnectorRhino.slnf +++ b/ConnectorRhino/ConnectorRhino.slnf @@ -10,7 +10,6 @@ "ConnectorRhino\\ConnectorRhino6\\ConnectorRhino6.csproj", "ConnectorRhino\\ConnectorRhino7\\ConnectorRhino7.csproj", "ConnectorRhino\\ConnectorRhino8\\ConnectorRhino8.csproj", - "ConnectorRhino\\ConnectorRhino\\ConnectorRhinoShared\\ConnectorRhinoShared.shproj", "Core\\Core\\Core.csproj", "Core\\Tests\\Speckle.Core.Tests.Unit\\Speckle.Core.Tests.Unit.csproj", "Core\\Transports\\DiskTransport\\DiskTransport.csproj", @@ -28,4 +27,4 @@ "Objects\\Tests\\Objects.Tests.Unit\\Objects.Tests.Unit.csproj" ] } -} \ No newline at end of file +} diff --git a/Core/Core/Api/GraphQL/Models/ServerInfo.cs b/Core/Core/Api/GraphQL/Models/ServerInfo.cs index 11f053ebe8..c0c36a5a6f 100644 --- a/Core/Core/Api/GraphQL/Models/ServerInfo.cs +++ b/Core/Core/Api/GraphQL/Models/ServerInfo.cs @@ -1,8 +1,11 @@ #nullable disable using System; +using System.Runtime.InteropServices; namespace Speckle.Core.Api.GraphQL.Models; +[ClassInterface(ClassInterfaceType.AutoDual)] +[ComVisible(true)] public sealed class ServerInfo { public string name { get; init; } diff --git a/Core/Core/Api/Operations/Operations.Send.Obsolete.cs b/Core/Core/Api/Operations/Operations.Send.Obsolete.cs index 2d07a601eb..39ebc58305 100644 --- a/Core/Core/Api/Operations/Operations.Send.Obsolete.cs +++ b/Core/Core/Api/Operations/Operations.Send.Obsolete.cs @@ -162,7 +162,7 @@ public static async Task Send( } else { - serializerV2 = new BaseObjectSerializerV2(transports, internalProgressAction, cancellationToken); + serializerV2 = new BaseObjectSerializerV2(transports, internalProgressAction, false, cancellationToken); } foreach (var t in transports) diff --git a/Core/Core/Api/Operations/Operations.Send.cs b/Core/Core/Api/Operations/Operations.Send.cs index 882a831c24..bf42693564 100644 --- a/Core/Core/Api/Operations/Operations.Send.cs +++ b/Core/Core/Api/Operations/Operations.Send.cs @@ -92,7 +92,7 @@ public static async Task Send( var internalProgressAction = GetInternalProgressAction(onProgressAction); - BaseObjectSerializerV2 serializerV2 = new(transports, internalProgressAction, cancellationToken); + BaseObjectSerializerV2 serializerV2 = new(transports, internalProgressAction, false, cancellationToken); foreach (var t in transports) { diff --git a/Core/Core/Core.csproj b/Core/Core/Core.csproj index 33510411b4..caaa2ab93a 100644 --- a/Core/Core/Core.csproj +++ b/Core/Core/Core.csproj @@ -17,22 +17,22 @@ true - - $(WarningsNotAsErrors); + + $(NoWarn); CA1000; CA1001; CA1003; CA1024; CA1033; CA1034; CA1051; CA1055; CA1063; CA1065; CA1502; CA1506; CA1708; CA1710; CA1711; CA1716; CA1720; CA1721; CA1724; CA1816; CA1851; CA1861; - CA2201; CS8618; + CA2201; CS8618; CA1054; CS0419; CS0618; CS0659; CS0809; CS8600; CS8602; CS8603; CS8604; IDE0032; IDE0059; IDE0130; IDE1006; - + - + - - + + + - - + + diff --git a/Core/Core/Credentials/Account.cs b/Core/Core/Credentials/Account.cs index 184797d1ab..4f4bf19173 100644 --- a/Core/Core/Credentials/Account.cs +++ b/Core/Core/Credentials/Account.cs @@ -1,11 +1,14 @@ #nullable disable using System; +using System.Runtime.InteropServices; using System.Threading.Tasks; using Speckle.Core.Api.GraphQL.Models; using Speckle.Core.Helpers; namespace Speckle.Core.Credentials; +[ClassInterface(ClassInterfaceType.AutoDual)] +[ComVisible(true)] public class Account : IEquatable { private string _id; diff --git a/Core/Core/Credentials/Responses.cs b/Core/Core/Credentials/Responses.cs index 212173f6bd..066dfde6d8 100644 --- a/Core/Core/Credentials/Responses.cs +++ b/Core/Core/Credentials/Responses.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.InteropServices; using Speckle.Core.Api; using Speckle.Core.Api.GraphQL.Models; @@ -16,6 +17,8 @@ internal sealed class TokenExchangeResponse public string refreshToken { get; init; } } +[ClassInterface(ClassInterfaceType.AutoDual)] +[ComVisible(true)] public sealed class UserInfo { public string id { get; init; } @@ -31,12 +34,16 @@ public sealed class UserInfo public Commits commits { get; init; } } +[ClassInterface(ClassInterfaceType.AutoDual)] +[ComVisible(true)] [Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class Streams { public int totalCount { get; set; } } +[ClassInterface(ClassInterfaceType.AutoDual)] +[ComVisible(true)] [Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class Commits { diff --git a/Core/Core/Kits/Applications.cs b/Core/Core/Kits/Applications.cs index 3f06c824e2..d80b63479e 100644 --- a/Core/Core/Kits/Applications.cs +++ b/Core/Core/Kits/Applications.cs @@ -5,6 +5,7 @@ namespace Speckle.Core.Kits; public enum HostAppVersion { v, + v3, v6, v7, v8, @@ -63,6 +64,7 @@ public static class HostApplications Unity = new("Unity", "unity"), GSA = new("GSA", "gsa"), Civil = new("Civil 3D", "civil3d"), + Civil3D = new("Civil 3D", "civil3d"), AutoCAD = new("AutoCAD", "autocad"), MicroStation = new("MicroStation", "microstation"), OpenRoads = new("OpenRoads", "openroads"), @@ -118,6 +120,11 @@ public static HostApplication GetHostAppFromString(string? appname) return AutoCAD; } + if (appname.Contains("civil3d")) + { + return Civil3D; + } + if (appname.Contains("civil")) { return Civil; diff --git a/Core/Core/Kits/Exceptions.cs b/Core/Core/Kits/Exceptions.cs index 39cf3e08f0..6e3c4d4daf 100644 --- a/Core/Core/Kits/Exceptions.cs +++ b/Core/Core/Kits/Exceptions.cs @@ -46,7 +46,7 @@ public class ConversionException : SpeckleException public ConversionException(string? message, object? objectToConvert, Exception? innerException = null) : base(message, innerException) { - this.ObjectThatFailed = objectToConvert; + ObjectThatFailed = objectToConvert; } public ConversionException(string? message, Exception? innerException) diff --git a/Core/Core/Models/Extras.cs b/Core/Core/Models/Extras.cs index fae7201fc7..371d97b6d2 100644 --- a/Core/Core/Models/Extras.cs +++ b/Core/Core/Models/Extras.cs @@ -59,11 +59,13 @@ public class DataChunk : Base public List data { get; set; } = new(); } -public class ObjectReference +public class ObjectReference : Base { - public string speckle_type = "reference"; + public new string speckle_type = "reference"; public string referencedId { get; set; } + + public Dictionary closure { get; set; } } public class ProgressEventArgs : EventArgs diff --git a/Core/Core/Models/GraphTraversal/TraversalContextExtensions.cs b/Core/Core/Models/GraphTraversal/TraversalContextExtensions.cs index b987cb1b45..dab65ed446 100644 --- a/Core/Core/Models/GraphTraversal/TraversalContextExtensions.cs +++ b/Core/Core/Models/GraphTraversal/TraversalContextExtensions.cs @@ -1,4 +1,6 @@ using System.Collections.Generic; +using System.Diagnostics.Contracts; +using System.Linq; namespace Speckle.Core.Models.GraphTraversal; @@ -10,6 +12,7 @@ public static class TraversalContextExtensions /// /// /// + [Pure] public static IEnumerable GetPropertyPath(this TraversalContext context) { TraversalContext? head = context; @@ -26,22 +29,31 @@ public static IEnumerable GetPropertyPath(this TraversalContext context) } /// - /// Walks up the tree, returning all typed ascendant, starting the closest , - /// walking up nodes + /// Walks up the tree, returning all ascendant, including /// /// - /// - public static IEnumerable GetAscendantOfType(this TraversalContext context) - where T : Base + /// and all its ascendants + [Pure] + public static IEnumerable GetAscendants(this TraversalContext context) { TraversalContext? head = context; do { - if (head.Current is T c) - { - yield return c; - } + yield return head.Current; head = head.Parent; } while (head != null); } + + /// + /// Walks up the tree, returning all typed ascendant, starting the closest , + /// walking up nodes + /// + /// + /// and all its ascendants of type + [Pure] + public static IEnumerable GetAscendantOfType(this TraversalContext context) + where T : Base + { + return context.GetAscendants().OfType(); + } } diff --git a/Core/Core/Models/Instances/IInstanceComponent.cs b/Core/Core/Models/Instances/IInstanceComponent.cs new file mode 100644 index 0000000000..9a6478607d --- /dev/null +++ b/Core/Core/Models/Instances/IInstanceComponent.cs @@ -0,0 +1,12 @@ +namespace Speckle.Core.Models.Instances; + +/// +/// Abstracts over and for sorting and grouping in receive operations. +/// +public interface IInstanceComponent +{ + /// + /// The maximum "depth" at which this or was found. On receive, as instances can be composed of other instances, we need to start from the deepest instance elements first when reconstructing them, starting with definitions first. + /// + public int MaxDepth { get; set; } +} diff --git a/Core/Core/Models/Instances/InstanceDefinitionProxy.cs b/Core/Core/Models/Instances/InstanceDefinitionProxy.cs new file mode 100644 index 0000000000..0fde842faa --- /dev/null +++ b/Core/Core/Models/Instances/InstanceDefinitionProxy.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; + +namespace Speckle.Core.Models.Instances; + +/// +/// A proxy class for an instance definition. +/// +public class InstanceDefinitionProxy : Base, IInstanceComponent +{ + /// + /// The original ids of the objects that are part of this definition, as present in the source host app. On receive, they will be mapped to corresponding newly created definition ids. + /// + public List Objects { get; set; } // source app application ids for the objects + + public int MaxDepth { get; set; } +} diff --git a/Core/Core/Models/Instances/InstanceProxy.cs b/Core/Core/Models/Instances/InstanceProxy.cs new file mode 100644 index 0000000000..5fa18496e9 --- /dev/null +++ b/Core/Core/Models/Instances/InstanceProxy.cs @@ -0,0 +1,26 @@ +using System.DoubleNumerics; + +namespace Speckle.Core.Models.Instances; + +/// +/// A proxy class for an instance (e.g, a rhino block, or an autocad block reference). +/// +public class InstanceProxy : Base, IInstanceComponent +{ + /// + /// The definition id as present in the original host app. On receive, it will be mapped to the newly created definition id. + /// + public string DefinitionId { get; set; } + + /// + /// The transform of the instance reference. + /// + public Matrix4x4 Transform { get; set; } + + /// + /// The units of the host application file. + /// + public string Units { get; set; } = Kits.Units.Meters; + + public int MaxDepth { get; set; } +} diff --git a/Core/Core/Models/Utilities.cs b/Core/Core/Models/Utilities.cs index 7f7a93c011..19e478f3bc 100644 --- a/Core/Core/Models/Utilities.cs +++ b/Core/Core/Models/Utilities.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; @@ -211,4 +212,38 @@ public static IEnumerable> SplitList(List list, int chunkSize = 50 yield return list.GetRange(i, Math.Min(chunkSize, list.Count - i)); } } + + /// + /// Utility function to flatten a conversion result that might have nested lists of objects. + /// This happens, for example, in the case of multiple display value fallbacks for a given object. + /// + /// + /// Assuming native objects are not inherited from IList. + /// + /// Object to flatten + /// Flattened objects after to host. + public static List FlattenToHostConversionResult(object item) + { + List convertedList = new(); + Stack stack = new(); + stack.Push(item); + + while (stack.Count > 0) + { + object current = stack.Pop(); + if (current is IList list) + { + foreach (object subItem in list) + { + stack.Push(subItem); + } + } + else + { + convertedList.Add(current); + } + } + + return convertedList; + } } diff --git a/Core/Core/Serialisation/BaseObjectSerializerV2.cs b/Core/Core/Serialisation/BaseObjectSerializerV2.cs index eef522c00d..5e63b5ec25 100644 --- a/Core/Core/Serialisation/BaseObjectSerializerV2.cs +++ b/Core/Core/Serialisation/BaseObjectSerializerV2.cs @@ -26,6 +26,14 @@ public class BaseObjectSerializerV2 private readonly Dictionary> _typedPropertiesCache = new(); private readonly Action? _onProgressAction; + private readonly bool _trackDetachedChildren; + + /// + /// Keeps track of all detached children created during serialisation that have an applicationId (provided this serializer instance has been told to track detached children). + /// This is currently used to cache previously converted objects and avoid their conversion if they haven't changed. See the DUI3 send bindings in rhino or another host app. + /// + public Dictionary ObjectReferences { get; } = new(); + /// The sync transport. This transport will be used synchronously. public IReadOnlyCollection WriteTransports { get; } @@ -37,15 +45,24 @@ public class BaseObjectSerializerV2 public BaseObjectSerializerV2() : this(Array.Empty()) { } + /// + /// Creates a new Serializer instance. + /// + /// The transports detached children should be persisted to. + /// Used to track progress. + /// Whether to store all detachable objects while serializing. They can be retrieved via post serialization. + /// public BaseObjectSerializerV2( IReadOnlyCollection writeTransports, Action? onProgressAction = null, + bool trackDetachedChildren = false, CancellationToken cancellationToken = default ) { WriteTransports = writeTransports; _onProgressAction = onProgressAction; CancellationToken = cancellationToken; + _trackDetachedChildren = trackDetachedChildren; } /// The object to serialize @@ -111,6 +128,28 @@ public string Serialize(Base baseObj) switch (obj) { + // Start with object references so they're not captured by the Base class case below + // Note: this change was needed as we've made the ObjectReference type inherit from Base for + // the purpose of the "do not convert unchanged previously converted objects" POC. + case ObjectReference r: + { + Dictionary ret = + new() + { + ["speckle_type"] = r.speckle_type, + ["referencedId"] = r.referencedId, + ["__closure"] = r.closure + }; + if (r.closure is not null) + { + foreach (var kvp in r.closure) + { + UpdateParentClosures(kvp.Key); + } + } + UpdateParentClosures(r.referencedId); + return ret; + } // Complex enough to deserve its own function case Base b: return PreserializeBase(b, computeClosures, inheritedDetachInfo); @@ -141,11 +180,6 @@ public string Serialize(Base baseObj) return ret; } - case ObjectReference r: - { - Dictionary ret = new() { ["speckle_type"] = r.speckle_type, ["referencedId"] = r.referencedId }; - return ret; - } case Enum: return (int)obj; // Support for simple types @@ -304,9 +338,20 @@ public string Serialize(Base baseObj) var objRefConverted = (IDictionary?)PreserializeObject(objRef); UpdateParentClosures(id); _onProgressAction?.Invoke("S", 1); + + // add to obj refs to return + if (baseObj.applicationId != null && _trackDetachedChildren) // && baseObj is not DataChunk && baseObj is not Abstract) // not needed, as data chunks will never have application ids, and abstract objs are not really used. + { + ObjectReferences[baseObj.applicationId] = new ObjectReference() + { + referencedId = id, + applicationId = baseObj.applicationId, + closure = closure + }; + } + return objRefConverted; } - return convertedBase; } diff --git a/Core/Core/Transports/SQLite.cs b/Core/Core/Transports/SQLite.cs index 5b3f4f9b72..06e21b3e9f 100644 --- a/Core/Core/Transports/SQLite.cs +++ b/Core/Core/Transports/SQLite.cs @@ -221,11 +221,15 @@ content TEXT /// Returns all the objects in the store. Note: do not use for large collections. /// /// + /// This function uses a separate so is safe to call concurrently (unlike most other transport functions) internal IEnumerable GetAllObjects() { CancellationToken.ThrowIfCancellationRequested(); - using var command = new SqliteCommand("SELECT * FROM objects", Connection); + using SqliteConnection connection = new(_connectionString); + connection.Open(); + + using var command = new SqliteCommand("SELECT * FROM objects", connection); using var reader = command.ExecuteReader(); while (reader.Read()) diff --git a/Core/Core/Transports/ServerV2.cs b/Core/Core/Transports/ServerV2.cs index b949f59295..ff1baa2e76 100644 --- a/Core/Core/Transports/ServerV2.cs +++ b/Core/Core/Transports/ServerV2.cs @@ -16,6 +16,10 @@ namespace Speckle.Core.Transports; public sealed class ServerTransport : IDisposable, ICloneable, ITransport, IBlobCapableTransport { + // POC: autofac uses this to construct a factory in place if this delegate and this can then be injected and parameters passed + // this should, and I think can, come out of this class, because I think autofac does magic based on the return type + public delegate ITransport Factory(Account account, string streamId, int timeoutSeconds, string? blobStorageFolder); + private readonly object _elapsedLock = new(); private Exception? _exception; diff --git a/Core/Tests/Speckle.Core.Tests.Performance/Api/Operations/ReceiveFromSQLite.cs b/Core/Tests/Speckle.Core.Tests.Performance/Api/Operations/ReceiveFromSQLite.cs index 8a42ce5b69..311bb71e1f 100644 --- a/Core/Tests/Speckle.Core.Tests.Performance/Api/Operations/ReceiveFromSQLite.cs +++ b/Core/Tests/Speckle.Core.Tests.Performance/Api/Operations/ReceiveFromSQLite.cs @@ -21,9 +21,9 @@ public async Task Setup() } [Benchmark] - public async Task Receive_FromSQLite() + public async Task Receive_FromSQLite() { - Base? b = await Speckle.Core.Api.Operations + Base b = await Speckle.Core.Api.Operations .Receive(_dataSource.ObjectId, null, _dataSource.Transport) .ConfigureAwait(false); diff --git a/Core/Tests/Speckle.Core.Tests.Performance/Speckle.Core.Tests.Performance.csproj b/Core/Tests/Speckle.Core.Tests.Performance/Speckle.Core.Tests.Performance.csproj index 653be577bd..625d087f71 100644 --- a/Core/Tests/Speckle.Core.Tests.Performance/Speckle.Core.Tests.Performance.csproj +++ b/Core/Tests/Speckle.Core.Tests.Performance/Speckle.Core.Tests.Performance.csproj @@ -1,9 +1,9 @@ - net481 + net48 enable - enable + disable exe true @@ -13,7 +13,7 @@ - + diff --git a/Core/Tests/Speckle.Core.Tests.Unit/Credentials/Accounts.cs b/Core/Tests/Speckle.Core.Tests.Unit/Credentials/Accounts.cs index a9221ac4d7..4ac91d4770 100644 --- a/Core/Tests/Speckle.Core.Tests.Unit/Credentials/Accounts.cs +++ b/Core/Tests/Speckle.Core.Tests.Unit/Credentials/Accounts.cs @@ -62,6 +62,20 @@ public void GetAllAccounts() Assert.That(accs, Has.Count.GreaterThanOrEqualTo(3)); // Tests are adding three accounts, you might have extra accounts on your machine when testing :D } + [Test] + public void GetAccount_ById() + { + var result = AccountManager.GetAccount(s_testAccount1.id); + + Assert.That(result, Is.EqualTo(s_testAccount1)); + } + + [Test] + public void GetAccount_ById_ThrowsWhenNotFound() + { + Assert.Throws(() => AccountManager.GetAccount("Non_existent_id")); + } + public static IEnumerable TestCases() { SetUp(); diff --git a/Core/Tests/Speckle.Core.Tests.Unit/Helpers/Path.cs b/Core/Tests/Speckle.Core.Tests.Unit/Helpers/Path.cs index 333954e93e..7878ab510c 100644 --- a/Core/Tests/Speckle.Core.Tests.Unit/Helpers/Path.cs +++ b/Core/Tests/Speckle.Core.Tests.Unit/Helpers/Path.cs @@ -1,5 +1,4 @@ using System.Runtime.InteropServices; -using System.Text.RegularExpressions; using NUnit.Framework; using Speckle.Core.Helpers; diff --git a/Core/Tests/Speckle.Core.Tests.Unit/Logging/SpeckleLogTests.cs b/Core/Tests/Speckle.Core.Tests.Unit/Logging/SpeckleLogTests.cs index 6a97323345..22b703300c 100644 --- a/Core/Tests/Speckle.Core.Tests.Unit/Logging/SpeckleLogTests.cs +++ b/Core/Tests/Speckle.Core.Tests.Unit/Logging/SpeckleLogTests.cs @@ -1,6 +1,5 @@ using NUnit.Framework; using Serilog.Context; -using Serilog.Core; using Serilog.Events; using Speckle.Core.Logging; diff --git a/Core/Tests/Speckle.Core.Tests.Unit/Models/Extensions/BaseExtensionsTests.cs b/Core/Tests/Speckle.Core.Tests.Unit/Models/Extensions/BaseExtensionsTests.cs index 09907b108a..c3c1af34ae 100644 --- a/Core/Tests/Speckle.Core.Tests.Unit/Models/Extensions/BaseExtensionsTests.cs +++ b/Core/Tests/Speckle.Core.Tests.Unit/Models/Extensions/BaseExtensionsTests.cs @@ -31,6 +31,30 @@ public void GetDetachedPropName_Instance(string propertyName) Assert.That(result, Is.EqualTo(propertyName)); } + [Test] + public void TraverseWithPath() + { + var collection = new Collection() { name = "collection" }; + var subCollection = new Collection { name = "subCollection" }; + collection.elements.Add(subCollection); + var data1 = new Base(); + subCollection.elements.Add(data1); + + var basePaths = collection.TraverseWithPath((obj => obj is not Collection)).ToList(); + + Assert.That(basePaths.Count, Is.EqualTo(3)); + Assert.That(basePaths[0].Item2.speckle_type, Is.EqualTo("Speckle.Core.Models.Collection")); + Assert.That(basePaths[0].Item2["name"], Is.EqualTo("collection")); + Assert.That(basePaths[0].Item1, Is.EqualTo(new List())); + + Assert.That(basePaths[1].Item2.speckle_type, Is.EqualTo("Speckle.Core.Models.Collection")); + Assert.That(basePaths[1].Item2["name"], Is.EqualTo("subCollection")); + Assert.That(basePaths[1].Item1, Is.EqualTo(new List() { "collection" })); + + Assert.That(basePaths[2].Item2.speckle_type, Is.EqualTo("Base")); + Assert.That(basePaths[2].Item1, Is.EqualTo(new List() { "collection", "subCollection" })); + } + public class TestBase : Base { public string myProperty { get; set; } diff --git a/Core/Tests/Speckle.Core.Tests.Unit/Models/GraphTraversal/TraversalContextExtensionsTests.cs b/Core/Tests/Speckle.Core.Tests.Unit/Models/GraphTraversal/TraversalContextExtensionsTests.cs new file mode 100644 index 0000000000..e8a38cf800 --- /dev/null +++ b/Core/Tests/Speckle.Core.Tests.Unit/Models/GraphTraversal/TraversalContextExtensionsTests.cs @@ -0,0 +1,60 @@ +using NUnit.Framework; +using Speckle.Core.Models; +using Speckle.Core.Models.GraphTraversal; + +namespace Speckle.Core.Tests.Unit.Models.GraphTraversal; + +[TestOf(typeof(TraversalContextExtensions))] +public class TraversalContextExtensionsTests +{ + public static int[] TestDepths => new[] { 1, 2, 10 }; + + private TraversalContext CreateLinkedList(int depth, Func createBaseFunc) + { + if (depth <= 0) + return null; + return new TraversalContext(createBaseFunc(depth), $"{depth}", CreateLinkedList(depth - 1, createBaseFunc)); + } + + [TestCaseSource(nameof(TestDepths))] + public void GetPropertyPath_ReturnsSequentialPath(int depth) + { + var testData = CreateLinkedList(depth, i => new()); + + var path = TraversalContextExtensions.GetPropertyPath(testData); + + var expected = Enumerable.Range(1, depth).Select(i => i.ToString()); + + Assert.That(path, Is.EquivalentTo(expected)); + } + + [TestCaseSource(nameof(TestDepths))] + public void GetAscendant(int depth) + { + var testData = CreateLinkedList(depth, i => new()); + + var all = TraversalContextExtensions.GetAscendants(testData).ToArray(); + + Assert.That(all, Has.Length.EqualTo(depth)); + } + + [TestCaseSource(nameof(TestDepths))] + public void GetAscendantOfType_AllBase(int depth) + { + var testData = CreateLinkedList(depth, i => new()); + + var all = TraversalContextExtensions.GetAscendantOfType(testData).ToArray(); + + Assert.That(all, Has.Length.EqualTo(depth)); + } + + [TestCaseSource(nameof(TestDepths))] + public void GetAscendantOfType_EveryOtherIsCollection(int depth) + { + var testData = CreateLinkedList(depth, i => i % 2 == 0 ? new Base() : new Collection()); + + var all = TraversalContextExtensions.GetAscendantOfType(testData).ToArray(); + + Assert.That(all, Has.Length.EqualTo(Math.Ceiling(depth / 2.0))); + } +} diff --git a/Core/Tests/Speckle.Core.Tests.Unit/Models/GraphTraversal/TraversalMockObjects.cs b/Core/Tests/Speckle.Core.Tests.Unit/Models/GraphTraversal/TraversalMockObjects.cs index e28b57810a..799fd24994 100644 --- a/Core/Tests/Speckle.Core.Tests.Unit/Models/GraphTraversal/TraversalMockObjects.cs +++ b/Core/Tests/Speckle.Core.Tests.Unit/Models/GraphTraversal/TraversalMockObjects.cs @@ -1,11 +1,10 @@ -#nullable enable using Speckle.Core.Models; namespace Speckle.Core.Tests.Unit.Models.GraphTraversal; public class TraversalMock : Base { - public Base? Child { get; set; } + public Base Child { get; set; } public object ObjectChild { get; set; } diff --git a/Core/Tests/Speckle.Core.Tests.Unit/Models/UtilitiesTests.cs b/Core/Tests/Speckle.Core.Tests.Unit/Models/UtilitiesTests.cs index a3a750d3a7..07176d1bda 100644 --- a/Core/Tests/Speckle.Core.Tests.Unit/Models/UtilitiesTests.cs +++ b/Core/Tests/Speckle.Core.Tests.Unit/Models/UtilitiesTests.cs @@ -28,4 +28,44 @@ public void Sha256(string input, string expected) Assert.That(lower, Is.EqualTo(expected.ToLower())); Assert.That(upper, Is.EqualTo(expected.ToUpper())); } + + [Test] + public void FlattenToNativeConversion() + { + var singleObject = new object(); + var nestedObjects = new List() + { + new List() + { + new(), // obj 1 + new() // obj 2 + }, + new() // obj 3 + }; + + var testEnum = new List() { new(), new() }.Select(o => o); + + var nestedObjectsWithEnumerableInherited = new List() + { + new List() + { + new(), // obj 1 + new(), // obj 2 + testEnum // obj 3 + }, + new() // obj 4 + }; + + var parentTestEnumFlattened = Core.Models.Utilities.FlattenToHostConversionResult(testEnum); + var singleObjectFlattened = Core.Models.Utilities.FlattenToHostConversionResult(singleObject); + var nestedObjectsFlattened = Core.Models.Utilities.FlattenToHostConversionResult(nestedObjects); + var nestedObjectsWithEnumerableInheritedFlattened = Core.Models.Utilities.FlattenToHostConversionResult( + nestedObjectsWithEnumerableInherited + ); + + Assert.That(parentTestEnumFlattened.Count, Is.EqualTo(1)); + Assert.That(singleObjectFlattened.Count, Is.EqualTo(1)); + Assert.That(nestedObjectsFlattened.Count, Is.EqualTo(3)); + Assert.That(nestedObjectsWithEnumerableInheritedFlattened.Count, Is.EqualTo(4)); + } } diff --git a/Core/Tests/Speckle.Core.Tests.Unit/Transports/SQLiteTransportTests.cs b/Core/Tests/Speckle.Core.Tests.Unit/Transports/SQLiteTransportTests.cs index a36135de23..735536cdcc 100644 --- a/Core/Tests/Speckle.Core.Tests.Unit/Transports/SQLiteTransportTests.cs +++ b/Core/Tests/Speckle.Core.Tests.Unit/Transports/SQLiteTransportTests.cs @@ -128,6 +128,35 @@ public void UpdateObject_WhileEnumerating() Assert.That(_sqlite.GetAllObjects().ToList(), Has.All.Length.EqualTo(length + UPDATE_STRING.Length)); } + [Test] + [Repeat(10)] + [TestCase(6, 32)] + [Description( + $"Tests that the {nameof(SQLiteTransport.GetAllObjects)} function can be called concurrently from multiple threads" + )] + public void GetAllObjects_IsThreadSafe(int dataSize, int parallelism) + { + foreach (int i in Enumerable.Range(0, dataSize)) + { + _sqlite.SaveObjectSync(i.ToString(), Guid.NewGuid().ToString()); + } + + List[] results = new List[parallelism]; + Parallel.ForEach( + Enumerable.Range(0, parallelism), + i => + { + results[i] = _sqlite.GetAllObjects().ToList(); + } + ); + + foreach (var result in results) + { + Assert.That(result, Is.EquivalentTo(results[0])); + Assert.That(result, Has.Count.EqualTo(dataSize)); + } + } + public void Dispose() { _sqlite?.Dispose(); diff --git a/Core/Transports/DiskTransport/DiskTransport.csproj b/Core/Transports/DiskTransport/DiskTransport.csproj index f94a434a45..27826aa45b 100644 --- a/Core/Transports/DiskTransport/DiskTransport.csproj +++ b/Core/Transports/DiskTransport/DiskTransport.csproj @@ -7,7 +7,6 @@ A Disk transport for Speckle $(PackageTags) disk transport Debug;Release - AnyCPU true enable Speckle.Core.Transports diff --git a/DUI3-DX-REVIT.slnf b/DUI3-DX-REVIT.slnf new file mode 100644 index 0000000000..e84ba6eed1 --- /dev/null +++ b/DUI3-DX-REVIT.slnf @@ -0,0 +1,19 @@ +{ + "solution": { + "path": "All.sln", + "projects": [ + "Core\\Core\\Core.csproj", + "DUI3-DX\\Connectors\\Revit\\Speckle.Connectors.Revit2023\\Speckle.Connectors.Revit2023.csproj", + "DUI3-DX\\Connectors\\Revit\\Speckle.Connectors.RevitShared\\Speckle.Connectors.RevitShared.shproj", + "DUI3-DX\\Converters\\Revit\\Speckle.Converters.Revit2023.DependencyInjection\\Speckle.Converters.Revit2023.DependencyInjection.csproj", + "DUI3-DX\\Converters\\Revit\\Speckle.Converters.Revit2023\\Speckle.Converters.Revit2023.csproj", + "DUI3-DX\\Converters\\Revit\\Speckle.Converters.RevitShared\\Speckle.Converters.RevitShared.shproj", + "DUI3-DX\\DUI3\\Speckle.Connectors.DUI\\Speckle.Connectors.DUI.csproj", + "DUI3-DX\\Sdk\\Speckle.Autofac\\Speckle.Autofac.csproj", + "DUI3-DX\\Sdk\\Speckle.Connectors.Utils\\Speckle.Connectors.Utils.csproj", + "DUI3-DX\\Sdk\\Speckle.Converters.Common.DependencyInjection\\Speckle.Converters.Common.DependencyInjection.csproj", + "DUI3-DX\\Sdk\\Speckle.Converters.Common\\Speckle.Converters.Common.csproj", + "Objects\\Objects\\Objects.csproj" + ] + } +} \ No newline at end of file diff --git a/DUI3-DX-RHINO.slnf b/DUI3-DX-RHINO.slnf new file mode 100644 index 0000000000..16ee76daf2 --- /dev/null +++ b/DUI3-DX-RHINO.slnf @@ -0,0 +1,17 @@ +{ + "solution": { + "path": "All.sln", + "projects": [ + "Core\\Core\\Core.csproj", + "DUI3-DX\\Connectors\\Rhino\\Speckle.Connectors.Rhino7\\Speckle.Connectors.Rhino7.csproj", + "DUI3-DX\\Converters\\Rhino\\Speckle.Converters.Rhino7.DependencyInjection\\Speckle.Converters.Rhino7.DependencyInjection.csproj", + "DUI3-DX\\Converters\\Rhino\\Speckle.Converters.Rhino7\\Speckle.Converters.Rhino7.csproj", + "DUI3-DX\\DUI3\\Speckle.Connectors.DUI\\Speckle.Connectors.DUI.csproj", + "DUI3-DX\\Sdk\\Speckle.Autofac\\Speckle.Autofac.csproj", + "DUI3-DX\\Sdk\\Speckle.Connectors.Utils\\Speckle.Connectors.Utils.csproj", + "DUI3-DX\\Sdk\\Speckle.Converters.Common.DependencyInjection\\Speckle.Converters.Common.DependencyInjection.csproj", + "DUI3-DX\\Sdk\\Speckle.Converters.Common\\Speckle.Converters.Common.csproj", + "Objects\\Objects\\Objects.csproj" + ] + } +} \ No newline at end of file diff --git a/DUI3-DX.slnf b/DUI3-DX.slnf new file mode 100644 index 0000000000..ae770c7ef8 --- /dev/null +++ b/DUI3-DX.slnf @@ -0,0 +1,36 @@ +{ + "solution": { + "path": "All.sln", + "projects": [ + "Build\\Build.csproj", + "DUI3-DX\\Connectors\\ArcGIS\\Speckle.Connectors.ArcGIS3\\Speckle.Connectors.ArcGIS3.csproj", + "DUI3-DX\\Connectors\\Autocad\\Speckle.Connectors.Autocad2023\\Speckle.Connectors.Autocad2023.csproj", + "DUI3-DX\\Connectors\\Autocad\\Speckle.Connectors.AutocadShared\\Speckle.Connectors.AutocadShared.shproj", + "DUI3-DX\\Connectors\\Autocad\\Speckle.Connectors.Civil3d2024\\Speckle.Connectors.Civil3d2024.csproj", + "DUI3-DX\\Connectors\\Revit\\Speckle.Connectors.Revit2023\\Speckle.Connectors.Revit2023.csproj", + "DUI3-DX\\Connectors\\Revit\\Speckle.Connectors.RevitShared\\Speckle.Connectors.RevitShared.shproj", + "DUI3-DX\\Connectors\\Rhino\\Speckle.Connectors.Rhino7\\Speckle.Connectors.Rhino7.csproj", + "DUI3-DX\\Converters\\ArcGIS\\Speckle.Converters.ArcGIS3.DependencyInjection\\Speckle.Converters.ArcGIS3.DependencyInjection.csproj", + "DUI3-DX\\Converters\\ArcGIS\\Speckle.Converters.ArcGIS3\\Speckle.Converters.ArcGIS3.csproj", + "DUI3-DX\\Converters\\Autocad\\Speckle.Converters.Autocad2023.DependencyInjection\\Speckle.Converters.Autocad2023.DependencyInjection.csproj", + "DUI3-DX\\Converters\\Autocad\\Speckle.Converters.Autocad2023\\Speckle.Converters.Autocad2023.csproj", + "DUI3-DX\\Converters\\Autocad\\Speckle.Converters.Autocad2024.DependencyInjection\\Speckle.Converters.Autocad2024.DependencyInjection.csproj", + "DUI3-DX\\Converters\\Autocad\\Speckle.Converters.Autocad2024\\Speckle.Converters.Autocad2024.csproj", + "DUI3-DX\\Converters\\Autocad\\Speckle.Converters.AutocadShared\\Speckle.Converters.AutocadShared.shproj", + "DUI3-DX\\Converters\\Civil3d\\Speckle.Converters.Civil3d2024.DependencyInjection\\Speckle.Converters.Civil3d2024.DependencyInjection.csproj", + "DUI3-DX\\Converters\\Civil3d\\Speckle.Converters.Civil3d2024\\Speckle.Converters.Civil3d2024.csproj", + "DUI3-DX\\Converters\\Civil3d\\Speckle.Converters.Civil3dShared\\Speckle.Converters.Civil3dShared.shproj", + "DUI3-DX\\Converters\\Revit\\Speckle.Converters.Revit2023.DependencyInjection\\Speckle.Converters.Revit2023.DependencyInjection.csproj", + "DUI3-DX\\Converters\\Revit\\Speckle.Converters.Revit2023\\Speckle.Converters.Revit2023.csproj", + "DUI3-DX\\Converters\\Revit\\Speckle.Converters.RevitShared\\Speckle.Converters.RevitShared.shproj", + "DUI3-DX\\Converters\\Rhino\\Speckle.Converters.Rhino7.DependencyInjection\\Speckle.Converters.Rhino7.DependencyInjection.csproj", + "DUI3-DX\\Converters\\Rhino\\Speckle.Converters.Rhino7\\Speckle.Converters.Rhino7.csproj", + "DUI3-DX\\DUI3\\Speckle.Connectors.DUI.WebView\\Speckle.Connectors.DUI.WebView.csproj", + "DUI3-DX\\DUI3\\Speckle.Connectors.DUI\\Speckle.Connectors.DUI.csproj", + "DUI3-DX\\Sdk\\Speckle.Autofac\\Speckle.Autofac.csproj", + "DUI3-DX\\Sdk\\Speckle.Connectors.Utils\\Speckle.Connectors.Utils.csproj", + "DUI3-DX\\Sdk\\Speckle.Converters.Common.DependencyInjection\\Speckle.Converters.Common.DependencyInjection.csproj", + "DUI3-DX\\Sdk\\Speckle.Converters.Common\\Speckle.Converters.Common.csproj" + ] + } +} \ No newline at end of file diff --git a/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Bindings/ArcGISReceiveBinding.cs b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Bindings/ArcGISReceiveBinding.cs new file mode 100644 index 0000000000..489e7296bd --- /dev/null +++ b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Bindings/ArcGISReceiveBinding.cs @@ -0,0 +1,82 @@ +using Speckle.Autofac.DependencyInjection; +using Speckle.Connectors.DUI.Bindings; +using Speckle.Connectors.DUI.Bridge; +using Speckle.Connectors.DUI.Models; +using Speckle.Connectors.DUI.Models.Card; +using Speckle.Connectors.Utils; +using Speckle.Connectors.Utils.Cancellation; +using Speckle.Connectors.Utils.Operations; + +namespace Speckle.Connectors.ArcGIS.Bindings; + +public sealed class ArcGISReceiveBinding : IReceiveBinding +{ + public string Name { get; } = "receiveBinding"; + private readonly CancellationManager _cancellationManager; + private readonly DocumentModelStore _store; + private readonly IUnitOfWorkFactory _unitOfWorkFactory; + + public ReceiveBindingUICommands Commands { get; } + public IBridge Parent { get; } + + public ArcGISReceiveBinding( + DocumentModelStore store, + IBridge parent, + CancellationManager cancellationManager, + IUnitOfWorkFactory unitOfWorkFactory + ) + { + _store = store; + _cancellationManager = cancellationManager; + Parent = parent; + Commands = new ReceiveBindingUICommands(parent); + _unitOfWorkFactory = unitOfWorkFactory; + } + + public async Task Receive(string modelCardId) + { + 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."); + } + + // Init cancellation token source -> Manager also cancel it if exist before + CancellationTokenSource cts = _cancellationManager.InitCancellationTokenSource(modelCardId); + + using IUnitOfWork unitOfWork = _unitOfWorkFactory.Resolve(); + + // Receive host objects + var receiveOperationResults = await unitOfWork.Service + .Execute( + modelCard.AccountId.NotNull(), // POC: I hear -you are saying why we're passing them separately. Not sure pass the DUI3-> Connectors.DUI project dependency to the SDK-> Connector.Utils + modelCard.ProjectId.NotNull(), + modelCard.ProjectName.NotNull(), + modelCard.ModelName.NotNull(), + modelCard.SelectedVersionId.NotNull(), + cts.Token, + (status, progress) => + Commands.SetModelProgress(modelCardId, new ModelCardProgress(modelCardId, status, progress), cts) + ) + .ConfigureAwait(false); + + modelCard.BakedObjectIds = receiveOperationResults.BakedObjectIds.ToList(); + Commands.SetModelReceiveResult( + modelCardId, + receiveOperationResults.BakedObjectIds, + receiveOperationResults.ConversionResults + ); + } + // Catch here specific exceptions if they related to model card. + catch (OperationCanceledException) + { + // SWALLOW -> UI handles it immediately, so we do not need to handle anything + return; + } + } + + public void CancelReceive(string modelCardId) => _cancellationManager.CancelOperation(modelCardId); +} diff --git a/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Bindings/ArcGISSelectionBinding.cs b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Bindings/ArcGISSelectionBinding.cs new file mode 100644 index 0000000000..a9c84e14f5 --- /dev/null +++ b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Bindings/ArcGISSelectionBinding.cs @@ -0,0 +1,44 @@ +using Speckle.Connectors.DUI.Bindings; +using Speckle.Connectors.DUI.Bridge; +using ArcGIS.Desktop.Mapping.Events; +using ArcGIS.Desktop.Mapping; + +namespace Speckle.Connectors.ArcGIS.Bindings; + +public class ArcGISSelectionBinding : ISelectionBinding +{ + public string Name => "selectionBinding"; + public IBridge Parent { get; } + + public ArcGISSelectionBinding(IBridge parent, ITopLevelExceptionHandler topLevelHandler) + { + Parent = parent; + + // 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); + } + + private void OnSelectionChanged() + { + SelectionInfo selInfo = GetSelection(); + Parent.Send(SelectionBindingEvents.SET_SELECTION, selInfo); + } + + public SelectionInfo GetSelection() + { + MapView mapView = MapView.Active; + List selectedMembers = new(); + selectedMembers.AddRange(mapView.GetSelectedLayers()); + selectedMembers.AddRange(mapView.GetSelectedStandaloneTables()); + + List objectTypes = selectedMembers + .Select(o => o.GetType().ToString().Split(".").Last()) + .Distinct() + .ToList(); + return new SelectionInfo( + selectedMembers.Select(x => x.URI).ToList(), + $"{selectedMembers.Count} layers ({string.Join(", ", objectTypes)})" + ); + } +} diff --git a/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Bindings/ArcGISSendBinding.cs b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Bindings/ArcGISSendBinding.cs new file mode 100644 index 0000000000..9ef113e74d --- /dev/null +++ b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Bindings/ArcGISSendBinding.cs @@ -0,0 +1,411 @@ +using System.Diagnostics.CodeAnalysis; +using Speckle.Autofac.DependencyInjection; +using Speckle.Connectors.DUI.Bindings; +using Speckle.Connectors.DUI.Bridge; +using Speckle.Connectors.Utils.Cancellation; +using Speckle.Connectors.DUI.Models; +using Speckle.Connectors.DUI.Models.Card; +using Speckle.Connectors.DUI.Models.Card.SendFilter; +using Speckle.Connectors.DUI.Settings; +using ArcGIS.Desktop.Mapping.Events; +using ArcGIS.Desktop.Mapping; +using Speckle.Connectors.ArcGIS.Filters; +using ArcGIS.Desktop.Editing.Events; +using ArcGIS.Desktop.Framework.Threading.Tasks; +using ArcGIS.Core.Data; +using Speckle.Connectors.DUI.Exceptions; +using Speckle.Connectors.Utils; +using Speckle.Connectors.Utils.Caching; +using Speckle.Connectors.Utils.Operations; + +namespace Speckle.Connectors.ArcGIS.Bindings; + +public sealed class ArcGISSendBinding : ISendBinding +{ + public string Name => "sendBinding"; + public SendBindingUICommands Commands { get; } + public IBridge Parent { get; } + + private readonly DocumentModelStore _store; + private readonly IUnitOfWorkFactory _unitOfWorkFactory; // POC: unused? :D + private readonly List _sendFilters; + private readonly CancellationManager _cancellationManager; + private readonly ISendConversionCache _sendConversionCache; + private readonly ITopLevelExceptionHandler _topLevelExceptionHandler; + + /// + /// Used internally to aggregate the changed objects' id. + /// + private HashSet ChangedObjectIds { get; set; } = new(); + private List SubscribedLayers { get; set; } = new(); + private List SubscribedTables { get; set; } = new(); + + public ArcGISSendBinding( + DocumentModelStore store, + IBridge parent, + IEnumerable sendFilters, + IUnitOfWorkFactory unitOfWorkFactory, + CancellationManager cancellationManager, + ISendConversionCache sendConversionCache, + ITopLevelExceptionHandler topLevelExceptionHandler + ) + { + _store = store; + _unitOfWorkFactory = unitOfWorkFactory; + _sendFilters = sendFilters.ToList(); + _cancellationManager = cancellationManager; + _sendConversionCache = sendConversionCache; + _topLevelExceptionHandler = topLevelExceptionHandler; + Parent = parent; + Commands = new SendBindingUICommands(parent); + SubscribeToArcGISEvents(); + } + + private void SubscribeToArcGISEvents() + { + LayersRemovedEvent.Subscribe( + a => _topLevelExceptionHandler.CatchUnhandled(() => GetIdsForLayersRemovedEvent(a)), + true + ); + + StandaloneTablesRemovedEvent.Subscribe( + a => _topLevelExceptionHandler.CatchUnhandled(() => GetIdsForStandaloneTablesRemovedEvent(a)), + true + ); + + MapPropertyChangedEvent.Subscribe( + a => _topLevelExceptionHandler.CatchUnhandled(() => GetIdsForMapPropertyChangedEvent(a)), + true + ); // Map units, CRS etc. + + MapMemberPropertiesChangedEvent.Subscribe( + a => _topLevelExceptionHandler.CatchUnhandled(() => GetIdsForMapMemberPropertiesChangedEvent(a)), + true + ); // e.g. Layer name + + ActiveMapViewChangedEvent.Subscribe( + _ => _topLevelExceptionHandler.CatchUnhandled(SubscribeToMapMembersDataSourceChange), + true + ); + + LayersAddedEvent.Subscribe(a => _topLevelExceptionHandler.CatchUnhandled(() => GetIdsForLayersAddedEvent(a)), true); + + StandaloneTablesAddedEvent.Subscribe( + a => _topLevelExceptionHandler.CatchUnhandled(() => GetIdsForStandaloneTablesAddedEvent(a)), + true + ); + } + + private void SubscribeToMapMembersDataSourceChange() + { + var task = QueuedTask.Run(() => + { + if (MapView.Active == null) + { + return; + } + + // subscribe to layers + foreach (Layer layer in MapView.Active.Map.Layers) + { + if (layer is FeatureLayer featureLayer) + { + SubscribeToFeatureLayerDataSourceChange(featureLayer); + } + } + // subscribe to tables + foreach (StandaloneTable table in MapView.Active.Map.StandaloneTables) + { + SubscribeToTableDataSourceChange(table); + } + }); + task.Wait(); + } + + private void SubscribeToFeatureLayerDataSourceChange(FeatureLayer layer) + { + if (SubscribedLayers.Contains(layer)) + { + return; + } + Table layerTable = layer.GetTable(); + if (layerTable != null) + { + SubscribeToAnyDataSourceChange(layerTable); + SubscribedLayers.Add(layer); + } + } + + private void SubscribeToTableDataSourceChange(StandaloneTable table) + { + if (SubscribedTables.Contains(table)) + { + return; + } + Table layerTable = table.GetTable(); + if (layerTable != null) + { + SubscribeToAnyDataSourceChange(layerTable); + SubscribedTables.Add(table); + } + } + + private void SubscribeToAnyDataSourceChange(Table layerTable) + { + RowCreatedEvent.Subscribe( + (args) => + { + OnRowChanged(args); + }, + layerTable + ); + RowChangedEvent.Subscribe( + (args) => + { + OnRowChanged(args); + }, + layerTable + ); + RowDeletedEvent.Subscribe( + (args) => + { + OnRowChanged(args); + }, + layerTable + ); + } + + private void OnRowChanged(RowChangedEventArgs args) + { + if (args == null || MapView.Active == null) + { + return; + } + + // get the path of the edited dataset + var datasetURI = args.Row.GetTable().GetPath(); + + // find all layers & tables reading from the dataset + foreach (Layer layer in MapView.Active.Map.Layers) + { + if (layer.GetPath() == datasetURI) + { + ChangedObjectIds.Add(layer.URI); + } + } + foreach (StandaloneTable table in MapView.Active.Map.StandaloneTables) + { + if (table.GetPath() == datasetURI) + { + ChangedObjectIds.Add(table.URI); + } + } + RunExpirationChecks(false); + } + + private void GetIdsForLayersRemovedEvent(LayerEventsArgs args) + { + foreach (Layer layer in args.Layers) + { + ChangedObjectIds.Add(layer.URI); + } + RunExpirationChecks(true); + } + + private void GetIdsForStandaloneTablesRemovedEvent(StandaloneTableEventArgs args) + { + foreach (StandaloneTable table in args.Tables) + { + ChangedObjectIds.Add(table.URI); + } + RunExpirationChecks(true); + } + + private void GetIdsForMapPropertyChangedEvent(MapPropertyChangedEventArgs args) + { + foreach (Map map in args.Maps) + { + foreach (MapMember member in map.Layers) + { + ChangedObjectIds.Add(member.URI); + } + } + RunExpirationChecks(false); + } + + private void GetIdsForLayersAddedEvent(LayerEventsArgs args) + { + foreach (Layer layer in args.Layers) + { + if (layer is FeatureLayer featureLayer) + { + SubscribeToFeatureLayerDataSourceChange(featureLayer); + } + } + } + + private void GetIdsForStandaloneTablesAddedEvent(StandaloneTableEventArgs args) + { + foreach (StandaloneTable table in args.Tables) + { + SubscribeToTableDataSourceChange(table); + } + } + + private void GetIdsForMapMemberPropertiesChangedEvent(MapMemberPropertiesChangedEventArgs args) + { + // don't subscribe to all events (e.g. expanding group, changing visibility etc.) + bool validEvent = false; + foreach (var hint in args.EventHints) + { + if ( + hint == MapMemberEventHint.DataSource + || hint == MapMemberEventHint.DefinitionQuery + || hint == MapMemberEventHint.LabelClasses + || hint == MapMemberEventHint.LabelVisibility + || hint == MapMemberEventHint.Name + || hint == MapMemberEventHint.Renderer + || hint == MapMemberEventHint.SceneLayerType + || hint == MapMemberEventHint.URL + ) + { + validEvent = true; + break; + } + } + + if (validEvent) + { + foreach (MapMember member in args.MapMembers) + { + ChangedObjectIds.Add(member.URI); + } + RunExpirationChecks(false); + } + } + + public List GetSendFilters() => _sendFilters; + + // POC: delete this + public List GetSendSettings() + { + return new List + { + new() + { + Id = "includeAttributes", + Title = "Include Attributes", + Value = true, + Type = "boolean" + }, + }; + } + + [SuppressMessage( + "Maintainability", + "CA1506:Avoid excessive class coupling", + Justification = "Being refactored on in parallel, muting this issue so CI can pass initially." + )] + public async Task Send(string modelCardId) + { + //poc: dupe code between connectors + using var unitOfWork = _unitOfWorkFactory.Resolve>(); + try + { + if (_store.GetModelById(modelCardId) is not SenderModelCard modelCard) + { + // Handle as GLOBAL ERROR at BrowserBridge + throw new InvalidOperationException("No publish model card was found."); + } + + // Init cancellation token source -> Manager also cancel it if exist before + CancellationTokenSource cts = _cancellationManager.InitCancellationTokenSource(modelCardId); + + var sendInfo = new SendInfo( + modelCard.AccountId.NotNull(), + modelCard.ProjectId.NotNull(), + modelCard.ModelId.NotNull(), + "ArcGIS" + ); + + var sendResult = await QueuedTask + .Run(async () => + { + List mapMembers = modelCard.SendFilter + .NotNull() + .GetObjectIds() + .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!" + ); + } + + var result = await unitOfWork.Service + .Execute( + mapMembers, + sendInfo, + (status, progress) => + Commands.SetModelProgress(modelCardId, new ModelCardProgress(modelCardId, status, progress), cts), + cts.Token + ) + .ConfigureAwait(false); + + return result; + }) + .ConfigureAwait(false); + + Commands.SetModelSendResult(modelCardId, sendResult.RootObjId, sendResult.ConversionResults); + } + // Catch here specific exceptions if they related to model card. + catch (SpeckleSendFilterException e) + { + Commands.SetModelError(modelCardId, e); + } + catch (OperationCanceledException) + { + // SWALLOW -> UI handles it immediately, so we do not need to handle anything + return; + } + } + + public void CancelSend(string modelCardId) => _cancellationManager.CancelOperation(modelCardId); + + /// + /// Checks if any sender model cards contain any of the changed objects. If so, also updates the changed objects hashset for each model card - this last part is important for on send change detection. + /// + private void RunExpirationChecks(bool idsDeleted) + { + var senders = _store.GetSenders(); + List expiredSenderIds = new(); + string[] objectIdsList = ChangedObjectIds.ToArray(); + + _sendConversionCache.EvictObjects(objectIdsList); + + foreach (SenderModelCard sender in senders) + { + var objIds = sender.SendFilter.NotNull().GetObjectIds(); + var intersection = objIds.Intersect(objectIdsList).ToList(); + bool isExpired = sender.SendFilter.NotNull().CheckExpiry(ChangedObjectIds.ToArray()); + if (isExpired) + { + expiredSenderIds.Add(sender.ModelCardId.NotNull()); + + // Update the model card object Ids + if (idsDeleted && sender.SendFilter is ArcGISSelectionFilter filter) + { + List remainingObjIds = objIds.SkipWhile(x => intersection.Contains(x)).ToList(); + filter.SelectedObjectIds = remainingObjIds; + } + } + } + + Commands.SetModelsExpired(expiredSenderIds); + ChangedObjectIds = new HashSet(); + } +} diff --git a/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Bindings/BasicConnectorBinding.cs b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Bindings/BasicConnectorBinding.cs new file mode 100644 index 0000000000..961ceb1576 --- /dev/null +++ b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Bindings/BasicConnectorBinding.cs @@ -0,0 +1,203 @@ +using System.Reflection; +using ArcGIS.Core.Data; +using ArcGIS.Desktop.Framework.Threading.Tasks; +using ArcGIS.Desktop.Mapping; +using Speckle.Connectors.ArcGIS.HostApp; +using Speckle.Connectors.ArcGIS.Utils; +using Speckle.Connectors.DUI.Bindings; +using Speckle.Connectors.DUI.Bridge; +using Speckle.Connectors.DUI.Models; +using Speckle.Connectors.DUI.Models.Card; +using Speckle.Connectors.Utils; +using Speckle.Connectors.Utils.Reflection; + +namespace Speckle.Connectors.ArcGIS.Bindings; + +//poc: dupe code between connectors +public class BasicConnectorBinding : IBasicConnectorBinding +{ + public string Name => "baseBinding"; + public IBridge Parent { get; } + + public BasicConnectorBindingCommands Commands { get; } + private readonly DocumentModelStore _store; + private readonly ArcGISSettings _settings; + + public BasicConnectorBinding(DocumentModelStore store, ArcGISSettings settings, IBridge parent) + { + _store = store; + _settings = settings; + Parent = parent; + Commands = new BasicConnectorBindingCommands(parent); + + _store.DocumentChanged += (_, _) => + { + Commands.NotifyDocumentChanged(); + }; + } + + public string GetSourceApplicationName() => _settings.HostAppInfo.Slug; + + public string GetSourceApplicationVersion() => _settings.HostAppInfo.GetVersion(_settings.HostAppVersion); + + public string GetConnectorVersion() => Assembly.GetAssembly(GetType()).NotNull().GetVersion(); + + public DocumentInfo? GetDocumentInfo() + { + if (MapView.Active is null) + { + return null; + } + + return new DocumentInfo(MapView.Active.Map.URI, MapView.Active.Map.Name, MapView.Active.Map.Name); + } + + public DocumentModelStore GetDocumentState() => _store; + + public void AddModel(ModelCard model) => _store.Models.Add(model); + + public void UpdateModel(ModelCard model) => _store.UpdateModel(model); + + public void RemoveModel(ModelCard model) => _store.RemoveModel(model); + + public void HighlightObjects(List objectIds) => + HighlightObjectsOnView(objectIds.Select(x => new ObjectID(x)).ToList()); + + public void HighlightModel(string modelCardId) + { + var model = _store.GetModelById(modelCardId); + + if (model is null) + { + return; + } + + var objectIds = new List(); + + if (model is SenderModelCard senderModelCard) + { + objectIds = senderModelCard.SendFilter.NotNull().GetObjectIds().Select(x => new ObjectID(x)).ToList(); + } + + if (model is ReceiverModelCard receiverModelCard) + { + objectIds = receiverModelCard.BakedObjectIds.NotNull().Select(x => new ObjectID(x)).ToList(); + } + + if (objectIds is null) + { + return; + } + HighlightObjectsOnView(objectIds); + } + + private async void HighlightObjectsOnView(List objectIds) + { + MapView mapView = MapView.Active; + + await QueuedTask + .Run(() => + { + List mapMembersFeatures = GetMapMembers(objectIds, mapView); + ClearSelectionInTOC(); + ClearSelection(); + SelectMapMembersInTOC(mapMembersFeatures); + SelectMapMembersAndFeatures(mapMembersFeatures); + mapView.ZoomToSelected(); + }) + .ConfigureAwait(false); + } + + private List GetMapMembers(List objectIds, MapView mapView) + { + // find the layer on the map (from the objectID) and add the featureID is available + List mapMembersFeatures = new(); + + foreach (ObjectID objectId in objectIds) + { + MapMember mapMember = mapView.Map.FindLayer(objectId.MappedLayerURI, true); + if (mapMember is null) + { + mapMember = mapView.Map.FindStandaloneTable(objectId.MappedLayerURI); + } + if (mapMember is not null) + { + MapMemberFeature mapMembersFeat = new(mapMember, objectId.FeatureId); + mapMembersFeatures.Add(mapMembersFeat); + } + } + return mapMembersFeatures; + } + + private void ClearSelection() + { + List mapMembers = MapView.Active.Map.GetLayersAsFlattenedList().ToList(); + foreach (var member in mapMembers) + { + if (member is FeatureLayer featureLayer) + { + featureLayer.ClearSelection(); + } + } + } + + private void ClearSelectionInTOC() + { + MapView.Active.ClearTOCSelection(); + } + + private void SelectMapMembersAndFeatures(List mapMembersFeatures) + { + foreach (MapMemberFeature mapMemberFeat in mapMembersFeatures) + { + MapMember member = mapMemberFeat.MapMember; + if (member is FeatureLayer layer) + { + if (mapMemberFeat.FeatureId == null) + { + // select full layer if featureID not specified + layer.Select(); + } + else + { + // query features by ID + var objectIDfield = layer.GetFeatureClass().GetDefinition().GetObjectIDField(); + + // FeatureID range starts from 0, but auto-assigned IDs in the layer start from 1 + QueryFilter anotherQueryFilter = new() { WhereClause = $"{objectIDfield} = {mapMemberFeat.FeatureId + 1}" }; + using (Selection onlyOneSelection = layer.Select(anotherQueryFilter, SelectionCombinationMethod.New)) { } + } + } + } + } + + private void SelectMapMembersInTOC(List mapMembersFeatures) + { + List layers = new(); + List tables = new(); + + foreach (MapMemberFeature mapMemberFeat in mapMembersFeatures) + { + MapMember member = mapMemberFeat.MapMember; + if (member is Layer layer) + { + if (member is not GroupLayer) // group layer selection clears other layers selection + { + layers.Add(layer); + } + } + else if (member is StandaloneTable table) + { + tables.Add(table); + } + } + MapView.Active.SelectLayers(layers); + + // this step clears previous selection, not clear how to ADD selection instead + // this is why, activating it only if no layers are selected + if (layers.Count == 0) + { + MapView.Active.SelectStandaloneTables(tables); + } + } +} diff --git a/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Config.daml b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Config.daml new file mode 100644 index 0000000000..7387b9250a --- /dev/null +++ b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Config.daml @@ -0,0 +1,69 @@ + + + + + Speckle + Speckle connector for ArcGIS + Images\AddinDesktop32.png + Speckle Systems + Speckle Systems + 8/5/2021 12:24:21 PM + Framework + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/DarkImages/AddInDesktop16.png b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/DarkImages/AddInDesktop16.png new file mode 100644 index 0000000000..0118942a92 Binary files /dev/null and b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/DarkImages/AddInDesktop16.png differ diff --git a/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/DarkImages/AddInDesktop32.png b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/DarkImages/AddInDesktop32.png new file mode 100644 index 0000000000..9713e3b154 Binary files /dev/null and b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/DarkImages/AddInDesktop32.png differ diff --git a/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/DarkImages/s2logo_16.png b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/DarkImages/s2logo_16.png new file mode 100644 index 0000000000..61872f0d0c Binary files /dev/null and b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/DarkImages/s2logo_16.png differ diff --git a/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/DarkImages/s2logo_32.png b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/DarkImages/s2logo_32.png new file mode 100644 index 0000000000..2ad0c346ea Binary files /dev/null and b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/DarkImages/s2logo_32.png differ diff --git a/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/DependencyInjection/ArcGISConnectorModule.cs b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/DependencyInjection/ArcGISConnectorModule.cs new file mode 100644 index 0000000000..c76e79c4ab --- /dev/null +++ b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/DependencyInjection/ArcGISConnectorModule.cs @@ -0,0 +1,63 @@ +using ArcGIS.Desktop.Mapping; +using Speckle.Autofac.DependencyInjection; +using Speckle.Connectors.ArcGIS.Bindings; +using Speckle.Connectors.ArcGis.Operations.Send; +using Speckle.Connectors.DUI.Bindings; +using Speckle.Connectors.ArcGIS.Utils; +using Speckle.Connectors.ArcGIS.Operations.Receive; +using Speckle.Connectors.DUI.Models.Card.SendFilter; +using Speckle.Connectors.DUI.WebView; +using Speckle.Connectors.Utils.Builders; +using Speckle.Autofac; +using Speckle.Connectors.ArcGIS.Filters; +using Speckle.Connectors.ArcGIS.HostApp; +using Speckle.Connectors.DUI; +using Speckle.Connectors.DUI.Models; +using Speckle.Connectors.Utils; +using Speckle.Connectors.Utils.Caching; +using Speckle.Connectors.Utils.Operations; +using Speckle.Core.Models.GraphTraversal; + +// POC: This is a temp reference to root object senders to tweak CI failing after having generic interfaces into common project. +// This should go whenever it is aligned. + +namespace Speckle.Connectors.ArcGIS.DependencyInjection; + +public class ArcGISConnectorModule : ISpeckleModule +{ + public void Load(SpeckleContainerBuilder builder) + { + builder.AddAutofac(); + builder.AddConnectorUtils(); + builder.AddDUI(); + builder.AddDUIView(); + + // POC: Overwriting the SyncToMainThread to SyncToCurrentThread for ArcGIS only! + // On SendOperation, once we called QueuedTask, it expect to run everything on same thread. + builder.AddSingletonInstance(); + + builder.AddSingleton(); + + // Register bindings + builder.AddSingleton(); + builder.AddSingleton("connectorName", "ArcGIS"); // POC: Easier like this for now, should be cleaned up later + builder.AddSingleton(); + builder.AddSingleton(); + builder.AddSingleton(); + builder.AddSingleton(); + builder.AddSingleton(); + builder.AddSingleton(); + + builder.AddTransient(); + builder.AddScoped(); + builder.AddSingleton(DefaultTraversal.CreateTraversalFunc()); + + // register send operation and dependencies + builder.AddScoped>(); + builder.AddScoped(); + builder.AddScoped, ArcGISRootObjectBuilder>(); + + // register send conversion cache + builder.AddSingleton(); + } +} diff --git a/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Esri.ArcGISPro.Extensions30.Speckle.targets b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Esri.ArcGISPro.Extensions30.Speckle.targets new file mode 100644 index 0000000000..a25abf1d91 --- /dev/null +++ b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Esri.ArcGISPro.Extensions30.Speckle.targets @@ -0,0 +1,352 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + (); + System.Uri relativeTo = new Uri(this.RelativeTo); + foreach (var i in Paths) { + try { + System.Uri itemFullPath = new Uri(i.GetMetadata("FullPath")); + var relativeUri = relativeTo.MakeRelativeUri(itemFullPath); + + result.Add(new TaskItem(Uri.UnescapeDataString(relativeUri.ToString()))); + } + catch { + return false; + } + } + RelativePaths = result.ToArray(); + foreach (var i in RelativePaths) + { + Log.LogMessage(MessageImportance.Low, "RelativePaths: " + i.ToString()); + } + return true; + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + Addin + + + $([MSBuild]::GetRegistryValueFromView('HKEY_LOCAL_MACHINE\SOFTWARE\ESRI\ArcGISPro', 'InstallDir', null, RegistryView.Registry64)) + $(registry:HKEY_CURRENT_USER\SOFTWARE\ESRI\ArcGISPro@InstallDir) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Filters/ArcGISEverythingFilter.cs b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Filters/ArcGISEverythingFilter.cs new file mode 100644 index 0000000000..082f0baa1e --- /dev/null +++ b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Filters/ArcGISEverythingFilter.cs @@ -0,0 +1,10 @@ +using Speckle.Connectors.DUI.Models.Card.SendFilter; + +namespace Speckle.Connectors.ArcGIS.Filters; + +public class ArcGISEverythingFilter : EverythingSendFilter +{ + public override List GetObjectIds() => new(); // TODO + + public override bool CheckExpiry(string[] changedObjectIds) => true; +} diff --git a/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Filters/ArcGISSelectionFilter.cs b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Filters/ArcGISSelectionFilter.cs new file mode 100644 index 0000000000..8b203dc18c --- /dev/null +++ b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Filters/ArcGISSelectionFilter.cs @@ -0,0 +1,10 @@ +using Speckle.Connectors.DUI.Models.Card.SendFilter; + +namespace Speckle.Connectors.ArcGIS.Filters; + +public class ArcGISSelectionFilter : DirectSelectionSendFilter +{ + public override List GetObjectIds() => SelectedObjectIds; + + public override bool CheckExpiry(string[] changedObjectIds) => SelectedObjectIds.Intersect(changedObjectIds).Any(); +} diff --git a/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/HostApp/ArcGISSettings.cs b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/HostApp/ArcGISSettings.cs new file mode 100644 index 0000000000..860ea96abd --- /dev/null +++ b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/HostApp/ArcGISSettings.cs @@ -0,0 +1,21 @@ +using System.IO; +using Speckle.Connectors.Utils; +using Speckle.Core.Kits; + +namespace Speckle.Connectors.ArcGIS.HostApp; + +//poc: dupe code bewtween connectors +public class ArcGISSettings +{ + public ArcGISSettings(HostApplication hostAppInfo, HostAppVersion hostAppVersion) + { + HostAppInfo = hostAppInfo; + HostAppVersion = hostAppVersion; + Modules = new[] { new DirectoryInfo(typeof(ArcGISSettings).Assembly.Location).Parent.NotNull().FullName }; //poc: Net6 requires us to use this `location` property rather than ToString, should we use this everywhere? + } + + public HostApplication HostAppInfo { get; private set; } + public HostAppVersion HostAppVersion { get; private set; } + + public IReadOnlyList Modules { get; private set; } +} diff --git a/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/HostApp/SyncToQueuedTask.cs b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/HostApp/SyncToQueuedTask.cs new file mode 100644 index 0000000000..58aa7875ca --- /dev/null +++ b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/HostApp/SyncToQueuedTask.cs @@ -0,0 +1,9 @@ +using ArcGIS.Desktop.Framework.Threading.Tasks; +using Speckle.Connectors.Utils.Operations; + +namespace Speckle.Connectors.ArcGIS.HostApp; + +public class SyncToQueuedTask : ISyncToThread +{ + public Task RunOnThread(Func func) => QueuedTask.Run(func); +} diff --git a/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Images/AddInDesktop16.png b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Images/AddInDesktop16.png new file mode 100644 index 0000000000..5910bbf3dc Binary files /dev/null and b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Images/AddInDesktop16.png differ diff --git a/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Images/AddInDesktop32.png b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Images/AddInDesktop32.png new file mode 100644 index 0000000000..1d19084ae6 Binary files /dev/null and b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Images/AddInDesktop32.png differ diff --git a/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Images/s2logo_16.png b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Images/s2logo_16.png new file mode 100644 index 0000000000..61872f0d0c Binary files /dev/null and b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Images/s2logo_16.png differ diff --git a/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Images/s2logo_32.png b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Images/s2logo_32.png new file mode 100644 index 0000000000..2ad0c346ea Binary files /dev/null and b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Images/s2logo_32.png differ diff --git a/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Operations/Receive/HostObjectBuilder.cs b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Operations/Receive/HostObjectBuilder.cs new file mode 100644 index 0000000000..953a6b2abb --- /dev/null +++ b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Operations/Receive/HostObjectBuilder.cs @@ -0,0 +1,293 @@ +using System.Diagnostics.Contracts; +using ArcGIS.Desktop.Mapping; +using Speckle.Connectors.Utils.Builders; +using Speckle.Converters.Common; +using Speckle.Core.Logging; +using Speckle.Core.Models; +using Speckle.Converters.ArcGIS3.Utils; +using ArcGIS.Core.Geometry; +using Objects.GIS; +using Speckle.Connectors.Utils.Conversion; +using Speckle.Core.Models.GraphTraversal; +using Speckle.Converters.ArcGIS3; +using RasterLayer = Objects.GIS.RasterLayer; +using Speckle.Connectors.ArcGIS.Utils; + +namespace Speckle.Connectors.ArcGIS.Operations.Receive; + +public class ArcGISHostObjectBuilder : IHostObjectBuilder +{ + private readonly IRootToHostConverter _converter; + private readonly INonNativeFeaturesUtils _nonGisFeaturesUtils; + + // POC: figure out the correct scope to only initialize on Receive + private readonly IConversionContextStack _contextStack; + private readonly GraphTraversal _traverseFunction; + + public ArcGISHostObjectBuilder( + IRootToHostConverter converter, + IConversionContextStack contextStack, + INonNativeFeaturesUtils nonGisFeaturesUtils, + GraphTraversal traverseFunction + ) + { + _converter = converter; + _contextStack = contextStack; + _nonGisFeaturesUtils = nonGisFeaturesUtils; + _traverseFunction = traverseFunction; + } + + public HostObjectBuilderResult Build( + Base rootObject, + string projectName, + string modelName, + Action? onOperationProgressed, + CancellationToken cancellationToken + ) + { + // Prompt the UI conversion started. Progress bar will swoosh. + onOperationProgressed?.Invoke("Converting", null); + + var objectsToConvert = _traverseFunction + .Traverse(rootObject) + .Where(ctx => ctx.Current is not Collection || IsGISType(ctx.Current)) + .Where(ctx => HasGISParent(ctx) is false) + .ToList(); + + int allCount = objectsToConvert.Count; + int count = 0; + Dictionary conversionTracker = new(); + + // 1. convert everything + List results = new(objectsToConvert.Count); + List bakedObjectIds = new(); + foreach (TraversalContext ctx in objectsToConvert) + { + string[] path = GetLayerPath(ctx); + Base obj = ctx.Current; + + cancellationToken.ThrowIfCancellationRequested(); + try + { + if (IsGISType(obj)) + { + string nestedLayerPath = $"{string.Join("\\", path)}"; + string datasetId = (string)_converter.Convert(obj); + conversionTracker[ctx] = new ObjectConversionTracker(obj, nestedLayerPath, datasetId); + } + else + { + string nestedLayerPath = $"{string.Join("\\", path)}\\{obj.speckle_type.Split(".")[^1]}"; + Geometry converted = (Geometry)_converter.Convert(obj); + conversionTracker[ctx] = new ObjectConversionTracker(obj, nestedLayerPath, converted); + } + } + catch (Exception ex) when (!ex.IsFatal()) // DO NOT CATCH SPECIFIC STUFF, conversion errors should be recoverable + { + results.Add(new(Status.ERROR, obj, null, null, ex)); + } + onOperationProgressed?.Invoke("Converting", (double)++count / allCount); + } + + // 2. convert Database entries with non-GIS geometry datasets + onOperationProgressed?.Invoke("Writing to Database", null); + _nonGisFeaturesUtils.WriteGeometriesToDatasets(conversionTracker); + + // Create main group layer + Dictionary createdLayerGroups = new(); + Map map = _contextStack.Current.Document.Map; + GroupLayer groupLayer = LayerFactory.Instance.CreateGroupLayer(map, 0, $"{projectName}: {modelName}"); + createdLayerGroups["Basic Speckle Group"] = groupLayer; // key doesn't really matter here + + // 3. add layer and tables to the Table Of Content + int bakeCount = 0; + Dictionary bakedMapMembers = new(); + onOperationProgressed?.Invoke("Adding to Map", bakeCount); + + foreach (var item in conversionTracker) + { + cancellationToken.ThrowIfCancellationRequested(); + var trackerItem = conversionTracker[item.Key]; // updated tracker object + + // BAKE OBJECTS HERE + if (trackerItem.Exception != null) + { + results.Add(new(Status.ERROR, trackerItem.Base, null, null, trackerItem.Exception)); + } + else if (trackerItem.DatasetId == null) + { + results.Add( + new( + Status.ERROR, + trackerItem.Base, + null, + null, + new ArgumentException($"Unknown error: Dataset not created for {trackerItem.Base.speckle_type}") + ) + ); + } + else if (bakedMapMembers.TryGetValue(trackerItem.DatasetId, out MapMember? value)) + { + // add layer and layer URI to tracker + trackerItem.AddConvertedMapMember(value); + trackerItem.AddLayerURI(value.URI); + conversionTracker[item.Key] = trackerItem; // not necessary atm, but needed if we use conversionTracker further + // only add a report item + AddResultsFromTracker(trackerItem, results); + } + else + { + // add layer to Map + MapMember mapMember = AddDatasetsToMap(trackerItem, createdLayerGroups); + + // add layer and layer URI to tracker + trackerItem.AddConvertedMapMember(mapMember); + trackerItem.AddLayerURI(mapMember.URI); + conversionTracker[item.Key] = trackerItem; // not necessary atm, but needed if we use conversionTracker further + + // add layer URI to bakedIds + bakedObjectIds.Add(trackerItem.MappedLayerURI == null ? "" : trackerItem.MappedLayerURI); + + // mark dataset as already created + bakedMapMembers[trackerItem.DatasetId] = mapMember; + + // add report item + AddResultsFromTracker(trackerItem, results); + } + onOperationProgressed?.Invoke("Adding to Map", (double)++bakeCount / conversionTracker.Count); + } + bakedObjectIds.AddRange(createdLayerGroups.Values.Select(x => x.URI)); + + // TODO: validated a correct set regarding bakedobject ids + return new(bakedObjectIds, results); + } + + private void AddResultsFromTracker(ObjectConversionTracker trackerItem, List results) + { + if (trackerItem.MappedLayerURI == null) // should not happen + { + results.Add( + new( + Status.ERROR, + trackerItem.Base, + null, + null, + new ArgumentException($"Created Layer URI not found for {trackerItem.Base.speckle_type}") + ) + ); + } + else + { + // encode layer ID and ID of its feature in 1 object represented as string + ObjectID objectId = new(trackerItem.MappedLayerURI, trackerItem.DatasetRow); + if (trackerItem.HostAppGeom != null) // individual hostAppGeometry + { + results.Add( + new( + Status.SUCCESS, + trackerItem.Base, + objectId.ObjectIdToString(), + trackerItem.HostAppGeom.GetType().ToString() + ) + ); + } + else // hostApp Layers + { + results.Add( + new( + Status.SUCCESS, + trackerItem.Base, + objectId.ObjectIdToString(), + trackerItem.HostAppMapMember?.GetType().ToString() + ) + ); + } + } + } + + private MapMember AddDatasetsToMap( + ObjectConversionTracker trackerItem, + Dictionary createdLayerGroups + ) + { + // get layer details + string? datasetId = trackerItem.DatasetId; // should not ne null here + Uri uri = new($"{_contextStack.Current.Document.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)); + + GroupLayer 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); + layer.SetExpanded(true); + return layer; + } + catch (ArgumentException) + { + var table = StandaloneTableFactory.Instance.CreateStandaloneTable(uri, groupLayer, tableName: shortName); + return table; + } + } + + private GroupLayer CreateNestedGroupLayer(string nestedLayerPath, Dictionary createdLayerGroups) + { + GroupLayer lastGroup = createdLayerGroups.FirstOrDefault().Value; + if (lastGroup == null) // if layer not found + { + throw new InvalidOperationException("Speckle Layer Group not found"); + } + + // iterate through each nested level + string createdGroupPath = ""; + var allPathElements = nestedLayerPath.Split("\\").Where(x => !string.IsNullOrEmpty(x)); + foreach (string pathElement in allPathElements) + { + createdGroupPath += "\\" + pathElement; + if (createdLayerGroups.TryGetValue(createdGroupPath, out var existingGroupLayer)) + { + lastGroup = existingGroupLayer; + } + else + { + // create new GroupLayer under last found Group, named with last pathElement + lastGroup = LayerFactory.Instance.CreateGroupLayer(lastGroup, 0, pathElement); + lastGroup.SetExpanded(true); + } + createdLayerGroups[createdGroupPath] = lastGroup; + } + return lastGroup; + } + + [Pure] + private static string[] GetLayerPath(TraversalContext context) + { + string[] collectionBasedPath = context.GetAscendantOfType().Select(c => c.name).ToArray(); + string[] reverseOrderPath = + collectionBasedPath.Length != 0 ? collectionBasedPath : context.GetPropertyPath().ToArray(); + + 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/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Operations/Send/ArcGISRootObjectBuilder.cs b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Operations/Send/ArcGISRootObjectBuilder.cs new file mode 100644 index 0000000000..9b371194e0 --- /dev/null +++ b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Operations/Send/ArcGISRootObjectBuilder.cs @@ -0,0 +1,87 @@ +using System.Diagnostics; +using ArcGIS.Desktop.Mapping; +using Speckle.Autofac.DependencyInjection; +using Speckle.Connectors.Utils.Builders; +using Speckle.Connectors.Utils.Caching; +using Speckle.Connectors.Utils.Conversion; +using Speckle.Connectors.Utils.Operations; +using Speckle.Converters.Common; +using Speckle.Core.Logging; +using Speckle.Core.Models; + +namespace Speckle.Connectors.ArcGis.Operations.Send; + +/// +/// Stateless builder object to turn an ISendFilter into a object +/// +public class ArcGISRootObjectBuilder : IRootObjectBuilder +{ + private readonly IUnitOfWorkFactory _unitOfWorkFactory; + private readonly ISendConversionCache _sendConversionCache; + + public ArcGISRootObjectBuilder(IUnitOfWorkFactory unitOfWorkFactory, ISendConversionCache sendConversionCache) + { + _unitOfWorkFactory = unitOfWorkFactory; + _sendConversionCache = sendConversionCache; + } + + public RootObjectBuilderResult Build( + IReadOnlyList objects, + SendInfo sendInfo, + Action? onOperationProgressed = null, + CancellationToken ct = default + ) + { + // POC: does this feel like the right place? I am wondering if this should be called from within send/rcv? + // begin the unit of work + using var uow = _unitOfWorkFactory.Resolve(); + var converter = uow.Service; + + int count = 0; + + Collection rootObjectCollection = new(); //TODO: Collections + + List results = new(objects.Count); + var cacheHitCount = 0; + + foreach (MapMember mapMember in objects) + { + ct.ThrowIfCancellationRequested(); + var collectionHost = rootObjectCollection; + var applicationId = mapMember.URI; + + try + { + Base converted; + if (_sendConversionCache.TryGetValue(sendInfo.ProjectId, applicationId, out ObjectReference value)) + { + converted = value; + cacheHitCount++; + } + else + { + converted = converter.Convert(mapMember); + converted.applicationId = applicationId; + } + + // add to host + collectionHost.elements.Add(converted); + results.Add(new(Status.SUCCESS, applicationId, mapMember.GetType().Name, converted)); + } + catch (Exception ex) when (!ex.IsFatal()) + { + results.Add(new(Status.ERROR, applicationId, mapMember.GetType().Name, null, ex)); + // POC: add logging + } + + onOperationProgressed?.Invoke("Converting", (double)++count / objects.Count); + } + + // 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(rootObjectCollection, results); + } +} diff --git a/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Properties/launchSettings.json b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Properties/launchSettings.json new file mode 100644 index 0000000000..5be7c6369a --- /dev/null +++ b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Properties/launchSettings.json @@ -0,0 +1,9 @@ +{ + "profiles": { + "Speckle.Connectors.ArcGIS3": { + "commandName": "Executable", + "executablePath": "C:\\Program Files\\ArcGIS\\Pro\\bin\\ArcGISPro.exe", + "commandLineArgs": "" + } + } +} diff --git a/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Speckle.Connectors.ArcGIS3.csproj b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Speckle.Connectors.ArcGIS3.csproj new file mode 100644 index 0000000000..3cbc19ac77 --- /dev/null +++ b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Speckle.Connectors.ArcGIS3.csproj @@ -0,0 +1,37 @@ + + + + net6.0-windows + true + x64 + win-x64 + Speckle.Connectors.ArcGIS + true + false + true + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/SpeckleDUI3ViewModel.cs b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/SpeckleDUI3ViewModel.cs new file mode 100644 index 0000000000..9d74daac4c --- /dev/null +++ b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/SpeckleDUI3ViewModel.cs @@ -0,0 +1,42 @@ +using ArcGIS.Desktop.Framework; +using ArcGIS.Desktop.Framework.Contracts; + +namespace Speckle.Connectors.ArcGIS; + +internal sealed class SpeckleDUI3ViewModel : DockPane +{ + private const string DOCKPANE_ID = "SpeckleDUI3_SpeckleDUI3"; + + internal static void Create() + { + var pane = FrameworkApplication.DockPaneManager.Find(DOCKPANE_ID); + pane?.Activate(); + } + + /// + /// Called when the pane is initialized. + /// + protected override async Task InitializeAsync() + { + await base.InitializeAsync().ConfigureAwait(false); + } + + /// + /// Called when the pane is uninitialized. + /// + protected override async Task UninitializeAsync() + { + await base.UninitializeAsync().ConfigureAwait(false); + } +} + +/// +/// Button implementation to create a new instance of the pane and activate it. +/// +internal sealed class SpeckleDUI3OpenButton : Button +{ + protected override void OnClick() + { + SpeckleDUI3ViewModel.Create(); + } +} diff --git a/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/SpeckleDUI3Wrapper.cs b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/SpeckleDUI3Wrapper.cs new file mode 100644 index 0000000000..2c120c765a --- /dev/null +++ b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/SpeckleDUI3Wrapper.cs @@ -0,0 +1,17 @@ +using System.Windows.Controls; +using Speckle.Connectors.DUI.WebView; + +namespace Speckle.Connectors.ArcGIS; + +public class SpeckleDUI3Wrapper : UserControl +{ + public SpeckleDUI3Wrapper() + { + Initialize(); + } + + private void Initialize() + { + Content = SpeckleModule.Current.Container.Resolve(); + } +} diff --git a/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/SpeckleModule.cs b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/SpeckleModule.cs new file mode 100644 index 0000000000..e457309fcf --- /dev/null +++ b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/SpeckleModule.cs @@ -0,0 +1,51 @@ +using System.Reflection; +using ArcGIS.Desktop.Framework; +using Speckle.Autofac; +using Speckle.Autofac.DependencyInjection; +using Speckle.Connectors.ArcGIS.HostApp; +using Speckle.Core.Kits; +using Module = ArcGIS.Desktop.Framework.Contracts.Module; + +namespace Speckle.Connectors.ArcGIS; + +/// +/// This sample shows how to implement pane that contains an Edge WebView2 control using the built-in ArcGIS Pro SDK's WebBrowser control. For details on how to utilize the WebBrowser control in an add-in see here: https://github.com/Esri/arcgis-pro-sdk/wiki/ProConcepts-Framework#webbrowser For details on how to utilize the Microsoft Edge web browser control in an add-in see here: https://github.com/Esri/arcgis-pro-sdk/wiki/ProConcepts-Framework#webbrowser-control +/// +internal sealed class SpeckleModule : Module +{ + private static SpeckleModule? s_this; + + /// + /// Retrieve the singleton instance to this module here + /// + public static SpeckleModule Current => + s_this ??= (SpeckleModule)FrameworkApplication.FindModule("ConnectorArcGIS_Module"); + + public SpeckleContainer Container { get; } + + public SpeckleModule() + { + AppDomain.CurrentDomain.AssemblyResolve += AssemblyResolver.OnAssemblyResolve; + + var builder = SpeckleContainerBuilder.CreateInstance(); + + // Register Settings + var arcgisSettings = new ArcGISSettings(HostApplications.ArcGIS, HostAppVersion.v3); + + Container = builder + .LoadAutofacModules(Assembly.GetExecutingAssembly(), arcgisSettings.Modules) + .AddSingleton(arcgisSettings) + .Build(); + } + + /// + /// Called by Framework when ArcGIS Pro is closing + /// + /// False to prevent Pro from closing, otherwise True + protected override bool CanUnload() + { + //TODO - add your business logic + //return false to ~cancel~ Application close + return true; + } +} diff --git a/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Utils/ArcGisDocumentStore.cs b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Utils/ArcGisDocumentStore.cs new file mode 100644 index 0000000000..eb420af874 --- /dev/null +++ b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Utils/ArcGisDocumentStore.cs @@ -0,0 +1,131 @@ +using System.Xml.Linq; +using ArcGIS.Desktop.Core.Events; +using ArcGIS.Desktop.Framework.Threading.Tasks; +using ArcGIS.Desktop.Mapping; +using ArcGIS.Desktop.Mapping.Events; +using Speckle.Connectors.DUI.Bridge; +using Speckle.Connectors.DUI.Models; +using Speckle.Connectors.Utils; +using Speckle.Newtonsoft.Json; + +namespace Speckle.Connectors.ArcGIS.Utils; + +public class ArcGISDocumentStore : DocumentModelStore +{ + public ArcGISDocumentStore( + JsonSerializerSettings serializerOption, + ITopLevelExceptionHandler topLevelExceptionHandler + ) + : base(serializerOption, true) + { + ActiveMapViewChangedEvent.Subscribe(a => topLevelExceptionHandler.CatchUnhandled(() => OnMapViewChanged(a)), true); + ProjectSavingEvent.Subscribe( + _ => + { + topLevelExceptionHandler.CatchUnhandled(OnProjectSaving); + return Task.CompletedTask; + }, + true + ); + ProjectClosingEvent.Subscribe( + _ => + { + topLevelExceptionHandler.CatchUnhandled(OnProjectClosing); + return Task.CompletedTask; + }, + true + ); + + // in case plugin was loaded into already opened Map, read metadata from the current Map + if (IsDocumentInit == false && MapView.Active != null) + { + IsDocumentInit = true; + ReadFromFile(); + OnDocumentChanged(); + } + } + + private void OnProjectClosing() + { + if (MapView.Active is null) + { + return; + } + + WriteToFile(); + } + + private void OnProjectSaving() + { + if (MapView.Active is not null) + { + WriteToFile(); + } + } + + /// + /// On map view switch, this event trigger twice, first for outgoing view, second for incoming view. + /// + private void OnMapViewChanged(ActiveMapViewChangedEventArgs args) + { + if (args.IncomingView is null) + { + return; + } + + IsDocumentInit = true; + ReadFromFile(); + 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) + { + speckleModelCardsElement.ReplaceWith(xmlModelCards); + } + else + { + existingXmlDocument.Root?.Add(xmlModelCards); + } + + 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(); + }); + } +} diff --git a/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Utils/MapMemberFeature.cs b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Utils/MapMemberFeature.cs new file mode 100644 index 0000000000..026e5165dc --- /dev/null +++ b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Utils/MapMemberFeature.cs @@ -0,0 +1,16 @@ +using ArcGIS.Desktop.Mapping; + +namespace Speckle.Connectors.ArcGIS.Utils; + +// bind together a layer object on the map, and auto-assigned ID if the specific feature +public readonly struct MapMemberFeature +{ + public int? FeatureId { get; } // unique feature id (start from 0) of a feature in the layer + public MapMember MapMember { get; } // layer object on the Map + + public MapMemberFeature(MapMember mapMember, int? featureId) + { + MapMember = mapMember; + FeatureId = featureId; + } +} diff --git a/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Utils/ObjectID.cs b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Utils/ObjectID.cs new file mode 100644 index 0000000000..8374181c5b --- /dev/null +++ b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Utils/ObjectID.cs @@ -0,0 +1,38 @@ +namespace Speckle.Connectors.ArcGIS.Utils; + +// this struct is needed to be able to parse single-string value into IDs of both a layer, and it's individual feature +public struct ObjectID +{ + private const string FEATURE_ID_SEPARATOR = "__speckleFeatureId__"; + public string MappedLayerURI { get; } // unique ID of the layer on the map + public int? FeatureId { get; } // unique feature id (start from 0) of a feature in the layer + + public ObjectID(string encodedId) + { + List stringParts = encodedId.Split(FEATURE_ID_SEPARATOR).ToList(); + MappedLayerURI = stringParts[0]; + FeatureId = null; + if (stringParts.Count > 1) + { + FeatureId = Convert.ToInt32(stringParts[1]); + } + } + + public ObjectID(string layerId, int? featureId) + { + MappedLayerURI = layerId; + FeatureId = featureId; + } + + public readonly string ObjectIdToString() + { + if (FeatureId == null) + { + return $"{MappedLayerURI}"; + } + else + { + return $"{MappedLayerURI}{FEATURE_ID_SEPARATOR}{FeatureId}"; + } + } +} diff --git a/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/packages.lock.json b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/packages.lock.json new file mode 100644 index 0000000000..b975016a9b --- /dev/null +++ b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/packages.lock.json @@ -0,0 +1,510 @@ +{ + "version": 2, + "dependencies": { + "net6.0-windows7.0": { + "Esri.ArcGISPro.Extensions30": { + "type": "Direct", + "requested": "[3.2.0.49743, )", + "resolved": "3.2.0.49743", + "contentHash": "fmnYm+mD14Cz0Uqh1ij37SfLJerkyFHK5581y5tXT/l3H2ZvUmVuuxjYquXzyzj9p7IexQzMW4xCpxe+mD922g==" + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Direct", + "requested": "[7.0.0, )", + "resolved": "7.0.0", + "contentHash": "kmn78+LPVMOWeITUjIlfxUPDsI0R6G0RkeAMBmQxAJ7vBJn4q2dTva7pWi65ceN5vPGjJ9q/Uae2WKgvfktJAw==" + }, + "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.5, )", + "resolved": "0.9.5", + "contentHash": "oU/L7pN1R7q8KkbrpQ3WJnHirPHqn+9DEA7asOcUiggV5dzVg1A/VYs7GOSusD24njxXh03tE3a2oTLOjt3cVg==" + }, + "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.AspNetCore.Http": { + "type": "Transitive", + "resolved": "2.2.2", + "contentHash": "BAibpoItxI5puk7YJbIGj95arZueM8B8M5xT1fXBn3hb3L2G3ucrZcYXv1gXdaroLbntUs8qeV8iuBrpjQsrKw==", + "dependencies": { + "Microsoft.AspNetCore.Http.Abstractions": "2.2.0", + "Microsoft.AspNetCore.WebUtilities": "2.2.0", + "Microsoft.Extensions.ObjectPool": "2.2.0", + "Microsoft.Extensions.Options": "2.2.0", + "Microsoft.Net.Http.Headers": "2.2.0" + } + }, + "Microsoft.AspNetCore.Http.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "Nxs7Z1q3f1STfLYKJSVXCs1iBl+Ya6E8o4Oy1bCxJ/rNI44E/0f6tbsrVqAWfB7jlnJfyaAtIalBVxPKUPQb4Q==", + "dependencies": { + "Microsoft.AspNetCore.Http.Features": "2.2.0", + "System.Text.Encodings.Web": "4.5.0" + } + }, + "Microsoft.AspNetCore.Http.Features": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "ziFz5zH8f33En4dX81LW84I6XrYXKf9jg6aM39cM+LffN9KJahViKZ61dGMSO2gd3e+qe5yBRwsesvyqlZaSMg==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.2.0" + } + }, + "Microsoft.AspNetCore.WebUtilities": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "9ErxAAKaDzxXASB/b5uLEkLgUWv1QbeVxyJYEHQwMaxXOeFFVkQxiq8RyfVcifLU7NR0QY0p3acqx4ZpYfhHDg==", + "dependencies": { + "Microsoft.Net.Http.Headers": "2.2.0", + "System.Text.Encodings.Web": "4.5.0" + } + }, + "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.DependencyInjection": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "elNeOmkeX3eDVG6pYVeV82p29hr+UKDaBhrZyWvWLw/EVZSYEkZlQdkp0V39k/Xehs2Qa0mvoCvkVj3eQxNQ1Q==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "h3j/QfmFN4S0w4C2A6X7arXij/M/OVw3uQHSOFxnND4DyAzO1F9eMX7Eti7lU/OkSthEE0WzRsfT/Dmx86jzCw==" + }, + "Microsoft.Extensions.Logging": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "Nw2muoNrOG5U5qa2ZekXwudUn2BJcD41e65zwmDHb1fQegTX66UokLWZkJRpqSSHXDOWZ5V0iqhbxOEky91atA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "7.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0", + "Microsoft.Extensions.Logging.Abstractions": "7.0.0", + "Microsoft.Extensions.Options": "7.0.0" + } + }, + "Microsoft.Extensions.ObjectPool": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "gA8H7uQOnM5gb+L0uTNjViHYr+hRDqCdfugheGo/MxQnuHzmhhzCBTIPm19qL1z1Xe0NEMabfcOBGv9QghlZ8g==" + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "lP1yBnTTU42cKpMozuafbvNtQ7QcBjr/CcK3bYOGEMH55Fjt+iecXjT6chR7vbgCMqy3PG3aNQSZgo/EuY/9qQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0", + "Microsoft.Extensions.Primitives": "7.0.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "um1KU5kxcRp3CNuI8o/GrZtD4AIOXDk+RLsytjZ9QPok3ttLUelLKpilVPuaFT3TFjOhSibUAso0odbOaCDj3Q==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "Microsoft.Net.Http.Headers": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "iZNkjYqlo8sIOI0bQfpsSoMTmB/kyvmV2h225ihyZT33aTp48ZpF6qYnXxzSXmHt8DpBAwBTX+1s1UFLbYfZKg==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.2.0", + "System.Buffers": "4.5.0" + } + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "Polly": { + "type": "Transitive", + "resolved": "7.2.3", + "contentHash": "DeCY0OFbNdNxsjntr1gTXHJ5pKUwYzp04Er2LLeN3g6pWhffsGuKVfMBLe1lw7x76HrPkLxKEFxBlpRxS2nDEQ==" + }, + "Polly.Contrib.WaitAndRetry": { + "type": "Transitive", + "resolved": "1.1.1", + "contentHash": "1MUQLiSo4KDkQe6nzQRhIU05lm9jlexX5BVsbuw0SL82ynZ+GzAHQxJVDPVBboxV37Po3SG077aX8DuSy8TkaA==" + }, + "Polly.Extensions.Http": { + "type": "Transitive", + "resolved": "3.0.0", + "contentHash": "drrG+hB3pYFY7w1c3BD+lSGYvH2oIclH8GRSehgfyP5kjnFnHKQuuBhuHLv+PWyFuaTDyk/vfRpnxOzd11+J8g==", + "dependencies": { + "Polly": "7.1.0" + } + }, + "Sentry": { + "type": "Transitive", + "resolved": "3.33.0", + "contentHash": "8vbD2o6IR2wrRrkSiRbnodWGWUOqIlwYtzpjvPNOb5raJdOf+zxMwfS8f6nx9bmrTTfDj7KrCB8C/5OuicAc8A==" + }, + "Sentry.Serilog": { + "type": "Transitive", + "resolved": "3.33.0", + "contentHash": "V8BU7QGWg2qLYfNPqtuTBhC1opysny5l+Ifp6J6PhOeAxU0FssR7nYfbJVetrnLIoh2rd3DlJ6hHYYQosQYcUQ==", + "dependencies": { + "Sentry": "3.33.0", + "Serilog": "2.10.0" + } + }, + "Serilog": { + "type": "Transitive", + "resolved": "2.12.0", + "contentHash": "xaiJLIdu6rYMKfQMYUZgTy8YK7SMZjB4Yk50C/u//Z4OsvxkUfSPJy4nknfvwAC34yr13q7kcyh4grbwhSxyZg==" + }, + "Serilog.Enrichers.ClientInfo": { + "type": "Transitive", + "resolved": "1.3.0", + "contentHash": "mTc7PM+wC9Hr7LWSwqt5mmnlAr7RJs+eTb3PGPRhwdOackk95MkhUZognuxXEdlW19HAFNmEBTSBY5DfLwM8jQ==", + "dependencies": { + "Microsoft.AspNetCore.Http": "2.2.2", + "Serilog": "2.9.0" + } + }, + "Serilog.Exceptions": { + "type": "Transitive", + "resolved": "8.4.0", + "contentHash": "nc/+hUw3lsdo0zCj0KMIybAu7perMx79vu72w0za9Nsi6mWyNkGXxYxakAjWB7nEmYL6zdmhEQRB4oJ2ALUeug==", + "dependencies": { + "Serilog": "2.8.0", + "System.Reflection.TypeExtensions": "4.7.0" + } + }, + "Serilog.Formatting.Compact": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "pNroKVjo+rDqlxNG5PXkRLpfSCuDOBY0ri6jp9PLe505ljqwhwZz8ospy2vWhQlFu5GkIesh3FcDs4n7sWZODA==", + "dependencies": { + "Serilog": "2.8.0" + } + }, + "Serilog.Sinks.Console": { + "type": "Transitive", + "resolved": "4.1.0", + "contentHash": "K6N5q+5fetjnJPvCmkWOpJ/V8IEIoMIB1s86OzBrbxwTyHxdx3pmz4H+8+O/Dc/ftUX12DM1aynx/dDowkwzqg==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "Serilog.Sinks.File": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "uwV5hdhWPwUH1szhO8PJpFiahqXmzPzJT/sOijH/kFgUx+cyoDTMM8MHD0adw9+Iem6itoibbUXHYslzXsLEAg==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "Serilog.Sinks.PeriodicBatching": { + "type": "Transitive", + "resolved": "3.1.0", + "contentHash": "NDWR7m3PalVlGEq3rzoktrXikjFMLmpwF0HI4sowo8YDdU+gqPlTHlDQiOGxHfB0sTfjPA9JjA7ctKG9zqjGkw==", + "dependencies": { + "Serilog": "2.0.0" + } + }, + "Serilog.Sinks.Seq": { + "type": "Transitive", + "resolved": "5.2.2", + "contentHash": "1Csmo5ua7NKUe0yXUx+zsRefjAniPWcXFhUXxXG8pwo0iMiw2gjn9SOkgYnnxbgWqmlGv236w0N/dHc2v5XwMg==", + "dependencies": { + "Serilog": "2.12.0", + "Serilog.Formatting.Compact": "1.1.0", + "Serilog.Sinks.File": "5.0.0", + "Serilog.Sinks.PeriodicBatching": "3.1.0" + } + }, + "SerilogTimings": { + "type": "Transitive", + "resolved": "3.0.1", + "contentHash": "Zs28eTgszAMwpIrbBnWHBI50yuxL50p/dmAUWmy75+axdZYK/Sjm5/5m1N/CisR8acJUhTVcjPZrsB1P5iv0Uw==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "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.Buffers": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "pL2ChpaRRWI/p4LXyy4RgeWlYF2sgfj/pnVMvBqwNFr5cXg7CXNnWZWxrOONLg8VGdFB8oB+EG2Qw4MLgTOe+A==" + }, + "System.DoubleNumerics": { + "type": "Transitive", + "resolved": "3.1.3", + "contentHash": "KRKEM/L3KBodjA9VOg3EifFVWUY6EOqaMB05UvPEDm7Zeby/kZW+4kdWUEPzW6xtkwf46p661L9NrbeeQhtLzw==", + "dependencies": { + "NETStandard.Library": "1.6.1" + } + }, + "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.TypeExtensions": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "VybpaOQQhqE6siHppMktjfGBw1GCwvCqiufqmP8F1nj7fTUNtW35LOEt3UZTEsECfo+ELAl/9o9nJx3U91i7vA==" + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "Xg4G4Indi4dqP1iuAiMSwpiWS54ZghzR644OtsRCm/m/lBMG8dUBhLVN7hLm8NNrNTR+iGbshCPTwrvxZPlm4g==" + }, + "speckle.autofac": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "[7.0.0, )" + } + }, + "speckle.connectors.dui": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "[7.0.0, )", + "Speckle.Autofac": "[2.0.999-local, )", + "Speckle.Connectors.Utils": "[2.0.999-local, )", + "Speckle.Core": "[3.0.1-alpha.14, )", + "System.Threading.Tasks.Dataflow": "[6.0.0, )" + } + }, + "speckle.connectors.dui.webview": { + "type": "Project", + "dependencies": { + "Microsoft.Web.WebView2": "[1.0.1823.32, )", + "Speckle.Connectors.DUI": "[2.0.999-local, )" + } + }, + "speckle.connectors.utils": { + "type": "Project", + "dependencies": { + "Serilog.Extensions.Logging": "[7.0.0, )", + "Speckle.Autofac": "[2.0.999-local, )", + "Speckle.Core": "[3.0.1-alpha.14, )" + } + }, + "speckle.converters.arcgis3": { + "type": "Project", + "dependencies": { + "Esri.ArcGISPro.Extensions30": "[3.2.0.49743, )", + "Speckle.Converters.Common": "[2.0.999-local, )" + } + }, + "speckle.converters.arcgis3.dependencyinjection": { + "type": "Project", + "dependencies": { + "Autofac": "[5.2.0, )", + "Speckle.Converters.ArcGIS3": "[2.0.999-local, )", + "Speckle.Converters.Common.DependencyInjection": "[2.0.999-local, )" + } + }, + "speckle.converters.common": { + "type": "Project", + "dependencies": { + "Speckle.Autofac": "[2.0.999-local, )", + "Speckle.Objects": "[3.0.1-alpha.14, )" + } + }, + "speckle.converters.common.dependencyinjection": { + "type": "Project", + "dependencies": { + "Speckle.Autofac": "[2.0.999-local, )", + "Speckle.Converters.Common": "[2.0.999-local, )" + } + }, + "Autofac": { + "type": "CentralTransitive", + "requested": "[5.2.0, )", + "resolved": "5.2.0", + "contentHash": "V8dBH0dsv75uDzl7Sw+HkhKDPUw2eXnlMjcSVMH+tLo2s67MpTKGyDj1pDcpR+IF2u4YRs0s3/x7R88YJzIWvg==" + }, + "Microsoft.Web.WebView2": { + "type": "CentralTransitive", + "requested": "[1.0.1823.32, )", + "resolved": "1.0.1823.32", + "contentHash": "ppRcWBUNggFIqyJp7PfgS4Oe8/7yli/mHcTNDOaqo3ZzJWnv9AOr0WE9ceEP5SPs1M7CQ3QvdGMR7KNz0v09EA==" + }, + "Serilog.Extensions.Logging": { + "type": "CentralTransitive", + "requested": "[7.0.0, )", + "resolved": "7.0.0", + "contentHash": "9faU0zNQqU7I6soVhLUMYaGNpgWv6cKlKb2S5AnS8gXxzW/em5Ladm/6FMrWTnX41cdbdGPOWNAo6adi4WaJ6A==", + "dependencies": { + "Microsoft.Extensions.Logging": "7.0.0", + "Serilog": "2.12.0" + } + }, + "Speckle.Core": { + "type": "CentralTransitive", + "requested": "[3.0.1-alpha.14, )", + "resolved": "3.0.1-alpha.14", + "contentHash": "RzQPVIGFFkKvG56YLr8ACtiwdWJE6IJ9vCQ4qHa0PIsUEpfzAIAi59jnzqtByOFC0FiFrFPow9bkfzylaZorAA==", + "dependencies": { + "GraphQL.Client": "6.0.0", + "Microsoft.CSharp": "4.7.0", + "Microsoft.Data.Sqlite": "7.0.5", + "Polly": "7.2.3", + "Polly.Contrib.WaitAndRetry": "1.1.1", + "Polly.Extensions.Http": "3.0.0", + "Sentry": "3.33.0", + "Sentry.Serilog": "3.33.0", + "Serilog": "2.12.0", + "Serilog.Enrichers.ClientInfo": "1.3.0", + "Serilog.Exceptions": "8.4.0", + "Serilog.Sinks.Console": "4.1.0", + "Serilog.Sinks.Seq": "5.2.2", + "SerilogTimings": "3.0.1", + "Speckle.Newtonsoft.Json": "13.0.2", + "System.DoubleNumerics": "3.1.3" + } + }, + "Speckle.Objects": { + "type": "CentralTransitive", + "requested": "[3.0.1-alpha.14, )", + "resolved": "3.0.1-alpha.14", + "contentHash": "z38LGryMvh7iU1uBW+4uo5DwsB3CwRgLt2uFexWFx3mPSid+A0l5XcJzOgLwgFhNl6B42Ryz4ezBsddTp1Uc/g==", + "dependencies": { + "Speckle.Core": "3.0.1-alpha.14" + } + }, + "System.Threading.Tasks.Dataflow": { + "type": "CentralTransitive", + "requested": "[6.0.0, )", + "resolved": "6.0.0", + "contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA==" + } + }, + "net6.0-windows7.0/win-x64": { + "SQLitePCLRaw.lib.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "2C9Q9eX7CPLveJA0rIhf9RXAvu+7nWZu1A2MdG6SD/NOu26TakGgL1nsbc0JAspGijFOo3HoN79xrx8a368fBg==" + }, + "Microsoft.Web.WebView2": { + "type": "CentralTransitive", + "requested": "[1.0.1823.32, )", + "resolved": "1.0.1823.32", + "contentHash": "ppRcWBUNggFIqyJp7PfgS4Oe8/7yli/mHcTNDOaqo3ZzJWnv9AOr0WE9ceEP5SPs1M7CQ3QvdGMR7KNz0v09EA==" + } + } + } +} \ No newline at end of file diff --git a/DUI3-DX/Connectors/Autocad/Speckle.Connectors.Autocad2023/Speckle.Connectors.Autocad2023.csproj b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.Autocad2023/Speckle.Connectors.Autocad2023.csproj new file mode 100644 index 0000000000..88e6b47cdf --- /dev/null +++ b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.Autocad2023/Speckle.Connectors.Autocad2023.csproj @@ -0,0 +1,25 @@ + + + Speckle.Connectors.Autocad + net48 + x64 + true + Program + $(ProgramW6432)\Autodesk\AutoCAD 2023\acad.exe + $(DefineConstants);AUTOCAD2023;AUTOCAD + + + + + + + + + + + + + + + + diff --git a/DUI3-DX/Connectors/Autocad/Speckle.Connectors.Autocad2023/packages.lock.json b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.Autocad2023/packages.lock.json new file mode 100644 index 0000000000..0fff9ad56a --- /dev/null +++ b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.Autocad2023/packages.lock.json @@ -0,0 +1,553 @@ +{ + "version": 2, + "dependencies": { + ".NETFramework,Version=v4.8": { + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Direct", + "requested": "[7.0.0, )", + "resolved": "7.0.0", + "contentHash": "kmn78+LPVMOWeITUjIlfxUPDsI0R6G0RkeAMBmQxAJ7vBJn4q2dTva7pWi65ceN5vPGjJ9q/Uae2WKgvfktJAw==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Memory": "4.5.5" + } + }, + "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.AutoCAD.API": { + "type": "Direct", + "requested": "[2023.0.0, )", + "resolved": "2023.0.0", + "contentHash": "aNfiNw9zRW8pCl8AAQK7afEJuea4bJ4sFNsGVSDrdq1egaonZrwALU01dSyFNCE8tne86eVjlprpOGG6r0+G/A==" + }, + "Speckle.InterfaceGenerator": { + "type": "Direct", + "requested": "[0.9.5, )", + "resolved": "0.9.5", + "contentHash": "oU/L7pN1R7q8KkbrpQ3WJnHirPHqn+9DEA7asOcUiggV5dzVg1A/VYs7GOSusD24njxXh03tE3a2oTLOjt3cVg==" + }, + "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": "7.0.0", + "contentHash": "3aeMZ1N0lJoSyzqiP03hqemtb1BijhsJADdobn/4nsMJ8V1H+CrpuduUe4hlRdx+ikBQju1VGjMD1GJ3Sk05Eg==", + "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.DependencyInjection": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "elNeOmkeX3eDVG6pYVeV82p29hr+UKDaBhrZyWvWLw/EVZSYEkZlQdkp0V39k/Xehs2Qa0mvoCvkVj3eQxNQ1Q==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "7.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0", + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "h3j/QfmFN4S0w4C2A6X7arXij/M/OVw3uQHSOFxnND4DyAzO1F9eMX7Eti7lU/OkSthEE0WzRsfT/Dmx86jzCw==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "7.0.0", + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "Microsoft.Extensions.Logging": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "Nw2muoNrOG5U5qa2ZekXwudUn2BJcD41e65zwmDHb1fQegTX66UokLWZkJRpqSSHXDOWZ5V0iqhbxOEky91atA==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "7.0.0", + "Microsoft.Extensions.DependencyInjection": "7.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0", + "Microsoft.Extensions.Logging.Abstractions": "7.0.0", + "Microsoft.Extensions.Options": "7.0.0", + "System.Diagnostics.DiagnosticSource": "7.0.0", + "System.ValueTuple": "4.5.0" + } + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "lP1yBnTTU42cKpMozuafbvNtQ7QcBjr/CcK3bYOGEMH55Fjt+iecXjT6chR7vbgCMqy3PG3aNQSZgo/EuY/9qQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0", + "Microsoft.Extensions.Primitives": "7.0.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "um1KU5kxcRp3CNuI8o/GrZtD4AIOXDk+RLsytjZ9QPok3ttLUelLKpilVPuaFT3TFjOhSibUAso0odbOaCDj3Q==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "Polly": { + "type": "Transitive", + "resolved": "7.2.3", + "contentHash": "DeCY0OFbNdNxsjntr1gTXHJ5pKUwYzp04Er2LLeN3g6pWhffsGuKVfMBLe1lw7x76HrPkLxKEFxBlpRxS2nDEQ==" + }, + "Polly.Contrib.WaitAndRetry": { + "type": "Transitive", + "resolved": "1.1.1", + "contentHash": "1MUQLiSo4KDkQe6nzQRhIU05lm9jlexX5BVsbuw0SL82ynZ+GzAHQxJVDPVBboxV37Po3SG077aX8DuSy8TkaA==" + }, + "Polly.Extensions.Http": { + "type": "Transitive", + "resolved": "3.0.0", + "contentHash": "drrG+hB3pYFY7w1c3BD+lSGYvH2oIclH8GRSehgfyP5kjnFnHKQuuBhuHLv+PWyFuaTDyk/vfRpnxOzd11+J8g==", + "dependencies": { + "Polly": "7.1.0" + } + }, + "Sentry": { + "type": "Transitive", + "resolved": "3.33.0", + "contentHash": "8vbD2o6IR2wrRrkSiRbnodWGWUOqIlwYtzpjvPNOb5raJdOf+zxMwfS8f6nx9bmrTTfDj7KrCB8C/5OuicAc8A==", + "dependencies": { + "System.Reflection.Metadata": "5.0.0", + "System.Runtime.InteropServices.RuntimeInformation": "4.3.0", + "System.Text.Json": "5.0.2" + } + }, + "Sentry.Serilog": { + "type": "Transitive", + "resolved": "3.33.0", + "contentHash": "V8BU7QGWg2qLYfNPqtuTBhC1opysny5l+Ifp6J6PhOeAxU0FssR7nYfbJVetrnLIoh2rd3DlJ6hHYYQosQYcUQ==", + "dependencies": { + "Sentry": "3.33.0", + "Serilog": "2.7.1" + } + }, + "Serilog": { + "type": "Transitive", + "resolved": "2.12.0", + "contentHash": "xaiJLIdu6rYMKfQMYUZgTy8YK7SMZjB4Yk50C/u//Z4OsvxkUfSPJy4nknfvwAC34yr13q7kcyh4grbwhSxyZg==" + }, + "Serilog.Enrichers.ClientInfo": { + "type": "Transitive", + "resolved": "1.3.0", + "contentHash": "mTc7PM+wC9Hr7LWSwqt5mmnlAr7RJs+eTb3PGPRhwdOackk95MkhUZognuxXEdlW19HAFNmEBTSBY5DfLwM8jQ==", + "dependencies": { + "Serilog": "2.4.0" + } + }, + "Serilog.Exceptions": { + "type": "Transitive", + "resolved": "8.4.0", + "contentHash": "nc/+hUw3lsdo0zCj0KMIybAu7perMx79vu72w0za9Nsi6mWyNkGXxYxakAjWB7nEmYL6zdmhEQRB4oJ2ALUeug==", + "dependencies": { + "Serilog": "2.8.0" + } + }, + "Serilog.Formatting.Compact": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "pNroKVjo+rDqlxNG5PXkRLpfSCuDOBY0ri6jp9PLe505ljqwhwZz8ospy2vWhQlFu5GkIesh3FcDs4n7sWZODA==", + "dependencies": { + "Serilog": "2.8.0" + } + }, + "Serilog.Sinks.Console": { + "type": "Transitive", + "resolved": "4.1.0", + "contentHash": "K6N5q+5fetjnJPvCmkWOpJ/V8IEIoMIB1s86OzBrbxwTyHxdx3pmz4H+8+O/Dc/ftUX12DM1aynx/dDowkwzqg==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "Serilog.Sinks.File": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "uwV5hdhWPwUH1szhO8PJpFiahqXmzPzJT/sOijH/kFgUx+cyoDTMM8MHD0adw9+Iem6itoibbUXHYslzXsLEAg==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "Serilog.Sinks.PeriodicBatching": { + "type": "Transitive", + "resolved": "3.1.0", + "contentHash": "NDWR7m3PalVlGEq3rzoktrXikjFMLmpwF0HI4sowo8YDdU+gqPlTHlDQiOGxHfB0sTfjPA9JjA7ctKG9zqjGkw==", + "dependencies": { + "Serilog": "2.0.0" + } + }, + "Serilog.Sinks.Seq": { + "type": "Transitive", + "resolved": "5.2.2", + "contentHash": "1Csmo5ua7NKUe0yXUx+zsRefjAniPWcXFhUXxXG8pwo0iMiw2gjn9SOkgYnnxbgWqmlGv236w0N/dHc2v5XwMg==", + "dependencies": { + "Serilog": "2.12.0", + "Serilog.Formatting.Compact": "1.1.0", + "Serilog.Sinks.File": "5.0.0", + "Serilog.Sinks.PeriodicBatching": "3.1.0" + } + }, + "SerilogTimings": { + "type": "Transitive", + "resolved": "3.0.1", + "contentHash": "Zs28eTgszAMwpIrbBnWHBI50yuxL50p/dmAUWmy75+axdZYK/Sjm5/5m1N/CisR8acJUhTVcjPZrsB1P5iv0Uw==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "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.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, + "System.Collections.Immutable": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "FXkLXiK0sVVewcso0imKQoOxjoPAj42R8HtjjbSjVPAzwDfzoyoznWxgA3c38LDbN9SJux1xXoXYAhz98j7r2g==", + "dependencies": { + "System.Memory": "4.5.4" + } + }, + "System.Diagnostics.DiagnosticSource": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "9W0ewWDuAyDqS2PigdTxk6jDKonfgscY/hP8hm7VpxYhNHZHKvZTdRckberlFk3VnCmr3xBUyMBut12Q+T2aOw==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "System.DoubleNumerics": { + "type": "Transitive", + "resolved": "3.1.3", + "contentHash": "KRKEM/L3KBodjA9VOg3EifFVWUY6EOqaMB05UvPEDm7Zeby/kZW+4kdWUEPzW6xtkwf46p661L9NrbeeQhtLzw==", + "dependencies": { + "NETStandard.Library": "1.6.1" + } + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Numerics.Vectors": "4.5.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + }, + "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.5.0", + "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" + }, + "System.Reactive": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "System.Reflection.Metadata": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "5NecZgXktdGg34rh1OenY1rFNDCI8xSjFr+Z4OU4cU06AQHUdRnIIEeWENu3Wl4YowbzkymAIMvi3WyK9U53pQ==", + "dependencies": { + "System.Collections.Immutable": "5.0.0" + } + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + }, + "System.Runtime.InteropServices.RuntimeInformation": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==" + }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "5.0.1", + "contentHash": "KmJ+CJXizDofbq6mpqDoRRLcxgOd2z9X3XoFNULSbvbqVRZkFX3istvr+MUjL6Zw1RT+RNdoI4GYidIINtgvqQ==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Memory": "4.5.4" + } + }, + "System.Text.Json": { + "type": "Transitive", + "resolved": "5.0.2", + "contentHash": "I47dVIGiV6SfAyppphxqupertT/5oZkYLDCX6vC3HpOI4ZLjyoKAreUoem2ie6G0RbRuFrlqz/PcTQjfb2DOfQ==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "5.0.0", + "System.Buffers": "4.5.1", + "System.Memory": "4.5.4", + "System.Numerics.Vectors": "4.5.0", + "System.Runtime.CompilerServices.Unsafe": "5.0.0", + "System.Text.Encodings.Web": "5.0.1", + "System.Threading.Tasks.Extensions": "4.5.4", + "System.ValueTuple": "4.5.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" + } + }, + "System.ValueTuple": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "okurQJO6NRE/apDIP23ajJ0hpiNmJ+f0BwOlB/cSqTLQlw5upkf+5+96+iG2Jw40G1fCVCyPz/FhIABUjMR+RQ==" + }, + "speckle.autofac": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "[7.0.0, )" + } + }, + "speckle.connectors.dui": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "[7.0.0, )", + "Speckle.Autofac": "[2.0.999-local, )", + "Speckle.Connectors.Utils": "[2.0.999-local, )", + "Speckle.Core": "[3.0.1-alpha.14, )", + "System.Threading.Tasks.Dataflow": "[6.0.0, )" + } + }, + "speckle.connectors.dui.webview": { + "type": "Project", + "dependencies": { + "Microsoft.Web.WebView2": "[1.0.1823.32, )", + "Speckle.Connectors.DUI": "[2.0.999-local, )" + } + }, + "speckle.connectors.utils": { + "type": "Project", + "dependencies": { + "Serilog.Extensions.Logging": "[7.0.0, )", + "Speckle.Autofac": "[2.0.999-local, )", + "Speckle.Core": "[3.0.1-alpha.14, )" + } + }, + "speckle.converters.autocad2023": { + "type": "Project", + "dependencies": { + "Speckle.AutoCAD.API": "[2023.0.0, )", + "Speckle.Converters.Common": "[2.0.999-local, )" + } + }, + "speckle.converters.autocad2023.dependencyinjection": { + "type": "Project", + "dependencies": { + "Autofac": "[5.2.0, )", + "Speckle.Converters.Autocad2023": "[2.0.999-local, )", + "Speckle.Converters.Common.DependencyInjection": "[2.0.999-local, )" + } + }, + "speckle.converters.common": { + "type": "Project", + "dependencies": { + "Speckle.Autofac": "[2.0.999-local, )", + "Speckle.Objects": "[3.0.1-alpha.14, )" + } + }, + "speckle.converters.common.dependencyinjection": { + "type": "Project", + "dependencies": { + "Speckle.Autofac": "[2.0.999-local, )", + "Speckle.Converters.Common": "[2.0.999-local, )" + } + }, + "Autofac": { + "type": "CentralTransitive", + "requested": "[5.2.0, )", + "resolved": "5.2.0", + "contentHash": "V8dBH0dsv75uDzl7Sw+HkhKDPUw2eXnlMjcSVMH+tLo2s67MpTKGyDj1pDcpR+IF2u4YRs0s3/x7R88YJzIWvg==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "1.1.0" + } + }, + "Microsoft.Web.WebView2": { + "type": "CentralTransitive", + "requested": "[1.0.1823.32, )", + "resolved": "1.0.1823.32", + "contentHash": "ppRcWBUNggFIqyJp7PfgS4Oe8/7yli/mHcTNDOaqo3ZzJWnv9AOr0WE9ceEP5SPs1M7CQ3QvdGMR7KNz0v09EA==" + }, + "Serilog.Extensions.Logging": { + "type": "CentralTransitive", + "requested": "[7.0.0, )", + "resolved": "7.0.0", + "contentHash": "9faU0zNQqU7I6soVhLUMYaGNpgWv6cKlKb2S5AnS8gXxzW/em5Ladm/6FMrWTnX41cdbdGPOWNAo6adi4WaJ6A==", + "dependencies": { + "Microsoft.Extensions.Logging": "7.0.0", + "Serilog": "2.12.0" + } + }, + "Speckle.Core": { + "type": "CentralTransitive", + "requested": "[3.0.1-alpha.14, )", + "resolved": "3.0.1-alpha.14", + "contentHash": "RzQPVIGFFkKvG56YLr8ACtiwdWJE6IJ9vCQ4qHa0PIsUEpfzAIAi59jnzqtByOFC0FiFrFPow9bkfzylaZorAA==", + "dependencies": { + "GraphQL.Client": "6.0.0", + "Microsoft.CSharp": "4.7.0", + "Microsoft.Data.Sqlite": "7.0.5", + "Polly": "7.2.3", + "Polly.Contrib.WaitAndRetry": "1.1.1", + "Polly.Extensions.Http": "3.0.0", + "Sentry": "3.33.0", + "Sentry.Serilog": "3.33.0", + "Serilog": "2.12.0", + "Serilog.Enrichers.ClientInfo": "1.3.0", + "Serilog.Exceptions": "8.4.0", + "Serilog.Sinks.Console": "4.1.0", + "Serilog.Sinks.Seq": "5.2.2", + "SerilogTimings": "3.0.1", + "Speckle.Newtonsoft.Json": "13.0.2", + "System.DoubleNumerics": "3.1.3" + } + }, + "Speckle.Objects": { + "type": "CentralTransitive", + "requested": "[3.0.1-alpha.14, )", + "resolved": "3.0.1-alpha.14", + "contentHash": "z38LGryMvh7iU1uBW+4uo5DwsB3CwRgLt2uFexWFx3mPSid+A0l5XcJzOgLwgFhNl6B42Ryz4ezBsddTp1Uc/g==", + "dependencies": { + "Speckle.Core": "3.0.1-alpha.14" + } + }, + "System.Threading.Tasks.Dataflow": { + "type": "CentralTransitive", + "requested": "[6.0.0, )", + "resolved": "6.0.0", + "contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA==" + } + } + } +} \ No newline at end of file diff --git a/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadBasicConnectorBinding.cs b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadBasicConnectorBinding.cs new file mode 100644 index 0000000000..a30179a2cb --- /dev/null +++ b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadBasicConnectorBinding.cs @@ -0,0 +1,158 @@ +using Autodesk.AutoCAD.DatabaseServices; +using Sentry.Reflection; +using Speckle.Connectors.Autocad.HostApp; +using Speckle.Connectors.DUI.Bindings; +using Speckle.Connectors.DUI.Bridge; +using Speckle.Connectors.DUI.Models; +using Speckle.Connectors.DUI.Models.Card; +using Speckle.Core.Credentials; +using Speckle.Connectors.Autocad.HostApp.Extensions; +using Speckle.Connectors.Utils; +using Speckle.Core.Logging; + +namespace Speckle.Connectors.Autocad.Bindings; + +public class AutocadBasicConnectorBinding : IBasicConnectorBinding +{ + public string Name { get; set; } = "baseBinding"; + public IBridge Parent { get; } + + private readonly DocumentModelStore _store; + private readonly AutocadSettings _settings; + + public BasicConnectorBindingCommands Commands { get; } + + public AutocadBasicConnectorBinding(DocumentModelStore store, AutocadSettings settings, IBridge parent) + { + _store = store; + _settings = settings; + Parent = parent; + Commands = new BasicConnectorBindingCommands(parent); + _store.DocumentChanged += (_, _) => + { + Commands.NotifyDocumentChanged(); + }; + } + + public string GetConnectorVersion() => + typeof(AutocadBasicConnectorBinding).Assembly.GetNameAndVersion().Version ?? "No version"; + + public string GetSourceApplicationName() => _settings.HostAppInfo.Slug; + + public string GetSourceApplicationVersion() => _settings.HostAppVersion.ToString(); + + public Account[] GetAccounts() => AccountManager.GetAccounts().ToArray(); + + public DocumentInfo? GetDocumentInfo() + { + // POC: Will be addressed to move it into AutocadContext! + var doc = Application.DocumentManager.MdiActiveDocument; + if (doc is null) + { + return null; + } + string name = doc.Name.Split(System.IO.Path.PathSeparator).Last(); + return new DocumentInfo(doc.Name, name, doc.GetHashCode().ToString()); + } + + public DocumentModelStore GetDocumentState() => _store; + + public void AddModel(ModelCard model) => _store.Models.Add(model); + + public void UpdateModel(ModelCard model) => _store.UpdateModel(model); + + public void RemoveModel(ModelCard model) => _store.RemoveModel(model); + + public void HighlightObjects(List objectIds) + { + // POC: Will be addressed to move it into AutocadContext! + var doc = Application.DocumentManager.MdiActiveDocument; + + var dbObjects = doc.GetObjects(objectIds); + var acadObjectIds = dbObjects.Select(tuple => tuple.Root.Id).ToArray(); + HighlightObjectsOnView(acadObjectIds); + } + + public void HighlightModel(string modelCardId) + { + // POC: Will be addressed to move it into AutocadContext! + var doc = Application.DocumentManager.MdiActiveDocument; + + if (doc == null) + { + return; + } + + var objectIds = Array.Empty(); + + var model = _store.GetModelById(modelCardId); + if (model == null) + { + return; + } + + if (model is SenderModelCard senderModelCard) + { + var dbObjects = doc.GetObjects(senderModelCard.SendFilter.NotNull().GetObjectIds()); + objectIds = dbObjects.Select(tuple => tuple.Root.Id).ToArray(); + } + + if (model is ReceiverModelCard receiverModelCard) + { + var dbObjects = doc.GetObjects(receiverModelCard.BakedObjectIds.NotNull()); + objectIds = dbObjects.Select(tuple => tuple.Root.Id).ToArray(); + } + + if (objectIds.Length == 0) + { + Commands.SetModelError(modelCardId, new OperationCanceledException("No objects found to highlight.")); + return; + } + + HighlightObjectsOnView(objectIds, modelCardId); + } + + private void HighlightObjectsOnView(ObjectId[] objectIds, string? modelCardId = null) + { + var doc = Application.DocumentManager.MdiActiveDocument; + + Parent.RunOnMainThread(() => + { + try + { + doc.Editor.SetImpliedSelection(Array.Empty()); // Deselects + doc.Editor.SetImpliedSelection(objectIds); // Selects + doc.Editor.UpdateScreen(); + + Extents3d selectedExtents = new(); + + var tr = doc.TransactionManager.StartTransaction(); + foreach (ObjectId objectId in objectIds) + { + var entity = (Entity)tr.GetObject(objectId, OpenMode.ForRead); + if (entity != null) + { + selectedExtents.AddExtents(entity.GeometricExtents); + } + } + + doc.Editor.Zoom(selectedExtents); + tr.Commit(); + Autodesk.AutoCAD.Internal.Utils.FlushGraphics(); + } + catch (Exception ex) when (!ex.IsFatal()) + { + if (modelCardId != null) + { + Commands.SetModelError(modelCardId, new OperationCanceledException("Failed to highlight objects.")); + } + 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/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadReceiveBinding.cs b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadReceiveBinding.cs new file mode 100644 index 0000000000..b1973d331d --- /dev/null +++ b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadReceiveBinding.cs @@ -0,0 +1,87 @@ +using Speckle.Autofac.DependencyInjection; +using Speckle.Connectors.DUI.Bindings; +using Speckle.Connectors.DUI.Bridge; +using Speckle.Connectors.DUI.Models; +using Speckle.Connectors.Utils.Cancellation; +using Speckle.Connectors.DUI.Models.Card; +using Speckle.Connectors.Utils; +using Speckle.Connectors.Utils.Operations; + +namespace Speckle.Connectors.Autocad.Bindings; + +public sealed class AutocadReceiveBinding : IReceiveBinding +{ + public string Name => "receiveBinding"; + public IBridge Parent { get; } + + private readonly DocumentModelStore _store; + private readonly CancellationManager _cancellationManager; + private readonly IUnitOfWorkFactory _unitOfWorkFactory; + + public ReceiveBindingUICommands Commands { get; } + + public AutocadReceiveBinding( + DocumentModelStore store, + IBridge parent, + CancellationManager cancellationManager, + IUnitOfWorkFactory unitOfWorkFactory + ) + { + _store = store; + _cancellationManager = cancellationManager; + _unitOfWorkFactory = unitOfWorkFactory; + Parent = parent; + Commands = new ReceiveBindingUICommands(parent); + } + + public void CancelReceive(string modelCardId) => _cancellationManager.CancelOperation(modelCardId); + + public async Task Receive(string modelCardId) + { + using var unitOfWork = _unitOfWorkFactory.Resolve(); + 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."); + } + + // Init cancellation token source -> Manager also cancel it if exist before + CancellationTokenSource cts = _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 unitOfWork.Service + .Execute( + modelCard.AccountId.NotNull(), // POC: I hear -you are saying why we're passing them separately. Not sure pass the DUI3-> Connectors.DUI project dependency to the SDK-> Connector.Utils + modelCard.ProjectId.NotNull(), + modelCard.ProjectName.NotNull(), + modelCard.ModelName.NotNull(), + modelCard.SelectedVersionId.NotNull(), + cts.Token, + (status, progress) => + Commands.SetModelProgress(modelCardId, new ModelCardProgress(modelCardId, status, progress), cts) + ) + .ConfigureAwait(false); + + Commands.SetModelReceiveResult(modelCardId, operationResults.BakedObjectIds, operationResults.ConversionResults); + } + // Catch here specific exceptions if they related to model card. + catch (OperationCanceledException) + { + // SWALLOW -> UI handles it immediately, so we do not need to handle anything + return; + } + finally + { + // renable document activation + Application.DocumentManager.DocumentActivationEnabled = true; + } + } +} diff --git a/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadSelectionBinding.cs b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadSelectionBinding.cs new file mode 100644 index 0000000000..1e13090a9f --- /dev/null +++ b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadSelectionBinding.cs @@ -0,0 +1,86 @@ +using Autodesk.AutoCAD.DatabaseServices; +using Speckle.Connectors.DUI.Bindings; +using Speckle.Connectors.DUI.Bridge; +using Autodesk.AutoCAD.EditorInput; + +namespace Speckle.Connectors.Autocad.Bindings; + +public class AutocadSelectionBinding : ISelectionBinding +{ + private const string SELECTION_EVENT = "setSelection"; + private readonly ITopLevelExceptionHandler _topLevelExceptionHandler; + private readonly HashSet _visitedDocuments = new(); + + public string Name => "selectionBinding"; + + public IBridge Parent { get; } + + public AutocadSelectionBinding(IBridge parent, ITopLevelExceptionHandler topLevelExceptionHandler) + { + _topLevelExceptionHandler = topLevelExceptionHandler; + Parent = parent; + + // POC: Use here Context for doc. In converters it's OK but we are still lacking to use context into bindings. + // It is with the case of if binding created with already a document + // This is valid when user opens acad file directly double clicking + TryRegisterDocumentForSelection(Application.DocumentManager.MdiActiveDocument); + Application.DocumentManager.DocumentActivated += (_, e) => + _topLevelExceptionHandler.CatchUnhandled(() => OnDocumentChanged(e.Document)); + } + + private void OnDocumentChanged(Document? document) => TryRegisterDocumentForSelection(document); + + private void TryRegisterDocumentForSelection(Document? document) + { + if (document == null) + { + return; + } + + if (!_visitedDocuments.Contains(document)) + { + document.ImpliedSelectionChanged += (_, _) => + _topLevelExceptionHandler.CatchUnhandled(() => Parent.RunOnMainThread(OnSelectionChanged)); + + _visitedDocuments.Add(document); + } + } + + private void OnSelectionChanged() + { + SelectionInfo selInfo = GetSelection(); + Parent.Send(SELECTION_EVENT, selInfo); + } + + public SelectionInfo GetSelection() + { + // POC: Will be addressed to move it into AutocadContext! https://spockle.atlassian.net/browse/CNX-9319 + Document? doc = Application.DocumentManager.MdiActiveDocument; + List objs = new(); + List objectTypes = new(); + if (doc != null) + { + PromptSelectionResult selection = doc.Editor.SelectImplied(); + if (selection.Status == PromptStatus.OK) + { + using var tr = doc.TransactionManager.StartTransaction(); + foreach (SelectedObject obj in selection.Value) + { + var dbObject = tr.GetObject(obj.ObjectId, OpenMode.ForRead); + if (dbObject == null) + { + continue; + } + + var handleString = dbObject.Handle.Value.ToString(); + objectTypes.Add(dbObject.GetType().Name); + objs.Add(handleString); + } + + tr.Commit(); + } + } + List flatObjectTypes = objectTypes.Select(o => o).Distinct().ToList(); + return new SelectionInfo(objs, $"{objs.Count} objects ({string.Join(", ", flatObjectTypes)})"); + } +} diff --git a/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadSendBinding.cs b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadSendBinding.cs new file mode 100644 index 0000000000..820fb90097 --- /dev/null +++ b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadSendBinding.cs @@ -0,0 +1,196 @@ +using Autodesk.AutoCAD.DatabaseServices; +using Speckle.Connectors.Autocad.HostApp; +using Speckle.Connectors.Autocad.HostApp.Extensions; +using Speckle.Connectors.DUI.Bindings; +using Speckle.Connectors.DUI.Bridge; +using Speckle.Connectors.DUI.Models; +using Speckle.Connectors.DUI.Models.Card; +using Speckle.Connectors.Utils.Cancellation; +using Speckle.Autofac.DependencyInjection; +using Speckle.Connectors.Autocad.Operations.Send; +using Speckle.Connectors.DUI.Exceptions; +using Speckle.Connectors.Utils.Operations; +using Speckle.Connectors.DUI.Models.Card.SendFilter; +using Speckle.Connectors.Utils; +using Speckle.Connectors.Utils.Caching; + +namespace Speckle.Connectors.Autocad.Bindings; + +public sealed class AutocadSendBinding : ISendBinding +{ + public string Name => "sendBinding"; + public SendBindingUICommands Commands { get; } + public IBridge Parent { get; } + + private readonly DocumentModelStore _store; + private readonly AutocadIdleManager _idleManager; + private readonly List _sendFilters; + private readonly CancellationManager _cancellationManager; + private readonly IUnitOfWorkFactory _unitOfWorkFactory; + private readonly AutocadSettings _autocadSettings; + private readonly ISendConversionCache _sendConversionCache; + private readonly ITopLevelExceptionHandler _topLevelExceptionHandler; + + /// + /// Used internally to aggregate the changed objects' id. + /// + private HashSet ChangedObjectIds { get; set; } = new(); + + public AutocadSendBinding( + DocumentModelStore store, + AutocadIdleManager idleManager, + IBridge parent, + IEnumerable sendFilters, + CancellationManager cancellationManager, + AutocadSettings autocadSettings, + IUnitOfWorkFactory unitOfWorkFactory, + ISendConversionCache sendConversionCache, + ITopLevelExceptionHandler topLevelExceptionHandler + ) + { + _store = store; + _idleManager = idleManager; + _unitOfWorkFactory = unitOfWorkFactory; + _autocadSettings = autocadSettings; + _cancellationManager = cancellationManager; + _sendFilters = sendFilters.ToList(); + _sendConversionCache = sendConversionCache; + _topLevelExceptionHandler = topLevelExceptionHandler; + Parent = parent; + Commands = new SendBindingUICommands(parent); + + Application.DocumentManager.DocumentActivated += (_, args) => + topLevelExceptionHandler.CatchUnhandled(() => SubscribeToObjectChanges(args.Document)); + + if (Application.DocumentManager.CurrentDocument != null) + { + // catches the case when autocad just opens up with a blank new doc + SubscribeToObjectChanges(Application.DocumentManager.CurrentDocument); + } + } + + private readonly List _docSubsTracker = new(); + + private void SubscribeToObjectChanges(Document doc) + { + if (doc == null || doc.Database == null || _docSubsTracker.Contains(doc.Name)) + { + return; + } + + _docSubsTracker.Add(doc.Name); + doc.Database.ObjectAppended += (_, e) => OnObjectChanged(e.DBObject); + doc.Database.ObjectErased += (_, e) => OnObjectChanged(e.DBObject); + doc.Database.ObjectModified += (_, e) => OnObjectChanged(e.DBObject); + } + + void OnObjectChanged(DBObject dbObject) + { + _topLevelExceptionHandler.CatchUnhandled(() => OnChangeChangedObjectIds(dbObject)); + } + + private void OnChangeChangedObjectIds(DBObject dBObject) + { + ChangedObjectIds.Add(dBObject.Handle.Value.ToString()); + _idleManager.SubscribeToIdle(RunExpirationChecks); + } + + private void RunExpirationChecks() + { + var senders = _store.GetSenders(); + string[] objectIdsList = ChangedObjectIds.ToArray(); + List expiredSenderIds = new(); + + _sendConversionCache.EvictObjects(objectIdsList); + + foreach (SenderModelCard modelCard in senders) + { + var intersection = modelCard.SendFilter.NotNull().GetObjectIds().Intersect(objectIdsList).ToList(); + bool isExpired = intersection.Count != 0; + if (isExpired) + { + expiredSenderIds.Add(modelCard.ModelCardId.NotNull()); + } + } + + Commands.SetModelsExpired(expiredSenderIds); + ChangedObjectIds = new HashSet(); + } + + public List GetSendFilters() => _sendFilters; + + public Task Send(string modelCardId) + { + Parent.RunOnMainThread(async () => await SendInternal(modelCardId).ConfigureAwait(false)); + return Task.CompletedTask; + } + + private async Task SendInternal(string modelCardId) + { + try + { + if (_store.GetModelById(modelCardId) is not SenderModelCard modelCard) + { + // Handle as GLOBAL ERROR at BrowserBridge + throw new InvalidOperationException("No publish model card was found."); + } + + using var uow = _unitOfWorkFactory.Resolve>(); + + // Init cancellation token source -> Manager also cancel it if exist before + CancellationTokenSource cts = _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; + + // Get elements to convert + List autocadObjects = Application.DocumentManager.CurrentDocument.GetObjects( + modelCard.SendFilter.NotNull().GetObjectIds() + ); + + if (autocadObjects.Count == 0) + { + // Handle as CARD ERROR in this function + throw new SpeckleSendFilterException("No objects were found to convert. Please update your publish filter!"); + } + + var sendInfo = new SendInfo( + modelCard.AccountId.NotNull(), + modelCard.ProjectId.NotNull(), + modelCard.ModelId.NotNull(), + _autocadSettings.HostAppInfo.Name + ); + + var sendResult = await uow.Service + .Execute( + autocadObjects, + sendInfo, + (status, progress) => + Commands.SetModelProgress(modelCardId, new ModelCardProgress(modelCardId, status, progress), cts), + cts.Token + ) + .ConfigureAwait(false); + + Commands.SetModelSendResult(modelCardId, sendResult.RootObjId, sendResult.ConversionResults); + } + // Catch here specific exceptions if they related to model card. + catch (OperationCanceledException) + { + // SWALLOW -> UI handles it immediately, so we do not need to handle anything + return; + } + catch (SpeckleSendFilterException e) + { + Commands.SetModelError(modelCardId, e); + } + finally + { + // renable document activation + Application.DocumentManager.DocumentActivationEnabled = true; + } + } + + public void CancelSend(string modelCardId) => _cancellationManager.CancelOperation(modelCardId); +} diff --git a/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/DependencyInjection/AutocadConnectorModule.cs b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/DependencyInjection/AutocadConnectorModule.cs new file mode 100644 index 0000000000..777a2ed66c --- /dev/null +++ b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/DependencyInjection/AutocadConnectorModule.cs @@ -0,0 +1,21 @@ +#if AUTOCAD +using Speckle.Autofac.DependencyInjection; +using Speckle.Connectors.DUI.Bindings; + +namespace Speckle.Connectors.Autocad.DependencyInjection; + +public class AutocadConnectorModule : ISpeckleModule +{ + public void Load(SpeckleContainerBuilder builder) + { + SharedRegistration.Load(builder); + + // Operations + SharedRegistration.LoadSend(builder); + SharedRegistration.LoadReceive(builder); + + // Register bindings + builder.AddSingleton("connectorName", "Autocad"); // POC: Easier like this for now, should be cleaned up later + } +} +#endif diff --git a/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/DependencyInjection/Civil3dConnectorModule.cs b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/DependencyInjection/Civil3dConnectorModule.cs new file mode 100644 index 0000000000..0b06869c6e --- /dev/null +++ b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/DependencyInjection/Civil3dConnectorModule.cs @@ -0,0 +1,19 @@ +#if CIVIL3D + +using Speckle.Autofac.DependencyInjection; +using Speckle.Connectors.DUI.Bindings; + +namespace Speckle.Connectors.Autocad.DependencyInjection; + +public class Civil3dConnectorModule : ISpeckleModule +{ + public void Load(SpeckleContainerBuilder builder) + { + SharedRegistration.Load(builder); + SharedRegistration.LoadSend(builder); + + // Register bindings + builder.AddSingleton("connectorName", "Civil3d"); // POC: Easier like this for now, should be cleaned up later + } +} +#endif diff --git a/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/DependencyInjection/SharedRegistration.cs b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/DependencyInjection/SharedRegistration.cs new file mode 100644 index 0000000000..b9b5856054 --- /dev/null +++ b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/DependencyInjection/SharedRegistration.cs @@ -0,0 +1,82 @@ +using Autodesk.AutoCAD.DatabaseServices; +using Speckle.Autofac; +using Speckle.Autofac.DependencyInjection; +using Speckle.Connectors.Autocad.Bindings; +using Speckle.Connectors.Autocad.HostApp; +using Speckle.Connectors.Autocad.Interfaces; +using Speckle.Connectors.Autocad.Plugin; +using Speckle.Connectors.DUI; +using Speckle.Connectors.DUI.Bindings; +using Speckle.Connectors.DUI.Models; +using Speckle.Connectors.DUI.WebView; +using Speckle.Connectors.Utils; +using Speckle.Connectors.Autocad.Filters; +using Speckle.Connectors.Autocad.Operations.Send; +using Speckle.Connectors.DUI.Models.Card.SendFilter; +using Speckle.Connectors.Utils.Builders; +using Speckle.Connectors.Utils.Caching; +using Speckle.Connectors.Utils.Operations; +using Speckle.Connectors.Utils.Instances; +using Speckle.Connectors.Autocad.Operations.Receive; + +using Speckle.Core.Models.GraphTraversal; + +namespace Speckle.Connectors.Autocad.DependencyInjection; + +public static class SharedRegistration +{ + public static void Load(SpeckleContainerBuilder builder) + { + builder.AddAutofac(); + builder.AddConnectorUtils(); + builder.AddDUI(); + builder.AddDUIView(); + + // Register other connector specific types + builder.AddSingleton(); + builder.AddTransient(); + builder.AddSingleton(new AutocadDocumentManager()); // TODO: Dependent to TransactionContext, can be moved to AutocadContext + builder.AddSingleton(); + builder.AddSingleton(); + builder.AddScoped(); + builder.AddSingleton(); + + // Register bindings + builder.AddSingleton(); + builder.AddSingleton(); + builder.AddSingleton(); + builder.AddSingleton(); + builder.AddSingleton(); + } + + public static void LoadSend(SpeckleContainerBuilder builder) + { + // Operations + builder.AddScoped>(); + + // Object Builders + builder.AddScoped, AutocadRootObjectBuilder>(); + + // Register bindings + builder.AddSingleton(); + + // register send filters + builder.AddTransient(); + + // register send conversion cache + builder.AddSingleton(); + builder.AddScoped>, AutocadInstanceObjectManager>(); + } + + public static void LoadReceive(SpeckleContainerBuilder builder) + { + // traversal + builder.AddSingleton(DefaultTraversal.CreateTraversalFunc()); + + // Object Builders + builder.AddScoped(); + + // Register bindings + builder.AddSingleton(); + } +} diff --git a/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/Filters/AutocadSelectionFilter.cs b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/Filters/AutocadSelectionFilter.cs new file mode 100644 index 0000000000..7b74ce1688 --- /dev/null +++ b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/Filters/AutocadSelectionFilter.cs @@ -0,0 +1,10 @@ +using Speckle.Connectors.DUI.Models.Card.SendFilter; + +namespace Speckle.Connectors.Autocad.Filters; + +public class AutocadSelectionFilter : DirectSelectionSendFilter +{ + public override List GetObjectIds() => SelectedObjectIds; + + public override bool CheckExpiry(string[] changedObjectIds) => SelectedObjectIds.Intersect(changedObjectIds).Any(); +} diff --git a/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/GlobalUsings.cs b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/GlobalUsings.cs new file mode 100644 index 0000000000..3d7268d128 --- /dev/null +++ b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/GlobalUsings.cs @@ -0,0 +1,2 @@ +global using Document = Autodesk.AutoCAD.ApplicationServices.Document; +global using Application = Autodesk.AutoCAD.ApplicationServices.Core.Application; diff --git a/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadContext.cs b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadContext.cs new file mode 100644 index 0000000000..644217d522 --- /dev/null +++ b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadContext.cs @@ -0,0 +1,19 @@ +namespace Speckle.Connectors.Autocad.HostApp; + +public class AutocadContext +{ + // POC: we may want to inject this autocadcontext with the active doc? + + private const string INVALID_CHARS = @"<>/\:;""?*|=,‘"; + + // POC: we can move this function into more relevant/general place. Not sure other connectors need similar functionality like this. + public string RemoveInvalidChars(string str) + { + foreach (char c in INVALID_CHARS) + { + str = str.Replace(c.ToString(), string.Empty); + } + + return str; + } +} diff --git a/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadDocumentManager.cs b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadDocumentManager.cs new file mode 100644 index 0000000000..2de2e27aa3 --- /dev/null +++ b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadDocumentManager.cs @@ -0,0 +1,144 @@ +using System.Diagnostics; +using System.Text; +using Autodesk.AutoCAD.DatabaseServices; + +namespace Speckle.Connectors.Autocad.HostApp; + +public class AutocadDocumentManager +{ + private const string SPECKLE_KEY = "Speckle_DUI3"; + private const string SPECKLE_MODEL_CARDS_KEY = "Speckle_DUI3_Model_Cards"; + + /// + /// Returns all the speckle model cards present in the current document. + /// + /// + /// + public string? ReadModelCards(Document doc) + { + using (TransactionContext.StartTransaction(doc)) + { + Transaction tr = doc.Database.TransactionManager.TopTransaction; + + var nod = (DBDictionary)tr.GetObject(doc.Database.NamedObjectsDictionaryId, OpenMode.ForRead); + if (!nod.Contains(SPECKLE_KEY)) + { + return null; + } + + var speckleDict = (DBDictionary)tr.GetObject(nod.GetAt(SPECKLE_KEY), OpenMode.ForRead); + if (speckleDict.Count == 0) + { + return null; + } + + ObjectId id = speckleDict.GetAt(SPECKLE_MODEL_CARDS_KEY); + if (id == ObjectId.Null) + { + return null; + } + + var record = (Xrecord)tr.GetObject(id, OpenMode.ForRead); + string value = GetXrecordData(record); + + try + { + //Try to decode here because there is old data + return Base64Decode(value); + } + catch (ApplicationException e) + { + Debug.WriteLine(e); + return null; + } + } + } + + /// + /// Writes the model cards to the current document. + /// + /// + /// + public void WriteModelCards(Document doc, string modelCardsString) + { + if (doc == null) + { + return; + } + + using (TransactionContext.StartTransaction(doc)) + { + Transaction tr = doc.Database.TransactionManager.TopTransaction; + + var nod = (DBDictionary)tr.GetObject(doc.Database.NamedObjectsDictionaryId, OpenMode.ForRead); + DBDictionary speckleDict; + if (nod.Contains(SPECKLE_KEY)) + { + speckleDict = (DBDictionary)tr.GetObject(nod.GetAt(SPECKLE_KEY), OpenMode.ForWrite); + } + else + { + speckleDict = new DBDictionary(); + nod.UpgradeOpen(); + nod.SetAt(SPECKLE_KEY, speckleDict); + tr.AddNewlyCreatedDBObject(speckleDict, true); + } + + Xrecord xRec = new() { Data = CreateResultBuffer(modelCardsString) }; + + speckleDict.SetAt(SPECKLE_MODEL_CARDS_KEY, xRec); + tr.AddNewlyCreatedDBObject(xRec, true); + } + } + + private ResultBuffer CreateResultBuffer(string value) + { + int size = 1024; + var valueEncoded = Base64Encode(value); + var valueEncodedList = SplitString(valueEncoded, size); + + ResultBuffer rb = new(); + + foreach (string valueEncodedSplit in valueEncodedList) + { + rb.Add(new TypedValue((int)DxfCode.Text, valueEncodedSplit)); + } + + return rb; + } + + private string GetXrecordData(Xrecord pXrecord) + { + StringBuilder valueEncoded = new(); + foreach (TypedValue typedValue in pXrecord.Data) + { + if (typedValue.TypeCode == (int)DxfCode.Text) + { + valueEncoded.Append(typedValue.Value.ToString()); + } + } + + return valueEncoded.ToString(); + } + + private string Base64Encode(string plainText) + { + var plainTextBytes = Encoding.UTF8.GetBytes(plainText); + return Convert.ToBase64String(plainTextBytes); + } + + private string Base64Decode(string base64EncodedData) + { + var base64EncodedBytes = Convert.FromBase64String(base64EncodedData); + return Encoding.UTF8.GetString(base64EncodedBytes); + } + + private IEnumerable SplitString(string text, int chunkSize) + { + for (int offset = 0; offset < text.Length; offset += chunkSize) + { + int size = Math.Min(chunkSize, text.Length - offset); + yield return text.Substring(offset, size); + } + } +} diff --git a/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadDocumentModelStore.cs b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadDocumentModelStore.cs new file mode 100644 index 0000000000..a01b4a0716 --- /dev/null +++ b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadDocumentModelStore.cs @@ -0,0 +1,89 @@ +using Speckle.Connectors.DUI.Bridge; +using Speckle.Connectors.DUI.Models; +using Speckle.Connectors.Utils; +using Speckle.Newtonsoft.Json; + +namespace Speckle.Connectors.Autocad.HostApp; + +public class AutocadDocumentStore : DocumentModelStore +{ + private readonly string _nullDocumentName = "Null Doc"; + private string _previousDocName; + private readonly AutocadDocumentManager _autocadDocumentManager; + + public AutocadDocumentStore( + JsonSerializerSettings jsonSerializerSettings, + AutocadDocumentManager autocadDocumentManager, + ITopLevelExceptionHandler topLevelExceptionHandler + ) + : base(jsonSerializerSettings, true) + { + _autocadDocumentManager = autocadDocumentManager; + _previousDocName = _nullDocumentName; + + // POC: Will be addressed to move it into AutocadContext! + if (Application.DocumentManager.MdiActiveDocument != null) + { + IsDocumentInit = true; + // POC: this logic might go when we have document management in context + // It is with the case of if binding created with already a document + // This is valid when user opens acad file directly double clicking + OnDocChangeInternal(Application.DocumentManager.MdiActiveDocument); + } + + Application.DocumentManager.DocumentActivated += (_, e) => + topLevelExceptionHandler.CatchUnhandled(() => OnDocChangeInternal(e.Document)); + + // since below event triggered as secondary, it breaks the logic in OnDocChangeInternal function, leaving it here for now. + // Autodesk.AutoCAD.ApplicationServices.Application.DocumentWindowCollection.DocumentWindowActivated += (_, args) => + // OnDocChangeInternal((Document)args.DocumentWindow.Document); + } + + private void OnDocChangeInternal(Document? doc) + { + var currentDocName = doc != null ? doc.Name : _nullDocumentName; + if (_previousDocName == currentDocName) + { + return; + } + + _previousDocName = currentDocName; + ReadFromFile(); + OnDocumentChanged(); + } + + public override void ReadFromFile() + { + Models = new(); + + // POC: Will be addressed to move it into AutocadContext! + Document? doc = Application.DocumentManager.MdiActiveDocument; + + if (doc == null) + { + return; + } + + string? serializedModelCards = _autocadDocumentManager.ReadModelCards(doc); + if (serializedModelCards == null) + { + return; + } + + Models = Deserialize(serializedModelCards).NotNull(); + } + + public override void WriteToFile() + { + // POC: Will be addressed to move it into AutocadContext! + Document doc = Application.DocumentManager.MdiActiveDocument; + + if (doc == null) + { + return; + } + + string modelCardsString = Serialize(); + _autocadDocumentManager.WriteModelCards(doc, modelCardsString); + } +} diff --git a/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadIdleManager.cs b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadIdleManager.cs new file mode 100644 index 0000000000..ef2a4bb0db --- /dev/null +++ b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadIdleManager.cs @@ -0,0 +1,38 @@ +using System.Collections.Concurrent; + +namespace Speckle.Connectors.Autocad.HostApp; + +public class AutocadIdleManager +{ + private readonly ConcurrentDictionary _sCalls = new(); + private bool _hasSubscribed; + + /// + /// Subscribe deferred action to AutocadIdle event to run it whenever Autocad become idle. + /// + /// Action to call whenever Autocad become Idle. + public void SubscribeToIdle(Action action) + { + _sCalls[action.Method.Name] = action; + + if (_hasSubscribed) + { + return; + } + + _hasSubscribed = true; + Application.Idle += OnIdleHandler; + } + + private void OnIdleHandler(object sender, EventArgs e) + { + foreach (var kvp in _sCalls) + { + kvp.Value(); + } + + _sCalls.Clear(); + _hasSubscribed = false; + Application.Idle -= OnIdleHandler; + } +} diff --git a/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadInstanceObjectManager.cs b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadInstanceObjectManager.cs new file mode 100644 index 0000000000..11067f52ba --- /dev/null +++ b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadInstanceObjectManager.cs @@ -0,0 +1,368 @@ +using System.DoubleNumerics; +using Autodesk.AutoCAD.DatabaseServices; +using Autodesk.AutoCAD.Geometry; +using Speckle.Connectors.Autocad.HostApp.Extensions; +using Speckle.Connectors.Autocad.Operations.Send; +using Speckle.Connectors.Utils.Conversion; +using Speckle.Connectors.Utils.Instances; +using Speckle.Core.Kits; +using Speckle.Core.Logging; +using Speckle.Core.Models; +using Speckle.Core.Models.Instances; + +namespace Speckle.Connectors.Autocad.HostApp; + +/// +/// +/// Expects to be a scoped dependency per send or receive operation. +/// +public class AutocadInstanceObjectManager : IInstanceObjectsManager> +{ + private readonly AutocadLayerManager _autocadLayerManager; + private Dictionary InstanceProxies { get; set; } = new(); + private Dictionary> InstanceProxiesByDefinitionId { get; set; } = new(); + private Dictionary DefinitionProxies { get; set; } = new(); + private Dictionary FlatAtomicObjects { get; set; } = new(); + + public AutocadInstanceObjectManager(AutocadLayerManager autocadLayerManager) + { + _autocadLayerManager = autocadLayerManager; + } + + public UnpackResult UnpackSelection(IEnumerable objects) + { + using var transaction = Application.DocumentManager.CurrentDocument.Database.TransactionManager.StartTransaction(); + + foreach (var obj in objects) + { + if (obj.Root is BlockReference blockReference && !blockReference.IsDynamicBlock) + { + UnpackInstance(blockReference, 0, transaction); + } + + FlatAtomicObjects[obj.ApplicationId] = obj; + } + return new(FlatAtomicObjects.Values.ToList(), InstanceProxies, DefinitionProxies.Values.ToList()); + } + + private void UnpackInstance(BlockReference instance, int depth, Transaction transaction) + { + var instanceIdString = instance.Handle.Value.ToString(); + var definitionId = instance.BlockTableRecord; + + InstanceProxies[instanceIdString] = new InstanceProxy() + { + applicationId = instanceIdString, + DefinitionId = definitionId.ToString(), + MaxDepth = depth, + Transform = GetMatrix(instance.BlockTransform.ToArray()), + Units = Application.DocumentManager.CurrentDocument.Database.Insunits.ToSpeckleString() + }; + + // For each block instance that has the same definition, we need to keep track of the "maximum depth" at which is found. + // This will enable on receive to create them in the correct order (descending by max depth, interleaved definitions and instances). + // We need to interleave the creation of definitions and instances, as some definitions may depend on instances. + if ( + !InstanceProxiesByDefinitionId.TryGetValue( + definitionId.ToString(), + out List instanceProxiesWithSameDefinition + ) + ) + { + instanceProxiesWithSameDefinition = new List(); + InstanceProxiesByDefinitionId[definitionId.ToString()] = instanceProxiesWithSameDefinition; + } + + // We ensure that all previous instance proxies that have the same definition are at this max depth. I kind of have a feeling this can be done more elegantly, but YOLO + foreach (var instanceProxy in instanceProxiesWithSameDefinition) + { + instanceProxy.MaxDepth = depth; + } + + instanceProxiesWithSameDefinition.Add(InstanceProxies[instanceIdString]); + + if (DefinitionProxies.TryGetValue(definitionId.ToString(), out InstanceDefinitionProxy value)) + { + value.MaxDepth = depth; + return; // exit fast - we've parsed this one so no need to go further + } + + var definition = (BlockTableRecord)transaction.GetObject(definitionId, OpenMode.ForRead); + // definition.Origin + var definitionProxy = new InstanceDefinitionProxy() + { + applicationId = definitionId.ToString(), + Objects = new(), + MaxDepth = depth, + ["name"] = definition.Name, + ["comments"] = definition.Comments, + ["units"] = definition.Units // ? not sure needed? + }; + + // Go through each definition object + foreach (ObjectId id in definition) + { + var obj = transaction.GetObject(id, OpenMode.ForRead); + var handleIdString = obj.Handle.Value.ToString(); + definitionProxy.Objects.Add(handleIdString); + + if (obj is BlockReference blockReference && !blockReference.IsDynamicBlock) + { + UnpackInstance(blockReference, depth + 1, transaction); + } + FlatAtomicObjects[handleIdString] = new(obj, handleIdString); + } + + DefinitionProxies[definitionId.ToString()] = definitionProxy; + } + + public BakeResult BakeInstances( + List<(string[] layerPath, IInstanceComponent obj)> instanceComponents, + Dictionary> applicationIdMap, + string baseLayerName, + Action? onOperationProgressed + ) + { + var sortedInstanceComponents = instanceComponents + .OrderByDescending(x => x.obj.MaxDepth) // Sort by max depth, so we start baking from the deepest element first + .ThenBy(x => x.obj is InstanceDefinitionProxy ? 0 : 1) // Ensure we bake the deepest definition first, then any instances that depend on it + .ToList(); + + 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 count = 0; + + foreach (var (path, instanceOrDefinition) in sortedInstanceComponents) + { + try + { + onOperationProgressed?.Invoke("Converting blocks", (double)++count / sortedInstanceComponents.Count); + if (instanceOrDefinition is InstanceDefinitionProxy { applicationId: not null } definitionProxy) + { + // TODO: create definition (block table record) + var constituentEntities = definitionProxy.Objects + .Select(id => applicationIdMap.TryGetValue(id, out List value) ? value : null) + .Where(x => x is not null) + .SelectMany(ent => ent) + .ToList(); + + var record = new BlockTableRecord(); + var objectIds = new ObjectIdCollection(); + record.Name = baseLayerName; + if (definitionProxy["name"] is string name) + { + record.Name += name; + } + else + { + record.Name += definitionProxy.applicationId; + } + + foreach (var entity in constituentEntities) + { + // record.AppendEntity(entity); + objectIds.Add(entity.ObjectId); + } + + using var blockTable = (BlockTable) + transaction.GetObject(Application.DocumentManager.CurrentDocument.Database.BlockTableId, OpenMode.ForWrite); + var id = blockTable.Add(record); + record.AssumeOwnershipOf(objectIds); + + definitionIdAndApplicationIdMap[definitionProxy.applicationId] = id; + transaction.AddNewlyCreatedDBObject(record, true); + var consumedEntitiesHandleValues = constituentEntities.Select(ent => ent.Handle.Value.ToString()).ToArray(); + consumedObjectIds.AddRange(consumedEntitiesHandleValues); + createdObjectIds.RemoveAll(newId => consumedEntitiesHandleValues.Contains(newId)); + } + else if ( + instanceOrDefinition is InstanceProxy instanceProxy + && definitionIdAndApplicationIdMap.TryGetValue(instanceProxy.DefinitionId, out ObjectId definitionId) + ) + { + var matrix3d = GetMatrix3d(instanceProxy.Transform, instanceProxy.Units); + var insertionPoint = Point3d.Origin.TransformBy(matrix3d); + + var modelSpaceBlockTableRecord = Application.DocumentManager.CurrentDocument.Database.GetModelSpace( + OpenMode.ForWrite + ); + _autocadLayerManager.CreateLayerForReceive(path[0]); + var blockRef = new BlockReference(insertionPoint, definitionId) + { + BlockTransform = matrix3d, + Layer = path[0], + }; + + modelSpaceBlockTableRecord.AppendEntity(blockRef); + + if (instanceProxy.applicationId != null) + { + applicationIdMap[instanceProxy.applicationId] = new List { blockRef }; + } + + transaction.AddNewlyCreatedDBObject(blockRef, true); + conversionResults.Add( + new(Status.SUCCESS, instanceProxy, blockRef.Handle.Value.ToString(), "Instance (Block)") + ); + createdObjectIds.Add(blockRef.Handle.Value.ToString()); + } + } + catch (Exception ex) when (!ex.IsFatal()) + { + conversionResults.Add(new(Status.ERROR, instanceOrDefinition as Base ?? new Base(), null, null, ex)); + } + } + transaction.Commit(); + return new(createdObjectIds, consumedObjectIds, conversionResults); + } + + /// + /// Cleans up any previously created instances. + /// POC: This function will not be able to delete block definitions if the user creates a new one composed out of received definitions. + /// + /// + public void PurgeInstances(string namePrefix) + { + using var transaction = Application.DocumentManager.CurrentDocument.Database.TransactionManager.StartTransaction(); + var instanceDefinitionsToDelete = new Dictionary(); + + // Helper function that recurses through a given block table record's constituent objects and purges inner instances as required. + void TraverseAndClean(BlockTableRecord btr) + { + foreach (var objectId in btr) + { + var obj = transaction.GetObject(objectId, OpenMode.ForRead) as BlockReference; + if (obj == null) + { + continue; + } + var definition = (BlockTableRecord)transaction.GetObject(obj.BlockTableRecord, OpenMode.ForRead); + if (obj.IsErased) + { + TraverseAndClean(definition); + continue; + } + + obj.UpgradeOpen(); + obj.Erase(); + TraverseAndClean(definition); + instanceDefinitionsToDelete[obj.BlockTableRecord.ToString()] = definition; + } + } + + using var blockTable = (BlockTable) + transaction.GetObject(Application.DocumentManager.CurrentDocument.Database.BlockTableId, OpenMode.ForRead); + + // deep clean definitions + foreach (var btrId in blockTable) + { + var btr = (BlockTableRecord)transaction.GetObject(btrId, OpenMode.ForRead); + if (btr.Name.Contains(namePrefix)) // POC: this is tightly coupled with a naming convention for definitions in the instance object manager + { + TraverseAndClean(btr); + instanceDefinitionsToDelete[btr.Name] = btr; + } + } + + foreach (var def in instanceDefinitionsToDelete.Values) + { + def.UpgradeOpen(); + def.Erase(); + } + + transaction.Commit(); + } + + private Matrix4x4 GetMatrix(double[] t) + { + return new Matrix4x4( + t[0], + t[1], + t[2], + t[3], + t[4], + t[5], + t[6], + t[7], + t[8], + t[9], + t[10], + t[11], + t[12], + t[13], + t[14], + t[15] + ); + } + + private Matrix3d GetMatrix3d(Matrix4x4 matrix, string units) + { + var sf = Units.GetConversionFactor( + units, + Application.DocumentManager.CurrentDocument.Database.Insunits.ToSpeckleString() + ); + + var scaledTransform = new[] + { + matrix.M11, + matrix.M12, + matrix.M13, + matrix.M14 * sf, + matrix.M21, + matrix.M22, + matrix.M23, + matrix.M24 * sf, + matrix.M31, + matrix.M32, + matrix.M33, + matrix.M34 * sf, + matrix.M41, + matrix.M42, + matrix.M43, + matrix.M44 + }; + + var m3d = new Matrix3d(scaledTransform); + if (!m3d.IsScaledOrtho()) + { + m3d = new Matrix3d(MakePerpendicular(m3d)); + } + + return m3d; + } + + // https://forums.autodesk.com/t5/net/set-blocktransform-values/m-p/6452121#M49479 + private static double[] MakePerpendicular(Matrix3d matrix) + { + // Get the basis vectors of the matrix + Vector3d right = new(matrix[0, 0], matrix[1, 0], matrix[2, 0]); + Vector3d up = new(matrix[0, 1], matrix[1, 1], matrix[2, 1]); + + Vector3d newForward = right.CrossProduct(up).GetNormal(); + Vector3d newUp = newForward.CrossProduct(right).GetNormal(); + + return new[] + { + right.X, + newUp.X, + newForward.X, + matrix[0, 3], + right.Y, + newUp.Y, + newForward.Y, + matrix[1, 3], + right.Z, + newUp.Z, + newForward.Z, + matrix[2, 3], + 0.0, + 0.0, + 0.0, + matrix[3, 3], + }; + } +} diff --git a/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadLayerManager.cs b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadLayerManager.cs new file mode 100644 index 0000000000..d172bea860 --- /dev/null +++ b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadLayerManager.cs @@ -0,0 +1,160 @@ +using Autodesk.AutoCAD.DatabaseServices; +using Autodesk.AutoCAD.EditorInput; +using Autodesk.AutoCAD.LayerManager; +using Speckle.Core.Models; +using Speckle.Core.Models.GraphTraversal; + +namespace Speckle.Connectors.Autocad.HostApp; + +/// +/// Expects to be a scoped dependency for a given operation and helps with layer creation and cleanup. +/// +public class AutocadLayerManager +{ + private readonly AutocadContext _autocadContext; + private readonly string _layerFilterName = "Speckle"; + + // POC: Will be addressed to move it into AutocadContext! + private Document Doc => Application.DocumentManager.MdiActiveDocument; + private readonly HashSet _uniqueLayerNames = new(); + + public AutocadLayerManager(AutocadContext autocadContext) + { + _autocadContext = autocadContext; + } + + /// + /// Will create a layer with the provided name, or, if it finds an existing one, will "purge" all objects from it. + /// This ensures we're creating the new objects we've just received rather than overlaying them. + /// + /// Name to search layer for purge and create. + public void CreateLayerForReceive(string layerName) + { + if (!_uniqueLayerNames.Add(layerName)) + { + return; + } + + Doc.LockDocument(); + using Transaction transaction = Doc.TransactionManager.StartTransaction(); + + LayerTable? layerTable = + transaction.TransactionManager.GetObject(Doc.Database.LayerTableId, OpenMode.ForRead) as LayerTable; + LayerTableRecord layerTableRecord = new() { Name = layerName }; + + bool hasLayer = layerTable != null && layerTable.Has(layerName); + if (hasLayer) + { + TypedValue[] tvs = { new((int)DxfCode.LayerName, layerName) }; + SelectionFilter selectionFilter = new(tvs); + SelectionSet selectionResult = Doc.Editor.SelectAll(selectionFilter).Value; + if (selectionResult == null) + { + return; + } + foreach (SelectedObject selectedObject in selectionResult) + { + transaction.GetObject(selectedObject.ObjectId, OpenMode.ForWrite).Erase(); + } + + return; + } + + layerTable?.UpgradeOpen(); + layerTable?.Add(layerTableRecord); + transaction.AddNewlyCreatedDBObject(layerTableRecord, true); + transaction.Commit(); + } + + public void DeleteAllLayersByPrefix(string prefix) + { + Doc.LockDocument(); + using Transaction transaction = Doc.TransactionManager.StartTransaction(); + + var layerTable = (LayerTable)transaction.TransactionManager.GetObject(Doc.Database.LayerTableId, OpenMode.ForRead); + foreach (var layerId in layerTable) + { + var layer = (LayerTableRecord)transaction.GetObject(layerId, OpenMode.ForRead); + var layerName = layer.Name; + if (layer.Name.Contains(prefix)) + { + // Delete objects from this layer + TypedValue[] tvs = { new((int)DxfCode.LayerName, layerName) }; + SelectionFilter selectionFilter = new(tvs); + SelectionSet selectionResult = Doc.Editor.SelectAll(selectionFilter).Value; + if (selectionResult == null) + { + return; + } + foreach (SelectedObject selectedObject in selectionResult) + { + transaction.GetObject(selectedObject.ObjectId, OpenMode.ForWrite).Erase(); + } + // Delete layer + layer.UpgradeOpen(); + layer.Erase(); + } + } + transaction.Commit(); + } + + /// + /// Creates a layer filter for the just received model, grouped under a top level filter "Speckle". Note: manual close and open of the layer properties panel required (it's an acad thing). + /// This comes in handy to quickly access the layers created for this specific model. + /// + /// + /// + public void CreateLayerFilter(string projectName, string modelName) + { + using var docLock = Doc.LockDocument(); + string filterName = _autocadContext.RemoveInvalidChars($@"{projectName}-{modelName}"); + LayerFilterTree layerFilterTree = Doc.Database.LayerFilters; + LayerFilterCollection? layerFilterCollection = layerFilterTree.Root.NestedFilters; + LayerFilter? groupFilter = null; + + // Find existing layer filter if exists + foreach (LayerFilter existingFilter in layerFilterCollection) + { + if (existingFilter.Name == _layerFilterName) + { + groupFilter = existingFilter; + break; + } + } + + // Create new one unless exists + if (groupFilter == null) + { + groupFilter = new LayerFilter() { Name = "Speckle", FilterExpression = $"NAME==\"SPK-*\"" }; + layerFilterCollection.Add(groupFilter); + } + + string layerFilterExpression = $"NAME==\"SPK-{filterName}*\""; + foreach (LayerFilter lf in groupFilter.NestedFilters) + { + if (lf.Name == filterName) + { + lf.FilterExpression = layerFilterExpression; + return; + } + } + var layerFilter = new LayerFilter() { Name = filterName, FilterExpression = layerFilterExpression }; + groupFilter.NestedFilters.Add(layerFilter); + Doc.Database.LayerFilters = layerFilterTree; + } + + /// + /// Gets a valid layer name for a given context. + /// + /// + /// + /// + public string GetLayerPath(TraversalContext context, string baseLayerPrefix) + { + string[] collectionBasedPath = context.GetAscendantOfType().Select(c => c.name).Reverse().ToArray(); + string[] path = collectionBasedPath.Length != 0 ? collectionBasedPath : context.GetPropertyPath().ToArray(); + + var name = baseLayerPrefix + string.Join("-", path); + return _autocadContext.RemoveInvalidChars(name); + } +} diff --git a/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadSettings.cs b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadSettings.cs new file mode 100644 index 0000000000..4d90418b83 --- /dev/null +++ b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadSettings.cs @@ -0,0 +1,19 @@ +using System.IO; +using Speckle.Core.Kits; // POC: Must go https://spockle.atlassian.net/browse/CNX-9325 + +namespace Speckle.Connectors.Autocad.HostApp; + +public class AutocadSettings +{ + public AutocadSettings(HostApplication hostAppInfo, HostAppVersion hostAppVersion) + { + HostAppInfo = hostAppInfo; + HostAppVersion = hostAppVersion; + Modules = new[] { new DirectoryInfo(typeof(AutocadSettings).Assembly.Location).Parent.FullName }; + } + + public HostApplication HostAppInfo { get; private set; } + public HostAppVersion HostAppVersion { get; private set; } + + public IReadOnlyList Modules { get; private set; } +} diff --git a/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/Extensions/AcadUnitsExtension.cs b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/Extensions/AcadUnitsExtension.cs new file mode 100644 index 0000000000..f4ee8e3a66 --- /dev/null +++ b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/Extensions/AcadUnitsExtension.cs @@ -0,0 +1,39 @@ +using Autodesk.AutoCAD.DatabaseServices; +using Speckle.Core.Kits; +using Speckle.Core.Logging; + +namespace Speckle.Connectors.Autocad.HostApp.Extensions; + +public static class AcadUnitsExtension +{ + public static string ToSpeckleString(this UnitsValue units) + { + switch (units) + { + case UnitsValue.Millimeters: + return Units.Millimeters; + case UnitsValue.Centimeters: + return Units.Centimeters; + case UnitsValue.Meters: + return Units.Meters; + case UnitsValue.Kilometers: + return Units.Kilometers; + case UnitsValue.Inches: + case UnitsValue.USSurveyInch: + return Units.Inches; + case UnitsValue.Feet: + case UnitsValue.USSurveyFeet: + return Units.Feet; + case UnitsValue.Yards: + case UnitsValue.USSurveyYard: + return Units.Yards; + case UnitsValue.Miles: + case UnitsValue.USSurveyMile: + return Units.Miles; + case UnitsValue.Undefined: + return Units.None; + default: + throw new SpeckleException($"The Unit System \"{units}\" is unsupported."); + } + } +} diff --git a/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/Extensions/DatabaseExtensions.cs b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/Extensions/DatabaseExtensions.cs new file mode 100644 index 0000000000..a763aa9eab --- /dev/null +++ b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/Extensions/DatabaseExtensions.cs @@ -0,0 +1,15 @@ +using Autodesk.AutoCAD.DatabaseServices; + +namespace Speckle.Connectors.Autocad.HostApp.Extensions; + +public static class DatabaseExtensions +{ + /// + /// Gets the document model space + /// + /// + /// + /// + public static BlockTableRecord GetModelSpace(this Database db, OpenMode mode = OpenMode.ForRead) => + (BlockTableRecord)SymbolUtilityServices.GetBlockModelSpaceId(db).GetObject(mode); +} diff --git a/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/Extensions/DocumentExtensions.cs b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/Extensions/DocumentExtensions.cs new file mode 100644 index 0000000000..300dd8e94f --- /dev/null +++ b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/Extensions/DocumentExtensions.cs @@ -0,0 +1,47 @@ +using Autodesk.AutoCAD.DatabaseServices; +using Autodesk.AutoCAD.Runtime; +using Speckle.Connectors.Autocad.Operations.Send; + +namespace Speckle.Connectors.Autocad.HostApp.Extensions; + +public static class DocumentExtensions +{ + public static List GetObjects(this Document doc, IEnumerable objectIds) + { + List objects = new(); + using (TransactionContext.StartTransaction(doc)) + { + Transaction tr = doc.Database.TransactionManager.TopTransaction; + + foreach (string objectIdHandle in objectIds) + { + if (long.TryParse(objectIdHandle, out long parsedId)) + { + Handle handle = new(parsedId); + // Note: Fatal crash happens here when objects are deleted, so we need to catch it. + try + { + if (doc.Database.TryGetObjectId(handle, out ObjectId myObjectId)) + { + if (tr.GetObject(myObjectId, OpenMode.ForRead) is DBObject dbObject) + { + objects.Add(new(dbObject, objectIdHandle)); + } + } + } + catch (Autodesk.AutoCAD.Runtime.Exception e) + { + if (e.ErrorStatus == ErrorStatus.WasErased) // Note: TBD if we want to catch more things in here. For now maybe not, but it does seem like this function gets into "crashes the host app territory" + { + continue; + } + + throw; + } + } + } + } + + return objects; + } +} diff --git a/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/Extensions/EditorExtensions.cs b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/Extensions/EditorExtensions.cs new file mode 100644 index 0000000000..5f61677369 --- /dev/null +++ b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/Extensions/EditorExtensions.cs @@ -0,0 +1,29 @@ +using Autodesk.AutoCAD.DatabaseServices; +using Autodesk.AutoCAD.EditorInput; +using Autodesk.AutoCAD.Geometry; + +namespace Speckle.Connectors.Autocad.HostApp.Extensions; + +public static class EditorExtensions +{ + public static void Zoom(this Editor editor, Extents3d ext) + { + if (editor == null) + { + throw new ArgumentNullException(nameof(editor)); + } + + using ViewTableRecord view = editor.GetCurrentView(); + + Matrix3d worldToEye = + Matrix3d.WorldToPlane(view.ViewDirection) + * Matrix3d.Displacement(Point3d.Origin - view.Target) + * Matrix3d.Rotation(view.ViewTwist, view.ViewDirection, view.Target); + + ext.TransformBy(worldToEye); + view.Width = ext.MaxPoint.X - ext.MinPoint.X; + view.Height = ext.MaxPoint.Y - ext.MinPoint.Y; + view.CenterPoint = new Point2d((ext.MaxPoint.X + ext.MinPoint.X) / 2.0, (ext.MaxPoint.Y + ext.MinPoint.Y) / 2.0); + editor.SetCurrentView(view); + } +} diff --git a/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/Extensions/EntityExtensions.cs b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/Extensions/EntityExtensions.cs new file mode 100644 index 0000000000..d98c98b3db --- /dev/null +++ b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/Extensions/EntityExtensions.cs @@ -0,0 +1,45 @@ +using Autodesk.AutoCAD.DatabaseServices; + +namespace Speckle.Connectors.Autocad.HostApp.Extensions; + +public static class EntityExtensions +{ + /// + /// Adds an entity to the autocad database model space record + /// + /// Entity to add into database. + /// Layer to append object. + /// Throws when there is no top transaction in the document. + public static ObjectId AppendToDb(this Entity entity, string? layer = null) + { + // POC: Will be addressed to move it into AutocadContext! + var db = entity.Database ?? Application.DocumentManager.MdiActiveDocument.Database; + Transaction tr = db.TransactionManager.TopTransaction; + if (tr == null) + { + throw new InvalidOperationException($"Document does not have a top transaction."); + } + + BlockTableRecord btr = db.GetModelSpace(OpenMode.ForWrite); + if (entity.IsNewObject) + { + if (layer != null) + { + entity.Layer = layer; + } + + var id = btr.AppendEntity(entity); + tr.AddNewlyCreatedDBObject(entity, true); + return id; + } + else + { + if (layer != null) + { + entity.Layer = layer; + } + + return entity.Id; + } + } +} diff --git a/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/TransactionContext.cs b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/TransactionContext.cs new file mode 100644 index 0000000000..8c6e97f041 --- /dev/null +++ b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/TransactionContext.cs @@ -0,0 +1,32 @@ +using Autodesk.AutoCAD.ApplicationServices; +using Autodesk.AutoCAD.DatabaseServices; + +namespace Speckle.Connectors.Autocad.HostApp; + +[System.Diagnostics.CodeAnalysis.SuppressMessage( + "Usage", + "CA2213:Disposable fields should be disposed", + Justification = "Analyzer false positive with Autocad classes" +)] +public sealed class TransactionContext : IDisposable +{ + private DocumentLock? _documentLock; + private Transaction? _transaction; + + public static TransactionContext StartTransaction(Document document) => new(document); + + private TransactionContext(Document document) + { + _documentLock = document.LockDocument(); + _transaction = document.Database.TransactionManager.StartTransaction(); + } + + public void Dispose() + { + _transaction?.Commit(); + _transaction = null; + + _documentLock?.Dispose(); + _documentLock = null; + } +} diff --git a/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/Interfaces/IAutocadPlugin.cs b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/Interfaces/IAutocadPlugin.cs new file mode 100644 index 0000000000..43ce46dcef --- /dev/null +++ b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/Interfaces/IAutocadPlugin.cs @@ -0,0 +1,7 @@ +namespace Speckle.Connectors.Autocad.Interfaces; + +internal interface IAutocadPlugin +{ + void Initialise(); + void Shutdown(); +} diff --git a/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/Operations/Receive/AutocadHostObjectBuilder.cs b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/Operations/Receive/AutocadHostObjectBuilder.cs new file mode 100644 index 0000000000..3e2e44c0fe --- /dev/null +++ b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/Operations/Receive/AutocadHostObjectBuilder.cs @@ -0,0 +1,181 @@ +using Autodesk.AutoCAD.DatabaseServices; +using Speckle.Connectors.Autocad.HostApp; +using Speckle.Connectors.Autocad.HostApp.Extensions; +using Speckle.Connectors.Autocad.Operations.Send; +using Speckle.Core.Models; +using Speckle.Connectors.Utils.Builders; +using Speckle.Connectors.Utils.Conversion; +using Speckle.Connectors.Utils.Instances; +using Speckle.Converters.Common; +using Speckle.Core.Logging; +using Speckle.Core.Models.GraphTraversal; +using Speckle.Core.Models.Instances; + +namespace Speckle.Connectors.Autocad.Operations.Receive; + +/// +/// Expects to be a scoped dependency per receive operation. +/// +public class AutocadHostObjectBuilder : IHostObjectBuilder +{ + private readonly AutocadLayerManager _autocadLayerManager; + private readonly IRootToHostConverter _converter; + private readonly GraphTraversal _traversalFunction; + + // private readonly HashSet _uniqueLayerNames = new(); + private readonly IInstanceObjectsManager> _instanceObjectsManager; + + public AutocadHostObjectBuilder( + IRootToHostConverter converter, + GraphTraversal traversalFunction, + AutocadLayerManager autocadLayerManager, + IInstanceObjectsManager> instanceObjectsManager + ) + { + _converter = converter; + _traversalFunction = traversalFunction; + _autocadLayerManager = autocadLayerManager; + _instanceObjectsManager = instanceObjectsManager; + } + + public HostObjectBuilderResult Build( + Base rootObject, + string projectName, + string modelName, + Action? onOperationProgressed, + CancellationToken cancellationToken + ) + { + // Prompt the UI conversion started. Progress bar will swoosh. + onOperationProgressed?.Invoke("Converting", null); + + // Layer filter for received commit with project and model name + _autocadLayerManager.CreateLayerFilter(projectName, modelName); + + //TODO: make the layerManager handle \/ ? + string baseLayerPrefix = $"SPK-{projectName}-{modelName}-"; + + PreReceiveDeepClean(baseLayerPrefix); + + List results = new(); + List bakedObjectIds = new(); + + // return new(bakedObjectIds, results); + + var objectGraph = _traversalFunction.Traverse(rootObject).Where(obj => obj.Current is not Collection); + + // POC: these are not captured by traversal, so we need to re-add them here + var instanceDefinitionProxies = (rootObject["instanceDefinitionProxies"] as List) + ?.Cast() + .ToList(); + + var instanceComponents = new List<(string[] path, IInstanceComponent obj)>(); + // POC: these are not captured by traversal, so we need to re-add them here + if (instanceDefinitionProxies != null && instanceDefinitionProxies.Count > 0) + { + var transformed = instanceDefinitionProxies.Select(proxy => (Array.Empty(), proxy as IInstanceComponent)); + instanceComponents.AddRange(transformed); + } + + var atomicObjects = new List<(string layerName, Base obj)>(); + + foreach (TraversalContext tc in objectGraph) + { + var layerName = _autocadLayerManager.GetLayerPath(tc, baseLayerPrefix); + if (tc.Current is IInstanceComponent instanceComponent) + { + instanceComponents.Add((new string[] { layerName }, instanceComponent)); + } + else + { + atomicObjects.Add((layerName, tc.Current)); + } + } + + // Stage 1: Convert atomic objects + Dictionary> applicationIdMap = new(); + var count = 0; + foreach (var (layerName, atomicObject) in atomicObjects) + { + onOperationProgressed?.Invoke("Converting objects", (double)++count / atomicObjects.Count); + try + { + var convertedObjects = ConvertObject(atomicObject, layerName).ToList(); + + if (atomicObject.applicationId != null) + { + applicationIdMap[atomicObject.applicationId] = convertedObjects; + } + + results.AddRange( + convertedObjects.Select( + e => + new ReceiveConversionResult( + Status.SUCCESS, + atomicObject, + e.Handle.Value.ToString(), + e.GetType().ToString() + ) + ) + ); + + bakedObjectIds.AddRange(convertedObjects.Select(e => e.Handle.Value.ToString())); + } + catch (Exception ex) when (!ex.IsFatal()) + { + results.Add(new(Status.ERROR, atomicObject, null, null, ex)); + } + } + + // Stage 2: Convert instances + var (createdInstanceIds, consumedObjectIds, instanceConversionResults) = _instanceObjectsManager.BakeInstances( + instanceComponents, + 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); + + return new(bakedObjectIds, results); + } + + private void PreReceiveDeepClean(string baseLayerPrefix) + { + _autocadLayerManager.DeleteAllLayersByPrefix(baseLayerPrefix); + _instanceObjectsManager.PurgeInstances(baseLayerPrefix); + } + + private IEnumerable ConvertObject(Base obj, string layerName) + { + using TransactionContext transactionContext = TransactionContext.StartTransaction( + Application.DocumentManager.MdiActiveDocument + ); + + _autocadLayerManager.CreateLayerForReceive(layerName); + + object converted; + using (var tr = Application.DocumentManager.CurrentDocument.Database.TransactionManager.StartTransaction()) + { + converted = _converter.Convert(obj); + tr.Commit(); + } + + IEnumerable flattened = Utilities.FlattenToHostConversionResult(converted).Cast(); + + foreach (Entity? conversionResult in flattened) + { + if (conversionResult == null) + { + // POC: This needed to be double checked why we check null and continue + continue; + } + + conversionResult.AppendToDb(layerName); + yield return conversionResult; + } + } +} diff --git a/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/Operations/Send/AutocadRootObject.cs b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/Operations/Send/AutocadRootObject.cs new file mode 100644 index 0000000000..02b33f8bff --- /dev/null +++ b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/Operations/Send/AutocadRootObject.cs @@ -0,0 +1,6 @@ +using Autodesk.AutoCAD.DatabaseServices; + +namespace Speckle.Connectors.Autocad.Operations.Send; + +// Note: naming is a bit confusing, Root is similar to base commit object, or root commit object, etc. It might be just in my head (dim) +public record AutocadRootObject(DBObject Root, string ApplicationId); diff --git a/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/Operations/Send/AutocadRootObjectBuilder.cs b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/Operations/Send/AutocadRootObjectBuilder.cs new file mode 100644 index 0000000000..5c09948f3b --- /dev/null +++ b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/Operations/Send/AutocadRootObjectBuilder.cs @@ -0,0 +1,113 @@ +using System.Diagnostics; +using Autodesk.AutoCAD.DatabaseServices; +using Speckle.Connectors.Utils.Builders; +using Speckle.Connectors.Utils.Caching; +using Speckle.Connectors.Utils.Conversion; +using Speckle.Connectors.Utils.Instances; +using Speckle.Connectors.Utils.Operations; +using Speckle.Converters.Common; +using Speckle.Core.Logging; +using Speckle.Core.Models; +using Speckle.Core.Models.Instances; + +namespace Speckle.Connectors.Autocad.Operations.Send; + +public class AutocadRootObjectBuilder : IRootObjectBuilder +{ + private readonly IRootToSpeckleConverter _converter; + private readonly string[] _documentPathSeparator = { "\\" }; + private readonly ISendConversionCache _sendConversionCache; + private readonly IInstanceObjectsManager> _instanceObjectsManager; + + public AutocadRootObjectBuilder( + IRootToSpeckleConverter converter, + ISendConversionCache sendConversionCache, + IInstanceObjectsManager> instanceObjectManager + ) + { + _converter = converter; + _sendConversionCache = sendConversionCache; + _instanceObjectsManager = instanceObjectManager; + } + + public RootObjectBuilderResult Build( + IReadOnlyList objects, + SendInfo sendInfo, + Action? onOperationProgressed = null, + CancellationToken ct = default + ) + { + Collection modelWithLayers = + new() + { + name = Application.DocumentManager.CurrentDocument.Name // POC: https://spockle.atlassian.net/browse/CNX-9319 + .Split(_documentPathSeparator, StringSplitOptions.None) + .Reverse() + .First(), + collectionType = "root" + }; + + // Cached dictionary to create Collection for autocad entity layers. We first look if collection exists. If so use it otherwise create new one for that layer. + Dictionary collectionCache = new(); + int count = 0; + + var (atomicObjects, instanceProxies, instanceDefinitionProxies) = _instanceObjectsManager.UnpackSelection(objects); + // POC: until we formalise a bit more the root object + modelWithLayers["instanceDefinitionProxies"] = instanceDefinitionProxies; + + List results = new(); + var cacheHitCount = 0; + + foreach (var (dbObject, applicationId) in atomicObjects) + { + ct.ThrowIfCancellationRequested(); + try + { + Base converted; + if (dbObject is BlockReference && instanceProxies.TryGetValue(applicationId, out InstanceProxy instanceProxy)) + { + converted = instanceProxy; + } + else if (_sendConversionCache.TryGetValue(sendInfo.ProjectId, applicationId, out ObjectReference value)) + { + converted = value; + cacheHitCount++; + } + else + { + converted = _converter.Convert(dbObject); + converted.applicationId = applicationId; + } + + // Create and add a collection for each layer if not done so already. + if ((dbObject as Entity)?.Layer is string layer) + { + if (!collectionCache.TryGetValue(layer, out Collection? collection)) + { + collection = new Collection() { name = layer, collectionType = "layer" }; + collectionCache[layer] = collection; + modelWithLayers.elements.Add(collectionCache[layer]); + } + + collection.elements.Add(converted); + } + + results.Add(new(Status.SUCCESS, applicationId, dbObject.GetType().ToString(), converted)); + } + catch (Exception ex) when (!ex.IsFatal()) + { + results.Add(new(Status.ERROR, applicationId, dbObject.GetType().ToString(), null, ex)); + // POC: add logging + } + + onOperationProgressed?.Invoke("Converting", (double)++count / atomicObjects.Count); + } + + // 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(modelWithLayers, results); + } +} diff --git a/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/Plugin/AutocadCommand.cs b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/Plugin/AutocadCommand.cs new file mode 100644 index 0000000000..2a08e16bc9 --- /dev/null +++ b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/Plugin/AutocadCommand.cs @@ -0,0 +1,69 @@ +using System.Drawing; +using System.Reflection; +using Autodesk.AutoCAD.Runtime; +using Autodesk.AutoCAD.Windows; +using Speckle.Autofac.DependencyInjection; +using Speckle.Connectors.Autocad.HostApp; +using Speckle.Core.Kits; +using Speckle.Connectors.Autocad.Interfaces; +using Speckle.Connectors.DUI.WebView; + +namespace Speckle.Connectors.Autocad.Plugin; + +public class AutocadCommand +{ + private static PaletteSet? PaletteSet { get; set; } + private static readonly Guid s_id = new("3223E594-1B09-4E54-B3DD-8EA0BECE7BA5"); + private IAutocadPlugin? _autocadPlugin; + + public SpeckleContainer? Container { get; private set; } + + [CommandMethod("SpeckleNewUI")] + public void Command() + { + if (PaletteSet != null) + { + FocusPalette(); + return; + } + + PaletteSet = new PaletteSet("Speckle DUI3", s_id) + { + Size = new Size(400, 500), + DockEnabled = (DockSides)((int)DockSides.Left + (int)DockSides.Right) + }; + + var builder = SpeckleContainerBuilder.CreateInstance(); + +#if CIVIL3D2024 + AutocadSettings autocadSettings = new (HostApplications.Civil3D, HostAppVersion.v2024); +#elif AUTOCAD2023 + AutocadSettings autocadSettings = new(HostApplications.AutoCAD, HostAppVersion.v2023); +#else + AutocadSettings autocadSettings = new(HostApplications.AutoCAD, HostAppVersion.v2023); +#endif + Container = builder + .LoadAutofacModules(Assembly.GetExecutingAssembly(), autocadSettings.Modules) + .AddSingleton(autocadSettings) + .Build(); + + // Resolve root plugin object and initialise. + _autocadPlugin = Container.Resolve(); + _autocadPlugin.Initialise(); + + var panelWebView = Container.Resolve(); + + PaletteSet.AddVisual("Speckle DUI3 WebView", panelWebView); + + FocusPalette(); + } + + private void FocusPalette() + { + if (PaletteSet != null) + { + PaletteSet.KeepFocus = true; + PaletteSet.Visible = true; + } + } +} diff --git a/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/Plugin/AutocadExtensionApplication.cs b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/Plugin/AutocadExtensionApplication.cs new file mode 100644 index 0000000000..d0773abcb0 --- /dev/null +++ b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/Plugin/AutocadExtensionApplication.cs @@ -0,0 +1,12 @@ +using Autodesk.AutoCAD.Runtime; +using Speckle.Autofac; + +namespace Speckle.Connectors.Autocad.Plugin; + +public class AutocadExtensionApplication : IExtensionApplication +{ + public void Initialize() => + AppDomain.CurrentDomain.AssemblyResolve += AssemblyResolver.OnAssemblyResolve; + + public void Terminate() { } +} diff --git a/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/Plugin/AutocadPlugin.cs b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/Plugin/AutocadPlugin.cs new file mode 100644 index 0000000000..5307a7b531 --- /dev/null +++ b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/Plugin/AutocadPlugin.cs @@ -0,0 +1,23 @@ +using Speckle.Connectors.Autocad.HostApp; +using Speckle.Connectors.Autocad.Interfaces; +using Speckle.Connectors.DUI.WebView; + +namespace Speckle.Connectors.Autocad.Plugin; + +public class AutocadPlugin : IAutocadPlugin +{ + private readonly AutocadIdleManager _idleManager; + private readonly DUI3ControlWebView _panel; + private readonly AutocadSettings _settings; + + public AutocadPlugin(DUI3ControlWebView panel, AutocadSettings settings, AutocadIdleManager idleManager) + { + _panel = panel; + _settings = settings; + _idleManager = idleManager; + } + + public void Initialise() { } + + public void Shutdown() { } +} diff --git a/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/Speckle.Connectors.AutocadShared.projitems b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/Speckle.Connectors.AutocadShared.projitems new file mode 100644 index 0000000000..8fb44c372f --- /dev/null +++ b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/Speckle.Connectors.AutocadShared.projitems @@ -0,0 +1,45 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + 55E65D72-2FE8-4E61-891C-16A4D551CCF7 + + + Speckle.Connectors.AutocadShared + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/Speckle.Connectors.AutocadShared.shproj b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/Speckle.Connectors.AutocadShared.shproj new file mode 100644 index 0000000000..afec409d7a --- /dev/null +++ b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/Speckle.Connectors.AutocadShared.shproj @@ -0,0 +1,12 @@ + + + + {41BC679F-887F-44CF-971D-A5502EE87DB0} + + + + + + + diff --git a/DUI3-DX/Connectors/Autocad/Speckle.Connectors.Civil3d2024/Speckle.Connectors.Civil3d2024.csproj b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.Civil3d2024/Speckle.Connectors.Civil3d2024.csproj new file mode 100644 index 0000000000..8e9190f710 --- /dev/null +++ b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.Civil3d2024/Speckle.Connectors.Civil3d2024.csproj @@ -0,0 +1,26 @@ + + + Speckle.Connectors.Civil3d + net48 + x64 + true + Program + $(ProgramW6432)\Autodesk\AutoCAD 2024\acad.exe + $(DefineConstants);CIVIL3D2024;CIVIL3D; + + + + + + + + + + + + + + + + + diff --git a/DUI3-DX/Connectors/Autocad/Speckle.Connectors.Civil3d2024/packages.lock.json b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.Civil3d2024/packages.lock.json new file mode 100644 index 0000000000..aff9c51d13 --- /dev/null +++ b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.Civil3d2024/packages.lock.json @@ -0,0 +1,563 @@ +{ + "version": 2, + "dependencies": { + ".NETFramework,Version=v4.8": { + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Direct", + "requested": "[7.0.0, )", + "resolved": "7.0.0", + "contentHash": "kmn78+LPVMOWeITUjIlfxUPDsI0R6G0RkeAMBmQxAJ7vBJn4q2dTva7pWi65ceN5vPGjJ9q/Uae2WKgvfktJAw==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Memory": "4.5.5" + } + }, + "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.AutoCAD.API": { + "type": "Direct", + "requested": "[2024.0.0, )", + "resolved": "2024.0.0", + "contentHash": "pZZ5uI+NXhZaQnsqRkgp/rywqBAjDObDJ9XNFGJvemT5k2OthDpHzlK/mKxz8QDCYie7uImQ8dv3uWj2QUFDPw==" + }, + "Speckle.Civil3D.API": { + "type": "Direct", + "requested": "[2024.0.0, )", + "resolved": "2024.0.0", + "contentHash": "9Q7M1k0DotN8w7MkiScQezErRdnZ4dAkxBMcPNhHSWoth/lSaT6UPV1aYEdl90RhehJWG4l3O7U2e3OXvVSFdw==", + "dependencies": { + "Speckle.AutoCAD.API": "2024.0.0" + } + }, + "Speckle.InterfaceGenerator": { + "type": "Direct", + "requested": "[0.9.5, )", + "resolved": "0.9.5", + "contentHash": "oU/L7pN1R7q8KkbrpQ3WJnHirPHqn+9DEA7asOcUiggV5dzVg1A/VYs7GOSusD24njxXh03tE3a2oTLOjt3cVg==" + }, + "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": "7.0.0", + "contentHash": "3aeMZ1N0lJoSyzqiP03hqemtb1BijhsJADdobn/4nsMJ8V1H+CrpuduUe4hlRdx+ikBQju1VGjMD1GJ3Sk05Eg==", + "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.DependencyInjection": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "elNeOmkeX3eDVG6pYVeV82p29hr+UKDaBhrZyWvWLw/EVZSYEkZlQdkp0V39k/Xehs2Qa0mvoCvkVj3eQxNQ1Q==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "7.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0", + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "h3j/QfmFN4S0w4C2A6X7arXij/M/OVw3uQHSOFxnND4DyAzO1F9eMX7Eti7lU/OkSthEE0WzRsfT/Dmx86jzCw==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "7.0.0", + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "Microsoft.Extensions.Logging": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "Nw2muoNrOG5U5qa2ZekXwudUn2BJcD41e65zwmDHb1fQegTX66UokLWZkJRpqSSHXDOWZ5V0iqhbxOEky91atA==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "7.0.0", + "Microsoft.Extensions.DependencyInjection": "7.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0", + "Microsoft.Extensions.Logging.Abstractions": "7.0.0", + "Microsoft.Extensions.Options": "7.0.0", + "System.Diagnostics.DiagnosticSource": "7.0.0", + "System.ValueTuple": "4.5.0" + } + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "lP1yBnTTU42cKpMozuafbvNtQ7QcBjr/CcK3bYOGEMH55Fjt+iecXjT6chR7vbgCMqy3PG3aNQSZgo/EuY/9qQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0", + "Microsoft.Extensions.Primitives": "7.0.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "um1KU5kxcRp3CNuI8o/GrZtD4AIOXDk+RLsytjZ9QPok3ttLUelLKpilVPuaFT3TFjOhSibUAso0odbOaCDj3Q==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "Polly": { + "type": "Transitive", + "resolved": "7.2.3", + "contentHash": "DeCY0OFbNdNxsjntr1gTXHJ5pKUwYzp04Er2LLeN3g6pWhffsGuKVfMBLe1lw7x76HrPkLxKEFxBlpRxS2nDEQ==" + }, + "Polly.Contrib.WaitAndRetry": { + "type": "Transitive", + "resolved": "1.1.1", + "contentHash": "1MUQLiSo4KDkQe6nzQRhIU05lm9jlexX5BVsbuw0SL82ynZ+GzAHQxJVDPVBboxV37Po3SG077aX8DuSy8TkaA==" + }, + "Polly.Extensions.Http": { + "type": "Transitive", + "resolved": "3.0.0", + "contentHash": "drrG+hB3pYFY7w1c3BD+lSGYvH2oIclH8GRSehgfyP5kjnFnHKQuuBhuHLv+PWyFuaTDyk/vfRpnxOzd11+J8g==", + "dependencies": { + "Polly": "7.1.0" + } + }, + "Sentry": { + "type": "Transitive", + "resolved": "3.33.0", + "contentHash": "8vbD2o6IR2wrRrkSiRbnodWGWUOqIlwYtzpjvPNOb5raJdOf+zxMwfS8f6nx9bmrTTfDj7KrCB8C/5OuicAc8A==", + "dependencies": { + "System.Reflection.Metadata": "5.0.0", + "System.Runtime.InteropServices.RuntimeInformation": "4.3.0", + "System.Text.Json": "5.0.2" + } + }, + "Sentry.Serilog": { + "type": "Transitive", + "resolved": "3.33.0", + "contentHash": "V8BU7QGWg2qLYfNPqtuTBhC1opysny5l+Ifp6J6PhOeAxU0FssR7nYfbJVetrnLIoh2rd3DlJ6hHYYQosQYcUQ==", + "dependencies": { + "Sentry": "3.33.0", + "Serilog": "2.7.1" + } + }, + "Serilog": { + "type": "Transitive", + "resolved": "2.12.0", + "contentHash": "xaiJLIdu6rYMKfQMYUZgTy8YK7SMZjB4Yk50C/u//Z4OsvxkUfSPJy4nknfvwAC34yr13q7kcyh4grbwhSxyZg==" + }, + "Serilog.Enrichers.ClientInfo": { + "type": "Transitive", + "resolved": "1.3.0", + "contentHash": "mTc7PM+wC9Hr7LWSwqt5mmnlAr7RJs+eTb3PGPRhwdOackk95MkhUZognuxXEdlW19HAFNmEBTSBY5DfLwM8jQ==", + "dependencies": { + "Serilog": "2.4.0" + } + }, + "Serilog.Exceptions": { + "type": "Transitive", + "resolved": "8.4.0", + "contentHash": "nc/+hUw3lsdo0zCj0KMIybAu7perMx79vu72w0za9Nsi6mWyNkGXxYxakAjWB7nEmYL6zdmhEQRB4oJ2ALUeug==", + "dependencies": { + "Serilog": "2.8.0" + } + }, + "Serilog.Formatting.Compact": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "pNroKVjo+rDqlxNG5PXkRLpfSCuDOBY0ri6jp9PLe505ljqwhwZz8ospy2vWhQlFu5GkIesh3FcDs4n7sWZODA==", + "dependencies": { + "Serilog": "2.8.0" + } + }, + "Serilog.Sinks.Console": { + "type": "Transitive", + "resolved": "4.1.0", + "contentHash": "K6N5q+5fetjnJPvCmkWOpJ/V8IEIoMIB1s86OzBrbxwTyHxdx3pmz4H+8+O/Dc/ftUX12DM1aynx/dDowkwzqg==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "Serilog.Sinks.File": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "uwV5hdhWPwUH1szhO8PJpFiahqXmzPzJT/sOijH/kFgUx+cyoDTMM8MHD0adw9+Iem6itoibbUXHYslzXsLEAg==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "Serilog.Sinks.PeriodicBatching": { + "type": "Transitive", + "resolved": "3.1.0", + "contentHash": "NDWR7m3PalVlGEq3rzoktrXikjFMLmpwF0HI4sowo8YDdU+gqPlTHlDQiOGxHfB0sTfjPA9JjA7ctKG9zqjGkw==", + "dependencies": { + "Serilog": "2.0.0" + } + }, + "Serilog.Sinks.Seq": { + "type": "Transitive", + "resolved": "5.2.2", + "contentHash": "1Csmo5ua7NKUe0yXUx+zsRefjAniPWcXFhUXxXG8pwo0iMiw2gjn9SOkgYnnxbgWqmlGv236w0N/dHc2v5XwMg==", + "dependencies": { + "Serilog": "2.12.0", + "Serilog.Formatting.Compact": "1.1.0", + "Serilog.Sinks.File": "5.0.0", + "Serilog.Sinks.PeriodicBatching": "3.1.0" + } + }, + "SerilogTimings": { + "type": "Transitive", + "resolved": "3.0.1", + "contentHash": "Zs28eTgszAMwpIrbBnWHBI50yuxL50p/dmAUWmy75+axdZYK/Sjm5/5m1N/CisR8acJUhTVcjPZrsB1P5iv0Uw==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "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.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, + "System.Collections.Immutable": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "FXkLXiK0sVVewcso0imKQoOxjoPAj42R8HtjjbSjVPAzwDfzoyoznWxgA3c38LDbN9SJux1xXoXYAhz98j7r2g==", + "dependencies": { + "System.Memory": "4.5.4" + } + }, + "System.Diagnostics.DiagnosticSource": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "9W0ewWDuAyDqS2PigdTxk6jDKonfgscY/hP8hm7VpxYhNHZHKvZTdRckberlFk3VnCmr3xBUyMBut12Q+T2aOw==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "System.DoubleNumerics": { + "type": "Transitive", + "resolved": "3.1.3", + "contentHash": "KRKEM/L3KBodjA9VOg3EifFVWUY6EOqaMB05UvPEDm7Zeby/kZW+4kdWUEPzW6xtkwf46p661L9NrbeeQhtLzw==", + "dependencies": { + "NETStandard.Library": "1.6.1" + } + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Numerics.Vectors": "4.5.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + }, + "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.5.0", + "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" + }, + "System.Reactive": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "System.Reflection.Metadata": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "5NecZgXktdGg34rh1OenY1rFNDCI8xSjFr+Z4OU4cU06AQHUdRnIIEeWENu3Wl4YowbzkymAIMvi3WyK9U53pQ==", + "dependencies": { + "System.Collections.Immutable": "5.0.0" + } + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + }, + "System.Runtime.InteropServices.RuntimeInformation": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==" + }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "5.0.1", + "contentHash": "KmJ+CJXizDofbq6mpqDoRRLcxgOd2z9X3XoFNULSbvbqVRZkFX3istvr+MUjL6Zw1RT+RNdoI4GYidIINtgvqQ==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Memory": "4.5.4" + } + }, + "System.Text.Json": { + "type": "Transitive", + "resolved": "5.0.2", + "contentHash": "I47dVIGiV6SfAyppphxqupertT/5oZkYLDCX6vC3HpOI4ZLjyoKAreUoem2ie6G0RbRuFrlqz/PcTQjfb2DOfQ==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "5.0.0", + "System.Buffers": "4.5.1", + "System.Memory": "4.5.4", + "System.Numerics.Vectors": "4.5.0", + "System.Runtime.CompilerServices.Unsafe": "5.0.0", + "System.Text.Encodings.Web": "5.0.1", + "System.Threading.Tasks.Extensions": "4.5.4", + "System.ValueTuple": "4.5.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" + } + }, + "System.ValueTuple": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "okurQJO6NRE/apDIP23ajJ0hpiNmJ+f0BwOlB/cSqTLQlw5upkf+5+96+iG2Jw40G1fCVCyPz/FhIABUjMR+RQ==" + }, + "speckle.autofac": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "[7.0.0, )" + } + }, + "speckle.connectors.dui": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "[7.0.0, )", + "Speckle.Autofac": "[2.0.999-local, )", + "Speckle.Connectors.Utils": "[2.0.999-local, )", + "Speckle.Core": "[3.0.1-alpha.14, )", + "System.Threading.Tasks.Dataflow": "[6.0.0, )" + } + }, + "speckle.connectors.dui.webview": { + "type": "Project", + "dependencies": { + "Microsoft.Web.WebView2": "[1.0.1823.32, )", + "Speckle.Connectors.DUI": "[2.0.999-local, )" + } + }, + "speckle.connectors.utils": { + "type": "Project", + "dependencies": { + "Serilog.Extensions.Logging": "[7.0.0, )", + "Speckle.Autofac": "[2.0.999-local, )", + "Speckle.Core": "[3.0.1-alpha.14, )" + } + }, + "speckle.converters.civil3d2024": { + "type": "Project", + "dependencies": { + "Speckle.AutoCAD.API": "[2024.0.0, )", + "Speckle.Civil3D.API": "[2024.0.0, )", + "Speckle.Converters.Common": "[2.0.999-local, )" + } + }, + "speckle.converters.civil3d2024.dependencyinjection": { + "type": "Project", + "dependencies": { + "Autofac": "[5.2.0, )", + "Speckle.Converters.Civil3d2024": "[2.0.999-local, )", + "Speckle.Converters.Common.DependencyInjection": "[2.0.999-local, )" + } + }, + "speckle.converters.common": { + "type": "Project", + "dependencies": { + "Speckle.Autofac": "[2.0.999-local, )", + "Speckle.Objects": "[3.0.1-alpha.14, )" + } + }, + "speckle.converters.common.dependencyinjection": { + "type": "Project", + "dependencies": { + "Speckle.Autofac": "[2.0.999-local, )", + "Speckle.Converters.Common": "[2.0.999-local, )" + } + }, + "Autofac": { + "type": "CentralTransitive", + "requested": "[5.2.0, )", + "resolved": "5.2.0", + "contentHash": "V8dBH0dsv75uDzl7Sw+HkhKDPUw2eXnlMjcSVMH+tLo2s67MpTKGyDj1pDcpR+IF2u4YRs0s3/x7R88YJzIWvg==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "1.1.0" + } + }, + "Microsoft.Web.WebView2": { + "type": "CentralTransitive", + "requested": "[1.0.1823.32, )", + "resolved": "1.0.1823.32", + "contentHash": "ppRcWBUNggFIqyJp7PfgS4Oe8/7yli/mHcTNDOaqo3ZzJWnv9AOr0WE9ceEP5SPs1M7CQ3QvdGMR7KNz0v09EA==" + }, + "Serilog.Extensions.Logging": { + "type": "CentralTransitive", + "requested": "[7.0.0, )", + "resolved": "7.0.0", + "contentHash": "9faU0zNQqU7I6soVhLUMYaGNpgWv6cKlKb2S5AnS8gXxzW/em5Ladm/6FMrWTnX41cdbdGPOWNAo6adi4WaJ6A==", + "dependencies": { + "Microsoft.Extensions.Logging": "7.0.0", + "Serilog": "2.12.0" + } + }, + "Speckle.Core": { + "type": "CentralTransitive", + "requested": "[3.0.1-alpha.14, )", + "resolved": "3.0.1-alpha.14", + "contentHash": "RzQPVIGFFkKvG56YLr8ACtiwdWJE6IJ9vCQ4qHa0PIsUEpfzAIAi59jnzqtByOFC0FiFrFPow9bkfzylaZorAA==", + "dependencies": { + "GraphQL.Client": "6.0.0", + "Microsoft.CSharp": "4.7.0", + "Microsoft.Data.Sqlite": "7.0.5", + "Polly": "7.2.3", + "Polly.Contrib.WaitAndRetry": "1.1.1", + "Polly.Extensions.Http": "3.0.0", + "Sentry": "3.33.0", + "Sentry.Serilog": "3.33.0", + "Serilog": "2.12.0", + "Serilog.Enrichers.ClientInfo": "1.3.0", + "Serilog.Exceptions": "8.4.0", + "Serilog.Sinks.Console": "4.1.0", + "Serilog.Sinks.Seq": "5.2.2", + "SerilogTimings": "3.0.1", + "Speckle.Newtonsoft.Json": "13.0.2", + "System.DoubleNumerics": "3.1.3" + } + }, + "Speckle.Objects": { + "type": "CentralTransitive", + "requested": "[3.0.1-alpha.14, )", + "resolved": "3.0.1-alpha.14", + "contentHash": "z38LGryMvh7iU1uBW+4uo5DwsB3CwRgLt2uFexWFx3mPSid+A0l5XcJzOgLwgFhNl6B42Ryz4ezBsddTp1Uc/g==", + "dependencies": { + "Speckle.Core": "3.0.1-alpha.14" + } + }, + "System.Threading.Tasks.Dataflow": { + "type": "CentralTransitive", + "requested": "[6.0.0, )", + "resolved": "6.0.0", + "contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA==" + } + } + } +} \ No newline at end of file diff --git a/DUI3-DX/Connectors/Revit/Speckle.Connectors.Revit2023/Properties/launchSettings.json b/DUI3-DX/Connectors/Revit/Speckle.Connectors.Revit2023/Properties/launchSettings.json new file mode 100644 index 0000000000..30c960cb70 --- /dev/null +++ b/DUI3-DX/Connectors/Revit/Speckle.Connectors.Revit2023/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "ConnectorRevit2023": { + "commandName": "Executable", + "executablePath": "C:\\Program Files\\Autodesk\\Revit 2023\\Revit.exe" + } + } +} diff --git a/DUI3-DX/Connectors/Revit/Speckle.Connectors.Revit2023/Speckle.Connectors.Revit2023.csproj b/DUI3-DX/Connectors/Revit/Speckle.Connectors.Revit2023/Speckle.Connectors.Revit2023.csproj new file mode 100644 index 0000000000..3cf6edeb8c --- /dev/null +++ b/DUI3-DX/Connectors/Revit/Speckle.Connectors.Revit2023/Speckle.Connectors.Revit2023.csproj @@ -0,0 +1,54 @@ + + + net48 + x64 + true + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DUI3-DX/Connectors/Revit/Speckle.Connectors.Revit2023/packages.lock.json b/DUI3-DX/Connectors/Revit/Speckle.Connectors.Revit2023/packages.lock.json new file mode 100644 index 0000000000..503e92ef50 --- /dev/null +++ b/DUI3-DX/Connectors/Revit/Speckle.Connectors.Revit2023/packages.lock.json @@ -0,0 +1,565 @@ +{ + "version": 2, + "dependencies": { + ".NETFramework,Version=v4.8": { + "CefSharp.Wpf": { + "type": "Direct", + "requested": "[92.0.260, )", + "resolved": "92.0.260", + "contentHash": "b0TW/HpHsvz2x3M1IQa/8fbCIRKShOgpmSqj5gKiibXY0v2cHgnWssfEX+3Q3UCpizkBJBQ+0fVp+fN8JCPcNQ==", + "dependencies": { + "CefSharp.Common": "[92.0.260]" + } + }, + "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==" + }, + "Revit.Async": { + "type": "Direct", + "requested": "[2.0.1, )", + "resolved": "2.0.1", + "contentHash": "B7D5zXznqgxMryBYdGgWob20ALfGSP7hJ6+bh9JdLM/LRkYN5dNf0AaI0+7VID9X7e8MA0koAxv9fIijJnmSnw==" + }, + "Speckle.InterfaceGenerator": { + "type": "Direct", + "requested": "[0.9.5, )", + "resolved": "0.9.5", + "contentHash": "oU/L7pN1R7q8KkbrpQ3WJnHirPHqn+9DEA7asOcUiggV5dzVg1A/VYs7GOSusD24njxXh03tE3a2oTLOjt3cVg==" + }, + "cef.redist.x64": { + "type": "Transitive", + "resolved": "92.0.26", + "contentHash": "wwwSGVgO9sdlj5TRnjVdzuCkNuYpTOXT90Z+dasnqGrnihrxKU+1UvulxxJa83O2k+l3kaNlAU9uBhiwGX7YyQ==" + }, + "cef.redist.x86": { + "type": "Transitive", + "resolved": "92.0.26", + "contentHash": "VT93sU6hH7LoUILjfaAxWbLc+m+b9QafeeRkuulnj3twJnb0PQ7nXd8PMUeOmYg96NzZi14G7qBYSY0dkxqztg==" + }, + "CefSharp.Common": { + "type": "Transitive", + "resolved": "92.0.260", + "contentHash": "0KS5z+kqg+mmB1X2KM17w3S7KQI5UTuJv+UOIQauJPvFYfRGWwTqmBbBxL3kCklGx8WSkaeBjPdSFxdzzXCSew==", + "dependencies": { + "cef.redist.x64": "[92.0.26]", + "cef.redist.x86": "[92.0.26]" + } + }, + "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": "7.0.0", + "contentHash": "3aeMZ1N0lJoSyzqiP03hqemtb1BijhsJADdobn/4nsMJ8V1H+CrpuduUe4hlRdx+ikBQju1VGjMD1GJ3Sk05Eg==", + "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.DependencyInjection": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "elNeOmkeX3eDVG6pYVeV82p29hr+UKDaBhrZyWvWLw/EVZSYEkZlQdkp0V39k/Xehs2Qa0mvoCvkVj3eQxNQ1Q==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "7.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0", + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "h3j/QfmFN4S0w4C2A6X7arXij/M/OVw3uQHSOFxnND4DyAzO1F9eMX7Eti7lU/OkSthEE0WzRsfT/Dmx86jzCw==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "7.0.0", + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "Microsoft.Extensions.Logging": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "Nw2muoNrOG5U5qa2ZekXwudUn2BJcD41e65zwmDHb1fQegTX66UokLWZkJRpqSSHXDOWZ5V0iqhbxOEky91atA==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "7.0.0", + "Microsoft.Extensions.DependencyInjection": "7.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0", + "Microsoft.Extensions.Logging.Abstractions": "7.0.0", + "Microsoft.Extensions.Options": "7.0.0", + "System.Diagnostics.DiagnosticSource": "7.0.0", + "System.ValueTuple": "4.5.0" + } + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "lP1yBnTTU42cKpMozuafbvNtQ7QcBjr/CcK3bYOGEMH55Fjt+iecXjT6chR7vbgCMqy3PG3aNQSZgo/EuY/9qQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0", + "Microsoft.Extensions.Primitives": "7.0.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "um1KU5kxcRp3CNuI8o/GrZtD4AIOXDk+RLsytjZ9QPok3ttLUelLKpilVPuaFT3TFjOhSibUAso0odbOaCDj3Q==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "Polly": { + "type": "Transitive", + "resolved": "7.2.3", + "contentHash": "DeCY0OFbNdNxsjntr1gTXHJ5pKUwYzp04Er2LLeN3g6pWhffsGuKVfMBLe1lw7x76HrPkLxKEFxBlpRxS2nDEQ==" + }, + "Polly.Contrib.WaitAndRetry": { + "type": "Transitive", + "resolved": "1.1.1", + "contentHash": "1MUQLiSo4KDkQe6nzQRhIU05lm9jlexX5BVsbuw0SL82ynZ+GzAHQxJVDPVBboxV37Po3SG077aX8DuSy8TkaA==" + }, + "Polly.Extensions.Http": { + "type": "Transitive", + "resolved": "3.0.0", + "contentHash": "drrG+hB3pYFY7w1c3BD+lSGYvH2oIclH8GRSehgfyP5kjnFnHKQuuBhuHLv+PWyFuaTDyk/vfRpnxOzd11+J8g==", + "dependencies": { + "Polly": "7.1.0" + } + }, + "Sentry": { + "type": "Transitive", + "resolved": "3.33.0", + "contentHash": "8vbD2o6IR2wrRrkSiRbnodWGWUOqIlwYtzpjvPNOb5raJdOf+zxMwfS8f6nx9bmrTTfDj7KrCB8C/5OuicAc8A==", + "dependencies": { + "System.Reflection.Metadata": "5.0.0", + "System.Runtime.InteropServices.RuntimeInformation": "4.3.0", + "System.Text.Json": "5.0.2" + } + }, + "Sentry.Serilog": { + "type": "Transitive", + "resolved": "3.33.0", + "contentHash": "V8BU7QGWg2qLYfNPqtuTBhC1opysny5l+Ifp6J6PhOeAxU0FssR7nYfbJVetrnLIoh2rd3DlJ6hHYYQosQYcUQ==", + "dependencies": { + "Sentry": "3.33.0", + "Serilog": "2.7.1" + } + }, + "Serilog": { + "type": "Transitive", + "resolved": "2.12.0", + "contentHash": "xaiJLIdu6rYMKfQMYUZgTy8YK7SMZjB4Yk50C/u//Z4OsvxkUfSPJy4nknfvwAC34yr13q7kcyh4grbwhSxyZg==" + }, + "Serilog.Enrichers.ClientInfo": { + "type": "Transitive", + "resolved": "1.3.0", + "contentHash": "mTc7PM+wC9Hr7LWSwqt5mmnlAr7RJs+eTb3PGPRhwdOackk95MkhUZognuxXEdlW19HAFNmEBTSBY5DfLwM8jQ==", + "dependencies": { + "Serilog": "2.4.0" + } + }, + "Serilog.Exceptions": { + "type": "Transitive", + "resolved": "8.4.0", + "contentHash": "nc/+hUw3lsdo0zCj0KMIybAu7perMx79vu72w0za9Nsi6mWyNkGXxYxakAjWB7nEmYL6zdmhEQRB4oJ2ALUeug==", + "dependencies": { + "Serilog": "2.8.0" + } + }, + "Serilog.Formatting.Compact": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "pNroKVjo+rDqlxNG5PXkRLpfSCuDOBY0ri6jp9PLe505ljqwhwZz8ospy2vWhQlFu5GkIesh3FcDs4n7sWZODA==", + "dependencies": { + "Serilog": "2.8.0" + } + }, + "Serilog.Sinks.Console": { + "type": "Transitive", + "resolved": "4.1.0", + "contentHash": "K6N5q+5fetjnJPvCmkWOpJ/V8IEIoMIB1s86OzBrbxwTyHxdx3pmz4H+8+O/Dc/ftUX12DM1aynx/dDowkwzqg==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "Serilog.Sinks.File": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "uwV5hdhWPwUH1szhO8PJpFiahqXmzPzJT/sOijH/kFgUx+cyoDTMM8MHD0adw9+Iem6itoibbUXHYslzXsLEAg==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "Serilog.Sinks.PeriodicBatching": { + "type": "Transitive", + "resolved": "3.1.0", + "contentHash": "NDWR7m3PalVlGEq3rzoktrXikjFMLmpwF0HI4sowo8YDdU+gqPlTHlDQiOGxHfB0sTfjPA9JjA7ctKG9zqjGkw==", + "dependencies": { + "Serilog": "2.0.0" + } + }, + "Serilog.Sinks.Seq": { + "type": "Transitive", + "resolved": "5.2.2", + "contentHash": "1Csmo5ua7NKUe0yXUx+zsRefjAniPWcXFhUXxXG8pwo0iMiw2gjn9SOkgYnnxbgWqmlGv236w0N/dHc2v5XwMg==", + "dependencies": { + "Serilog": "2.12.0", + "Serilog.Formatting.Compact": "1.1.0", + "Serilog.Sinks.File": "5.0.0", + "Serilog.Sinks.PeriodicBatching": "3.1.0" + } + }, + "SerilogTimings": { + "type": "Transitive", + "resolved": "3.0.1", + "contentHash": "Zs28eTgszAMwpIrbBnWHBI50yuxL50p/dmAUWmy75+axdZYK/Sjm5/5m1N/CisR8acJUhTVcjPZrsB1P5iv0Uw==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "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.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, + "System.Collections.Immutable": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "FXkLXiK0sVVewcso0imKQoOxjoPAj42R8HtjjbSjVPAzwDfzoyoznWxgA3c38LDbN9SJux1xXoXYAhz98j7r2g==", + "dependencies": { + "System.Memory": "4.5.4" + } + }, + "System.Diagnostics.DiagnosticSource": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "9W0ewWDuAyDqS2PigdTxk6jDKonfgscY/hP8hm7VpxYhNHZHKvZTdRckberlFk3VnCmr3xBUyMBut12Q+T2aOw==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "System.DoubleNumerics": { + "type": "Transitive", + "resolved": "3.1.3", + "contentHash": "KRKEM/L3KBodjA9VOg3EifFVWUY6EOqaMB05UvPEDm7Zeby/kZW+4kdWUEPzW6xtkwf46p661L9NrbeeQhtLzw==", + "dependencies": { + "NETStandard.Library": "1.6.1" + } + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Numerics.Vectors": "4.5.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + }, + "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.5.0", + "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" + }, + "System.Reactive": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "System.Reflection.Metadata": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "5NecZgXktdGg34rh1OenY1rFNDCI8xSjFr+Z4OU4cU06AQHUdRnIIEeWENu3Wl4YowbzkymAIMvi3WyK9U53pQ==", + "dependencies": { + "System.Collections.Immutable": "5.0.0" + } + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + }, + "System.Runtime.InteropServices.RuntimeInformation": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==" + }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "5.0.1", + "contentHash": "KmJ+CJXizDofbq6mpqDoRRLcxgOd2z9X3XoFNULSbvbqVRZkFX3istvr+MUjL6Zw1RT+RNdoI4GYidIINtgvqQ==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Memory": "4.5.4" + } + }, + "System.Text.Json": { + "type": "Transitive", + "resolved": "5.0.2", + "contentHash": "I47dVIGiV6SfAyppphxqupertT/5oZkYLDCX6vC3HpOI4ZLjyoKAreUoem2ie6G0RbRuFrlqz/PcTQjfb2DOfQ==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "5.0.0", + "System.Buffers": "4.5.1", + "System.Memory": "4.5.4", + "System.Numerics.Vectors": "4.5.0", + "System.Runtime.CompilerServices.Unsafe": "5.0.0", + "System.Text.Encodings.Web": "5.0.1", + "System.Threading.Tasks.Extensions": "4.5.4", + "System.ValueTuple": "4.5.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" + } + }, + "System.ValueTuple": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "okurQJO6NRE/apDIP23ajJ0hpiNmJ+f0BwOlB/cSqTLQlw5upkf+5+96+iG2Jw40G1fCVCyPz/FhIABUjMR+RQ==" + }, + "speckle.autofac": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "[7.0.0, )" + } + }, + "speckle.connectors.dui": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "[7.0.0, )", + "Speckle.Autofac": "[2.0.999-local, )", + "Speckle.Connectors.Utils": "[2.0.999-local, )", + "Speckle.Core": "[3.0.1-alpha.14, )", + "System.Threading.Tasks.Dataflow": "[6.0.0, )" + } + }, + "speckle.connectors.utils": { + "type": "Project", + "dependencies": { + "Serilog.Extensions.Logging": "[7.0.0, )", + "Speckle.Autofac": "[2.0.999-local, )", + "Speckle.Core": "[3.0.1-alpha.14, )" + } + }, + "speckle.converters.common": { + "type": "Project", + "dependencies": { + "Speckle.Autofac": "[2.0.999-local, )", + "Speckle.Objects": "[3.0.1-alpha.14, )" + } + }, + "speckle.converters.common.dependencyinjection": { + "type": "Project", + "dependencies": { + "Speckle.Autofac": "[2.0.999-local, )", + "Speckle.Converters.Common": "[2.0.999-local, )" + } + }, + "speckle.converters.revit2023": { + "type": "Project", + "dependencies": { + "Speckle.Converters.Common": "[2.0.999-local, )", + "Speckle.Revit.API": "[2023.0.0, )" + } + }, + "speckle.converters.revit2023.dependencyinjection": { + "type": "Project", + "dependencies": { + "Speckle.Converters.Common": "[2.0.999-local, )", + "Speckle.Converters.Common.DependencyInjection": "[2.0.999-local, )", + "Speckle.Converters.Revit2023": "[2.0.999-local, )" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "CentralTransitive", + "requested": "[7.0.0, )", + "resolved": "7.0.0", + "contentHash": "kmn78+LPVMOWeITUjIlfxUPDsI0R6G0RkeAMBmQxAJ7vBJn4q2dTva7pWi65ceN5vPGjJ9q/Uae2WKgvfktJAw==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Memory": "4.5.5" + } + }, + "Serilog.Extensions.Logging": { + "type": "CentralTransitive", + "requested": "[7.0.0, )", + "resolved": "7.0.0", + "contentHash": "9faU0zNQqU7I6soVhLUMYaGNpgWv6cKlKb2S5AnS8gXxzW/em5Ladm/6FMrWTnX41cdbdGPOWNAo6adi4WaJ6A==", + "dependencies": { + "Microsoft.Extensions.Logging": "7.0.0", + "Serilog": "2.12.0" + } + }, + "Speckle.Core": { + "type": "CentralTransitive", + "requested": "[3.0.1-alpha.14, )", + "resolved": "3.0.1-alpha.14", + "contentHash": "RzQPVIGFFkKvG56YLr8ACtiwdWJE6IJ9vCQ4qHa0PIsUEpfzAIAi59jnzqtByOFC0FiFrFPow9bkfzylaZorAA==", + "dependencies": { + "GraphQL.Client": "6.0.0", + "Microsoft.CSharp": "4.7.0", + "Microsoft.Data.Sqlite": "7.0.5", + "Polly": "7.2.3", + "Polly.Contrib.WaitAndRetry": "1.1.1", + "Polly.Extensions.Http": "3.0.0", + "Sentry": "3.33.0", + "Sentry.Serilog": "3.33.0", + "Serilog": "2.12.0", + "Serilog.Enrichers.ClientInfo": "1.3.0", + "Serilog.Exceptions": "8.4.0", + "Serilog.Sinks.Console": "4.1.0", + "Serilog.Sinks.Seq": "5.2.2", + "SerilogTimings": "3.0.1", + "Speckle.Newtonsoft.Json": "13.0.2", + "System.DoubleNumerics": "3.1.3" + } + }, + "Speckle.Objects": { + "type": "CentralTransitive", + "requested": "[3.0.1-alpha.14, )", + "resolved": "3.0.1-alpha.14", + "contentHash": "z38LGryMvh7iU1uBW+4uo5DwsB3CwRgLt2uFexWFx3mPSid+A0l5XcJzOgLwgFhNl6B42Ryz4ezBsddTp1Uc/g==", + "dependencies": { + "Speckle.Core": "3.0.1-alpha.14" + } + }, + "Speckle.Revit.API": { + "type": "CentralTransitive", + "requested": "[2023.0.0, )", + "resolved": "2023.0.0", + "contentHash": "tq40eD7psgTbV+epNouYyqfo6+hEi7FmXZqcxEOsAV7zfYyWhL6Rt3vmojkWGNuerGbH6oRI6KIIxrnlCNb8Hw==" + }, + "System.Threading.Tasks.Dataflow": { + "type": "CentralTransitive", + "requested": "[6.0.0, )", + "resolved": "6.0.0", + "contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA==" + } + } + } +} \ No newline at end of file diff --git a/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Assets/logo16.png b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Assets/logo16.png new file mode 100644 index 0000000000..61872f0d0c Binary files /dev/null and b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Assets/logo16.png differ diff --git a/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Assets/logo32.png b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Assets/logo32.png new file mode 100644 index 0000000000..2ad0c346ea Binary files /dev/null and b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Assets/logo32.png differ diff --git a/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Bindings/BasicConnectorBindingRevit.cs b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Bindings/BasicConnectorBindingRevit.cs new file mode 100644 index 0000000000..3aa6be233f --- /dev/null +++ b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Bindings/BasicConnectorBindingRevit.cs @@ -0,0 +1,117 @@ +using System.Reflection; +using Autodesk.Revit.DB; +using Revit.Async; +using Speckle.Connectors.Utils.Reflection; +using Speckle.Connectors.DUI.Bridge; +using Speckle.Connectors.DUI.Models; +using Speckle.Connectors.DUI.Models.Card; +using Speckle.Connectors.Revit.Plugin; +using Speckle.Connectors.Utils; +using Speckle.Converters.RevitShared.Helpers; +using Speckle.Core.Logging; + +namespace Speckle.Connectors.DUI.Bindings; + +internal sealed class BasicConnectorBindingRevit : IBasicConnectorBinding +{ + // POC: name and bridge might be better for them to be protected props? + public string Name { get; private set; } + public IBridge Parent { get; private set; } + + public BasicConnectorBindingCommands Commands { get; } + + private readonly DocumentModelStore _store; + private readonly RevitContext _revitContext; + private readonly RevitSettings _revitSettings; + + public BasicConnectorBindingRevit( + DocumentModelStore store, + RevitSettings revitSettings, + IBridge parent, + RevitContext revitContext + ) + { + Name = "baseBinding"; + Parent = parent; + _store = store; + _revitContext = revitContext; + _revitSettings = revitSettings; + Commands = new BasicConnectorBindingCommands(parent); + + // POC: event binding? + _store.DocumentChanged += (_, _) => + { + Commands.NotifyDocumentChanged(); + }; + } + + public string GetConnectorVersion() + { + return Assembly.GetAssembly(GetType()).GetVersion(); + } + + public string GetSourceApplicationName() => _revitSettings.HostSlug.ToLower(); // POC: maybe not right place but... // ANOTHER POC: We should align this naming from somewhere in common DUI projects instead old structs. I know there are other POC comments around this + + public string GetSourceApplicationVersion() => _revitSettings.HostAppVersion; // POC: maybe not right place but... + + public DocumentInfo? GetDocumentInfo() + { + // POC: not sure why this would ever be null, is this needed? + _revitContext.UIApplication.NotNull(); + + var doc = _revitContext.UIApplication.ActiveUIDocument?.Document; + if (doc is null) + { + return null; + } + + if (doc.IsFamilyDocument) + { + return new DocumentInfo("", "", "") { Message = "Family environment files not supported by Speckle." }; + } + + var info = new DocumentInfo(doc.PathName, doc.Title, doc.GetHashCode().ToString()); + + return info; + } + + public DocumentModelStore GetDocumentState() => _store; + + public void AddModel(ModelCard model) => _store.Models.Add(model); + + public void UpdateModel(ModelCard model) => _store.UpdateModel(model); + + public void RemoveModel(ModelCard model) => _store.RemoveModel(model); + + public void HighlightModel(string modelCardId) + { + SenderModelCard model = (SenderModelCard)_store.GetModelById(modelCardId); + + var elementIds = model.SendFilter.NotNull().GetObjectIds().Select(ElementId.Parse).ToList(); + if (elementIds.Count == 0) + { + Commands.SetModelError(modelCardId, new InvalidOperationException("No objects found to highlight.")); + return; + } + + HighlightObjectsOnView(elementIds); + } + + public void HighlightObjects(List objectIds) => + HighlightObjectsOnView(objectIds.Select(ElementId.Parse).ToList()); + + private void HighlightObjectsOnView(List objectIds) + { + // POC: don't know if we can rely on storing the ActiveUIDocument, hence getting it each time + var activeUIDoc = + _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. + RevitTask.RunAsync(() => + { + activeUIDoc.Selection.SetElementIds(objectIds); + activeUIDoc.ShowElements(objectIds); + }); + } +} diff --git a/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Bindings/Filters.cs b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Bindings/Filters.cs new file mode 100644 index 0000000000..4299f92d64 --- /dev/null +++ b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Bindings/Filters.cs @@ -0,0 +1,30 @@ +using Speckle.Connectors.DUI.Models.Card.SendFilter; + +namespace Speckle.Connectors.Revit.Bindings; + +public class RevitEverythingFilter : EverythingSendFilter +{ + public override List GetObjectIds() + { + // TODO + return new List(); + } + + public override bool CheckExpiry(string[] changedObjectIds) + { + return true; + } +} + +public class RevitSelectionFilter : DirectSelectionSendFilter +{ + public override List GetObjectIds() + { + return SelectedObjectIds; + } + + public override bool CheckExpiry(string[] changedObjectIds) + { + return SelectedObjectIds.Intersect(changedObjectIds).Any(); + } +} diff --git a/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Bindings/RevitBaseBinding.cs b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Bindings/RevitBaseBinding.cs new file mode 100644 index 0000000000..9774517d94 --- /dev/null +++ b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Bindings/RevitBaseBinding.cs @@ -0,0 +1,24 @@ +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; + +internal abstract class RevitBaseBinding : IBinding +{ + // POC: name and bridge might be better for them to be protected props? + public string Name { get; } + public IBridge Parent { get; } + + protected readonly DocumentModelStore Store; + protected readonly RevitContext RevitContext; + + protected RevitBaseBinding(string name, DocumentModelStore store, IBridge bridge, RevitContext revitContext) + { + Name = name; + Parent = bridge; + Store = store; + RevitContext = revitContext; + } +} diff --git a/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Bindings/RevitReceiveBinding.cs b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Bindings/RevitReceiveBinding.cs new file mode 100644 index 0000000000..77119f01a0 --- /dev/null +++ b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Bindings/RevitReceiveBinding.cs @@ -0,0 +1,82 @@ +using Speckle.Connectors.DUI.Bindings; +using Speckle.Connectors.DUI.Bridge; +using Speckle.Connectors.DUI.Models.Card; +using Speckle.Connectors.DUI.Models; +using Speckle.Connectors.Utils.Builders; +using Speckle.Connectors.Utils.Cancellation; +using Speckle.Connectors.Utils.Operations; +using Speckle.Autofac.DependencyInjection; +using Speckle.Connectors.Utils; + +namespace Speckle.Connectors.Revit.Bindings; + +internal class RevitReceiveBinding : IReceiveBinding +{ + public string Name => "receiveBinding"; + public IBridge Parent { get; } + + private readonly CancellationManager _cancellationManager; + private readonly DocumentModelStore _store; + private readonly IUnitOfWorkFactory _unitOfWorkFactory; + public ReceiveBindingUICommands Commands { get; } + + public RevitReceiveBinding( + DocumentModelStore store, + CancellationManager cancellationManager, + IBridge parent, + IUnitOfWorkFactory unitOfWorkFactory + ) + { + Parent = parent; + _store = store; + _unitOfWorkFactory = unitOfWorkFactory; + _cancellationManager = cancellationManager; + Commands = new ReceiveBindingUICommands(parent); + } + + public void CancelReceive(string modelCardId) => _cancellationManager.CancelOperation(modelCardId); + + public async Task Receive(string modelCardId) + { + using var unitOfWork = _unitOfWorkFactory.Resolve(); + 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."); + } + + // Init cancellation token source -> Manager also cancel it if exist before + CancellationTokenSource cts = _cancellationManager.InitCancellationTokenSource(modelCardId); + + // Receive host objects + HostObjectBuilderResult conversionResults = await unitOfWork.Service + .Execute( + modelCard.AccountId.NotNull(), // POC: I hear -you are saying why we're passing them separately. Not sure pass the DUI3-> Connectors.DUI project dependency to the SDK-> Connector.Utils + modelCard.ProjectId.NotNull(), + modelCard.ProjectName.NotNull(), + modelCard.ModelName.NotNull(), + modelCard.SelectedVersionId.NotNull(), + cts.Token, + (status, progress) => + Commands.SetModelProgress(modelCardId, new ModelCardProgress(modelCardId, status, progress), cts) + ) + .ConfigureAwait(false); + + modelCard.BakedObjectIds = conversionResults.BakedObjectIds.ToList(); + Commands.SetModelReceiveResult( + modelCardId, + conversionResults.BakedObjectIds, + conversionResults.ConversionResults + ); + } + // Catch here specific exceptions if they related to model card. + catch (OperationCanceledException) + { + // SWALLOW -> UI handles it immediately, so we do not need to handle anything + return; + } + } +} diff --git a/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Bindings/RevitSendBinding.cs b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Bindings/RevitSendBinding.cs new file mode 100644 index 0000000000..ee437aeea2 --- /dev/null +++ b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Bindings/RevitSendBinding.cs @@ -0,0 +1,200 @@ +using Autodesk.Revit.DB; +using Speckle.Connectors.DUI.Models.Card.SendFilter; +using Speckle.Connectors.DUI.Bridge; +using Speckle.Connectors.Revit.Plugin; +using Speckle.Connectors.Utils; +using Speckle.Converters.RevitShared.Helpers; +using Speckle.Connectors.DUI.Models.Card; +using Speckle.Connectors.DUI.Bindings; +using Speckle.Autofac.DependencyInjection; +using Speckle.Connectors.DUI.Exceptions; +using Speckle.Connectors.DUI.Models; +using Speckle.Connectors.Utils.Caching; +using Speckle.Connectors.Utils.Cancellation; +using Speckle.Connectors.Utils.Operations; + +namespace Speckle.Connectors.Revit.Bindings; + +internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding +{ + // POC:does it need injecting? + + // POC: does it need injecting? + private HashSet ChangedObjectIds { get; set; } = new(); + + private readonly RevitSettings _revitSettings; + private readonly IRevitIdleManager _idleManager; + private readonly CancellationManager _cancellationManager; + private readonly IUnitOfWorkFactory _unitOfWorkFactory; + private readonly ISendConversionCache _sendConversionCache; + private readonly ITopLevelExceptionHandler _topLevelExceptionHandler; + + public RevitSendBinding( + IRevitIdleManager idleManager, + RevitContext revitContext, + DocumentModelStore store, + CancellationManager cancellationManager, + IBridge bridge, + IUnitOfWorkFactory unitOfWorkFactory, + RevitSettings revitSettings, + ISendConversionCache sendConversionCache, + ITopLevelExceptionHandler topLevelExceptionHandler + ) + : base("sendBinding", store, bridge, revitContext) + { + _idleManager = idleManager; + _cancellationManager = cancellationManager; + _unitOfWorkFactory = unitOfWorkFactory; + _revitSettings = revitSettings; + _sendConversionCache = sendConversionCache; + _topLevelExceptionHandler = topLevelExceptionHandler; + + Commands = new SendBindingUICommands(bridge); + // TODO expiry events + // TODO filters need refresh events + revitContext.UIApplication.NotNull().Application.DocumentChanged += (_, e) => + _topLevelExceptionHandler.CatchUnhandled(() => DocChangeHandler(e)); + + Store.DocumentChanged += (_, _) => _topLevelExceptionHandler.CatchUnhandled(OnDocumentChanged); + } + + public List GetSendFilters() + { + return new List { new RevitSelectionFilter() { IsDefault = true } }; + } + + public void CancelSend(string modelCardId) + { + _cancellationManager.CancelOperation(modelCardId); + } + + public SendBindingUICommands Commands { get; } + + public async Task Send(string modelCardId) + { + // Note: removed top level handling thing as it was confusing me + try + { + if (Store.GetModelById(modelCardId) is not SenderModelCard modelCard) + { + // Handle as GLOBAL ERROR at BrowserBridge + throw new InvalidOperationException("No publish model card was found."); + } + + // POC: probably the CTS SHOULD be injected as InstancePerLifetimeScope and then + // it can be injected where needed instead of passing it around like a bomb :D + CancellationTokenSource cts = _cancellationManager.InitCancellationTokenSource(modelCardId); + + using IUnitOfWork> sendOperation = _unitOfWorkFactory.Resolve< + SendOperation + >(); + + List revitObjects = modelCard.SendFilter + .NotNull() + .GetObjectIds() + .Select(id => ElementId.Parse(id)) + .ToList(); + + if (revitObjects.Count == 0) + { + // Handle as CARD ERROR in this function + throw new SpeckleSendFilterException("No objects were found to convert. Please update your publish filter!"); + } + + var sendInfo = new SendInfo( + modelCard.AccountId.NotNull(), + modelCard.ProjectId.NotNull(), + modelCard.ModelId.NotNull(), + _revitSettings.HostSlug.NotNull() + ); + + var sendResult = await sendOperation.Service + .Execute( + revitObjects, + sendInfo, + (status, progress) => + Commands.SetModelProgress(modelCardId, new ModelCardProgress(modelCardId, status, progress), cts), + cts.Token + ) + .ConfigureAwait(false); + + Commands.SetModelSendResult(modelCardId, sendResult.RootObjId, sendResult.ConversionResults); + } + // Catch here specific exceptions if they related to model card. + catch (SpeckleSendFilterException e) + { + Commands.SetModelError(modelCardId, e); + } + catch (OperationCanceledException) + { + return; + } + } + + /// + /// Keeps track of the changed element ids as well as checks if any of them need to trigger + /// a filter refresh (e.g., views being added). + /// + /// + private void DocChangeHandler(Autodesk.Revit.DB.Events.DocumentChangedEventArgs e) + { + ICollection addedElementIds = e.GetAddedElementIds(); + ICollection deletedElementIds = e.GetDeletedElementIds(); + ICollection modifiedElementIds = e.GetModifiedElementIds(); + + foreach (ElementId elementId in addedElementIds) + { + ChangedObjectIds.Add(elementId.ToString()); + } + + foreach (ElementId elementId in deletedElementIds) + { + ChangedObjectIds.Add(elementId.ToString()); + } + + foreach (ElementId elementId in modifiedElementIds) + { + ChangedObjectIds.Add(elementId.ToString()); + } + + // TODO: CHECK IF ANY OF THE ABOVE ELEMENTS NEED TO TRIGGER A FILTER REFRESH + _idleManager.SubscribeToIdle(RunExpirationChecks); + } + + private void RunExpirationChecks() + { + var senders = Store.GetSenders(); + string[] objectIdsList = ChangedObjectIds.ToArray(); + List expiredSenderIds = new(); + + _sendConversionCache.EvictObjects(objectIdsList); + + foreach (SenderModelCard modelCard in senders) + { + var intersection = modelCard.SendFilter.NotNull().GetObjectIds().Intersect(objectIdsList).ToList(); + bool isExpired = intersection.Count != 0; + if (isExpired) + { + expiredSenderIds.Add(modelCard.ModelCardId.NotNull()); + } + } + + Commands.SetModelsExpired(expiredSenderIds); + ChangedObjectIds = new HashSet(); + } + + // POC: Will be re-addressed later with better UX with host apps that are friendly on async doc operations. + // That's why don't bother for now how to get rid of from dup logic in other bindings. + private void OnDocumentChanged() + { + if (_cancellationManager.NumberOfOperations > 0) + { + _cancellationManager.CancelAllOperations(); + Commands.SetGlobalNotification( + ToastNotificationType.INFO, + "Document Switch", + "Operations cancelled because of document swap!" + ); + } + } +} diff --git a/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Bindings/SelectionBinding.cs b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Bindings/SelectionBinding.cs new file mode 100644 index 0000000000..c74a0606bd --- /dev/null +++ b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Bindings/SelectionBinding.cs @@ -0,0 +1,49 @@ +using Speckle.Connectors.DUI.Bindings; +using Speckle.Connectors.DUI.Bridge; +using Speckle.Connectors.DUI.Models; +using Speckle.Connectors.Revit.Plugin; +using Speckle.Converters.Common; +using Speckle.Converters.RevitShared.Helpers; + +namespace Speckle.Connectors.Revit.Bindings; + +// POC: we need a base a RevitBaseBinding +internal sealed class SelectionBinding : RevitBaseBinding, ISelectionBinding +{ + private readonly IRevitIdleManager _revitIdleManager; + private readonly ITopLevelExceptionHandler _topLevelExceptionHandler; + + public SelectionBinding( + RevitContext revitContext, + DocumentModelStore store, + IRevitIdleManager idleManager, + IBridge bridge, + ITopLevelExceptionHandler topLevelExceptionHandler + ) + : base("selectionBinding", store, bridge, revitContext) + { + _revitIdleManager = idleManager; + _topLevelExceptionHandler = topLevelExceptionHandler; + // POC: we can inject the solution here + // TODO: Need to figure it out equivalent of SelectionChanged for Revit2020 + RevitContext.UIApplication.NotNull().SelectionChanged += (_, _) => + topLevelExceptionHandler.CatchUnhandled(() => _revitIdleManager.SubscribeToIdle(OnSelectionChanged)); + } + + private void OnSelectionChanged() + { + Parent.Send(SelectionBindingEvents.SET_SELECTION, GetSelection()); + } + + public SelectionInfo GetSelection() + { + // POC: this was also being called on shutdown + // probably the bridge needs to be able to know if the plugin has been terminated + // also on termination the OnSelectionChanged event needs unwinding + var selectionIds = (RevitContext.UIApplication?.ActiveUIDocument?.Selection.GetElementIds()) + .NotNull() + .Select(id => id.ToString()) + .ToList(); + return new SelectionInfo(selectionIds, $"{selectionIds.Count} objects selected."); + } +} diff --git a/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/DependencyInjection/RevitConnectorModule.cs b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/DependencyInjection/RevitConnectorModule.cs new file mode 100644 index 0000000000..7ec943cde0 --- /dev/null +++ b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/DependencyInjection/RevitConnectorModule.cs @@ -0,0 +1,72 @@ +using Autodesk.Revit.DB; +using CefSharp; +using Speckle.Autofac; +using Speckle.Autofac.DependencyInjection; +using Speckle.Connectors.DUI; +using Speckle.Connectors.DUI.Bindings; +using Speckle.Connectors.DUI.Models; +using Speckle.Connectors.Revit.Bindings; +using Speckle.Connectors.Revit.HostApp; +using Speckle.Connectors.Revit.Operations.Receive; +using Speckle.Connectors.Revit.Operations.Send; +using Speckle.Connectors.Revit.Plugin; +using Speckle.Connectors.Utils; +using Speckle.Connectors.Utils.Builders; +using Speckle.Connectors.Utils.Caching; +using Speckle.Connectors.Utils.Operations; +using Speckle.Core.Models.GraphTraversal; + +namespace Speckle.Connectors.Revit.DependencyInjection; + +// POC: should interface out things that are not +public class RevitConnectorModule : ISpeckleModule +{ + public void Load(SpeckleContainerBuilder builder) + { + builder.AddAutofac(); + builder.AddConnectorUtils(); + builder.AddDUI(); + //builder.AddDUIView(); + + builder.AddSingletonInstance(); + + // POC: different versons for different versions of CEF + builder.AddSingleton(BindingOptions.DefaultBinder); + + var panel = new CefSharpPanel(); + panel.Browser.JavascriptObjectRepository.NameConverter = null; + + builder.AddSingleton(panel); + builder.AddSingleton(); + + // register + builder.AddSingleton(); + + // Storage Schema + builder.AddScoped(); + builder.AddScoped(); + + // POC: we need to review the scopes and create a document on what the policy is + // and where the UoW should be + // register UI bindings + builder.AddSingleton(); + builder.AddSingleton("connectorName", "Revit"); // POC: Easier like this for now, should be cleaned up later + builder.AddSingleton(); + builder.AddSingleton(); + builder.AddSingleton(); + builder.AddSingleton(); + builder.AddSingleton(); + builder.AddSingleton(); + builder.AddSingleton(); + + // send operation and dependencies + builder.AddScoped>(); + builder.AddScoped, RevitRootObjectBuilder>(); + builder.AddSingleton(); + + // receive operation and dependencies + builder.AddScoped(); + builder.AddScoped(); + builder.AddSingleton(DefaultTraversal.CreateTraversalFunc()); + } +} diff --git a/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/HostApp/DocumentModelStorageSchema.cs b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/HostApp/DocumentModelStorageSchema.cs new file mode 100644 index 0000000000..ded698f755 --- /dev/null +++ b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/HostApp/DocumentModelStorageSchema.cs @@ -0,0 +1,22 @@ +using Autodesk.Revit.DB.ExtensibleStorage; + +namespace Speckle.Connectors.Revit.HostApp; + +public class DocumentModelStorageSchema : IStorageSchema +{ + private readonly Guid _schemaGuid = new("D690F2B4-BDB0-4CB4-8657-17844ADF42AA"); + + public Schema GetSchema() + { + Schema schema = Schema.Lookup(_schemaGuid); + if (schema != null) + { + return schema; + } + + using SchemaBuilder builder = new(_schemaGuid); + builder.SetSchemaName("DUI3State"); + builder.AddSimpleField("contents", typeof(string)); + return builder.Finish(); + } +} diff --git a/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/HostApp/Elements.cs b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/HostApp/Elements.cs new file mode 100644 index 0000000000..acf4dc1d2e --- /dev/null +++ b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/HostApp/Elements.cs @@ -0,0 +1,12 @@ +using Autodesk.Revit.DB; + +namespace Speckle.Connectors.Revit.HostApp; + +// POC: is this really better than injection? :/ +public static class Elements +{ + public static IEnumerable GetElements(this Document doc, IEnumerable objectIds) + { + return objectIds.Select(doc.GetElement).Where(x => x != null); + } +} diff --git a/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/HostApp/IStorageSchema.cs b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/HostApp/IStorageSchema.cs new file mode 100644 index 0000000000..4a1a68274b --- /dev/null +++ b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/HostApp/IStorageSchema.cs @@ -0,0 +1,8 @@ +using Autodesk.Revit.DB.ExtensibleStorage; + +namespace Speckle.Connectors.Revit.HostApp; + +public interface IStorageSchema +{ + Schema GetSchema(); +} diff --git a/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/HostApp/IdStorageSchema.cs b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/HostApp/IdStorageSchema.cs new file mode 100644 index 0000000000..398c972678 --- /dev/null +++ b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/HostApp/IdStorageSchema.cs @@ -0,0 +1,22 @@ +using Autodesk.Revit.DB.ExtensibleStorage; + +namespace Speckle.Connectors.Revit.HostApp; + +public class IdStorageSchema : IStorageSchema +{ + private readonly Guid _schemaGuid = new("D0E2AD18-0DE0-41CF-A2B7-5384267061D7"); + + public Schema GetSchema() + { + Schema schema = Schema.Lookup(_schemaGuid); + if (schema != null) + { + return schema; + } + + using SchemaBuilder builder = new(_schemaGuid); + builder.SetSchemaName("DataStorageUniqueId"); + builder.AddSimpleField("Id", typeof(Guid)); + return builder.Finish(); + } +} diff --git a/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/HostApp/RevitDocumentStore.cs b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/HostApp/RevitDocumentStore.cs new file mode 100644 index 0000000000..42acdc95ca --- /dev/null +++ b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/HostApp/RevitDocumentStore.cs @@ -0,0 +1,183 @@ +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.DUI.Bridge; +using Speckle.Connectors.DUI.Models; +using Speckle.Connectors.Revit.Plugin; +using Speckle.Connectors.Utils; +using Speckle.Converters.RevitShared.Helpers; +using Speckle.Core.Logging; +using Speckle.Newtonsoft.Json; + +namespace Speckle.Connectors.Revit.HostApp; + +// POC: should be interfaced out +internal sealed class RevitDocumentStore : DocumentModelStore +{ + // POC: move to somewhere central? + private static readonly Guid s_revitDocumentStoreId = new("D35B3695-EDC9-4E15-B62A-D3FC2CB83FA3"); + + private readonly RevitContext _revitContext; + private readonly IRevitIdleManager _idleManager; + private readonly DocumentModelStorageSchema _documentModelStorageSchema; + private readonly IdStorageSchema _idStorageSchema; + + public RevitDocumentStore( + IRevitIdleManager idleManager, + RevitContext revitContext, + JsonSerializerSettings serializerSettings, + DocumentModelStorageSchema documentModelStorageSchema, + IdStorageSchema idStorageSchema, + ITopLevelExceptionHandler topLevelExceptionHandler + ) + : base(serializerSettings, true) + { + _idleManager = idleManager; + _revitContext = revitContext; + _documentModelStorageSchema = documentModelStorageSchema; + _idStorageSchema = idStorageSchema; + + UIApplication uiApplication = _revitContext.UIApplication.NotNull(); + + uiApplication.ViewActivated += (s, e) => topLevelExceptionHandler.CatchUnhandled(() => OnViewActivated(s, e)); + + uiApplication.Application.DocumentOpening += (_, _) => + topLevelExceptionHandler.CatchUnhandled(() => IsDocumentInit = false); + + uiApplication.Application.DocumentOpened += (_, _) => + topLevelExceptionHandler.CatchUnhandled(() => IsDocumentInit = false); + + Models.CollectionChanged += (_, _) => topLevelExceptionHandler.CatchUnhandled(WriteToFile); + + // 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(); + OnDocumentChanged(); + } + + /// + /// This is the place where we track document switch for new document -> Responsible to Read from new doc + /// + private void OnViewActivated(object sender, ViewActivatedEventArgs e) + { + if (e.Document == null) + { + return; + } + + // Return only if we are switching views that belongs to same document + if (e.PreviousActiveView?.Document != null && e.PreviousActiveView.Document.Equals(e.CurrentActiveView.Document)) + { + return; + } + + IsDocumentInit = true; + _idleManager.SubscribeToIdle(() => + { + ReadFromFile(); + OnDocumentChanged(); + }); + } + + public override void WriteToFile() + { + var doc = _revitContext.UIApplication?.ActiveUIDocument.Document; + // POC: this can happen? A: Not really, imho (dim) + if (doc == null) + { + return; + } + + RevitTask.RunAsync(() => + { + 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 idEntity = new(_idStorageSchema.GetSchema()); + idEntity.Set("Id", s_revitDocumentStoreId); + + ds.SetEntity(idEntity); + ds.SetEntity(stateEntity); + t.Commit(); + }); + } + + public override void ReadFromFile() + { + try + { + var stateEntity = GetSpeckleEntity(_revitContext.UIApplication?.ActiveUIDocument?.Document); + if (stateEntity == null || !stateEntity.IsValid()) + { + Models = new(); + 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 + } + } + + private DataStorage? GetSettingsDataStorage(Document doc) + { + using FilteredElementCollector collector = new(doc); + FilteredElementCollector dataStorages = collector.OfClass(typeof(DataStorage)); + + foreach (Element element in dataStorages) + { + DataStorage dataStorage = (DataStorage)element; + Entity settingIdEntity = dataStorage.GetEntity(_idStorageSchema.GetSchema()); + if (!settingIdEntity.IsValid()) + { + continue; + } + + Guid id = settingIdEntity.Get("Id"); + if (!id.Equals(s_revitDocumentStoreId)) + { + continue; + } + + return dataStorage; + } + + return null; + } + + private Entity? GetSpeckleEntity(Document? doc) + { + if (doc is null) + { + return null; + } + using FilteredElementCollector collector = new(doc); + + FilteredElementCollector dataStorages = collector.OfClass(typeof(DataStorage)); + foreach (Element element in dataStorages) + { + DataStorage dataStorage = (DataStorage)element; + Entity settingEntity = dataStorage.GetEntity(_documentModelStorageSchema.GetSchema()); + if (!settingEntity.IsValid()) + { + continue; + } + + return settingEntity; + } + + return null; + } +} diff --git a/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Operations/Receive/ITransactionManager.cs b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Operations/Receive/ITransactionManager.cs new file mode 100644 index 0000000000..d698dfac0f --- /dev/null +++ b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Operations/Receive/ITransactionManager.cs @@ -0,0 +1,13 @@ +using Autodesk.Revit.DB; + +namespace Speckle.Connectors.Revit.Operations.Receive; + +public interface ITransactionManager : IDisposable +{ + TransactionStatus CommitSubtransaction(); + TransactionStatus CommitTransaction(); + void RollbackSubTransaction(); + void RollbackTransaction(); + void StartSubtransaction(); + void StartTransaction(); +} diff --git a/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Operations/Receive/RevitContextAccessor.cs b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Operations/Receive/RevitContextAccessor.cs new file mode 100644 index 0000000000..d90ce9076b --- /dev/null +++ b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Operations/Receive/RevitContextAccessor.cs @@ -0,0 +1,9 @@ +using Revit.Async; +using Speckle.Connectors.Utils.Operations; + +namespace Speckle.Connectors.Revit.Operations.Receive; + +internal class RevitContextAccessor : ISyncToThread +{ + public Task RunOnThread(Func func) => RevitTask.RunAsync(func); +} diff --git a/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Operations/Receive/RevitHostObjectBuilder.cs b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Operations/Receive/RevitHostObjectBuilder.cs new file mode 100644 index 0000000000..245284543d --- /dev/null +++ b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Operations/Receive/RevitHostObjectBuilder.cs @@ -0,0 +1,85 @@ +using Speckle.Connectors.Utils.Builders; +using Speckle.Connectors.Utils.Conversion; +using Speckle.Converters.Common; +using Speckle.Core.Logging; +using Speckle.Core.Models.GraphTraversal; +using Speckle.Core.Models; +using Speckle.Converters.RevitShared.Helpers; +using Autodesk.Revit.DB; + +namespace Speckle.Connectors.Revit.Operations.Receive; + +/// +/// Potentially consolidate all application specific IHostObjectBuilders +/// https://spockle.atlassian.net/browse/DUI3-465 +/// +internal class RevitHostObjectBuilder : IHostObjectBuilder, IDisposable +{ + private readonly IRootToHostConverter _converter; + private readonly IRevitConversionContextStack _contextStack; + private readonly GraphTraversal _traverseFunction; + private readonly ITransactionManager _transactionManager; + + public RevitHostObjectBuilder( + IRootToHostConverter converter, + IRevitConversionContextStack contextStack, + GraphTraversal traverseFunction, + ITransactionManager transactionManager + ) + { + _converter = converter; + _contextStack = contextStack; + _traverseFunction = traverseFunction; + _transactionManager = transactionManager; + } + + public HostObjectBuilderResult Build( + Base rootObject, + string projectName, + string modelName, + Action? onOperationProgressed, + CancellationToken cancellationToken + ) + { + var objectsToConvert = _traverseFunction + .TraverseWithProgress(rootObject, onOperationProgressed, cancellationToken) + .Where(obj => obj.Current is not Collection); + + using TransactionGroup transactionGroup = new(_contextStack.Current.Document, $"Received data from {projectName}"); + transactionGroup.Start(); + _transactionManager.StartTransaction(); + + var conversionResults = BakeObjects(objectsToConvert); + + _transactionManager.CommitTransaction(); + transactionGroup.Assimilate(); + + return conversionResults; + } + + // POC: Potentially refactor out into an IObjectBaker. + private HostObjectBuilderResult BakeObjects(IEnumerable objectsGraph) + { + var conversionResults = new List(); + var bakedObjectIds = new List(); + + foreach (TraversalContext tc in objectsGraph) + { + try + { + var result = _converter.Convert(tc.Current); + } + catch (Exception ex) when (!ex.IsFatal()) + { + conversionResults.Add(new(Status.ERROR, tc.Current, null, null, ex)); + } + } + + return new(bakedObjectIds, conversionResults); + } + + public void Dispose() + { + _transactionManager?.Dispose(); + } +} diff --git a/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Operations/Receive/TransactionManager.cs b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Operations/Receive/TransactionManager.cs new file mode 100644 index 0000000000..aa2ec3153a --- /dev/null +++ b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Operations/Receive/TransactionManager.cs @@ -0,0 +1,124 @@ +using Autodesk.Revit.DB; +using Speckle.Converters.RevitShared.Helpers; + +namespace Speckle.Connectors.Revit.Operations.Receive; + +/// +/// Is responsible for all functionality regarding subtransactions, transactions, and transaction groups. +/// This includes starting, pausing, committing, and rolling back transactions +/// +public sealed class TransactionManager : ITransactionManager +{ + private readonly IRevitConversionContextStack _contextStack; + private Document Document => _contextStack.Current.Document; + + public TransactionManager(IRevitConversionContextStack contextStack) + { + _contextStack = contextStack; + } + + // poc : these are being disposed. I'm not sure why I need to supress this warning +#pragma warning disable CA2213 // Disposable fields should be disposed + private Transaction? _transaction; + private SubTransaction? _subTransaction; +#pragma warning restore CA2213 // Disposable fields should be disposed + + public void StartTransaction() + { + if (_transaction == null || !_transaction.IsValidObject || _transaction.GetStatus() != TransactionStatus.Started) + { + _transaction = new Transaction(Document, "Speckle Transaction"); + var failOpts = _transaction.GetFailureHandlingOptions(); + // POC: make sure to implement and add the failure preprocessor + // https://spockle.atlassian.net/browse/DUI3-461 + //failOpts.SetFailuresPreprocessor(_errorPreprocessingService); + failOpts.SetClearAfterRollback(true); + _transaction.SetFailureHandlingOptions(failOpts); + _transaction.Start(); + } + } + + public TransactionStatus CommitTransaction() + { + if ( + _subTransaction != null + && _subTransaction.IsValidObject + && _subTransaction.GetStatus() == TransactionStatus.Started + ) + { + var status = _subTransaction.Commit(); + if (status != TransactionStatus.Committed) + { + // POC: handle failed commit + //HandleFailedCommit(status); + } + } + if (_transaction != null && _transaction.IsValidObject && _transaction.GetStatus() == TransactionStatus.Started) + { + var status = _transaction.Commit(); + if (status != TransactionStatus.Committed) + { + // POC: handle failed commit + //HandleFailedCommit(status); + } + return status; + } + return TransactionStatus.Uninitialized; + } + + public void RollbackTransaction() + { + RollbackSubTransaction(); + if (_transaction != null && _transaction.IsValidObject && _transaction.GetStatus() == TransactionStatus.Started) + { + _transaction.RollBack(); + } + } + + public void StartSubtransaction() + { + StartTransaction(); + if ( + _subTransaction == null + || !_subTransaction.IsValidObject + || _subTransaction.GetStatus() != TransactionStatus.Started + ) + { + _subTransaction = new SubTransaction(Document); + _subTransaction.Start(); + } + } + + public TransactionStatus CommitSubtransaction() + { + if (_subTransaction != null && _subTransaction.IsValidObject) + { + var status = _subTransaction.Commit(); + if (status != TransactionStatus.Committed) + { + // POC: handle failed commit + //HandleFailedCommit(status); + } + return status; + } + return TransactionStatus.Uninitialized; + } + + public void RollbackSubTransaction() + { + if ( + _subTransaction != null + && _subTransaction.IsValidObject + && _subTransaction.GetStatus() == TransactionStatus.Started + ) + { + _subTransaction.RollBack(); + } + } + + public void Dispose() + { + _subTransaction?.Dispose(); + _transaction?.Dispose(); + } +} diff --git a/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Operations/Send/RevitRootObjectBuilder.cs b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Operations/Send/RevitRootObjectBuilder.cs new file mode 100644 index 0000000000..df84c9218e --- /dev/null +++ b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Operations/Send/RevitRootObjectBuilder.cs @@ -0,0 +1,156 @@ +using System.Diagnostics; +using Speckle.Converters.Common; +using Speckle.Core.Models; +using Autodesk.Revit.DB; +using Speckle.Connectors.DUI.Exceptions; +using Speckle.Converters.RevitShared.Helpers; +using Speckle.Connectors.Utils.Builders; +using Speckle.Connectors.Utils.Caching; +using Speckle.Connectors.Utils.Conversion; +using Speckle.Connectors.Utils.Operations; +using Speckle.Core.Logging; + +namespace Speckle.Connectors.Revit.Operations.Send; + +public class RevitRootObjectBuilder : IRootObjectBuilder +{ + // POC: SendSelection and RevitConversionContextStack should be interfaces, former needs interfaces + private readonly IRootToSpeckleConverter _converter; + private readonly IRevitConversionContextStack _contextStack; + private readonly Dictionary _collectionCache; + private readonly Collection _rootObject; + private readonly ISendConversionCache _sendConversionCache; + + public RevitRootObjectBuilder( + IRootToSpeckleConverter converter, + IRevitConversionContextStack contextStack, + ISendConversionCache sendConversionCache + ) + { + _converter = converter; + _contextStack = contextStack; + _sendConversionCache = sendConversionCache; + // Note, this class is instantiated per unit of work (aka per send operation), so we can safely initialize what we need in here. + _collectionCache = new Dictionary(); + _rootObject = new Collection() + { + name = _contextStack.Current.Document.PathName.Split('\\').Last().Split('.').First() + }; + } + + public RootObjectBuilderResult Build( + IReadOnlyList objects, + SendInfo sendInfo, + Action? onOperationProgressed = null, + CancellationToken ct = default + ) + { + var doc = _contextStack.Current.Document; + + if (doc.IsFamilyDocument) + { + throw new SpeckleException("Family Environment documents are not supported."); + } + + var revitElements = new List(); + + foreach (var id in objects) + { + var el = _contextStack.Current.Document.GetElement(id); + if (el != null) + { + revitElements.Add(el); + } + } + + if (revitElements.Count == 0) + { + throw new SpeckleSendFilterException("No objects were found. Please update your send filter!"); + } + + var countProgress = 0; // because for(int i = 0; ...) loops are so last year + var cacheHitCount = 0; + List results = new(revitElements.Count); + var path = new string[2]; + foreach (Element revitElement in revitElements) + { + ct.ThrowIfCancellationRequested(); + + var cat = revitElement.Category.Name; + path[0] = doc.GetElement(revitElement.LevelId) is not Level level ? "No level" : level.Name; + path[1] = cat; + var collection = GetAndCreateObjectHostCollection(path); + + var applicationId = revitElement.Id.ToString(); + try + { + Base converted; + if (_sendConversionCache.TryGetValue(sendInfo.ProjectId, applicationId, out ObjectReference value)) + { + converted = value; + cacheHitCount++; + } + else + { + converted = _converter.Convert(revitElement); + converted.applicationId = applicationId; + } + + collection.elements.Add(converted); + results.Add(new(Status.SUCCESS, applicationId, revitElement.GetType().Name, converted)); + } + catch (Exception ex) when (!ex.IsFatal()) + { + results.Add(new(Status.ERROR, applicationId, revitElement.GetType().Name, null, ex)); + // POC: add logging + } + + onOperationProgressed?.Invoke("Converting", (double)++countProgress / revitElements.Count); + } + + // 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(_rootObject, results); + } + + /// + /// Creates and nests collections based on the provided path within the root collection provided. This will not return a new collection each time is called, but an existing one if one is found. + /// For example, you can use this to use (or re-use) a new collection for a path of (level, category) as it's currently implemented. + /// + /// + /// + private Collection GetAndCreateObjectHostCollection(string[] path) + { + string fullPathName = string.Concat(path); + if (_collectionCache.TryGetValue(fullPathName, out Collection value)) + { + return value; + } + + string flatPathName = ""; + Collection previousCollection = _rootObject; + + foreach (var pathItem in path) + { + flatPathName += pathItem; + Collection childCollection; + if (_collectionCache.TryGetValue(flatPathName, out Collection? collection)) + { + childCollection = collection; + } + else + { + childCollection = new Collection(pathItem, "layer"); + previousCollection.elements.Add(childCollection); + _collectionCache[flatPathName] = childCollection; + } + + previousCollection = childCollection; + } + + return previousCollection; + } +} diff --git a/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Plugin/CefSharpPanel.xaml b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Plugin/CefSharpPanel.xaml new file mode 100644 index 0000000000..34292d3ff8 --- /dev/null +++ b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Plugin/CefSharpPanel.xaml @@ -0,0 +1,18 @@ + + + + + + diff --git a/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Plugin/CefSharpPanel.xaml.cs b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Plugin/CefSharpPanel.xaml.cs new file mode 100644 index 0000000000..a82285335c --- /dev/null +++ b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Plugin/CefSharpPanel.xaml.cs @@ -0,0 +1,29 @@ +using System.Windows.Controls; +using Autodesk.Revit.UI; +using CefSharp; +using System.Windows.Threading; + +namespace Speckle.Connectors.Revit; + +public partial class CefSharpPanel : Page, Autodesk.Revit.UI.IDockablePaneProvider +{ + public CefSharpPanel() + { + InitializeComponent(); + } + + public void ExecuteScriptAsync(string script) => + Browser.Dispatcher.Invoke(() => Browser.ExecuteScriptAsync(script), DispatcherPriority.Background); + + public void ShowDevTools() => Browser.ShowDevTools(); + + public void SetupDockablePane(Autodesk.Revit.UI.DockablePaneProviderData data) + { + data.FrameworkElement = this; + data.InitialState = new Autodesk.Revit.UI.DockablePaneState + { + DockPosition = DockPosition.Tabbed, + TabBehind = Autodesk.Revit.UI.DockablePanes.BuiltInDockablePanes.ProjectBrowser + }; + } +} diff --git a/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Plugin/IRevitIdleManager.cs b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Plugin/IRevitIdleManager.cs new file mode 100644 index 0000000000..3a8360cc90 --- /dev/null +++ b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Plugin/IRevitIdleManager.cs @@ -0,0 +1,14 @@ +namespace Speckle.Connectors.Revit.Plugin; + +// POC: needs interface +// is probably misnamed, perhaps OnIdleCallbackManager +internal interface IRevitIdleManager +{ + /// + /// Subscribe deferred action to Idling event to run it whenever Revit becomes idle. + /// + /// Action to call whenever Revit becomes Idle. + /// some events in host app are trigerred many times, we might get 10x per object + /// Making this more like a deferred action, so we don't update the UI many times + void SubscribeToIdle(Action action); +} diff --git a/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Plugin/IRevitPlugin.cs b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Plugin/IRevitPlugin.cs new file mode 100644 index 0000000000..fb88ffc44a --- /dev/null +++ b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Plugin/IRevitPlugin.cs @@ -0,0 +1,7 @@ +namespace Speckle.Connectors.Revit.Plugin; + +internal interface IRevitPlugin +{ + void Initialise(); + void Shutdown(); +} diff --git a/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Plugin/RevitCommand.cs b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Plugin/RevitCommand.cs new file mode 100644 index 0000000000..7697246693 --- /dev/null +++ b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Plugin/RevitCommand.cs @@ -0,0 +1,17 @@ +using Autodesk.Revit.Attributes; +using Autodesk.Revit.DB; +using Autodesk.Revit.UI; + +namespace Speckle.Connectors.Revit.Plugin; + +[Transaction(TransactionMode.Manual)] +internal sealed class SpeckleRevitCommand : IExternalCommand +{ + public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements) + { + DockablePane panel = commandData.Application.GetDockablePane(RevitExternalApplication.DoackablePanelId); + panel.Show(); + + return Result.Succeeded; + } +} diff --git a/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Plugin/RevitExternalApplication.cs b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Plugin/RevitExternalApplication.cs new file mode 100644 index 0000000000..6de43da0c7 --- /dev/null +++ b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Plugin/RevitExternalApplication.cs @@ -0,0 +1,85 @@ +using Autodesk.Revit.UI; +using Speckle.Autofac.DependencyInjection; +using System.IO; +using System.Reflection; +using Speckle.Autofac; +using Speckle.Connectors.Utils; + +namespace Speckle.Connectors.Revit.Plugin; + +internal sealed class RevitExternalApplication : IExternalApplication +{ + private IRevitPlugin? _revitPlugin; + + private SpeckleContainer? _container; + + // POC: this is getting hard coded - need a way of injecting it + // I am beginning to think the shared project is not the way + // and an assembly which is invoked with some specialisation is the right way to go + // maybe subclassing, or some hook to inject som configuration + private readonly RevitSettings _revitSettings; + + // POC: move to somewhere central? + public static readonly DockablePaneId DoackablePanelId = new(new Guid("{f7b5da7c-366c-4b13-8455-b56f433f461e}")); + + public RevitExternalApplication() + { + // POC: load from JSON file? + _revitSettings = new RevitSettings( + "Speckle New UI", + "Speckle", + "Speckle New UI", + "2023", + "Speckle New UI", + "Revit", + new[] { Path.GetDirectoryName(typeof(RevitExternalApplication).Assembly.Location) }, + "Revit Connector", + "2023" //POC: app version? + ); + } + + public Result OnStartup(UIControlledApplication application) + { + try + { + // POC: not sure what this is doing... could be messing up our Aliasing???? + AppDomain.CurrentDomain.AssemblyResolve += AssemblyResolver.OnAssemblyResolve; + var containerBuilder = SpeckleContainerBuilder.CreateInstance(); + // init DI + _container = containerBuilder + .LoadAutofacModules(Assembly.GetExecutingAssembly(), _revitSettings.ModuleFolders.NotNull()) + .AddSingleton(_revitSettings) // apply revit settings into DI + .AddSingleton(application) // inject UIControlledApplication application + .Build(); + + // resolve root object + _revitPlugin = _container.Resolve(); + _revitPlugin.Initialise(); + } + catch (Exception e) when (!e.IsFatal()) + { + // POC: feedback? + return Result.Failed; + } + + return Result.Succeeded; + } + + public Result OnShutdown(UIControlledApplication application) + { + try + { + // POC: could this be more a generic Connector Init() Shutdown() + // possibly with injected pieces or with some abstract methods? + // need to look for commonality + _revitPlugin?.Shutdown(); + } + catch (Exception e) when (!e.IsFatal()) + { + // POC: feedback? + return Result.Failed; + } + + return Result.Succeeded; + } +} diff --git a/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Plugin/RevitIdleManager.cs b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Plugin/RevitIdleManager.cs new file mode 100644 index 0000000000..1b46efa267 --- /dev/null +++ b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Plugin/RevitIdleManager.cs @@ -0,0 +1,66 @@ +using System.Collections.Concurrent; +using Autodesk.Revit.UI; +using Autodesk.Revit.UI.Events; +using Speckle.Connectors.DUI.Bridge; +using Speckle.Converters.RevitShared.Helpers; + +namespace Speckle.Connectors.Revit.Plugin; + +// POC: needs interface +// is probably misnamed, perhaps OnIdleCallbackManager +internal sealed class RevitIdleManager : IRevitIdleManager +{ + private readonly ITopLevelExceptionHandler _topLevelExceptionHandler; + private readonly UIApplication _uiApplication; + + private readonly ConcurrentDictionary _calls = new(); + + // POC: still not thread safe + private volatile bool _hasSubscribed; + + public RevitIdleManager(RevitContext revitContext, ITopLevelExceptionHandler topLevelExceptionHandler) + { + _topLevelExceptionHandler = topLevelExceptionHandler; + _uiApplication = revitContext.UIApplication!; + } + + /// + /// Subscribe deferred action to Idling event to run it whenever Revit becomes idle. + /// + /// Action to call whenever Revit becomes Idle. + /// some events in host app are trigerred many times, we might get 10x per object + /// Making this more like a deferred action, so we don't update the UI many times + public void SubscribeToIdle(Action action) + { + // POC: key for method is brittle | thread safe is not this is + // I want to be called back ONCE when the host app has become idle once more + // would this work "action.Method.Name" with anonymous function, including the SAME function + // does this work across class instances? Should it? What about functions of the same name? Fully qualified name might be better + _calls[action.Method.Name] = action; + + if (_hasSubscribed) + { + return; + } + + _hasSubscribed = true; + _uiApplication.Idling += RevitAppOnIdle; + } + + private void RevitAppOnIdle(object sender, IdlingEventArgs e) + { + _topLevelExceptionHandler.CatchUnhandled(() => + { + foreach (KeyValuePair kvp in _calls) + { + kvp.Value.Invoke(); + } + + _calls.Clear(); + _uiApplication.Idling -= RevitAppOnIdle; + + // setting last will delay ntering re-subscritption + _hasSubscribed = false; + }); + } +} diff --git a/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Plugin/RevitPlugin.cs b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Plugin/RevitPlugin.cs new file mode 100644 index 0000000000..13d3500b2f --- /dev/null +++ b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Plugin/RevitPlugin.cs @@ -0,0 +1,198 @@ +using Autodesk.Revit.UI; +using Autodesk.Revit.ApplicationServices; +using Revit.Async; +using CefSharp; +using Speckle.Connectors.DUI.Bridge; +using Speckle.Connectors.DUI.Bindings; +using System.Diagnostics; +using Speckle.Converters.RevitShared.Helpers; +using Speckle.Core.Logging; +using System.Reflection; +using System.Windows.Media.Imaging; +using System.Windows.Media; +using System.IO; + +namespace Speckle.Connectors.Revit.Plugin; + +internal sealed class RevitPlugin : IRevitPlugin +{ + private readonly UIControlledApplication _uIControlledApplication; + private readonly RevitSettings _revitSettings; + private readonly IEnumerable> _bindings; // should be lazy to ensure the bindings are not created too early + private readonly BindingOptions _bindingOptions; + private readonly RevitContext _revitContext; + private readonly CefSharpPanel _cefSharpPanel; + + public RevitPlugin( + UIControlledApplication uIControlledApplication, + RevitSettings revitSettings, + IEnumerable> bindings, + BindingOptions bindingOptions, + RevitContext revitContext, + CefSharpPanel cefSharpPanel + ) + { + _uIControlledApplication = uIControlledApplication; + _revitSettings = revitSettings; + _bindings = bindings; + _bindingOptions = bindingOptions; + _revitContext = revitContext; + _cefSharpPanel = cefSharpPanel; + } + + 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(_revitSettings.RevitTabName); + } + 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(_revitSettings.RevitTabName, _revitSettings.RevitTabTitle); + var dui3Button = (PushButton) + specklePanel.AddItem( + new PushButtonData( + _revitSettings.RevitButtonName, + _revitSettings.RevitButtonText, + typeof(RevitExternalApplication).Assembly.Location, + typeof(SpeckleRevitCommand).FullName + ) + ); + + string path = typeof(RevitPlugin).Assembly.Location; + dui3Button.Image = LoadPngImgSource( + $"Speckle.Connectors.Revit{_revitSettings.RevitVersionName}.Assets.logo16.png", + path + ); + dui3Button.LargeImage = LoadPngImgSource( + $"Speckle.Connectors.Revit{_revitSettings.RevitVersionName}.Assets.logo32.png", + path + ); + dui3Button.ToolTipImage = LoadPngImgSource( + $"Speckle.Connectors.Revit{_revitSettings.RevitVersionName}.Assets.logo32.png", + path + ); + dui3Button.ToolTip = "Speckle Connector for Revit New UI"; + //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); + + PostApplicationInit(); // for double-click file open + } + + /// + /// Actions to run after UiApplication initialized. This is needed for double-click file open issue. + /// + private void PostApplicationInit() + { + // binding the bindings to each bridge + foreach (IBinding binding in _bindings.Select(x => x.Value)) + { + Debug.WriteLine(binding.Name); + binding.Parent.AssociateWithBinding( + binding, + _cefSharpPanel.ExecuteScriptAsync, + _cefSharpPanel, + _cefSharpPanel.ShowDevTools + ); + } + + _cefSharpPanel.Browser.IsBrowserInitializedChanged += (sender, e) => + { + if (e.NewValue is false) + { + return; + } + + foreach (IBinding binding in _bindings.Select(x => x.Value)) + { + IBridge bridge = binding.Parent; + + _cefSharpPanel.Browser.JavascriptObjectRepository.Register( + bridge.FrontendBoundName, + bridge, + true, + _bindingOptions + ); + } + + // POC: Below line seems unneccesary but not removing just in case we did it like this? Maybe check it later + // with some other revit connectors again since CefSharp version is different + // _cefSharpPanel.Browser.Load("https://boisterous-douhua-e3cefb.netlify.app/"); + + // POC: not sure where this comes from +#if REVIT2020 + // NOTE: Cef65 does not work with DUI3 in yarn dev mode. To test things you need to do `yarn build` and serve the build + // folder at port 3000 (or change it to something else if you want to). Guru meditation: Je sais, pas ideal. Mais q'est que nous pouvons faire? Rien. C'est l'autodesk vie. + // NOTE: To run the ui from a build, follow these steps: + // - run `yarn build` in the DUI3 folder + // - run ` PORT=3003 node .output/server/index.mjs` after the build + + CefSharpPanel.Browser.Load("http://localhost:3003"); + CefSharpPanel.Browser.ShowDevTools(); +#endif +#if REVIT2023 + CefSharpPanel.Browser.Load("http://localhost:8082"); +#endif + }; + } + + private void RegisterDockablePane() + { + CefSharpSettings.ConcurrentTaskExecution = true; + + // Registering dockable pane should happen before UiApplication is initialized with RevitTask. + // Otherwise pane cannot be registered for double-click file open. + _uIControlledApplication.RegisterDockablePane( + RevitExternalApplication.DoackablePanelId, + _revitSettings.RevitPanelName, + _cefSharpPanel + ); + } + + 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; + } +} diff --git a/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Plugin/RevitSettings.cs b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Plugin/RevitSettings.cs new file mode 100644 index 0000000000..a69ef03230 --- /dev/null +++ b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Plugin/RevitSettings.cs @@ -0,0 +1,13 @@ +namespace Speckle.Connectors.Revit.Plugin; + +internal record RevitSettings( // POC: need to derive some interface for things that require IHostSettings{ + string RevitPanelName, + string RevitTabName, + string RevitTabTitle, + string RevitVersionName, + string RevitButtonName, + string RevitButtonText, + string[] ModuleFolders, + string HostSlug, + string HostAppVersion +); diff --git a/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Plugin/Speckle.Connectors.Revit2023.addin b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Plugin/Speckle.Connectors.Revit2023.addin new file mode 100644 index 0000000000..5e07999bbd --- /dev/null +++ b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Plugin/Speckle.Connectors.Revit2023.addin @@ -0,0 +1,12 @@ + + + + Speckle Revit Connector New UI + Speckle Revit Connector New UI + Speckle.Connectors.Revit2023\Speckle.Connectors.Revit2023.dll + Speckle.Connectors.Revit.Plugin.RevitExternalApplication + 27ccff2c-011c-4374-bb79-b93990d0c86a + speckle + Speckle: Empowering your design and construction data. For any problems, visit our community forum https://speckle.community + + diff --git a/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Speckle.Connectors.RevitShared.projitems b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Speckle.Connectors.RevitShared.projitems new file mode 100644 index 0000000000..1abd960016 --- /dev/null +++ b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Speckle.Connectors.RevitShared.projitems @@ -0,0 +1,53 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + 32aea787-c986-4b76-a57f-0da5453aa1f5 + + + Speckle.Connectors.Revit + + + + + + Always + + + + + + + + + + + + + + + + + + + + + + CefSharpPanel.xaml + + + + + + + + + + + + Designer + MSBuild:Compile + + + \ No newline at end of file diff --git a/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Speckle.Connectors.RevitShared.shproj b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Speckle.Connectors.RevitShared.shproj new file mode 100644 index 0000000000..e6073eb4d9 --- /dev/null +++ b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Speckle.Connectors.RevitShared.shproj @@ -0,0 +1,13 @@ + + + + {DC570FFF-6FE5-47BD-8BC1-B471A6067786} + 14.0 + + + + + + + + \ No newline at end of file diff --git a/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Bindings/RhinoBasicConnectorBinding.cs b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Bindings/RhinoBasicConnectorBinding.cs new file mode 100644 index 0000000000..1ceb31b69a --- /dev/null +++ b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Bindings/RhinoBasicConnectorBinding.cs @@ -0,0 +1,149 @@ +using Rhino; +using Rhino.DocObjects; +using Rhino.Geometry; +using Sentry.Reflection; +using Speckle.Connectors.DUI.Bindings; +using Speckle.Connectors.DUI.Bridge; +using Speckle.Connectors.DUI.Models; +using Speckle.Connectors.DUI.Models.Card; +using Speckle.Connectors.Rhino7.Extensions; +using Speckle.Connectors.Rhino7.HostApp; +using Speckle.Connectors.Utils; + +namespace Speckle.Connectors.Rhino7.Bindings; + +public class RhinoBasicConnectorBinding : IBasicConnectorBinding +{ + public string Name => "baseBinding"; + public IBridge Parent { get; } + public BasicConnectorBindingCommands Commands { get; } + + private readonly DocumentModelStore _store; + private readonly RhinoSettings _settings; + + public RhinoBasicConnectorBinding(DocumentModelStore store, RhinoSettings settings, IBridge parent) + { + _store = store; + _settings = settings; + Parent = parent; + Commands = new BasicConnectorBindingCommands(parent); + + _store.DocumentChanged += (_, _) => + { + Commands.NotifyDocumentChanged(); + }; + } + + public string GetConnectorVersion() => + typeof(RhinoBasicConnectorBinding).Assembly.GetNameAndVersion().Version ?? "No version"; + + public string GetSourceApplicationName() => _settings.HostAppInfo.Slug; + + public string GetSourceApplicationVersion() => "7"; + + public DocumentInfo GetDocumentInfo() => + new(RhinoDoc.ActiveDoc.Path, RhinoDoc.ActiveDoc.Name, RhinoDoc.ActiveDoc.RuntimeSerialNumber.ToString()); + + public DocumentModelStore GetDocumentState() => _store; + + public void AddModel(ModelCard model) => _store.Models.Add(model); + + public void UpdateModel(ModelCard model) => _store.UpdateModel(model); + + public void RemoveModel(ModelCard model) => _store.RemoveModel(model); + + public void HighlightObjects(List objectIds) + { + var objects = GetObjectsFromIds(objectIds); + + if (objects.rhinoObjects.Count == 0 && objects.groups.Count == 0) + { + throw new InvalidOperationException( + "Highlighting RhinoObject is not successful.", + new ArgumentException($"{objectIds} is not a valid id", nameof(objectIds)) + ); + } + + HighlightObjectsOnView(objects.rhinoObjects, objects.groups); + } + + public void HighlightModel(string modelCardId) + { + var objectIds = new List(); + var myModel = _store.GetModelById(modelCardId); + + if (myModel is SenderModelCard sender) + { + objectIds = sender.SendFilter.NotNull().GetObjectIds(); + } + + if (myModel is ReceiverModelCard receiver && receiver.BakedObjectIds != null) + { + objectIds = receiver.BakedObjectIds; + } + + if (objectIds.Count == 0) + { + Commands.SetModelError(modelCardId, new OperationCanceledException("No objects found to highlight.")); + return; + } + + var objects = GetObjectsFromIds(objectIds); + + RhinoDoc.ActiveDoc.Objects.UnselectAll(); + + if (objects.rhinoObjects.Count == 0 && objects.groups.Count == 0) + { + Commands.SetModelError(modelCardId, new OperationCanceledException("No objects found to highlight.")); + return; + } + + HighlightObjectsOnView(objects.rhinoObjects, objects.groups); + } + + private (List rhinoObjects, List groups) GetObjectsFromIds(List objectIds) + { + List rhinoObjects = objectIds + .Select((id) => RhinoDoc.ActiveDoc.Objects.FindId(new Guid(id))) + .Where(o => o != null) + .ToList(); + + // POC: On receive we group objects if return multiple objects + List groups = objectIds + .Select((id) => RhinoDoc.ActiveDoc.Groups.FindId(new Guid(id))) + .Where(o => o != null) + .ToList(); + + return (rhinoObjects, groups); + } + + private void HighlightObjectsOnView(IReadOnlyList rhinoObjects, IReadOnlyList groups) + { + RhinoDoc.ActiveDoc.Objects.UnselectAll(); + List rhinoObjectsToSelect = new(rhinoObjects); + + foreach (Group group in groups) + { + int groupIndex = RhinoDoc.ActiveDoc.Groups.Find(group.Name); + if (groupIndex < 0) + { + continue; + } + var allRhinoObjects = RhinoDoc.ActiveDoc.Objects.GetObjectList(ObjectType.AnyObject); + var subRhinoObjects = allRhinoObjects.Where(o => o.GetGroupList().Contains(groupIndex)); + rhinoObjectsToSelect.AddRange(subRhinoObjects); + } + RhinoDoc.ActiveDoc.Objects.Select(rhinoObjectsToSelect.Select(o => o.Id)); + + // Calculate the bounding box of the selected objects + BoundingBox boundingBox = BoundingBoxExtensions.UnionRhinoObjects(rhinoObjectsToSelect); + + // Zoom to the calculated bounding box + if (boundingBox.IsValid) + { + RhinoDoc.ActiveDoc.Views.ActiveView.ActiveViewport.ZoomBoundingBox(boundingBox); + } + + RhinoDoc.ActiveDoc.Views.Redraw(); + } +} diff --git a/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Bindings/RhinoReceiveBinding.cs b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Bindings/RhinoReceiveBinding.cs new file mode 100644 index 0000000000..6c78f254df --- /dev/null +++ b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Bindings/RhinoReceiveBinding.cs @@ -0,0 +1,84 @@ +using Speckle.Autofac.DependencyInjection; +using Speckle.Connectors.DUI.Bindings; +using Speckle.Connectors.DUI.Bridge; +using Speckle.Connectors.DUI.Models; +using Speckle.Connectors.DUI.Models.Card; +using Speckle.Connectors.Utils; +using Speckle.Connectors.Utils.Builders; +using Speckle.Connectors.Utils.Cancellation; +using Speckle.Connectors.Utils.Operations; + +namespace Speckle.Connectors.Rhino7.Bindings; + +public class RhinoReceiveBinding : IReceiveBinding +{ + public string Name => "receiveBinding"; + public IBridge Parent { get; } + + private readonly CancellationManager _cancellationManager; + private readonly DocumentModelStore _store; + private readonly IUnitOfWorkFactory _unitOfWorkFactory; + public ReceiveBindingUICommands Commands { get; } + + public RhinoReceiveBinding( + DocumentModelStore store, + CancellationManager cancellationManager, + IBridge parent, + IUnitOfWorkFactory unitOfWorkFactory + ) + { + Parent = parent; + _store = store; + _unitOfWorkFactory = unitOfWorkFactory; + _cancellationManager = cancellationManager; + Commands = new ReceiveBindingUICommands(parent); + } + + public void CancelReceive(string modelCardId) => _cancellationManager.CancelOperation(modelCardId); + + public async Task Receive(string modelCardId) + { + using var unitOfWork = _unitOfWorkFactory.Resolve(); + 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."); + } + + // Init cancellation token source -> Manager also cancel it if exist before + CancellationTokenSource cts = _cancellationManager.InitCancellationTokenSource(modelCardId); + + // Receive host objects + HostObjectBuilderResult conversionResults = await unitOfWork.Service + .Execute( + modelCard.AccountId.NotNull(), // POC: I hear -you are saying why we're passing them separately. Not sure pass the DUI3-> Connectors.DUI project dependency to the SDK-> Connector.Utils + modelCard.ProjectId.NotNull(), + modelCard.ProjectName.NotNull(), + modelCard.ModelName.NotNull(), + modelCard.SelectedVersionId.NotNull(), + cts.Token, + (status, progress) => + Commands.SetModelProgress(modelCardId, new ModelCardProgress(modelCardId, status, progress), cts) + ) + .ConfigureAwait(false); + + modelCard.BakedObjectIds = conversionResults.BakedObjectIds.ToList(); + Commands.SetModelReceiveResult( + modelCardId, + conversionResults.BakedObjectIds, + conversionResults.ConversionResults + ); + } + // Catch here specific exceptions if they related to model card. + catch (OperationCanceledException) + { + // SWALLOW -> UI handles it immediately, so we do not need to handle anything + return; + } + } + + public void CancelSend(string modelCardId) => _cancellationManager.CancelOperation(modelCardId); +} diff --git a/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Bindings/RhinoSelectionBinding.cs b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Bindings/RhinoSelectionBinding.cs new file mode 100644 index 0000000000..dcb7c57fcf --- /dev/null +++ b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Bindings/RhinoSelectionBinding.cs @@ -0,0 +1,55 @@ +using Rhino; +using Rhino.DocObjects; +using Speckle.Connectors.DUI.Bindings; +using Speckle.Connectors.DUI.Bridge; +using Speckle.Connectors.Rhino7.HostApp; + +namespace Speckle.Connectors.Rhino7.Bindings; + +public class RhinoSelectionBinding : ISelectionBinding +{ + private readonly IRhinoIdleManager _idleManager; + private readonly ITopLevelExceptionHandler _topLevelExceptionHandler; + private const string SELECTION_EVENT = "setSelection"; + + public string Name => "selectionBinding"; + public IBridge Parent { get; } + + public RhinoSelectionBinding( + IRhinoIdleManager idleManager, + IBridge parent, + ITopLevelExceptionHandler topLevelExceptionHandler + ) + { + _idleManager = idleManager; + _topLevelExceptionHandler = topLevelExceptionHandler; + Parent = parent; + + RhinoDoc.SelectObjects += OnSelectionChange; + RhinoDoc.DeselectObjects += OnSelectionChange; + RhinoDoc.DeselectAllObjects += OnSelectionChange; + } + + void OnSelectionChange(object o, EventArgs eventArgs) + { + _idleManager.SubscribeToIdle(() => _topLevelExceptionHandler.CatchUnhandled(UpdateSelection)); + } + + private void UpdateSelection() + { + SelectionInfo selInfo = GetSelection(); + Parent.Send(SELECTION_EVENT, selInfo); + } + + public SelectionInfo GetSelection() + { + List objects = RhinoDoc.ActiveDoc.Objects.GetSelectedObjects(false, false).ToList(); + List objectIds = objects.Select(o => o.Id.ToString()).ToList(); + int layerCount = objects.Select(o => o.Attributes.LayerIndex).Distinct().Count(); + List objectTypes = objects.Select(o => o.ObjectType.ToString()).Distinct().ToList(); + return new SelectionInfo( + objectIds, + $"{objectIds.Count} objects ({string.Join(", ", objectTypes)}) from {layerCount} layer{(layerCount != 1 ? "s" : "")}" + ); + } +} diff --git a/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Bindings/RhinoSendBinding.cs b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Bindings/RhinoSendBinding.cs new file mode 100644 index 0000000000..9005692407 --- /dev/null +++ b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Bindings/RhinoSendBinding.cs @@ -0,0 +1,223 @@ +using Rhino; +using Rhino.Commands; +using Speckle.Connectors.DUI.Bindings; +using Speckle.Connectors.DUI.Bridge; +using Speckle.Connectors.DUI.Models; +using Speckle.Connectors.DUI.Models.Card; +using Speckle.Connectors.Rhino7.HostApp; +using Speckle.Connectors.Utils.Cancellation; +using Speckle.Autofac.DependencyInjection; +using Rhino.DocObjects; +using Speckle.Connectors.DUI.Exceptions; +using Speckle.Connectors.DUI.Models.Card.SendFilter; +using Speckle.Connectors.Utils.Operations; +using Speckle.Connectors.DUI.Settings; +using Speckle.Connectors.Utils; +using Speckle.Connectors.Utils.Caching; + +namespace Speckle.Connectors.Rhino7.Bindings; + +public sealed class RhinoSendBinding : ISendBinding +{ + public string Name => "sendBinding"; + public SendBindingUICommands Commands { get; } + public IBridge Parent { get; } + + private readonly DocumentModelStore _store; + private readonly IRhinoIdleManager _idleManager; + private readonly IUnitOfWorkFactory _unitOfWorkFactory; + private readonly List _sendFilters; + private readonly CancellationManager _cancellationManager; + private readonly RhinoSettings _rhinoSettings; + + /// + /// Used internally to aggregate the changed objects' id. + /// + private HashSet ChangedObjectIds { get; set; } = new(); + + private readonly ISendConversionCache _sendConversionCache; + private readonly ITopLevelExceptionHandler _topLevelExceptionHandler; + + public RhinoSendBinding( + DocumentModelStore store, + IRhinoIdleManager idleManager, + IBridge parent, + IEnumerable sendFilters, + IUnitOfWorkFactory unitOfWorkFactory, + RhinoSettings rhinoSettings, + CancellationManager cancellationManager, + ISendConversionCache sendConversionCache, + ITopLevelExceptionHandler topLevelExceptionHandler + ) + { + _store = store; + _idleManager = idleManager; + _unitOfWorkFactory = unitOfWorkFactory; + _sendFilters = sendFilters.ToList(); + _rhinoSettings = rhinoSettings; + _cancellationManager = cancellationManager; + _sendConversionCache = sendConversionCache; + _topLevelExceptionHandler = topLevelExceptionHandler; + Parent = parent; + Commands = new SendBindingUICommands(parent); // POC: Commands are tightly coupled with their bindings, at least for now, saves us injecting a factory. + SubscribeToRhinoEvents(); + } + + private void SubscribeToRhinoEvents() + { + RhinoDoc.LayerTableEvent += (_, _) => + { + Commands.RefreshSendFilters(); + }; + + Command.BeginCommand += (_, e) => + { + if (e.CommandEnglishName == "BlockEdit") + { + var selectedObject = RhinoDoc.ActiveDoc.Objects.GetSelectedObjects(false, false).First(); + ChangedObjectIds.Add(selectedObject.Id.ToString()); + } + }; + + RhinoDoc.AddRhinoObject += (_, e) => + _topLevelExceptionHandler.CatchUnhandled(() => + { + // NOTE: This does not work if rhino starts and opens a blank doc; + if (!_store.IsDocumentInit) + { + return; + } + + ChangedObjectIds.Add(e.ObjectId.ToString()); + _idleManager.SubscribeToIdle(RunExpirationChecks); + }); + + RhinoDoc.DeleteRhinoObject += (_, e) => + _topLevelExceptionHandler.CatchUnhandled(() => + { + // NOTE: This does not work if rhino starts and opens a blank doc; + if (!_store.IsDocumentInit) + { + return; + } + + ChangedObjectIds.Add(e.ObjectId.ToString()); + _idleManager.SubscribeToIdle(RunExpirationChecks); + }); + + RhinoDoc.ReplaceRhinoObject += (_, e) => + _topLevelExceptionHandler.CatchUnhandled(() => + { + // NOTE: This does not work if rhino starts and opens a blank doc; + if (!_store.IsDocumentInit) + { + return; + } + + ChangedObjectIds.Add(e.NewRhinoObject.Id.ToString()); + ChangedObjectIds.Add(e.OldRhinoObject.Id.ToString()); + _idleManager.SubscribeToIdle(RunExpirationChecks); + }); + } + + public List GetSendFilters() => _sendFilters; + + public List GetSendSettings() + { + return new List + { + new() + { + Id = "includeAttributes", + Title = "Include Attributes", + Value = true, + Type = "boolean" + }, + }; + } + + public async Task Send(string modelCardId) + { + using var unitOfWork = _unitOfWorkFactory.Resolve>(); + try + { + if (_store.GetModelById(modelCardId) is not SenderModelCard modelCard) + { + // Handle as GLOBAL ERROR at BrowserBridge + throw new InvalidOperationException("No publish model card was found."); + } + + // Init cancellation token source -> Manager also cancel it if exist before + CancellationTokenSource cts = _cancellationManager.InitCancellationTokenSource(modelCardId); + + List rhinoObjects = modelCard.SendFilter + .NotNull() + .GetObjectIds() + .Select(id => RhinoDoc.ActiveDoc.Objects.FindId(new Guid(id))) + .Where(obj => obj != null) + .ToList(); + + if (rhinoObjects.Count == 0) + { + // Handle as CARD ERROR in this function + throw new SpeckleSendFilterException("No objects were found to convert. Please update your publish filter!"); + } + + var sendInfo = new SendInfo( + modelCard.AccountId.NotNull(), + modelCard.ProjectId.NotNull(), + modelCard.ModelId.NotNull(), + _rhinoSettings.HostAppInfo.Name + ); + + var sendResult = await unitOfWork.Service + .Execute( + rhinoObjects, + sendInfo, + (status, progress) => + Commands.SetModelProgress(modelCardId, new ModelCardProgress(modelCardId, status, progress), cts), + cts.Token + ) + .ConfigureAwait(false); + + Commands.SetModelSendResult(modelCardId, sendResult.RootObjId, sendResult.ConversionResults); + } + // Catch here specific exceptions if they related to model card. + catch (SpeckleSendFilterException e) + { + Commands.SetModelError(modelCardId, e); + } + catch (OperationCanceledException) + { + // SWALLOW -> UI handles it immediately, so we do not need to handle anything + return; + } + } + + public void CancelSend(string modelCardId) => _cancellationManager.CancelOperation(modelCardId); + + /// + /// Checks if any sender model cards contain any of the changed objects. If so, also updates the changed objects hashset for each model card - this last part is important for on send change detection. + /// + private void RunExpirationChecks() + { + var senders = _store.GetSenders(); + string[] objectIdsList = ChangedObjectIds.ToArray(); + List expiredSenderIds = new(); + + _sendConversionCache.EvictObjects(objectIdsList); + + foreach (SenderModelCard modelCard in senders) + { + var intersection = modelCard.SendFilter.NotNull().GetObjectIds().Intersect(objectIdsList).ToList(); + var isExpired = intersection.Count != 0; + if (isExpired) + { + expiredSenderIds.Add(modelCard.ModelCardId.NotNull()); + } + } + + Commands.SetModelsExpired(expiredSenderIds); + ChangedObjectIds = new HashSet(); + } +} diff --git a/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/DependencyInjection/RhinoConnectorModule.cs b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/DependencyInjection/RhinoConnectorModule.cs new file mode 100644 index 0000000000..a21fee9903 --- /dev/null +++ b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/DependencyInjection/RhinoConnectorModule.cs @@ -0,0 +1,77 @@ +using Rhino.Commands; +using Rhino.DocObjects; +using Rhino.PlugIns; +using Speckle.Autofac; +using Speckle.Autofac.DependencyInjection; +using Speckle.Connectors.DUI; +using Speckle.Connectors.DUI.Bindings; +using Speckle.Connectors.DUI.Bridge; +using Speckle.Connectors.DUI.Models; +using Speckle.Connectors.Rhino7.Bindings; +using Speckle.Connectors.Rhino7.Filters; +using Speckle.Connectors.Rhino7.HostApp; +using Speckle.Connectors.Rhino7.Operations.Send; +using Speckle.Connectors.Rhino7.Plugin; +using Speckle.Connectors.Utils.Cancellation; +using Speckle.Connectors.DUI.Models.Card.SendFilter; +using Speckle.Connectors.DUI.WebView; +using Speckle.Connectors.Rhino7.Operations.Receive; +using Speckle.Connectors.Utils; +using Speckle.Connectors.Utils.Builders; +using Speckle.Connectors.Utils.Caching; +using Speckle.Connectors.Utils.Instances; +using Speckle.Connectors.Utils.Operations; +using Speckle.Core.Models.GraphTraversal; + +namespace Speckle.Connectors.Rhino7.DependencyInjection; + +public class RhinoConnectorModule : ISpeckleModule +{ + public void Load(SpeckleContainerBuilder builder) + { + // Register instances initialised by Rhino + builder.AddSingleton(SpeckleConnectorsRhino7Plugin.Instance); + builder.AddSingleton(SpeckleConnectorsRhino7Command.Instance); + + builder.AddAutofac(); + builder.AddConnectorUtils(); + builder.AddDUI(); + builder.AddDUIView(); + + // POC: Overwriting the SyncToMainThread to SyncToCurrentThread for Rhino! + builder.AddSingletonInstance(); + + // Register other connector specific types + builder.AddSingleton(); + builder.AddSingleton(); + builder.AddSingleton(); + + // Register bindings + builder.AddSingleton(); + builder.AddSingleton("connectorName", "Rhino"); // POC: Easier like this for now, should be cleaned up later + builder.AddSingleton(); + builder.AddSingleton(); + builder.AddSingleton(); + builder.AddSingleton(); + builder.AddSingleton(); + builder.AddSingleton(); + + // binding dependencies + builder.AddTransient(); + + // register send filters + builder.AddScoped(); + builder.AddScoped(); + + // register send conversion cache + builder.AddSingleton(); + + // register send operation and dependencies + builder.AddScoped>(); + builder.AddSingleton(DefaultTraversal.CreateTraversalFunc()); + + builder.AddScoped, RhinoRootObjectBuilder>(); + builder.AddScoped>, RhinoInstanceObjectsManager>(); + builder.AddScoped(); + } +} diff --git a/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/DependencyInjection/RhinoPlugin.cs b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/DependencyInjection/RhinoPlugin.cs new file mode 100644 index 0000000000..2262f93f9f --- /dev/null +++ b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/DependencyInjection/RhinoPlugin.cs @@ -0,0 +1,24 @@ +using Rhino; +using Speckle.Connectors.Rhino7.HostApp; +using Speckle.Connectors.Rhino7.Plugin; +using Speckle.InterfaceGenerator; + +namespace Speckle.Connectors.Rhino7.DependencyInjection; + +[GenerateAutoInterface] +public class RhinoPlugin : IRhinoPlugin +{ + private readonly IRhinoIdleManager _idleManager; + + public RhinoPlugin(IRhinoIdleManager idleManager) + { + _idleManager = idleManager; + } + + public void Initialise() + { + _idleManager.SubscribeToIdle(() => RhinoApp.RunScript(SpeckleConnectorsRhino7Command.Instance.EnglishName, false)); + } + + public void Shutdown() { } +} diff --git a/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/EmbeddedResources/plugin-utility.ico b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/EmbeddedResources/plugin-utility.ico new file mode 100644 index 0000000000..022d1f7884 Binary files /dev/null and b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/EmbeddedResources/plugin-utility.ico differ diff --git a/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Extensions/BoundingBox.cs b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Extensions/BoundingBox.cs new file mode 100644 index 0000000000..983a8d2a40 --- /dev/null +++ b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Extensions/BoundingBox.cs @@ -0,0 +1,34 @@ +using Rhino.DocObjects; +using Rhino.Geometry; + +namespace Speckle.Connectors.Rhino7.Extensions; + +public static class BoundingBoxExtensions +{ + /// + /// Calculate bounding box with given rhino objects. + /// + /// RhinoObjects to calculate union of bounding box. + /// + public static BoundingBox UnionRhinoObjects(IEnumerable rhinoObjects) + { + BoundingBox boundingBox = BoundingBox.Unset; + foreach (RhinoObject obj in rhinoObjects) + { + BoundingBox objBoundingBox = obj.Geometry.GetBoundingBox(false); + if (objBoundingBox.IsValid) + { + if (boundingBox.IsValid) + { + boundingBox.Union(objBoundingBox); + } + else + { + boundingBox = objBoundingBox; + } + } + } + + return boundingBox; + } +} diff --git a/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Extensions/RhinoUnitsExtension.cs b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Extensions/RhinoUnitsExtension.cs new file mode 100644 index 0000000000..2d431afadc --- /dev/null +++ b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Extensions/RhinoUnitsExtension.cs @@ -0,0 +1,37 @@ +using Rhino; +using Speckle.Core.Kits; +using Speckle.Core.Logging; + +namespace Speckle.Connectors.Rhino7.Extensions; + +public static class RhinoUnitsExtension +{ + public static string ToSpeckleString(this UnitSystem unitSystem) + { + switch (unitSystem) + { + case UnitSystem.None: + return Units.Meters; + case UnitSystem.Millimeters: + return Units.Millimeters; + case UnitSystem.Centimeters: + return Units.Centimeters; + case UnitSystem.Meters: + return Units.Meters; + case UnitSystem.Kilometers: + return Units.Kilometers; + case UnitSystem.Inches: + return Units.Inches; + case UnitSystem.Feet: + return Units.Feet; + case UnitSystem.Yards: + return Units.Yards; + case UnitSystem.Miles: + return Units.Miles; + case UnitSystem.Unset: + return Units.Meters; + default: + throw new SpeckleException($"The Unit System \"{unitSystem}\" is unsupported."); + } + } +} diff --git a/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Filters/RhinoEverythingFilter.cs b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Filters/RhinoEverythingFilter.cs new file mode 100644 index 0000000000..431edbaa6b --- /dev/null +++ b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Filters/RhinoEverythingFilter.cs @@ -0,0 +1,10 @@ +using Speckle.Connectors.DUI.Models.Card.SendFilter; + +namespace Speckle.Connectors.Rhino7.Filters; + +public class RhinoEverythingFilter : EverythingSendFilter +{ + public override List GetObjectIds() => new(); // TODO + + public override bool CheckExpiry(string[] changedObjectIds) => true; +} diff --git a/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Filters/RhinoSelectionFilter.cs b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Filters/RhinoSelectionFilter.cs new file mode 100644 index 0000000000..21d153b940 --- /dev/null +++ b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Filters/RhinoSelectionFilter.cs @@ -0,0 +1,10 @@ +using Speckle.Connectors.DUI.Models.Card.SendFilter; + +namespace Speckle.Connectors.Rhino7.Filters; + +public class RhinoSelectionFilter : DirectSelectionSendFilter +{ + public override List GetObjectIds() => SelectedObjectIds; + + public override bool CheckExpiry(string[] changedObjectIds) => SelectedObjectIds.Intersect(changedObjectIds).Any(); +} diff --git a/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/HostApp/RhinoContext.cs b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/HostApp/RhinoContext.cs new file mode 100644 index 0000000000..17b532035a --- /dev/null +++ b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/HostApp/RhinoContext.cs @@ -0,0 +1,8 @@ +using Rhino; + +namespace Speckle.Connectors.Rhino7.HostApp; + +public class RhinoContext +{ + public RhinoDoc Document { get; } = RhinoDoc.ActiveDoc; +} diff --git a/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/HostApp/RhinoDocumentStore.cs b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/HostApp/RhinoDocumentStore.cs new file mode 100644 index 0000000000..40ac283bce --- /dev/null +++ b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/HostApp/RhinoDocumentStore.cs @@ -0,0 +1,64 @@ +using Rhino; +using Speckle.Connectors.DUI.Bridge; +using Speckle.Connectors.DUI.Models; +using Speckle.Newtonsoft.Json; + +namespace Speckle.Connectors.Rhino7.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( + JsonSerializerSettings jsonSerializerSettings, + ITopLevelExceptionHandler topLevelExceptionHandler + ) + : base(jsonSerializerSettings, true) + { + _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(); + OnDocumentChanged(); + }); + } + + public override void WriteToFile() + { + 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); + } + + public override void ReadFromFile() + { + string stateString = RhinoDoc.ActiveDoc.Strings.GetValue(SPECKLE_KEY, SPECKLE_KEY); + if (stateString == null) + { + Models = new(); + return; + } + Models = Deserialize(stateString) ?? new(); + } +} diff --git a/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/HostApp/RhinoIdleManager.cs b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/HostApp/RhinoIdleManager.cs new file mode 100644 index 0000000000..5a88ed6c20 --- /dev/null +++ b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/HostApp/RhinoIdleManager.cs @@ -0,0 +1,46 @@ +using System.Collections.Concurrent; +using Rhino; +using Speckle.InterfaceGenerator; + +namespace Speckle.Connectors.Rhino7.HostApp; + +/// +/// Rhino Idle Manager is a helper util to manage deferred actions. +/// +[GenerateAutoInterface] +public class RhinoIdleManager : IRhinoIdleManager +{ + // NOTE: ConcurrentDictionary possibly removing the collection has been modified errors in here + private readonly ConcurrentDictionary _sCalls = new(); + private bool _hasSubscribed; + + /// + /// Subscribe deferred action to RhinoIdle event to run it whenever Rhino become idle. + /// + /// Action to call whenever Rhino become Idle. + public void SubscribeToIdle(Action action) + { + _sCalls[action.Method.Name] = action; + + if (_hasSubscribed) + { + return; + } + + _hasSubscribed = true; + RhinoApp.Idle += RhinoAppOnIdle; + } + + private void RhinoAppOnIdle(object sender, EventArgs e) + { + foreach (var kvp in _sCalls) + { + kvp.Value(); + } + + _sCalls.Clear(); + + _hasSubscribed = false; + RhinoApp.Idle -= RhinoAppOnIdle; + } +} diff --git a/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/HostApp/RhinoInstanceObjectsManager.cs b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/HostApp/RhinoInstanceObjectsManager.cs new file mode 100644 index 0000000000..e79073bddf --- /dev/null +++ b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/HostApp/RhinoInstanceObjectsManager.cs @@ -0,0 +1,254 @@ +using System.DoubleNumerics; +using Rhino; +using Rhino.DocObjects; +using Rhino.Geometry; +using Speckle.Connectors.Rhino7.Extensions; +using Speckle.Connectors.Utils.Conversion; +using Speckle.Connectors.Utils.Instances; +using Speckle.Core.Kits; +using Speckle.Core.Logging; +using Speckle.Core.Models; +using Speckle.Core.Models.Instances; + +namespace Speckle.Connectors.Rhino7.HostApp; + +/// +/// +/// Expects to be a scoped dependency per send or receive operation. +/// +public class RhinoInstanceObjectsManager : IInstanceObjectsManager> +{ + private readonly Dictionary _instanceProxies = new(); + private readonly Dictionary> _instanceProxiesByDefinitionId = new(); + private readonly Dictionary _definitionProxies = new(); + private readonly Dictionary _flatAtomicObjects = new(); + private readonly RhinoLayerManager _layerManager; + + public RhinoInstanceObjectsManager(RhinoLayerManager layerManager) + { + _layerManager = layerManager; + } + + public UnpackResult UnpackSelection(IEnumerable objects) + { + foreach (var obj in objects) + { + if (obj is InstanceObject instanceObject) + { + UnpackInstance(instanceObject); + } + _flatAtomicObjects[obj.Id.ToString()] = obj; + } + return new(_flatAtomicObjects.Values.ToList(), _instanceProxies, _definitionProxies.Values.ToList()); + } + + private void UnpackInstance(InstanceObject instance, int depth = 0) + { + var instanceId = instance.Id.ToString(); + var instanceDefinitionId = instance.InstanceDefinition.Id.ToString(); + var currentDoc = RhinoDoc.ActiveDoc; // POC: too much right now to interface around + + _instanceProxies[instanceId] = new InstanceProxy() + { + applicationId = instanceId, + DefinitionId = instance.InstanceDefinition.Id.ToString(), + Transform = XFormToMatrix(instance.InstanceXform), + MaxDepth = depth, + Units = currentDoc.ModelUnitSystem.ToSpeckleString() + }; + + // For each block instance that has the same definition, we need to keep track of the "maximum depth" at which is found. + // This will enable on receive to create them in the correct order (descending by max depth, interleaved definitions and instances). + // We need to interleave the creation of definitions and instances, as some definitions may depend on instances. + if ( + !_instanceProxiesByDefinitionId.TryGetValue( + instanceDefinitionId, + out List instanceProxiesWithSameDefinition + ) + ) + { + instanceProxiesWithSameDefinition = new List(); + _instanceProxiesByDefinitionId[instanceDefinitionId] = instanceProxiesWithSameDefinition; + } + + // We ensure that all previous instance proxies that have the same definition are at this max depth. I kind of have a feeling this can be done more elegantly, but YOLO + foreach (var instanceProxy in instanceProxiesWithSameDefinition) + { + instanceProxy.MaxDepth = depth; + } + + instanceProxiesWithSameDefinition.Add(_instanceProxies[instanceId]); + + if (_definitionProxies.TryGetValue(instanceDefinitionId, out InstanceDefinitionProxy value)) + { + value.MaxDepth = depth; + return; + } + + var definition = new InstanceDefinitionProxy + { + applicationId = instanceDefinitionId, + Objects = new List(), + MaxDepth = depth, + ["name"] = instance.InstanceDefinition.Name, + ["description"] = instance.InstanceDefinition.Description + }; + + _definitionProxies[instance.InstanceDefinition.Id.ToString()] = definition; + + foreach (var obj in instance.InstanceDefinition.GetObjects()) + { + definition.Objects.Add(obj.Id.ToString()); + if (obj is InstanceObject localInstance) + { + UnpackInstance(localInstance, depth + 1); + } + _flatAtomicObjects[obj.Id.ToString()] = obj; + } + } + + /// + /// Bakes in the host app doc instances. Assumes constituent atomic objects already present in the host app. + /// + /// Instance definitions and instances that need creating. + /// A dict mapping { original application id -> [resulting application ids post conversion] } + /// + public BakeResult BakeInstances( + List<(string[] layerPath, IInstanceComponent obj)> instanceComponents, + Dictionary> applicationIdMap, + string baseLayerName, + Action? onOperationProgressed + ) + { + // var doc = _contextStack.Current.Document; + var doc = RhinoDoc.ActiveDoc; // POC: too much right now to interface around + + var sortedInstanceComponents = instanceComponents + .OrderByDescending(x => x.obj.MaxDepth) // Sort by max depth, so we start baking from the deepest element first + .ThenBy(x => x.obj is InstanceDefinitionProxy ? 0 : 1) // Ensure we bake the deepest definition first, then any instances that depend on it + .ToList(); + var definitionIdAndApplicationIdMap = new Dictionary(); + + var count = 0; + var conversionResults = new List(); + var createdObjectIds = new List(); + var consumedObjectIds = new List(); + foreach (var (path, instanceOrDefinition) in sortedInstanceComponents) + { + onOperationProgressed?.Invoke("Converting blocks", (double)++count / sortedInstanceComponents.Count); + try + { + if (instanceOrDefinition is InstanceDefinitionProxy definitionProxy) + { + var currentApplicationObjectsIds = definitionProxy.Objects + .Select(x => applicationIdMap.TryGetValue(x, out List value) ? value : null) + .Where(x => x is not null) + .SelectMany(id => id) + .ToList(); + + var definitionGeometryList = new List(); + var attributes = new List(); + + foreach (var id in currentApplicationObjectsIds) + { + var docObject = doc.Objects.FindId(new Guid(id)); + definitionGeometryList.Add(docObject.Geometry); + attributes.Add(docObject.Attributes); + } + + // POC: Currently we're relying on the definition name for identification if it's coming from speckle and from which model; could we do something else? + var defName = $"{baseLayerName} ({definitionProxy.applicationId})"; + var defIndex = doc.InstanceDefinitions.Add( + defName, + "No description", // POC: perhaps bring it along from source? We'd need to look at ACAD first + Point3d.Origin, + definitionGeometryList, + attributes + ); + + // POC: check on defIndex -1, means we haven't created anything - this is most likely an recoverable error at this stage + if (defIndex == -1) + { + throw new ConversionException("Failed to create an instance defintion object."); + } + + if (definitionProxy.applicationId != null) + { + definitionIdAndApplicationIdMap[definitionProxy.applicationId] = defIndex; + } + + // 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 + } + + if ( + instanceOrDefinition is InstanceProxy instanceProxy + && definitionIdAndApplicationIdMap.TryGetValue(instanceProxy.DefinitionId, out int index) + ) + { + var transform = MatrixToTransform(instanceProxy.Transform, instanceProxy.Units); + var layerIndex = _layerManager.GetAndCreateLayerFromPath(path, baseLayerName); + var id = doc.Objects.AddInstanceObject(index, transform, new ObjectAttributes() { LayerIndex = layerIndex }); + if (instanceProxy.applicationId != null) + { + applicationIdMap[instanceProxy.applicationId] = new List() { id.ToString() }; + } + + createdObjectIds.Add(id.ToString()); + conversionResults.Add(new(Status.SUCCESS, instanceProxy, id.ToString(), "Instance (Block)")); + } + } + catch (Exception ex) when (!ex.IsFatal()) + { + conversionResults.Add(new(Status.ERROR, instanceOrDefinition as Base ?? new Base(), null, null, ex)); + } + } + + return new(createdObjectIds, consumedObjectIds, conversionResults); + } + + public void PurgeInstances(string namePrefix) + { + var currentDoc = RhinoDoc.ActiveDoc; // POC: too much right now to interface around + foreach (var definition in currentDoc.InstanceDefinitions) + { + if (!definition.IsDeleted && definition.Name.Contains(namePrefix)) + { + currentDoc.InstanceDefinitions.Delete(definition.Index, true, false); + } + } + } + + private Matrix4x4 XFormToMatrix(Transform t) => + new(t.M00, t.M01, t.M02, t.M03, t.M10, t.M11, t.M12, t.M13, t.M20, t.M21, t.M22, t.M23, t.M30, t.M31, t.M32, t.M33); + + private Transform MatrixToTransform(Matrix4x4 matrix, string units) + { + var currentDoc = RhinoDoc.ActiveDoc; // POC: too much right now to interface around + var conversionFactor = Units.GetConversionFactor(units, currentDoc.ModelUnitSystem.ToSpeckleString()); + + var t = Transform.Identity; + t.M00 = matrix.M11; + t.M01 = matrix.M12; + t.M02 = matrix.M13; + t.M03 = matrix.M14 * conversionFactor; + + t.M10 = matrix.M21; + t.M11 = matrix.M22; + t.M12 = matrix.M23; + t.M13 = matrix.M24 * conversionFactor; + + t.M20 = matrix.M31; + t.M21 = matrix.M32; + t.M22 = matrix.M33; + t.M23 = matrix.M34 * conversionFactor; + + t.M30 = matrix.M41; + t.M31 = matrix.M42; + t.M32 = matrix.M43; + t.M33 = matrix.M44; + return t; + } +} diff --git a/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/HostApp/RhinoLayerManager.cs b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/HostApp/RhinoLayerManager.cs new file mode 100644 index 0000000000..55323bf099 --- /dev/null +++ b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/HostApp/RhinoLayerManager.cs @@ -0,0 +1,122 @@ +using System.Diagnostics.Contracts; +using Rhino; +using Rhino.DocObjects; +using Speckle.Core.Models; +using Speckle.Core.Models.GraphTraversal; + +namespace Speckle.Connectors.Rhino7.HostApp; + +/// +/// Utility class managing layer creation and/or extraction from rhino. Expects to be a scoped dependency per send or receive operation. +/// +public class RhinoLayerManager +{ + private readonly Dictionary _hostLayerCache = new(); + private readonly Dictionary _layerCollectionCache = new(); + + /// + /// Creates the base layer and adds it to the cache. + /// + /// + public void CreateBaseLayer(string baseLayerName) + { + var index = RhinoDoc.ActiveDoc.Layers.Add(new Layer { Name = baseLayerName }); // POC: too much effort right now to wrap around the interfaced layers and doc + // var index = _contextStack.Current.Document.Layers.Add(new Layer { Name = baseLayerName }); + _hostLayerCache.Add(baseLayerName, index); + } + + /// + /// For receive: Use this method to construct layers in the host app when receiving.. + /// + /// + /// + /// + public int GetAndCreateLayerFromPath(string[] path, string baseLayerName) + { + var fullLayerName = string.Join(Layer.PathSeparator, path); + if (_hostLayerCache.TryGetValue(fullLayerName, out int existingLayerIndex)) + { + return existingLayerIndex; + } + + var currentLayerName = baseLayerName; + var currentDocument = RhinoDoc.ActiveDoc; // POC: too much effort right now to wrap around the interfaced layers + + var previousLayer = currentDocument.Layers.FindName(currentLayerName); + foreach (var layerName in path) + { + currentLayerName = baseLayerName + Layer.PathSeparator + layerName; + currentLayerName = currentLayerName.Replace("{", "").Replace("}", ""); // Rhino specific cleanup for gh (see RemoveInvalidRhinoChars) + if (_hostLayerCache.TryGetValue(currentLayerName, out int value)) + { + previousLayer = currentDocument.Layers.FindIndex(value); + continue; + } + + var cleanNewLayerName = layerName.Replace("{", "").Replace("}", ""); + var newLayer = new Layer { Name = cleanNewLayerName, ParentLayerId = previousLayer.Id }; + var index = currentDocument.Layers.Add(newLayer); + _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 + } + return previousLayer.Index; + } + + /// + /// For send: 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. + /// + /// The layer you want the equivalent collection for. + /// The root object that will be sent to Speckle, and will host all collections. + /// + public Collection GetHostObjectCollection(Layer layer, Collection rootObjectCollection) + { + if (_layerCollectionCache.TryGetValue(layer.Index, out Collection value)) + { + return value; + } + + var names = layer.FullPath.Split(new[] { Layer.PathSeparator }, StringSplitOptions.None); + var path = names[0]; + var index = 0; + var previousCollection = rootObjectCollection; + foreach (var layerName in names) + { + var existingLayerIndex = RhinoDoc.ActiveDoc.Layers.FindByFullPath(path, -1); + Collection? childCollection = null; + if (_layerCollectionCache.TryGetValue(existingLayerIndex, out Collection? collection)) + { + childCollection = collection; + } + else + { + childCollection = new Collection(layerName, "layer") + { + applicationId = RhinoDoc.ActiveDoc.Layers[existingLayerIndex].Id.ToString() + }; + previousCollection.elements.Add(childCollection); + _layerCollectionCache[existingLayerIndex] = childCollection; + } + + previousCollection = childCollection; + + if (index < names.Length - 1) + { + path += Layer.PathSeparator + names[index + 1]; + } + index++; + } + + _layerCollectionCache[layer.Index] = previousCollection; + return previousCollection; + } + + [Pure] + public string[] GetLayerPath(TraversalContext context) + { + string[] collectionBasedPath = context.GetAscendantOfType().Select(c => c.name).ToArray(); + string[] reverseOrderPath = + collectionBasedPath.Length != 0 ? collectionBasedPath : context.GetPropertyPath().ToArray(); + return reverseOrderPath.Reverse().ToArray(); + } +} diff --git a/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/HostApp/RhinoSettings.cs b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/HostApp/RhinoSettings.cs new file mode 100644 index 0000000000..1bb2e89116 --- /dev/null +++ b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/HostApp/RhinoSettings.cs @@ -0,0 +1,19 @@ +using System.IO; +using Speckle.Core.Kits; + +namespace Speckle.Connectors.Rhino7.HostApp; + +public class RhinoSettings +{ + public RhinoSettings(HostApplication hostAppInfo, HostAppVersion hostAppVersion) + { + HostAppInfo = hostAppInfo; + HostAppVersion = hostAppVersion; + Modules = new[] { new DirectoryInfo(typeof(RhinoSettings).Assembly.Location).Parent.FullName }; + } + + public HostApplication HostAppInfo { get; private set; } + public HostAppVersion HostAppVersion { get; private set; } + + public IReadOnlyList Modules { get; private set; } +} diff --git a/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/HostApp/SpeckleRhinoPanelHost.cs b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/HostApp/SpeckleRhinoPanelHost.cs new file mode 100644 index 0000000000..1852a8e1e5 --- /dev/null +++ b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/HostApp/SpeckleRhinoPanelHost.cs @@ -0,0 +1,53 @@ +using System.Runtime.InteropServices; +using System.Windows; +using System.Windows.Controls; +using Rhino.UI; +using Speckle.Connectors.DUI.WebView; +using Speckle.Connectors.Rhino7.Plugin; + +namespace Speckle.Connectors.Rhino7.HostApp; + +[Guid("39BC44A4-C9DC-4B0A-9A51-4C31ACBCD76A")] +public class SpeckleRhinoPanelHost : RhinoWindows.Controls.WpfElementHost +{ + private readonly uint _docSn; + private readonly DUI3ControlWebView? _webView; + + public SpeckleRhinoPanelHost(uint docSn) + : base(SpeckleConnectorsRhino7Plugin.Instance.Container?.Resolve(), null) + { + _docSn = docSn; + _webView = SpeckleConnectorsRhino7Plugin.Instance.Container?.Resolve(); + Panels.Closed += PanelsOnClosed; + } + + private void PanelsOnClosed(object sender, PanelEventArgs e) + { + if (e.PanelId == typeof(SpeckleRhinoPanelHost).GUID) + { + // 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; + } + + // Unsubscribe from the event to prevent growing registrations. + Panels.Closed -= PanelsOnClosed; + + // Disconnect UIElement from WpfElementHost. Otherwise, we can't reinit panel with same DUI3ControlWebView + if (_webView != null) + { + // Since WpfHost inherited from Border, find the parent as border and set null it's Child. + if (LogicalTreeHelper.GetParent(_webView) is Border border) + { + border.Child = null; + } + } + } + } +} diff --git a/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Operations/Receive/DisableRedrawScope.cs b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Operations/Receive/DisableRedrawScope.cs new file mode 100644 index 0000000000..1182e5de7a --- /dev/null +++ b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Operations/Receive/DisableRedrawScope.cs @@ -0,0 +1,25 @@ +using Rhino.DocObjects.Tables; + +namespace Speckle.Connectors.Rhino7.Operations.Receive; + +/// +/// Helper class to disable within a scope +/// +public sealed class DisableRedrawScope : IDisposable +{ + private readonly ViewTable _viewTable; + private readonly bool _returnToStatus; + + public DisableRedrawScope(ViewTable viewTable, bool returnToStatus = true) + { + _viewTable = viewTable; + _returnToStatus = returnToStatus; + + _viewTable.RedrawEnabled = false; + } + + public void Dispose() + { + _viewTable.RedrawEnabled = _returnToStatus; + } +} diff --git a/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Operations/Receive/RhinoHostObjectBuilder.cs b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Operations/Receive/RhinoHostObjectBuilder.cs new file mode 100644 index 0000000000..1967f3a2bf --- /dev/null +++ b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Operations/Receive/RhinoHostObjectBuilder.cs @@ -0,0 +1,223 @@ +using Rhino; +using Rhino.DocObjects; +using Rhino.Geometry; +using Speckle.Connectors.Rhino7.HostApp; +using Speckle.Connectors.Utils.Builders; +using Speckle.Connectors.Utils.Conversion; +using Speckle.Connectors.Utils.Instances; +using Speckle.Converters.Common; +using Speckle.Core.Logging; +using Speckle.Core.Models; +using Speckle.Core.Models.GraphTraversal; +using Speckle.Core.Models.Instances; + +namespace Speckle.Connectors.Rhino7.Operations.Receive; + +/// +/// Expects to be a scoped dependency per receive operation. +/// +public class RhinoHostObjectBuilder : IHostObjectBuilder +{ + private readonly IRootToHostConverter _converter; + private readonly IConversionContextStack _contextStack; + private readonly GraphTraversal _traverseFunction; + + private readonly IInstanceObjectsManager> _instanceObjectsManager; + private readonly RhinoLayerManager _layerManager; + + public RhinoHostObjectBuilder( + IRootToHostConverter converter, + IConversionContextStack contextStack, + GraphTraversal traverseFunction, + RhinoLayerManager layerManager, + IInstanceObjectsManager> instanceObjectsManager + ) + { + _converter = converter; + _contextStack = contextStack; + _traverseFunction = traverseFunction; + _layerManager = layerManager; + _instanceObjectsManager = instanceObjectsManager; + } + + public HostObjectBuilderResult Build( + Base rootObject, + string projectName, + string modelName, + Action? onOperationProgressed, + CancellationToken cancellationToken + ) + { + // POC: This is where the top level base-layer name is set. Could be abstracted or injected in the context? + var baseLayerName = $"Project {projectName}: Model {modelName}"; + + var objectsToConvert = _traverseFunction + .TraverseWithProgress(rootObject, onOperationProgressed, cancellationToken) + .Where(obj => obj.Current is not Collection); + + var instanceDefinitionProxies = (rootObject["instanceDefinitionProxies"] as List) + ?.Cast() + .ToList(); + + var conversionResults = BakeObjects( + objectsToConvert, + instanceDefinitionProxies, + baseLayerName, + onOperationProgressed + ); + + _contextStack.Current.Document.Views.Redraw(); + + return conversionResults; + } + + private HostObjectBuilderResult BakeObjects( + IEnumerable objectsGraph, + List? instanceDefinitionProxies, + string baseLayerName, + Action? onOperationProgressed + ) + { + RhinoDoc doc = _contextStack.Current.Document; + var rootLayerIndex = _contextStack.Current.Document.Layers.Find(Guid.Empty, baseLayerName, RhinoMath.UnsetIntIndex); + + PreReceiveDeepClean(baseLayerName, rootLayerIndex); + _layerManager.CreateBaseLayer(baseLayerName); + + using var noDraw = new DisableRedrawScope(doc.Views); + + var conversionResults = new List(); + var bakedObjectIds = new List(); + + var instanceComponents = new List<(string[] layerPath, IInstanceComponent obj)>(); + + // POC: these are not captured by traversal, so we need to re-add them here + if (instanceDefinitionProxies != null && instanceDefinitionProxies.Count > 0) + { + var transformed = instanceDefinitionProxies.Select(proxy => (Array.Empty(), proxy as IInstanceComponent)); + instanceComponents.AddRange(transformed); + } + + var atomicObjects = new List<(string[] layerPath, Base obj)>(); + + // Split up the instances from the non-instances + foreach (TraversalContext tc in objectsGraph) + { + var path = _layerManager.GetLayerPath(tc); + if (tc.Current is IInstanceComponent instanceComponent) + { + instanceComponents.Add((path, instanceComponent)); + } + else + { + atomicObjects.Add((path, tc.Current)); + } + } + + // Stage 1: Convert atomic objects + // Note: this can become encapsulated later in an "atomic object baker" of sorts, if needed. + var applicationIdMap = new Dictionary>(); // used in converting blocks in stage 2. keeps track of original app id => resulting new app ids post baking + var count = 0; + foreach (var (path, obj) in atomicObjects) + { + onOperationProgressed?.Invoke("Converting objects", (double)++count / atomicObjects.Count); + try + { + var layerIndex = _layerManager.GetAndCreateLayerFromPath(path, baseLayerName); + var result = _converter.Convert(obj); + var conversionIds = HandleConversionResult(result, obj, layerIndex).ToList(); + foreach (var r in conversionIds) + { + conversionResults.Add(new(Status.SUCCESS, obj, r, result.GetType().ToString())); + bakedObjectIds.Add(r); + } + + if (obj.applicationId != null) + { + applicationIdMap[obj.applicationId] = conversionIds; + } + } + catch (Exception ex) when (!ex.IsFatal()) + { + conversionResults.Add(new(Status.ERROR, obj, null, null, ex)); + } + } + + // Stage 2: Convert instances + var (createdInstanceIds, consumedObjectIds, instanceConversionResults) = _instanceObjectsManager.BakeInstances( + instanceComponents, + 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 + + // Stage 3: Return + return new(bakedObjectIds, conversionResults); + } + + private void PreReceiveDeepClean(string baseLayerName, int rootLayerIndex) + { + _instanceObjectsManager.PurgeInstances(baseLayerName); + + var doc = _contextStack.Current.Document; + // Cleans up any previously received objects + if (rootLayerIndex != RhinoMath.UnsetIntIndex) + { + var documentLayer = doc.Layers[rootLayerIndex]; + var childLayers = documentLayer.GetChildren(); + if (childLayers != null) + { + using var layerNoDraw = new DisableRedrawScope(doc.Views); + foreach (var layer in childLayers) + { + var purgeSuccess = doc.Layers.Purge(layer.Index, true); + if (!purgeSuccess) + { + Console.WriteLine($"Failed to purge layer: {layer}"); + } + } + } + } + } + + private IReadOnlyList HandleConversionResult(object conversionResult, Base originalObject, int layerIndex) + { + var doc = _contextStack.Current.Document; + List newObjectIds = new(); + switch (conversionResult) + { + case IEnumerable list: + { + Group group = BakeObjectsAsGroup(originalObject.id, list, layerIndex); + newObjectIds.Add(group.Id.ToString()); + break; + } + case GeometryBase newObject: + { + var newObjectGuid = doc.Objects.Add(newObject, new ObjectAttributes { LayerIndex = layerIndex }); + newObjectIds.Add(newObjectGuid.ToString()); + break; + } + default: + throw new SpeckleConversionException( + $"Unexpected result from conversion: Expected {nameof(GeometryBase)} but instead got {conversionResult.GetType().Name}" + ); + } + + return newObjectIds; + } + + private Group BakeObjectsAsGroup(string groupName, IEnumerable list, int layerIndex) + { + var doc = _contextStack.Current.Document; + var objectIds = list.Select(obj => doc.Objects.Add(obj, new ObjectAttributes { LayerIndex = layerIndex })); + var groupIndex = _contextStack.Current.Document.Groups.Add(groupName, objectIds); + var group = _contextStack.Current.Document.Groups.FindIndex(groupIndex); + return group; + } +} diff --git a/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Operations/Send/RhinoRootObjectBuilder.cs b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Operations/Send/RhinoRootObjectBuilder.cs new file mode 100644 index 0000000000..03d01c8568 --- /dev/null +++ b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Operations/Send/RhinoRootObjectBuilder.cs @@ -0,0 +1,132 @@ +using System.Diagnostics; +using Rhino; +using Rhino.DocObjects; +using Speckle.Core.Models; +using Speckle.Autofac.DependencyInjection; +using Speckle.Converters.Common; +using Speckle.Connectors.DUI.Models.Card.SendFilter; +using Speckle.Connectors.Rhino7.HostApp; +using Speckle.Connectors.Utils.Builders; +using Speckle.Connectors.Utils.Caching; +using Speckle.Connectors.Utils.Conversion; +using Speckle.Connectors.Utils.Instances; +using Speckle.Connectors.Utils.Operations; +using Speckle.Core.Logging; + +namespace Speckle.Connectors.Rhino7.Operations.Send; + +/// +/// Stateless builder object to turn an into a object +/// +public class RhinoRootObjectBuilder : IRootObjectBuilder +{ + private readonly IUnitOfWorkFactory _unitOfWorkFactory; + private readonly ISendConversionCache _sendConversionCache; + private readonly IInstanceObjectsManager> _instanceObjectsManager; + private readonly IConversionContextStack _contextStack; + private readonly RhinoLayerManager _layerManager; + + public RhinoRootObjectBuilder( + IUnitOfWorkFactory unitOfWorkFactory, + ISendConversionCache sendConversionCache, + IConversionContextStack contextStack, + RhinoLayerManager layerManager, + IInstanceObjectsManager> instanceObjectsManager + ) + { + _unitOfWorkFactory = unitOfWorkFactory; + _sendConversionCache = sendConversionCache; + _contextStack = contextStack; + _layerManager = layerManager; + _instanceObjectsManager = instanceObjectsManager; + } + + public RootObjectBuilderResult Build( + IReadOnlyList objects, + SendInfo sendInfo, + Action? onOperationProgressed = null, + CancellationToken ct = default + ) => ConvertObjects(objects, sendInfo, onOperationProgressed, ct); + + private RootObjectBuilderResult ConvertObjects( + IReadOnlyList rhinoObjects, + SendInfo sendInfo, + Action? onOperationProgressed = null, + CancellationToken cancellationToken = default + ) + { + // POC: does this feel like the right place? I am wondering if this should be called from within send/rcv? + // begin the unit of work + using var uow = _unitOfWorkFactory.Resolve(); + var converter = uow.Service; + + var rootObjectCollection = new Collection { name = _contextStack.Current.Document.Name ?? "Unnamed document" }; + int count = 0; + + Dictionary layerCollectionCache = new(); + + var (atomicObjects, instanceProxies, instanceDefinitionProxies) = _instanceObjectsManager.UnpackSelection( + rhinoObjects + ); + + // POC: we should formalise this, sooner or later - or somehow fix it a bit more + rootObjectCollection["instanceDefinitionProxies"] = instanceDefinitionProxies; // this won't work re traversal on receive + + // POC: Handle blocks. + List results = new(rhinoObjects.Count); + var cacheHitCount = 0; + foreach (RhinoObject rhinoObject in atomicObjects) + { + cancellationToken.ThrowIfCancellationRequested(); + // RhinoDoc.ActiveDoc.Layers + var layer = _contextStack.Current.Document.Layers[rhinoObject.Attributes.LayerIndex]; + + var collectionHost = _layerManager.GetHostObjectCollection(layer, rootObjectCollection); + var applicationId = rhinoObject.Id.ToString(); + + try + { + // get from cache or convert: + // What we actually do here is check if the object has been previously converted AND has not changed. + // If that's the case, we insert in the host collection just its object reference which has been saved from the prior conversion. + Base converted; + if (rhinoObject is InstanceObject) + { + converted = instanceProxies[applicationId]; + } + else if (_sendConversionCache.TryGetValue(sendInfo.ProjectId, applicationId, out ObjectReference value)) + { + converted = value; + cacheHitCount++; + } + else + { + converted = converter.Convert(rhinoObject); + converted.applicationId = applicationId; + } + + // add to host + collectionHost.elements.Add(converted); + + results.Add(new(Status.SUCCESS, applicationId, rhinoObject.ObjectType.ToString(), converted)); + } + catch (Exception ex) when (!ex.IsFatal()) + { + results.Add(new(Status.ERROR, applicationId, rhinoObject.ObjectType.ToString(), null, ex)); + } + + onOperationProgressed?.Invoke("Converting", (double)++count / rhinoObjects.Count); + + // NOTE: useful for testing ui states, pls keep for now so we can easily uncomment + // Thread.Sleep(550); + } + + // POC: Log would be nice, or can be removed. + Debug.WriteLine( + $"Cache hit count {cacheHitCount} out of {rhinoObjects.Count} ({(double)cacheHitCount / rhinoObjects.Count})" + ); + + // 5. profit + return new(rootObjectCollection, results); + } +} diff --git a/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Plugin/Speckle.Connectors.Rhino7Command.cs b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Plugin/Speckle.Connectors.Rhino7Command.cs new file mode 100644 index 0000000000..f6f9effeb2 --- /dev/null +++ b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Plugin/Speckle.Connectors.Rhino7Command.cs @@ -0,0 +1,97 @@ +using Rhino; +using Rhino.Commands; +using Rhino.Input.Custom; +using Rhino.UI; +using Speckle.Connectors.Rhino7.HostApp; +using Speckle.Connectors.Rhino7.Properties; + +namespace Speckle.Connectors.Rhino7.Plugin; + +public class SpeckleConnectorsRhino7Command : Command +{ + public SpeckleConnectorsRhino7Command() + { + // Rhino only creates one instance of each command class defined in a + // plug-in, so it is safe to store a reference in a static property. + Instance = this; + Panels.RegisterPanel( + SpeckleConnectorsRhino7Plugin.Instance, + typeof(SpeckleRhinoPanelHost), + "Speckle (New UI)", + Resources.speckle32, + PanelType.System + ); + } + + ///The only instance of this command. +#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. + public static SpeckleConnectorsRhino7Command Instance { get; private set; } +#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. + + ///The command name as it appears on the Rhino command line. + public override string EnglishName => "SpeckleNewUI"; + + protected override Result RunCommand(RhinoDoc doc, RunMode mode) + { + Guid panelId = typeof(SpeckleRhinoPanelHost).GUID; + + if (mode == RunMode.Interactive) + { + Panels.OpenPanel(panelId); + return Result.Success; + } + + bool panelVisible = Panels.IsPanelVisible(panelId); + + string prompt = panelVisible + ? "SpeckleWebUIWebView2 panel is visible. New value" + : "SpeckleWebUIWebView2 panel is hidden. New value"; + + using GetOption go = new(); + go.SetCommandPrompt(prompt); + int hideIndex = go.AddOption("Hide"); + int showIndex = go.AddOption("Show"); + int toggleIndex = go.AddOption("Toggle"); + go.Get(); + + if (go.CommandResult() != Result.Success) + { + return go.CommandResult(); + } + + CommandLineOption option = go.Option(); + if (null == option) + { + return Result.Failure; + } + + int index = option.Index; + if (index == hideIndex) + { + if (panelVisible) + { + Panels.ClosePanel(panelId); + } + } + else if (index == showIndex) + { + if (!panelVisible) + { + Panels.OpenPanel(panelId); + } + } + else if (index == toggleIndex) + { + switch (panelVisible) + { + case true: + Panels.ClosePanel(panelId); + break; + default: + Panels.OpenPanel(panelId); + break; + } + } + return Result.Success; + } +} diff --git a/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Plugin/Speckle.Connectors.Rhino7Plugin.cs b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Plugin/Speckle.Connectors.Rhino7Plugin.cs new file mode 100644 index 0000000000..bd1cff5176 --- /dev/null +++ b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Plugin/Speckle.Connectors.Rhino7Plugin.cs @@ -0,0 +1,77 @@ +using System.Reflection; +using Rhino.PlugIns; +using Speckle.Autofac; +using Speckle.Autofac.DependencyInjection; +using Speckle.Connectors.Rhino7.DependencyInjection; +using Speckle.Connectors.Rhino7.HostApp; +using Speckle.Core.Kits; +using Speckle.Core.Models.Extensions; + +namespace Speckle.Connectors.Rhino7.Plugin; + +/// +/// Every RhinoCommon .rhp assembly must have one and only one PlugIn-derived +/// class. DO NOT create instances of this class yourself. It is the +/// responsibility of Rhino to create an instance of this class. +/// To complete plug-in information, please also see all PlugInDescription +/// attributes in AssemblyInfo.cs (you might need to click "Project" -> +/// "Show All Files" to see it in the "Solution Explorer" window). +/// +public class SpeckleConnectorsRhino7Plugin : PlugIn +{ + private IRhinoPlugin? _rhinoPlugin; + + protected override string LocalPlugInName => "Speckle (New UI)"; + public SpeckleContainer? Container { get; private set; } + + public SpeckleConnectorsRhino7Plugin() + { + Instance = this; + } + + ///Gets the only instance of the Speckle_Connectors_Rhino7Plugin plug-in. +#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. + public static SpeckleConnectorsRhino7Plugin Instance { get; private set; } +#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. + + // You can override methods here to change the plug-in behavior on + // loading and shut down, add options pages to the Rhino _Option command + // and maintain plug-in wide options in a document. + + protected override LoadReturnCode OnLoad(ref string errorMessage) + { + try + { + AppDomain.CurrentDomain.AssemblyResolve += AssemblyResolver.OnAssemblyResolve; + + var builder = SpeckleContainerBuilder.CreateInstance(); + + // Register Settings + var rhinoSettings = new RhinoSettings(HostApplications.Rhino, HostAppVersion.v7); + + // POC: We must load the Rhino connector module manually because we only search for DLL files when calling `LoadAutofacModules`, + // but the Rhino connector has `.rhp` as it's extension. + Container = builder + .LoadAutofacModules(Assembly.GetExecutingAssembly(), rhinoSettings.Modules) + .AddSingleton(rhinoSettings) + .Build(); + + // Resolve root plugin object and initialise. + _rhinoPlugin = Container.Resolve(); + _rhinoPlugin.Initialise(); + + return LoadReturnCode.Success; + } + catch (Exception e) when (!e.IsFatal()) + { + errorMessage = e.ToFormattedString(); + return LoadReturnCode.ErrorShowDialog; + } + } + + protected override void OnShutdown() + { + _rhinoPlugin?.Shutdown(); + base.OnShutdown(); + } +} diff --git a/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Properties/AssemblyInfo.cs b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..abbb317629 --- /dev/null +++ b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Properties/AssemblyInfo.cs @@ -0,0 +1,23 @@ +using System.Resources; +using System.Runtime.InteropServices; +using Rhino.PlugIns; + +// Plug-in Description Attributes - all of these are optional. +// These will show in Rhino's option dialog, in the tab Plug-ins. +[assembly: PlugInDescription(DescriptionType.Address, "")] +[assembly: PlugInDescription(DescriptionType.Country, "")] +[assembly: PlugInDescription(DescriptionType.Email, "hello@speckle.systems")] +[assembly: PlugInDescription(DescriptionType.Phone, "")] +[assembly: PlugInDescription(DescriptionType.Fax, "")] +[assembly: PlugInDescription(DescriptionType.Organization, "Speckle Systems Ltd.")] +[assembly: PlugInDescription(DescriptionType.UpdateUrl, "")] +[assembly: PlugInDescription(DescriptionType.WebSite, "https://speckle.systems")] + +// Icons should be Windows .ico files and contain 32-bit images in the following sizes: 16, 24, 32, 48, and 256. +[assembly: PlugInDescription(DescriptionType.Icon, "Speckle.Connectors.Rhino7.Resources.speckle32.ico")] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +// This will also be the Guid of the Rhino plug-in +[assembly: Guid("2153799A-0CEC-40DE-BC3A-01E5055222FF")] + +[assembly: NeutralResourcesLanguage("en")] diff --git a/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Properties/Resources.Designer.cs b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Properties/Resources.Designer.cs new file mode 100644 index 0000000000..ec3cee7dbb --- /dev/null +++ b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Properties/Resources.Designer.cs @@ -0,0 +1,83 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Speckle.Connectors.Rhino7.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Speckle.Connectors.Rhino7.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap logo32 { + get { + object obj = ResourceManager.GetObject("logo32", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon). + /// + internal static System.Drawing.Icon speckle32 { + get { + object obj = ResourceManager.GetObject("speckle32", resourceCulture); + return ((System.Drawing.Icon)(obj)); + } + } + } +} diff --git a/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Properties/Resources.resx b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Properties/Resources.resx new file mode 100644 index 0000000000..61bb1d8964 --- /dev/null +++ b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Properties/Resources.resx @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\Resources\logo32.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\speckle32.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + \ No newline at end of file diff --git a/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Properties/launchSettings.json b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Properties/launchSettings.json new file mode 100644 index 0000000000..6a541028e4 --- /dev/null +++ b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Properties/launchSettings.json @@ -0,0 +1,9 @@ +{ + "profiles": { + "Speckle.Connectors.Rhino7": { + "commandName": "Executable", + "executablePath": "C:\\Program Files\\Rhino 7\\System\\Rhino.exe", + "commandLineArgs": "\"$(TargetPath)\"" + } + } +} diff --git a/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Resources/logo32.png b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Resources/logo32.png new file mode 100644 index 0000000000..39074dca39 Binary files /dev/null and b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Resources/logo32.png differ diff --git a/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Resources/speckle32.ico b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Resources/speckle32.ico new file mode 100644 index 0000000000..87c4f42107 Binary files /dev/null and b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Resources/speckle32.ico differ diff --git a/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Speckle.Connectors.Rhino7.csproj b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Speckle.Connectors.Rhino7.csproj new file mode 100644 index 0000000000..ef4a884995 --- /dev/null +++ b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Speckle.Connectors.Rhino7.csproj @@ -0,0 +1,45 @@ + + + net48 + .rhp + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + + True + True + Resources.resx + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + diff --git a/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/packages.lock.json b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/packages.lock.json new file mode 100644 index 0000000000..53de68cb60 --- /dev/null +++ b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/packages.lock.json @@ -0,0 +1,553 @@ +{ + "version": 2, + "dependencies": { + ".NETFramework,Version=v4.8": { + "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==" + }, + "RhinoCommon": { + "type": "Direct", + "requested": "[7.13.21348.13001, )", + "resolved": "7.13.21348.13001", + "contentHash": "JQdaNw61ddBqIe08E9O4N/grwrN1hjDHcYW7tWylwCZyFR7SepoCD4NS+6LN6+oSQhNbhLi9Bf+hQOFYFdRAEA==" + }, + "RhinoWindows": { + "type": "Direct", + "requested": "[7.13.21348.13001, )", + "resolved": "7.13.21348.13001", + "contentHash": "V94T8emmJmFfmbd5cu+uTNS0neZApx1Q5MXvsQGFtt/mEGEbdHE+dFOETNgbOOJXSdNboAnCR3uo0GosOFX+/g==", + "dependencies": { + "RhinoCommon": "[7.13.21348.13001]" + } + }, + "Speckle.InterfaceGenerator": { + "type": "Direct", + "requested": "[0.9.5, )", + "resolved": "0.9.5", + "contentHash": "oU/L7pN1R7q8KkbrpQ3WJnHirPHqn+9DEA7asOcUiggV5dzVg1A/VYs7GOSusD24njxXh03tE3a2oTLOjt3cVg==" + }, + "System.Resources.Extensions": { + "type": "Direct", + "requested": "[7.0.0, )", + "resolved": "7.0.0", + "contentHash": "65ufm9ABXvxRkQ//hMcUDrQXbGWkC7z0WWZAvHlQ6Qv+JmrIwHH1lmX8aXlNlXpIrT9KxDpuZPqJTVqqwzMD8Q==", + "dependencies": { + "System.Memory": "4.5.5" + } + }, + "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": "7.0.0", + "contentHash": "3aeMZ1N0lJoSyzqiP03hqemtb1BijhsJADdobn/4nsMJ8V1H+CrpuduUe4hlRdx+ikBQju1VGjMD1GJ3Sk05Eg==", + "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.DependencyInjection": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "elNeOmkeX3eDVG6pYVeV82p29hr+UKDaBhrZyWvWLw/EVZSYEkZlQdkp0V39k/Xehs2Qa0mvoCvkVj3eQxNQ1Q==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "7.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0", + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "h3j/QfmFN4S0w4C2A6X7arXij/M/OVw3uQHSOFxnND4DyAzO1F9eMX7Eti7lU/OkSthEE0WzRsfT/Dmx86jzCw==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "7.0.0", + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "Microsoft.Extensions.Logging": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "Nw2muoNrOG5U5qa2ZekXwudUn2BJcD41e65zwmDHb1fQegTX66UokLWZkJRpqSSHXDOWZ5V0iqhbxOEky91atA==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "7.0.0", + "Microsoft.Extensions.DependencyInjection": "7.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0", + "Microsoft.Extensions.Logging.Abstractions": "7.0.0", + "Microsoft.Extensions.Options": "7.0.0", + "System.Diagnostics.DiagnosticSource": "7.0.0", + "System.ValueTuple": "4.5.0" + } + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "lP1yBnTTU42cKpMozuafbvNtQ7QcBjr/CcK3bYOGEMH55Fjt+iecXjT6chR7vbgCMqy3PG3aNQSZgo/EuY/9qQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0", + "Microsoft.Extensions.Primitives": "7.0.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "um1KU5kxcRp3CNuI8o/GrZtD4AIOXDk+RLsytjZ9QPok3ttLUelLKpilVPuaFT3TFjOhSibUAso0odbOaCDj3Q==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "Polly": { + "type": "Transitive", + "resolved": "7.2.3", + "contentHash": "DeCY0OFbNdNxsjntr1gTXHJ5pKUwYzp04Er2LLeN3g6pWhffsGuKVfMBLe1lw7x76HrPkLxKEFxBlpRxS2nDEQ==" + }, + "Polly.Contrib.WaitAndRetry": { + "type": "Transitive", + "resolved": "1.1.1", + "contentHash": "1MUQLiSo4KDkQe6nzQRhIU05lm9jlexX5BVsbuw0SL82ynZ+GzAHQxJVDPVBboxV37Po3SG077aX8DuSy8TkaA==" + }, + "Polly.Extensions.Http": { + "type": "Transitive", + "resolved": "3.0.0", + "contentHash": "drrG+hB3pYFY7w1c3BD+lSGYvH2oIclH8GRSehgfyP5kjnFnHKQuuBhuHLv+PWyFuaTDyk/vfRpnxOzd11+J8g==", + "dependencies": { + "Polly": "7.1.0" + } + }, + "Sentry": { + "type": "Transitive", + "resolved": "3.33.0", + "contentHash": "8vbD2o6IR2wrRrkSiRbnodWGWUOqIlwYtzpjvPNOb5raJdOf+zxMwfS8f6nx9bmrTTfDj7KrCB8C/5OuicAc8A==", + "dependencies": { + "System.Reflection.Metadata": "5.0.0", + "System.Runtime.InteropServices.RuntimeInformation": "4.3.0", + "System.Text.Json": "5.0.2" + } + }, + "Sentry.Serilog": { + "type": "Transitive", + "resolved": "3.33.0", + "contentHash": "V8BU7QGWg2qLYfNPqtuTBhC1opysny5l+Ifp6J6PhOeAxU0FssR7nYfbJVetrnLIoh2rd3DlJ6hHYYQosQYcUQ==", + "dependencies": { + "Sentry": "3.33.0", + "Serilog": "2.7.1" + } + }, + "Serilog": { + "type": "Transitive", + "resolved": "2.12.0", + "contentHash": "xaiJLIdu6rYMKfQMYUZgTy8YK7SMZjB4Yk50C/u//Z4OsvxkUfSPJy4nknfvwAC34yr13q7kcyh4grbwhSxyZg==" + }, + "Serilog.Enrichers.ClientInfo": { + "type": "Transitive", + "resolved": "1.3.0", + "contentHash": "mTc7PM+wC9Hr7LWSwqt5mmnlAr7RJs+eTb3PGPRhwdOackk95MkhUZognuxXEdlW19HAFNmEBTSBY5DfLwM8jQ==", + "dependencies": { + "Serilog": "2.4.0" + } + }, + "Serilog.Exceptions": { + "type": "Transitive", + "resolved": "8.4.0", + "contentHash": "nc/+hUw3lsdo0zCj0KMIybAu7perMx79vu72w0za9Nsi6mWyNkGXxYxakAjWB7nEmYL6zdmhEQRB4oJ2ALUeug==", + "dependencies": { + "Serilog": "2.8.0" + } + }, + "Serilog.Formatting.Compact": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "pNroKVjo+rDqlxNG5PXkRLpfSCuDOBY0ri6jp9PLe505ljqwhwZz8ospy2vWhQlFu5GkIesh3FcDs4n7sWZODA==", + "dependencies": { + "Serilog": "2.8.0" + } + }, + "Serilog.Sinks.Console": { + "type": "Transitive", + "resolved": "4.1.0", + "contentHash": "K6N5q+5fetjnJPvCmkWOpJ/V8IEIoMIB1s86OzBrbxwTyHxdx3pmz4H+8+O/Dc/ftUX12DM1aynx/dDowkwzqg==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "Serilog.Sinks.File": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "uwV5hdhWPwUH1szhO8PJpFiahqXmzPzJT/sOijH/kFgUx+cyoDTMM8MHD0adw9+Iem6itoibbUXHYslzXsLEAg==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "Serilog.Sinks.PeriodicBatching": { + "type": "Transitive", + "resolved": "3.1.0", + "contentHash": "NDWR7m3PalVlGEq3rzoktrXikjFMLmpwF0HI4sowo8YDdU+gqPlTHlDQiOGxHfB0sTfjPA9JjA7ctKG9zqjGkw==", + "dependencies": { + "Serilog": "2.0.0" + } + }, + "Serilog.Sinks.Seq": { + "type": "Transitive", + "resolved": "5.2.2", + "contentHash": "1Csmo5ua7NKUe0yXUx+zsRefjAniPWcXFhUXxXG8pwo0iMiw2gjn9SOkgYnnxbgWqmlGv236w0N/dHc2v5XwMg==", + "dependencies": { + "Serilog": "2.12.0", + "Serilog.Formatting.Compact": "1.1.0", + "Serilog.Sinks.File": "5.0.0", + "Serilog.Sinks.PeriodicBatching": "3.1.0" + } + }, + "SerilogTimings": { + "type": "Transitive", + "resolved": "3.0.1", + "contentHash": "Zs28eTgszAMwpIrbBnWHBI50yuxL50p/dmAUWmy75+axdZYK/Sjm5/5m1N/CisR8acJUhTVcjPZrsB1P5iv0Uw==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "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.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, + "System.Collections.Immutable": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "FXkLXiK0sVVewcso0imKQoOxjoPAj42R8HtjjbSjVPAzwDfzoyoznWxgA3c38LDbN9SJux1xXoXYAhz98j7r2g==", + "dependencies": { + "System.Memory": "4.5.4" + } + }, + "System.Diagnostics.DiagnosticSource": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "9W0ewWDuAyDqS2PigdTxk6jDKonfgscY/hP8hm7VpxYhNHZHKvZTdRckberlFk3VnCmr3xBUyMBut12Q+T2aOw==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "System.DoubleNumerics": { + "type": "Transitive", + "resolved": "3.1.3", + "contentHash": "KRKEM/L3KBodjA9VOg3EifFVWUY6EOqaMB05UvPEDm7Zeby/kZW+4kdWUEPzW6xtkwf46p661L9NrbeeQhtLzw==", + "dependencies": { + "NETStandard.Library": "1.6.1" + } + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Numerics.Vectors": "4.5.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + }, + "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.5.0", + "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" + }, + "System.Reactive": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "System.Reflection.Metadata": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "5NecZgXktdGg34rh1OenY1rFNDCI8xSjFr+Z4OU4cU06AQHUdRnIIEeWENu3Wl4YowbzkymAIMvi3WyK9U53pQ==", + "dependencies": { + "System.Collections.Immutable": "5.0.0" + } + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + }, + "System.Runtime.InteropServices.RuntimeInformation": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==" + }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "5.0.1", + "contentHash": "KmJ+CJXizDofbq6mpqDoRRLcxgOd2z9X3XoFNULSbvbqVRZkFX3istvr+MUjL6Zw1RT+RNdoI4GYidIINtgvqQ==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Memory": "4.5.4" + } + }, + "System.Text.Json": { + "type": "Transitive", + "resolved": "5.0.2", + "contentHash": "I47dVIGiV6SfAyppphxqupertT/5oZkYLDCX6vC3HpOI4ZLjyoKAreUoem2ie6G0RbRuFrlqz/PcTQjfb2DOfQ==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "5.0.0", + "System.Buffers": "4.5.1", + "System.Memory": "4.5.4", + "System.Numerics.Vectors": "4.5.0", + "System.Runtime.CompilerServices.Unsafe": "5.0.0", + "System.Text.Encodings.Web": "5.0.1", + "System.Threading.Tasks.Extensions": "4.5.4", + "System.ValueTuple": "4.5.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" + } + }, + "System.ValueTuple": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "okurQJO6NRE/apDIP23ajJ0hpiNmJ+f0BwOlB/cSqTLQlw5upkf+5+96+iG2Jw40G1fCVCyPz/FhIABUjMR+RQ==" + }, + "speckle.autofac": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "[7.0.0, )" + } + }, + "speckle.connectors.dui": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "[7.0.0, )", + "Speckle.Autofac": "[2.0.999-local, )", + "Speckle.Connectors.Utils": "[2.0.999-local, )", + "Speckle.Core": "[3.0.1-alpha.14, )", + "System.Threading.Tasks.Dataflow": "[6.0.0, )" + } + }, + "speckle.connectors.dui.webview": { + "type": "Project", + "dependencies": { + "Microsoft.Web.WebView2": "[1.0.1823.32, )", + "Speckle.Connectors.DUI": "[2.0.999-local, )" + } + }, + "speckle.connectors.utils": { + "type": "Project", + "dependencies": { + "Serilog.Extensions.Logging": "[7.0.0, )", + "Speckle.Autofac": "[2.0.999-local, )", + "Speckle.Core": "[3.0.1-alpha.14, )" + } + }, + "speckle.converters.common": { + "type": "Project", + "dependencies": { + "Speckle.Autofac": "[2.0.999-local, )", + "Speckle.Objects": "[3.0.1-alpha.14, )" + } + }, + "speckle.converters.common.dependencyinjection": { + "type": "Project", + "dependencies": { + "Speckle.Autofac": "[2.0.999-local, )", + "Speckle.Converters.Common": "[2.0.999-local, )" + } + }, + "speckle.converters.rhino7.dependencyinjection": { + "type": "Project", + "dependencies": { + "RhinoCommon": "[7.13.21348.13001, )" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "CentralTransitive", + "requested": "[7.0.0, )", + "resolved": "7.0.0", + "contentHash": "kmn78+LPVMOWeITUjIlfxUPDsI0R6G0RkeAMBmQxAJ7vBJn4q2dTva7pWi65ceN5vPGjJ9q/Uae2WKgvfktJAw==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Memory": "4.5.5" + } + }, + "Microsoft.Web.WebView2": { + "type": "CentralTransitive", + "requested": "[1.0.1823.32, )", + "resolved": "1.0.1823.32", + "contentHash": "ppRcWBUNggFIqyJp7PfgS4Oe8/7yli/mHcTNDOaqo3ZzJWnv9AOr0WE9ceEP5SPs1M7CQ3QvdGMR7KNz0v09EA==" + }, + "Serilog.Extensions.Logging": { + "type": "CentralTransitive", + "requested": "[7.0.0, )", + "resolved": "7.0.0", + "contentHash": "9faU0zNQqU7I6soVhLUMYaGNpgWv6cKlKb2S5AnS8gXxzW/em5Ladm/6FMrWTnX41cdbdGPOWNAo6adi4WaJ6A==", + "dependencies": { + "Microsoft.Extensions.Logging": "7.0.0", + "Serilog": "2.12.0" + } + }, + "Speckle.Core": { + "type": "CentralTransitive", + "requested": "[3.0.1-alpha.14, )", + "resolved": "3.0.1-alpha.14", + "contentHash": "RzQPVIGFFkKvG56YLr8ACtiwdWJE6IJ9vCQ4qHa0PIsUEpfzAIAi59jnzqtByOFC0FiFrFPow9bkfzylaZorAA==", + "dependencies": { + "GraphQL.Client": "6.0.0", + "Microsoft.CSharp": "4.7.0", + "Microsoft.Data.Sqlite": "7.0.5", + "Polly": "7.2.3", + "Polly.Contrib.WaitAndRetry": "1.1.1", + "Polly.Extensions.Http": "3.0.0", + "Sentry": "3.33.0", + "Sentry.Serilog": "3.33.0", + "Serilog": "2.12.0", + "Serilog.Enrichers.ClientInfo": "1.3.0", + "Serilog.Exceptions": "8.4.0", + "Serilog.Sinks.Console": "4.1.0", + "Serilog.Sinks.Seq": "5.2.2", + "SerilogTimings": "3.0.1", + "Speckle.Newtonsoft.Json": "13.0.2", + "System.DoubleNumerics": "3.1.3" + } + }, + "Speckle.Objects": { + "type": "CentralTransitive", + "requested": "[3.0.1-alpha.14, )", + "resolved": "3.0.1-alpha.14", + "contentHash": "z38LGryMvh7iU1uBW+4uo5DwsB3CwRgLt2uFexWFx3mPSid+A0l5XcJzOgLwgFhNl6B42Ryz4ezBsddTp1Uc/g==", + "dependencies": { + "Speckle.Core": "3.0.1-alpha.14" + } + }, + "System.Threading.Tasks.Dataflow": { + "type": "CentralTransitive", + "requested": "[6.0.0, )", + "resolved": "6.0.0", + "contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA==" + } + } + } +} \ No newline at end of file diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3.DependencyInjection/ArcGISConverterModule.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3.DependencyInjection/ArcGISConverterModule.cs new file mode 100644 index 0000000000..c9ea4f55a7 --- /dev/null +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3.DependencyInjection/ArcGISConverterModule.cs @@ -0,0 +1,32 @@ +using ArcGIS.Core.Geometry; +using Speckle.Autofac.DependencyInjection; +using Speckle.Converters.ArcGIS3.Utils; +using Speckle.Converters.Common; +using Speckle.Converters.Common.DependencyInjection; + +namespace Speckle.Converters.ArcGIS3.DependencyInjection; + +public class ArcGISConverterModule : ISpeckleModule +{ + public void Load(SpeckleContainerBuilder builder) + { + // add single root converter + //don't need a host specific RootToSpeckleConverter + builder.AddRootCommon(); + + // add application converters + builder.AddApplicationConverters(); + + // most things should be InstancePerLifetimeScope so we get one per operation + builder.AddScoped(); + builder.AddScoped(); + builder.AddScoped(); + builder.AddScoped(); + builder.AddScoped(); + + builder.AddScoped, ArcGISToSpeckleUnitConverter>(); + + // single stack per conversion + builder.AddScoped, ArcGISConversionContextStack>(); + } +} diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3.DependencyInjection/Speckle.Converters.ArcGIS3.DependencyInjection.csproj b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3.DependencyInjection/Speckle.Converters.ArcGIS3.DependencyInjection.csproj new file mode 100644 index 0000000000..3c4c103894 --- /dev/null +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3.DependencyInjection/Speckle.Converters.ArcGIS3.DependencyInjection.csproj @@ -0,0 +1,17 @@ + + + + net6.0-windows + x64 + + + + + + + + + + + + diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3.DependencyInjection/packages.lock.json b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3.DependencyInjection/packages.lock.json new file mode 100644 index 0000000000..9f83e4c13d --- /dev/null +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3.DependencyInjection/packages.lock.json @@ -0,0 +1,430 @@ +{ + "version": 2, + "dependencies": { + "net6.0-windows7.0": { + "Autofac": { + "type": "Direct", + "requested": "[5.2.0, )", + "resolved": "5.2.0", + "contentHash": "V8dBH0dsv75uDzl7Sw+HkhKDPUw2eXnlMjcSVMH+tLo2s67MpTKGyDj1pDcpR+IF2u4YRs0s3/x7R88YJzIWvg==" + }, + "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.5, )", + "resolved": "0.9.5", + "contentHash": "oU/L7pN1R7q8KkbrpQ3WJnHirPHqn+9DEA7asOcUiggV5dzVg1A/VYs7GOSusD24njxXh03tE3a2oTLOjt3cVg==" + }, + "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.AspNetCore.Http": { + "type": "Transitive", + "resolved": "2.2.2", + "contentHash": "BAibpoItxI5puk7YJbIGj95arZueM8B8M5xT1fXBn3hb3L2G3ucrZcYXv1gXdaroLbntUs8qeV8iuBrpjQsrKw==", + "dependencies": { + "Microsoft.AspNetCore.Http.Abstractions": "2.2.0", + "Microsoft.AspNetCore.WebUtilities": "2.2.0", + "Microsoft.Extensions.ObjectPool": "2.2.0", + "Microsoft.Extensions.Options": "2.2.0", + "Microsoft.Net.Http.Headers": "2.2.0" + } + }, + "Microsoft.AspNetCore.Http.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "Nxs7Z1q3f1STfLYKJSVXCs1iBl+Ya6E8o4Oy1bCxJ/rNI44E/0f6tbsrVqAWfB7jlnJfyaAtIalBVxPKUPQb4Q==", + "dependencies": { + "Microsoft.AspNetCore.Http.Features": "2.2.0", + "System.Text.Encodings.Web": "4.5.0" + } + }, + "Microsoft.AspNetCore.Http.Features": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "ziFz5zH8f33En4dX81LW84I6XrYXKf9jg6aM39cM+LffN9KJahViKZ61dGMSO2gd3e+qe5yBRwsesvyqlZaSMg==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.2.0" + } + }, + "Microsoft.AspNetCore.WebUtilities": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "9ErxAAKaDzxXASB/b5uLEkLgUWv1QbeVxyJYEHQwMaxXOeFFVkQxiq8RyfVcifLU7NR0QY0p3acqx4ZpYfhHDg==", + "dependencies": { + "Microsoft.Net.Http.Headers": "2.2.0", + "System.Text.Encodings.Web": "4.5.0" + } + }, + "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.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw==" + }, + "Microsoft.Extensions.ObjectPool": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "gA8H7uQOnM5gb+L0uTNjViHYr+hRDqCdfugheGo/MxQnuHzmhhzCBTIPm19qL1z1Xe0NEMabfcOBGv9QghlZ8g==" + }, + "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.Net.Http.Headers": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "iZNkjYqlo8sIOI0bQfpsSoMTmB/kyvmV2h225ihyZT33aTp48ZpF6qYnXxzSXmHt8DpBAwBTX+1s1UFLbYfZKg==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.2.0", + "System.Buffers": "4.5.0" + } + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "Polly": { + "type": "Transitive", + "resolved": "7.2.3", + "contentHash": "DeCY0OFbNdNxsjntr1gTXHJ5pKUwYzp04Er2LLeN3g6pWhffsGuKVfMBLe1lw7x76HrPkLxKEFxBlpRxS2nDEQ==" + }, + "Polly.Contrib.WaitAndRetry": { + "type": "Transitive", + "resolved": "1.1.1", + "contentHash": "1MUQLiSo4KDkQe6nzQRhIU05lm9jlexX5BVsbuw0SL82ynZ+GzAHQxJVDPVBboxV37Po3SG077aX8DuSy8TkaA==" + }, + "Polly.Extensions.Http": { + "type": "Transitive", + "resolved": "3.0.0", + "contentHash": "drrG+hB3pYFY7w1c3BD+lSGYvH2oIclH8GRSehgfyP5kjnFnHKQuuBhuHLv+PWyFuaTDyk/vfRpnxOzd11+J8g==", + "dependencies": { + "Polly": "7.1.0" + } + }, + "Sentry": { + "type": "Transitive", + "resolved": "3.33.0", + "contentHash": "8vbD2o6IR2wrRrkSiRbnodWGWUOqIlwYtzpjvPNOb5raJdOf+zxMwfS8f6nx9bmrTTfDj7KrCB8C/5OuicAc8A==" + }, + "Sentry.Serilog": { + "type": "Transitive", + "resolved": "3.33.0", + "contentHash": "V8BU7QGWg2qLYfNPqtuTBhC1opysny5l+Ifp6J6PhOeAxU0FssR7nYfbJVetrnLIoh2rd3DlJ6hHYYQosQYcUQ==", + "dependencies": { + "Sentry": "3.33.0", + "Serilog": "2.10.0" + } + }, + "Serilog": { + "type": "Transitive", + "resolved": "2.12.0", + "contentHash": "xaiJLIdu6rYMKfQMYUZgTy8YK7SMZjB4Yk50C/u//Z4OsvxkUfSPJy4nknfvwAC34yr13q7kcyh4grbwhSxyZg==" + }, + "Serilog.Enrichers.ClientInfo": { + "type": "Transitive", + "resolved": "1.3.0", + "contentHash": "mTc7PM+wC9Hr7LWSwqt5mmnlAr7RJs+eTb3PGPRhwdOackk95MkhUZognuxXEdlW19HAFNmEBTSBY5DfLwM8jQ==", + "dependencies": { + "Microsoft.AspNetCore.Http": "2.2.2", + "Serilog": "2.9.0" + } + }, + "Serilog.Exceptions": { + "type": "Transitive", + "resolved": "8.4.0", + "contentHash": "nc/+hUw3lsdo0zCj0KMIybAu7perMx79vu72w0za9Nsi6mWyNkGXxYxakAjWB7nEmYL6zdmhEQRB4oJ2ALUeug==", + "dependencies": { + "Serilog": "2.8.0", + "System.Reflection.TypeExtensions": "4.7.0" + } + }, + "Serilog.Formatting.Compact": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "pNroKVjo+rDqlxNG5PXkRLpfSCuDOBY0ri6jp9PLe505ljqwhwZz8ospy2vWhQlFu5GkIesh3FcDs4n7sWZODA==", + "dependencies": { + "Serilog": "2.8.0" + } + }, + "Serilog.Sinks.Console": { + "type": "Transitive", + "resolved": "4.1.0", + "contentHash": "K6N5q+5fetjnJPvCmkWOpJ/V8IEIoMIB1s86OzBrbxwTyHxdx3pmz4H+8+O/Dc/ftUX12DM1aynx/dDowkwzqg==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "Serilog.Sinks.File": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "uwV5hdhWPwUH1szhO8PJpFiahqXmzPzJT/sOijH/kFgUx+cyoDTMM8MHD0adw9+Iem6itoibbUXHYslzXsLEAg==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "Serilog.Sinks.PeriodicBatching": { + "type": "Transitive", + "resolved": "3.1.0", + "contentHash": "NDWR7m3PalVlGEq3rzoktrXikjFMLmpwF0HI4sowo8YDdU+gqPlTHlDQiOGxHfB0sTfjPA9JjA7ctKG9zqjGkw==", + "dependencies": { + "Serilog": "2.0.0" + } + }, + "Serilog.Sinks.Seq": { + "type": "Transitive", + "resolved": "5.2.2", + "contentHash": "1Csmo5ua7NKUe0yXUx+zsRefjAniPWcXFhUXxXG8pwo0iMiw2gjn9SOkgYnnxbgWqmlGv236w0N/dHc2v5XwMg==", + "dependencies": { + "Serilog": "2.12.0", + "Serilog.Formatting.Compact": "1.1.0", + "Serilog.Sinks.File": "5.0.0", + "Serilog.Sinks.PeriodicBatching": "3.1.0" + } + }, + "SerilogTimings": { + "type": "Transitive", + "resolved": "3.0.1", + "contentHash": "Zs28eTgszAMwpIrbBnWHBI50yuxL50p/dmAUWmy75+axdZYK/Sjm5/5m1N/CisR8acJUhTVcjPZrsB1P5iv0Uw==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "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.Buffers": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "pL2ChpaRRWI/p4LXyy4RgeWlYF2sgfj/pnVMvBqwNFr5cXg7CXNnWZWxrOONLg8VGdFB8oB+EG2Qw4MLgTOe+A==" + }, + "System.ComponentModel.Annotations": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "UxYQ3FGUOtzJ7LfSdnYSFd7+oEv6M8NgUatatIN2HxNtDdlcvFAf+VIq4Of9cDMJEJC0aSRv/x898RYhB4Yppg==" + }, + "System.DoubleNumerics": { + "type": "Transitive", + "resolved": "3.1.3", + "contentHash": "KRKEM/L3KBodjA9VOg3EifFVWUY6EOqaMB05UvPEDm7Zeby/kZW+4kdWUEPzW6xtkwf46p661L9NrbeeQhtLzw==", + "dependencies": { + "NETStandard.Library": "1.6.1" + } + }, + "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.TypeExtensions": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "VybpaOQQhqE6siHppMktjfGBw1GCwvCqiufqmP8F1nj7fTUNtW35LOEt3UZTEsECfo+ELAl/9o9nJx3U91i7vA==" + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Zh8t8oqolRaFa9vmOZfdQm/qKejdqz0J9kr7o2Fu0vPeoH3BL1EOXipKWwkWtLT1JPzjByrF19fGuFlNbmPpiw==" + }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "Xg4G4Indi4dqP1iuAiMSwpiWS54ZghzR644OtsRCm/m/lBMG8dUBhLVN7hLm8NNrNTR+iGbshCPTwrvxZPlm4g==" + }, + "speckle.autofac": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "[7.0.0, )" + } + }, + "speckle.converters.arcgis3": { + "type": "Project", + "dependencies": { + "Esri.ArcGISPro.Extensions30": "[3.2.0.49743, )", + "Speckle.Converters.Common": "[2.0.999-local, )" + } + }, + "speckle.converters.common": { + "type": "Project", + "dependencies": { + "Speckle.Autofac": "[2.0.999-local, )", + "Speckle.Objects": "[3.0.1-alpha.14, )" + } + }, + "speckle.converters.common.dependencyinjection": { + "type": "Project", + "dependencies": { + "Speckle.Autofac": "[2.0.999-local, )", + "Speckle.Converters.Common": "[2.0.999-local, )" + } + }, + "Esri.ArcGISPro.Extensions30": { + "type": "CentralTransitive", + "requested": "[3.2.0.49743, )", + "resolved": "3.2.0.49743", + "contentHash": "fmnYm+mD14Cz0Uqh1ij37SfLJerkyFHK5581y5tXT/l3H2ZvUmVuuxjYquXzyzj9p7IexQzMW4xCpxe+mD922g==" + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "CentralTransitive", + "requested": "[7.0.0, )", + "resolved": "7.0.0", + "contentHash": "kmn78+LPVMOWeITUjIlfxUPDsI0R6G0RkeAMBmQxAJ7vBJn4q2dTva7pWi65ceN5vPGjJ9q/Uae2WKgvfktJAw==" + }, + "Speckle.Core": { + "type": "CentralTransitive", + "requested": "[3.0.1-alpha.14, )", + "resolved": "3.0.1-alpha.14", + "contentHash": "RzQPVIGFFkKvG56YLr8ACtiwdWJE6IJ9vCQ4qHa0PIsUEpfzAIAi59jnzqtByOFC0FiFrFPow9bkfzylaZorAA==", + "dependencies": { + "GraphQL.Client": "6.0.0", + "Microsoft.CSharp": "4.7.0", + "Microsoft.Data.Sqlite": "7.0.5", + "Polly": "7.2.3", + "Polly.Contrib.WaitAndRetry": "1.1.1", + "Polly.Extensions.Http": "3.0.0", + "Sentry": "3.33.0", + "Sentry.Serilog": "3.33.0", + "Serilog": "2.12.0", + "Serilog.Enrichers.ClientInfo": "1.3.0", + "Serilog.Exceptions": "8.4.0", + "Serilog.Sinks.Console": "4.1.0", + "Serilog.Sinks.Seq": "5.2.2", + "SerilogTimings": "3.0.1", + "Speckle.Newtonsoft.Json": "13.0.2", + "System.DoubleNumerics": "3.1.3" + } + }, + "Speckle.Objects": { + "type": "CentralTransitive", + "requested": "[3.0.1-alpha.14, )", + "resolved": "3.0.1-alpha.14", + "contentHash": "z38LGryMvh7iU1uBW+4uo5DwsB3CwRgLt2uFexWFx3mPSid+A0l5XcJzOgLwgFhNl6B42Ryz4ezBsddTp1Uc/g==", + "dependencies": { + "Speckle.Core": "3.0.1-alpha.14" + } + } + } + } +} \ No newline at end of file diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ArcGISConversionContextStack.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ArcGISConversionContextStack.cs new file mode 100644 index 0000000000..1649d205dc --- /dev/null +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ArcGISConversionContextStack.cs @@ -0,0 +1,110 @@ +using System.Diagnostics.CodeAnalysis; +using ArcGIS.Core.Data.DDL; +using ArcGIS.Core.Data; +using ArcGIS.Desktop.Core; +using ArcGIS.Desktop.Framework.Threading.Tasks; +using ArcGIS.Desktop.Mapping; +using Speckle.Converters.Common; + +namespace Speckle.Converters.ArcGIS3; + +public class ArcGISDocument +{ + public Project Project { get; } + public Map Map { get; } + public Uri SpeckleDatabasePath { get; } + + public ArcGISDocument() + { + Project = Project.Current; + Map = MapView.Active.Map; + SpeckleDatabasePath = EnsureOrAddSpeckleDatabase(); + } + + private const string FGDB_NAME = "Speckle.gdb"; + + public Uri EnsureOrAddSpeckleDatabase() + { + return AddDatabaseToProject(GetDatabasePath()); + } + + public Uri GetDatabasePath() + { + try + { + var parentDirectory = Directory.GetParent(Project.Current.URI); + if (parentDirectory == null) + { + throw new ArgumentException($"Project directory {Project.Current.URI} not found"); + } + var fGdbPath = new Uri(parentDirectory.FullName); + return new Uri($"{fGdbPath}/{FGDB_NAME}"); + } + catch (Exception ex) + when (ex + is IOException + or UnauthorizedAccessException + or ArgumentException + or NotSupportedException + or System.Security.SecurityException + ) + { + throw; + } + } + + public Uri AddDatabaseToProject(Uri databasePath) + { + // Create a FileGeodatabaseConnectionPath with the name of the file geodatabase you wish to create + FileGeodatabaseConnectionPath fileGeodatabaseConnectionPath = new(databasePath); + // Create actual database in the specified Path unless already exists + try + { + Geodatabase geodatabase = SchemaBuilder.CreateGeodatabase(fileGeodatabaseConnectionPath); + geodatabase.Dispose(); + } + catch (ArcGIS.Core.Data.Exceptions.GeodatabaseWorkspaceException) + { + // geodatabase already exists, do nothing + } + + // Add a folder connection to a project + var parentFolder = Path.GetDirectoryName(databasePath.AbsolutePath); + if (parentFolder == null) + { + // POC: customize the exception type + throw new ArgumentException($"Invalid path: {databasePath}"); + } + var fGdbName = databasePath.Segments[^1]; + Item folderToAdd = ItemFactory.Instance.Create(parentFolder); + // POC: QueuedTask + QueuedTask.Run(() => Project.Current.AddItem(folderToAdd as IProjectItem)); + + // Add a file geodatabase or a SQLite or enterprise database connection to a project + var gdbToAdd = folderToAdd + .GetItems() + .FirstOrDefault(folderItem => folderItem.Name.Equals(fGdbName, StringComparison.Ordinal)); + if (gdbToAdd is not null) + { + // POC: QueuedTask + var addedGeodatabase = QueuedTask.Run(() => Project.Current.AddItem(gdbToAdd as IProjectItem)); + } + + return databasePath; + } +} + +// POC: Suppressed naming warning for now, but we should evaluate if we should follow this or disable it. +[SuppressMessage( + "Naming", + "CA1711:Identifiers should not have incorrect suffix", + Justification = "Name ends in Stack but it is in fact a Stack, just not inheriting from `System.Collections.Stack`" +)] +public class ArcGISConversionContextStack : ConversionContextStack +{ + public ArcGISConversionContextStack( + IHostToSpeckleUnitConverter unitConverter, + ArcGISDocument arcGisDocument + ) + : base(arcGisDocument, MapView.Active.Map.SpatialReference.Unit, unitConverter) { } +} diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ArcGISToSpeckleUnitConverter.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ArcGISToSpeckleUnitConverter.cs new file mode 100644 index 0000000000..44d9786b8f --- /dev/null +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ArcGISToSpeckleUnitConverter.cs @@ -0,0 +1,41 @@ +using ArcGIS.Core.Geometry; +using Speckle.Converters.Common; +using Speckle.Core.Kits; +using Speckle.Core.Logging; // POC: boy do I think this is the wrong place for SpeckleException! + +namespace Speckle.Converters.ArcGIS3; + +public class ArcGISToSpeckleUnitConverter : IHostToSpeckleUnitConverter +{ + private static readonly IReadOnlyDictionary s_unitMapping = Create(); + + private static IReadOnlyDictionary Create() + { + // POC: we should have a unit test to confirm these are as expected and don't change + // more units: https://pro.arcgis.com/en/pro-app/latest/sdk/api-reference/topic8349.html + var dict = new Dictionary(); + dict[LinearUnit.Millimeters.FactoryCode] = Units.Millimeters; + dict[LinearUnit.Centimeters.FactoryCode] = Units.Centimeters; + dict[LinearUnit.Meters.FactoryCode] = Units.Meters; + dict[LinearUnit.Kilometers.FactoryCode] = Units.Kilometers; + dict[LinearUnit.Inches.FactoryCode] = Units.Inches; + dict[LinearUnit.Feet.FactoryCode] = Units.Feet; + dict[LinearUnit.Yards.FactoryCode] = Units.Yards; + dict[LinearUnit.Miles.FactoryCode] = Units.Miles; + dict[9003] = Units.USFeet; + return dict; + } + + public string ConvertOrThrow(Unit hostUnit) + { + int linearUnit = LinearUnit.CreateLinearUnit(hostUnit.Wkt).FactoryCode; + + if (s_unitMapping.TryGetValue(linearUnit, out string? value)) + { + return value; + } + + // POC: probably would prefer something more specific + throw new SpeckleException($"The Unit System \"{hostUnit}\" is unsupported."); + } +} diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/FakeTopLevelConverter.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/FakeTopLevelConverter.cs new file mode 100644 index 0000000000..1c02e2120b --- /dev/null +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/FakeTopLevelConverter.cs @@ -0,0 +1,17 @@ +using Objects.Geometry; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; + +namespace Speckle.Converters.ArcGIS3; + +[NameAndRankValue(nameof(String), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class FakeTopLevelConverter : IToSpeckleTopLevelConverter, ITypedConverter +{ + public Base Convert(object target) => Convert((String)target); + + public Point Convert(String target) + { + return new Point(0, 0, 100) { ["customText"] = target }; + } +} diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/GlobalUsings.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/GlobalUsings.cs new file mode 100644 index 0000000000..3bdba69b4f --- /dev/null +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/GlobalUsings.cs @@ -0,0 +1,4 @@ +global using SOG = Objects.Geometry; +global using SGIS = Objects.GIS; +// global using SOP = Objects.Primitive; +global using ACG = ArcGIS.Core.Geometry; diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Speckle.Converters.ArcGIS3.csproj b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Speckle.Converters.ArcGIS3.csproj new file mode 100644 index 0000000000..9d4668fbe4 --- /dev/null +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Speckle.Converters.ArcGIS3.csproj @@ -0,0 +1,18 @@ + + + + net6.0-windows + x64 + true + + + + + + + + + + + diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/Raw/CurveToHostConverter.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/Raw/CurveToHostConverter.cs new file mode 100644 index 0000000000..57bd7b3c83 --- /dev/null +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/Raw/CurveToHostConverter.cs @@ -0,0 +1,52 @@ +using Objects; +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.ArcGIS3.ToHost.Raw; + +public class CurveToHostConverter : ITypedConverter +{ + private readonly ITypedConverter _lineConverter; + private readonly ITypedConverter _arcConverter; + private readonly ITypedConverter _ellipseConverter; + private readonly ITypedConverter _circleConverter; + private readonly ITypedConverter _polylineConverter; + private readonly ITypedConverter _polyCurveConverter; + + public CurveToHostConverter( + ITypedConverter lineConverter, + ITypedConverter arcConverter, + ITypedConverter ellipseConverter, + ITypedConverter circleConverter, + ITypedConverter polylineConverter, + ITypedConverter polyCurveConverter + ) + { + _lineConverter = lineConverter; + _arcConverter = arcConverter; + _ellipseConverter = ellipseConverter; + _circleConverter = circleConverter; + _polylineConverter = polylineConverter; + _polyCurveConverter = polyCurveConverter; + } + + /// + /// Converts a given ICurve object to an ACG.Polyline object. + /// + /// The ICurve object to convert. + /// The converted RG.Curve object. + /// Thrown when the conversion is not supported for the given type of curve. + /// ⚠️ This conversion does NOT perform scaling. + public ACG.Polyline Convert(ICurve target) => + target switch + { + SOG.Line line => _lineConverter.Convert(line), + SOG.Arc arc => _arcConverter.Convert(arc), + SOG.Circle circle => _circleConverter.Convert(circle), + SOG.Ellipse ellipse => _ellipseConverter.Convert(ellipse), + SOG.Spiral spiral => _polylineConverter.Convert(spiral.displayValue), + SOG.Polyline polyline => _polylineConverter.Convert(polyline), + SOG.Curve curve => _polylineConverter.Convert(curve.displayValue), + SOG.Polycurve polyCurve => _polyCurveConverter.Convert(polyCurve), + _ => throw new NotSupportedException($"Unable to convert curves of type {target.GetType().Name}") + }; +} diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/Raw/FeatureClassToHostConverter.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/Raw/FeatureClassToHostConverter.cs new file mode 100644 index 0000000000..d491ca2a1e --- /dev/null +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/Raw/FeatureClassToHostConverter.cs @@ -0,0 +1,151 @@ +using ArcGIS.Core.Data; +using ArcGIS.Core.Data.DDL; +using ArcGIS.Core.Data.Exceptions; +using Objects.GIS; +using Speckle.Converters.ArcGIS3.Utils; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; +using FieldDescription = ArcGIS.Core.Data.DDL.FieldDescription; + +namespace Speckle.Converters.ArcGIS3.ToHost.Raw; + +public class FeatureClassToHostConverter : ITypedConverter +{ + private readonly ITypedConverter, ACG.Geometry> _gisGeometryConverter; + private readonly IFeatureClassUtils _featureClassUtils; + private readonly IArcGISFieldUtils _fieldsUtils; + private readonly IConversionContextStack _contextStack; + + public FeatureClassToHostConverter( + ITypedConverter, ACG.Geometry> gisGeometryConverter, + IFeatureClassUtils featureClassUtils, + IArcGISFieldUtils fieldsUtils, + IConversionContextStack contextStack + ) + { + _gisGeometryConverter = gisGeometryConverter; + _featureClassUtils = featureClassUtils; + _fieldsUtils = fieldsUtils; + _contextStack = contextStack; + } + + private List RecoverOutdatedGisFeatures(VectorLayer target) + { + List gisFeatures = new(); + foreach (Base baseElement in target.elements) + { + if (baseElement is GisFeature feature) + { + gisFeatures.Add(feature); + } + else + { + if ( + baseElement["geometry"] is List originalGeometries + && baseElement["attributes"] is Base originalAttrs + && (baseElement["displayValue"] is List || baseElement["displayValue"] == null) + ) + { + var originalDisplayVal = baseElement["displayValue"]; + List geometry = originalGeometries.Select(x => (Base)x).ToList(); + Base attributes = originalAttrs; + List? displayValue = + originalDisplayVal == null + ? new List() + : ((List)originalDisplayVal).Select(x => (Base)x).ToList(); + GisFeature newfeature = + new() + { + geometry = geometry, + attributes = attributes, + displayValue = displayValue + }; + gisFeatures.Add(newfeature); + } + else + { + gisFeatures.Add((GisFeature)baseElement); + } + } + } + return gisFeatures; + } + + public FeatureClass Convert(VectorLayer target) + { + ACG.GeometryType geomType = _featureClassUtils.GetLayerGeometryType(target); + + FileGeodatabaseConnectionPath fileGeodatabaseConnectionPath = + new(_contextStack.Current.Document.SpeckleDatabasePath); + Geodatabase geodatabase = new(fileGeodatabaseConnectionPath); + SchemaBuilder schemaBuilder = new(geodatabase); + + // create Spatial Reference (i.e. Coordinate Reference System - CRS) + string wktString = string.Empty; + if (target.crs is not null && target.crs.wkt is not null) + { + wktString = target.crs.wkt.ToString(); + } + ACG.SpatialReference spatialRef = ACG.SpatialReferenceBuilder.CreateSpatialReference(wktString); + + // create Fields + List fields = _fieldsUtils.GetFieldsFromSpeckleLayer(target); + + // getting rid of forbidden symbols in the class name: adding a letter in the beginning + // https://pro.arcgis.com/en/pro-app/3.1/tool-reference/tool-errors-and-warnings/001001-010000/tool-errors-and-warnings-00001-00025-000020.htm + string featureClassName = "speckleID_" + target.id; + + // delete FeatureClass if already exists + foreach (FeatureClassDefinition fClassDefinition in geodatabase.GetDefinitions()) + { + // will cause GeodatabaseCatalogDatasetException if doesn't exist in the database + if (fClassDefinition.GetName() == featureClassName) + { + FeatureClassDescription existingDescription = new(fClassDefinition); + schemaBuilder.Delete(existingDescription); + schemaBuilder.Build(); + } + } + + // Create FeatureClass + try + { + // POC: make sure class has a valid crs + ShapeDescription shpDescription = new(geomType, spatialRef) { HasZ = true }; + FeatureClassDescription featureClassDescription = new(featureClassName, fields, shpDescription); + FeatureClassToken featureClassToken = schemaBuilder.Create(featureClassDescription); + } + catch (ArgumentException) + { + // POC: review the exception + // if name has invalid characters/combinations + throw; + } + bool buildStatus = schemaBuilder.Build(); + if (!buildStatus) + { + // POC: log somewhere the error in building the feature class + IReadOnlyList errors = schemaBuilder.ErrorMessages; + } + + try + { + FeatureClass newFeatureClass = geodatabase.OpenDataset(featureClassName); + // backwards compatibility: + List gisFeatures = RecoverOutdatedGisFeatures(target); + // Add features to the FeatureClass + geodatabase.ApplyEdits(() => + { + _featureClassUtils.AddFeaturesToFeatureClass(newFeatureClass, gisFeatures, fields, _gisGeometryConverter); + }); + + return newFeatureClass; + } + catch (GeodatabaseException) + { + // POC: review the exception + throw; + } + } +} diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/Raw/GeometryToHostConverter.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/Raw/GeometryToHostConverter.cs new file mode 100644 index 0000000000..9ec0f3d279 --- /dev/null +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/Raw/GeometryToHostConverter.cs @@ -0,0 +1,60 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; + +namespace Speckle.Converters.ArcGIS3.ToHost.Raw; + +public class GeometryToHostConverter : ITypedConverter, ACG.Geometry> +{ + 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 + ) + { + _polylineConverter = polylineConverter; + _multipointConverter = multipointConverter; + _polygon3dConverter = polygon3dConverter; + _polygonConverter = polygonConverter; + _multipatchConverter = multipatchConverter; + } + + public ACG.Geometry Convert(IReadOnlyList target) + { + try + { + if (target.Count > 0) + { + switch (target[0]) + { + case SOG.Point point: + return _multipointConverter.Convert(target.Cast().ToList()); + case SOG.Polyline polyline: + return _polylineConverter.Convert(target.Cast().ToList()); + case SGIS.PolygonGeometry3d geometry3d: + return _polygon3dConverter.Convert(target.Cast().ToList()); + case SGIS.PolygonGeometry geometry: + return _polygonConverter.Convert(target.Cast().ToList()); + case SGIS.GisMultipatchGeometry mesh: + return _multipatchConverter.Convert(target.Cast().ToList()); + default: + throw new NotSupportedException($"No conversion found for type {target[0]}"); + } + } + throw new NotSupportedException($"Feature contains no geometry"); + } + catch (SpeckleConversionException e) + { + Console.WriteLine(e); + throw; // log errors + } + } +} diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/Raw/MeshListToHostConverter.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/Raw/MeshListToHostConverter.cs new file mode 100644 index 0000000000..941f6ac70f --- /dev/null +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/Raw/MeshListToHostConverter.cs @@ -0,0 +1,52 @@ +using Objects.Utils; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.ArcGIS3.ToHost.Raw; + +public class MeshListToHostConverter : ITypedConverter, ACG.Multipatch> +{ + private readonly ITypedConverter _pointConverter; + private readonly IConversionContextStack _contextStack; + + public MeshListToHostConverter( + ITypedConverter pointConverter, + IConversionContextStack contextStack + ) + { + _pointConverter = pointConverter; + _contextStack = contextStack; + } + + public ACG.Multipatch Convert(List target) + { + if (target.Count == 0) + { + throw new SpeckleConversionException("Feature contains no geometries"); + } + ACG.MultipatchBuilderEx multipatchPart = new(_contextStack.Current.Document.Map.SpatialReference); + foreach (SOG.Mesh part in target) + { + part.TriangulateMesh(); + ACG.Patch newPatch = multipatchPart.MakePatch(ACG.PatchType.Triangles); + for (int i = 0; i < part.faces.Count; i++) + { + if (i % 4 == 0) + { + continue; + } + int ptIndex = part.faces[i]; + newPatch.AddPoint( + _pointConverter.Convert( + new SOG.Point(part.vertices[ptIndex * 3], part.vertices[ptIndex * 3 + 1], part.vertices[ptIndex * 3 + 2]) + { + units = part.units + } + ) + ); + } + multipatchPart.Patches.Add(newPatch); + } + return multipatchPart.ToGeometry(); + } +} diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/Raw/MultipatchListToHostConverter.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/Raw/MultipatchListToHostConverter.cs new file mode 100644 index 0000000000..44c961e9bc --- /dev/null +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/Raw/MultipatchListToHostConverter.cs @@ -0,0 +1,37 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; + +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 SpeckleConversionException("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]) + ) + ); + } + multipatchPart.Patches.Add(newPatch); + } + return multipatchPart.ToGeometry(); + } +} diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/Raw/PointListToHostConverter.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/Raw/PointListToHostConverter.cs new file mode 100644 index 0000000000..e1204f36c5 --- /dev/null +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/Raw/PointListToHostConverter.cs @@ -0,0 +1,28 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.ArcGIS3.ToHost.Raw; + +public class PointListToHostConverter : ITypedConverter, ACG.Multipoint> +{ + private readonly ITypedConverter _pointConverter; + + public PointListToHostConverter(ITypedConverter pointConverter) + { + _pointConverter = pointConverter; + } + + public ACG.Multipoint Convert(List target) + { + if (target.Count == 0) + { + throw new SpeckleConversionException("Feature contains no geometries"); + } + List pointList = new(); + foreach (SOG.Point pt in target) + { + pointList.Add(_pointConverter.Convert(pt)); + } + return new ACG.MultipointBuilderEx(pointList, ACG.AttributeFlags.HasZ).ToGeometry(); + } +} diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/Raw/PointSingleToHostConverter.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/Raw/PointSingleToHostConverter.cs new file mode 100644 index 0000000000..75bb57b476 --- /dev/null +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/Raw/PointSingleToHostConverter.cs @@ -0,0 +1,29 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Kits; +using Speckle.Core.Models; + +namespace Speckle.Converters.ArcGIS3.ToHost.Raw; + +public class PointToHostConverter : ITypedConverter +{ + private readonly IConversionContextStack _contextStack; + + public PointToHostConverter(IConversionContextStack contextStack) + { + _contextStack = contextStack; + } + + public object Convert(Base target) => Convert((SOG.Point)target); + + public ACG.MapPoint Convert(SOG.Point target) + { + double scaleFactor = Units.GetConversionFactor(target.units, _contextStack.Current.SpeckleUnits); + return new ACG.MapPointBuilderEx( + target.x * scaleFactor, + target.y * scaleFactor, + target.z * scaleFactor, + _contextStack.Current.Document.Map.SpatialReference + ).ToGeometry(); + } +} diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/Raw/PointcloudLayerToHostConverter.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/Raw/PointcloudLayerToHostConverter.cs new file mode 100644 index 0000000000..fa04b92fa0 --- /dev/null +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/Raw/PointcloudLayerToHostConverter.cs @@ -0,0 +1,17 @@ +using ArcGIS.Desktop.Mapping; +using Objects.GIS; +using Speckle.Converters.Common.Objects; +using Speckle.Core.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/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/Raw/Polygon3dListToHostConverter.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/Raw/Polygon3dListToHostConverter.cs new file mode 100644 index 0000000000..5d68c5be91 --- /dev/null +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/Raw/Polygon3dListToHostConverter.cs @@ -0,0 +1,51 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; + +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 SpeckleConversionException("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/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/Raw/PolygonListToHostConverter.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/Raw/PolygonListToHostConverter.cs new file mode 100644 index 0000000000..6acc30b826 --- /dev/null +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/Raw/PolygonListToHostConverter.cs @@ -0,0 +1,38 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; + +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 SpeckleConversionException("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/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/Raw/PolylineListToHostConverter.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/Raw/PolylineListToHostConverter.cs new file mode 100644 index 0000000000..d512a23d94 --- /dev/null +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/Raw/PolylineListToHostConverter.cs @@ -0,0 +1,29 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.ArcGIS3.ToHost.Raw; + +public class PolylineListToHostConverter : ITypedConverter, ACG.Polyline> +{ + private readonly ITypedConverter _polylineConverter; + + public PolylineListToHostConverter(ITypedConverter polylineConverter) + { + _polylineConverter = polylineConverter; + } + + public ACG.Polyline Convert(List target) + { + if (target.Count == 0) + { + throw new SpeckleConversionException("Feature contains no geometries"); + } + List polyList = new(); + foreach (SOG.Polyline poly in target) + { + ACG.Polyline arcgisPoly = _polylineConverter.Convert(poly); + polyList.Add(arcgisPoly); + } + return new ACG.PolylineBuilderEx(polyList, ACG.AttributeFlags.HasZ).ToGeometry(); + } +} diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/Raw/TableToHostConverter.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/Raw/TableToHostConverter.cs new file mode 100644 index 0000000000..8af12954d6 --- /dev/null +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/Raw/TableToHostConverter.cs @@ -0,0 +1,92 @@ +using ArcGIS.Core.Data; +using ArcGIS.Core.Data.DDL; +using ArcGIS.Core.Data.Exceptions; +using Objects.GIS; +using Speckle.Converters.ArcGIS3.Utils; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using FieldDescription = ArcGIS.Core.Data.DDL.FieldDescription; + +namespace Speckle.Converters.ArcGIS3.ToHost.Raw; + +public class TableLayerToHostConverter : ITypedConverter +{ + private readonly IFeatureClassUtils _featureClassUtils; + private readonly IArcGISFieldUtils _fieldsUtils; + private readonly IConversionContextStack _contextStack; + + public TableLayerToHostConverter( + IFeatureClassUtils featureClassUtils, + IConversionContextStack contextStack, + IArcGISFieldUtils fieldsUtils + ) + { + _featureClassUtils = featureClassUtils; + _contextStack = contextStack; + _fieldsUtils = fieldsUtils; + } + + public Table Convert(VectorLayer target) + { + FileGeodatabaseConnectionPath fileGeodatabaseConnectionPath = + new(_contextStack.Current.Document.SpeckleDatabasePath); + Geodatabase geodatabase = new(fileGeodatabaseConnectionPath); + SchemaBuilder schemaBuilder = new(geodatabase); + + // create Fields + List fields = _fieldsUtils.GetFieldsFromSpeckleLayer(target); + + // getting rid of forbidden symbols in the class name: adding a letter in the beginning + // https://pro.arcgis.com/en/pro-app/3.1/tool-reference/tool-errors-and-warnings/001001-010000/tool-errors-and-warnings-00001-00025-000020.htm + string featureClassName = "speckleID_" + target.id; + + // delete FeatureClass if already exists + foreach (TableDefinition fClassDefinition in geodatabase.GetDefinitions()) + { + // will cause GeodatabaseCatalogDatasetException if doesn't exist in the database + if (fClassDefinition.GetName() == featureClassName) + { + TableDescription existingDescription = new(fClassDefinition); + schemaBuilder.Delete(existingDescription); + schemaBuilder.Build(); + } + } + + // Create Table + try + { + TableDescription featureClassDescription = new(featureClassName, fields); + TableToken featureClassToken = schemaBuilder.Create(featureClassDescription); + } + catch (ArgumentException) + { + // POC: review the exception + // if name has invalid characters/combinations + throw; + } + bool buildStatus = schemaBuilder.Build(); + if (!buildStatus) + { + // POC: log somewhere the error in building the feature class + IReadOnlyList errors = schemaBuilder.ErrorMessages; + } + + try + { + Table newFeatureClass = geodatabase.OpenDataset(featureClassName); + // Add features to the FeatureClass + List gisFeatures = target.elements.Select(x => (GisFeature)x).ToList(); + geodatabase.ApplyEdits(() => + { + _featureClassUtils.AddFeaturesToTable(newFeatureClass, gisFeatures, fields); + }); + + return newFeatureClass; + } + catch (GeodatabaseException) + { + // POC: review the exception + throw; + } + } +} diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/ArcToHostConverter.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/ArcToHostConverter.cs new file mode 100644 index 0000000000..f334713612 --- /dev/null +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/ArcToHostConverter.cs @@ -0,0 +1,47 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; + +namespace Speckle.Converters.ArcGIS3.ToHost.TopLevel; + +[NameAndRankValue(nameof(SOG.Arc), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class ArcToHostConverter : IToHostTopLevelConverter, ITypedConverter +{ + private readonly ITypedConverter _pointConverter; + private readonly IConversionContextStack _contextStack; + + public ArcToHostConverter( + ITypedConverter pointConverter, + IConversionContextStack contextStack + ) + { + _pointConverter = pointConverter; + _contextStack = contextStack; + } + + public object Convert(Base target) => Convert((SOG.Arc)target); + + public ACG.Polyline Convert(SOG.Arc target) + { + if (target.startPoint.z != target.midPoint.z || target.startPoint.z != target.endPoint.z) + { + throw new ArgumentException("Only Arc in XY plane are supported"); + } + ACG.MapPoint fromPt = _pointConverter.Convert(target.startPoint); + ACG.MapPoint toPt = _pointConverter.Convert(target.endPoint); + ACG.MapPoint midPt = _pointConverter.Convert(target.midPoint); + + ACG.EllipticArcSegment segment = ACG.EllipticArcBuilderEx.CreateCircularArc( + fromPt, + toPt, + new ACG.Coordinate2D(midPt), + _contextStack.Current.Document.Map.SpatialReference + ); + + return new ACG.PolylineBuilderEx( + segment, + ACG.AttributeFlags.HasZ, + _contextStack.Current.Document.Map.SpatialReference + ).ToGeometry(); + } +} diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/CircleToHostConverter.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/CircleToHostConverter.cs new file mode 100644 index 0000000000..089ac64b1b --- /dev/null +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/CircleToHostConverter.cs @@ -0,0 +1,55 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Kits; +using Speckle.Core.Models; + +namespace Speckle.Converters.ArcGIS3.ToHost.TopLevel; + +[NameAndRankValue(nameof(SOG.Circle), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class CircleToHostConverter : IToHostTopLevelConverter, ITypedConverter +{ + private readonly ITypedConverter _pointConverter; + private readonly IConversionContextStack _contextStack; + + public CircleToHostConverter( + ITypedConverter pointConverter, + IConversionContextStack contextStack + ) + { + _pointConverter = pointConverter; + _contextStack = contextStack; + } + + public object Convert(Base target) => Convert((SOG.Circle)target); + + public ACG.Polyline Convert(SOG.Circle target) + { + if (target.radius == null) + { + throw new SpeckleConversionException("Conversion failed: Circle doesn't have a radius"); + } + if ( + target.plane.normal.x != 0 || target.plane.normal.y != 0 || target.plane.xdir.z != 0 || target.plane.ydir.z != 0 + ) + { + throw new ArgumentException("Only Circles in XY plane are supported"); + } + + // create a native ArcGIS circle segment + ACG.MapPoint centerPt = _pointConverter.Convert(target.plane.origin); + + double scaleFactor = Units.GetConversionFactor(target.units, _contextStack.Current.SpeckleUnits); + ACG.EllipticArcSegment circleSegment = ACG.EllipticArcBuilderEx.CreateCircle( + new ACG.Coordinate2D(centerPt.X, centerPt.Y), + (double)target.radius * scaleFactor, + ACG.ArcOrientation.ArcClockwise, + _contextStack.Current.Document.Map.SpatialReference + ); + + return new ACG.PolylineBuilderEx( + circleSegment, + ACG.AttributeFlags.HasZ, + _contextStack.Current.Document.Map.SpatialReference + ).ToGeometry(); + } +} diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/EllipseToHostConverter.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/EllipseToHostConverter.cs new file mode 100644 index 0000000000..d839109fb2 --- /dev/null +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/EllipseToHostConverter.cs @@ -0,0 +1,70 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Kits; +using Speckle.Core.Models; + +namespace Speckle.Converters.ArcGIS3.ToHost.TopLevel; + +[NameAndRankValue(nameof(SOG.Ellipse), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class EllipseToHostConverter : IToHostTopLevelConverter, ITypedConverter +{ + private readonly ITypedConverter _pointConverter; + private readonly IConversionContextStack _contextStack; + + public EllipseToHostConverter( + ITypedConverter pointConverter, + IConversionContextStack contextStack + ) + { + _pointConverter = pointConverter; + _contextStack = contextStack; + } + + public object Convert(Base target) => Convert((SOG.Ellipse)target); + + public ACG.Polyline Convert(SOG.Ellipse target) + { + // dummy check + if (target.firstRadius == null || target.secondRadius == null) + { + throw new ArgumentException("Ellipse is missing the first or second radius"); + } + if ( + target.plane.normal.x != 0 || target.plane.normal.y != 0 || target.plane.xdir.z != 0 || target.plane.ydir.z != 0 + ) + { + throw new ArgumentException("Only Ellipses in XY plane are supported"); + } + + ACG.MapPoint centerPt = _pointConverter.Convert(target.plane.origin); + double scaleFactor = Units.GetConversionFactor(target.units, _contextStack.Current.SpeckleUnits); + + // set default values + double angle = Math.Atan2(target.plane.xdir.y, target.plane.xdir.x); + double majorAxisRadius = (double)target.firstRadius; + double minorAxisRatio = (double)target.secondRadius / majorAxisRadius; + + // adjust if needed + if (minorAxisRatio > 1) + { + majorAxisRadius = (double)target.secondRadius; + minorAxisRatio = 1 / minorAxisRatio; + angle += Math.PI / 2; + } + + ACG.EllipticArcSegment segment = ACG.EllipticArcBuilderEx.CreateEllipse( + new ACG.Coordinate2D(centerPt), + angle, + majorAxisRadius * scaleFactor, + minorAxisRatio, + ACG.ArcOrientation.ArcCounterClockwise, + _contextStack.Current.Document.Map.SpatialReference + ); + + return new ACG.PolylineBuilderEx( + segment, + ACG.AttributeFlags.HasZ, + _contextStack.Current.Document.Map.SpatialReference + ).ToGeometry(); + } +} diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/FallbackToHostConverter.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/FallbackToHostConverter.cs new file mode 100644 index 0000000000..0f777c297c --- /dev/null +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/FallbackToHostConverter.cs @@ -0,0 +1,44 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; + +namespace Speckle.Converters.ArcGIS3.ToHost.TopLevel; + +[NameAndRankValue(nameof(DisplayableObject), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class FallbackToHostConverter : IToHostTopLevelConverter, ITypedConverter +{ + private readonly ITypedConverter, ACG.Multipatch> _meshListConverter; + private readonly ITypedConverter, ACG.Polyline> _polylineListConverter; + private readonly ITypedConverter, ACG.Multipoint> _pointListConverter; + + public FallbackToHostConverter( + ITypedConverter, ACG.Multipatch> meshListConverter, + ITypedConverter, ACG.Polyline> polylineListConverter, + ITypedConverter, ACG.Multipoint> pointListConverter + ) + { + _meshListConverter = meshListConverter; + _polylineListConverter = polylineListConverter; + _pointListConverter = pointListConverter; + } + + public object Convert(Base target) => Convert((DisplayableObject)target); + + public ACG.Geometry Convert(DisplayableObject target) + { + if (!target.displayValue.Any()) + { + throw new NotSupportedException($"Zero fallback values specified"); + } + + var first = target.displayValue[0]; + + return first switch + { + SOG.Polyline => _polylineListConverter.Convert(target.displayValue.Cast().ToList()), + SOG.Mesh => _meshListConverter.Convert(target.displayValue.Cast().ToList()), + SOG.Point => _pointListConverter.Convert(target.displayValue.Cast().ToList()), + _ => throw new NotSupportedException($"Found unsupported fallback geometry: {first.GetType()}") + }; + } +} diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/LineToHostConverter.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/LineToHostConverter.cs new file mode 100644 index 0000000000..ab562f2395 --- /dev/null +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/LineToHostConverter.cs @@ -0,0 +1,34 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; + +namespace Speckle.Converters.ArcGIS3.ToHost.TopLevel; + +[NameAndRankValue(nameof(SOG.Line), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class LineSingleToHostConverter : IToHostTopLevelConverter, ITypedConverter +{ + private readonly ITypedConverter _pointConverter; + private readonly IConversionContextStack _contextStack; + + public LineSingleToHostConverter( + ITypedConverter pointConverter, + IConversionContextStack contextStack + ) + { + _pointConverter = pointConverter; + _contextStack = contextStack; + } + + public object Convert(Base target) => Convert((SOG.Line)target); + + public ACG.Polyline Convert(SOG.Line target) + { + List originalPoints = new() { target.start, target.end }; + IEnumerable points = originalPoints.Select(x => _pointConverter.Convert(x)); + return new ACG.PolylineBuilderEx( + points, + ACG.AttributeFlags.HasZ, + _contextStack.Current.Document.Map.SpatialReference + ).ToGeometry(); + } +} diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/MeshToHostConverter.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/MeshToHostConverter.cs new file mode 100644 index 0000000000..d9f60803f5 --- /dev/null +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/MeshToHostConverter.cs @@ -0,0 +1,23 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; + +namespace Speckle.Converters.ArcGIS3.ToHost.TopLevel; + +[NameAndRankValue(nameof(SOG.Mesh), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class MeshToHostConverter : IToHostTopLevelConverter, ITypedConverter +{ + private readonly ITypedConverter, ACG.Multipatch> _meshConverter; + + public MeshToHostConverter(ITypedConverter, ACG.Multipatch> meshConverter) + { + _meshConverter = meshConverter; + } + + public object Convert(Base target) => Convert((SOG.Mesh)target); + + public ACG.Multipatch Convert(SOG.Mesh target) + { + return _meshConverter.Convert(new List { target }); + } +} diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/PointToHostConverter.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/PointToHostConverter.cs new file mode 100644 index 0000000000..4fe87d57ac --- /dev/null +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/PointToHostConverter.cs @@ -0,0 +1,18 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; + +namespace Speckle.Converters.ArcGIS3.ToHost.TopLevel; + +[NameAndRankValue(nameof(SOG.Point), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class PointToHostConverter : IToHostTopLevelConverter +{ + private readonly ITypedConverter, ACG.Multipoint> _pointConverter; + + public PointToHostConverter(ITypedConverter, ACG.Multipoint> pointConverter) + { + _pointConverter = pointConverter; + } + + public object Convert(Base target) => _pointConverter.Convert(new List { (SOG.Point)target }); +} diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/PolycurveToHostConverter.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/PolycurveToHostConverter.cs new file mode 100644 index 0000000000..ba57e347f6 --- /dev/null +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/PolycurveToHostConverter.cs @@ -0,0 +1,60 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; + +namespace Speckle.Converters.ArcGIS3.ToHost.TopLevel; + +[NameAndRankValue(nameof(SOG.Polycurve), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class PolycurveToHostConverter : IToHostTopLevelConverter, ITypedConverter +{ + private readonly ITypedConverter _pointConverter; + private readonly IRootToHostConverter _converter; + private readonly IConversionContextStack _contextStack; + + public PolycurveToHostConverter( + ITypedConverter pointConverter, + IRootToHostConverter converter, + IConversionContextStack contextStack + ) + { + _pointConverter = pointConverter; + _converter = converter; + _contextStack = contextStack; + } + + public object Convert(Base target) => Convert((SOG.Polycurve)target); + + public ACG.Polyline Convert(SOG.Polycurve target) + { + ACG.MapPoint? lastConvertedPt = null; + List segments = new(); + + foreach (var segment in target.segments) + { + ACG.Polyline converted = (ACG.Polyline)_converter.Convert((Base)segment); //CurveConverter.NotNull().Convert(segment); + List segmentPts = converted.Points.ToList(); + + if ( + lastConvertedPt != null + && segmentPts.Count > 0 + && ( + Math.Round(lastConvertedPt.X, 6) != Math.Round(segmentPts[0].X, 6) + || Math.Round(lastConvertedPt.Y, 6) != Math.Round(segmentPts[0].Y, 6) + || Math.Round(lastConvertedPt.Z, 6) != Math.Round(segmentPts[0].Z, 6) + ) + ) + { + throw new SpeckleConversionException("Polycurve segments are not in a correct sequence/orientation"); + } + + lastConvertedPt = segmentPts[^1]; + segments.Add(converted); + } + + return new ACG.PolylineBuilderEx( + segments, + ACG.AttributeFlags.HasZ, + _contextStack.Current.Document.Map.SpatialReference + ).ToGeometry(); + } +} diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/PolylineToHostConverter.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/PolylineToHostConverter.cs new file mode 100644 index 0000000000..d32e32d022 --- /dev/null +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/PolylineToHostConverter.cs @@ -0,0 +1,38 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; + +namespace Speckle.Converters.ArcGIS3.ToHost.TopLevel; + +[NameAndRankValue(nameof(SOG.Polyline), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class PolylineToHostConverter : IToHostTopLevelConverter, ITypedConverter +{ + private readonly ITypedConverter _pointConverter; + private readonly IConversionContextStack _contextStack; + + public PolylineToHostConverter( + ITypedConverter pointConverter, + IConversionContextStack contextStack + ) + { + _pointConverter = pointConverter; + _contextStack = contextStack; + } + + public object Convert(Base target) => Convert((SOG.Polyline)target); + + public ACG.Polyline Convert(SOG.Polyline target) + { + List originalPts = target.GetPoints(); + var points = originalPts.Select(x => _pointConverter.Convert(x)).ToList(); + if (target.closed && originalPts[0] != originalPts[^1]) + { + points.Add(points[0]); + } + return new ACG.PolylineBuilderEx( + points, + ACG.AttributeFlags.HasZ, + _contextStack.Current.Document.Map.SpatialReference + ).ToGeometry(); + } +} diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/RasterLayerToHostConverter.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/RasterLayerToHostConverter.cs new file mode 100644 index 0000000000..6ffb334009 --- /dev/null +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/RasterLayerToHostConverter.cs @@ -0,0 +1,18 @@ +using Objects.GIS; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.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/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/VectorLayerToHostConverter.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/VectorLayerToHostConverter.cs new file mode 100644 index 0000000000..5c7dd8c9eb --- /dev/null +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToHost/TopLevel/VectorLayerToHostConverter.cs @@ -0,0 +1,55 @@ +using ArcGIS.Core.Data; +using ArcGIS.Desktop.Mapping; +using Objects.GIS; +using Speckle.Converters.ArcGIS3.Utils; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; + +namespace Speckle.Converters.ArcGIS3.ToHost.TopLevel; + +[NameAndRankValue(nameof(VectorLayer), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class VectorLayerToHostConverter : IToHostTopLevelConverter, ITypedConverter +{ + private readonly ITypedConverter _featureClassConverter; + private readonly ITypedConverter _tableConverter; + private readonly ITypedConverter _pointcloudLayerConverter; + private readonly IFeatureClassUtils _featureClassUtils; + + public VectorLayerToHostConverter( + ITypedConverter featureClassConverter, + ITypedConverter tableConverter, + ITypedConverter pointcloudLayerConverter, + IFeatureClassUtils featureClassUtils + ) + { + _featureClassConverter = featureClassConverter; + _tableConverter = tableConverter; + _pointcloudLayerConverter = pointcloudLayerConverter; + _featureClassUtils = featureClassUtils; + } + + public object Convert(Base target) => Convert((VectorLayer)target); + + public string Convert(VectorLayer target) + { + // pointcloud layers need to be checked separately, because there is no ArcGIS Geometry type + // for Pointcloud. In ArcGIS it's a completely different layer class, so "GetLayerGeometryType" + // will return "Invalid" type + if (target.geomType == GISLayerGeometryType.POINTCLOUD) + { + return _pointcloudLayerConverter.Convert(target).Name; + } + + // check if Speckle VectorLayer should become a FeatureClass, StandaloneTable or PointcloudLayer + ACG.GeometryType geomType = _featureClassUtils.GetLayerGeometryType(target); + if (geomType != ACG.GeometryType.Unknown) // feature class + { + return _featureClassConverter.Convert(target).GetName(); + } + else // table + { + return _tableConverter.Convert(target).GetName(); + } + } +} diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/Raw/EnvelopBoxToSpeckleConverter.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/Raw/EnvelopBoxToSpeckleConverter.cs new file mode 100644 index 0000000000..0296c5d328 --- /dev/null +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/Raw/EnvelopBoxToSpeckleConverter.cs @@ -0,0 +1,55 @@ +using ArcGIS.Core.Geometry; +using Objects.Primitive; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.ArcGIS3.ToSpeckle.Raw; + +public class EnvelopToSpeckleConverter : ITypedConverter +{ + private readonly IConversionContextStack _contextStack; + private readonly ITypedConverter _pointConverter; + + public EnvelopToSpeckleConverter( + IConversionContextStack contextStack, + ITypedConverter pointConverter + ) + { + _contextStack = contextStack; + _pointConverter = pointConverter; + } + + public SOG.Box Convert(Envelope target) + { + MapPoint pointMin = new MapPointBuilderEx( + target.XMin, + target.YMin, + target.ZMin, + _contextStack.Current.Document.Map.SpatialReference + ).ToGeometry(); + MapPoint pointMax = new MapPointBuilderEx( + target.XMax, + target.YMax, + target.ZMax, + _contextStack.Current.Document.Map.SpatialReference + ).ToGeometry(); + SOG.Point minPtSpeckle = _pointConverter.Convert(pointMin); + SOG.Point maxPtSpeckle = _pointConverter.Convert(pointMax); + + var units = _contextStack.Current.SpeckleUnits; + + return new( + new SOG.Plane( + minPtSpeckle, + new SOG.Vector(0, 0, 1, units), + new SOG.Vector(1, 0, 0, units), + new SOG.Vector(0, 1, 0, units), + units + ), + new Interval(minPtSpeckle.x, maxPtSpeckle.x), + new Interval(minPtSpeckle.y, maxPtSpeckle.y), + new Interval(minPtSpeckle.z, maxPtSpeckle.z), + _contextStack.Current.SpeckleUnits + ); + } +} diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/Raw/GeometryToSpeckleBase.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/Raw/GeometryToSpeckleBase.cs new file mode 100644 index 0000000000..951432f74c --- /dev/null +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/Raw/GeometryToSpeckleBase.cs @@ -0,0 +1,50 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; + +namespace Speckle.Converters.ArcGIS3.ToSpeckle.Raw; + +public class GeometryToSpeckleBaseList : ITypedConverter> +{ + private readonly ITypedConverter _pointToSpeckleConverter; + private readonly ITypedConverter> _multiPointFeatureConverter; + private readonly ITypedConverter> _polylineFeatureConverter; + private readonly ITypedConverter> _polygonFeatureConverter; + private readonly ITypedConverter> _multipatchFeatureConverter; + + public GeometryToSpeckleBaseList( + ITypedConverter pointToSpeckleConverter, + ITypedConverter> multiPointFeatureConverter, + ITypedConverter> polylineFeatureConverter, + ITypedConverter> polygonFeatureConverter, + ITypedConverter> multipatchFeatureConverter + ) + { + _pointToSpeckleConverter = pointToSpeckleConverter; + _multiPointFeatureConverter = multiPointFeatureConverter; + _polylineFeatureConverter = polylineFeatureConverter; + _polygonFeatureConverter = polygonFeatureConverter; + _multipatchFeatureConverter = multipatchFeatureConverter; + } + + public IReadOnlyList Convert(ACG.Geometry target) + { + try + { + return target switch + { + ACG.MapPoint point => new List() { _pointToSpeckleConverter.Convert(point) }, + ACG.Multipoint multipoint => _multiPointFeatureConverter.Convert(multipoint), + ACG.Polyline polyline => _polylineFeatureConverter.Convert(polyline), + ACG.Polygon polygon => _polygonFeatureConverter.Convert(polygon), + ACG.Multipatch multipatch => _multipatchFeatureConverter.Convert(multipatch), // GisMultipatchGeometry or PolygonGeometry3d + _ => throw new NotSupportedException($"No conversion found for {target.GetType().Name}"), + }; + } + catch (SpeckleConversionException e) + { + Console.WriteLine(e); + throw; // Just rethrowing for now, Logs may be needed here. + } + } +} diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/Raw/GisFeatureToSpeckleConverter.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/Raw/GisFeatureToSpeckleConverter.cs new file mode 100644 index 0000000000..aa82bf5a2c --- /dev/null +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/Raw/GisFeatureToSpeckleConverter.cs @@ -0,0 +1,113 @@ +using ArcGIS.Core.Data; +using Speckle.Converters.ArcGIS3.Utils; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; + +namespace Speckle.Converters.ArcGIS3.ToSpeckle.Raw; + +public class GisFeatureToSpeckleConverter : ITypedConverter +{ + private readonly ITypedConverter> _geometryConverter; + + public GisFeatureToSpeckleConverter(ITypedConverter> geometryConverter) + { + _geometryConverter = geometryConverter; + } + + private List GenerateFeatureDisplayValueList(List speckleShapes) + { + List displayVal = new(); + foreach (var shp in speckleShapes) + { + if (shp is SGIS.PolygonGeometry polygon) // also will be valid for Polygon3d, as it inherits from Polygon + { + try + { + SOG.Mesh displayMesh = polygon.CreateDisplayMeshForPolygon(); + displayVal.Add(displayMesh); + } + catch (SpeckleConversionException) + { + break; + } + } + else if (shp is SGIS.GisMultipatchGeometry multipatch) + { + try + { + SOG.Mesh displayMesh = multipatch.CreateDisplayMeshForMultipatch(); + displayVal.Add(displayMesh); + } + catch (SpeckleConversionException) + { + break; + } + } + } + return displayVal; + } + + public SGIS.GisFeature Convert(Row target) + { + // get attributes + var attributes = new Base(); + bool hasGeometry = false; + string geometryField = "Shape"; + IReadOnlyList fields = target.GetFields(); + foreach (Field field in fields) + { + // POC: check for all possible reserved Shape names + if (field.FieldType == FieldType.Geometry) // ignore the field with geometry itself + { + hasGeometry = true; + geometryField = field.Name; + } + // Raster FieldType is not properly supported through API + else if ( + field.FieldType == FieldType.Raster || field.FieldType == FieldType.Blob || field.FieldType == FieldType.XML + ) + { + attributes[field.Name] = null; + } + // to not break serializer (DateOnly) and to simplify complex types + else + { + attributes[field.Name] = GISAttributeFieldType.FieldValueToSpeckle(target, field); + } + } + + // return GisFeatures that don't have geometry + if (!hasGeometry) + { + return new SGIS.GisFeature(attributes); + } + else + { + var shape = (ACG.Geometry)target[geometryField]; + var speckleShapes = _geometryConverter.Convert(shape).ToList(); + + // if geometry is primitive + if ( + speckleShapes.Count > 0 + && speckleShapes[0] is not SGIS.PolygonGeometry + && speckleShapes[0] is not SGIS.GisMultipatchGeometry + ) + { + return new SGIS.GisFeature(speckleShapes, attributes); + } + // if geometry is Polygon or Multipatch, add DisplayValue to the feature + else + { + List displayVal = GenerateFeatureDisplayValueList(speckleShapes); + // add display value ONLY if meshes were generates for all geometry parts + // otherwise those without displayValue will be lost both in Viewer and in fallback Receive conversions + if (speckleShapes.Count == displayVal.Count) + { + return new SGIS.GisFeature(speckleShapes, attributes, displayVal); + } + return new SGIS.GisFeature(speckleShapes, attributes); + } + } + } +} diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/Raw/GisRasterToSpeckleConverter.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/Raw/GisRasterToSpeckleConverter.cs new file mode 100644 index 0000000000..7d67bfd67c --- /dev/null +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/Raw/GisRasterToSpeckleConverter.cs @@ -0,0 +1,185 @@ +using ArcGIS.Core.Data.Raster; +using Objects.GIS; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; + +namespace Speckle.Converters.ArcGIS3.ToSpeckle.Raw; + +public class GisRasterToSpeckleConverter : ITypedConverter +{ + private readonly ITypedConverter> _geometryConverter; + private readonly IConversionContextStack _contextStack; + + public GisRasterToSpeckleConverter( + ITypedConverter> geometryConverter, + IConversionContextStack contextStack + ) + { + _geometryConverter = geometryConverter; + _contextStack = contextStack; + } + + public Base Convert(object target) => Convert((Raster)target); + + private List GetRasterMeshCoords(Raster target, List> pixelValsPerBand) + { + List pixelsList = pixelValsPerBand[^1]; + var extent = target.GetExtent(); + var cellSize = target.GetMeanCellSize(); + + int bandCount = target.GetBandCount(); + float xOrigin = (float)extent.XMin; + float yOrigin = (float)extent.YMax; + int xSize = target.GetWidth(); + int ySize = target.GetHeight(); + float xResolution = (float)cellSize.Item1; + float yResolution = -1 * (float)cellSize.Item2; + + List newCoords = pixelsList + .SelectMany( + (_, ind) => + new List() + { + xOrigin + xResolution * (int)Math.Floor((double)ind / ySize), + yOrigin + yResolution * (ind % ySize), + 0, + xOrigin + xResolution * ((int)Math.Floor((double)ind / ySize) + 1), + yOrigin + yResolution * (ind % ySize), + 0, + xOrigin + xResolution * (int)Math.Floor((double)ind / ySize + 1), + yOrigin + yResolution * (ind % ySize + 1), + 0, + xOrigin + xResolution * (int)Math.Floor((double)ind / ySize), + yOrigin + yResolution * (ind % ySize + 1), + 0 + } + ) + .ToList(); + return newCoords; + } + + private List GetRasterColors(int bandCount, List> pixelValsPerBand) + { + List newColors = new(); + List pixelsList = pixelValsPerBand[^1]; + if (bandCount == 3 || bandCount == 4) // RGB + { + var pixMin0 = pixelValsPerBand[0].Min(); + var pixMax0 = pixelValsPerBand[0].Max(); + var pixMin1 = pixelValsPerBand[1].Min(); + var pixMax1 = pixelValsPerBand[1].Max(); + var pixMin2 = pixelValsPerBand[2].Min(); + var pixMax2 = pixelValsPerBand[2].Max(); + newColors = pixelsList + .Select( + (_, ind) => + (255 << 24) + | (255 * (pixelValsPerBand[0][ind] - pixMin0) / (pixMax0 - pixMin0) << 16) + | (255 * (pixelValsPerBand[1][ind] - pixMin1) / (pixMax1 - pixMin1) << 8) + | 255 * (pixelValsPerBand[2][ind] - pixMin2) / (pixMax2 - pixMin2) + ) + .SelectMany(x => new List() { x, x, x, x }) + .ToList(); + } + else // greyscale + { + var pixMin = pixelValsPerBand[0].Min(); + var pixMax = pixelValsPerBand[0].Max(); + newColors = pixelsList + .Select( + (_, ind) => + (255 << 24) + | (255 * (pixelValsPerBand[0][ind] - pixMin) / (pixMax - pixMin) << 16) + | (255 * (pixelValsPerBand[0][ind] - pixMin) / (pixMax - pixMin) << 8) + | 255 * (pixelValsPerBand[0][ind] - pixMin) / (pixMax - pixMin) + ) + .SelectMany(x => new List() { x, x, x, x }) + .ToList(); + } + return newColors; + } + + public RasterElement Convert(Raster target) + { + // 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; + int xSize = target.GetWidth(); + int ySize = target.GetHeight(); + float xResolution = (float)cellSize.Item1; + float yResolution = -1 * (float)cellSize.Item2; + + var pixelType = target.GetPixelType(); // e.g. UCHAR + var xyOrigin = target.PixelToMap(0, 0); + + RasterElement rasterElement = + new(bandCount, new List(), 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(newCoords, newFaces, newColors, null, _contextStack.Current.SpeckleUnits, null) { }; + rasterElement.displayValue = new List() { mesh }; + + return rasterElement; + } +} diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/Raw/MultipatchFeatureToSpeckleConverter.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/Raw/MultipatchFeatureToSpeckleConverter.cs new file mode 100644 index 0000000000..7b60ee416f --- /dev/null +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/Raw/MultipatchFeatureToSpeckleConverter.cs @@ -0,0 +1,121 @@ +using Speckle.Converters.ArcGIS3.Utils; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; + +namespace Speckle.Converters.ArcGIS3.ToSpeckle.Raw; + +public class MultipatchFeatureToSpeckleConverter : ITypedConverter> +{ + private readonly IConversionContextStack _contextStack; + private readonly ITypedConverter _pointConverter; + + public MultipatchFeatureToSpeckleConverter( + IConversionContextStack contextStack, + ITypedConverter pointConverter + ) + { + _contextStack = contextStack; + _pointConverter = pointConverter; + } + + public IReadOnlyList Convert(ACG.Multipatch target) + { + List converted = new(); + // placeholder, needs to be declared in order to be used in the Ring patch type + SGIS.PolygonGeometry3d polygonGeom = new() { }; + + // convert and store all multipatch points per Part + List> allPoints = new(); + for (int idx = 0; idx < target.PartCount; idx++) + { + List pointList = new(); + int ptStartIndex = target.GetPatchStartPointIndex(idx); + int ptCount = target.GetPatchPointCount(idx); + for (int ptIdx = ptStartIndex; ptIdx < ptStartIndex + ptCount; ptIdx++) + { + pointList.Add(_pointConverter.Convert(target.Points[ptIdx])); + } + allPoints.Add(pointList); + } + + for (int idx = 0; idx < target.PartCount; idx++) + { + // get the patch type to get the point arrangement in the mesh + // https://pro.arcgis.com/en/pro-app/latest/sdk/api-reference/topic27403.html + ACG.PatchType patchType = target.GetPatchType(idx); + int ptCount = target.GetPatchPointCount(idx); + + if (patchType == ACG.PatchType.TriangleStrip) + { + SGIS.GisMultipatchGeometry multipatch = target.CompleteMultipatchTriangleStrip(allPoints, idx); + multipatch.units = _contextStack.Current.SpeckleUnits; + converted.Add(multipatch); + } + else if (patchType == ACG.PatchType.Triangles) + { + SGIS.GisMultipatchGeometry multipatch = target.CompleteMultipatchTriangles(allPoints, idx); + multipatch.units = _contextStack.Current.SpeckleUnits; + converted.Add(multipatch); + } + else if (patchType == ACG.PatchType.TriangleFan) + { + SGIS.GisMultipatchGeometry multipatch = target.CompleteMultipatchTriangleFan(allPoints, idx); + multipatch.units = _contextStack.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() }; + List pointCoords = allPoints[idx].SelectMany(x => new List() { x.x, x.y, x.z }).ToList(); + + SOG.Polyline polyline = new(pointCoords, _contextStack.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) + { + List pointCoords = allPoints[idx].SelectMany(x => new List() { x.x, x.y, x.z }).ToList(); + SOG.Polyline polyline = new(pointCoords, _contextStack.Current.SpeckleUnits) { }; + + // 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 }; + } + // if it's already the last part, add to list + if (idx == target.PartCount - 1) + { + converted.Add(polygonGeom); + } + } + else + { + throw new NotSupportedException($"Patch type {patchType} is not supported"); + } + } + return converted; + } +} diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/Raw/MultipointFeatureToSpeckleConverter.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/Raw/MultipointFeatureToSpeckleConverter.cs new file mode 100644 index 0000000000..c6d6299ffb --- /dev/null +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/Raw/MultipointFeatureToSpeckleConverter.cs @@ -0,0 +1,24 @@ +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.ArcGIS3.ToSpeckle.Raw; + +public class MultipointFeatureToSpeckleConverter : ITypedConverter> +{ + private readonly ITypedConverter _pointConverter; + + public MultipointFeatureToSpeckleConverter(ITypedConverter pointConverter) + { + _pointConverter = pointConverter; + } + + public IReadOnlyList Convert(ACG.Multipoint target) + { + List multipoint = new(); + foreach (ACG.MapPoint point in target.Points) + { + multipoint.Add(_pointConverter.Convert(point)); + } + + return multipoint; + } +} diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/Raw/PointToSpeckleConverter.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/Raw/PointToSpeckleConverter.cs new file mode 100644 index 0000000000..46ffb8f937 --- /dev/null +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/Raw/PointToSpeckleConverter.cs @@ -0,0 +1,39 @@ +using ArcGIS.Core.Geometry; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.ArcGIS3.ToSpeckle.Raw; + +public class PointToSpeckleConverter : ITypedConverter +{ + private readonly IConversionContextStack _contextStack; + + public PointToSpeckleConverter(IConversionContextStack contextStack) + { + _contextStack = contextStack; + } + + public SOG.Point Convert(MapPoint target) + { + try + { + if ( + GeometryEngine.Instance.Project(target, _contextStack.Current.Document.Map.SpatialReference) + is not MapPoint reprojectedPt + ) + { + throw new SpeckleConversionException( + $"Conversion to Spatial Reference {_contextStack.Current.Document.Map.SpatialReference.Name} failed" + ); + } + return new(reprojectedPt.X, reprojectedPt.Y, reprojectedPt.Z, _contextStack.Current.SpeckleUnits); + } + catch (ArgumentException ex) + { + throw new SpeckleConversionException( + $"Conversion to Spatial Reference {_contextStack.Current.Document.Map.SpatialReference} failed", + ex + ); + } + } +} diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/Raw/PolygonFeatureToSpeckleConverter.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/Raw/PolygonFeatureToSpeckleConverter.cs new file mode 100644 index 0000000000..23034aa454 --- /dev/null +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/Raw/PolygonFeatureToSpeckleConverter.cs @@ -0,0 +1,53 @@ +using Objects.GIS; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.ArcGIS3.ToSpeckle.Raw; + +public class PolygonFeatureToSpeckleConverter : ITypedConverter> +{ + private readonly ITypedConverter _segmentConverter; + + public PolygonFeatureToSpeckleConverter(ITypedConverter segmentConverter) + { + _segmentConverter = segmentConverter; + } + + 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; + + if (partCount == 0) + { + throw new SpeckleConversionException("ArcGIS Polygon contains no parts"); + } + + PolygonGeometry? polygon = null; + + // test each part for "exterior ring" + for (int idx = 0; idx < partCount; idx++) + { + ACG.ReadOnlySegmentCollection segmentCollection = target.Parts[idx]; + SOG.Polyline polyline = _segmentConverter.Convert(segmentCollection); + + bool isExteriorRing = target.IsExteriorRing(idx); + if (isExteriorRing is true) + { + polygon = new() { boundary = polyline, voids = new List() }; + polygonList.Add(polygon); + } + else // interior part + { + if (polygon == null) + { + throw new SpeckleConversionException("Invalid ArcGIS Polygon. Interior part preceeding the exterior ring."); + } + polygon.voids.Add(polyline); + } + } + + return polygonList; + } +} diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/Raw/PolylineFeatureToSpeckleConverter.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/Raw/PolylineFeatureToSpeckleConverter.cs new file mode 100644 index 0000000000..a8cc1a99d3 --- /dev/null +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/Raw/PolylineFeatureToSpeckleConverter.cs @@ -0,0 +1,48 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.ArcGIS3.ToSpeckle.Raw; + +public class PolyineFeatureToSpeckleConverter : ITypedConverter> +{ + private readonly ITypedConverter _segmentConverter; + private readonly IConversionContextStack _contextStack; + + public PolyineFeatureToSpeckleConverter( + ITypedConverter segmentConverter, + IConversionContextStack contextStack + ) + { + _segmentConverter = segmentConverter; + _contextStack = contextStack; + } + + public IReadOnlyList Convert(ACG.Polyline target) + { + // https://pro.arcgis.com/en/pro-app/latest/sdk/api-reference/topic8480.html + List polylineList = new(); + ACG.Polyline polylineToConvert = target; + + // densify the polylines with curves using precision value of the Map's Spatial Reference + if (target.HasCurves is true) + { + double tolerance = _contextStack.Current.Document.Map.SpatialReference.XYTolerance; + double conversionFactorToMeter = _contextStack.Current.Document.Map.SpatialReference.Unit.ConversionFactor; + var densifiedPolyline = ACG.GeometryEngine.Instance.DensifyByDeviation( + target, + tolerance * conversionFactorToMeter + ); + if (densifiedPolyline == null) + { + throw new ArgumentException("Polyline densification failed"); + } + polylineToConvert = (ACG.Polyline)densifiedPolyline; + } + + foreach (var segmentCollection in polylineToConvert.Parts) + { + polylineList.Add(_segmentConverter.Convert(segmentCollection)); + } + return polylineList; + } +} diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/Raw/SegmentCollectionToSpeckleConverter.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/Raw/SegmentCollectionToSpeckleConverter.cs new file mode 100644 index 0000000000..f0b71ef47c --- /dev/null +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/Raw/SegmentCollectionToSpeckleConverter.cs @@ -0,0 +1,98 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.ArcGIS3.ToSpeckle.Raw; + +public class SegmentCollectionToSpeckleConverter : ITypedConverter +{ + private readonly IConversionContextStack _contextStack; + private readonly ITypedConverter _pointConverter; + + public SegmentCollectionToSpeckleConverter( + IConversionContextStack contextStack, + ITypedConverter pointConverter + ) + { + _contextStack = contextStack; + _pointConverter = pointConverter; + } + + public SOG.Polyline Convert(ACG.ReadOnlySegmentCollection target) + { + // https://pro.arcgis.com/en/pro-app/latest/sdk/api-reference/topic8480.html + double len = 0; + + List points = new(); + foreach (var segment in target) + { + len += segment.Length; + + if (segment.SegmentType != ACG.SegmentType.Line) + { + // densify the segments with curves using precision value of the Map's Spatial Reference + ACG.Polyline polylineFromSegment = new ACG.PolylineBuilderEx( + segment, + ACG.AttributeFlags.HasZ, + _contextStack.Current.Document.Map.SpatialReference + ).ToGeometry(); + + double tolerance = _contextStack.Current.Document.Map.SpatialReference.XYTolerance; + double conversionFactorToMeter = _contextStack.Current.Document.Map.SpatialReference.Unit.ConversionFactor; + var densifiedPolyline = ACG.GeometryEngine.Instance.DensifyByDeviation( + polylineFromSegment, + tolerance * conversionFactorToMeter + ); + if (densifiedPolyline == null) + { + throw new ArgumentException("Segment densification failed"); + } + + ACG.Polyline polylineToConvert = (ACG.Polyline)densifiedPolyline; + // add points from each segment of the densified original segment + ACG.ReadOnlyPartCollection subParts = polylineToConvert.Parts; + foreach (ACG.ReadOnlySegmentCollection subSegments in subParts) + { + foreach (ACG.Segment? subSegment in subSegments) + { + points = AddPtsToPolylinePts( + points, + new List() + { + _pointConverter.Convert(subSegment.StartPoint), + _pointConverter.Convert(subSegment.EndPoint) + } + ); + } + } + } + else + { + points = AddPtsToPolylinePts( + points, + new List() + { + _pointConverter.Convert(segment.StartPoint), + _pointConverter.Convert(segment.EndPoint) + } + ); + } + } + SOG.Polyline polyline = + new(points.SelectMany(pt => new[] { pt.x, pt.y, pt.z }).ToList(), _contextStack.Current.SpeckleUnits) { }; + + return polyline; + } + + private List AddPtsToPolylinePts(List points, List newSegmentPts) + { + if (points.Count == 0 || points[^1] != newSegmentPts[0]) + { + points.AddRange(newSegmentPts); + } + else + { + points.AddRange(newSegmentPts.GetRange(1, newSegmentPts.Count - 1)); + } + return points; + } +} diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/TopLevel/PointcloudLayerToSpeckleConverter.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/TopLevel/PointcloudLayerToSpeckleConverter.cs new file mode 100644 index 0000000000..2ad672dea9 --- /dev/null +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/TopLevel/PointcloudLayerToSpeckleConverter.cs @@ -0,0 +1,126 @@ +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.Core.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 IConversionContextStack _contextStack; + + public PointCloudToSpeckleConverter( + ITypedConverter pointConverter, + ITypedConverter boxConverter, + IConversionContextStack contextStack + ) + { + _pointConverter = pointConverter; + _boxConverter = boxConverter; + _contextStack = contextStack; + } + + 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(); + + // get document CRS (for writing geometry coords) + var spatialRef = _contextStack.Current.Document.Map.SpatialReference; + speckleLayer.crs = new SGIS.CRS + { + wkt = spatialRef.Wkt, + name = spatialRef.Name, + units_native = spatialRef.Unit.ToString(), + }; + + // other properties + speckleLayer.name = target.Name; + speckleLayer.units = _contextStack.Current.SpeckleUnits; + speckleLayer.nativeGeomType = target.MapLayerType.ToString(); + speckleLayer.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 = _contextStack.Current.SpeckleUnits + }; + + speckleLayer.elements.Add(cloud); + return speckleLayer; + } +} diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/TopLevel/RasterLayerToSpeckleConverter.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/TopLevel/RasterLayerToSpeckleConverter.cs new file mode 100644 index 0000000000..22a93c52a9 --- /dev/null +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/TopLevel/RasterLayerToSpeckleConverter.cs @@ -0,0 +1,68 @@ +using ArcGIS.Core.Data.Raster; +using ArcGIS.Core.Geometry; +using Objects.GIS; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.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 IConversionContextStack _contextStack; + + public RasterLayerToSpeckleConverter( + ITypedConverter gisRasterConverter, + IConversionContextStack contextStack + ) + { + _gisRasterConverter = gisRasterConverter; + _contextStack = contextStack; + } + + public Base Convert(object target) + { + return Convert((RasterLayer)target); + } + + public SGIS.RasterLayer Convert(RasterLayer target) + { + var speckleLayer = new SGIS.RasterLayer(); + + // get document CRS (for writing geometry coords) + var spatialRef = _contextStack.Current.Document.Map.SpatialReference; + speckleLayer.crs = new CRS + { + wkt = spatialRef.Wkt, + name = spatialRef.Name, + units_native = spatialRef.Unit.ToString(), + }; + + // 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 = _contextStack.Current.Document.Map.SpatialReference; + } + speckleLayer.rasterCrs = new CRS + { + wkt = spatialRefRaster.Wkt, + name = spatialRefRaster.Name, + units_native = spatialRefRaster.Unit.ToString(), + }; + + // other properties + speckleLayer.name = target.Name; + speckleLayer.units = _contextStack.Current.SpeckleUnits; + + // write details about the Raster + RasterElement element = _gisRasterConverter.Convert(target.GetRaster()); + speckleLayer.elements.Add(element); + + return speckleLayer; + } +} diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/TopLevel/TableToSpeckleConverter.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/TopLevel/TableToSpeckleConverter.cs new file mode 100644 index 0000000000..7649211f18 --- /dev/null +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/TopLevel/TableToSpeckleConverter.cs @@ -0,0 +1,72 @@ +using ArcGIS.Core.Data; +using ArcGIS.Desktop.Mapping; +using Objects.GIS; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; + +namespace Speckle.Converters.ArcGIS3.ToSpeckle.TopLevel; + +[NameAndRankValue(nameof(StandaloneTable), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class StandaloneTableToSpeckleConverter + : IToSpeckleTopLevelConverter, + ITypedConverter +{ + private readonly ITypedConverter _gisFeatureConverter; + + public StandaloneTableToSpeckleConverter(ITypedConverter gisFeatureConverter) + { + _gisFeatureConverter = gisFeatureConverter; + } + + public Base Convert(object target) + { + return Convert((StandaloneTable)target); + } + + public VectorLayer Convert(StandaloneTable target) + { + VectorLayer speckleLayer = new() { name = target.Name, }; + + // get feature class fields + var attributes = new Base(); + var dispayTable = target as IDisplayTable; + var fieldDescriptions = dispayTable.GetFieldDescriptions(); + foreach (var field in fieldDescriptions) + { + if (field.IsVisible) + { + string name = field.Name; + attributes[name] = (int)field.Type; + } + } + + speckleLayer.attributes = attributes; + string spekleGeometryType = "None"; + + using (RowCursor rowCursor = dispayTable.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) + { + GisFeature element = _gisFeatureConverter.Convert(row); + + // replace "attributes", to remove non-visible layer attributes + Base elementAttributes = new(); + foreach (FieldDescription field in fieldDescriptions) + { + elementAttributes[field.Name] = element.attributes[field.Name]; + } + element.attributes = elementAttributes; + + speckleLayer.elements.Add(element); + } + } + } + + speckleLayer.geomType = spekleGeometryType; + return speckleLayer; + } +} diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/TopLevel/VectorLayerToSpeckleConverter.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/TopLevel/VectorLayerToSpeckleConverter.cs new file mode 100644 index 0000000000..8d2a36e33f --- /dev/null +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/ToSpeckle/TopLevel/VectorLayerToSpeckleConverter.cs @@ -0,0 +1,129 @@ +using ArcGIS.Core.CIM; +using ArcGIS.Core.Data; +using ArcGIS.Core.Geometry; +using ArcGIS.Desktop.Mapping; +using Objects.GIS; +using Speckle.Converters.ArcGIS3.Utils; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; + +namespace Speckle.Converters.ArcGIS3.ToSpeckle.TopLevel; + +[NameAndRankValue(nameof(FeatureLayer), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class VectorLayerToSpeckleConverter : IToSpeckleTopLevelConverter, ITypedConverter +{ + private readonly ITypedConverter _gisFeatureConverter; + private readonly IFeatureClassUtils _featureClassUtils; + private readonly IArcGISFieldUtils _fieldsUtils; + private readonly IConversionContextStack _contextStack; + + public VectorLayerToSpeckleConverter( + ITypedConverter gisFeatureConverter, + IFeatureClassUtils featureClassUtils, + IArcGISFieldUtils fieldsUtils, + IConversionContextStack contextStack + ) + { + _gisFeatureConverter = gisFeatureConverter; + _featureClassUtils = featureClassUtils; + _fieldsUtils = fieldsUtils; + _contextStack = contextStack; + } + + public Base Convert(object target) + { + return Convert((FeatureLayer)target); + } + + private string AssignSpeckleGeometryType(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 VectorLayer Convert(FeatureLayer target) + { + VectorLayer speckleLayer = new(); + + // get document CRS (for writing geometry coords) + var spatialRef = _contextStack.Current.Document.Map.SpatialReference; + speckleLayer.crs = new CRS + { + wkt = spatialRef.Wkt, + name = spatialRef.Name, + units_native = spatialRef.Unit.ToString(), + }; + + // other properties + speckleLayer.name = target.Name; + speckleLayer.units = _contextStack.Current.SpeckleUnits; + + // get feature class fields + var allLayerAttributes = new Base(); + var dispayTable = target as IDisplayTable; + IReadOnlyList allFieldDescriptions = dispayTable.GetFieldDescriptions(); + List addedFieldDescriptions = new(); + foreach (FieldDescription field in allFieldDescriptions) + { + 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; + } + addedFieldDescriptions.Add(field); + allLayerAttributes[name] = GISAttributeFieldType.FieldTypeToSpeckle(field.Type); + } + } + speckleLayer.attributes = allLayerAttributes; + + // get a simple geometry type + string spekleGeometryType = AssignSpeckleGeometryType(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 + + 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) + { + GisFeature element = _gisFeatureConverter.Convert(row); + + // replace element "attributes", to remove those non-visible on Layer level + Base elementAttributes = new(); + foreach (FieldDescription field in addedFieldDescriptions) + { + if (field.IsVisible) + { + elementAttributes[field.Name] = element.attributes[field.Name]; + } + } + element.attributes = elementAttributes; + speckleLayer.elements.Add(element); + } + } + } + + return speckleLayer; + } +} diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/ArcGISFieldUtils.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/ArcGISFieldUtils.cs new file mode 100644 index 0000000000..aff09a3d2c --- /dev/null +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/ArcGISFieldUtils.cs @@ -0,0 +1,276 @@ +using System.Collections; +using ArcGIS.Core.Data; +using ArcGIS.Core.Data.Exceptions; +using Objects.GIS; +using Speckle.Core.Logging; +using Speckle.Core.Models; +using FieldDescription = ArcGIS.Core.Data.DDL.FieldDescription; + +namespace Speckle.Converters.ArcGIS3.Utils; + +public class ArcGISFieldUtils : IArcGISFieldUtils +{ + private readonly ICharacterCleaner _characterCleaner; + private const string FID_FIELD_NAME = "OBJECTID"; + + public ArcGISFieldUtils(ICharacterCleaner characterCleaner) + { + _characterCleaner = characterCleaner; + } + + public RowBuffer AssignFieldValuesToRow( + RowBuffer rowBuffer, + List fields, + Dictionary attributes + ) + { + foreach (FieldDescription field in fields) + { + // try to assign values to writeable fields + if (attributes is not null) + { + string key = field.AliasName; // use Alias, as Name is simplified to alphanumeric + FieldType fieldType = field.FieldType; + var value = attributes[key]; + + try + { + rowBuffer[key] = GISAttributeFieldType.SpeckleValueToNativeFieldType(fieldType, value); + } + catch (GeodatabaseFeatureException) + { + //'The value type is incompatible.' + // log error! + rowBuffer[key] = null; + } + catch (GeodatabaseFieldException) + { + // non-editable Field, do nothing + } + catch (GeodatabaseGeneralException) + { + // The index passed was not within the valid range. // unclear reason of the error + } + } + } + return rowBuffer; + } + + public List GetFieldsFromSpeckleLayer(VectorLayer target) + { + List fields = new(); + List fieldAdded = new(); + + foreach (var field in target.attributes.GetMembers(DynamicBaseMemberType.Dynamic)) + { + 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 + } + } + catch (GeodatabaseFieldException) + { + // log missing field + } + } + } + return fields; + } + + public List<(FieldDescription, Func)> CreateFieldsFromListOfBase(List target) + { + List<(FieldDescription, Func)> fieldsAndFunctions = new(); + List fieldAdded = new(); + + foreach (var baseObj in target) + { + // get all members by default, but only Dynamic ones from the basic geometry + Dictionary members = new(); + + // leave out until we decide which properties to support on Receive + /* + if (baseObj.speckle_type.StartsWith("Objects.Geometry")) + { + members = baseObj.GetMembers(DynamicBaseMemberType.Dynamic); + } + else + { + members = baseObj.GetMembers(DynamicBaseMemberType.All); + } + */ + + foreach (KeyValuePair field in members) + { + // 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 + Func function = x => x[field.Key]; + TraverseAttributes(field, function, fieldsAndFunctions, fieldAdded); + } + } + + // change all FieldType.Blob to String + // "Blob" will never be used on receive, so it is a placeholder for non-properly identified fields + for (int i = 0; i < fieldsAndFunctions.Count; i++) + { + (FieldDescription description, Func function) = fieldsAndFunctions[i]; + if (description.FieldType is FieldType.Blob) + { + fieldsAndFunctions[i] = new( + new FieldDescription(description.Name, FieldType.String) { AliasName = description.AliasName }, + function + ); + } + } + + return fieldsAndFunctions; + } + + private void TraverseAttributes( + KeyValuePair field, + Func function, + List<(FieldDescription, Func)> fieldsAndFunctions, + List fieldAdded + ) + { + if (field.Value is Base attributeBase) + { + // only traverse Base if it's Rhino userStrings, or Revit parameter, or Base containing Revit parameters + if (field.Key == "parameters") + { + foreach (KeyValuePair attributField in attributeBase.GetMembers(DynamicBaseMemberType.Dynamic)) + { + // only iterate through elements if they are actually Revit Parameters or parameter IDs + if ( + attributField.Value is Objects.BuiltElements.Revit.Parameter + || attributField.Key == "applicationId" + || attributField.Key == "id" + ) + { + KeyValuePair newAttributField = + new($"{field.Key}.{attributField.Key}", attributField.Value); + Func functionAdded = x => (function(x) as Base)?[attributField.Key]; + TraverseAttributes(newAttributField, functionAdded, fieldsAndFunctions, fieldAdded); + } + } + } + else if (field.Key == "userStrings") + { + foreach (KeyValuePair attributField in attributeBase.GetMembers(DynamicBaseMemberType.Dynamic)) + { + KeyValuePair newAttributField = new($"{field.Key}.{attributField.Key}", attributField.Value); + Func functionAdded = x => (function(x) as Base)?[attributField.Key]; + TraverseAttributes(newAttributField, functionAdded, fieldsAndFunctions, fieldAdded); + } + } + else if (field.Value is Objects.BuiltElements.Revit.Parameter) + { + foreach ( + KeyValuePair attributField in attributeBase.GetMembers(DynamicBaseMemberType.Instance) + ) + { + KeyValuePair newAttributField = new($"{field.Key}.{attributField.Key}", attributField.Value); + Func functionAdded = x => (function(x) as Base)?[attributField.Key]; + TraverseAttributes(newAttributField, functionAdded, fieldsAndFunctions, fieldAdded); + } + } + else + { + // for now, ignore all other properties of Base type + } + } + else if (field.Value is IList attributeList) + { + int count = 0; + foreach (var attributField in attributeList) + { + KeyValuePair newAttributField = new($"{field.Key}[{count}]", attributField); + Func functionAdded = x => (function(x) as List)?[count]; + TraverseAttributes(newAttributField, functionAdded, fieldsAndFunctions, fieldAdded); + count += 1; + } + } + else + { + TryAddField(field, function, fieldsAndFunctions, fieldAdded); + } + } + + private void TryAddField( + KeyValuePair field, + Func function, + List<(FieldDescription, Func)> fieldsAndFunctions, + List fieldAdded + ) + { + try + { + string key = field.Key; + string cleanKey = _characterCleaner.CleanCharacters(key); + + if (cleanKey == FID_FIELD_NAME) // we cannot add field with reserved name + { + return; + } + + if (!fieldAdded.Contains(cleanKey)) + { + // use field.Value to define FieldType + FieldType fieldType = GISAttributeFieldType.GetFieldTypeFromRawValue(field.Value); + + FieldDescription fieldDescription = new(cleanKey, fieldType) { AliasName = key }; + fieldsAndFunctions.Add((fieldDescription, function)); + fieldAdded.Add(cleanKey); + } + else + { + // if field exists, check field.Value again, and revise FieldType if needed + int index = fieldsAndFunctions.TakeWhile(x => x.Item1.Name != cleanKey).Count(); + + (FieldDescription, Func) itemInList; + try + { + itemInList = fieldsAndFunctions[index]; + } + catch (Exception ex) when (!ex.IsFatal()) + { + return; + } + + FieldType existingFieldType = itemInList.Item1.FieldType; + FieldType newFieldType = GISAttributeFieldType.GetFieldTypeFromRawValue(field.Value); + + // adjust FieldType if needed, default everything to Strings if fields types differ: + // 1. change to NewType, if old type was undefined ("Blob") + // 2. change to NewType if it's String (and the old one is not) + if ( + newFieldType != FieldType.Blob && existingFieldType == FieldType.Blob + || (newFieldType == FieldType.String && existingFieldType != FieldType.String) + ) + { + fieldsAndFunctions[index] = ( + new FieldDescription(itemInList.Item1.Name, newFieldType) { AliasName = itemInList.Item1.AliasName }, + itemInList.Item2 + ); + } + } + } + catch (GeodatabaseFieldException) + { + // do nothing + } + } +} diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/CharacterCleaner.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/CharacterCleaner.cs new file mode 100644 index 0000000000..45f65206b5 --- /dev/null +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/CharacterCleaner.cs @@ -0,0 +1,30 @@ +using System.Text.RegularExpressions; + +namespace Speckle.Converters.ArcGIS3.Utils; + +public class CharacterCleaner : ICharacterCleaner +{ + public string CleanCharacters(string key) + { + Regex rg = new(@"^[a-zA-Z0-9_]*$"); + if (rg.IsMatch(key)) + { + return key; + } + + string result = ""; + foreach (char c in key) + { + Regex rg_character = new(@"^[a-zA-Z0-9_]*$"); + if (rg_character.IsMatch(c.ToString())) + { + result += c.ToString(); + } + else + { + result += "_"; + } + } + return result; + } +} diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/ConversionTracker.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/ConversionTracker.cs new file mode 100644 index 0000000000..cb01772ed7 --- /dev/null +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/ConversionTracker.cs @@ -0,0 +1,88 @@ +using ArcGIS.Desktop.Mapping; +using Speckle.Core.Models; + +namespace Speckle.Converters.ArcGIS3.Utils; + +/// +/// Container connecting the received Base object, converted hostApp object, dataset it was written to, +/// URI of the layer mapped from the dataset and, if applicable, feature/row id. +/// +public struct ObjectConversionTracker +{ + public Base Base { get; set; } + public string NestedLayerName { get; set; } + public ACG.Geometry? HostAppGeom { get; set; } + public MapMember? HostAppMapMember { get; set; } + public string? DatasetId { get; set; } + public int? DatasetRow { get; set; } + public string? MappedLayerURI { get; set; } + public Exception? Exception { get; set; } + + public void AddException(Exception ex) + { + Exception = ex; + HostAppGeom = null; + DatasetId = null; + DatasetRow = null; + MappedLayerURI = null; + } + + public void AddDatasetId(string datasetId) + { + DatasetId = datasetId; + } + + public void AddDatasetRow(int datasetRow) + { + DatasetRow = datasetRow; + } + + public void AddConvertedMapMember(MapMember mapMember) + { + HostAppMapMember = mapMember; + } + + public void AddLayerURI(string layerURIstring) + { + MappedLayerURI = layerURIstring; + } + + /// + /// Initializes a new instance of . + /// + /// Original received Base object. + /// String with the full traversed path to the object. Will be used to create nested layer structure in the TOC. + public ObjectConversionTracker(Base baseObj, string nestedLayerName) + { + Base = baseObj; + NestedLayerName = nestedLayerName; + } + + /// + /// Constructor for received non-GIS geometries. + /// Initializes a new instance of , accepting converted hostApp geometry. + /// + /// Original received Base object. + /// String with the full traversed path to the object. Will be used to create nested layer structure in the TOC. + /// Converted ArcGIS.Core.Geometry. + public ObjectConversionTracker(Base baseObj, string nestedLayerName, ACG.Geometry hostAppGeom) + { + Base = baseObj; + NestedLayerName = nestedLayerName; + HostAppGeom = hostAppGeom; + } + + /// + /// Constructor for received native GIS layers. + /// Initializes a new instance of , accepting datasetID of a coverted Speckle layer. + /// + /// Original received Base object. + /// String with the full traversed path to the object. Will be used to create nested layer structure in the TOC. + /// ID of the locally written dataset, created from received Speckle layer. + public ObjectConversionTracker(Base baseObj, string nestedLayerName, string datasetId) + { + Base = baseObj; + NestedLayerName = nestedLayerName; + DatasetId = datasetId; + } +} diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/FeatureClassUtils.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/FeatureClassUtils.cs new file mode 100644 index 0000000000..6e3a587b45 --- /dev/null +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/FeatureClassUtils.cs @@ -0,0 +1,132 @@ +using ArcGIS.Core.Data; +using Objects.GIS; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; +using FieldDescription = ArcGIS.Core.Data.DDL.FieldDescription; + +namespace Speckle.Converters.ArcGIS3.Utils; + +public class FeatureClassUtils : IFeatureClassUtils +{ + private readonly IArcGISFieldUtils _fieldsUtils; + + public FeatureClassUtils(IArcGISFieldUtils fieldsUtils) + { + _fieldsUtils = fieldsUtils; + } + + public void AddFeaturesToTable(Table newFeatureClass, List gisFeatures, List fields) + { + foreach (GisFeature feat in gisFeatures) + { + using (RowBuffer rowBuffer = newFeatureClass.CreateRowBuffer()) + { + newFeatureClass + .CreateRow( + _fieldsUtils.AssignFieldValuesToRow( + rowBuffer, + fields, + feat.attributes.GetMembers(DynamicBaseMemberType.Dynamic) + ) + ) + .Dispose(); + } + } + } + + public void AddFeaturesToFeatureClass( + FeatureClass newFeatureClass, + List gisFeatures, + List fields, + ITypedConverter, ACG.Geometry> gisGeometryConverter + ) + { + foreach (GisFeature feat in gisFeatures) + { + using (RowBuffer rowBuffer = newFeatureClass.CreateRowBuffer()) + { + if (feat.geometry != null) + { + List geometryToConvert = feat.geometry; + ACG.Geometry nativeShape = gisGeometryConverter.Convert(geometryToConvert); + rowBuffer[newFeatureClass.GetDefinition().GetShapeField()] = nativeShape; + } + else + { + throw new SpeckleConversionException("No geomerty to write"); + } + + // get attributes + newFeatureClass + .CreateRow( + _fieldsUtils.AssignFieldValuesToRow( + rowBuffer, + fields, + feat.attributes.GetMembers(DynamicBaseMemberType.Dynamic) + ) + ) + .Dispose(); + } + } + } + + public void AddNonGISFeaturesToFeatureClass( + FeatureClass newFeatureClass, + List<(Base baseObj, ACG.Geometry convertedGeom)> featuresTuples, + List<(FieldDescription, Func)> fieldsAndFunctions + ) + { + foreach ((Base baseObj, ACG.Geometry geom) in featuresTuples) + { + using (RowBuffer rowBuffer = newFeatureClass.CreateRowBuffer()) + { + ACG.Geometry newGeom = geom; + + // exception for Points: turn into MultiPoint layer + if (geom is ACG.MapPoint pointGeom) + { + newGeom = new ACG.MultipointBuilderEx( + new List() { pointGeom }, + ACG.AttributeFlags.HasZ + ).ToGeometry(); + } + + rowBuffer[newFeatureClass.GetDefinition().GetShapeField()] = newGeom; + + // set and pass attributes + Dictionary attributes = new(); + foreach ((FieldDescription field, Func function) in fieldsAndFunctions) + { + string key = field.AliasName; + attributes[key] = function(baseObj); + } + // newFeatureClass.CreateRow(rowBuffer).Dispose(); // without extra attributes + newFeatureClass + .CreateRow( + _fieldsUtils.AssignFieldValuesToRow(rowBuffer, fieldsAndFunctions.Select(x => x.Item1).ToList(), attributes) + ) + .Dispose(); + } + } + } + + public ACG.GeometryType GetLayerGeometryType(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), + $"Geometry type '{originalGeomType}' is not recognized." + ), + }; + } +} diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/GISAttributeFieldType.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/GISAttributeFieldType.cs new file mode 100644 index 0000000000..4da3d09004 --- /dev/null +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/GISAttributeFieldType.cs @@ -0,0 +1,141 @@ +using ArcGIS.Core.Data; + +namespace Speckle.Converters.ArcGIS3.Utils; + +public static class GISAttributeFieldType +{ + public const string GUID_TYPE = "Guid"; + public const string OID = "Oid"; // object identifier: int + public const string STRING_TYPE = "String"; + public const string FLOAT_TYPE = "Float"; // single-precision floating point number + public const string INTEGER_TYPE = "Integer"; // 32-bit int + public const string BIGINTEGER = "BigInteger"; // 64-bit int + public const string SMALLINTEGER = "SmallInteger"; // 16-bit int + public const string DOUBLE_TYPE = "Double"; + public const string DATETIME = "DateTime"; + public const string DATEONLY = "DateOnly"; + public const string TIMEONLY = "TimeOnly"; + public const string TIMESTAMPOFFSET = "TimeStampOffset"; + + 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) + { + return fieldStringType switch + { + GUID_TYPE => FieldType.GUID, + OID => FieldType.OID, + STRING_TYPE => FieldType.String, + FLOAT_TYPE => FieldType.Single, + INTEGER_TYPE => FieldType.Integer, + BIGINTEGER => FieldType.BigInteger, + SMALLINTEGER => FieldType.SmallInteger, + DOUBLE_TYPE => FieldType.Double, + DATETIME => FieldType.Date, + DATEONLY => FieldType.DateOnly, + TIMEONLY => FieldType.TimeOnly, + TIMESTAMPOFFSET => FieldType.String, // sending and receiving as stings + _ => throw new ArgumentOutOfRangeException(nameof(fieldType)), + }; + } + // old way: + return (FieldType)(int)(long)fieldType; + } + + public static object? FieldValueToSpeckle(Row row, Field field) + { + if ( + field.FieldType == FieldType.DateOnly + || field.FieldType == FieldType.TimeOnly + || field.FieldType == FieldType.TimestampOffset + ) + { + return row[field.Name]?.ToString(); + } + else + { + return row[field.Name]; + } + } + + public static object? SpeckleValueToNativeFieldType(FieldType fieldType, object? value) + { + // Geometry: ignored + // Blob, Raster, TimestampOffset, XML: converted to String (field type already converted to String on Send) + switch (fieldType) + { + case FieldType.GUID: + return value; + case FieldType.OID: + return value; + } + + if (value != null) + { + try + { + static object? GetValue(string? s, Func func) => s is null ? null : func(s); + return fieldType switch + { + FieldType.String => Convert.ToString(value), + FieldType.Single => Convert.ToSingle(value), + FieldType.Integer => Convert.ToInt32(value), // need this step because sent "ints" seem to be received as "longs" + FieldType.BigInteger => Convert.ToInt64(value), + FieldType.SmallInteger => Convert.ToInt16(value), + FieldType.Double => Convert.ToDouble(value), + FieldType.Date => GetValue(value.ToString(), s => DateTime.Parse(s, null)), + FieldType.DateOnly => GetValue(value.ToString(), s => DateOnly.Parse(s, null)), + FieldType.TimeOnly => GetValue(value.ToString(), s => TimeOnly.Parse(s, null)), + _ => value, + }; + } + catch (Exception ex) when (ex is InvalidCastException or FormatException or ArgumentNullException) + { + return null; + } + } + else + { + return null; + } + } + + public static FieldType GetFieldTypeFromRawValue(object? value) + { + // using "Blob" as a placeholder for unrecognized values/nulls. + // Once all elements are iterated, FieldType.Blob will be replaced with FieldType.String if no better type found + if (value is not null) + { + return value switch + { + string => FieldType.String, + int => FieldType.Integer, + long => FieldType.BigInteger, + double => FieldType.Double, + _ => FieldType.Blob, + }; + } + + return FieldType.Blob; + } +} diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/GISLayerGeometryType.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/GISLayerGeometryType.cs new file mode 100644 index 0000000000..612eff4807 --- /dev/null +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/GISLayerGeometryType.cs @@ -0,0 +1,12 @@ +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"; +} diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/GeometryExtension.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/GeometryExtension.cs new file mode 100644 index 0000000000..1bc46399f2 --- /dev/null +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/GeometryExtension.cs @@ -0,0 +1,175 @@ +using ArcGIS.Core.CIM; +using Speckle.Converters.Common; + +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 SOG.Mesh CreateDisplayMeshForPolygon(this SGIS.PolygonGeometry polygon) + { + if (polygon.voids.Count == 0) + { + // 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 + int ptCount = boundaryPts.Count; + List faces = new() { ptCount }; + faces.AddRange(Enumerable.Range(0, ptCount).ToList()); + + return new SOG.Mesh(boundaryPts.SelectMany(x => new List { x.x, x.y, x.z }).ToList(), faces); + } + else + { + throw new SpeckleConversionException("Cannot generate display value for polygons with voids"); + } + } + + public static SOG.Mesh CreateDisplayMeshForMultipatch(this SGIS.GisMultipatchGeometry multipatch) + { + return new SOG.Mesh(multipatch.vertices, multipatch.faces); + } + + 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/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/IArcGISFieldUtils.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/IArcGISFieldUtils.cs new file mode 100644 index 0000000000..6bda9e1bff --- /dev/null +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/IArcGISFieldUtils.cs @@ -0,0 +1,18 @@ +using ArcGIS.Core.Data; +using Objects.GIS; +using Speckle.Core.Models; +using FieldDescription = ArcGIS.Core.Data.DDL.FieldDescription; + +namespace Speckle.Converters.ArcGIS3.Utils; + +public interface IArcGISFieldUtils +{ + public RowBuffer AssignFieldValuesToRow( + RowBuffer rowBuffer, + List fields, + Dictionary attributes + ); + public List GetFieldsFromSpeckleLayer(VectorLayer target); + + public List<(FieldDescription, Func)> CreateFieldsFromListOfBase(List target); +} diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/ICharacterCleaner.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/ICharacterCleaner.cs new file mode 100644 index 0000000000..aa4c879bed --- /dev/null +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/ICharacterCleaner.cs @@ -0,0 +1,6 @@ +namespace Speckle.Converters.ArcGIS3.Utils; + +public interface ICharacterCleaner +{ + public string CleanCharacters(string key); +} diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/IFeatureClassUtils.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/IFeatureClassUtils.cs new file mode 100644 index 0000000000..b60cf4bfea --- /dev/null +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/IFeatureClassUtils.cs @@ -0,0 +1,24 @@ +using ArcGIS.Core.Data; +using Objects.GIS; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; +using FieldDescription = ArcGIS.Core.Data.DDL.FieldDescription; + +namespace Speckle.Converters.ArcGIS3.Utils; + +public interface IFeatureClassUtils +{ + void AddFeaturesToFeatureClass( + FeatureClass newFeatureClass, + List gisFeatures, + List fields, + ITypedConverter, ACG.Geometry> gisGeometryConverter + ); + void AddNonGISFeaturesToFeatureClass( + FeatureClass newFeatureClass, + List<(Base baseObj, ACG.Geometry convertedGeom)> featuresTuples, + List<(FieldDescription, Func)> fieldsAndFunctions + ); + void AddFeaturesToTable(Table newFeatureClass, List gisFeatures, List fields); + public ACG.GeometryType GetLayerGeometryType(VectorLayer target); +} diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/INonNativeFeaturesUtils.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/INonNativeFeaturesUtils.cs new file mode 100644 index 0000000000..25cd904fca --- /dev/null +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/INonNativeFeaturesUtils.cs @@ -0,0 +1,8 @@ +using Speckle.Core.Models.GraphTraversal; + +namespace Speckle.Converters.ArcGIS3.Utils; + +public interface INonNativeFeaturesUtils +{ + public void WriteGeometriesToDatasets(Dictionary conversionTracker); +} diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/NonNativeFeaturesUtils.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/NonNativeFeaturesUtils.cs new file mode 100644 index 0000000000..3a17ff8796 --- /dev/null +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/NonNativeFeaturesUtils.cs @@ -0,0 +1,183 @@ +using System.Diagnostics; +using ArcGIS.Core.Data; +using ArcGIS.Core.Data.DDL; +using ArcGIS.Core.Data.Exceptions; +using Speckle.Converters.Common; +using FieldDescription = ArcGIS.Core.Data.DDL.FieldDescription; +using Speckle.Core.Logging; +using Speckle.Core.Models.GraphTraversal; +using Speckle.Core.Models; + +namespace Speckle.Converters.ArcGIS3.Utils; + +public class NonNativeFeaturesUtils : INonNativeFeaturesUtils +{ + private readonly IFeatureClassUtils _featureClassUtils; + private readonly IConversionContextStack _contextStack; + private readonly IArcGISFieldUtils _fieldUtils; + + public NonNativeFeaturesUtils( + IFeatureClassUtils featureClassUtils, + IConversionContextStack contextStack, + IArcGISFieldUtils fieldUtils + ) + { + _featureClassUtils = featureClassUtils; + _contextStack = contextStack; + _fieldUtils = fieldUtils; + } + + public void WriteGeometriesToDatasets( + // Dictionary conversionTracker + Dictionary conversionTracker + ) + { + // 1. Sort features into groups by path and geom type + Dictionary> geometryGroups = new(); + foreach (var item in conversionTracker) + { + try + { + TraversalContext context = item.Key; + var trackerItem = item.Value; + ACG.Geometry? geom = trackerItem.HostAppGeom; + string? datasetId = trackerItem.DatasetId; + if (geom != null && datasetId == null) // only non-native geomerties, not written into a dataset yet + { + string speckle_type = trackerItem.Base.speckle_type.Split(".")[^1]; + string? parentId = context.Parent?.Current.id; + + // add dictionnary item if doesn't exist yet + // Key must be unique per parent and speckle_type + string uniqueKey = $"speckleTYPE_{speckle_type}_speckleID_{parentId}"; + if (!geometryGroups.TryGetValue(uniqueKey, out _)) + { + geometryGroups[uniqueKey] = new(); + } + + geometryGroups[uniqueKey].Add((trackerItem.Base, geom)); + + // record changes in conversion tracker + trackerItem.AddDatasetId(uniqueKey); + trackerItem.AddDatasetRow(geometryGroups[uniqueKey].Count - 1); + conversionTracker[item.Key] = trackerItem; + } + else if (geom == null && datasetId != null) // GIS layers, already written to a dataset + { + continue; + } + else + { + throw new ArgumentException($"Unexpected geometry and datasetId values: {geom}, {datasetId}"); + } + } + catch (Exception ex) when (!ex.IsFatal()) + { + // POC: report, etc. + var trackerItem = item.Value; + trackerItem.AddException(ex); + conversionTracker[item.Key] = trackerItem; + Debug.WriteLine($"conversion error happened. {ex.Message}"); + } + } + + // 2. for each group create a Dataset and add geometries there as Features + foreach (var item in geometryGroups) + { + string uniqueKey = item.Key; + List<(Base, ACG.Geometry)> listOfGeometryTuples = item.Value; + try + { + CreateDatasetInDatabase(uniqueKey, listOfGeometryTuples); + } + catch (GeodatabaseGeometryException ex) + { + // do nothing if writing of some geometry groups fails + // only record in conversionTracker: + foreach (var conversionItem in conversionTracker) + { + if (conversionItem.Value.DatasetId == uniqueKey) + { + var trackerItem = conversionItem.Value; + trackerItem.AddException(ex); + conversionTracker[conversionItem.Key] = trackerItem; + } + } + } + } + } + + private void CreateDatasetInDatabase( + string featureClassName, + List<(Base baseObj, ACG.Geometry convertedGeom)> listOfGeometryTuples + ) + { + FileGeodatabaseConnectionPath fileGeodatabaseConnectionPath = + new(_contextStack.Current.Document.SpeckleDatabasePath); + Geodatabase geodatabase = new(fileGeodatabaseConnectionPath); + SchemaBuilder schemaBuilder = new(geodatabase); + + // get Spatial Reference from the document + ACG.SpatialReference spatialRef = _contextStack.Current.Document.Map.SpatialReference; + + // create Fields + List<(FieldDescription, Func)> fieldsAndFunctions = _fieldUtils.CreateFieldsFromListOfBase( + listOfGeometryTuples.Select(x => x.baseObj).ToList() + ); + + // delete FeatureClass if already exists + try + { + FeatureClassDefinition fClassDefinition = geodatabase.GetDefinition(featureClassName); + FeatureClassDescription existingDescription = new(fClassDefinition); + schemaBuilder.Delete(existingDescription); + schemaBuilder.Build(); + } + catch (Exception ex) when (!ex.IsFatal()) //(GeodatabaseTableException) + { + // "The table was not found." + // delete Table if already exists + try + { + TableDefinition fClassDefinition = geodatabase.GetDefinition(featureClassName); + TableDescription existingDescription = new(fClassDefinition); + schemaBuilder.Delete(existingDescription); + schemaBuilder.Build(); + } + catch (Exception ex2) when (!ex2.IsFatal()) //(GeodatabaseTableException) + { + // "The table was not found.", do nothing + } + } + + // Create FeatureClass + try + { + // POC: make sure class has a valid crs + ACG.GeometryType geomType = listOfGeometryTuples[0].convertedGeom.GeometryType; + ShapeDescription shpDescription = new(geomType, spatialRef) { HasZ = true }; + FeatureClassDescription featureClassDescription = + new(featureClassName, fieldsAndFunctions.Select(x => x.Item1), shpDescription); + FeatureClassToken featureClassToken = schemaBuilder.Create(featureClassDescription); + } + catch (ArgumentException ex) + { + // if name has invalid characters/combinations + // or 'The table contains multiple fields with the same name.: + throw new ArgumentException($"{ex.Message}: {featureClassName}", ex); + } + bool buildStatus = schemaBuilder.Build(); + if (!buildStatus) + { + // POC: log somewhere the error in building the feature class + IReadOnlyList errors = schemaBuilder.ErrorMessages; + } + + FeatureClass newFeatureClass = geodatabase.OpenDataset(featureClassName); + // Add features to the FeatureClass + geodatabase.ApplyEdits(() => + { + _featureClassUtils.AddNonGISFeaturesToFeatureClass(newFeatureClass, listOfGeometryTuples, fieldsAndFunctions); + }); + } +} diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/packages.lock.json b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/packages.lock.json new file mode 100644 index 0000000000..b77f27c0be --- /dev/null +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/packages.lock.json @@ -0,0 +1,410 @@ +{ + "version": 2, + "dependencies": { + "net6.0-windows7.0": { + "Esri.ArcGISPro.Extensions30": { + "type": "Direct", + "requested": "[3.2.0.49743, )", + "resolved": "3.2.0.49743", + "contentHash": "fmnYm+mD14Cz0Uqh1ij37SfLJerkyFHK5581y5tXT/l3H2ZvUmVuuxjYquXzyzj9p7IexQzMW4xCpxe+mD922g==" + }, + "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.5, )", + "resolved": "0.9.5", + "contentHash": "oU/L7pN1R7q8KkbrpQ3WJnHirPHqn+9DEA7asOcUiggV5dzVg1A/VYs7GOSusD24njxXh03tE3a2oTLOjt3cVg==" + }, + "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.AspNetCore.Http": { + "type": "Transitive", + "resolved": "2.2.2", + "contentHash": "BAibpoItxI5puk7YJbIGj95arZueM8B8M5xT1fXBn3hb3L2G3ucrZcYXv1gXdaroLbntUs8qeV8iuBrpjQsrKw==", + "dependencies": { + "Microsoft.AspNetCore.Http.Abstractions": "2.2.0", + "Microsoft.AspNetCore.WebUtilities": "2.2.0", + "Microsoft.Extensions.ObjectPool": "2.2.0", + "Microsoft.Extensions.Options": "2.2.0", + "Microsoft.Net.Http.Headers": "2.2.0" + } + }, + "Microsoft.AspNetCore.Http.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "Nxs7Z1q3f1STfLYKJSVXCs1iBl+Ya6E8o4Oy1bCxJ/rNI44E/0f6tbsrVqAWfB7jlnJfyaAtIalBVxPKUPQb4Q==", + "dependencies": { + "Microsoft.AspNetCore.Http.Features": "2.2.0", + "System.Text.Encodings.Web": "4.5.0" + } + }, + "Microsoft.AspNetCore.Http.Features": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "ziFz5zH8f33En4dX81LW84I6XrYXKf9jg6aM39cM+LffN9KJahViKZ61dGMSO2gd3e+qe5yBRwsesvyqlZaSMg==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.2.0" + } + }, + "Microsoft.AspNetCore.WebUtilities": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "9ErxAAKaDzxXASB/b5uLEkLgUWv1QbeVxyJYEHQwMaxXOeFFVkQxiq8RyfVcifLU7NR0QY0p3acqx4ZpYfhHDg==", + "dependencies": { + "Microsoft.Net.Http.Headers": "2.2.0", + "System.Text.Encodings.Web": "4.5.0" + } + }, + "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.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw==" + }, + "Microsoft.Extensions.ObjectPool": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "gA8H7uQOnM5gb+L0uTNjViHYr+hRDqCdfugheGo/MxQnuHzmhhzCBTIPm19qL1z1Xe0NEMabfcOBGv9QghlZ8g==" + }, + "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.Net.Http.Headers": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "iZNkjYqlo8sIOI0bQfpsSoMTmB/kyvmV2h225ihyZT33aTp48ZpF6qYnXxzSXmHt8DpBAwBTX+1s1UFLbYfZKg==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.2.0", + "System.Buffers": "4.5.0" + } + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "Polly": { + "type": "Transitive", + "resolved": "7.2.3", + "contentHash": "DeCY0OFbNdNxsjntr1gTXHJ5pKUwYzp04Er2LLeN3g6pWhffsGuKVfMBLe1lw7x76HrPkLxKEFxBlpRxS2nDEQ==" + }, + "Polly.Contrib.WaitAndRetry": { + "type": "Transitive", + "resolved": "1.1.1", + "contentHash": "1MUQLiSo4KDkQe6nzQRhIU05lm9jlexX5BVsbuw0SL82ynZ+GzAHQxJVDPVBboxV37Po3SG077aX8DuSy8TkaA==" + }, + "Polly.Extensions.Http": { + "type": "Transitive", + "resolved": "3.0.0", + "contentHash": "drrG+hB3pYFY7w1c3BD+lSGYvH2oIclH8GRSehgfyP5kjnFnHKQuuBhuHLv+PWyFuaTDyk/vfRpnxOzd11+J8g==", + "dependencies": { + "Polly": "7.1.0" + } + }, + "Sentry": { + "type": "Transitive", + "resolved": "3.33.0", + "contentHash": "8vbD2o6IR2wrRrkSiRbnodWGWUOqIlwYtzpjvPNOb5raJdOf+zxMwfS8f6nx9bmrTTfDj7KrCB8C/5OuicAc8A==" + }, + "Sentry.Serilog": { + "type": "Transitive", + "resolved": "3.33.0", + "contentHash": "V8BU7QGWg2qLYfNPqtuTBhC1opysny5l+Ifp6J6PhOeAxU0FssR7nYfbJVetrnLIoh2rd3DlJ6hHYYQosQYcUQ==", + "dependencies": { + "Sentry": "3.33.0", + "Serilog": "2.10.0" + } + }, + "Serilog": { + "type": "Transitive", + "resolved": "2.12.0", + "contentHash": "xaiJLIdu6rYMKfQMYUZgTy8YK7SMZjB4Yk50C/u//Z4OsvxkUfSPJy4nknfvwAC34yr13q7kcyh4grbwhSxyZg==" + }, + "Serilog.Enrichers.ClientInfo": { + "type": "Transitive", + "resolved": "1.3.0", + "contentHash": "mTc7PM+wC9Hr7LWSwqt5mmnlAr7RJs+eTb3PGPRhwdOackk95MkhUZognuxXEdlW19HAFNmEBTSBY5DfLwM8jQ==", + "dependencies": { + "Microsoft.AspNetCore.Http": "2.2.2", + "Serilog": "2.9.0" + } + }, + "Serilog.Exceptions": { + "type": "Transitive", + "resolved": "8.4.0", + "contentHash": "nc/+hUw3lsdo0zCj0KMIybAu7perMx79vu72w0za9Nsi6mWyNkGXxYxakAjWB7nEmYL6zdmhEQRB4oJ2ALUeug==", + "dependencies": { + "Serilog": "2.8.0", + "System.Reflection.TypeExtensions": "4.7.0" + } + }, + "Serilog.Formatting.Compact": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "pNroKVjo+rDqlxNG5PXkRLpfSCuDOBY0ri6jp9PLe505ljqwhwZz8ospy2vWhQlFu5GkIesh3FcDs4n7sWZODA==", + "dependencies": { + "Serilog": "2.8.0" + } + }, + "Serilog.Sinks.Console": { + "type": "Transitive", + "resolved": "4.1.0", + "contentHash": "K6N5q+5fetjnJPvCmkWOpJ/V8IEIoMIB1s86OzBrbxwTyHxdx3pmz4H+8+O/Dc/ftUX12DM1aynx/dDowkwzqg==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "Serilog.Sinks.File": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "uwV5hdhWPwUH1szhO8PJpFiahqXmzPzJT/sOijH/kFgUx+cyoDTMM8MHD0adw9+Iem6itoibbUXHYslzXsLEAg==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "Serilog.Sinks.PeriodicBatching": { + "type": "Transitive", + "resolved": "3.1.0", + "contentHash": "NDWR7m3PalVlGEq3rzoktrXikjFMLmpwF0HI4sowo8YDdU+gqPlTHlDQiOGxHfB0sTfjPA9JjA7ctKG9zqjGkw==", + "dependencies": { + "Serilog": "2.0.0" + } + }, + "Serilog.Sinks.Seq": { + "type": "Transitive", + "resolved": "5.2.2", + "contentHash": "1Csmo5ua7NKUe0yXUx+zsRefjAniPWcXFhUXxXG8pwo0iMiw2gjn9SOkgYnnxbgWqmlGv236w0N/dHc2v5XwMg==", + "dependencies": { + "Serilog": "2.12.0", + "Serilog.Formatting.Compact": "1.1.0", + "Serilog.Sinks.File": "5.0.0", + "Serilog.Sinks.PeriodicBatching": "3.1.0" + } + }, + "SerilogTimings": { + "type": "Transitive", + "resolved": "3.0.1", + "contentHash": "Zs28eTgszAMwpIrbBnWHBI50yuxL50p/dmAUWmy75+axdZYK/Sjm5/5m1N/CisR8acJUhTVcjPZrsB1P5iv0Uw==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "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.Buffers": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "pL2ChpaRRWI/p4LXyy4RgeWlYF2sgfj/pnVMvBqwNFr5cXg7CXNnWZWxrOONLg8VGdFB8oB+EG2Qw4MLgTOe+A==" + }, + "System.ComponentModel.Annotations": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "UxYQ3FGUOtzJ7LfSdnYSFd7+oEv6M8NgUatatIN2HxNtDdlcvFAf+VIq4Of9cDMJEJC0aSRv/x898RYhB4Yppg==" + }, + "System.DoubleNumerics": { + "type": "Transitive", + "resolved": "3.1.3", + "contentHash": "KRKEM/L3KBodjA9VOg3EifFVWUY6EOqaMB05UvPEDm7Zeby/kZW+4kdWUEPzW6xtkwf46p661L9NrbeeQhtLzw==", + "dependencies": { + "NETStandard.Library": "1.6.1" + } + }, + "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.TypeExtensions": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "VybpaOQQhqE6siHppMktjfGBw1GCwvCqiufqmP8F1nj7fTUNtW35LOEt3UZTEsECfo+ELAl/9o9nJx3U91i7vA==" + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Zh8t8oqolRaFa9vmOZfdQm/qKejdqz0J9kr7o2Fu0vPeoH3BL1EOXipKWwkWtLT1JPzjByrF19fGuFlNbmPpiw==" + }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "Xg4G4Indi4dqP1iuAiMSwpiWS54ZghzR644OtsRCm/m/lBMG8dUBhLVN7hLm8NNrNTR+iGbshCPTwrvxZPlm4g==" + }, + "speckle.autofac": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "[7.0.0, )" + } + }, + "speckle.converters.common": { + "type": "Project", + "dependencies": { + "Speckle.Autofac": "[2.0.999-local, )", + "Speckle.Objects": "[3.0.1-alpha.14, )" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "CentralTransitive", + "requested": "[7.0.0, )", + "resolved": "7.0.0", + "contentHash": "kmn78+LPVMOWeITUjIlfxUPDsI0R6G0RkeAMBmQxAJ7vBJn4q2dTva7pWi65ceN5vPGjJ9q/Uae2WKgvfktJAw==" + }, + "Speckle.Core": { + "type": "CentralTransitive", + "requested": "[3.0.1-alpha.14, )", + "resolved": "3.0.1-alpha.14", + "contentHash": "RzQPVIGFFkKvG56YLr8ACtiwdWJE6IJ9vCQ4qHa0PIsUEpfzAIAi59jnzqtByOFC0FiFrFPow9bkfzylaZorAA==", + "dependencies": { + "GraphQL.Client": "6.0.0", + "Microsoft.CSharp": "4.7.0", + "Microsoft.Data.Sqlite": "7.0.5", + "Polly": "7.2.3", + "Polly.Contrib.WaitAndRetry": "1.1.1", + "Polly.Extensions.Http": "3.0.0", + "Sentry": "3.33.0", + "Sentry.Serilog": "3.33.0", + "Serilog": "2.12.0", + "Serilog.Enrichers.ClientInfo": "1.3.0", + "Serilog.Exceptions": "8.4.0", + "Serilog.Sinks.Console": "4.1.0", + "Serilog.Sinks.Seq": "5.2.2", + "SerilogTimings": "3.0.1", + "Speckle.Newtonsoft.Json": "13.0.2", + "System.DoubleNumerics": "3.1.3" + } + }, + "Speckle.Objects": { + "type": "CentralTransitive", + "requested": "[3.0.1-alpha.14, )", + "resolved": "3.0.1-alpha.14", + "contentHash": "z38LGryMvh7iU1uBW+4uo5DwsB3CwRgLt2uFexWFx3mPSid+A0l5XcJzOgLwgFhNl6B42Ryz4ezBsddTp1Uc/g==", + "dependencies": { + "Speckle.Core": "3.0.1-alpha.14" + } + } + } + } +} \ No newline at end of file diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.Autocad2023.DependencyInjection/AutocadConverterModule.cs b/DUI3-DX/Converters/Autocad/Speckle.Converters.Autocad2023.DependencyInjection/AutocadConverterModule.cs new file mode 100644 index 0000000000..f37ac6541f --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.Autocad2023.DependencyInjection/AutocadConverterModule.cs @@ -0,0 +1,21 @@ +using Autodesk.AutoCAD.ApplicationServices; +using Autodesk.AutoCAD.DatabaseServices; +using Speckle.Autofac.DependencyInjection; +using Speckle.Converters.Autocad; +using Speckle.Converters.Common; +using Speckle.Converters.Common.DependencyInjection; + +namespace Speckle.Converters.Autocad2023.DependencyInjection; + +public class AutocadConverterModule : ISpeckleModule +{ + public void Load(SpeckleContainerBuilder builder) + { + // add single root converter + builder.AddRootCommon(); + + // add application converters and context stack + builder.AddApplicationConverters(); + builder.AddScoped, AutocadConversionContextStack>(); + } +} diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.Autocad2023.DependencyInjection/Speckle.Converters.Autocad2023.DependencyInjection.csproj b/DUI3-DX/Converters/Autocad/Speckle.Converters.Autocad2023.DependencyInjection/Speckle.Converters.Autocad2023.DependencyInjection.csproj new file mode 100644 index 0000000000..aa5a5af310 --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.Autocad2023.DependencyInjection/Speckle.Converters.Autocad2023.DependencyInjection.csproj @@ -0,0 +1,17 @@ + + + + net48 + x64 + + + + + + + + + + + + diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.Autocad2023.DependencyInjection/packages.lock.json b/DUI3-DX/Converters/Autocad/Speckle.Converters.Autocad2023.DependencyInjection/packages.lock.json new file mode 100644 index 0000000000..99b4b1fb8c --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.Autocad2023.DependencyInjection/packages.lock.json @@ -0,0 +1,438 @@ +{ + "version": 2, + "dependencies": { + ".NETFramework,Version=v4.8": { + "Autofac": { + "type": "Direct", + "requested": "[5.2.0, )", + "resolved": "5.2.0", + "contentHash": "V8dBH0dsv75uDzl7Sw+HkhKDPUw2eXnlMjcSVMH+tLo2s67MpTKGyDj1pDcpR+IF2u4YRs0s3/x7R88YJzIWvg==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "1.1.0" + } + }, + "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.5, )", + "resolved": "0.9.5", + "contentHash": "oU/L7pN1R7q8KkbrpQ3WJnHirPHqn+9DEA7asOcUiggV5dzVg1A/VYs7GOSusD24njxXh03tE3a2oTLOjt3cVg==" + }, + "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.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "Polly": { + "type": "Transitive", + "resolved": "7.2.3", + "contentHash": "DeCY0OFbNdNxsjntr1gTXHJ5pKUwYzp04Er2LLeN3g6pWhffsGuKVfMBLe1lw7x76HrPkLxKEFxBlpRxS2nDEQ==" + }, + "Polly.Contrib.WaitAndRetry": { + "type": "Transitive", + "resolved": "1.1.1", + "contentHash": "1MUQLiSo4KDkQe6nzQRhIU05lm9jlexX5BVsbuw0SL82ynZ+GzAHQxJVDPVBboxV37Po3SG077aX8DuSy8TkaA==" + }, + "Polly.Extensions.Http": { + "type": "Transitive", + "resolved": "3.0.0", + "contentHash": "drrG+hB3pYFY7w1c3BD+lSGYvH2oIclH8GRSehgfyP5kjnFnHKQuuBhuHLv+PWyFuaTDyk/vfRpnxOzd11+J8g==", + "dependencies": { + "Polly": "7.1.0" + } + }, + "Sentry": { + "type": "Transitive", + "resolved": "3.33.0", + "contentHash": "8vbD2o6IR2wrRrkSiRbnodWGWUOqIlwYtzpjvPNOb5raJdOf+zxMwfS8f6nx9bmrTTfDj7KrCB8C/5OuicAc8A==", + "dependencies": { + "System.Reflection.Metadata": "5.0.0", + "System.Runtime.InteropServices.RuntimeInformation": "4.3.0", + "System.Text.Json": "5.0.2" + } + }, + "Sentry.Serilog": { + "type": "Transitive", + "resolved": "3.33.0", + "contentHash": "V8BU7QGWg2qLYfNPqtuTBhC1opysny5l+Ifp6J6PhOeAxU0FssR7nYfbJVetrnLIoh2rd3DlJ6hHYYQosQYcUQ==", + "dependencies": { + "Sentry": "3.33.0", + "Serilog": "2.7.1" + } + }, + "Serilog": { + "type": "Transitive", + "resolved": "2.12.0", + "contentHash": "xaiJLIdu6rYMKfQMYUZgTy8YK7SMZjB4Yk50C/u//Z4OsvxkUfSPJy4nknfvwAC34yr13q7kcyh4grbwhSxyZg==" + }, + "Serilog.Enrichers.ClientInfo": { + "type": "Transitive", + "resolved": "1.3.0", + "contentHash": "mTc7PM+wC9Hr7LWSwqt5mmnlAr7RJs+eTb3PGPRhwdOackk95MkhUZognuxXEdlW19HAFNmEBTSBY5DfLwM8jQ==", + "dependencies": { + "Serilog": "2.4.0" + } + }, + "Serilog.Exceptions": { + "type": "Transitive", + "resolved": "8.4.0", + "contentHash": "nc/+hUw3lsdo0zCj0KMIybAu7perMx79vu72w0za9Nsi6mWyNkGXxYxakAjWB7nEmYL6zdmhEQRB4oJ2ALUeug==", + "dependencies": { + "Serilog": "2.8.0" + } + }, + "Serilog.Formatting.Compact": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "pNroKVjo+rDqlxNG5PXkRLpfSCuDOBY0ri6jp9PLe505ljqwhwZz8ospy2vWhQlFu5GkIesh3FcDs4n7sWZODA==", + "dependencies": { + "Serilog": "2.8.0" + } + }, + "Serilog.Sinks.Console": { + "type": "Transitive", + "resolved": "4.1.0", + "contentHash": "K6N5q+5fetjnJPvCmkWOpJ/V8IEIoMIB1s86OzBrbxwTyHxdx3pmz4H+8+O/Dc/ftUX12DM1aynx/dDowkwzqg==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "Serilog.Sinks.File": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "uwV5hdhWPwUH1szhO8PJpFiahqXmzPzJT/sOijH/kFgUx+cyoDTMM8MHD0adw9+Iem6itoibbUXHYslzXsLEAg==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "Serilog.Sinks.PeriodicBatching": { + "type": "Transitive", + "resolved": "3.1.0", + "contentHash": "NDWR7m3PalVlGEq3rzoktrXikjFMLmpwF0HI4sowo8YDdU+gqPlTHlDQiOGxHfB0sTfjPA9JjA7ctKG9zqjGkw==", + "dependencies": { + "Serilog": "2.0.0" + } + }, + "Serilog.Sinks.Seq": { + "type": "Transitive", + "resolved": "5.2.2", + "contentHash": "1Csmo5ua7NKUe0yXUx+zsRefjAniPWcXFhUXxXG8pwo0iMiw2gjn9SOkgYnnxbgWqmlGv236w0N/dHc2v5XwMg==", + "dependencies": { + "Serilog": "2.12.0", + "Serilog.Formatting.Compact": "1.1.0", + "Serilog.Sinks.File": "5.0.0", + "Serilog.Sinks.PeriodicBatching": "3.1.0" + } + }, + "SerilogTimings": { + "type": "Transitive", + "resolved": "3.0.1", + "contentHash": "Zs28eTgszAMwpIrbBnWHBI50yuxL50p/dmAUWmy75+axdZYK/Sjm5/5m1N/CisR8acJUhTVcjPZrsB1P5iv0Uw==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "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.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, + "System.Collections.Immutable": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "FXkLXiK0sVVewcso0imKQoOxjoPAj42R8HtjjbSjVPAzwDfzoyoznWxgA3c38LDbN9SJux1xXoXYAhz98j7r2g==", + "dependencies": { + "System.Memory": "4.5.4" + } + }, + "System.DoubleNumerics": { + "type": "Transitive", + "resolved": "3.1.3", + "contentHash": "KRKEM/L3KBodjA9VOg3EifFVWUY6EOqaMB05UvPEDm7Zeby/kZW+4kdWUEPzW6xtkwf46p661L9NrbeeQhtLzw==", + "dependencies": { + "NETStandard.Library": "1.6.1" + } + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Numerics.Vectors": "4.5.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + }, + "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.5.0", + "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" + }, + "System.Reactive": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "System.Reflection.Metadata": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "5NecZgXktdGg34rh1OenY1rFNDCI8xSjFr+Z4OU4cU06AQHUdRnIIEeWENu3Wl4YowbzkymAIMvi3WyK9U53pQ==", + "dependencies": { + "System.Collections.Immutable": "5.0.0" + } + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "ZD9TMpsmYJLrxbbmdvhwt9YEgG5WntEnZ/d1eH8JBX9LBp+Ju8BSBhUGbZMNVHHomWo2KVImJhTDl2hIgw/6MA==" + }, + "System.Runtime.InteropServices.RuntimeInformation": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==" + }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "5.0.1", + "contentHash": "KmJ+CJXizDofbq6mpqDoRRLcxgOd2z9X3XoFNULSbvbqVRZkFX3istvr+MUjL6Zw1RT+RNdoI4GYidIINtgvqQ==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Memory": "4.5.4" + } + }, + "System.Text.Json": { + "type": "Transitive", + "resolved": "5.0.2", + "contentHash": "I47dVIGiV6SfAyppphxqupertT/5oZkYLDCX6vC3HpOI4ZLjyoKAreUoem2ie6G0RbRuFrlqz/PcTQjfb2DOfQ==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "5.0.0", + "System.Buffers": "4.5.1", + "System.Memory": "4.5.4", + "System.Numerics.Vectors": "4.5.0", + "System.Runtime.CompilerServices.Unsafe": "5.0.0", + "System.Text.Encodings.Web": "5.0.1", + "System.Threading.Tasks.Extensions": "4.5.4", + "System.ValueTuple": "4.5.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" + } + }, + "System.ValueTuple": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "okurQJO6NRE/apDIP23ajJ0hpiNmJ+f0BwOlB/cSqTLQlw5upkf+5+96+iG2Jw40G1fCVCyPz/FhIABUjMR+RQ==" + }, + "speckle.autofac": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "[7.0.0, )" + } + }, + "speckle.converters.autocad2023": { + "type": "Project", + "dependencies": { + "Speckle.AutoCAD.API": "[2023.0.0, )", + "Speckle.Converters.Common": "[2.0.999-local, )" + } + }, + "speckle.converters.common": { + "type": "Project", + "dependencies": { + "Speckle.Autofac": "[2.0.999-local, )", + "Speckle.Objects": "[3.0.1-alpha.14, )" + } + }, + "speckle.converters.common.dependencyinjection": { + "type": "Project", + "dependencies": { + "Speckle.Autofac": "[2.0.999-local, )", + "Speckle.Converters.Common": "[2.0.999-local, )" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "CentralTransitive", + "requested": "[7.0.0, )", + "resolved": "7.0.0", + "contentHash": "kmn78+LPVMOWeITUjIlfxUPDsI0R6G0RkeAMBmQxAJ7vBJn4q2dTva7pWi65ceN5vPGjJ9q/Uae2WKgvfktJAw==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Memory": "4.5.5" + } + }, + "Speckle.AutoCAD.API": { + "type": "CentralTransitive", + "requested": "[2024.0.0, )", + "resolved": "2023.0.0", + "contentHash": "aNfiNw9zRW8pCl8AAQK7afEJuea4bJ4sFNsGVSDrdq1egaonZrwALU01dSyFNCE8tne86eVjlprpOGG6r0+G/A==" + }, + "Speckle.Core": { + "type": "CentralTransitive", + "requested": "[3.0.1-alpha.14, )", + "resolved": "3.0.1-alpha.14", + "contentHash": "RzQPVIGFFkKvG56YLr8ACtiwdWJE6IJ9vCQ4qHa0PIsUEpfzAIAi59jnzqtByOFC0FiFrFPow9bkfzylaZorAA==", + "dependencies": { + "GraphQL.Client": "6.0.0", + "Microsoft.CSharp": "4.7.0", + "Microsoft.Data.Sqlite": "7.0.5", + "Polly": "7.2.3", + "Polly.Contrib.WaitAndRetry": "1.1.1", + "Polly.Extensions.Http": "3.0.0", + "Sentry": "3.33.0", + "Sentry.Serilog": "3.33.0", + "Serilog": "2.12.0", + "Serilog.Enrichers.ClientInfo": "1.3.0", + "Serilog.Exceptions": "8.4.0", + "Serilog.Sinks.Console": "4.1.0", + "Serilog.Sinks.Seq": "5.2.2", + "SerilogTimings": "3.0.1", + "Speckle.Newtonsoft.Json": "13.0.2", + "System.DoubleNumerics": "3.1.3" + } + }, + "Speckle.Objects": { + "type": "CentralTransitive", + "requested": "[3.0.1-alpha.14, )", + "resolved": "3.0.1-alpha.14", + "contentHash": "z38LGryMvh7iU1uBW+4uo5DwsB3CwRgLt2uFexWFx3mPSid+A0l5XcJzOgLwgFhNl6B42Ryz4ezBsddTp1Uc/g==", + "dependencies": { + "Speckle.Core": "3.0.1-alpha.14" + } + } + } + } +} \ No newline at end of file diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.Autocad2023/Speckle.Converters.Autocad2023.csproj b/DUI3-DX/Converters/Autocad/Speckle.Converters.Autocad2023/Speckle.Converters.Autocad2023.csproj new file mode 100644 index 0000000000..06ab0541d9 --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.Autocad2023/Speckle.Converters.Autocad2023.csproj @@ -0,0 +1,18 @@ + + + + net48 + x64 + + + + + + + + + + + + + diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.Autocad2023/packages.lock.json b/DUI3-DX/Converters/Autocad/Speckle.Converters.Autocad2023/packages.lock.json new file mode 100644 index 0000000000..3c281f9dcf --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.Autocad2023/packages.lock.json @@ -0,0 +1,415 @@ +{ + "version": 2, + "dependencies": { + ".NETFramework,Version=v4.8": { + "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.AutoCAD.API": { + "type": "Direct", + "requested": "[2023.0.0, )", + "resolved": "2023.0.0", + "contentHash": "aNfiNw9zRW8pCl8AAQK7afEJuea4bJ4sFNsGVSDrdq1egaonZrwALU01dSyFNCE8tne86eVjlprpOGG6r0+G/A==" + }, + "Speckle.InterfaceGenerator": { + "type": "Direct", + "requested": "[0.9.5, )", + "resolved": "0.9.5", + "contentHash": "oU/L7pN1R7q8KkbrpQ3WJnHirPHqn+9DEA7asOcUiggV5dzVg1A/VYs7GOSusD24njxXh03tE3a2oTLOjt3cVg==" + }, + "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.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "Polly": { + "type": "Transitive", + "resolved": "7.2.3", + "contentHash": "DeCY0OFbNdNxsjntr1gTXHJ5pKUwYzp04Er2LLeN3g6pWhffsGuKVfMBLe1lw7x76HrPkLxKEFxBlpRxS2nDEQ==" + }, + "Polly.Contrib.WaitAndRetry": { + "type": "Transitive", + "resolved": "1.1.1", + "contentHash": "1MUQLiSo4KDkQe6nzQRhIU05lm9jlexX5BVsbuw0SL82ynZ+GzAHQxJVDPVBboxV37Po3SG077aX8DuSy8TkaA==" + }, + "Polly.Extensions.Http": { + "type": "Transitive", + "resolved": "3.0.0", + "contentHash": "drrG+hB3pYFY7w1c3BD+lSGYvH2oIclH8GRSehgfyP5kjnFnHKQuuBhuHLv+PWyFuaTDyk/vfRpnxOzd11+J8g==", + "dependencies": { + "Polly": "7.1.0" + } + }, + "Sentry": { + "type": "Transitive", + "resolved": "3.33.0", + "contentHash": "8vbD2o6IR2wrRrkSiRbnodWGWUOqIlwYtzpjvPNOb5raJdOf+zxMwfS8f6nx9bmrTTfDj7KrCB8C/5OuicAc8A==", + "dependencies": { + "System.Reflection.Metadata": "5.0.0", + "System.Runtime.InteropServices.RuntimeInformation": "4.3.0", + "System.Text.Json": "5.0.2" + } + }, + "Sentry.Serilog": { + "type": "Transitive", + "resolved": "3.33.0", + "contentHash": "V8BU7QGWg2qLYfNPqtuTBhC1opysny5l+Ifp6J6PhOeAxU0FssR7nYfbJVetrnLIoh2rd3DlJ6hHYYQosQYcUQ==", + "dependencies": { + "Sentry": "3.33.0", + "Serilog": "2.7.1" + } + }, + "Serilog": { + "type": "Transitive", + "resolved": "2.12.0", + "contentHash": "xaiJLIdu6rYMKfQMYUZgTy8YK7SMZjB4Yk50C/u//Z4OsvxkUfSPJy4nknfvwAC34yr13q7kcyh4grbwhSxyZg==" + }, + "Serilog.Enrichers.ClientInfo": { + "type": "Transitive", + "resolved": "1.3.0", + "contentHash": "mTc7PM+wC9Hr7LWSwqt5mmnlAr7RJs+eTb3PGPRhwdOackk95MkhUZognuxXEdlW19HAFNmEBTSBY5DfLwM8jQ==", + "dependencies": { + "Serilog": "2.4.0" + } + }, + "Serilog.Exceptions": { + "type": "Transitive", + "resolved": "8.4.0", + "contentHash": "nc/+hUw3lsdo0zCj0KMIybAu7perMx79vu72w0za9Nsi6mWyNkGXxYxakAjWB7nEmYL6zdmhEQRB4oJ2ALUeug==", + "dependencies": { + "Serilog": "2.8.0" + } + }, + "Serilog.Formatting.Compact": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "pNroKVjo+rDqlxNG5PXkRLpfSCuDOBY0ri6jp9PLe505ljqwhwZz8ospy2vWhQlFu5GkIesh3FcDs4n7sWZODA==", + "dependencies": { + "Serilog": "2.8.0" + } + }, + "Serilog.Sinks.Console": { + "type": "Transitive", + "resolved": "4.1.0", + "contentHash": "K6N5q+5fetjnJPvCmkWOpJ/V8IEIoMIB1s86OzBrbxwTyHxdx3pmz4H+8+O/Dc/ftUX12DM1aynx/dDowkwzqg==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "Serilog.Sinks.File": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "uwV5hdhWPwUH1szhO8PJpFiahqXmzPzJT/sOijH/kFgUx+cyoDTMM8MHD0adw9+Iem6itoibbUXHYslzXsLEAg==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "Serilog.Sinks.PeriodicBatching": { + "type": "Transitive", + "resolved": "3.1.0", + "contentHash": "NDWR7m3PalVlGEq3rzoktrXikjFMLmpwF0HI4sowo8YDdU+gqPlTHlDQiOGxHfB0sTfjPA9JjA7ctKG9zqjGkw==", + "dependencies": { + "Serilog": "2.0.0" + } + }, + "Serilog.Sinks.Seq": { + "type": "Transitive", + "resolved": "5.2.2", + "contentHash": "1Csmo5ua7NKUe0yXUx+zsRefjAniPWcXFhUXxXG8pwo0iMiw2gjn9SOkgYnnxbgWqmlGv236w0N/dHc2v5XwMg==", + "dependencies": { + "Serilog": "2.12.0", + "Serilog.Formatting.Compact": "1.1.0", + "Serilog.Sinks.File": "5.0.0", + "Serilog.Sinks.PeriodicBatching": "3.1.0" + } + }, + "SerilogTimings": { + "type": "Transitive", + "resolved": "3.0.1", + "contentHash": "Zs28eTgszAMwpIrbBnWHBI50yuxL50p/dmAUWmy75+axdZYK/Sjm5/5m1N/CisR8acJUhTVcjPZrsB1P5iv0Uw==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "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.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, + "System.Collections.Immutable": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "FXkLXiK0sVVewcso0imKQoOxjoPAj42R8HtjjbSjVPAzwDfzoyoznWxgA3c38LDbN9SJux1xXoXYAhz98j7r2g==", + "dependencies": { + "System.Memory": "4.5.4" + } + }, + "System.DoubleNumerics": { + "type": "Transitive", + "resolved": "3.1.3", + "contentHash": "KRKEM/L3KBodjA9VOg3EifFVWUY6EOqaMB05UvPEDm7Zeby/kZW+4kdWUEPzW6xtkwf46p661L9NrbeeQhtLzw==", + "dependencies": { + "NETStandard.Library": "1.6.1" + } + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Numerics.Vectors": "4.5.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + }, + "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.5.0", + "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" + }, + "System.Reactive": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "System.Reflection.Metadata": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "5NecZgXktdGg34rh1OenY1rFNDCI8xSjFr+Z4OU4cU06AQHUdRnIIEeWENu3Wl4YowbzkymAIMvi3WyK9U53pQ==", + "dependencies": { + "System.Collections.Immutable": "5.0.0" + } + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "ZD9TMpsmYJLrxbbmdvhwt9YEgG5WntEnZ/d1eH8JBX9LBp+Ju8BSBhUGbZMNVHHomWo2KVImJhTDl2hIgw/6MA==" + }, + "System.Runtime.InteropServices.RuntimeInformation": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==" + }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "5.0.1", + "contentHash": "KmJ+CJXizDofbq6mpqDoRRLcxgOd2z9X3XoFNULSbvbqVRZkFX3istvr+MUjL6Zw1RT+RNdoI4GYidIINtgvqQ==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Memory": "4.5.4" + } + }, + "System.Text.Json": { + "type": "Transitive", + "resolved": "5.0.2", + "contentHash": "I47dVIGiV6SfAyppphxqupertT/5oZkYLDCX6vC3HpOI4ZLjyoKAreUoem2ie6G0RbRuFrlqz/PcTQjfb2DOfQ==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "5.0.0", + "System.Buffers": "4.5.1", + "System.Memory": "4.5.4", + "System.Numerics.Vectors": "4.5.0", + "System.Runtime.CompilerServices.Unsafe": "5.0.0", + "System.Text.Encodings.Web": "5.0.1", + "System.Threading.Tasks.Extensions": "4.5.4", + "System.ValueTuple": "4.5.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" + } + }, + "System.ValueTuple": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "okurQJO6NRE/apDIP23ajJ0hpiNmJ+f0BwOlB/cSqTLQlw5upkf+5+96+iG2Jw40G1fCVCyPz/FhIABUjMR+RQ==" + }, + "speckle.autofac": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "[7.0.0, )" + } + }, + "speckle.converters.common": { + "type": "Project", + "dependencies": { + "Speckle.Autofac": "[2.0.999-local, )", + "Speckle.Objects": "[3.0.1-alpha.14, )" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "CentralTransitive", + "requested": "[7.0.0, )", + "resolved": "7.0.0", + "contentHash": "kmn78+LPVMOWeITUjIlfxUPDsI0R6G0RkeAMBmQxAJ7vBJn4q2dTva7pWi65ceN5vPGjJ9q/Uae2WKgvfktJAw==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Memory": "4.5.5" + } + }, + "Speckle.Core": { + "type": "CentralTransitive", + "requested": "[3.0.1-alpha.14, )", + "resolved": "3.0.1-alpha.14", + "contentHash": "RzQPVIGFFkKvG56YLr8ACtiwdWJE6IJ9vCQ4qHa0PIsUEpfzAIAi59jnzqtByOFC0FiFrFPow9bkfzylaZorAA==", + "dependencies": { + "GraphQL.Client": "6.0.0", + "Microsoft.CSharp": "4.7.0", + "Microsoft.Data.Sqlite": "7.0.5", + "Polly": "7.2.3", + "Polly.Contrib.WaitAndRetry": "1.1.1", + "Polly.Extensions.Http": "3.0.0", + "Sentry": "3.33.0", + "Sentry.Serilog": "3.33.0", + "Serilog": "2.12.0", + "Serilog.Enrichers.ClientInfo": "1.3.0", + "Serilog.Exceptions": "8.4.0", + "Serilog.Sinks.Console": "4.1.0", + "Serilog.Sinks.Seq": "5.2.2", + "SerilogTimings": "3.0.1", + "Speckle.Newtonsoft.Json": "13.0.2", + "System.DoubleNumerics": "3.1.3" + } + }, + "Speckle.Objects": { + "type": "CentralTransitive", + "requested": "[3.0.1-alpha.14, )", + "resolved": "3.0.1-alpha.14", + "contentHash": "z38LGryMvh7iU1uBW+4uo5DwsB3CwRgLt2uFexWFx3mPSid+A0l5XcJzOgLwgFhNl6B42Ryz4ezBsddTp1Uc/g==", + "dependencies": { + "Speckle.Core": "3.0.1-alpha.14" + } + } + } + } +} \ No newline at end of file diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.Autocad2024.DependencyInjection/AutocadConverterModule.cs b/DUI3-DX/Converters/Autocad/Speckle.Converters.Autocad2024.DependencyInjection/AutocadConverterModule.cs new file mode 100644 index 0000000000..6d9b3a767a --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.Autocad2024.DependencyInjection/AutocadConverterModule.cs @@ -0,0 +1,21 @@ +using Autodesk.AutoCAD.ApplicationServices; +using Autodesk.AutoCAD.DatabaseServices; +using Speckle.Autofac.DependencyInjection; +using Speckle.Converters.Autocad; +using Speckle.Converters.Common; +using Speckle.Converters.Common.DependencyInjection; + +namespace Speckle.Converters.Autocad2024.DependencyInjection; + +public class AutocadConverterModule : ISpeckleModule +{ + public void Load(SpeckleContainerBuilder builder) + { + // add single root converter + builder.AddRootCommon(); + + // add application converters and context stack + builder.AddApplicationConverters(); + builder.AddScoped, AutocadConversionContextStack>(); + } +} diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.Autocad2024.DependencyInjection/Speckle.Converters.Autocad2024.DependencyInjection.csproj b/DUI3-DX/Converters/Autocad/Speckle.Converters.Autocad2024.DependencyInjection/Speckle.Converters.Autocad2024.DependencyInjection.csproj new file mode 100644 index 0000000000..79c93f345c --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.Autocad2024.DependencyInjection/Speckle.Converters.Autocad2024.DependencyInjection.csproj @@ -0,0 +1,17 @@ + + + + net48 + x64 + + + + + + + + + + + + diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.Autocad2024.DependencyInjection/packages.lock.json b/DUI3-DX/Converters/Autocad/Speckle.Converters.Autocad2024.DependencyInjection/packages.lock.json new file mode 100644 index 0000000000..fe9923d926 --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.Autocad2024.DependencyInjection/packages.lock.json @@ -0,0 +1,438 @@ +{ + "version": 2, + "dependencies": { + ".NETFramework,Version=v4.8": { + "Autofac": { + "type": "Direct", + "requested": "[5.2.0, )", + "resolved": "5.2.0", + "contentHash": "V8dBH0dsv75uDzl7Sw+HkhKDPUw2eXnlMjcSVMH+tLo2s67MpTKGyDj1pDcpR+IF2u4YRs0s3/x7R88YJzIWvg==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "1.1.0" + } + }, + "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.5, )", + "resolved": "0.9.5", + "contentHash": "oU/L7pN1R7q8KkbrpQ3WJnHirPHqn+9DEA7asOcUiggV5dzVg1A/VYs7GOSusD24njxXh03tE3a2oTLOjt3cVg==" + }, + "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.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "Polly": { + "type": "Transitive", + "resolved": "7.2.3", + "contentHash": "DeCY0OFbNdNxsjntr1gTXHJ5pKUwYzp04Er2LLeN3g6pWhffsGuKVfMBLe1lw7x76HrPkLxKEFxBlpRxS2nDEQ==" + }, + "Polly.Contrib.WaitAndRetry": { + "type": "Transitive", + "resolved": "1.1.1", + "contentHash": "1MUQLiSo4KDkQe6nzQRhIU05lm9jlexX5BVsbuw0SL82ynZ+GzAHQxJVDPVBboxV37Po3SG077aX8DuSy8TkaA==" + }, + "Polly.Extensions.Http": { + "type": "Transitive", + "resolved": "3.0.0", + "contentHash": "drrG+hB3pYFY7w1c3BD+lSGYvH2oIclH8GRSehgfyP5kjnFnHKQuuBhuHLv+PWyFuaTDyk/vfRpnxOzd11+J8g==", + "dependencies": { + "Polly": "7.1.0" + } + }, + "Sentry": { + "type": "Transitive", + "resolved": "3.33.0", + "contentHash": "8vbD2o6IR2wrRrkSiRbnodWGWUOqIlwYtzpjvPNOb5raJdOf+zxMwfS8f6nx9bmrTTfDj7KrCB8C/5OuicAc8A==", + "dependencies": { + "System.Reflection.Metadata": "5.0.0", + "System.Runtime.InteropServices.RuntimeInformation": "4.3.0", + "System.Text.Json": "5.0.2" + } + }, + "Sentry.Serilog": { + "type": "Transitive", + "resolved": "3.33.0", + "contentHash": "V8BU7QGWg2qLYfNPqtuTBhC1opysny5l+Ifp6J6PhOeAxU0FssR7nYfbJVetrnLIoh2rd3DlJ6hHYYQosQYcUQ==", + "dependencies": { + "Sentry": "3.33.0", + "Serilog": "2.7.1" + } + }, + "Serilog": { + "type": "Transitive", + "resolved": "2.12.0", + "contentHash": "xaiJLIdu6rYMKfQMYUZgTy8YK7SMZjB4Yk50C/u//Z4OsvxkUfSPJy4nknfvwAC34yr13q7kcyh4grbwhSxyZg==" + }, + "Serilog.Enrichers.ClientInfo": { + "type": "Transitive", + "resolved": "1.3.0", + "contentHash": "mTc7PM+wC9Hr7LWSwqt5mmnlAr7RJs+eTb3PGPRhwdOackk95MkhUZognuxXEdlW19HAFNmEBTSBY5DfLwM8jQ==", + "dependencies": { + "Serilog": "2.4.0" + } + }, + "Serilog.Exceptions": { + "type": "Transitive", + "resolved": "8.4.0", + "contentHash": "nc/+hUw3lsdo0zCj0KMIybAu7perMx79vu72w0za9Nsi6mWyNkGXxYxakAjWB7nEmYL6zdmhEQRB4oJ2ALUeug==", + "dependencies": { + "Serilog": "2.8.0" + } + }, + "Serilog.Formatting.Compact": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "pNroKVjo+rDqlxNG5PXkRLpfSCuDOBY0ri6jp9PLe505ljqwhwZz8ospy2vWhQlFu5GkIesh3FcDs4n7sWZODA==", + "dependencies": { + "Serilog": "2.8.0" + } + }, + "Serilog.Sinks.Console": { + "type": "Transitive", + "resolved": "4.1.0", + "contentHash": "K6N5q+5fetjnJPvCmkWOpJ/V8IEIoMIB1s86OzBrbxwTyHxdx3pmz4H+8+O/Dc/ftUX12DM1aynx/dDowkwzqg==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "Serilog.Sinks.File": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "uwV5hdhWPwUH1szhO8PJpFiahqXmzPzJT/sOijH/kFgUx+cyoDTMM8MHD0adw9+Iem6itoibbUXHYslzXsLEAg==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "Serilog.Sinks.PeriodicBatching": { + "type": "Transitive", + "resolved": "3.1.0", + "contentHash": "NDWR7m3PalVlGEq3rzoktrXikjFMLmpwF0HI4sowo8YDdU+gqPlTHlDQiOGxHfB0sTfjPA9JjA7ctKG9zqjGkw==", + "dependencies": { + "Serilog": "2.0.0" + } + }, + "Serilog.Sinks.Seq": { + "type": "Transitive", + "resolved": "5.2.2", + "contentHash": "1Csmo5ua7NKUe0yXUx+zsRefjAniPWcXFhUXxXG8pwo0iMiw2gjn9SOkgYnnxbgWqmlGv236w0N/dHc2v5XwMg==", + "dependencies": { + "Serilog": "2.12.0", + "Serilog.Formatting.Compact": "1.1.0", + "Serilog.Sinks.File": "5.0.0", + "Serilog.Sinks.PeriodicBatching": "3.1.0" + } + }, + "SerilogTimings": { + "type": "Transitive", + "resolved": "3.0.1", + "contentHash": "Zs28eTgszAMwpIrbBnWHBI50yuxL50p/dmAUWmy75+axdZYK/Sjm5/5m1N/CisR8acJUhTVcjPZrsB1P5iv0Uw==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "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.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, + "System.Collections.Immutable": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "FXkLXiK0sVVewcso0imKQoOxjoPAj42R8HtjjbSjVPAzwDfzoyoznWxgA3c38LDbN9SJux1xXoXYAhz98j7r2g==", + "dependencies": { + "System.Memory": "4.5.4" + } + }, + "System.DoubleNumerics": { + "type": "Transitive", + "resolved": "3.1.3", + "contentHash": "KRKEM/L3KBodjA9VOg3EifFVWUY6EOqaMB05UvPEDm7Zeby/kZW+4kdWUEPzW6xtkwf46p661L9NrbeeQhtLzw==", + "dependencies": { + "NETStandard.Library": "1.6.1" + } + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Numerics.Vectors": "4.5.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + }, + "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.5.0", + "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" + }, + "System.Reactive": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "System.Reflection.Metadata": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "5NecZgXktdGg34rh1OenY1rFNDCI8xSjFr+Z4OU4cU06AQHUdRnIIEeWENu3Wl4YowbzkymAIMvi3WyK9U53pQ==", + "dependencies": { + "System.Collections.Immutable": "5.0.0" + } + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "ZD9TMpsmYJLrxbbmdvhwt9YEgG5WntEnZ/d1eH8JBX9LBp+Ju8BSBhUGbZMNVHHomWo2KVImJhTDl2hIgw/6MA==" + }, + "System.Runtime.InteropServices.RuntimeInformation": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==" + }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "5.0.1", + "contentHash": "KmJ+CJXizDofbq6mpqDoRRLcxgOd2z9X3XoFNULSbvbqVRZkFX3istvr+MUjL6Zw1RT+RNdoI4GYidIINtgvqQ==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Memory": "4.5.4" + } + }, + "System.Text.Json": { + "type": "Transitive", + "resolved": "5.0.2", + "contentHash": "I47dVIGiV6SfAyppphxqupertT/5oZkYLDCX6vC3HpOI4ZLjyoKAreUoem2ie6G0RbRuFrlqz/PcTQjfb2DOfQ==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "5.0.0", + "System.Buffers": "4.5.1", + "System.Memory": "4.5.4", + "System.Numerics.Vectors": "4.5.0", + "System.Runtime.CompilerServices.Unsafe": "5.0.0", + "System.Text.Encodings.Web": "5.0.1", + "System.Threading.Tasks.Extensions": "4.5.4", + "System.ValueTuple": "4.5.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" + } + }, + "System.ValueTuple": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "okurQJO6NRE/apDIP23ajJ0hpiNmJ+f0BwOlB/cSqTLQlw5upkf+5+96+iG2Jw40G1fCVCyPz/FhIABUjMR+RQ==" + }, + "speckle.autofac": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "[7.0.0, )" + } + }, + "speckle.converters.autocad2024": { + "type": "Project", + "dependencies": { + "Speckle.AutoCAD.API": "[2024.0.0, )", + "Speckle.Converters.Common": "[2.0.999-local, )" + } + }, + "speckle.converters.common": { + "type": "Project", + "dependencies": { + "Speckle.Autofac": "[2.0.999-local, )", + "Speckle.Objects": "[3.0.1-alpha.14, )" + } + }, + "speckle.converters.common.dependencyinjection": { + "type": "Project", + "dependencies": { + "Speckle.Autofac": "[2.0.999-local, )", + "Speckle.Converters.Common": "[2.0.999-local, )" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "CentralTransitive", + "requested": "[7.0.0, )", + "resolved": "7.0.0", + "contentHash": "kmn78+LPVMOWeITUjIlfxUPDsI0R6G0RkeAMBmQxAJ7vBJn4q2dTva7pWi65ceN5vPGjJ9q/Uae2WKgvfktJAw==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Memory": "4.5.5" + } + }, + "Speckle.AutoCAD.API": { + "type": "CentralTransitive", + "requested": "[2024.0.0, )", + "resolved": "2024.0.0", + "contentHash": "pZZ5uI+NXhZaQnsqRkgp/rywqBAjDObDJ9XNFGJvemT5k2OthDpHzlK/mKxz8QDCYie7uImQ8dv3uWj2QUFDPw==" + }, + "Speckle.Core": { + "type": "CentralTransitive", + "requested": "[3.0.1-alpha.14, )", + "resolved": "3.0.1-alpha.14", + "contentHash": "RzQPVIGFFkKvG56YLr8ACtiwdWJE6IJ9vCQ4qHa0PIsUEpfzAIAi59jnzqtByOFC0FiFrFPow9bkfzylaZorAA==", + "dependencies": { + "GraphQL.Client": "6.0.0", + "Microsoft.CSharp": "4.7.0", + "Microsoft.Data.Sqlite": "7.0.5", + "Polly": "7.2.3", + "Polly.Contrib.WaitAndRetry": "1.1.1", + "Polly.Extensions.Http": "3.0.0", + "Sentry": "3.33.0", + "Sentry.Serilog": "3.33.0", + "Serilog": "2.12.0", + "Serilog.Enrichers.ClientInfo": "1.3.0", + "Serilog.Exceptions": "8.4.0", + "Serilog.Sinks.Console": "4.1.0", + "Serilog.Sinks.Seq": "5.2.2", + "SerilogTimings": "3.0.1", + "Speckle.Newtonsoft.Json": "13.0.2", + "System.DoubleNumerics": "3.1.3" + } + }, + "Speckle.Objects": { + "type": "CentralTransitive", + "requested": "[3.0.1-alpha.14, )", + "resolved": "3.0.1-alpha.14", + "contentHash": "z38LGryMvh7iU1uBW+4uo5DwsB3CwRgLt2uFexWFx3mPSid+A0l5XcJzOgLwgFhNl6B42Ryz4ezBsddTp1Uc/g==", + "dependencies": { + "Speckle.Core": "3.0.1-alpha.14" + } + } + } + } +} \ No newline at end of file diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.Autocad2024/Speckle.Converters.Autocad2024.csproj b/DUI3-DX/Converters/Autocad/Speckle.Converters.Autocad2024/Speckle.Converters.Autocad2024.csproj new file mode 100644 index 0000000000..da1163a6ad --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.Autocad2024/Speckle.Converters.Autocad2024.csproj @@ -0,0 +1,18 @@ + + + + net48 + x64 + + + + + + + + + + + + + diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.Autocad2024/packages.lock.json b/DUI3-DX/Converters/Autocad/Speckle.Converters.Autocad2024/packages.lock.json new file mode 100644 index 0000000000..0a1d936cee --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.Autocad2024/packages.lock.json @@ -0,0 +1,415 @@ +{ + "version": 2, + "dependencies": { + ".NETFramework,Version=v4.8": { + "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.AutoCAD.API": { + "type": "Direct", + "requested": "[2024.0.0, )", + "resolved": "2024.0.0", + "contentHash": "pZZ5uI+NXhZaQnsqRkgp/rywqBAjDObDJ9XNFGJvemT5k2OthDpHzlK/mKxz8QDCYie7uImQ8dv3uWj2QUFDPw==" + }, + "Speckle.InterfaceGenerator": { + "type": "Direct", + "requested": "[0.9.5, )", + "resolved": "0.9.5", + "contentHash": "oU/L7pN1R7q8KkbrpQ3WJnHirPHqn+9DEA7asOcUiggV5dzVg1A/VYs7GOSusD24njxXh03tE3a2oTLOjt3cVg==" + }, + "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.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "Polly": { + "type": "Transitive", + "resolved": "7.2.3", + "contentHash": "DeCY0OFbNdNxsjntr1gTXHJ5pKUwYzp04Er2LLeN3g6pWhffsGuKVfMBLe1lw7x76HrPkLxKEFxBlpRxS2nDEQ==" + }, + "Polly.Contrib.WaitAndRetry": { + "type": "Transitive", + "resolved": "1.1.1", + "contentHash": "1MUQLiSo4KDkQe6nzQRhIU05lm9jlexX5BVsbuw0SL82ynZ+GzAHQxJVDPVBboxV37Po3SG077aX8DuSy8TkaA==" + }, + "Polly.Extensions.Http": { + "type": "Transitive", + "resolved": "3.0.0", + "contentHash": "drrG+hB3pYFY7w1c3BD+lSGYvH2oIclH8GRSehgfyP5kjnFnHKQuuBhuHLv+PWyFuaTDyk/vfRpnxOzd11+J8g==", + "dependencies": { + "Polly": "7.1.0" + } + }, + "Sentry": { + "type": "Transitive", + "resolved": "3.33.0", + "contentHash": "8vbD2o6IR2wrRrkSiRbnodWGWUOqIlwYtzpjvPNOb5raJdOf+zxMwfS8f6nx9bmrTTfDj7KrCB8C/5OuicAc8A==", + "dependencies": { + "System.Reflection.Metadata": "5.0.0", + "System.Runtime.InteropServices.RuntimeInformation": "4.3.0", + "System.Text.Json": "5.0.2" + } + }, + "Sentry.Serilog": { + "type": "Transitive", + "resolved": "3.33.0", + "contentHash": "V8BU7QGWg2qLYfNPqtuTBhC1opysny5l+Ifp6J6PhOeAxU0FssR7nYfbJVetrnLIoh2rd3DlJ6hHYYQosQYcUQ==", + "dependencies": { + "Sentry": "3.33.0", + "Serilog": "2.7.1" + } + }, + "Serilog": { + "type": "Transitive", + "resolved": "2.12.0", + "contentHash": "xaiJLIdu6rYMKfQMYUZgTy8YK7SMZjB4Yk50C/u//Z4OsvxkUfSPJy4nknfvwAC34yr13q7kcyh4grbwhSxyZg==" + }, + "Serilog.Enrichers.ClientInfo": { + "type": "Transitive", + "resolved": "1.3.0", + "contentHash": "mTc7PM+wC9Hr7LWSwqt5mmnlAr7RJs+eTb3PGPRhwdOackk95MkhUZognuxXEdlW19HAFNmEBTSBY5DfLwM8jQ==", + "dependencies": { + "Serilog": "2.4.0" + } + }, + "Serilog.Exceptions": { + "type": "Transitive", + "resolved": "8.4.0", + "contentHash": "nc/+hUw3lsdo0zCj0KMIybAu7perMx79vu72w0za9Nsi6mWyNkGXxYxakAjWB7nEmYL6zdmhEQRB4oJ2ALUeug==", + "dependencies": { + "Serilog": "2.8.0" + } + }, + "Serilog.Formatting.Compact": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "pNroKVjo+rDqlxNG5PXkRLpfSCuDOBY0ri6jp9PLe505ljqwhwZz8ospy2vWhQlFu5GkIesh3FcDs4n7sWZODA==", + "dependencies": { + "Serilog": "2.8.0" + } + }, + "Serilog.Sinks.Console": { + "type": "Transitive", + "resolved": "4.1.0", + "contentHash": "K6N5q+5fetjnJPvCmkWOpJ/V8IEIoMIB1s86OzBrbxwTyHxdx3pmz4H+8+O/Dc/ftUX12DM1aynx/dDowkwzqg==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "Serilog.Sinks.File": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "uwV5hdhWPwUH1szhO8PJpFiahqXmzPzJT/sOijH/kFgUx+cyoDTMM8MHD0adw9+Iem6itoibbUXHYslzXsLEAg==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "Serilog.Sinks.PeriodicBatching": { + "type": "Transitive", + "resolved": "3.1.0", + "contentHash": "NDWR7m3PalVlGEq3rzoktrXikjFMLmpwF0HI4sowo8YDdU+gqPlTHlDQiOGxHfB0sTfjPA9JjA7ctKG9zqjGkw==", + "dependencies": { + "Serilog": "2.0.0" + } + }, + "Serilog.Sinks.Seq": { + "type": "Transitive", + "resolved": "5.2.2", + "contentHash": "1Csmo5ua7NKUe0yXUx+zsRefjAniPWcXFhUXxXG8pwo0iMiw2gjn9SOkgYnnxbgWqmlGv236w0N/dHc2v5XwMg==", + "dependencies": { + "Serilog": "2.12.0", + "Serilog.Formatting.Compact": "1.1.0", + "Serilog.Sinks.File": "5.0.0", + "Serilog.Sinks.PeriodicBatching": "3.1.0" + } + }, + "SerilogTimings": { + "type": "Transitive", + "resolved": "3.0.1", + "contentHash": "Zs28eTgszAMwpIrbBnWHBI50yuxL50p/dmAUWmy75+axdZYK/Sjm5/5m1N/CisR8acJUhTVcjPZrsB1P5iv0Uw==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "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.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, + "System.Collections.Immutable": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "FXkLXiK0sVVewcso0imKQoOxjoPAj42R8HtjjbSjVPAzwDfzoyoznWxgA3c38LDbN9SJux1xXoXYAhz98j7r2g==", + "dependencies": { + "System.Memory": "4.5.4" + } + }, + "System.DoubleNumerics": { + "type": "Transitive", + "resolved": "3.1.3", + "contentHash": "KRKEM/L3KBodjA9VOg3EifFVWUY6EOqaMB05UvPEDm7Zeby/kZW+4kdWUEPzW6xtkwf46p661L9NrbeeQhtLzw==", + "dependencies": { + "NETStandard.Library": "1.6.1" + } + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Numerics.Vectors": "4.5.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + }, + "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.5.0", + "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" + }, + "System.Reactive": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "System.Reflection.Metadata": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "5NecZgXktdGg34rh1OenY1rFNDCI8xSjFr+Z4OU4cU06AQHUdRnIIEeWENu3Wl4YowbzkymAIMvi3WyK9U53pQ==", + "dependencies": { + "System.Collections.Immutable": "5.0.0" + } + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "ZD9TMpsmYJLrxbbmdvhwt9YEgG5WntEnZ/d1eH8JBX9LBp+Ju8BSBhUGbZMNVHHomWo2KVImJhTDl2hIgw/6MA==" + }, + "System.Runtime.InteropServices.RuntimeInformation": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==" + }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "5.0.1", + "contentHash": "KmJ+CJXizDofbq6mpqDoRRLcxgOd2z9X3XoFNULSbvbqVRZkFX3istvr+MUjL6Zw1RT+RNdoI4GYidIINtgvqQ==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Memory": "4.5.4" + } + }, + "System.Text.Json": { + "type": "Transitive", + "resolved": "5.0.2", + "contentHash": "I47dVIGiV6SfAyppphxqupertT/5oZkYLDCX6vC3HpOI4ZLjyoKAreUoem2ie6G0RbRuFrlqz/PcTQjfb2DOfQ==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "5.0.0", + "System.Buffers": "4.5.1", + "System.Memory": "4.5.4", + "System.Numerics.Vectors": "4.5.0", + "System.Runtime.CompilerServices.Unsafe": "5.0.0", + "System.Text.Encodings.Web": "5.0.1", + "System.Threading.Tasks.Extensions": "4.5.4", + "System.ValueTuple": "4.5.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" + } + }, + "System.ValueTuple": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "okurQJO6NRE/apDIP23ajJ0hpiNmJ+f0BwOlB/cSqTLQlw5upkf+5+96+iG2Jw40G1fCVCyPz/FhIABUjMR+RQ==" + }, + "speckle.autofac": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "[7.0.0, )" + } + }, + "speckle.converters.common": { + "type": "Project", + "dependencies": { + "Speckle.Autofac": "[2.0.999-local, )", + "Speckle.Objects": "[3.0.1-alpha.14, )" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "CentralTransitive", + "requested": "[7.0.0, )", + "resolved": "7.0.0", + "contentHash": "kmn78+LPVMOWeITUjIlfxUPDsI0R6G0RkeAMBmQxAJ7vBJn4q2dTva7pWi65ceN5vPGjJ9q/Uae2WKgvfktJAw==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Memory": "4.5.5" + } + }, + "Speckle.Core": { + "type": "CentralTransitive", + "requested": "[3.0.1-alpha.14, )", + "resolved": "3.0.1-alpha.14", + "contentHash": "RzQPVIGFFkKvG56YLr8ACtiwdWJE6IJ9vCQ4qHa0PIsUEpfzAIAi59jnzqtByOFC0FiFrFPow9bkfzylaZorAA==", + "dependencies": { + "GraphQL.Client": "6.0.0", + "Microsoft.CSharp": "4.7.0", + "Microsoft.Data.Sqlite": "7.0.5", + "Polly": "7.2.3", + "Polly.Contrib.WaitAndRetry": "1.1.1", + "Polly.Extensions.Http": "3.0.0", + "Sentry": "3.33.0", + "Sentry.Serilog": "3.33.0", + "Serilog": "2.12.0", + "Serilog.Enrichers.ClientInfo": "1.3.0", + "Serilog.Exceptions": "8.4.0", + "Serilog.Sinks.Console": "4.1.0", + "Serilog.Sinks.Seq": "5.2.2", + "SerilogTimings": "3.0.1", + "Speckle.Newtonsoft.Json": "13.0.2", + "System.DoubleNumerics": "3.1.3" + } + }, + "Speckle.Objects": { + "type": "CentralTransitive", + "requested": "[3.0.1-alpha.14, )", + "resolved": "3.0.1-alpha.14", + "contentHash": "z38LGryMvh7iU1uBW+4uo5DwsB3CwRgLt2uFexWFx3mPSid+A0l5XcJzOgLwgFhNl6B42Ryz4ezBsddTp1Uc/g==", + "dependencies": { + "Speckle.Core": "3.0.1-alpha.14" + } + } + } + } +} \ No newline at end of file diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/AutocadConversionContextStack.cs b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/AutocadConversionContextStack.cs new file mode 100644 index 0000000000..1e08b6596a --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/AutocadConversionContextStack.cs @@ -0,0 +1,21 @@ +using System.Diagnostics.CodeAnalysis; +using Autodesk.AutoCAD.DatabaseServices; +using Speckle.Converters.Common; + +namespace Speckle.Converters.Autocad; + +// POC: Suppressed naming warning for now, but we should evaluate if we should follow this or disable it. +[SuppressMessage( + "Naming", + "CA1711:Identifiers should not have incorrect suffix", + Justification = "Name ends in Stack but it is in fact a Stack, just not inheriting from `System.Collections.Stack`" +)] +public class AutocadConversionContextStack : ConversionContextStack +{ + public AutocadConversionContextStack(IHostToSpeckleUnitConverter unitConverter) + : base( + Application.DocumentManager.CurrentDocument, + Application.DocumentManager.CurrentDocument.Database.Insunits, + unitConverter + ) { } +} diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/AutocadRootToHostConverter.cs b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/AutocadRootToHostConverter.cs new file mode 100644 index 0000000000..4a53d211c2 --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/AutocadRootToHostConverter.cs @@ -0,0 +1,59 @@ +using Autodesk.AutoCAD.DatabaseServices; +using Speckle.Autofac.DependencyInjection; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; + +namespace Speckle.Converters.Autocad; + +public class AutocadRootToHostConverter : IRootToSpeckleConverter +{ + private readonly IFactory _toSpeckle; + private readonly IConversionContextStack _contextStack; + + public AutocadRootToHostConverter( + IFactory toSpeckle, + IConversionContextStack contextStack + ) + { + _toSpeckle = toSpeckle; + _contextStack = contextStack; + } + + public Base Convert(object target) + { + if (target is not DBObject dbObject) + { + throw new NotSupportedException( + $"Conversion of {target.GetType().Name} to Speckle is not supported. Only objects that inherit from DBObject are." + ); + } + + Type type = dbObject.GetType(); + + try + { + using (var l = _contextStack.Current.Document.LockDocument()) + { + using (var tr = _contextStack.Current.Document.Database.TransactionManager.StartTransaction()) + { + var objectConverter = _toSpeckle.ResolveInstance(type.Name); + + if (objectConverter == null) + { + throw new NotSupportedException($"No conversion found for {target.GetType().Name}"); + } + + var convertedObject = objectConverter.Convert(dbObject); + tr.Commit(); + return convertedObject; + } + } + } + catch (SpeckleConversionException e) + { + Console.WriteLine(e); + throw; // Just rethrowing for now, Logs may be needed here. + } + } +} diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/AutocadToSpeckleUnitConverter.cs b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/AutocadToSpeckleUnitConverter.cs new file mode 100644 index 0000000000..b5b611e2d9 --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/AutocadToSpeckleUnitConverter.cs @@ -0,0 +1,38 @@ +using Autodesk.AutoCAD.DatabaseServices; +using Speckle.Converters.Common; +using Speckle.Core.Kits; +using Speckle.Core.Logging; // POC: boy do I think this is the wrong place for SpeckleException! + +namespace Speckle.Converters.Autocad; + +public class AutocadToSpeckleUnitConverter : IHostToSpeckleUnitConverter +{ + private static readonly IReadOnlyDictionary s_unitsMapping = Create(); + + private static IReadOnlyDictionary Create() + { + var dict = new Dictionary(); + // POC: we should have a unit test to confirm these are as expected and don't change + dict[UnitsValue.Undefined] = Units.Meters; + dict[UnitsValue.Millimeters] = Units.Millimeters; + dict[UnitsValue.Centimeters] = Units.Centimeters; + dict[UnitsValue.Meters] = Units.Meters; + dict[UnitsValue.Kilometers] = Units.Kilometers; + dict[UnitsValue.Inches] = Units.Inches; + dict[UnitsValue.Feet] = Units.Feet; + dict[UnitsValue.Yards] = Units.Yards; + dict[UnitsValue.Miles] = Units.Miles; + return dict; + } + + public string ConvertOrThrow(UnitsValue hostUnit) + { + if (s_unitsMapping.TryGetValue(hostUnit, out string value)) + { + return value; + } + + // POC: probably would prefer something more specific + throw new SpeckleException($"The Unit System \"{hostUnit}\" is unsupported."); + } +} diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/Extensions/EntityExtensions.cs b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/Extensions/EntityExtensions.cs new file mode 100644 index 0000000000..9f99c36098 --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/Extensions/EntityExtensions.cs @@ -0,0 +1,56 @@ +using Autodesk.AutoCAD.DatabaseServices; +using System.Collections; + +namespace Speckle.Converters.Autocad.Extensions; + +public static class EntityExtensions +{ + /// + /// Worker for enumeration of IEnumerable entities that return differently typed subentities depending on the database-residency of the entity. + /// + /// + /// Database-resident entities which are enumerable will return ObjectIds of subentities when enumerated, and requires a transaction. + /// Non database-resident entities (eg polyline2ds from exploded blocks) will return the typed subentities, are also not database-resident. + /// Can be used for retrieving in , + /// in and in , + /// in and in . + /// + /// Thrown when source arg is null and owner entity was not of type IEnumerable. + /// Thrown when input transaction is null and owner database has no active top transaction. + public static IEnumerable GetSubEntities( + this Entity owner, + OpenMode mode = OpenMode.ForRead, + Transaction? trans = null, + IEnumerable? source = null, + bool forceOpenOnLockedLayer = false + ) + where T : Entity + { + if ((source ?? owner as IEnumerable) is not IEnumerable enumerable) + { + throw new System.ArgumentException("IEnumerable source is null and owner Entity is not of type IEnumerable."); + } + + if (owner.Database is Database db) + { + if ((trans ?? db.TransactionManager.TopTransaction) is not Transaction tr) + { + throw new System.InvalidOperationException( + "Transaction is null and the owner database has no active top transaction." + ); + } + + foreach (ObjectId id in enumerable) + { + yield return (T)tr.GetObject(id, mode, false, forceOpenOnLockedLayer); + } + } + else + { + foreach (T entity in enumerable) + { + yield return entity; + } + } + } +} diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/Extensions/ListExtensions.cs b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/Extensions/ListExtensions.cs new file mode 100644 index 0000000000..82ea22ce53 --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/Extensions/ListExtensions.cs @@ -0,0 +1,55 @@ +namespace Speckle.Converters.Autocad.Extensions; + +public static class ListExtensions +{ + public static SOG.Polyline ConvertToSpecklePolyline(this List pointList, string speckleUnits) + { + // throw if list is malformed + if (pointList.Count % 3 != 0) + { + throw new System.ArgumentException("Point list of xyz values is malformed."); + } + + return new(pointList, speckleUnits); + } + + public static List ConvertToPoint2d(this List pointList, double conversionFactor = 1) + { + // throw if list is malformed + if (pointList.Count % 2 != 0) + { + throw new System.ArgumentException("Point list of xy values is malformed."); + } + + List points2d = new(pointList.Count / 2); + for (int i = 1; i < pointList.Count; i += 2) + { + points2d.Add(new AG.Point2d(pointList[i - 1] * conversionFactor, pointList[i] * conversionFactor)); + } + + return points2d; + } + + public static List ConvertToPoint3d(this List pointList, double conversionFactor = 1) + { + // throw if list is malformed + if (pointList.Count % 3 != 0) + { + throw new System.ArgumentException("Point list of xyz values is malformed."); + } + + List points3d = new(pointList.Count / 3); + for (int i = 2; i < pointList.Count; i += 3) + { + points3d.Add( + new AG.Point3d( + pointList[i - 2] * conversionFactor, + pointList[i - 1] * conversionFactor, + pointList[i] * conversionFactor + ) + ); + } + + return points3d; + } +} diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/GlobalUsings.cs b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/GlobalUsings.cs new file mode 100644 index 0000000000..7922a59807 --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/GlobalUsings.cs @@ -0,0 +1,7 @@ +global using ADB = Autodesk.AutoCAD.DatabaseServices; +global using AG = Autodesk.AutoCAD.Geometry; +global using ABR = Autodesk.AutoCAD.BoundaryRepresentation; +global using SOG = Objects.Geometry; +global using SOP = Objects.Primitive; +global using Document = Autodesk.AutoCAD.ApplicationServices.Document; +global using Application = Autodesk.AutoCAD.ApplicationServices.Core.Application; diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/Speckle.Converters.AutocadShared.projitems b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/Speckle.Converters.AutocadShared.projitems new file mode 100644 index 0000000000..8fb784a3af --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/Speckle.Converters.AutocadShared.projitems @@ -0,0 +1,67 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + 9add1b7a-6401-4202-8613-f668e2fbc0a4 + + + Speckle.Converters.AutocadShared + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/Speckle.Converters.AutocadShared.shproj b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/Speckle.Converters.AutocadShared.shproj new file mode 100644 index 0000000000..5001bbd32b --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/Speckle.Converters.AutocadShared.shproj @@ -0,0 +1,13 @@ + + + + 9add1b7a-6401-4202-8613-f668e2fbc0a4 + 14.0 + + + + + + + + diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/ArcToHostConverter.cs b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/ArcToHostConverter.cs new file mode 100644 index 0000000000..0d639ab956 --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/ArcToHostConverter.cs @@ -0,0 +1,41 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; + +namespace Speckle.Converters.Autocad.ToHost.Geometry; + +[NameAndRankValue(nameof(SOG.Arc), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class ArcToHostConverter : IToHostTopLevelConverter, ITypedConverter +{ + private readonly ITypedConverter _arcConverter; + private readonly ITypedConverter _planeConverter; + private readonly IConversionContextStack _contextStack; + + public ArcToHostConverter( + ITypedConverter arcConverter, + ITypedConverter planeConverter, + IConversionContextStack contextStack + ) + { + _arcConverter = arcConverter; + _planeConverter = planeConverter; + _contextStack = contextStack; + } + + public object Convert(Base target) => Convert((SOG.Arc)target); + + public ADB.Arc Convert(SOG.Arc target) + { + // the most reliable method to convert to autocad convention is to calculate from start, end, and midpoint + // because of different plane & start/end angle conventions + AG.CircularArc3d circularArc = _arcConverter.Convert(target); + + // calculate adjusted start and end angles from circularArc reference + AG.Plane plane = _planeConverter.Convert(target.plane); + double angle = circularArc.ReferenceVector.AngleOnPlane(plane); + double startAngle = circularArc.StartAngle + angle; + double endAngle = circularArc.EndAngle + angle; + + return new(circularArc.Center, circularArc.Normal, circularArc.Radius, startAngle, endAngle); + } +} diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/AutocadPolycurveToHostConverter.cs b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/AutocadPolycurveToHostConverter.cs new file mode 100644 index 0000000000..2458753639 --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/AutocadPolycurveToHostConverter.cs @@ -0,0 +1,49 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; + +namespace Speckle.Converters.Autocad2023.ToHost.Geometry; + +[NameAndRankValue(nameof(SOG.Autocad.AutocadPolycurve), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class AutocadPolycurveToHostConverter : IToHostTopLevelConverter +{ + private readonly ITypedConverter _polylineConverter; + private readonly ITypedConverter _polyline2dConverter; + private readonly ITypedConverter _polyline3dConverter; + + public AutocadPolycurveToHostConverter( + ITypedConverter polylineConverter, + ITypedConverter polyline2dConverter, + ITypedConverter polyline3dConverter + ) + { + _polylineConverter = polylineConverter; + _polyline2dConverter = polyline2dConverter; + _polyline3dConverter = polyline3dConverter; + } + + public object Convert(Base target) + { + SOG.Autocad.AutocadPolycurve polycurve = (SOG.Autocad.AutocadPolycurve)target; + + switch (polycurve.polyType) + { + case SOG.Autocad.AutocadPolyType.Light: + return _polylineConverter.Convert(polycurve); + + case SOG.Autocad.AutocadPolyType.Simple2d: + case SOG.Autocad.AutocadPolyType.FitCurve2d: + case SOG.Autocad.AutocadPolyType.CubicSpline2d: + case SOG.Autocad.AutocadPolyType.QuadSpline2d: + return _polyline2dConverter.Convert(polycurve); + + case SOG.Autocad.AutocadPolyType.Simple3d: + case SOG.Autocad.AutocadPolyType.CubicSpline3d: + case SOG.Autocad.AutocadPolyType.QuadSpline3d: + return _polyline3dConverter.Convert(polycurve); + + default: + throw new SpeckleConversionException("Unknown poly type for AutocadPolycurve"); + } + } +} diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/CircleToHostConverter.cs b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/CircleToHostConverter.cs new file mode 100644 index 0000000000..6ec53e825f --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/CircleToHostConverter.cs @@ -0,0 +1,42 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Kits; +using Speckle.Core.Models; + +namespace Speckle.Converters.Autocad.ToHost.Geometry; + +[NameAndRankValue(nameof(SOG.Circle), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class CircleToHostConverter : IToHostTopLevelConverter, ITypedConverter +{ + private readonly ITypedConverter _pointConverter; + private readonly ITypedConverter _vectorConverter; + private readonly IConversionContextStack _contextStack; + + public CircleToHostConverter( + ITypedConverter pointConverter, + ITypedConverter vectorConverter, + IConversionContextStack contextStack + ) + { + _pointConverter = pointConverter; + _vectorConverter = vectorConverter; + _contextStack = contextStack; + } + + public object Convert(Base target) => Convert((SOG.Circle)target); + + public ADB.Circle Convert(SOG.Circle target) + { + AG.Vector3d normal = _vectorConverter.Convert(target.plane.normal); + AG.Point3d origin = _pointConverter.Convert(target.plane.origin); + double f = Units.GetConversionFactor(target.units, _contextStack.Current.SpeckleUnits); + + if (target.radius is null) + { + throw new ArgumentNullException(nameof(target), "Cannot convert circle without radius value."); + } + + var radius = f * (double)target.radius; + return new(origin, normal, radius); + } +} diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/CurveToHostConverter.cs b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/CurveToHostConverter.cs new file mode 100644 index 0000000000..2f49ebad17 --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/CurveToHostConverter.cs @@ -0,0 +1,20 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; + +namespace Speckle.Converters.AutocadShared.ToHost.Geometry; + +[NameAndRankValue(nameof(SOG.Curve), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class CurveToHostConverter : IToHostTopLevelConverter, ITypedConverter +{ + private readonly ITypedConverter _curveConverter; + + public CurveToHostConverter(ITypedConverter curveConverter) + { + _curveConverter = curveConverter; + } + + public object Convert(Base target) => Convert((SOG.Curve)target); + + public ADB.Curve Convert(SOG.Curve target) => ADB.Curve.CreateFromGeCurve(_curveConverter.Convert(target)); +} diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/EllipseToHostConverter.cs b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/EllipseToHostConverter.cs new file mode 100644 index 0000000000..58a59a6803 --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/EllipseToHostConverter.cs @@ -0,0 +1,65 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Kits; +using Speckle.Core.Models; + +namespace Speckle.Converters.Autocad.ToHost.Geometry; + +[NameAndRankValue(nameof(SOG.Ellipse), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class EllipseToHostConverter : IToHostTopLevelConverter, ITypedConverter +{ + private readonly ITypedConverter _pointConverter; + private readonly ITypedConverter _vectorConverter; + private readonly IConversionContextStack _contextStack; + + public EllipseToHostConverter( + ITypedConverter pointConverter, + ITypedConverter vectorConverter, + IConversionContextStack contextStack + ) + { + _pointConverter = pointConverter; + _vectorConverter = vectorConverter; + _contextStack = contextStack; + } + + public object Convert(Base target) => Convert((SOG.Ellipse)target); + + /// Throws if any ellipse radius value is null. + public ADB.Ellipse Convert(SOG.Ellipse target) + { + double f = Units.GetConversionFactor(target.units, _contextStack.Current.SpeckleUnits); + AG.Point3d origin = _pointConverter.Convert(target.plane.origin); + AG.Vector3d normal = _vectorConverter.Convert(target.plane.normal); + AG.Vector3d xAxis = _vectorConverter.Convert(target.plane.xdir); + + // POC: how possibly we might have firstRadius and secondRadius is possibly null? + if (target.firstRadius is null || target.secondRadius is null) + { + throw new ArgumentNullException(nameof(target), "Cannot convert ellipse without radius values."); + } + + AG.Vector3d majorAxis = f * (double)target.firstRadius * xAxis.GetNormal(); + double radiusRatio = (double)target.secondRadius / (double)target.firstRadius; + + // get trim + double startAngle = 0; + double endAngle = Math.PI * 2; + if ( + target.domain.start is double domainStart + && target.domain.end is double domainEnd + && target.trimDomain is SOP.Interval trim + && trim.start is double start + && trim.end is double end + ) + { + // normalize the start and end trim values to [0,2pi] + startAngle = (start - domainStart) / (domainEnd - domainStart) * Math.PI * 2; + endAngle = (end - domainStart) / (domainEnd - domainStart) * Math.PI * 2; + } + + ADB.Ellipse ellipse = new(origin, normal, majorAxis, radiusRatio, startAngle, endAngle); + + return ellipse; + } +} diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/LineToHostConverter.cs b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/LineToHostConverter.cs new file mode 100644 index 0000000000..d1144816a8 --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/LineToHostConverter.cs @@ -0,0 +1,21 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; + +namespace Speckle.Converters.Autocad.ToHost.Geometry; + +[NameAndRankValue(nameof(SOG.Line), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class LineToHostConverter : IToHostTopLevelConverter, ITypedConverter +{ + private readonly ITypedConverter _pointConverter; + + public LineToHostConverter(ITypedConverter pointConverter) + { + _pointConverter = pointConverter; + } + + public object Convert(Base target) => Convert((SOG.Line)target); + + public ADB.Line Convert(SOG.Line target) => + new(_pointConverter.Convert(target.start), _pointConverter.Convert(target.end)); +} diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/MeshToHostConverter.cs b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/MeshToHostConverter.cs new file mode 100644 index 0000000000..5a90a27d1d --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/MeshToHostConverter.cs @@ -0,0 +1,115 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; +using Objects.Utils; +using Speckle.Core.Logging; + +namespace Speckle.Converters.Autocad.Geometry; + +[NameAndRankValue(nameof(SOG.Mesh), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class MeshToHostConverter : IToHostTopLevelConverter, ITypedConverter +{ + private readonly ITypedConverter _pointConverter; + private readonly IConversionContextStack _contextStack; + + public MeshToHostConverter( + ITypedConverter pointConverter, + IConversionContextStack contextStack + ) + { + _pointConverter = pointConverter; + _contextStack = contextStack; + } + + public object Convert(Base target) => Convert((SOG.Mesh)target); + + /// + /// Mesh conversion requires transaction since it's vertices needed to be added into database in advance.. + /// + public ADB.PolyFaceMesh Convert(SOG.Mesh target) + { + target.TriangulateMesh(true); + + // get vertex points + using AG.Point3dCollection vertices = new(); + List points = target.GetPoints().Select(o => _pointConverter.Convert(o)).ToList(); + foreach (var point in points) + { + vertices.Add(point); + } + + ADB.PolyFaceMesh mesh = new(); + + ADB.Transaction tr = _contextStack.Current.Document.TransactionManager.TopTransaction; + + mesh.SetDatabaseDefaults(); + + // append mesh to blocktable record - necessary before adding vertices and faces + var btr = (ADB.BlockTableRecord) + tr.GetObject(_contextStack.Current.Document.Database.CurrentSpaceId, ADB.OpenMode.ForWrite); + btr.AppendEntity(mesh); + tr.AddNewlyCreatedDBObject(mesh, true); + + // add polyfacemesh vertices + for (int i = 0; i < vertices.Count; i++) + { + var vertex = new ADB.PolyFaceMeshVertex(points[i]); + if (i < target.colors.Count) + { + try + { + if (System.Drawing.Color.FromArgb(target.colors[i]) is System.Drawing.Color color) + { + vertex.Color = Autodesk.AutoCAD.Colors.Color.FromRgb(color.R, color.G, color.B); + } + } + catch (System.Exception e) when (!e.IsFatal()) + { + // POC: should we warn user? + // Couldn't set vertex color, but this should not prevent conversion. + } + } + + if (vertex.IsNewObject) + { + mesh.AppendVertex(vertex); + tr.AddNewlyCreatedDBObject(vertex, true); + } + } + + // add polyfacemesh faces. vertex index starts at 1 sigh + int j = 0; + while (j < target.faces.Count) + { + ADB.FaceRecord face; + if (target.faces[j] == 3) // triangle + { + face = new ADB.FaceRecord( + (short)(target.faces[j + 1] + 1), + (short)(target.faces[j + 2] + 1), + (short)(target.faces[j + 3] + 1), + 0 + ); + j += 4; + } + else // quad + { + face = new ADB.FaceRecord( + (short)(target.faces[j + 1] + 1), + (short)(target.faces[j + 2] + 1), + (short)(target.faces[j + 3] + 1), + (short)(target.faces[j + 4] + 1) + ); + j += 5; + } + + if (face.IsNewObject) + { + mesh.AppendFaceRecord(face); + tr.AddNewlyCreatedDBObject(face, true); + } + } + + return mesh; + } +} diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/PointToHostConverter.cs b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/PointToHostConverter.cs new file mode 100644 index 0000000000..5816423983 --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/PointToHostConverter.cs @@ -0,0 +1,20 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; + +namespace Speckle.Converters.Autocad.ToHost.Geometry; + +[NameAndRankValue(nameof(SOG.Point), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class PointToHostConverter : IToHostTopLevelConverter, ITypedConverter +{ + private readonly ITypedConverter _pointConverter; + + public PointToHostConverter(ITypedConverter pointConverter) + { + _pointConverter = pointConverter; + } + + public object Convert(Base target) => Convert((SOG.Point)target); + + public ADB.DBPoint Convert(SOG.Point target) => new(_pointConverter.Convert(target)); +} diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/PolycurveToHostConverter.cs b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/PolycurveToHostConverter.cs new file mode 100644 index 0000000000..7ba28edb9e --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/PolycurveToHostConverter.cs @@ -0,0 +1,89 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; + +namespace Speckle.Converters.AutocadShared.ToHost.Geometry; + +/// +/// A polycurve has segments as list and it can contain different kind of ICurve objects like Arc, Line, Polyline, Curve etc.. +/// 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 +{ + private readonly ITypedConverter _polylineConverter; + private readonly ITypedConverter> _splineConverter; + + public PolycurveToHostConverter( + ITypedConverter polylineConverter, + ITypedConverter> splineConverter + ) + { + _polylineConverter = polylineConverter; + _splineConverter = splineConverter; + } + + public object Convert(Base 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); + + if (convertAsSpline || !isPlanar) + { + return _splineConverter.Convert(polycurve); + } + else + { + return _polylineConverter.Convert(polycurve); + } + } + + private bool IsPolycurvePlanar(SOG.Polycurve polycurve) + { + double? z = null; + foreach (Objects.ICurve segment in polycurve.segments) + { + switch (segment) + { + case SOG.Line o: + z ??= o.start.z; + if (o.start.z != z || o.end.z != z) + { + return false; + } + + break; + case SOG.Arc o: + z ??= o.startPoint.z; + if (o.startPoint.z != z || o.midPoint.z != z || o.endPoint.z != z) + { + return false; + } + + break; + case SOG.Curve o: + z ??= o.points[2]; + for (int i = 2; i < o.points.Count; i += 3) + { + if (o.points[i] != z) + { + return false; + } + } + + break; + case SOG.Spiral o: + z ??= o.startPoint.z; + if (o.startPoint.z != z || o.endPoint.z != z) + { + return false; + } + + break; + } + } + return true; + } +} diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/PolylineToHostConverter.cs b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/PolylineToHostConverter.cs new file mode 100644 index 0000000000..7bbf367950 --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/PolylineToHostConverter.cs @@ -0,0 +1,25 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; + +namespace Speckle.Converters.Autocad.ToHost.Geometry; + +[NameAndRankValue(nameof(SOG.Polyline), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class PolylineToHostConverter : IToHostTopLevelConverter, ITypedConverter +{ + private readonly ITypedConverter _pointConverter; + + public PolylineToHostConverter(ITypedConverter pointConverter) + { + _pointConverter = pointConverter; + } + + public object Convert(Base target) => Convert((SOG.Polyline)target); + + public ADB.Polyline3d Convert(SOG.Polyline target) + { + AG.Point3dCollection vertices = new(); + target.GetPoints().ForEach(o => vertices.Add(_pointConverter.Convert(o))); + return new(ADB.Poly3dType.SimplePoly, vertices, target.closed); + } +} diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/SpeckleFallbackToHostConversion.cs b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/SpeckleFallbackToHostConversion.cs new file mode 100644 index 0000000000..113ae43963 --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Geometry/SpeckleFallbackToHostConversion.cs @@ -0,0 +1,46 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.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; + + public SpeckleFallbackToAutocadTopLevelConverter( + ITypedConverter lineConverter, + ITypedConverter polylineConverter, + ITypedConverter meshConverter + ) + { + _lineConverter = lineConverter; + _polylineConverter = polylineConverter; + _meshConverter = meshConverter; + } + + 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), + _ => throw new NotSupportedException($"Found unsupported fallback geometry: {item.GetType()}") + }; + result.Add(x); + } + + return result; + } +} diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Raw/ArcToHostRawConverter.cs b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Raw/ArcToHostRawConverter.cs new file mode 100644 index 0000000000..73158e8970 --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Raw/ArcToHostRawConverter.cs @@ -0,0 +1,42 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; + +namespace Speckle.Converters.Autocad.ToHost.Raw; + +[NameAndRankValue(nameof(SOG.Arc), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class ArcToHostRowConverter : ITypedConverter +{ + private readonly ITypedConverter _pointConverter; + private readonly ITypedConverter _vectorConverter; + + public ArcToHostRowConverter( + ITypedConverter pointConverter, + ITypedConverter vectorConverter + ) + { + _pointConverter = pointConverter; + _vectorConverter = vectorConverter; + } + + public object Convert(Base target) => Convert((SOG.Arc)target); + + public AG.CircularArc3d Convert(SOG.Arc target) + { + AG.Point3d start = _pointConverter.Convert(target.startPoint); + AG.Point3d end = _pointConverter.Convert(target.endPoint); + AG.Point3d mid = _pointConverter.Convert(target.midPoint); + AG.CircularArc3d arc = new(start, mid, end); + + AG.Vector3d normal = _vectorConverter.Convert(target.plane.normal); + AG.Vector3d xdir = _vectorConverter.Convert(target.plane.xdir); + arc.SetAxes(normal, xdir); + + if (target.startAngle is double startAngle && target.endAngle is double endAngle) + { + arc.SetAngles(startAngle, endAngle); + } + + return arc; + } +} diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Raw/AutocadPolycurveToHostPolyline2dRawConverter.cs b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Raw/AutocadPolycurveToHostPolyline2dRawConverter.cs new file mode 100644 index 0000000000..8c78f79845 --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Raw/AutocadPolycurveToHostPolyline2dRawConverter.cs @@ -0,0 +1,107 @@ +using Speckle.Converters.Autocad.Extensions; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Kits; + +namespace Speckle.Converters.Autocad2023.ToHost.Raw; + +public class AutocadPolycurveToHostPolyline2dRawConverter + : ITypedConverter +{ + private readonly ITypedConverter _vectorConverter; + private readonly IConversionContextStack _contextStack; + + public AutocadPolycurveToHostPolyline2dRawConverter( + ITypedConverter vectorConverter, + IConversionContextStack contextStack + ) + { + _vectorConverter = vectorConverter; + _contextStack = contextStack; + } + + public ADB.Polyline2d Convert(SOG.Autocad.AutocadPolycurve target) + { + // check for normal + if (target.normal is not SOG.Vector normal) + { + throw new System.ArgumentException($"Autocad polycurve of type {target.polyType} did not have a normal"); + } + + // check for elevation + if (target.elevation is not double elevation) + { + throw new System.ArgumentException($"Autocad polycurve of type {target.polyType} did not have an elevation"); + } + + // get vertices + double f = Units.GetConversionFactor(target.units, _contextStack.Current.SpeckleUnits); + List points = target.value.ConvertToPoint3d(f); + + // check for invalid bulges + if (target.bulges is null || target.bulges.Count < points.Count) + { + throw new System.ArgumentException($"Autocad polycurve of type {target.polyType} had null or malformed bulges"); + } + + // check for invalid tangents + if (target.tangents is null || target.tangents.Count < points.Count) + { + throw new System.ArgumentException($"Autocad polycurve of type {target.polyType} had null or malformed tangents"); + } + + // create the polyline2d using the empty constructor + AG.Vector3d convertedNormal = _vectorConverter.Convert(normal); + double convertedElevation = elevation * f; + ADB.Polyline2d polyline = + new() + { + Elevation = convertedElevation, + Normal = convertedNormal, + Closed = target.closed + }; + + // add polyline2d to document + ADB.Transaction tr = _contextStack.Current.Document.TransactionManager.TopTransaction; + var btr = (ADB.BlockTableRecord) + tr.GetObject(_contextStack.Current.Document.Database.CurrentSpaceId, ADB.OpenMode.ForWrite); + btr.AppendEntity(polyline); + tr.AddNewlyCreatedDBObject(polyline, true); + + // append vertices + for (int i = 0; i < points.Count; i++) + { + double? tangent = target.tangents[i]; + ADB.Vertex2d vertex = new(points[i], target.bulges[i], 0, 0, tangent ?? 0); + if (tangent is not null) + { + vertex.TangentUsed = true; + } + + polyline.AppendVertex(vertex); + tr.AddNewlyCreatedDBObject(vertex, true); + } + + // convert to polytype + ADB.Poly2dType polyType = ADB.Poly2dType.SimplePoly; + switch (target.polyType) + { + case SOG.Autocad.AutocadPolyType.FitCurve2d: + polyType = ADB.Poly2dType.FitCurvePoly; + break; + case SOG.Autocad.AutocadPolyType.CubicSpline2d: + polyType = ADB.Poly2dType.CubicSplinePoly; + break; + case SOG.Autocad.AutocadPolyType.QuadSpline2d: + polyType = ADB.Poly2dType.QuadSplinePoly; + break; + } + + if (polyType is not ADB.Poly2dType.SimplePoly) + { + polyline.ConvertToPolyType(polyType); + } + + return polyline; + } +} diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Raw/AutocadPolycurveToHostPolyline3dRawConverter.cs b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Raw/AutocadPolycurveToHostPolyline3dRawConverter.cs new file mode 100644 index 0000000000..1444d8da6a --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Raw/AutocadPolycurveToHostPolyline3dRawConverter.cs @@ -0,0 +1,61 @@ +using Speckle.Converters.Autocad.Extensions; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Kits; +using Speckle.Converters.Common; + +namespace Speckle.Converters.Autocad2023.ToHost.Raw; + +public class AutocadPolycurveToHostPolyline3dRawConverter + : ITypedConverter +{ + private readonly IConversionContextStack _contextStack; + + public AutocadPolycurveToHostPolyline3dRawConverter(IConversionContextStack contextStack) + { + _contextStack = contextStack; + } + + public ADB.Polyline3d Convert(SOG.Autocad.AutocadPolycurve target) + { + // get vertices + double f = Units.GetConversionFactor(target.units, _contextStack.Current.SpeckleUnits); + List points = target.value.ConvertToPoint3d(f); + + // create the polyline3d using the empty constructor + ADB.Polyline3d polyline = new() { Closed = target.closed }; + + // add polyline3d to document + ADB.Transaction tr = _contextStack.Current.Document.TransactionManager.TopTransaction; + var btr = (ADB.BlockTableRecord) + tr.GetObject(_contextStack.Current.Document.Database.CurrentSpaceId, ADB.OpenMode.ForWrite); + btr.AppendEntity(polyline); + tr.AddNewlyCreatedDBObject(polyline, true); + + // append vertices + for (int i = 0; i < points.Count; i++) + { + ADB.PolylineVertex3d vertex = new(points[i]); + polyline.AppendVertex(vertex); + tr.AddNewlyCreatedDBObject(vertex, true); + } + + // convert to polytype + ADB.Poly3dType polyType = ADB.Poly3dType.SimplePoly; + switch (target.polyType) + { + case SOG.Autocad.AutocadPolyType.CubicSpline3d: + polyType = ADB.Poly3dType.CubicSplinePoly; + break; + case SOG.Autocad.AutocadPolyType.QuadSpline3d: + polyType = ADB.Poly3dType.QuadSplinePoly; + break; + } + + if (polyType is not ADB.Poly3dType.SimplePoly) + { + polyline.ConvertToPolyType(polyType); + } + + return polyline; + } +} diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Raw/AutocadPolycurveToHostPolylineRawConverter.cs b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Raw/AutocadPolycurveToHostPolylineRawConverter.cs new file mode 100644 index 0000000000..a6dd91aa1c --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Raw/AutocadPolycurveToHostPolylineRawConverter.cs @@ -0,0 +1,50 @@ +using Speckle.Converters.Autocad.Extensions; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Kits; + +namespace Speckle.Converters.Autocad2023.ToHost.Raw; + +public class AutocadPolycurveToHostPolylineRawConverter : ITypedConverter +{ + private readonly ITypedConverter _vectorConverter; + private readonly IConversionContextStack _contextStack; + + public AutocadPolycurveToHostPolylineRawConverter( + ITypedConverter vectorConverter, + IConversionContextStack contextStack + ) + { + _vectorConverter = vectorConverter; + _contextStack = contextStack; + } + + public ADB.Polyline Convert(SOG.Autocad.AutocadPolycurve target) + { + if (target.normal is null || target.elevation is null) + { + throw new System.ArgumentException( + "Autocad polycurve of type light did not have a valid normal and/or elevation" + ); + } + + double f = Units.GetConversionFactor(target.units, _contextStack.Current.SpeckleUnits); + List points2d = target.value.ConvertToPoint2d(f); + + ADB.Polyline polyline = + new() + { + Normal = _vectorConverter.Convert(target.normal), + Elevation = (double)target.elevation * f, + Closed = target.closed + }; + + for (int i = 0; i < points2d.Count; i++) + { + var bulge = target.bulges is null ? 0 : target.bulges[i]; + polyline.AddVertexAt(i, points2d[i], bulge, 0, 0); + } + + return polyline; + } +} diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Raw/CurveToHostRawConverter.cs b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Raw/CurveToHostRawConverter.cs new file mode 100644 index 0000000000..5e3e6cc805 --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Raw/CurveToHostRawConverter.cs @@ -0,0 +1,74 @@ +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.AutocadShared.ToHost.Raw; + +public class CurveToHostRawConverter : ITypedConverter +{ + private readonly ITypedConverter _pointConverter; + private readonly ITypedConverter _intervalConverter; + + public CurveToHostRawConverter( + ITypedConverter pointConverter, + ITypedConverter intervalConverter + ) + { + _pointConverter = pointConverter; + _intervalConverter = intervalConverter; + } + + public AG.NurbCurve3d Convert(SOG.Curve target) + { + var points = target.GetPoints().Select(p => _pointConverter.Convert(p)).ToList(); + if (target.closed && target.periodic) + { + points = points.GetRange(0, points.Count - target.degree); + } + + var pointCollection = new AG.Point3dCollection(points.ToArray()); + + // process knots + // NOTE: Autocad defines spline knots as a vector of size # control points + degree + 1. (# at start and end should match degree) + // Conversions for autocad need to make sure this is satisfied, otherwise will cause protected mem crash. + // NOTE: for **closed periodic** curves that have "n" control pts, # of knots should be n + 1. Remove degree = 3 knots from start and end. + List knotList = target.knots; + if (target.knots.Count == points.Count + target.degree - 1) // handles rhino format curves + { + knotList.Insert(0, knotList[0]); + knotList.Insert(knotList.Count - 1, knotList[^1]); + } + if (target.closed && target.periodic) // handles closed periodic curves + { + knotList = knotList.GetRange(target.degree, knotList.Count - target.degree * 2); + } + + var knots = new AG.KnotCollection(); + foreach (var knot in knotList) + { + knots.Add(knot); + } + + // process weights + // NOTE: if all weights are the same, autocad convention is to pass an empty list (this will assign them a value of -1) + var weightsList = target.weights; + if (target.closed && target.periodic) // handles closed periodic curves + { + weightsList = target.weights.GetRange(0, points.Count); + } + + AG.DoubleCollection weights; + weights = + (weightsList.Distinct().Count() == 1) + ? new AG.DoubleCollection() + : new AG.DoubleCollection(weightsList.ToArray()); + + AG.NurbCurve3d curve = new(target.degree, knots, pointCollection, weights, target.periodic); + if (target.closed) + { + curve.MakeClosed(); + } + + curve.SetInterval(_intervalConverter.Convert(target.domain)); + + return curve; + } +} diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Raw/IntervalToHostRawConverter.cs b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Raw/IntervalToHostRawConverter.cs new file mode 100644 index 0000000000..713266fba4 --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Raw/IntervalToHostRawConverter.cs @@ -0,0 +1,18 @@ +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.AutocadShared.ToHost.Raw; + +public class IntervalToHostRawConverter : ITypedConverter +{ + /// Throws if target start or end value is null. + public AG.Interval Convert(SOP.Interval target) + { + // POC: the tolerance might be in some settings or in some context? + if (target.start is null || target.end is null) + { + throw new ArgumentNullException(nameof(target), "Cannot convert interval without start or end values."); + } + + return new((double)target.start, (double)target.end, 0.000); + } +} diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Raw/PlaneToHostRawConverter.cs b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Raw/PlaneToHostRawConverter.cs new file mode 100644 index 0000000000..6b100dd28e --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Raw/PlaneToHostRawConverter.cs @@ -0,0 +1,24 @@ +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; + +namespace Speckle.Converters.Autocad.ToHost.Raw; + +public class PlaneToHostRawConverter : ITypedConverter +{ + private readonly ITypedConverter _pointConverter; + private readonly ITypedConverter _vectorConverter; + + public PlaneToHostRawConverter( + ITypedConverter pointConverter, + ITypedConverter vectorConverter + ) + { + _pointConverter = pointConverter; + _vectorConverter = vectorConverter; + } + + public object Convert(Base target) => Convert((SOG.Plane)target); + + public AG.Plane Convert(SOG.Plane target) => + new(_pointConverter.Convert(target.origin), _vectorConverter.Convert(target.normal)); +} diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Raw/PointToHostRawConverter.cs b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Raw/PointToHostRawConverter.cs new file mode 100644 index 0000000000..467448fbe8 --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Raw/PointToHostRawConverter.cs @@ -0,0 +1,22 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Kits; + +namespace Speckle.Converters.Autocad.ToHost.Raw; + +public class PointToHostRawConverter : ITypedConverter +{ + private readonly IConversionContextStack _contextStack; + + public PointToHostRawConverter(IConversionContextStack contextStack) + { + _contextStack = contextStack; + } + + public AG.Point3d Convert(SOG.Point target) + { + double f = Units.GetConversionFactor(target.units, _contextStack.Current.SpeckleUnits); + AG.Point3d point = new(target.x * f, target.y * f, target.z * f); + return point; + } +} diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Raw/PolycurveToHostPolylineRawConverter.cs b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Raw/PolycurveToHostPolylineRawConverter.cs new file mode 100644 index 0000000000..1a3425ebe7 --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Raw/PolycurveToHostPolylineRawConverter.cs @@ -0,0 +1,93 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.AutocadShared.ToHost.Raw; + +/// +/// If polycurve segments consist of only with Line and Arc, we convert it as ADB.Polyline. +/// +public class PolycurveToHostPolylineRawConverter : ITypedConverter +{ + private readonly IConversionContextStack _contextStack; + private readonly ITypedConverter _pointConverter; + + public PolycurveToHostPolylineRawConverter( + IConversionContextStack contextStack, + ITypedConverter pointConverter + ) + { + _contextStack = contextStack; + _pointConverter = pointConverter; + } + + public ADB.Polyline Convert(SOG.Polycurve target) + { + ADB.Polyline polyline = new() { Closed = target.closed }; + AG.Plane plane = + new( + AG.Point3d.Origin, + AG.Vector3d.ZAxis.TransformBy(_contextStack.Current.Document.Editor.CurrentUserCoordinateSystem) + ); + + int count = 0; + foreach (Objects.ICurve segment in target.segments) + { + switch (segment) + { + case SOG.Line o: + polyline.AddVertexAt(count, _pointConverter.Convert(o.start).Convert2d(plane), 0, 0, 0); + if (!target.closed && count == target.segments.Count - 1) + { + polyline.AddVertexAt(count + 1, _pointConverter.Convert(o.end).Convert2d(plane), 0, 0, 0); + } + + count++; + break; + case SOG.Arc arc: + // POC: possibly endAngle and startAngle null? + double? angle = arc.endAngle - arc.startAngle; + angle = angle < 0 ? angle + 2 * Math.PI : angle; + if (angle is null) + { + throw new ArgumentNullException(nameof(target), "Cannot convert arc without angle value."); + } + + var bulge = Math.Tan((double)angle / 4) * BulgeDirection(arc.startPoint, arc.midPoint, arc.endPoint); + polyline.AddVertexAt(count, _pointConverter.Convert(arc.startPoint).Convert2d(plane), bulge, 0, 0); + if (!target.closed && count == target.segments.Count - 1) + { + polyline.AddVertexAt(count + 1, _pointConverter.Convert(arc.endPoint).Convert2d(plane), 0, 0, 0); + } + + count++; + break; + case SOG.Spiral o: + List vertices = o.displayValue.GetPoints().Select(_pointConverter.Convert).ToList(); + foreach (AG.Point3d vertex in vertices) + { + polyline.AddVertexAt(count, vertex.Convert2d(plane), 0, 0, 0); + count++; + } + + break; + default: + break; + } + } + + return polyline; + } + + // calculates bulge direction: (-) clockwise, (+) counterclockwise + private int BulgeDirection(SOG.Point start, SOG.Point mid, SOG.Point end) + { + // get vectors from points + double[] v1 = new double[] { end.x - start.x, end.y - start.y, end.z - start.z }; // vector from start to end point + double[] v2 = new double[] { mid.x - start.x, mid.y - start.y, mid.z - start.z }; // vector from start to mid point + + // calculate cross product z direction + double z = v1[0] * v2[1] - v2[0] * v1[1]; + + return z > 0 ? -1 : 1; + } +} diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Raw/PolycurveToHostSplineRawConverter.cs b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Raw/PolycurveToHostSplineRawConverter.cs new file mode 100644 index 0000000000..c9c1e1ce66 --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Raw/PolycurveToHostSplineRawConverter.cs @@ -0,0 +1,57 @@ +using Speckle.Converters.Common.Objects; + +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> +{ + private readonly ITypedConverter _lineConverter; + private readonly ITypedConverter _polylineConverter; + private readonly ITypedConverter _arcConverter; + private readonly ITypedConverter _curveConverter; + + public PolycurveToHostSplineRawConverter( + ITypedConverter lineConverter, + ITypedConverter polylineConverter, + ITypedConverter arcConverter, + ITypedConverter curveConverter + ) + { + _lineConverter = lineConverter; + _polylineConverter = polylineConverter; + _arcConverter = arcConverter; + _curveConverter = curveConverter; + } + + public List 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? + var list = new List(); + + foreach (var segment in target.segments) + { + switch (segment) + { + case SOG.Arc arc: + list.Add(_arcConverter.Convert(arc)); + break; + case SOG.Line line: + list.Add(_lineConverter.Convert(line)); + break; + case SOG.Polyline polyline: + list.Add(_polylineConverter.Convert(polyline)); + break; + case SOG.Curve curve: + list.Add(_curveConverter.Convert(curve)); + break; + default: + break; + } + } + + return list; + } +} diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Raw/VectorToHostRawConverter.cs b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Raw/VectorToHostRawConverter.cs new file mode 100644 index 0000000000..5897dcf651 --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToHost/Raw/VectorToHostRawConverter.cs @@ -0,0 +1,24 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Kits; +using Speckle.Core.Models; + +namespace Speckle.Converters.Autocad.ToHost.Raw; + +public class VectorToHostRawConverter : ITypedConverter +{ + private readonly IConversionContextStack _contextStack; + + public VectorToHostRawConverter(IConversionContextStack contextStack) + { + _contextStack = contextStack; + } + + public object Convert(Base target) => Convert((SOG.Vector)target); + + public AG.Vector3d Convert(SOG.Vector target) + { + double f = Units.GetConversionFactor(target.units, _contextStack.Current.SpeckleUnits); + return new(target.x * f, target.y * f, target.z * f); + } +} diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/ArcToSpeckleConverter.cs b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/ArcToSpeckleConverter.cs new file mode 100644 index 0000000000..c437aa04e0 --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/ArcToSpeckleConverter.cs @@ -0,0 +1,20 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; + +namespace Speckle.Converters.Autocad.ToSpeckle.Geometry; + +[NameAndRankValue(nameof(ADB.Arc), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class DBArcToSpeckleConverter : IToSpeckleTopLevelConverter +{ + private readonly ITypedConverter _arcConverter; + + public DBArcToSpeckleConverter(ITypedConverter arcConverter) + { + _arcConverter = arcConverter; + } + + public Base Convert(object target) => Convert((ADB.Arc)target); + + public SOG.Arc Convert(ADB.Arc target) => _arcConverter.Convert(target); +} diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/CircleToSpeckleConverter.cs b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/CircleToSpeckleConverter.cs new file mode 100644 index 0000000000..16b1a2bd26 --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/CircleToSpeckleConverter.cs @@ -0,0 +1,20 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; + +namespace Speckle.Converters.Autocad.ToSpeckle.Geometry; + +[NameAndRankValue(nameof(ADB.Circle), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class DBCircleToSpeckleConverter : IToSpeckleTopLevelConverter +{ + private readonly ITypedConverter _circleConverter; + + public DBCircleToSpeckleConverter(ITypedConverter circleConverter) + { + _circleConverter = circleConverter; + } + + public Base Convert(object target) => RawConvert((ADB.Circle)target); + + public SOG.Circle RawConvert(ADB.Circle target) => _circleConverter.Convert(target); +} diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/EllipseToSpeckleConverter.cs b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/EllipseToSpeckleConverter.cs new file mode 100644 index 0000000000..53d3d7e5b7 --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/EllipseToSpeckleConverter.cs @@ -0,0 +1,20 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; + +namespace Speckle.Converters.Autocad.ToSpeckle.Geometry; + +[NameAndRankValue(nameof(ADB.Ellipse), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class DBEllipseToSpeckleConverter : IToSpeckleTopLevelConverter +{ + private readonly ITypedConverter _ellipseConverter; + + public DBEllipseToSpeckleConverter(ITypedConverter ellipseConverter) + { + _ellipseConverter = ellipseConverter; + } + + public Base Convert(object target) => RawConvert((ADB.Ellipse)target); + + public SOG.Ellipse RawConvert(ADB.Ellipse target) => _ellipseConverter.Convert(target); +} diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/LineToSpeckleConverter.cs b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/LineToSpeckleConverter.cs new file mode 100644 index 0000000000..710a607f9e --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/LineToSpeckleConverter.cs @@ -0,0 +1,20 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; + +namespace Speckle.Converters.Autocad.ToSpeckle.Geometry; + +[NameAndRankValue(nameof(ADB.Line), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class LineToSpeckleConverter : IToSpeckleTopLevelConverter +{ + private readonly ITypedConverter _lineConverter; + + public LineToSpeckleConverter(ITypedConverter lineConverter) + { + _lineConverter = lineConverter; + } + + public Base Convert(object target) => Convert((ADB.Line)target); + + public SOG.Line Convert(ADB.Line target) => _lineConverter.Convert(target); +} diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/PointToSpeckleConverter.cs b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/PointToSpeckleConverter.cs new file mode 100644 index 0000000000..20b59aa1aa --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/PointToSpeckleConverter.cs @@ -0,0 +1,20 @@ +using Speckle.Converters.Common.Objects; +using Speckle.Converters.Common; +using Speckle.Core.Models; + +namespace Speckle.Converters.Autocad.ToSpeckle.Geometry; + +[NameAndRankValue(nameof(ADB.DBPoint), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class PointToSpeckleConverter : IToSpeckleTopLevelConverter +{ + private readonly ITypedConverter _pointConverter; + + public PointToSpeckleConverter(ITypedConverter pointConverter) + { + _pointConverter = pointConverter; + } + + public Base Convert(object target) => RawConvert((ADB.DBPoint)target); + + public SOG.Point RawConvert(ADB.DBPoint target) => _pointConverter.Convert(target.Position); +} diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/PolyfaceMeshToSpeckleConverter.cs b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/PolyfaceMeshToSpeckleConverter.cs new file mode 100644 index 0000000000..f96503a298 --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/PolyfaceMeshToSpeckleConverter.cs @@ -0,0 +1,104 @@ +using Autodesk.AutoCAD.Geometry; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; + +namespace Speckle.Converters.Autocad.Geometry; + +/// +/// The class converter. Converts to . +/// +/// +/// 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)] +public class DBPolyfaceMeshToSpeckleConverter : IToSpeckleTopLevelConverter +{ + private readonly ITypedConverter _pointConverter; + private readonly ITypedConverter _boxConverter; + private readonly IConversionContextStack _contextStack; + + public DBPolyfaceMeshToSpeckleConverter( + ITypedConverter pointConverter, + ITypedConverter boxConverter, + IConversionContextStack contextStack + ) + { + _pointConverter = pointConverter; + _boxConverter = boxConverter; + _contextStack = contextStack; + } + + public Base Convert(object target) => RawConvert((ADB.PolyFaceMesh)target); + + public SOG.Mesh RawConvert(ADB.PolyFaceMesh target) + { + List dbVertices = new(); + List faces = new(); + List faceVisibility = new(); + List colors = new(); + using (ADB.Transaction tr = _contextStack.Current.Document.Database.TransactionManager.StartTransaction()) + { + foreach (ADB.ObjectId id in target) + { + ADB.DBObject obj = tr.GetObject(id, ADB.OpenMode.ForRead); + switch (obj) + { + case ADB.PolyFaceMeshVertex o: + dbVertices.Add(o.Position); + colors.Add(o.Color.ColorValue.ToArgb()); + break; + case ADB.FaceRecord o: + List indices = new(); + List hidden = new(); + for (short i = 0; i < 4; i++) + { + short index = o.GetVertexAt(i); + if (index == 0) + { + continue; + } + + // vertices are 1 indexed, and can be negative (hidden) + int adjustedIndex = index > 0 ? index - 1 : Math.Abs(index) - 1; + indices.Add(adjustedIndex); + + // 0 indicates hidden vertex on the face: 1 indicates a visible vertex + hidden.Add(index < 0 ? 0 : 1); + } + + if (indices.Count == 4) + { + faces.AddRange(new List { 4, indices[0], indices[1], indices[2], indices[3] }); + faceVisibility.AddRange(new List { 4, hidden[0], hidden[1], hidden[2], hidden[3] }); + } + else + { + faces.AddRange(new List { 3, indices[0], indices[1], indices[2] }); + faceVisibility.AddRange(new List { 3, hidden[0], hidden[1], hidden[2] }); + } + + break; + } + } + tr.Commit(); + } + + List vertices = new(dbVertices.Count * 3); + foreach (Point3d vert in dbVertices) + { + vertices.AddRange(_pointConverter.Convert(vert).ToList()); + } + + SOG.Box bbox = _boxConverter.Convert(target.GeometricExtents); + + SOG.Mesh speckleMesh = + new(vertices, faces, colors, null, _contextStack.Current.SpeckleUnits) + { + bbox = bbox, + ["faceVisibility"] = faceVisibility + }; + + return speckleMesh; + } +} diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/Polyline2dToSpeckleConverter.cs b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/Polyline2dToSpeckleConverter.cs new file mode 100644 index 0000000000..e0b053c12c --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/Polyline2dToSpeckleConverter.cs @@ -0,0 +1,201 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; +using Speckle.Converters.Autocad.Extensions; + +namespace Speckle.Converters.Autocad.Geometry; + +/// +/// The class converter. Converts to . +/// +/// +/// of type will have only s and s in . +/// of type will have only s in . +/// 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)] +public class Polyline2dToSpeckleConverter + : IToSpeckleTopLevelConverter, + ITypedConverter +{ + private readonly ITypedConverter _arcConverter; + private readonly ITypedConverter _lineConverter; + private readonly ITypedConverter _polylineConverter; + private readonly ITypedConverter _splineConverter; + private readonly ITypedConverter _vectorConverter; + private readonly ITypedConverter _boxConverter; + private readonly IConversionContextStack _contextStack; + + public Polyline2dToSpeckleConverter( + ITypedConverter arcConverter, + ITypedConverter lineConverter, + ITypedConverter polylineConverter, + ITypedConverter splineConverter, + ITypedConverter vectorConverter, + ITypedConverter boxConverter, + IConversionContextStack contextStack + ) + { + _arcConverter = arcConverter; + _lineConverter = lineConverter; + _polylineConverter = polylineConverter; + _splineConverter = splineConverter; + _vectorConverter = vectorConverter; + _boxConverter = boxConverter; + _contextStack = contextStack; + } + + public Base Convert(object target) => Convert((ADB.Polyline2d)target); + + public SOG.Autocad.AutocadPolycurve Convert(ADB.Polyline2d target) + { + // get the poly type + var polyType = SOG.Autocad.AutocadPolyType.Unknown; + bool isSpline = false; + switch (target.PolyType) + { + case ADB.Poly2dType.SimplePoly: + polyType = SOG.Autocad.AutocadPolyType.Simple2d; + break; + case ADB.Poly2dType.FitCurvePoly: + polyType = SOG.Autocad.AutocadPolyType.FitCurve2d; + break; + case ADB.Poly2dType.CubicSplinePoly: + polyType = SOG.Autocad.AutocadPolyType.CubicSpline2d; + isSpline = true; + break; + case ADB.Poly2dType.QuadSplinePoly: + polyType = SOG.Autocad.AutocadPolyType.QuadSpline2d; + isSpline = true; + break; + } + + // get all vertex data + List value = new(); + List bulges = new(); + List tangents = new(); + List vertices = target + .GetSubEntities( + ADB.OpenMode.ForRead, + _contextStack.Current.Document.TransactionManager.TopTransaction + ) + .Where(e => e.VertexType != ADB.Vertex2dType.CurveFitVertex && e.VertexType != ADB.Vertex2dType.SplineFitVertex) // Do not collect fit vertex points, they are not used for creation + .ToList(); + + for (int i = 0; i < vertices.Count; i++) + { + ADB.Vertex2d vertex = vertices[i]; + + // get vertex value in the Global Coordinate System (GCS). + // NOTE: for some reason, the z value of the position for rotated polyline2ds doesn't seem to match the exploded segment endpoint values + value.AddRange(vertex.Position.ToArray()); + + // get the bulge and tangent + bulges.Add(vertex.Bulge); + tangents.Add(vertex.TangentUsed ? vertex.Tangent : null); + } + + // explode the polyline + // exploded segments will be the polyecurve segments for non-spline poly2ds, and the displayvalue for spline poly2ds + // NOTE: exploded segments may not be in order or in the correct direction + List segments = new(); + List segmentValues = new(); + ADB.DBObjectCollection exploded = new(); + target.Explode(exploded); + AG.Point3d previousPoint = new(); + + for (int i = 0; i < exploded.Count; i++) + { + if (exploded[i] is ADB.Curve segment) + { + // for splines, just store point values for display value creation + if (isSpline) + { + segmentValues.AddRange(segment.StartPoint.ToArray()); + if (i == exploded.Count - 1) + { + segmentValues.AddRange(segment.EndPoint.ToArray()); + } + } + // for non-splines, convert the curve and add to segments list + else + { + // for the first segment, the only way we can correctly determine its orientation is to find the connection point to the next segment + // this is because the z value of rotated polyline2d vertices is unreliable, so we can't use the first vertex + if (i == 0 && exploded.Count > 1 && exploded[1] is ADB.Curve nextSegment) + { + previousPoint = + segment.StartPoint.IsEqualTo(nextSegment.StartPoint) || segment.StartPoint.IsEqualTo(nextSegment.EndPoint) + ? segment.EndPoint + : segment.StartPoint; + } + + switch (segment) + { + case ADB.Arc arc: + if (ShouldReverseCurve(arc, previousPoint)) + { + arc.ReverseCurve(); + } + + segments.Add(_arcConverter.Convert(arc)); + previousPoint = arc.EndPoint; + break; + case ADB.Line line: + if (ShouldReverseCurve(line, previousPoint)) + { + line.ReverseCurve(); + } + + segments.Add(_lineConverter.Convert(line)); + previousPoint = line.EndPoint; + break; + } + } + } + } + + // for splines, convert the spline curve and display value and add to the segments list + if (isSpline) + { + SOG.Curve spline = _splineConverter.Convert(target.Spline); + SOG.Polyline displayValue = segmentValues.ConvertToSpecklePolyline(_contextStack.Current.SpeckleUnits); + if (displayValue != null) + { + spline.displayValue = displayValue; + } + + segments.Add(spline); + } + + SOG.Vector normal = _vectorConverter.Convert(target.Normal); + SOG.Box bbox = _boxConverter.Convert(target.GeometricExtents); + SOG.Autocad.AutocadPolycurve polycurve = + new() + { + segments = segments, + value = value, + bulges = bulges, + tangents = tangents, + normal = normal, + elevation = target.Elevation, + polyType = polyType, + closed = target.Closed, + length = target.Length, + area = target.Area, + bbox = bbox, + units = _contextStack.Current.SpeckleUnits + }; + + return polycurve; + } + + /// + /// Determines if the input curve is reversed according to the input start point + /// + /// + /// Should match either the start or the end point of the curve. + /// True if the endpoint of the curve matches the startpoint, or false if it doesn't. + private bool ShouldReverseCurve(ADB.Curve curve, AG.Point3d startPoint) => curve.EndPoint.IsEqualTo(startPoint); +} diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/Polyline3dToSpeckleConverter.cs b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/Polyline3dToSpeckleConverter.cs new file mode 100644 index 0000000000..87b90ad1e9 --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/Polyline3dToSpeckleConverter.cs @@ -0,0 +1,133 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; +using Speckle.Converters.Autocad.Extensions; + +namespace Speckle.Converters.Autocad.Geometry; + +/// +/// The class converter. Converts to . +/// +/// +/// of type will have only one in . +/// 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)] +public class Polyline3dToSpeckleConverter + : IToSpeckleTopLevelConverter, + ITypedConverter +{ + private readonly ITypedConverter _pointConverter; + private readonly ITypedConverter _splineConverter; + private readonly ITypedConverter _boxConverter; + private readonly IConversionContextStack _contextStack; + + public Polyline3dToSpeckleConverter( + ITypedConverter pointConverter, + ITypedConverter splineConverter, + ITypedConverter boxConverter, + IConversionContextStack contextStack + ) + { + _pointConverter = pointConverter; + _splineConverter = splineConverter; + _boxConverter = boxConverter; + _contextStack = contextStack; + } + + public Base Convert(object target) => Convert((ADB.Polyline3d)target); + + public SOG.Autocad.AutocadPolycurve Convert(ADB.Polyline3d target) + { + // get the poly type + var polyType = SOG.Autocad.AutocadPolyType.Unknown; + switch (target.PolyType) + { + case ADB.Poly3dType.SimplePoly: + polyType = SOG.Autocad.AutocadPolyType.Simple3d; + break; + case ADB.Poly3dType.CubicSplinePoly: + polyType = SOG.Autocad.AutocadPolyType.CubicSpline3d; + break; + case ADB.Poly3dType.QuadSplinePoly: + polyType = SOG.Autocad.AutocadPolyType.QuadSpline3d; + break; + } + + // get all vertex data except control vertices + List value = new(); + List vertices = target + .GetSubEntities( + ADB.OpenMode.ForRead, + _contextStack.Current.Document.TransactionManager.TopTransaction + ) + .Where(e => e.VertexType != ADB.Vertex3dType.FitVertex) // Do not collect fit vertex points, they are not used for creation + .ToList(); + for (int i = 0; i < vertices.Count; i++) + { + // vertex value is in the Global Coordinate System (GCS). + value.AddRange(vertices[i].Position.ToArray()); + } + + List segments = new(); + // for spline polyline3ds, get the spline curve segment + // and explode the curve to get the spline displayvalue + if (target.PolyType is not ADB.Poly3dType.SimplePoly) + { + // get the spline segment + SOG.Curve spline = _splineConverter.Convert(target.Spline); + + // get the spline displayvalue by exploding the polyline + List segmentValues = new(); + ADB.DBObjectCollection exploded = new(); + target.Explode(exploded); + for (int i = 0; i < exploded.Count; i++) + { + if (exploded[i] is ADB.Curve segment) + { + segmentValues.AddRange(segment.StartPoint.ToArray()); + if (i == exploded.Count - 1) + { + segmentValues.AddRange(segment.EndPoint.ToArray()); + } + } + } + + SOG.Polyline displayValue = segmentValues.ConvertToSpecklePolyline(_contextStack.Current.SpeckleUnits); + if (displayValue != null) + { + spline.displayValue = displayValue; + } + + segments.Add(spline); + } + // for simple polyline3ds just get the polyline segment from the value + else + { + SOG.Polyline polyline = value.ConvertToSpecklePolyline(_contextStack.Current.SpeckleUnits); + if (target.Closed) + { + polyline.closed = true; + } + + segments.Add(polyline); + } + + SOG.Box bbox = _boxConverter.Convert(target.GeometricExtents); + + SOG.Autocad.AutocadPolycurve polycurve = + new() + { + segments = segments, + value = value, + polyType = polyType, + closed = target.Closed, + length = target.Length, + bbox = bbox, + units = _contextStack.Current.SpeckleUnits + }; + + return polycurve; + } +} diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/PolylineToSpeckleConverter.cs b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/PolylineToSpeckleConverter.cs new file mode 100644 index 0000000000..33a57b6b7f --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/PolylineToSpeckleConverter.cs @@ -0,0 +1,94 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; + +namespace Speckle.Converters.Autocad.Geometry; + +/// +/// The class converter. Converts to . +/// +/// +/// is of type and will have only s and s in . +/// +[NameAndRankValue(nameof(ADB.Polyline), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class PolylineToSpeckleConverter + : IToSpeckleTopLevelConverter, + ITypedConverter +{ + private readonly ITypedConverter _lineConverter; + private readonly ITypedConverter _arcConverter; + private readonly ITypedConverter _vectorConverter; + private readonly ITypedConverter _boxConverter; + private readonly IConversionContextStack _contextStack; + + public PolylineToSpeckleConverter( + ITypedConverter lineConverter, + ITypedConverter arcConverter, + ITypedConverter vectorConverter, + ITypedConverter boxConverter, + IConversionContextStack contextStack + ) + { + _lineConverter = lineConverter; + _arcConverter = arcConverter; + _vectorConverter = vectorConverter; + _boxConverter = boxConverter; + _contextStack = contextStack; + } + + public Base Convert(object target) => Convert((ADB.Polyline)target); + + public SOG.Autocad.AutocadPolycurve Convert(ADB.Polyline target) + { + List value = new(target.NumberOfVertices * 3); + List bulges = new(target.NumberOfVertices); + List segments = new(); + for (int i = 0; i < target.NumberOfVertices; i++) + { + // get vertex value in the Object Coordinate System (OCS) + AG.Point2d vertex = target.GetPoint2dAt(i); + value.AddRange(vertex.ToArray()); + + // get the bulge + bulges.Add(target.GetBulgeAt(i)); + + // get segment in the Global Coordinate System (GCS) + ADB.SegmentType type = target.GetSegmentType(i); + switch (type) + { + case ADB.SegmentType.Line: + AG.LineSegment3d line = target.GetLineSegmentAt(i); + segments.Add(_lineConverter.Convert(line)); + break; + case ADB.SegmentType.Arc: + AG.CircularArc3d arc = target.GetArcSegmentAt(i); + segments.Add(_arcConverter.Convert(arc)); + break; + default: + // we are skipping segments of type Empty, Point, and Coincident + break; + } + } + + SOG.Vector normal = _vectorConverter.Convert(target.Normal); + SOG.Box bbox = _boxConverter.Convert(target.GeometricExtents); + + SOG.Autocad.AutocadPolycurve polycurve = + new() + { + segments = segments, + value = value, + bulges = bulges, + normal = normal, + elevation = target.Elevation, + polyType = SOG.Autocad.AutocadPolyType.Light, + closed = target.Closed, + length = target.Length, + area = target.Area, + bbox = bbox, + units = _contextStack.Current.SpeckleUnits + }; + + return polycurve; + } +} diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/Solid3dToSpeckleConverter.cs b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/Solid3dToSpeckleConverter.cs new file mode 100644 index 0000000000..841dc1120b --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/Solid3dToSpeckleConverter.cs @@ -0,0 +1,23 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; + +namespace Speckle.Converters.Autocad.Geometry; + +[NameAndRankValue(nameof(ADB.Solid3d), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class Solid3dToSpeckleConverter : IToSpeckleTopLevelConverter +{ + private readonly ITypedConverter _solidConverter; + + public Solid3dToSpeckleConverter(ITypedConverter solidConverter) + { + _solidConverter = solidConverter; + } + + public Base Convert(object target) => RawConvert((ADB.Solid3d)target); + + public SOG.Mesh RawConvert(ADB.Solid3d target) + { + return _solidConverter.Convert(target); + } +} diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/SplineToSpeckleConverter.cs b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/SplineToSpeckleConverter.cs new file mode 100644 index 0000000000..d182376cd7 --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/SplineToSpeckleConverter.cs @@ -0,0 +1,20 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; + +namespace Speckle.Converters.Autocad.ToSpeckle.Geometry; + +[NameAndRankValue(nameof(ADB.Spline), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class SplineToSpeckleConverter : IToSpeckleTopLevelConverter +{ + private readonly ITypedConverter _splineConverter; + + public SplineToSpeckleConverter(ITypedConverter splineConverter) + { + _splineConverter = splineConverter; + } + + public Base Convert(object target) => Convert((ADB.Spline)target); + + public SOG.Curve Convert(ADB.Spline target) => _splineConverter.Convert(target); +} diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/SubDMeshToSpeckleConverter.cs b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/SubDMeshToSpeckleConverter.cs new file mode 100644 index 0000000000..98686f8006 --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/SubDMeshToSpeckleConverter.cs @@ -0,0 +1,76 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; + +namespace Speckle.Converters.Autocad.Geometry; + +[NameAndRankValue(nameof(ADB.SubDMesh), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class DBSubDMeshToSpeckleConverter : IToSpeckleTopLevelConverter +{ + private readonly ITypedConverter _pointConverter; + private readonly ITypedConverter _boxConverter; + private readonly IConversionContextStack _contextStack; + + public DBSubDMeshToSpeckleConverter( + ITypedConverter pointConverter, + ITypedConverter boxConverter, + IConversionContextStack contextStack + ) + { + _pointConverter = pointConverter; + _boxConverter = boxConverter; + _contextStack = contextStack; + } + + public Base Convert(object target) => RawConvert((ADB.SubDMesh)target); + + public SOG.Mesh RawConvert(ADB.SubDMesh target) + { + //vertices + var vertices = new List(target.Vertices.Count * 3); + foreach (AG.Point3d vert in target.Vertices) + { + vertices.AddRange(_pointConverter.Convert(vert).ToList()); + } + + // faces + var faces = new List(); + int[] faceArr = target.FaceArray.ToArray(); // contains vertex indices + int edgeCount = 0; + for (int i = 0; i < faceArr.Length; i = i + edgeCount + 1) + { + List faceVertices = new(); + edgeCount = faceArr[i]; + for (int j = i + 1; j <= i + edgeCount; j++) + { + faceVertices.Add(faceArr[j]); + } + + if (edgeCount == 4) // quad face + { + faces.AddRange(new List { 4, faceVertices[0], faceVertices[1], faceVertices[2], faceVertices[3] }); + } + else // triangle face + { + faces.AddRange(new List { 3, faceVertices[0], faceVertices[1], faceVertices[2] }); + } + } + + // colors + var colors = target.VertexColorArray + .Select( + o => + System.Drawing.Color + .FromArgb(System.Convert.ToInt32(o.Red), System.Convert.ToInt32(o.Green), System.Convert.ToInt32(o.Blue)) + .ToArgb() + ) + .ToList(); + + // bbox + SOG.Box bbox = _boxConverter.Convert(target.GeometricExtents); + + SOG.Mesh speckleMesh = new(vertices, faces, colors, null, _contextStack.Current.SpeckleUnits) { bbox = bbox }; + + return speckleMesh; + } +} diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Raw/BoxToSpeckleRawConverter.cs b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Raw/BoxToSpeckleRawConverter.cs new file mode 100644 index 0000000000..a36bbb60f6 --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Raw/BoxToSpeckleRawConverter.cs @@ -0,0 +1,40 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; + +namespace Speckle.Converters.Autocad.ToSpeckle.Raw; + +public class BoxToSpeckleRawConverter : ITypedConverter +{ + private readonly ITypedConverter _planeConverter; + private readonly IConversionContextStack _contextStack; + + public BoxToSpeckleRawConverter( + ITypedConverter planeConverter, + IConversionContextStack contextStack + ) + { + _planeConverter = planeConverter; + _contextStack = contextStack; + } + + public Base Convert(object target) => Convert((ADB.Extents3d)target); + + public SOG.Box Convert(ADB.Extents3d target) + { + // get dimension intervals and volume + SOP.Interval xSize = new(target.MinPoint.X, target.MaxPoint.X); + SOP.Interval ySize = new(target.MinPoint.Y, target.MaxPoint.Y); + SOP.Interval zSize = new(target.MinPoint.Z, target.MaxPoint.Z); + double volume = xSize.Length * ySize.Length * zSize.Length; + + // get the base plane of the bounding box from extents and current UCS + var ucs = _contextStack.Current.Document.Editor.CurrentUserCoordinateSystem.CoordinateSystem3d; + AG.Plane acadPlane = new(target.MinPoint, ucs.Xaxis, ucs.Yaxis); + SOG.Plane plane = _planeConverter.Convert(acadPlane); + + SOG.Box box = new(plane, xSize, ySize, zSize, _contextStack.Current.SpeckleUnits) { volume = volume }; + + return box; + } +} diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Raw/CircularArc3dToSpeckleRawConverter.cs b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Raw/CircularArc3dToSpeckleRawConverter.cs new file mode 100644 index 0000000000..ec8f91e0b7 --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Raw/CircularArc3dToSpeckleRawConverter.cs @@ -0,0 +1,50 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.Autocad.ToSpeckle.Raw; + +public class CircularArc3dToSpeckleConverter : ITypedConverter +{ + private readonly ITypedConverter _pointConverter; + private readonly ITypedConverter _planeConverter; + private readonly IConversionContextStack _contextStack; + + public CircularArc3dToSpeckleConverter( + ITypedConverter pointConverter, + ITypedConverter planeConverter, + IConversionContextStack contextStack + ) + { + _pointConverter = pointConverter; + _planeConverter = planeConverter; + _contextStack = contextStack; + } + + public SOG.Arc Convert(AG.CircularArc3d target) + { + SOG.Plane plane = _planeConverter.Convert(target.GetPlane()); + SOG.Point start = _pointConverter.Convert(target.StartPoint); + SOG.Point end = _pointConverter.Convert(target.EndPoint); + SOG.Point mid = _pointConverter.Convert(target.EvaluatePoint(0.5)); // POC: testing, unsure + SOP.Interval domain = new(target.GetInterval().LowerBound, target.GetInterval().UpperBound); + + SOG.Arc arc = + new( + plane, + target.Radius, + target.StartAngle, + target.EndAngle, + target.EndAngle - target.StartAngle, // POC: testing, unsure + _contextStack.Current.SpeckleUnits + ) + { + startPoint = start, + endPoint = end, + midPoint = mid, + domain = domain, + length = target.GetLength(0, 1, 0.000) + }; + + return arc; + } +} diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Raw/DBArcToSpeckleRawConverter.cs b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Raw/DBArcToSpeckleRawConverter.cs new file mode 100644 index 0000000000..8b45be3de3 --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Raw/DBArcToSpeckleRawConverter.cs @@ -0,0 +1,58 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; + +namespace Speckle.Converters.Autocad.ToSpeckle.Raw; + +public class DBArcToSpeckleRawConverter : ITypedConverter +{ + private readonly ITypedConverter _pointConverter; + private readonly ITypedConverter _planeConverter; + private readonly ITypedConverter _boxConverter; + private readonly IConversionContextStack _contextStack; + + public DBArcToSpeckleRawConverter( + ITypedConverter pointConverter, + ITypedConverter planeConverter, + ITypedConverter boxConverter, + IConversionContextStack contextStack + ) + { + _pointConverter = pointConverter; + _planeConverter = planeConverter; + _boxConverter = boxConverter; + _contextStack = contextStack; + } + + public Base Convert(object target) => Convert((ADB.Arc)target); + + public SOG.Arc Convert(ADB.Arc target) + { + SOG.Plane plane = _planeConverter.Convert(target.GetPlane()); + SOG.Point start = _pointConverter.Convert(target.StartPoint); + SOG.Point end = _pointConverter.Convert(target.EndPoint); + SOG.Point mid = _pointConverter.Convert(target.GetPointAtDist(target.Length / 2.0)); + SOP.Interval domain = new(target.StartParam, target.EndParam); + SOG.Box bbox = _boxConverter.Convert(target.GeometricExtents); + + SOG.Arc arc = + new( + plane, + target.Radius, + target.StartAngle, + target.EndAngle, + target.TotalAngle, + _contextStack.Current.SpeckleUnits + ) + { + startPoint = start, + endPoint = end, + midPoint = mid, + domain = domain, + length = target.Length, + bbox = bbox + }; + + return arc; + } +} diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Raw/DBCircleToSpeckleRawConverter.cs b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Raw/DBCircleToSpeckleRawConverter.cs new file mode 100644 index 0000000000..06f4cf6877 --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Raw/DBCircleToSpeckleRawConverter.cs @@ -0,0 +1,35 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; + +namespace Speckle.Converters.Autocad.ToSpeckle.Raw; + +public class DBCircleToSpeckleRawConverter : ITypedConverter +{ + private readonly ITypedConverter _planeConverter; + private readonly ITypedConverter _boxConverter; + private readonly IConversionContextStack _contextStack; + + public DBCircleToSpeckleRawConverter( + ITypedConverter planeConverter, + ITypedConverter boxConverter, + IConversionContextStack contextStack + ) + { + _planeConverter = planeConverter; + _boxConverter = boxConverter; + _contextStack = contextStack; + } + + public Base Convert(object target) => Convert((ADB.Circle)target); + + public SOG.Circle Convert(ADB.Circle target) + { + SOG.Plane plane = _planeConverter.Convert(target.GetPlane()); + SOG.Box bbox = _boxConverter.Convert(target.GeometricExtents); + SOG.Circle circle = + new(plane, target.Radius, _contextStack.Current.SpeckleUnits) { length = target.Circumference, bbox = bbox }; + + return circle; + } +} diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Raw/DBCurveToSpeckleRawConverter.cs b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Raw/DBCurveToSpeckleRawConverter.cs new file mode 100644 index 0000000000..ef392923be --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Raw/DBCurveToSpeckleRawConverter.cs @@ -0,0 +1,67 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; + +namespace Speckle.Converters.Autocad.ToSpeckle.Raw; + +public class DBCurveToSpeckleRawConverter : ITypedConverter, ITypedConverter +{ + private readonly ITypedConverter _lineConverter; + + private readonly ITypedConverter _polylineConverter; + private readonly ITypedConverter _polyline2dConverter; + private readonly ITypedConverter _polyline3dConverter; + private readonly ITypedConverter _arcConverter; + private readonly ITypedConverter _circleConverter; + private readonly ITypedConverter _ellipseConverter; + private readonly ITypedConverter _splineConverter; + private readonly IConversionContextStack _contextStack; + + public DBCurveToSpeckleRawConverter( + ITypedConverter lineConverter, + ITypedConverter polylineConverter, + ITypedConverter polyline2dConverter, + ITypedConverter polyline3dConverter, + ITypedConverter arcConverter, + ITypedConverter circleConverter, + ITypedConverter ellipseConverter, + ITypedConverter splineConverter, + IConversionContextStack contextStack + ) + { + _lineConverter = lineConverter; + _polylineConverter = polylineConverter; + _polyline2dConverter = polyline2dConverter; + _polyline3dConverter = polyline3dConverter; + _arcConverter = arcConverter; + _circleConverter = circleConverter; + _ellipseConverter = ellipseConverter; + _splineConverter = splineConverter; + _contextStack = contextStack; + } + + /// + /// Converts an Autocad curve to a Speckle ICurve. + /// + /// The Autocad curve to convert. + /// The Speckle curve. + /// + /// This is the main converter when the type of curve you input or output does not matter to the caller.
+ /// ⚠️ If an unsupported type of Curve is input, it will be converted as Spline. + ///
+ public Objects.ICurve Convert(ADB.Curve target) => + target switch + { + ADB.Line line => _lineConverter.Convert(line), + //ADB.Polyline polyline => _polylineConverter.Convert(polyline), + //ADB.Polyline2d polyline2d => _polyline2dConverter.Convert(polyline2d), + //ADB.Polyline3d polyline3d => _polyline3dConverter.Convert(polyline3d), + ADB.Arc arc => _arcConverter.Convert(arc), + ADB.Circle circle => _circleConverter.Convert(circle), + ADB.Ellipse ellipse => _ellipseConverter.Convert(ellipse), + ADB.Spline spline => _splineConverter.Convert(spline), + _ => _splineConverter.Convert(target.Spline) + }; + + Base ITypedConverter.Convert(ADB.Curve target) => (Base)Convert(target); +} diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Raw/DBEllipseToSpeckleRawConverter.cs b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Raw/DBEllipseToSpeckleRawConverter.cs new file mode 100644 index 0000000000..c763bb0e66 --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Raw/DBEllipseToSpeckleRawConverter.cs @@ -0,0 +1,45 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; + +namespace Speckle.Converters.Autocad.ToSpeckle.Raw; + +public class DBEllipseToSpeckleRawConverter : ITypedConverter +{ + private readonly ITypedConverter _planeConverter; + private readonly ITypedConverter _boxConverter; + private readonly IConversionContextStack _contextStack; + + public DBEllipseToSpeckleRawConverter( + ITypedConverter planeConverter, + ITypedConverter boxConverter, + IConversionContextStack contextStack + ) + { + _planeConverter = planeConverter; + _boxConverter = boxConverter; + _contextStack = contextStack; + } + + public Base Convert(object target) => Convert((ADB.Ellipse)target); + + public SOG.Ellipse Convert(ADB.Ellipse target) + { + SOG.Plane plane = _planeConverter.Convert(new AG.Plane(target.Center, target.MajorAxis, target.MinorAxis)); + SOG.Box bbox = _boxConverter.Convert(target.GeometricExtents); + + // the start and end param corresponds to start and end angle in radians + SOP.Interval trim = new(target.StartAngle, target.EndAngle); + + SOG.Ellipse ellipse = + new(plane, target.MajorRadius, target.MinorRadius, _contextStack.Current.SpeckleUnits) + { + domain = new(0, Math.PI * 2), + trimDomain = trim, + length = target.GetDistanceAtParameter(target.EndParam), + bbox = bbox + }; + + return ellipse; + } +} diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Raw/DBLineToSpeckleRawConverter.cs b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Raw/DBLineToSpeckleRawConverter.cs new file mode 100644 index 0000000000..b4a083c8e3 --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Raw/DBLineToSpeckleRawConverter.cs @@ -0,0 +1,37 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; + +namespace Speckle.Converters.Autocad.ToSpeckle.Raw; + +public class DBLineToSpeckleRawConverter : ITypedConverter +{ + private readonly ITypedConverter _pointConverter; + private readonly ITypedConverter _boxConverter; + private readonly IConversionContextStack _contextStack; + + public DBLineToSpeckleRawConverter( + ITypedConverter pointConverter, + ITypedConverter boxConverter, + IConversionContextStack contextStack + ) + { + _pointConverter = pointConverter; + _boxConverter = boxConverter; + _contextStack = contextStack; + } + + public Base Convert(object target) => Convert((ADB.Line)target); + + public SOG.Line Convert(ADB.Line target) => + new( + _pointConverter.Convert(target.StartPoint), + _pointConverter.Convert(target.EndPoint), + _contextStack.Current.SpeckleUnits + ) + { + length = target.Length, + domain = new SOP.Interval(0, target.Length), + bbox = _boxConverter.Convert(target.GeometricExtents) + }; +} diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Raw/DBSolid3dToSpeckleRawConverter.cs b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Raw/DBSolid3dToSpeckleRawConverter.cs new file mode 100644 index 0000000000..45c2d531a4 --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Raw/DBSolid3dToSpeckleRawConverter.cs @@ -0,0 +1,84 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; + +namespace Speckle.Converters.Autocad.ToSpeckle.Raw; + +public class Solid3dToSpeckleRawConverter : ITypedConverter +{ + private readonly ITypedConverter _pointConverter; + private readonly ITypedConverter _boxConverter; + private readonly IConversionContextStack _contextStack; + + public Solid3dToSpeckleRawConverter( + ITypedConverter pointConverter, + ITypedConverter boxConverter, + IConversionContextStack contextStack + ) + { + _pointConverter = pointConverter; + _boxConverter = boxConverter; + _contextStack = contextStack; + } + + public Base Convert(object target) => Convert((ADB.Solid3d)target); + + public SOG.Mesh Convert(ADB.Solid3d target) + { + using ABR.Brep brep = new(target); + if (brep.IsNull) + { + throw new SpeckleConversionException("Could not retrieve brep from the solid3d."); + } + + 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(); + double volume = target.MassProperties.Volume; + double area = target.Area; + SOG.Box bbox = _boxConverter.Convert(target.GeometricExtents); + + // create speckle mesh + SOG.Mesh mesh = + new(convertedVertices, faces) + { + units = _contextStack.Current.SpeckleUnits, + bbox = bbox, + area = area, + volume = volume + }; + + return mesh; + } +} diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Raw/DBSplineToSpeckleRawConverter.cs b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Raw/DBSplineToSpeckleRawConverter.cs new file mode 100644 index 0000000000..d46b9de0a7 --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Raw/DBSplineToSpeckleRawConverter.cs @@ -0,0 +1,155 @@ +using Autodesk.AutoCAD.Geometry; +using Speckle.Converters.Autocad.Extensions; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; + +namespace Speckle.Converters.Autocad.ToSpeckle.Raw; + +public class DBSplineToSpeckleRawConverter : ITypedConverter +{ + private readonly ITypedConverter _intervalConverter; + private readonly ITypedConverter _boxConverter; + private readonly IConversionContextStack _contextStack; + + public DBSplineToSpeckleRawConverter( + ITypedConverter intervalConverter, + ITypedConverter boxConverter, + IConversionContextStack contextStack + ) + { + _intervalConverter = intervalConverter; + _boxConverter = boxConverter; + _contextStack = contextStack; + } + + public Base Convert(object target) => Convert((ADB.Spline)target); + + public SOG.Curve Convert(ADB.Spline target) + { + // get nurbs and geo data + ADB.NurbsData data = target.NurbsData; + + // POC: HACK: check for incorrectly closed periodic curves (this seems like acad bug, has resulted from receiving rhino curves) + bool periodicClosed = false; + double length = 0; + SOP.Interval domain = new(); + if (target.GetGeCurve() is NurbCurve3d nurbs) + { + length = nurbs.GetLength(nurbs.StartParameter, nurbs.EndParameter, 0.001); + domain = _intervalConverter.Convert(nurbs.GetInterval()); + if (nurbs.Knots.Count < nurbs.NumberOfControlPoints + nurbs.Degree + 1 && target.IsPeriodic) + { + periodicClosed = true; + } + } + + // get points + List points = new(); + foreach (Point3d point in data.GetControlPoints().OfType()) + { + points.Add(point); + } + + // NOTE: for closed periodic splines, autocad does not track last #degree points. + // Add the first #degree control points to the list if so. + if (periodicClosed) + { + points.AddRange(points.GetRange(0, target.Degree)); + } + + // get knots + // NOTE: for closed periodic splines, autocad has #control points + 1 knots. + // Add #degree extra knots to beginning and end with #degree - 1 multiplicity for first and last + var knots = data.GetKnots().OfType().ToList(); + if (periodicClosed) + { + double interval = knots[1] - knots[0]; //knot interval + + for (int i = 0; i < data.Degree; i++) + { + if (i < 2) + { + knots.Insert(knots.Count, knots[^1] + interval); + knots.Insert(0, knots[0] - interval); + } + else + { + knots.Insert(knots.Count, knots[^1]); + knots.Insert(0, knots[0]); + } + } + } + + // get weights + // NOTE: autocad assigns unweighted points a value of -1, and will return an empty list in the spline's nurbsdata if no points are weighted + // NOTE: for closed periodic splines, autocad does not track last #degree points. Add the first #degree weights to the list if so. + List weights = new(); + for (int i = 0; i < target.NumControlPoints; i++) + { + double weight = target.WeightAt(i); + weights.Add(weight <= 0 ? 1 : weight); + } + + if (periodicClosed) + { + weights.AddRange(weights.GetRange(0, target.Degree)); + } + + // set nurbs curve info + var curve = new SOG.Curve + { + points = points.SelectMany(o => o.ToArray()).ToList(), + knots = knots, + weights = weights, + degree = target.Degree, + periodic = target.IsPeriodic, + rational = target.IsRational, + closed = periodicClosed || target.Closed, + length = length, + domain = domain, + bbox = _boxConverter.Convert(target.GeometricExtents), + units = _contextStack.Current.SpeckleUnits + }; + + // POC: get display value if this is a database-resident spline + // POC: if this is called by another converter that has created a spline, assumes the display value is set by that converter + if (target.Database is not null) + { + curve.displayValue = GetDisplayValue(target); + } + + return curve; + } + + // POC: we might have DisplayValue converter/mapper? + private SOG.Polyline GetDisplayValue(ADB.Spline spline) + { + ADB.Curve polySpline = spline.ToPolylineWithPrecision(10, false, false); + List verticesList = new(); + switch (polySpline) + { + case ADB.Polyline2d o: + verticesList = o.GetSubEntities( + ADB.OpenMode.ForRead, + _contextStack.Current.Document.TransactionManager.TopTransaction + ) + .Where(e => e.VertexType != ADB.Vertex2dType.SplineControlVertex) // POC: not validated yet! + .SelectMany(o => o.Position.ToArray()) + .ToList(); + + break; + case ADB.Polyline3d o: + verticesList = o.GetSubEntities( + ADB.OpenMode.ForRead, + _contextStack.Current.Document.TransactionManager.TopTransaction + ) + .Where(e => e.VertexType != ADB.Vertex3dType.ControlVertex) + .SelectMany(o => o.Position.ToArray()) + .ToList(); + break; + } + + return verticesList.ConvertToSpecklePolyline(_contextStack.Current.SpeckleUnits); + } +} diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Raw/IntervalToSpeckleRawConverter.cs b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Raw/IntervalToSpeckleRawConverter.cs new file mode 100644 index 0000000000..fe5ac853d4 --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Raw/IntervalToSpeckleRawConverter.cs @@ -0,0 +1,11 @@ +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; + +namespace Speckle.Converters.Autocad.ToSpeckle.Raw; + +public class IntervalToSpeckleConverter : ITypedConverter +{ + public Base Convert(object target) => Convert((AG.Interval)target); + + public SOP.Interval Convert(AG.Interval target) => new(target.LowerBound, target.UpperBound); +} diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Raw/LineSegment3dToSpeckleRawConverter.cs b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Raw/LineSegment3dToSpeckleRawConverter.cs new file mode 100644 index 0000000000..f4079ca5ee --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Raw/LineSegment3dToSpeckleRawConverter.cs @@ -0,0 +1,30 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.Autocad.ToSpeckle.Raw; + +public class LineSegment3dToSpeckleRawConverter : ITypedConverter +{ + private readonly ITypedConverter _pointConverter; + private readonly IConversionContextStack _contextStack; + + public LineSegment3dToSpeckleRawConverter( + ITypedConverter pointConverter, + IConversionContextStack contextStack + ) + { + _pointConverter = pointConverter; + _contextStack = contextStack; + } + + public SOG.Line Convert(AG.LineSegment3d target) => + new( + _pointConverter.Convert(target.StartPoint), + _pointConverter.Convert(target.EndPoint), + _contextStack.Current.SpeckleUnits + ) + { + length = target.Length, + domain = new SOP.Interval(0, target.Length), + }; +} diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Raw/PlaneToSpeckleRawConverter.cs b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Raw/PlaneToSpeckleRawConverter.cs new file mode 100644 index 0000000000..5e712b033b --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Raw/PlaneToSpeckleRawConverter.cs @@ -0,0 +1,34 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; + +namespace Speckle.Converters.Autocad.ToSpeckle.Raw; + +public class PlaneToSpeckleRawConverter : ITypedConverter +{ + private readonly ITypedConverter _vectorConverter; + private readonly ITypedConverter _pointConverter; + private readonly IConversionContextStack _contextStack; + + public PlaneToSpeckleRawConverter( + ITypedConverter vectorConverter, + ITypedConverter pointConverter, + IConversionContextStack contextStack + ) + { + _vectorConverter = vectorConverter; + _pointConverter = pointConverter; + _contextStack = contextStack; + } + + public Base Convert(object target) => Convert((AG.Plane)target); + + public SOG.Plane Convert(AG.Plane target) => + new( + _pointConverter.Convert(target.PointOnPlane), + _vectorConverter.Convert(target.Normal), + _vectorConverter.Convert(target.GetCoordinateSystem().Xaxis), + _vectorConverter.Convert(target.GetCoordinateSystem().Yaxis), + _contextStack.Current.SpeckleUnits + ); +} diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Raw/PointToSpeckleRawConverter.cs b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Raw/PointToSpeckleRawConverter.cs new file mode 100644 index 0000000000..df8c45f237 --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Raw/PointToSpeckleRawConverter.cs @@ -0,0 +1,16 @@ +using Speckle.Converters.Common.Objects; +using Speckle.Converters.Common; + +namespace Speckle.Converters.Autocad.ToSpeckle.Raw; + +public class PointToSpeckleRawConverter : ITypedConverter +{ + private readonly IConversionContextStack _contextStack; + + public PointToSpeckleRawConverter(IConversionContextStack contextStack) + { + _contextStack = contextStack; + } + + public SOG.Point Convert(AG.Point3d target) => new(target.X, target.Y, target.Z, _contextStack.Current.SpeckleUnits); +} diff --git a/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Raw/VectorToSpeckleRawConverter.cs b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Raw/VectorToSpeckleRawConverter.cs new file mode 100644 index 0000000000..2eca1149e5 --- /dev/null +++ b/DUI3-DX/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Raw/VectorToSpeckleRawConverter.cs @@ -0,0 +1,17 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.Autocad.ToSpeckle.Raw; + +public class VectorToSpeckleRawConverter : ITypedConverter +{ + private readonly IConversionContextStack _contextStack; + + public VectorToSpeckleRawConverter(IConversionContextStack contextStack) + { + _contextStack = contextStack; + } + + public SOG.Vector Convert(AG.Vector3d target) => + new(target.X, target.Y, target.Z, _contextStack.Current.SpeckleUnits); +} diff --git a/DUI3-DX/Converters/Civil3d/Speckle.Converters.Civil3d2024.DependencyInjection/Civil3dConverterModule.cs b/DUI3-DX/Converters/Civil3d/Speckle.Converters.Civil3d2024.DependencyInjection/Civil3dConverterModule.cs new file mode 100644 index 0000000000..193648613b --- /dev/null +++ b/DUI3-DX/Converters/Civil3d/Speckle.Converters.Civil3d2024.DependencyInjection/Civil3dConverterModule.cs @@ -0,0 +1,24 @@ +using Autodesk.AutoCAD.ApplicationServices; +using Speckle.Autofac.DependencyInjection; +using Speckle.Converters.Civil3d; +using Speckle.Converters.Common; +using Speckle.Converters.Common.DependencyInjection; +using Autodesk.AutoCAD.DatabaseServices; +using Speckle.Converters.Autocad; + +namespace Speckle.Converters.Civil3d2024.DependencyInjection; + +public class Civil3dConverterModule : ISpeckleModule +{ + public void Load(SpeckleContainerBuilder builder) + { + // Register single root + builder.AddRootCommon(); + + // register all application converters + builder.AddApplicationConverters(); + builder.AddApplicationConverters(); + builder.AddScoped, Civil3dConversionContextStack>(); + builder.AddScoped, AutocadConversionContextStack>(); + } +} diff --git a/DUI3-DX/Converters/Civil3d/Speckle.Converters.Civil3d2024.DependencyInjection/Speckle.Converters.Civil3d2024.DependencyInjection.csproj b/DUI3-DX/Converters/Civil3d/Speckle.Converters.Civil3d2024.DependencyInjection/Speckle.Converters.Civil3d2024.DependencyInjection.csproj new file mode 100644 index 0000000000..9cbf64f01c --- /dev/null +++ b/DUI3-DX/Converters/Civil3d/Speckle.Converters.Civil3d2024.DependencyInjection/Speckle.Converters.Civil3d2024.DependencyInjection.csproj @@ -0,0 +1,17 @@ + + + + net48 + x64 + + + + + + + + + + + + diff --git a/DUI3-DX/Converters/Civil3d/Speckle.Converters.Civil3d2024.DependencyInjection/packages.lock.json b/DUI3-DX/Converters/Civil3d/Speckle.Converters.Civil3d2024.DependencyInjection/packages.lock.json new file mode 100644 index 0000000000..90e69d514d --- /dev/null +++ b/DUI3-DX/Converters/Civil3d/Speckle.Converters.Civil3d2024.DependencyInjection/packages.lock.json @@ -0,0 +1,448 @@ +{ + "version": 2, + "dependencies": { + ".NETFramework,Version=v4.8": { + "Autofac": { + "type": "Direct", + "requested": "[5.2.0, )", + "resolved": "5.2.0", + "contentHash": "V8dBH0dsv75uDzl7Sw+HkhKDPUw2eXnlMjcSVMH+tLo2s67MpTKGyDj1pDcpR+IF2u4YRs0s3/x7R88YJzIWvg==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "1.1.0" + } + }, + "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.5, )", + "resolved": "0.9.5", + "contentHash": "oU/L7pN1R7q8KkbrpQ3WJnHirPHqn+9DEA7asOcUiggV5dzVg1A/VYs7GOSusD24njxXh03tE3a2oTLOjt3cVg==" + }, + "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.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "Polly": { + "type": "Transitive", + "resolved": "7.2.3", + "contentHash": "DeCY0OFbNdNxsjntr1gTXHJ5pKUwYzp04Er2LLeN3g6pWhffsGuKVfMBLe1lw7x76HrPkLxKEFxBlpRxS2nDEQ==" + }, + "Polly.Contrib.WaitAndRetry": { + "type": "Transitive", + "resolved": "1.1.1", + "contentHash": "1MUQLiSo4KDkQe6nzQRhIU05lm9jlexX5BVsbuw0SL82ynZ+GzAHQxJVDPVBboxV37Po3SG077aX8DuSy8TkaA==" + }, + "Polly.Extensions.Http": { + "type": "Transitive", + "resolved": "3.0.0", + "contentHash": "drrG+hB3pYFY7w1c3BD+lSGYvH2oIclH8GRSehgfyP5kjnFnHKQuuBhuHLv+PWyFuaTDyk/vfRpnxOzd11+J8g==", + "dependencies": { + "Polly": "7.1.0" + } + }, + "Sentry": { + "type": "Transitive", + "resolved": "3.33.0", + "contentHash": "8vbD2o6IR2wrRrkSiRbnodWGWUOqIlwYtzpjvPNOb5raJdOf+zxMwfS8f6nx9bmrTTfDj7KrCB8C/5OuicAc8A==", + "dependencies": { + "System.Reflection.Metadata": "5.0.0", + "System.Runtime.InteropServices.RuntimeInformation": "4.3.0", + "System.Text.Json": "5.0.2" + } + }, + "Sentry.Serilog": { + "type": "Transitive", + "resolved": "3.33.0", + "contentHash": "V8BU7QGWg2qLYfNPqtuTBhC1opysny5l+Ifp6J6PhOeAxU0FssR7nYfbJVetrnLIoh2rd3DlJ6hHYYQosQYcUQ==", + "dependencies": { + "Sentry": "3.33.0", + "Serilog": "2.7.1" + } + }, + "Serilog": { + "type": "Transitive", + "resolved": "2.12.0", + "contentHash": "xaiJLIdu6rYMKfQMYUZgTy8YK7SMZjB4Yk50C/u//Z4OsvxkUfSPJy4nknfvwAC34yr13q7kcyh4grbwhSxyZg==" + }, + "Serilog.Enrichers.ClientInfo": { + "type": "Transitive", + "resolved": "1.3.0", + "contentHash": "mTc7PM+wC9Hr7LWSwqt5mmnlAr7RJs+eTb3PGPRhwdOackk95MkhUZognuxXEdlW19HAFNmEBTSBY5DfLwM8jQ==", + "dependencies": { + "Serilog": "2.4.0" + } + }, + "Serilog.Exceptions": { + "type": "Transitive", + "resolved": "8.4.0", + "contentHash": "nc/+hUw3lsdo0zCj0KMIybAu7perMx79vu72w0za9Nsi6mWyNkGXxYxakAjWB7nEmYL6zdmhEQRB4oJ2ALUeug==", + "dependencies": { + "Serilog": "2.8.0" + } + }, + "Serilog.Formatting.Compact": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "pNroKVjo+rDqlxNG5PXkRLpfSCuDOBY0ri6jp9PLe505ljqwhwZz8ospy2vWhQlFu5GkIesh3FcDs4n7sWZODA==", + "dependencies": { + "Serilog": "2.8.0" + } + }, + "Serilog.Sinks.Console": { + "type": "Transitive", + "resolved": "4.1.0", + "contentHash": "K6N5q+5fetjnJPvCmkWOpJ/V8IEIoMIB1s86OzBrbxwTyHxdx3pmz4H+8+O/Dc/ftUX12DM1aynx/dDowkwzqg==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "Serilog.Sinks.File": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "uwV5hdhWPwUH1szhO8PJpFiahqXmzPzJT/sOijH/kFgUx+cyoDTMM8MHD0adw9+Iem6itoibbUXHYslzXsLEAg==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "Serilog.Sinks.PeriodicBatching": { + "type": "Transitive", + "resolved": "3.1.0", + "contentHash": "NDWR7m3PalVlGEq3rzoktrXikjFMLmpwF0HI4sowo8YDdU+gqPlTHlDQiOGxHfB0sTfjPA9JjA7ctKG9zqjGkw==", + "dependencies": { + "Serilog": "2.0.0" + } + }, + "Serilog.Sinks.Seq": { + "type": "Transitive", + "resolved": "5.2.2", + "contentHash": "1Csmo5ua7NKUe0yXUx+zsRefjAniPWcXFhUXxXG8pwo0iMiw2gjn9SOkgYnnxbgWqmlGv236w0N/dHc2v5XwMg==", + "dependencies": { + "Serilog": "2.12.0", + "Serilog.Formatting.Compact": "1.1.0", + "Serilog.Sinks.File": "5.0.0", + "Serilog.Sinks.PeriodicBatching": "3.1.0" + } + }, + "SerilogTimings": { + "type": "Transitive", + "resolved": "3.0.1", + "contentHash": "Zs28eTgszAMwpIrbBnWHBI50yuxL50p/dmAUWmy75+axdZYK/Sjm5/5m1N/CisR8acJUhTVcjPZrsB1P5iv0Uw==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "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.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, + "System.Collections.Immutable": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "FXkLXiK0sVVewcso0imKQoOxjoPAj42R8HtjjbSjVPAzwDfzoyoznWxgA3c38LDbN9SJux1xXoXYAhz98j7r2g==", + "dependencies": { + "System.Memory": "4.5.4" + } + }, + "System.DoubleNumerics": { + "type": "Transitive", + "resolved": "3.1.3", + "contentHash": "KRKEM/L3KBodjA9VOg3EifFVWUY6EOqaMB05UvPEDm7Zeby/kZW+4kdWUEPzW6xtkwf46p661L9NrbeeQhtLzw==", + "dependencies": { + "NETStandard.Library": "1.6.1" + } + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Numerics.Vectors": "4.5.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + }, + "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.5.0", + "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" + }, + "System.Reactive": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "System.Reflection.Metadata": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "5NecZgXktdGg34rh1OenY1rFNDCI8xSjFr+Z4OU4cU06AQHUdRnIIEeWENu3Wl4YowbzkymAIMvi3WyK9U53pQ==", + "dependencies": { + "System.Collections.Immutable": "5.0.0" + } + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "ZD9TMpsmYJLrxbbmdvhwt9YEgG5WntEnZ/d1eH8JBX9LBp+Ju8BSBhUGbZMNVHHomWo2KVImJhTDl2hIgw/6MA==" + }, + "System.Runtime.InteropServices.RuntimeInformation": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==" + }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "5.0.1", + "contentHash": "KmJ+CJXizDofbq6mpqDoRRLcxgOd2z9X3XoFNULSbvbqVRZkFX3istvr+MUjL6Zw1RT+RNdoI4GYidIINtgvqQ==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Memory": "4.5.4" + } + }, + "System.Text.Json": { + "type": "Transitive", + "resolved": "5.0.2", + "contentHash": "I47dVIGiV6SfAyppphxqupertT/5oZkYLDCX6vC3HpOI4ZLjyoKAreUoem2ie6G0RbRuFrlqz/PcTQjfb2DOfQ==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "5.0.0", + "System.Buffers": "4.5.1", + "System.Memory": "4.5.4", + "System.Numerics.Vectors": "4.5.0", + "System.Runtime.CompilerServices.Unsafe": "5.0.0", + "System.Text.Encodings.Web": "5.0.1", + "System.Threading.Tasks.Extensions": "4.5.4", + "System.ValueTuple": "4.5.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" + } + }, + "System.ValueTuple": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "okurQJO6NRE/apDIP23ajJ0hpiNmJ+f0BwOlB/cSqTLQlw5upkf+5+96+iG2Jw40G1fCVCyPz/FhIABUjMR+RQ==" + }, + "speckle.autofac": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "[7.0.0, )" + } + }, + "speckle.converters.civil3d2024": { + "type": "Project", + "dependencies": { + "Speckle.AutoCAD.API": "[2024.0.0, )", + "Speckle.Civil3D.API": "[2024.0.0, )", + "Speckle.Converters.Common": "[2.0.999-local, )" + } + }, + "speckle.converters.common": { + "type": "Project", + "dependencies": { + "Speckle.Autofac": "[2.0.999-local, )", + "Speckle.Objects": "[3.0.1-alpha.14, )" + } + }, + "speckle.converters.common.dependencyinjection": { + "type": "Project", + "dependencies": { + "Speckle.Autofac": "[2.0.999-local, )", + "Speckle.Converters.Common": "[2.0.999-local, )" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "CentralTransitive", + "requested": "[7.0.0, )", + "resolved": "7.0.0", + "contentHash": "kmn78+LPVMOWeITUjIlfxUPDsI0R6G0RkeAMBmQxAJ7vBJn4q2dTva7pWi65ceN5vPGjJ9q/Uae2WKgvfktJAw==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Memory": "4.5.5" + } + }, + "Speckle.AutoCAD.API": { + "type": "CentralTransitive", + "requested": "[2024.0.0, )", + "resolved": "2024.0.0", + "contentHash": "pZZ5uI+NXhZaQnsqRkgp/rywqBAjDObDJ9XNFGJvemT5k2OthDpHzlK/mKxz8QDCYie7uImQ8dv3uWj2QUFDPw==" + }, + "Speckle.Civil3D.API": { + "type": "CentralTransitive", + "requested": "[2024.0.0, )", + "resolved": "2024.0.0", + "contentHash": "9Q7M1k0DotN8w7MkiScQezErRdnZ4dAkxBMcPNhHSWoth/lSaT6UPV1aYEdl90RhehJWG4l3O7U2e3OXvVSFdw==", + "dependencies": { + "Speckle.AutoCAD.API": "2024.0.0" + } + }, + "Speckle.Core": { + "type": "CentralTransitive", + "requested": "[3.0.1-alpha.14, )", + "resolved": "3.0.1-alpha.14", + "contentHash": "RzQPVIGFFkKvG56YLr8ACtiwdWJE6IJ9vCQ4qHa0PIsUEpfzAIAi59jnzqtByOFC0FiFrFPow9bkfzylaZorAA==", + "dependencies": { + "GraphQL.Client": "6.0.0", + "Microsoft.CSharp": "4.7.0", + "Microsoft.Data.Sqlite": "7.0.5", + "Polly": "7.2.3", + "Polly.Contrib.WaitAndRetry": "1.1.1", + "Polly.Extensions.Http": "3.0.0", + "Sentry": "3.33.0", + "Sentry.Serilog": "3.33.0", + "Serilog": "2.12.0", + "Serilog.Enrichers.ClientInfo": "1.3.0", + "Serilog.Exceptions": "8.4.0", + "Serilog.Sinks.Console": "4.1.0", + "Serilog.Sinks.Seq": "5.2.2", + "SerilogTimings": "3.0.1", + "Speckle.Newtonsoft.Json": "13.0.2", + "System.DoubleNumerics": "3.1.3" + } + }, + "Speckle.Objects": { + "type": "CentralTransitive", + "requested": "[3.0.1-alpha.14, )", + "resolved": "3.0.1-alpha.14", + "contentHash": "z38LGryMvh7iU1uBW+4uo5DwsB3CwRgLt2uFexWFx3mPSid+A0l5XcJzOgLwgFhNl6B42Ryz4ezBsddTp1Uc/g==", + "dependencies": { + "Speckle.Core": "3.0.1-alpha.14" + } + } + } + } +} \ No newline at end of file diff --git a/DUI3-DX/Converters/Civil3d/Speckle.Converters.Civil3d2024/Speckle.Converters.Civil3d2024.csproj b/DUI3-DX/Converters/Civil3d/Speckle.Converters.Civil3d2024/Speckle.Converters.Civil3d2024.csproj new file mode 100644 index 0000000000..5cd6f9707b --- /dev/null +++ b/DUI3-DX/Converters/Civil3d/Speckle.Converters.Civil3d2024/Speckle.Converters.Civil3d2024.csproj @@ -0,0 +1,21 @@ + + + + net48 + x64 + + + + + + + + + + + + + + + + diff --git a/DUI3-DX/Converters/Civil3d/Speckle.Converters.Civil3d2024/packages.lock.json b/DUI3-DX/Converters/Civil3d/Speckle.Converters.Civil3d2024/packages.lock.json new file mode 100644 index 0000000000..853f08eb85 --- /dev/null +++ b/DUI3-DX/Converters/Civil3d/Speckle.Converters.Civil3d2024/packages.lock.json @@ -0,0 +1,424 @@ +{ + "version": 2, + "dependencies": { + ".NETFramework,Version=v4.8": { + "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.AutoCAD.API": { + "type": "Direct", + "requested": "[2024.0.0, )", + "resolved": "2024.0.0", + "contentHash": "pZZ5uI+NXhZaQnsqRkgp/rywqBAjDObDJ9XNFGJvemT5k2OthDpHzlK/mKxz8QDCYie7uImQ8dv3uWj2QUFDPw==" + }, + "Speckle.Civil3D.API": { + "type": "Direct", + "requested": "[2024.0.0, )", + "resolved": "2024.0.0", + "contentHash": "9Q7M1k0DotN8w7MkiScQezErRdnZ4dAkxBMcPNhHSWoth/lSaT6UPV1aYEdl90RhehJWG4l3O7U2e3OXvVSFdw==", + "dependencies": { + "Speckle.AutoCAD.API": "2024.0.0" + } + }, + "Speckle.InterfaceGenerator": { + "type": "Direct", + "requested": "[0.9.5, )", + "resolved": "0.9.5", + "contentHash": "oU/L7pN1R7q8KkbrpQ3WJnHirPHqn+9DEA7asOcUiggV5dzVg1A/VYs7GOSusD24njxXh03tE3a2oTLOjt3cVg==" + }, + "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.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "Polly": { + "type": "Transitive", + "resolved": "7.2.3", + "contentHash": "DeCY0OFbNdNxsjntr1gTXHJ5pKUwYzp04Er2LLeN3g6pWhffsGuKVfMBLe1lw7x76HrPkLxKEFxBlpRxS2nDEQ==" + }, + "Polly.Contrib.WaitAndRetry": { + "type": "Transitive", + "resolved": "1.1.1", + "contentHash": "1MUQLiSo4KDkQe6nzQRhIU05lm9jlexX5BVsbuw0SL82ynZ+GzAHQxJVDPVBboxV37Po3SG077aX8DuSy8TkaA==" + }, + "Polly.Extensions.Http": { + "type": "Transitive", + "resolved": "3.0.0", + "contentHash": "drrG+hB3pYFY7w1c3BD+lSGYvH2oIclH8GRSehgfyP5kjnFnHKQuuBhuHLv+PWyFuaTDyk/vfRpnxOzd11+J8g==", + "dependencies": { + "Polly": "7.1.0" + } + }, + "Sentry": { + "type": "Transitive", + "resolved": "3.33.0", + "contentHash": "8vbD2o6IR2wrRrkSiRbnodWGWUOqIlwYtzpjvPNOb5raJdOf+zxMwfS8f6nx9bmrTTfDj7KrCB8C/5OuicAc8A==", + "dependencies": { + "System.Reflection.Metadata": "5.0.0", + "System.Runtime.InteropServices.RuntimeInformation": "4.3.0", + "System.Text.Json": "5.0.2" + } + }, + "Sentry.Serilog": { + "type": "Transitive", + "resolved": "3.33.0", + "contentHash": "V8BU7QGWg2qLYfNPqtuTBhC1opysny5l+Ifp6J6PhOeAxU0FssR7nYfbJVetrnLIoh2rd3DlJ6hHYYQosQYcUQ==", + "dependencies": { + "Sentry": "3.33.0", + "Serilog": "2.7.1" + } + }, + "Serilog": { + "type": "Transitive", + "resolved": "2.12.0", + "contentHash": "xaiJLIdu6rYMKfQMYUZgTy8YK7SMZjB4Yk50C/u//Z4OsvxkUfSPJy4nknfvwAC34yr13q7kcyh4grbwhSxyZg==" + }, + "Serilog.Enrichers.ClientInfo": { + "type": "Transitive", + "resolved": "1.3.0", + "contentHash": "mTc7PM+wC9Hr7LWSwqt5mmnlAr7RJs+eTb3PGPRhwdOackk95MkhUZognuxXEdlW19HAFNmEBTSBY5DfLwM8jQ==", + "dependencies": { + "Serilog": "2.4.0" + } + }, + "Serilog.Exceptions": { + "type": "Transitive", + "resolved": "8.4.0", + "contentHash": "nc/+hUw3lsdo0zCj0KMIybAu7perMx79vu72w0za9Nsi6mWyNkGXxYxakAjWB7nEmYL6zdmhEQRB4oJ2ALUeug==", + "dependencies": { + "Serilog": "2.8.0" + } + }, + "Serilog.Formatting.Compact": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "pNroKVjo+rDqlxNG5PXkRLpfSCuDOBY0ri6jp9PLe505ljqwhwZz8ospy2vWhQlFu5GkIesh3FcDs4n7sWZODA==", + "dependencies": { + "Serilog": "2.8.0" + } + }, + "Serilog.Sinks.Console": { + "type": "Transitive", + "resolved": "4.1.0", + "contentHash": "K6N5q+5fetjnJPvCmkWOpJ/V8IEIoMIB1s86OzBrbxwTyHxdx3pmz4H+8+O/Dc/ftUX12DM1aynx/dDowkwzqg==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "Serilog.Sinks.File": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "uwV5hdhWPwUH1szhO8PJpFiahqXmzPzJT/sOijH/kFgUx+cyoDTMM8MHD0adw9+Iem6itoibbUXHYslzXsLEAg==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "Serilog.Sinks.PeriodicBatching": { + "type": "Transitive", + "resolved": "3.1.0", + "contentHash": "NDWR7m3PalVlGEq3rzoktrXikjFMLmpwF0HI4sowo8YDdU+gqPlTHlDQiOGxHfB0sTfjPA9JjA7ctKG9zqjGkw==", + "dependencies": { + "Serilog": "2.0.0" + } + }, + "Serilog.Sinks.Seq": { + "type": "Transitive", + "resolved": "5.2.2", + "contentHash": "1Csmo5ua7NKUe0yXUx+zsRefjAniPWcXFhUXxXG8pwo0iMiw2gjn9SOkgYnnxbgWqmlGv236w0N/dHc2v5XwMg==", + "dependencies": { + "Serilog": "2.12.0", + "Serilog.Formatting.Compact": "1.1.0", + "Serilog.Sinks.File": "5.0.0", + "Serilog.Sinks.PeriodicBatching": "3.1.0" + } + }, + "SerilogTimings": { + "type": "Transitive", + "resolved": "3.0.1", + "contentHash": "Zs28eTgszAMwpIrbBnWHBI50yuxL50p/dmAUWmy75+axdZYK/Sjm5/5m1N/CisR8acJUhTVcjPZrsB1P5iv0Uw==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "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.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, + "System.Collections.Immutable": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "FXkLXiK0sVVewcso0imKQoOxjoPAj42R8HtjjbSjVPAzwDfzoyoznWxgA3c38LDbN9SJux1xXoXYAhz98j7r2g==", + "dependencies": { + "System.Memory": "4.5.4" + } + }, + "System.DoubleNumerics": { + "type": "Transitive", + "resolved": "3.1.3", + "contentHash": "KRKEM/L3KBodjA9VOg3EifFVWUY6EOqaMB05UvPEDm7Zeby/kZW+4kdWUEPzW6xtkwf46p661L9NrbeeQhtLzw==", + "dependencies": { + "NETStandard.Library": "1.6.1" + } + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Numerics.Vectors": "4.5.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + }, + "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.5.0", + "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" + }, + "System.Reactive": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "System.Reflection.Metadata": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "5NecZgXktdGg34rh1OenY1rFNDCI8xSjFr+Z4OU4cU06AQHUdRnIIEeWENu3Wl4YowbzkymAIMvi3WyK9U53pQ==", + "dependencies": { + "System.Collections.Immutable": "5.0.0" + } + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "ZD9TMpsmYJLrxbbmdvhwt9YEgG5WntEnZ/d1eH8JBX9LBp+Ju8BSBhUGbZMNVHHomWo2KVImJhTDl2hIgw/6MA==" + }, + "System.Runtime.InteropServices.RuntimeInformation": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==" + }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "5.0.1", + "contentHash": "KmJ+CJXizDofbq6mpqDoRRLcxgOd2z9X3XoFNULSbvbqVRZkFX3istvr+MUjL6Zw1RT+RNdoI4GYidIINtgvqQ==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Memory": "4.5.4" + } + }, + "System.Text.Json": { + "type": "Transitive", + "resolved": "5.0.2", + "contentHash": "I47dVIGiV6SfAyppphxqupertT/5oZkYLDCX6vC3HpOI4ZLjyoKAreUoem2ie6G0RbRuFrlqz/PcTQjfb2DOfQ==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "5.0.0", + "System.Buffers": "4.5.1", + "System.Memory": "4.5.4", + "System.Numerics.Vectors": "4.5.0", + "System.Runtime.CompilerServices.Unsafe": "5.0.0", + "System.Text.Encodings.Web": "5.0.1", + "System.Threading.Tasks.Extensions": "4.5.4", + "System.ValueTuple": "4.5.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" + } + }, + "System.ValueTuple": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "okurQJO6NRE/apDIP23ajJ0hpiNmJ+f0BwOlB/cSqTLQlw5upkf+5+96+iG2Jw40G1fCVCyPz/FhIABUjMR+RQ==" + }, + "speckle.autofac": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "[7.0.0, )" + } + }, + "speckle.converters.common": { + "type": "Project", + "dependencies": { + "Speckle.Autofac": "[2.0.999-local, )", + "Speckle.Objects": "[3.0.1-alpha.14, )" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "CentralTransitive", + "requested": "[7.0.0, )", + "resolved": "7.0.0", + "contentHash": "kmn78+LPVMOWeITUjIlfxUPDsI0R6G0RkeAMBmQxAJ7vBJn4q2dTva7pWi65ceN5vPGjJ9q/Uae2WKgvfktJAw==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Memory": "4.5.5" + } + }, + "Speckle.Core": { + "type": "CentralTransitive", + "requested": "[3.0.1-alpha.14, )", + "resolved": "3.0.1-alpha.14", + "contentHash": "RzQPVIGFFkKvG56YLr8ACtiwdWJE6IJ9vCQ4qHa0PIsUEpfzAIAi59jnzqtByOFC0FiFrFPow9bkfzylaZorAA==", + "dependencies": { + "GraphQL.Client": "6.0.0", + "Microsoft.CSharp": "4.7.0", + "Microsoft.Data.Sqlite": "7.0.5", + "Polly": "7.2.3", + "Polly.Contrib.WaitAndRetry": "1.1.1", + "Polly.Extensions.Http": "3.0.0", + "Sentry": "3.33.0", + "Sentry.Serilog": "3.33.0", + "Serilog": "2.12.0", + "Serilog.Enrichers.ClientInfo": "1.3.0", + "Serilog.Exceptions": "8.4.0", + "Serilog.Sinks.Console": "4.1.0", + "Serilog.Sinks.Seq": "5.2.2", + "SerilogTimings": "3.0.1", + "Speckle.Newtonsoft.Json": "13.0.2", + "System.DoubleNumerics": "3.1.3" + } + }, + "Speckle.Objects": { + "type": "CentralTransitive", + "requested": "[3.0.1-alpha.14, )", + "resolved": "3.0.1-alpha.14", + "contentHash": "z38LGryMvh7iU1uBW+4uo5DwsB3CwRgLt2uFexWFx3mPSid+A0l5XcJzOgLwgFhNl6B42Ryz4ezBsddTp1Uc/g==", + "dependencies": { + "Speckle.Core": "3.0.1-alpha.14" + } + } + } + } +} \ No newline at end of file diff --git a/DUI3-DX/Converters/Civil3d/Speckle.Converters.Civil3dShared/Civil3dConversionContextStack.cs b/DUI3-DX/Converters/Civil3d/Speckle.Converters.Civil3dShared/Civil3dConversionContextStack.cs new file mode 100644 index 0000000000..0d803db250 --- /dev/null +++ b/DUI3-DX/Converters/Civil3d/Speckle.Converters.Civil3dShared/Civil3dConversionContextStack.cs @@ -0,0 +1,38 @@ +using System.Diagnostics.CodeAnalysis; +using Speckle.Converters.Common; + +namespace Speckle.Converters.Civil3d; + +// POC: Suppressed naming warning for now, but we should evaluate if we should follow this or disable it. +[SuppressMessage( + "Naming", + "CA1711:Identifiers should not have incorrect suffix", + Justification = "Name ends in Stack but it is in fact a Stack, just not inheriting from `System.Collections.Stack`" +)] +public class Civil3dConversionContextStack : ConversionContextStack +{ + public Civil3dConversionContextStack(IHostToSpeckleUnitConverter unitConverter) + : base( + Application.DocumentManager.CurrentDocument, + GetDocBuiltInUnit(Application.DocumentManager.CurrentDocument), + unitConverter + ) { } + + private static AAEC.BuiltInUnit GetDocBuiltInUnit(Document doc) + { + AAEC.BuiltInUnit unit = AAEC.BuiltInUnit.Dimensionless; + + using (ADB.Transaction tr = doc.Database.TransactionManager.StartTransaction()) + { + ADB.ObjectId id = AAEC.ApplicationServices.DrawingSetupVariables.GetInstance(doc.Database, false); + if (tr.GetObject(id, ADB.OpenMode.ForRead) is AAEC.ApplicationServices.DrawingSetupVariables setupVariables) + { + unit = setupVariables.LinearUnit; + } + + tr.Commit(); + } + + return unit; + } +} diff --git a/DUI3-DX/Converters/Civil3d/Speckle.Converters.Civil3dShared/Civil3dRootToHostConverter.cs b/DUI3-DX/Converters/Civil3d/Speckle.Converters.Civil3dShared/Civil3dRootToHostConverter.cs new file mode 100644 index 0000000000..265cd3a796 --- /dev/null +++ b/DUI3-DX/Converters/Civil3d/Speckle.Converters.Civil3dShared/Civil3dRootToHostConverter.cs @@ -0,0 +1,59 @@ +using Autodesk.AutoCAD.DatabaseServices; +using Speckle.Autofac.DependencyInjection; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; + +namespace Speckle.Converters.Civil3d; + +public class Civil3dRootToHostConverter : IRootToSpeckleConverter +{ + private readonly IFactory _toSpeckle; + private readonly IConversionContextStack _contextStack; + + public Civil3dRootToHostConverter( + IFactory toSpeckle, + IConversionContextStack contextStack + ) + { + _toSpeckle = toSpeckle; + _contextStack = contextStack; + } + + public Base Convert(object target) + { + if (target is not DBObject dbObject) + { + throw new NotSupportedException( + $"Conversion of {target.GetType().Name} to Speckle is not supported. Only objects that inherit from DBObject are." + ); + } + + Type type = dbObject.GetType(); + + try + { + using (var l = _contextStack.Current.Document.LockDocument()) + { + using (var tr = _contextStack.Current.Document.Database.TransactionManager.StartTransaction()) + { + var objectConverter = _toSpeckle.ResolveInstance(type.Name); + + if (objectConverter == null) + { + throw new NotSupportedException($"No conversion found for {target.GetType().Name}"); + } + + var convertedObject = objectConverter.Convert(dbObject); + tr.Commit(); + return convertedObject; + } + } + } + catch (SpeckleConversionException e) + { + Console.WriteLine(e); + throw; // Just rethrowing for now, Logs may be needed here. + } + } +} diff --git a/DUI3-DX/Converters/Civil3d/Speckle.Converters.Civil3dShared/Civil3dToSpeckleUnitConverter.cs b/DUI3-DX/Converters/Civil3d/Speckle.Converters.Civil3dShared/Civil3dToSpeckleUnitConverter.cs new file mode 100644 index 0000000000..0c61621a4c --- /dev/null +++ b/DUI3-DX/Converters/Civil3d/Speckle.Converters.Civil3dShared/Civil3dToSpeckleUnitConverter.cs @@ -0,0 +1,39 @@ +using Speckle.Converters.Common; +using Speckle.Core.Kits; +using Speckle.Core.Logging; // POC: boy do I think this is the wrong place for SpeckleException! + +namespace Speckle.Converters.Civil3d; + +public class Civil3dToSpeckleUnitConverter : IHostToSpeckleUnitConverter +{ + private static readonly IReadOnlyDictionary s_unitsMapping = Create(); + + private static IReadOnlyDictionary Create() + { + var dict = new Dictionary(); + + // POC: we should have a unit test to confirm these are as expected and don't change + dict[AAEC.BuiltInUnit.Kilometer] = Units.Kilometers; + dict[AAEC.BuiltInUnit.Meter] = Units.Meters; + dict[AAEC.BuiltInUnit.Centimeter] = Units.Centimeters; + dict[AAEC.BuiltInUnit.Millimeter] = Units.Millimeters; + dict[AAEC.BuiltInUnit.Mile] = Units.Miles; + dict[AAEC.BuiltInUnit.Yards] = Units.Yards; + dict[AAEC.BuiltInUnit.Foot] = Units.Feet; + dict[AAEC.BuiltInUnit.SurveyFoot] = Units.USFeet; + dict[AAEC.BuiltInUnit.Inch] = Units.Inches; + dict[AAEC.BuiltInUnit.Dimensionless] = Units.None; + return dict; + } + + public string ConvertOrThrow(AAEC.BuiltInUnit hostUnit) + { + if (s_unitsMapping.TryGetValue(hostUnit, out string value)) + { + return value; + } + + // POC: probably would prefer something more specific + throw new SpeckleException($"The Unit System \"{hostUnit}\" is unsupported."); + } +} diff --git a/DUI3-DX/Converters/Civil3d/Speckle.Converters.Civil3dShared/GlobalUsings.cs b/DUI3-DX/Converters/Civil3d/Speckle.Converters.Civil3dShared/GlobalUsings.cs new file mode 100644 index 0000000000..c4134c866e --- /dev/null +++ b/DUI3-DX/Converters/Civil3d/Speckle.Converters.Civil3dShared/GlobalUsings.cs @@ -0,0 +1,3 @@ +global using AAEC = Autodesk.Aec; +global using CDB = Autodesk.Civil.DatabaseServices; +global using SOBE = Objects.BuiltElements; diff --git a/DUI3-DX/Converters/Civil3d/Speckle.Converters.Civil3dShared/Speckle.Converters.Civil3dShared.projitems b/DUI3-DX/Converters/Civil3d/Speckle.Converters.Civil3dShared/Speckle.Converters.Civil3dShared.projitems new file mode 100644 index 0000000000..1dc6ebd3b8 --- /dev/null +++ b/DUI3-DX/Converters/Civil3d/Speckle.Converters.Civil3dShared/Speckle.Converters.Civil3dShared.projitems @@ -0,0 +1,19 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + 35175682-da83-4c0a-a49d-b191f5885d8e + + + Speckle.Converters.Civil3dShared + + + + + + + + + + \ No newline at end of file diff --git a/DUI3-DX/Converters/Civil3d/Speckle.Converters.Civil3dShared/Speckle.Converters.Civil3dShared.shproj b/DUI3-DX/Converters/Civil3d/Speckle.Converters.Civil3dShared/Speckle.Converters.Civil3dShared.shproj new file mode 100644 index 0000000000..ab81b391e7 --- /dev/null +++ b/DUI3-DX/Converters/Civil3d/Speckle.Converters.Civil3dShared/Speckle.Converters.Civil3dShared.shproj @@ -0,0 +1,13 @@ + + + + 35175682-da83-4c0a-a49d-b191f5885d8e + 14.0 + + + + + + + + diff --git a/DUI3-DX/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/BuiltElements/PipeToSpeckleConverter.cs b/DUI3-DX/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/BuiltElements/PipeToSpeckleConverter.cs new file mode 100644 index 0000000000..f061a890bd --- /dev/null +++ b/DUI3-DX/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/BuiltElements/PipeToSpeckleConverter.cs @@ -0,0 +1,60 @@ +using Objects; +using Objects.Other; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; +using AECPropDB = Autodesk.Aec.PropertyData.DatabaseServices; + +namespace Speckle.Converters.Civil3d.ToSpeckle.BuiltElements; + +[NameAndRankValue(nameof(CDB.Pipe), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class PipeToSpeckleConverter : IToSpeckleTopLevelConverter +{ + private readonly ITypedConverter _pointConverter; + private readonly ITypedConverter _curveConverter; + private readonly ITypedConverter _boxConverter; + private readonly ITypedConverter _solidConverter; + private readonly ITypedConverter> _propertySetConverter; + private readonly IConversionContextStack _contextStack; + + public PipeToSpeckleConverter( + ITypedConverter pointConverter, + ITypedConverter curveConverter, + ITypedConverter boxConverter, + ITypedConverter solidConverter, + ITypedConverter> propertySetConverter, + IConversionContextStack contextStack + ) + { + _pointConverter = pointConverter; + _curveConverter = curveConverter; + _boxConverter = boxConverter; + _solidConverter = solidConverter; + _propertySetConverter = propertySetConverter; + _contextStack = contextStack; + } + + public Base Convert(object target) => Convert((CDB.Pipe)target); + + public SOBE.Pipe Convert(CDB.Pipe target) + { + ICurve curve = _curveConverter.Convert(target.BaseCurve); + SOG.Mesh pipeMesh = _solidConverter.Convert(target.Solid3dBody); + + SOBE.Pipe specklePipe = + new() + { + baseCurve = curve, + diameter = target.InnerDiameterOrWidth, + length = target.Length3DToInsideEdge, + displayValue = new List { pipeMesh }, + units = _contextStack.Current.SpeckleUnits + }; + + // POC: not setting property sets yet, need to determine connector parameter interoperability + // POC: not setting part data yet, same reason as above + // POC: not setting additional pipe properties, probably should scope a CivilPipe class + + return specklePipe; + } +} diff --git a/DUI3-DX/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/Raw/PropertySetToSpeckleRawConverter.cs b/DUI3-DX/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/Raw/PropertySetToSpeckleRawConverter.cs new file mode 100644 index 0000000000..d3576871cc --- /dev/null +++ b/DUI3-DX/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/Raw/PropertySetToSpeckleRawConverter.cs @@ -0,0 +1,55 @@ +using Objects.Other; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using AECPropDB = Autodesk.Aec.PropertyData.DatabaseServices; + +namespace Speckle.Converters.Civil3d.ToSpeckle.Raw; + +public class PropertySetToSpeckleRawConverter : ITypedConverter> +{ + private readonly ITypedConverter _vectorConverter; + private readonly ITypedConverter _pointConverter; + private readonly IConversionContextStack _contextStack; + + public PropertySetToSpeckleRawConverter( + ITypedConverter vectorConverter, + ITypedConverter pointConverter, + IConversionContextStack contextStack + ) + { + _vectorConverter = vectorConverter; + _pointConverter = pointConverter; + _contextStack = contextStack; + } + + public List Convert(object target) => Convert((AECPropDB.PropertySet)target); + + public List Convert(AECPropDB.PropertySet target) + { + List properties = new(); + + ADB.Transaction tr = _contextStack.Current.Document.TransactionManager.TopTransaction; + AECPropDB.PropertySetDefinition setDef = (AECPropDB.PropertySetDefinition) + tr.GetObject(target.PropertySetDefinition, ADB.OpenMode.ForRead); + + // get property definitions + var propDefs = new Dictionary(); + foreach (AECPropDB.PropertyDefinition def in setDef.Definitions) + { + propDefs.Add(def.Id, def); + } + + foreach (AECPropDB.PropertySetData data in target.PropertySetData) + { + string fieldName = propDefs.TryGetValue(data.Id, out AECPropDB.PropertyDefinition value) + ? value.Name + : data.FieldBucketId; + + object fieldData = data.GetData(); + DataField field = new(fieldName, fieldData.GetType().Name, data.UnitType.PluralName(false), fieldData); + properties.Add(field); + } + + return properties; + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.Revit2023.DependencyInjection/RevitConverterModule.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.Revit2023.DependencyInjection/RevitConverterModule.cs new file mode 100644 index 0000000000..e82fe0100a --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.Revit2023.DependencyInjection/RevitConverterModule.cs @@ -0,0 +1,43 @@ +using Autodesk.Revit.DB; +using Speckle.Autofac.DependencyInjection; +using Speckle.Converters.Common; +using Speckle.Converters.Common.DependencyInjection; +using Speckle.Converters.RevitShared; +using Speckle.Converters.RevitShared.Helpers; +using Speckle.Converters.RevitShared.Services; +using Speckle.Converters.RevitShared.ToSpeckle; + +namespace Speckle.Converters.Revit2023.DependencyInjection; + +public class RevitConverterModule : ISpeckleModule +{ + public void Load(SpeckleContainerBuilder builder) + { + // Register single root + builder.AddRootCommon(); + + // register all application converters + builder.AddApplicationConverters(); + + builder.AddScoped(); + builder.AddSingleton(new RevitContext()); + + // POC: do we need ToSpeckleScalingService as is, do we need to interface it out? + builder.AddScoped(); + builder.AddScoped(); + + // POC: the concrete type can come out if we remove all the reference to it + builder.AddScoped(); + + builder.AddScoped(); + builder.AddScoped(); + + builder.AddScoped(); + + builder.AddScoped(); + builder.AddScoped(); + builder.AddScoped(); + builder.AddScoped(); + builder.AddScoped(); + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.Revit2023.DependencyInjection/Speckle.Converters.Revit2023.DependencyInjection.csproj b/DUI3-DX/Converters/Revit/Speckle.Converters.Revit2023.DependencyInjection/Speckle.Converters.Revit2023.DependencyInjection.csproj new file mode 100644 index 0000000000..704fef95ed --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.Revit2023.DependencyInjection/Speckle.Converters.Revit2023.DependencyInjection.csproj @@ -0,0 +1,14 @@ + + + + net48 + x64 + + + + + + + + + diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.Revit2023.DependencyInjection/packages.lock.json b/DUI3-DX/Converters/Revit/Speckle.Converters.Revit2023.DependencyInjection/packages.lock.json new file mode 100644 index 0000000000..254754bd42 --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.Revit2023.DependencyInjection/packages.lock.json @@ -0,0 +1,429 @@ +{ + "version": 2, + "dependencies": { + ".NETFramework,Version=v4.8": { + "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.5, )", + "resolved": "0.9.5", + "contentHash": "oU/L7pN1R7q8KkbrpQ3WJnHirPHqn+9DEA7asOcUiggV5dzVg1A/VYs7GOSusD24njxXh03tE3a2oTLOjt3cVg==" + }, + "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.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "Polly": { + "type": "Transitive", + "resolved": "7.2.3", + "contentHash": "DeCY0OFbNdNxsjntr1gTXHJ5pKUwYzp04Er2LLeN3g6pWhffsGuKVfMBLe1lw7x76HrPkLxKEFxBlpRxS2nDEQ==" + }, + "Polly.Contrib.WaitAndRetry": { + "type": "Transitive", + "resolved": "1.1.1", + "contentHash": "1MUQLiSo4KDkQe6nzQRhIU05lm9jlexX5BVsbuw0SL82ynZ+GzAHQxJVDPVBboxV37Po3SG077aX8DuSy8TkaA==" + }, + "Polly.Extensions.Http": { + "type": "Transitive", + "resolved": "3.0.0", + "contentHash": "drrG+hB3pYFY7w1c3BD+lSGYvH2oIclH8GRSehgfyP5kjnFnHKQuuBhuHLv+PWyFuaTDyk/vfRpnxOzd11+J8g==", + "dependencies": { + "Polly": "7.1.0" + } + }, + "Sentry": { + "type": "Transitive", + "resolved": "3.33.0", + "contentHash": "8vbD2o6IR2wrRrkSiRbnodWGWUOqIlwYtzpjvPNOb5raJdOf+zxMwfS8f6nx9bmrTTfDj7KrCB8C/5OuicAc8A==", + "dependencies": { + "System.Reflection.Metadata": "5.0.0", + "System.Runtime.InteropServices.RuntimeInformation": "4.3.0", + "System.Text.Json": "5.0.2" + } + }, + "Sentry.Serilog": { + "type": "Transitive", + "resolved": "3.33.0", + "contentHash": "V8BU7QGWg2qLYfNPqtuTBhC1opysny5l+Ifp6J6PhOeAxU0FssR7nYfbJVetrnLIoh2rd3DlJ6hHYYQosQYcUQ==", + "dependencies": { + "Sentry": "3.33.0", + "Serilog": "2.7.1" + } + }, + "Serilog": { + "type": "Transitive", + "resolved": "2.12.0", + "contentHash": "xaiJLIdu6rYMKfQMYUZgTy8YK7SMZjB4Yk50C/u//Z4OsvxkUfSPJy4nknfvwAC34yr13q7kcyh4grbwhSxyZg==" + }, + "Serilog.Enrichers.ClientInfo": { + "type": "Transitive", + "resolved": "1.3.0", + "contentHash": "mTc7PM+wC9Hr7LWSwqt5mmnlAr7RJs+eTb3PGPRhwdOackk95MkhUZognuxXEdlW19HAFNmEBTSBY5DfLwM8jQ==", + "dependencies": { + "Serilog": "2.4.0" + } + }, + "Serilog.Exceptions": { + "type": "Transitive", + "resolved": "8.4.0", + "contentHash": "nc/+hUw3lsdo0zCj0KMIybAu7perMx79vu72w0za9Nsi6mWyNkGXxYxakAjWB7nEmYL6zdmhEQRB4oJ2ALUeug==", + "dependencies": { + "Serilog": "2.8.0" + } + }, + "Serilog.Formatting.Compact": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "pNroKVjo+rDqlxNG5PXkRLpfSCuDOBY0ri6jp9PLe505ljqwhwZz8ospy2vWhQlFu5GkIesh3FcDs4n7sWZODA==", + "dependencies": { + "Serilog": "2.8.0" + } + }, + "Serilog.Sinks.Console": { + "type": "Transitive", + "resolved": "4.1.0", + "contentHash": "K6N5q+5fetjnJPvCmkWOpJ/V8IEIoMIB1s86OzBrbxwTyHxdx3pmz4H+8+O/Dc/ftUX12DM1aynx/dDowkwzqg==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "Serilog.Sinks.File": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "uwV5hdhWPwUH1szhO8PJpFiahqXmzPzJT/sOijH/kFgUx+cyoDTMM8MHD0adw9+Iem6itoibbUXHYslzXsLEAg==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "Serilog.Sinks.PeriodicBatching": { + "type": "Transitive", + "resolved": "3.1.0", + "contentHash": "NDWR7m3PalVlGEq3rzoktrXikjFMLmpwF0HI4sowo8YDdU+gqPlTHlDQiOGxHfB0sTfjPA9JjA7ctKG9zqjGkw==", + "dependencies": { + "Serilog": "2.0.0" + } + }, + "Serilog.Sinks.Seq": { + "type": "Transitive", + "resolved": "5.2.2", + "contentHash": "1Csmo5ua7NKUe0yXUx+zsRefjAniPWcXFhUXxXG8pwo0iMiw2gjn9SOkgYnnxbgWqmlGv236w0N/dHc2v5XwMg==", + "dependencies": { + "Serilog": "2.12.0", + "Serilog.Formatting.Compact": "1.1.0", + "Serilog.Sinks.File": "5.0.0", + "Serilog.Sinks.PeriodicBatching": "3.1.0" + } + }, + "SerilogTimings": { + "type": "Transitive", + "resolved": "3.0.1", + "contentHash": "Zs28eTgszAMwpIrbBnWHBI50yuxL50p/dmAUWmy75+axdZYK/Sjm5/5m1N/CisR8acJUhTVcjPZrsB1P5iv0Uw==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "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.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, + "System.Collections.Immutable": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "FXkLXiK0sVVewcso0imKQoOxjoPAj42R8HtjjbSjVPAzwDfzoyoznWxgA3c38LDbN9SJux1xXoXYAhz98j7r2g==", + "dependencies": { + "System.Memory": "4.5.4" + } + }, + "System.DoubleNumerics": { + "type": "Transitive", + "resolved": "3.1.3", + "contentHash": "KRKEM/L3KBodjA9VOg3EifFVWUY6EOqaMB05UvPEDm7Zeby/kZW+4kdWUEPzW6xtkwf46p661L9NrbeeQhtLzw==", + "dependencies": { + "NETStandard.Library": "1.6.1" + } + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Numerics.Vectors": "4.5.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + }, + "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.5.0", + "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" + }, + "System.Reactive": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "System.Reflection.Metadata": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "5NecZgXktdGg34rh1OenY1rFNDCI8xSjFr+Z4OU4cU06AQHUdRnIIEeWENu3Wl4YowbzkymAIMvi3WyK9U53pQ==", + "dependencies": { + "System.Collections.Immutable": "5.0.0" + } + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "ZD9TMpsmYJLrxbbmdvhwt9YEgG5WntEnZ/d1eH8JBX9LBp+Ju8BSBhUGbZMNVHHomWo2KVImJhTDl2hIgw/6MA==" + }, + "System.Runtime.InteropServices.RuntimeInformation": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==" + }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "5.0.1", + "contentHash": "KmJ+CJXizDofbq6mpqDoRRLcxgOd2z9X3XoFNULSbvbqVRZkFX3istvr+MUjL6Zw1RT+RNdoI4GYidIINtgvqQ==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Memory": "4.5.4" + } + }, + "System.Text.Json": { + "type": "Transitive", + "resolved": "5.0.2", + "contentHash": "I47dVIGiV6SfAyppphxqupertT/5oZkYLDCX6vC3HpOI4ZLjyoKAreUoem2ie6G0RbRuFrlqz/PcTQjfb2DOfQ==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "5.0.0", + "System.Buffers": "4.5.1", + "System.Memory": "4.5.4", + "System.Numerics.Vectors": "4.5.0", + "System.Runtime.CompilerServices.Unsafe": "5.0.0", + "System.Text.Encodings.Web": "5.0.1", + "System.Threading.Tasks.Extensions": "4.5.4", + "System.ValueTuple": "4.5.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" + } + }, + "System.ValueTuple": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "okurQJO6NRE/apDIP23ajJ0hpiNmJ+f0BwOlB/cSqTLQlw5upkf+5+96+iG2Jw40G1fCVCyPz/FhIABUjMR+RQ==" + }, + "speckle.autofac": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "[7.0.0, )" + } + }, + "speckle.converters.common": { + "type": "Project", + "dependencies": { + "Speckle.Autofac": "[2.0.999-local, )", + "Speckle.Objects": "[3.0.1-alpha.14, )" + } + }, + "speckle.converters.common.dependencyinjection": { + "type": "Project", + "dependencies": { + "Speckle.Autofac": "[2.0.999-local, )", + "Speckle.Converters.Common": "[2.0.999-local, )" + } + }, + "speckle.converters.revit2023": { + "type": "Project", + "dependencies": { + "Speckle.Converters.Common": "[2.0.999-local, )", + "Speckle.Revit.API": "[2023.0.0, )" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "CentralTransitive", + "requested": "[7.0.0, )", + "resolved": "7.0.0", + "contentHash": "kmn78+LPVMOWeITUjIlfxUPDsI0R6G0RkeAMBmQxAJ7vBJn4q2dTva7pWi65ceN5vPGjJ9q/Uae2WKgvfktJAw==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Memory": "4.5.5" + } + }, + "Speckle.Core": { + "type": "CentralTransitive", + "requested": "[3.0.1-alpha.14, )", + "resolved": "3.0.1-alpha.14", + "contentHash": "RzQPVIGFFkKvG56YLr8ACtiwdWJE6IJ9vCQ4qHa0PIsUEpfzAIAi59jnzqtByOFC0FiFrFPow9bkfzylaZorAA==", + "dependencies": { + "GraphQL.Client": "6.0.0", + "Microsoft.CSharp": "4.7.0", + "Microsoft.Data.Sqlite": "7.0.5", + "Polly": "7.2.3", + "Polly.Contrib.WaitAndRetry": "1.1.1", + "Polly.Extensions.Http": "3.0.0", + "Sentry": "3.33.0", + "Sentry.Serilog": "3.33.0", + "Serilog": "2.12.0", + "Serilog.Enrichers.ClientInfo": "1.3.0", + "Serilog.Exceptions": "8.4.0", + "Serilog.Sinks.Console": "4.1.0", + "Serilog.Sinks.Seq": "5.2.2", + "SerilogTimings": "3.0.1", + "Speckle.Newtonsoft.Json": "13.0.2", + "System.DoubleNumerics": "3.1.3" + } + }, + "Speckle.Objects": { + "type": "CentralTransitive", + "requested": "[3.0.1-alpha.14, )", + "resolved": "3.0.1-alpha.14", + "contentHash": "z38LGryMvh7iU1uBW+4uo5DwsB3CwRgLt2uFexWFx3mPSid+A0l5XcJzOgLwgFhNl6B42Ryz4ezBsddTp1Uc/g==", + "dependencies": { + "Speckle.Core": "3.0.1-alpha.14" + } + }, + "Speckle.Revit.API": { + "type": "CentralTransitive", + "requested": "[2023.0.0, )", + "resolved": "2023.0.0", + "contentHash": "tq40eD7psgTbV+epNouYyqfo6+hEi7FmXZqcxEOsAV7zfYyWhL6Rt3vmojkWGNuerGbH6oRI6KIIxrnlCNb8Hw==" + } + } + } +} \ No newline at end of file diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.Revit2023/RevitVersionConversionHelper.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.Revit2023/RevitVersionConversionHelper.cs new file mode 100644 index 0000000000..515c2e057f --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.Revit2023/RevitVersionConversionHelper.cs @@ -0,0 +1,19 @@ +using Speckle.Converters.RevitShared; + +namespace Speckle.Converters.Revit2023; + +public class RevitVersionConversionHelper : IRevitVersionConversionHelper +{ + public bool IsCurveClosed(DB.NurbSpline nurbsSpline) + { + try + { + return nurbsSpline.IsClosed; + } + catch (Autodesk.Revit.Exceptions.ApplicationException) + { + // POC: is this actually a good assumption? + return true; + } + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.Revit2023/Speckle.Converters.Revit2023.csproj b/DUI3-DX/Converters/Revit/Speckle.Converters.Revit2023/Speckle.Converters.Revit2023.csproj new file mode 100644 index 0000000000..4b7de6a4df --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.Revit2023/Speckle.Converters.Revit2023.csproj @@ -0,0 +1,18 @@ + + + + net48 + x64 + + + + + + + + + + + + + diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.Revit2023/packages.lock.json b/DUI3-DX/Converters/Revit/Speckle.Converters.Revit2023/packages.lock.json new file mode 100644 index 0000000000..742efddba5 --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.Revit2023/packages.lock.json @@ -0,0 +1,415 @@ +{ + "version": 2, + "dependencies": { + ".NETFramework,Version=v4.8": { + "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.5, )", + "resolved": "0.9.5", + "contentHash": "oU/L7pN1R7q8KkbrpQ3WJnHirPHqn+9DEA7asOcUiggV5dzVg1A/VYs7GOSusD24njxXh03tE3a2oTLOjt3cVg==" + }, + "Speckle.Revit.API": { + "type": "Direct", + "requested": "[2023.0.0, )", + "resolved": "2023.0.0", + "contentHash": "tq40eD7psgTbV+epNouYyqfo6+hEi7FmXZqcxEOsAV7zfYyWhL6Rt3vmojkWGNuerGbH6oRI6KIIxrnlCNb8Hw==" + }, + "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.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "Polly": { + "type": "Transitive", + "resolved": "7.2.3", + "contentHash": "DeCY0OFbNdNxsjntr1gTXHJ5pKUwYzp04Er2LLeN3g6pWhffsGuKVfMBLe1lw7x76HrPkLxKEFxBlpRxS2nDEQ==" + }, + "Polly.Contrib.WaitAndRetry": { + "type": "Transitive", + "resolved": "1.1.1", + "contentHash": "1MUQLiSo4KDkQe6nzQRhIU05lm9jlexX5BVsbuw0SL82ynZ+GzAHQxJVDPVBboxV37Po3SG077aX8DuSy8TkaA==" + }, + "Polly.Extensions.Http": { + "type": "Transitive", + "resolved": "3.0.0", + "contentHash": "drrG+hB3pYFY7w1c3BD+lSGYvH2oIclH8GRSehgfyP5kjnFnHKQuuBhuHLv+PWyFuaTDyk/vfRpnxOzd11+J8g==", + "dependencies": { + "Polly": "7.1.0" + } + }, + "Sentry": { + "type": "Transitive", + "resolved": "3.33.0", + "contentHash": "8vbD2o6IR2wrRrkSiRbnodWGWUOqIlwYtzpjvPNOb5raJdOf+zxMwfS8f6nx9bmrTTfDj7KrCB8C/5OuicAc8A==", + "dependencies": { + "System.Reflection.Metadata": "5.0.0", + "System.Runtime.InteropServices.RuntimeInformation": "4.3.0", + "System.Text.Json": "5.0.2" + } + }, + "Sentry.Serilog": { + "type": "Transitive", + "resolved": "3.33.0", + "contentHash": "V8BU7QGWg2qLYfNPqtuTBhC1opysny5l+Ifp6J6PhOeAxU0FssR7nYfbJVetrnLIoh2rd3DlJ6hHYYQosQYcUQ==", + "dependencies": { + "Sentry": "3.33.0", + "Serilog": "2.7.1" + } + }, + "Serilog": { + "type": "Transitive", + "resolved": "2.12.0", + "contentHash": "xaiJLIdu6rYMKfQMYUZgTy8YK7SMZjB4Yk50C/u//Z4OsvxkUfSPJy4nknfvwAC34yr13q7kcyh4grbwhSxyZg==" + }, + "Serilog.Enrichers.ClientInfo": { + "type": "Transitive", + "resolved": "1.3.0", + "contentHash": "mTc7PM+wC9Hr7LWSwqt5mmnlAr7RJs+eTb3PGPRhwdOackk95MkhUZognuxXEdlW19HAFNmEBTSBY5DfLwM8jQ==", + "dependencies": { + "Serilog": "2.4.0" + } + }, + "Serilog.Exceptions": { + "type": "Transitive", + "resolved": "8.4.0", + "contentHash": "nc/+hUw3lsdo0zCj0KMIybAu7perMx79vu72w0za9Nsi6mWyNkGXxYxakAjWB7nEmYL6zdmhEQRB4oJ2ALUeug==", + "dependencies": { + "Serilog": "2.8.0" + } + }, + "Serilog.Formatting.Compact": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "pNroKVjo+rDqlxNG5PXkRLpfSCuDOBY0ri6jp9PLe505ljqwhwZz8ospy2vWhQlFu5GkIesh3FcDs4n7sWZODA==", + "dependencies": { + "Serilog": "2.8.0" + } + }, + "Serilog.Sinks.Console": { + "type": "Transitive", + "resolved": "4.1.0", + "contentHash": "K6N5q+5fetjnJPvCmkWOpJ/V8IEIoMIB1s86OzBrbxwTyHxdx3pmz4H+8+O/Dc/ftUX12DM1aynx/dDowkwzqg==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "Serilog.Sinks.File": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "uwV5hdhWPwUH1szhO8PJpFiahqXmzPzJT/sOijH/kFgUx+cyoDTMM8MHD0adw9+Iem6itoibbUXHYslzXsLEAg==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "Serilog.Sinks.PeriodicBatching": { + "type": "Transitive", + "resolved": "3.1.0", + "contentHash": "NDWR7m3PalVlGEq3rzoktrXikjFMLmpwF0HI4sowo8YDdU+gqPlTHlDQiOGxHfB0sTfjPA9JjA7ctKG9zqjGkw==", + "dependencies": { + "Serilog": "2.0.0" + } + }, + "Serilog.Sinks.Seq": { + "type": "Transitive", + "resolved": "5.2.2", + "contentHash": "1Csmo5ua7NKUe0yXUx+zsRefjAniPWcXFhUXxXG8pwo0iMiw2gjn9SOkgYnnxbgWqmlGv236w0N/dHc2v5XwMg==", + "dependencies": { + "Serilog": "2.12.0", + "Serilog.Formatting.Compact": "1.1.0", + "Serilog.Sinks.File": "5.0.0", + "Serilog.Sinks.PeriodicBatching": "3.1.0" + } + }, + "SerilogTimings": { + "type": "Transitive", + "resolved": "3.0.1", + "contentHash": "Zs28eTgszAMwpIrbBnWHBI50yuxL50p/dmAUWmy75+axdZYK/Sjm5/5m1N/CisR8acJUhTVcjPZrsB1P5iv0Uw==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "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.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, + "System.Collections.Immutable": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "FXkLXiK0sVVewcso0imKQoOxjoPAj42R8HtjjbSjVPAzwDfzoyoznWxgA3c38LDbN9SJux1xXoXYAhz98j7r2g==", + "dependencies": { + "System.Memory": "4.5.4" + } + }, + "System.DoubleNumerics": { + "type": "Transitive", + "resolved": "3.1.3", + "contentHash": "KRKEM/L3KBodjA9VOg3EifFVWUY6EOqaMB05UvPEDm7Zeby/kZW+4kdWUEPzW6xtkwf46p661L9NrbeeQhtLzw==", + "dependencies": { + "NETStandard.Library": "1.6.1" + } + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Numerics.Vectors": "4.5.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + }, + "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.5.0", + "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" + }, + "System.Reactive": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "System.Reflection.Metadata": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "5NecZgXktdGg34rh1OenY1rFNDCI8xSjFr+Z4OU4cU06AQHUdRnIIEeWENu3Wl4YowbzkymAIMvi3WyK9U53pQ==", + "dependencies": { + "System.Collections.Immutable": "5.0.0" + } + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "ZD9TMpsmYJLrxbbmdvhwt9YEgG5WntEnZ/d1eH8JBX9LBp+Ju8BSBhUGbZMNVHHomWo2KVImJhTDl2hIgw/6MA==" + }, + "System.Runtime.InteropServices.RuntimeInformation": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==" + }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "5.0.1", + "contentHash": "KmJ+CJXizDofbq6mpqDoRRLcxgOd2z9X3XoFNULSbvbqVRZkFX3istvr+MUjL6Zw1RT+RNdoI4GYidIINtgvqQ==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Memory": "4.5.4" + } + }, + "System.Text.Json": { + "type": "Transitive", + "resolved": "5.0.2", + "contentHash": "I47dVIGiV6SfAyppphxqupertT/5oZkYLDCX6vC3HpOI4ZLjyoKAreUoem2ie6G0RbRuFrlqz/PcTQjfb2DOfQ==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "5.0.0", + "System.Buffers": "4.5.1", + "System.Memory": "4.5.4", + "System.Numerics.Vectors": "4.5.0", + "System.Runtime.CompilerServices.Unsafe": "5.0.0", + "System.Text.Encodings.Web": "5.0.1", + "System.Threading.Tasks.Extensions": "4.5.4", + "System.ValueTuple": "4.5.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" + } + }, + "System.ValueTuple": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "okurQJO6NRE/apDIP23ajJ0hpiNmJ+f0BwOlB/cSqTLQlw5upkf+5+96+iG2Jw40G1fCVCyPz/FhIABUjMR+RQ==" + }, + "speckle.autofac": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "[7.0.0, )" + } + }, + "speckle.converters.common": { + "type": "Project", + "dependencies": { + "Speckle.Autofac": "[2.0.999-local, )", + "Speckle.Objects": "[3.0.1-alpha.14, )" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "CentralTransitive", + "requested": "[7.0.0, )", + "resolved": "7.0.0", + "contentHash": "kmn78+LPVMOWeITUjIlfxUPDsI0R6G0RkeAMBmQxAJ7vBJn4q2dTva7pWi65ceN5vPGjJ9q/Uae2WKgvfktJAw==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Memory": "4.5.5" + } + }, + "Speckle.Core": { + "type": "CentralTransitive", + "requested": "[3.0.1-alpha.14, )", + "resolved": "3.0.1-alpha.14", + "contentHash": "RzQPVIGFFkKvG56YLr8ACtiwdWJE6IJ9vCQ4qHa0PIsUEpfzAIAi59jnzqtByOFC0FiFrFPow9bkfzylaZorAA==", + "dependencies": { + "GraphQL.Client": "6.0.0", + "Microsoft.CSharp": "4.7.0", + "Microsoft.Data.Sqlite": "7.0.5", + "Polly": "7.2.3", + "Polly.Contrib.WaitAndRetry": "1.1.1", + "Polly.Extensions.Http": "3.0.0", + "Sentry": "3.33.0", + "Sentry.Serilog": "3.33.0", + "Serilog": "2.12.0", + "Serilog.Enrichers.ClientInfo": "1.3.0", + "Serilog.Exceptions": "8.4.0", + "Serilog.Sinks.Console": "4.1.0", + "Serilog.Sinks.Seq": "5.2.2", + "SerilogTimings": "3.0.1", + "Speckle.Newtonsoft.Json": "13.0.2", + "System.DoubleNumerics": "3.1.3" + } + }, + "Speckle.Objects": { + "type": "CentralTransitive", + "requested": "[3.0.1-alpha.14, )", + "resolved": "3.0.1-alpha.14", + "contentHash": "z38LGryMvh7iU1uBW+4uo5DwsB3CwRgLt2uFexWFx3mPSid+A0l5XcJzOgLwgFhNl6B42Ryz4ezBsddTp1Uc/g==", + "dependencies": { + "Speckle.Core": "3.0.1-alpha.14" + } + } + } + } +} \ No newline at end of file diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Extensions/CategoryExtensions.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Extensions/CategoryExtensions.cs new file mode 100644 index 0000000000..9083375a2e --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Extensions/CategoryExtensions.cs @@ -0,0 +1,42 @@ +using Autodesk.Revit.DB; + +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 NotSupportedException($"Built-in category {builtInCategory} is not supported."); + } + + return cat; + } + + public static BuiltInCategory GetBuiltInCategory(this Category category) + { + return (BuiltInCategory)category.Id.IntegerValue; + } + + 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/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Extensions/DefinitionExtensions.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Extensions/DefinitionExtensions.cs new file mode 100644 index 0000000000..77aff9d11b --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Extensions/DefinitionExtensions.cs @@ -0,0 +1,16 @@ +using Autodesk.Revit.DB; + +namespace Speckle.Converters.RevitShared.Extensions; + +public static class DefinitionExtensions +{ + // POC: can we just interface these specialisations out and thereby avoid this kind of BS :D + public static string GetUnitTypeString(this Definition definition) + { +#if REVIT2020 || REVIT2021 + return definition.UnitType.ToString(); +#else + return definition.GetDataType().TypeId; +#endif + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Extensions/ElementExtensions.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Extensions/ElementExtensions.cs new file mode 100644 index 0000000000..468a98fd0f --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Extensions/ElementExtensions.cs @@ -0,0 +1,35 @@ +using Autodesk.Revit.DB; + +namespace Speckle.Converters.RevitShared.Extensions; + +public static class ElementExtensions +{ + // POC: should this be an injected service? + public static IList GetHostedElementIds(this Element host) + { + IList ids; + if (host is HostObject hostObject) + { + ids = hostObject.FindInserts(true, false, false, false); + } + else + { + var typeFilter = new ElementIsElementTypeFilter(true); + var categoryFilter = new ElementMulticategoryFilter( + new List() + { + BuiltInCategory.OST_CLines, + BuiltInCategory.OST_SketchLines, + BuiltInCategory.OST_WeakDims + }, + true + ); + ids = host.GetDependentElements(new LogicalAndFilter(typeFilter, categoryFilter)); + } + + // dont include host elementId + ids.Remove(host.Id); + + return ids; + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Extensions/ForgeTypeIdExtensions.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Extensions/ForgeTypeIdExtensions.cs new file mode 100644 index 0000000000..790e92a07a --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Extensions/ForgeTypeIdExtensions.cs @@ -0,0 +1,26 @@ +using Autodesk.Revit.DB; + +namespace Speckle.Converters.RevitShared.Extensions; + +public static class ForgeTypeIdExtensions +{ + public static string? GetSymbol(this ForgeTypeId forgeTypeId) + { + if (!FormatOptions.CanHaveSymbol(forgeTypeId)) + { + return null; + } + var validSymbols = FormatOptions.GetValidSymbols(forgeTypeId); + var typeId = validSymbols.Where(x => !x.Empty()); + foreach (DB.ForgeTypeId symbolId in typeId) + { + return LabelUtils.GetLabelForSymbol(symbolId); + } + return null; + } + + public static string ToUniqueString(this ForgeTypeId forgeTypeId) + { + return forgeTypeId.TypeId; + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Extensions/ParameterExtensions.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Extensions/ParameterExtensions.cs new file mode 100644 index 0000000000..c8ac6f92e8 --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Extensions/ParameterExtensions.cs @@ -0,0 +1,36 @@ +using Autodesk.Revit.DB; + +namespace Speckle.Converters.RevitShared.Extensions; + +public static class ParameterExtensions +{ + /// + /// Shared parameters use a GUID to be uniquely identified + /// Other parameters use a BuiltInParameter enum + /// + /// + /// + public static string GetInternalName(this DB.Parameter rp) + { + if (rp.IsShared) + { + return rp.GUID.ToString(); + } + else + { + var def = (InternalDefinition)rp.Definition; + if (def.BuiltInParameter == BuiltInParameter.INVALID) + { + return def.Name; + } + + return def.BuiltInParameter.ToString(); + } + } + + public static BuiltInParameter? GetBuiltInParameter(this Parameter rp) + { + var def = rp.Definition as InternalDefinition; + return def?.BuiltInParameter; + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/GlobalUsings.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/GlobalUsings.cs new file mode 100644 index 0000000000..5a7a8f20bd --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/GlobalUsings.cs @@ -0,0 +1,5 @@ +global using DB = Autodesk.Revit.DB; +global using DBA = Autodesk.Revit.DB.Architecture; +global using SOG = Objects.Geometry; +global using SOBR = Objects.BuiltElements.Revit; +global using SOBE = Objects.BuiltElements; diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Helpers/DisplayValueExtractor.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Helpers/DisplayValueExtractor.cs new file mode 100644 index 0000000000..7980fa5b02 --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Helpers/DisplayValueExtractor.cs @@ -0,0 +1,287 @@ +using Microsoft.Extensions.Logging; +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.RevitShared.Helpers; + +// POC: needs breaking down https://spockle.atlassian.net/browse/CNX-9354 +public sealed class DisplayValueExtractor +{ + private readonly ITypedConverter>, List> _meshByMaterialConverter; + private readonly ILogger _logger; + + public DisplayValueExtractor( + ITypedConverter>, List> meshByMaterialConverter, + ILogger logger + ) + { + _meshByMaterialConverter = meshByMaterialConverter; + _logger = logger; + } + + public List GetDisplayValue( + DB.Element element, + DB.Options? options = null, + // POC: should this be part of the context? + DB.Transform? transform = null + ) + { + var displayMeshes = new List(); + + // test if the element is a group first + if (element is DB.Group g) + { + foreach (var id in g.GetMemberIds()) + { + var groupMeshes = GetDisplayValue(element.Document.GetElement(id), options); + displayMeshes.AddRange(groupMeshes); + } + return displayMeshes; + } + + var (solids, meshes) = GetSolidsAndMeshesFromElement(element, options, transform); + + var meshesByMaterial = GetMeshesByMaterial(meshes, solids); + + return _meshByMaterialConverter.Convert(meshesByMaterial); + } + + private static Dictionary> GetMeshesByMaterial( + List meshes, + List solids + ) + { + var meshesByMaterial = new Dictionary>(); + foreach (var mesh in meshes) + { + var materialId = mesh.MaterialElementId; + if (!meshesByMaterial.TryGetValue(materialId, out List? value)) + { + value = new List(); + meshesByMaterial[materialId] = value; + } + + value.Add(mesh); + } + + foreach (var solid in solids) + { + foreach (DB.Face face in solid.Faces) + { + var materialId = face.MaterialElementId; + if (!meshesByMaterial.TryGetValue(materialId, out List? value)) + { + value = new List(); + meshesByMaterial[materialId] = value; + } + + value.Add(face.Triangulate()); + } + } + + return meshesByMaterial; + } + + private (List, List) GetSolidsAndMeshesFromElement( + DB.Element element, + DB.Options? options, + DB.Transform? transform = null + ) + { + //options = ViewSpecificOptions ?? options ?? new Options() { DetailLevel = DetailLevelSetting }; + options ??= new DB.Options { DetailLevel = DB.ViewDetailLevel.Fine }; + + DB.GeometryElement geom; + try + { + geom = element.get_Geometry(options); + } + // POC: should we be trying to continue? + catch (Autodesk.Revit.Exceptions.ArgumentException) + { + options.ComputeReferences = false; + geom = element.get_Geometry(options); + } + + var solids = new List(); + var meshes = new List(); + + if (geom != null) + { + // retrieves all meshes and solids from a geometry element + SortGeometry(element, solids, meshes, geom, transform?.Inverse); + } + + return (solids, meshes); + } + + /// + /// According to the remarks on the GeometryInstance class in the RevitAPIDocs, + /// https://www.revitapidocs.com/2024/fe25b14f-5866-ca0f-a660-c157484c3a56.htm, + /// a family instance geometryElement should have a top-level geometry instance when the symbol + /// does not have modified geometry (the docs say that modified geometry will not have a geom instance, + /// however in my experience, all family instances have a top-level geom instance, but if the family instance + /// is modified, then the geom instance won't contain any geometry.) + /// + /// This remark also leads me to think that a family instance will not have top-level solids and geom instances. + /// We are logging cases where this is not true. + /// + /// + /// + /// + /// + /// + private void SortGeometry( + DB.Element element, + List solids, + List meshes, + DB.GeometryElement geom, + DB.Transform? inverseTransform = null + ) + { + var topLevelSolidsCount = 0; + var topLevelMeshesCount = 0; + var topLevelGeomElementCount = 0; + var topLevelGeomInstanceCount = 0; + bool hasSymbolGeometry = false; + + foreach (DB.GeometryObject geomObj in geom) + { + // POC: switch could possibly become factory and IIndex<,> pattern and move conversions to + // separate IComeConversionInterfaces + switch (geomObj) + { + case DB.Solid solid: + // skip invalid solid + if ( + solid.Faces.Size == 0 + || Math.Abs(solid.SurfaceArea) == 0 + || IsSkippableGraphicStyle(solid.GraphicsStyleId, element.Document) + ) + { + continue; + } + + if (inverseTransform != null) + { + topLevelSolidsCount++; + solid = DB.SolidUtils.CreateTransformed(solid, inverseTransform); + } + + solids.Add(solid); + break; + case DB.Mesh mesh: + if (IsSkippableGraphicStyle(mesh.GraphicsStyleId, element.Document)) + { + continue; + } + + if (inverseTransform != null) + { + topLevelMeshesCount++; + mesh = mesh.get_Transformed(inverseTransform); + } + + meshes.Add(mesh); + 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 + if (inverseTransform != null) + { + topLevelGeomInstanceCount++; + SortGeometry(element, solids, meshes, instance.GetSymbolGeometry()); + if (meshes.Count > 0 || solids.Count > 0) + { + hasSymbolGeometry = true; + } + } + else + { + SortGeometry(element, solids, meshes, instance.GetInstanceGeometry()); + } + break; + case DB.GeometryElement geometryElement: + if (inverseTransform != null) + { + topLevelGeomElementCount++; + } + SortGeometry(element, solids, meshes, geometryElement); + break; + } + } + + if (inverseTransform != null) + { + LogInstanceMeshRetrievalWarnings( + element, + topLevelSolidsCount, + topLevelMeshesCount, + topLevelGeomElementCount, + hasSymbolGeometry + ); + } + } + + // POC: should be hoovered up with the new reporting, logging, exception philosophy + private void LogInstanceMeshRetrievalWarnings( + DB.Element element, + int topLevelSolidsCount, + int topLevelMeshesCount, + int topLevelGeomElementCount, + bool hasSymbolGeom + ) + { + if (hasSymbolGeom) + { + if (topLevelSolidsCount > 0) + { + _logger.LogWarning( + $"Element of type {element.GetType()} with uniqueId {element.UniqueId} has valid symbol geometry and {topLevelSolidsCount} top level solids. See comment on method SortInstanceGeometry for link to RevitAPI docs that leads us to believe this shouldn't happen" + ); + } + if (topLevelMeshesCount > 0) + { + _logger.LogWarning( + $"Element of type {element.GetType()} with uniqueId {element.UniqueId} has valid symbol geometry and {topLevelMeshesCount} top level meshes. See comment on method SortInstanceGeometry for link to RevitAPI docs that leads us to believe this shouldn't happen" + ); + } + if (topLevelGeomElementCount > 0) + { + _logger.LogWarning( + $"Element of type {element.GetType()} with uniqueId {element.UniqueId} has valid symbol geometry and {topLevelGeomElementCount} top level geometry elements. See comment on method SortInstanceGeometry for link to RevitAPI docs that leads us to believe this shouldn't happen" + ); + } + } + } + + /// + /// We're caching a dictionary of graphic styles and their ids as it can be a costly operation doing Document.GetElement(solid.GraphicsStyleId) for every solid + /// + private readonly Dictionary _graphicStyleCache = new(); + + /// + /// Exclude light source cones and potentially other geometries by their graphic style + /// + /// + /// + /// + private bool IsSkippableGraphicStyle(DB.ElementId id, DB.Document doc) + { + var key = id.ToString(); + if (_graphicStyleCache.TryGetValue(key, out var graphicStyle)) + { + graphicStyle = (DB.GraphicsStyle)doc.GetElement(id); + _graphicStyleCache.Add(key, graphicStyle); + } + + if ( + graphicStyle != null + && graphicStyle.GraphicsStyleCategory.Id.IntegerValue == (int)DB.BuiltInCategory.OST_LightingFixtureSource + ) + { + return true; + } + + return false; + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Helpers/IParameterValueExtractor.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Helpers/IParameterValueExtractor.cs new file mode 100644 index 0000000000..7366b0d055 --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Helpers/IParameterValueExtractor.cs @@ -0,0 +1,34 @@ +using System.Diagnostics.CodeAnalysis; +using Speckle.Revit.Interfaces; + +namespace Speckle.Converters.RevitShared.Helpers; + +//not auto because of NotNullWhen +public interface IParameterValueExtractor +{ + object? GetValue(IRevitParameter parameter); + double GetValueAsDouble(IRevitElement element, RevitBuiltInParameter builtInParameter); + bool TryGetValueAsDouble( + IRevitElement element, + RevitBuiltInParameter builtInParameter, + [NotNullWhen(true)] out double? value + ); + int GetValueAsInt(IRevitElement element, RevitBuiltInParameter builtInParameter); + bool? GetValueAsBool(IRevitElement element, RevitBuiltInParameter builtInParameter); + string? GetValueAsString(IRevitElement element, RevitBuiltInParameter builtInParameter); + IRevitElementId GetValueAsElementId(IRevitElement element, RevitBuiltInParameter builtInParameter); + bool TryGetValueAsElementId( + IRevitElement element, + RevitBuiltInParameter builtInParameter, + out IRevitElementId? elementId + ); + IRevitElementId? GetValueAsElementId(IRevitParameter parameter); + IRevitLevel GetValueAsRevitLevel(IRevitElement element, RevitBuiltInParameter builtInParameter); + bool TryGetValueAsRevitLevel( + IRevitElement element, + RevitBuiltInParameter builtInParameter, + [NotNullWhen(true)] out IRevitLevel? revitLevel + ); + Dictionary GetAllRemainingParams(IRevitElement revitElement); + void RemoveUniqueId(string uniqueId); +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Helpers/IRevitConversionContextStack.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Helpers/IRevitConversionContextStack.cs new file mode 100644 index 0000000000..1cf5d4b80c --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Helpers/IRevitConversionContextStack.cs @@ -0,0 +1,13 @@ +using Autodesk.Revit.DB; +using Speckle.Converters.Common; + +namespace Speckle.Converters.RevitShared.Helpers; + +[System.Diagnostics.CodeAnalysis.SuppressMessage( + "Naming", + "CA1711:Identifiers should not have incorrect suffix", + Justification = "See base class justification" +)] +// POC: so this should *probably* be Document and NOT UI.UIDocument, the former is Conversion centric +// and the latter is more for connector +public interface IRevitConversionContextStack : IConversionContextStack { } diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Helpers/ISlopeArrowExtractor.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Helpers/ISlopeArrowExtractor.cs new file mode 100644 index 0000000000..750f4d1073 --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Helpers/ISlopeArrowExtractor.cs @@ -0,0 +1,10 @@ +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/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Helpers/ParameterObjectAssigner.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Helpers/ParameterObjectAssigner.cs new file mode 100644 index 0000000000..86735165b1 --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Helpers/ParameterObjectAssigner.cs @@ -0,0 +1,80 @@ +using Autodesk.Revit.DB; +using Microsoft.Extensions.Logging; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; + +namespace Speckle.Converters.RevitShared.Helpers; + +// POC: rationalise whether this and ParameterObjectBuilder are sufficiently different?? +// did it go away? +public sealed class ParameterObjectAssigner +{ + private readonly ITypedConverter _paramConverter; + private readonly ParameterValueExtractor _parameterValueExtractor; + private readonly ILogger _logger; + + public ParameterObjectAssigner( + ITypedConverter paramConverter, + ParameterValueExtractor parameterValueExtractor, + ILogger logger + ) + { + _paramConverter = paramConverter; + _parameterValueExtractor = parameterValueExtractor; + _logger = logger; + } + + public void AssignParametersToBase(Element target, Base @base) + { + Dictionary instanceParameters = _parameterValueExtractor.GetAllRemainingParams(target); + ElementId elementId = target.GetTypeId(); + + Base paramBase = new(); + AssignSpeckleParamToBaseObject(instanceParameters, paramBase); + + // POC: Some elements can have an invalid element type ID, I don't think we want to continue here. + if (elementId != ElementId.InvalidElementId && target is not Level) //ignore type props of levels..! + { + var elementType = target.Document.GetElement(elementId); + // I don't think we should be adding the type parameters to the object like this + Dictionary typeParameters = _parameterValueExtractor.GetAllRemainingParams(elementType); + AssignSpeckleParamToBaseObject(typeParameters, paramBase, true); + } + + if (paramBase.GetMembers(DynamicBaseMemberType.Dynamic).Count > 0) + { + @base["parameters"] = paramBase; + } + } + + private void AssignSpeckleParamToBaseObject( + IEnumerable> parameters, + Base paramBase, + bool isTypeParameter = false + ) + { + //sort by key + foreach (var kv in parameters.OrderBy(x => x.Key)) + { + try + { + SOBR.Parameter speckleParam = _paramConverter.Convert(kv.Value); + speckleParam.isTypeParameter = isTypeParameter; + paramBase[kv.Key] = speckleParam; + } + // POC swallow and continue seems bad? + // maybe hoover these into one exception or into our reporting strategy + catch (InvalidPropNameException) + { + //ignore + } + // POC swallow and continue seems bad? + // maybe hoover these into one exception or into our reporting strategy + catch (SpeckleConversionException ex) + { + _logger.LogWarning(ex, $"Error thrown when trying to set property named {kv.Key}"); + } + } + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Helpers/ParameterObjectBuilder.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Helpers/ParameterObjectBuilder.cs new file mode 100644 index 0000000000..71cf930224 --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Helpers/ParameterObjectBuilder.cs @@ -0,0 +1,50 @@ +using System.Collections.Generic; +using System.Linq; +using Autodesk.Revit.DB; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Logging; +using Speckle.Core.Models; + +namespace Speckle.Converters.RevitShared.Helpers; + +public sealed class ParameterObjectAssigner +{ + private readonly IRawConversion _paramConverter; + private readonly ParameterValueExtractor _parameterValueExtractor; + + public ParameterObjectAssigner( + IRawConversion paramConverter, + ParameterValueExtractor parameterValueExtractor + ) + { + _paramConverter = paramConverter; + _parameterValueExtractor = parameterValueExtractor; + } + + public void AssignParametersToBase(Element target, Base @base) + { + Dictionary allParams = _parameterValueExtractor.GetAllRemainingParams(target); + Base paramBase = new(); + //sort by key + foreach (var kv in allParams.OrderBy(x => x.Key)) + { + try + { + paramBase[kv.Key] = _paramConverter.RawConvert(kv.Value); + } + catch (InvalidPropNameException) + { + //ignore + } + catch (SpeckleException ex) + { + SpeckleLog.Logger.Warning(ex, "Error thrown when trying to set property named {propName}", kv.Key); + } + } + + if (paramBase.GetMembers(DynamicBaseMemberType.Dynamic).Count > 0) + { + @base["parameters"] = paramBase; + } + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Helpers/ParameterValueExtractor.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Helpers/ParameterValueExtractor.cs new file mode 100644 index 0000000000..cdc8e69243 --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Helpers/ParameterValueExtractor.cs @@ -0,0 +1,246 @@ +using Autodesk.Revit.DB; +using Speckle.Converters.Common; +using Speckle.Converters.RevitShared.Extensions; +using Speckle.Converters.RevitShared.Services; + +namespace Speckle.Converters.RevitShared.Helpers; + +// POC: needs reviewing, it may be fine, not sure how open/closed it is +// really if we have to edit a switch statement... +// maybe also better as an extension method, but maybe is fine? +// POC: there are a lot of public methods here. Maybe consider consolodating +public class ParameterValueExtractor +{ + private readonly ScalingServiceToSpeckle _scalingService; + private readonly Dictionary> _uniqueIdToUsedParameterSetMap = new(); + + public ParameterValueExtractor(ScalingServiceToSpeckle scalingService) + { + _scalingService = scalingService; + } + + public object? GetValue(Parameter parameter) + { + if (!parameter.HasValue) + { + return null; + } + + return parameter.StorageType switch + { + StorageType.Double => GetValueAsDouble(parameter), + StorageType.Integer => GetValueAsInt(parameter), + StorageType.String => GetValueAsString(parameter), + StorageType.ElementId => GetValueAsElementId(parameter)?.ToString(), + StorageType.None + or _ + => throw new SpeckleConversionException($"Unsupported parameter storage type {parameter.StorageType}") + }; + } + + public double GetValueAsDouble(Element element, BuiltInParameter builtInParameter) + { + if (!TryGetValueAsDouble(element, builtInParameter, out double? value)) + { + throw new SpeckleConversionException($"Failed to get {builtInParameter} as double."); + } + + return value!.Value; // If TryGet returns true, we succeeded in obtaining the value, and it will not be null. + } + + public bool TryGetValueAsDouble(Element element, BuiltInParameter builtInParameter, out double? value) + { + var number = GetValueGeneric( + element, + builtInParameter, + StorageType.Double, + (parameter) => _scalingService.Scale(parameter.AsDouble(), parameter.GetUnitTypeId()) + ); + if (number.HasValue) + { + value = number.Value; + return true; + } + + value = default; + return false; + } + + private double? GetValueAsDouble(Parameter parameter) + { + return GetValueGeneric( + parameter, + StorageType.Double, + (parameter) => _scalingService.Scale(parameter.AsDouble(), parameter.GetUnitTypeId()) + ); + } + + public int GetValueAsInt(Element element, BuiltInParameter builtInParameter) + { + return GetValueGeneric(element, builtInParameter, StorageType.Integer, (parameter) => parameter.AsInteger()) + ?? throw new SpeckleConversionException( + $"Expected int but got null for property {builtInParameter} on element of type {element.GetType()}" + ); + } + + private int? GetValueAsInt(Parameter parameter) + { + return GetValueGeneric(parameter, StorageType.Integer, (parameter) => parameter.AsInteger()); + } + + public bool? GetValueAsBool(Element element, BuiltInParameter builtInParameter) + { + var intVal = GetValueGeneric( + element, + builtInParameter, + StorageType.Integer, + (parameter) => parameter.AsInteger() + ); + + return intVal.HasValue ? Convert.ToBoolean(intVal.Value) : null; + } + + public string? GetValueAsString(Element element, BuiltInParameter builtInParameter) + { + return GetValueGeneric(element, builtInParameter, StorageType.String, (parameter) => parameter.AsString()); + } + + private string? GetValueAsString(Parameter parameter) + { + return GetValueGeneric(parameter, StorageType.String, (parameter) => parameter.AsString()); + } + + public ElementId GetValueAsElementId(Element element, BuiltInParameter builtInParameter) + { + if (TryGetValueAsElementId(element, builtInParameter, out var elementId)) + { + return elementId!; + } + throw new SpeckleConversionException( + $"Failed to get {builtInParameter} on element of type {element.GetType()} as ElementId" + ); + } + + public bool TryGetValueAsElementId(Element element, BuiltInParameter builtInParameter, out ElementId? elementId) + { + if ( + GetValueGeneric(element, builtInParameter, StorageType.ElementId, (parameter) => parameter.AsElementId()) + is ElementId elementIdNotNull + ) + { + elementId = elementIdNotNull; + return true; + } + + elementId = null; + return false; + } + + public ElementId? GetValueAsElementId(Parameter parameter) + { + return GetValueGeneric(parameter, StorageType.ElementId, (parameter) => parameter.AsElementId()); + } + + public bool TryGetValueAsDocumentObject(Element element, BuiltInParameter builtInParameter, out T? value) + { + if (!TryGetValueAsElementId(element, builtInParameter, out var elementId)) + { + value = default; + return false; + } + + Element paramElement = element.Document.GetElement(elementId); + if (paramElement is not T typedElement) + { + value = default; + return false; + } + + value = typedElement; + return true; + } + + public T GetValueAsDocumentObject(Element element, BuiltInParameter builtInParameter) + where T : class + { + if (!TryGetValueAsDocumentObject(element, builtInParameter, out var value)) + { + throw new SpeckleConversionException($"Failed to get {builtInParameter} as an element of type {typeof(T)}"); + } + + return value!; // If TryGet returns true, we succeeded in obtaining the value, and it will not be null. + } + + private TResult? GetValueGeneric( + Element element, + BuiltInParameter builtInParameter, + StorageType expectedStorageType, + Func getParamValue + ) + { + if (!_uniqueIdToUsedParameterSetMap.TryGetValue(element.UniqueId, out HashSet usedParameters)) + { + usedParameters = new(); + _uniqueIdToUsedParameterSetMap[element.UniqueId] = usedParameters; + } + usedParameters.Add(builtInParameter); + var parameter = element.get_Parameter(builtInParameter); + return GetValueGeneric(parameter, expectedStorageType, getParamValue); + } + + private TResult? GetValueGeneric( + Parameter parameter, + StorageType expectedStorageType, + Func getParamValue + ) + { + if (parameter == null || !parameter.HasValue) + { + return default; + } + + if (parameter.StorageType != expectedStorageType) + { + throw new SpeckleConversionException( + $"Expected parameter of storage type {expectedStorageType} but got parameter of storage type {parameter.StorageType}" + ); + } + + return getParamValue(parameter); + } + + public Dictionary GetAllRemainingParams(DB.Element revitElement) + { + var allParams = new Dictionary(); + AddElementParamsToDict(revitElement, allParams); + + return allParams; + } + + private void AddElementParamsToDict(DB.Element element, Dictionary paramDict) + { + _uniqueIdToUsedParameterSetMap.TryGetValue(element.UniqueId, out HashSet? usedParameters); + + using var parameters = element.Parameters; + foreach (DB.Parameter param in parameters) + { + var internalName = param.GetInternalName(); + if (paramDict.ContainsKey(internalName)) + { + continue; + } + + if (param.GetBuiltInParameter() is BuiltInParameter bip && (usedParameters?.Contains(bip) ?? false)) + { + continue; + } + + paramDict[internalName] = param; + } + } + + public void RemoveUniqueId(string uniqueId) + { + _uniqueIdToUsedParameterSetMap.Remove(uniqueId); + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Helpers/RevitCategories.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Helpers/RevitCategories.cs new file mode 100644 index 0000000000..13e898c47d --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Helpers/RevitCategories.cs @@ -0,0 +1,150 @@ +using Autodesk.Revit.DB; + +namespace Speckle.Converters.RevitShared.Helpers; + +/// +/// Contains predefined categories of revit objects as well as the types that make up the elements +/// in that category +/// +/// +// POC: is quite a messy looking thing, need some review +// might be legit or maybe a bette/cleaner pattern rather than the dictionary of pain, but maybe not +// some whitespace between each at the least! +public static class RevitCategories +{ + public static Dictionary All { get; } + + static RevitCategories() + { + All = new(StringComparer.OrdinalIgnoreCase) + { + { nameof(CableTray), CableTray }, + { nameof(Ceiling), Ceiling }, + { nameof(Column), Column }, + { nameof(Conduit), Conduit }, + { nameof(Door), Door }, + { nameof(Duct), Duct }, + { nameof(FamilyInstance), FamilyInstance }, + { nameof(Floor), Floor }, + { nameof(Furniture), Furniture }, + { nameof(Pipe), Pipe }, + { nameof(PlumbingFixture), PlumbingFixture }, + { nameof(Roof), Roof }, + { nameof(Railing), Railing }, + { nameof(StructuralFraming), StructuralFraming }, + { nameof(Wall), Wall }, + { nameof(Window), Window }, + { nameof(Wire), Wire }, + { nameof(Undefined), Undefined }, + }; + } + + public static RevitCategoryInfo CableTray { get; } = + new( + nameof(CableTray), + typeof(DB.Electrical.CableTray), + typeof(DB.Electrical.CableTrayType), + new List() + ); + public static RevitCategoryInfo Ceiling { get; } = + new(nameof(Ceiling), typeof(DB.Ceiling), typeof(CeilingType), new List()); + public static RevitCategoryInfo Column { get; } = + new( + nameof(Column), + typeof(FamilyInstance), + typeof(FamilySymbol), + new List { BuiltInCategory.OST_Columns, BuiltInCategory.OST_StructuralColumns } + ); + public static RevitCategoryInfo Conduit { get; } = + new(nameof(Conduit), typeof(DB.Electrical.Conduit), typeof(DB.Electrical.ConduitType), new List()); + public static RevitCategoryInfo Door { get; } = + new( + nameof(Door), + typeof(DB.FamilyInstance), + typeof(DB.FamilySymbol), + new List { BuiltInCategory.OST_Doors } + ); + public static RevitCategoryInfo Duct { get; } = + new( + nameof(Duct), + typeof(DB.Mechanical.Duct), + typeof(DB.MEPCurveType), + new List { BuiltInCategory.OST_DuctCurves, BuiltInCategory.OST_FlexDuctCurves } + ); + public static RevitCategoryInfo FamilyInstance { get; } = + new(nameof(FamilyInstance), typeof(DB.FamilyInstance), typeof(DB.FamilySymbol), new List()); + public static RevitCategoryInfo Floor { get; } = + new( + nameof(Floor), + typeof(DB.Floor), + typeof(DB.FloorType), + new List { BuiltInCategory.OST_Floors } + ); + public static RevitCategoryInfo Furniture { get; } = + new( + nameof(Furniture), + typeof(DB.FamilyInstance), + typeof(DB.FamilySymbol), + new List { BuiltInCategory.OST_Furniture } + ); + + //public static RevitCategoryInfo Material { get; } = new( + // nameof(Material), + // typeof(DB.Material), + // null, + // new List + // { + // BuiltInCategory.OST_Materials, + // BuiltInCategory.OST_PipeMaterials, + // BuiltInCategory.OST_WireMaterials + // }); + public static RevitCategoryInfo Pipe { get; } = + new( + nameof(Pipe), + typeof(DB.Plumbing.Pipe), + typeof(DB.MEPCurveType), + new List { BuiltInCategory.OST_PipeCurves, BuiltInCategory.OST_FlexPipeCurves } + ); + public static RevitCategoryInfo PlumbingFixture { get; } = + new( + nameof(PlumbingFixture), + typeof(DB.FamilyInstance), + typeof(DB.FamilySymbol), + new List { BuiltInCategory.OST_PlumbingFixtures } + ); + public static RevitCategoryInfo Roof { get; } = + new( + nameof(Roof), + typeof(DB.RoofBase), + typeof(DB.RoofType), + new List { BuiltInCategory.OST_Roofs, } + ); + public static RevitCategoryInfo Railing { get; } = + new( + nameof(Railing), + typeof(DB.Architecture.Railing), + typeof(DB.Architecture.RailingType), + new List() + ); + public static RevitCategoryInfo StructuralFraming { get; } = + new( + nameof(StructuralFraming), + typeof(DB.FamilyInstance), + typeof(DB.FamilySymbol), + new List { BuiltInCategory.OST_StructuralFraming }, + new List { "beam", "brace", "framing" } + ); + public static RevitCategoryInfo Wall { get; } = + new(nameof(Wall), typeof(DB.Wall), typeof(DB.WallType), new List { BuiltInCategory.OST_Walls }); + public static RevitCategoryInfo Window { get; } = + new( + nameof(Window), + typeof(DB.FamilyInstance), + typeof(DB.FamilySymbol), + new List { BuiltInCategory.OST_Windows } + ); + public static RevitCategoryInfo Wire { get; } = + new(nameof(Wire), typeof(DB.Electrical.Wire), typeof(DB.Electrical.WireType), new List()); + public static RevitCategoryInfo Undefined { get; } = + new(nameof(Undefined), typeof(RevitCategoryInfo), typeof(RevitCategoryInfo), new List()); +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Helpers/RevitCategoryInfo.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Helpers/RevitCategoryInfo.cs new file mode 100644 index 0000000000..f883f7c9e8 --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Helpers/RevitCategoryInfo.cs @@ -0,0 +1,65 @@ +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) + { + return BuiltInCategories.Select(x => (int)x).Contains(category.Id.IntegerValue); + } + + 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/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Helpers/RevitContext.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Helpers/RevitContext.cs new file mode 100644 index 0000000000..59c3892a75 --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Helpers/RevitContext.cs @@ -0,0 +1,22 @@ +using Autodesk.Revit.UI; + +namespace Speckle.Converters.RevitShared.Helpers; + +public class RevitContext +{ + private UIApplication? _uiApplication; + + public UIApplication? UIApplication + { + get => _uiApplication; + set + { + if (_uiApplication != null) + { + throw new ArgumentException("UIApplication already set"); + } + + _uiApplication = value; + } + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Helpers/RevitConversionContextStack.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Helpers/RevitConversionContextStack.cs new file mode 100644 index 0000000000..ce43a88901 --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Helpers/RevitConversionContextStack.cs @@ -0,0 +1,28 @@ +using Autodesk.Revit.DB; +using Speckle.Converters.Common; + +namespace Speckle.Converters.RevitShared.Helpers; + +[System.Diagnostics.CodeAnalysis.SuppressMessage( + "Naming", + "CA1711:Identifiers should not have incorrect suffix", + Justification = "See base class justification" +)] +// POC: so this should *probably* be Document and NOT UI.UIDocument, the former is Conversion centric +// and the latter is more for connector +public class RevitConversionContextStack : ConversionContextStack, IRevitConversionContextStack +{ + public const double TOLERANCE = 0.0164042; // 5mm in ft + + public RevitConversionContextStack(RevitContext context, IHostToSpeckleUnitConverter unitConverter) + : base( + // POC: we probably should not get here without a valid document + // so should this perpetuate or do we assume this is valid? + // relting on the context.UIApplication?.ActiveUIDocument is not right + // this should be some IActiveDocument I suspect? + context.UIApplication?.ActiveUIDocument?.Document + ?? throw new SpeckleConversionException("Active UI document could not be determined"), + context.UIApplication.ActiveUIDocument.Document.GetUnits().GetFormatOptions(SpecTypeId.Length).GetUnitTypeId(), + unitConverter + ) { } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Helpers/SendSelection.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Helpers/SendSelection.cs new file mode 100644 index 0000000000..c6b4590a6d --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Helpers/SendSelection.cs @@ -0,0 +1,17 @@ +namespace Speckle.Converters.RevitShared.Helpers; + +// POC: why do we need this send selection? +// why does conversion need to know about selection in this way? +public class SendSelection +{ + private readonly HashSet _selectedItemIds; + + public SendSelection(IEnumerable selectedItemIds) + { + _selectedItemIds = new HashSet(selectedItemIds); + } + + public bool Contains(string elementId) => _selectedItemIds.Contains(elementId); + + public IReadOnlyCollection SelectedItems => _selectedItemIds.ToList().AsReadOnly(); +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Helpers/SlopeArrowExtractor.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Helpers/SlopeArrowExtractor.cs new file mode 100644 index 0000000000..9db6d14583 --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Helpers/SlopeArrowExtractor.cs @@ -0,0 +1,88 @@ +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/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Helpers/ToSpeckleConvertedObjectsCache.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Helpers/ToSpeckleConvertedObjectsCache.cs new file mode 100644 index 0000000000..c4c91d9fb8 --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Helpers/ToSpeckleConvertedObjectsCache.cs @@ -0,0 +1,25 @@ +using Speckle.Core.Models; + +namespace Speckle.Converters.RevitShared.Helpers; + +// POC: review the cache? should this be a common class? +// Does caching work this way everywhere, i.e, string key and base value? +public sealed class ToSpeckleConvertedObjectsCache +{ + private readonly Dictionary _uniqueIdToConvertedBaseDict = new(); + + public void AddConvertedBase(string revitUniqueId, Base b) + { + _uniqueIdToConvertedBaseDict.Add(revitUniqueId, b); + } + + public bool ContainsBaseConvertedFromId(string revitUniqueId) + { + return _uniqueIdToConvertedBaseDict.ContainsKey(revitUniqueId); + } + + public bool TryGetConvertedBase(string revitUniqueId, out Base? value) + { + return _uniqueIdToConvertedBaseDict.TryGetValue(revitUniqueId, out value); + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/IReferencePointConverter.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/IReferencePointConverter.cs new file mode 100644 index 0000000000..601ab31128 --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/IReferencePointConverter.cs @@ -0,0 +1,8 @@ +namespace Speckle.Converters.RevitShared; + +public interface IReferencePointConverter +{ + DB.XYZ ConvertToExternalCoordindates(DB.XYZ p, bool isPoint); + + DB.XYZ ToInternalCoordinates(DB.XYZ p, bool isPoint); +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/IRevitVersionConversionHelper.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/IRevitVersionConversionHelper.cs new file mode 100644 index 0000000000..18a593cbbd --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/IRevitVersionConversionHelper.cs @@ -0,0 +1,6 @@ +namespace Speckle.Converters.RevitShared; + +public interface IRevitVersionConversionHelper +{ + bool IsCurveClosed(DB.NurbSpline nurbsSpline); +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ReferencePointConverter.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ReferencePointConverter.cs new file mode 100644 index 0000000000..1cf1394bb0 --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ReferencePointConverter.cs @@ -0,0 +1,112 @@ +using System.Diagnostics.CodeAnalysis; +using Autodesk.Revit.DB; +using Speckle.Converters.RevitShared.Helpers; + +namespace Speckle.Converters.RevitShared; + +// POC: this could perhaps becomes some RevitDocumentService but also... +// This reference point feature needs review. We could do with knowing whether this feature is widely used. +// there's also some bogus disposal happening +// https://spockle.atlassian.net/browse/CNX-9357 +public class ReferencePointConverter : IReferencePointConverter +{ + // POC: probably not the best place for this + private const string REFPOINT_INTERNAL_ORIGIN = "Internal Origin (default)"; + private const string REFPOINT_PROJECT_BASE = "Project Base"; + private const string REFPOINT_SURVEY = "Survey"; + + private readonly RevitConversionSettings _revitSettings; + private readonly IRevitConversionContextStack _contextStack; + + private readonly Dictionary _docTransforms = new(); + + public ReferencePointConverter(IRevitConversionContextStack contextStack, RevitConversionSettings revitSettings) + { + _contextStack = contextStack; + _revitSettings = revitSettings; + } + + // POC: the original allowed for the document to be passed in + // if required, we would probably need to push the stack with a new document if the + // doc can change during the lifeycycle of the conversions. This may need some looking into + public DB.XYZ ConvertToExternalCoordindates(DB.XYZ p, bool isPoint) + { + var rpt = GetDocReferencePointTransform(_contextStack.Current.Document); + return (isPoint) ? rpt.Inverse.OfPoint(p) : rpt.Inverse.OfVector(p); + } + + /// + /// For importing in Revit, moves and rotates a point according to this document BasePoint + /// + /// + /// + public XYZ ToInternalCoordinates(XYZ p, bool isPoint) + { + var rpt = GetDocReferencePointTransform(_contextStack.Current.Document); + return (isPoint) ? rpt.OfPoint(p) : rpt.OfVector(p); + } + + // POC: this might be better in some RevitDocumentService + // we could probably return that instance instead of the Doc from the context, maybe... + public DB.Transform GetDocReferencePointTransform(DB.Document doc) + { + //linked files are always saved to disc and will have a path name + //if the current doc is unsaved it will not, but then it'll be the only one :) + var id = doc.PathName; + + if (!_docTransforms.TryGetValue(id, out DB.Transform? transform)) + { + // get from settings + var referencePointSetting = _revitSettings.TryGetSettingString("reference-point", out string value) + ? value + : string.Empty; + transform = GetReferencePointTransform(referencePointSetting); + _docTransforms[id] = transform; + } + + return transform; + } + + [SuppressMessage("Reliability", "CA2000:Dispose objects before losing scope")] + public DB.Transform GetReferencePointTransform(string referencePointSetting) + { + // first get the main doc base points and reference setting transform + var referencePointTransform = DB.Transform.Identity; + + // POC: bogus disposal below + var points = new DB.FilteredElementCollector(_contextStack.Current.Document) + .OfClass(typeof(DB.BasePoint)) + .Cast() + .ToList(); + + var projectPoint = points.FirstOrDefault(o => o.IsShared == false); + var surveyPoint = points.FirstOrDefault(o => o.IsShared); + + // POC: it's not clear what support is needed for this + switch (referencePointSetting) + { + case REFPOINT_PROJECT_BASE: // note that the project base (ui) rotation is registered on the survey pt, not on the base point + referencePointTransform = DB.Transform.CreateTranslation(projectPoint.Position); + break; + + case REFPOINT_SURVEY: + // note that the project base (ui) rotation is registered on the survey pt, not on the base point + // retrieve the survey point rotation from the project point + + // POC: should a null angle resolve to 0? + var angle = projectPoint.get_Parameter(DB.BuiltInParameter.BASEPOINT_ANGLETON_PARAM)?.AsDouble() ?? 0; + + // POC: following disposed incorrectly or early or maybe a false negative? + referencePointTransform = DB.Transform + .CreateTranslation(surveyPoint.Position) + .Multiply(DB.Transform.CreateRotation(DB.XYZ.BasisZ, angle)); + + break; + + case REFPOINT_INTERNAL_ORIGIN: + break; + } + + return referencePointTransform; + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/RevitConversionSettings.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/RevitConversionSettings.cs new file mode 100644 index 0000000000..3fb4466120 --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/RevitConversionSettings.cs @@ -0,0 +1,19 @@ +namespace Speckle.Converters.RevitShared; + +// POC: probably NOT the right place, probably needs passing in with the send/rcv operation +// not clear how this should get configured or if we should have it, the shape probably needs to change +// this was dragged in because it (or something like it) is required for reference point conversion. +// have made it a strongly typed thing encapsulating a dictionary so as not to rely on injecting a weak type. +// relates to https://spockle.atlassian.net/browse/CNX-9357 +public class RevitConversionSettings +{ + private Dictionary Settings { get; } = new Dictionary(); + + public bool TryGetSettingString(string key, out string value) => Settings.TryGetValue(key, out value); + + public string this[string key] + { + get => Settings[key]; + set => Settings[key] = value; + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/RevitRootElementProvider.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/RevitRootElementProvider.cs new file mode 100644 index 0000000000..65aeca21da --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/RevitRootElementProvider.cs @@ -0,0 +1,11 @@ +using Speckle.Converters.Common; +using Speckle.Revit.Interfaces; + +namespace Speckle.Converters.RevitShared; + +public class RevitRootElementProvider : IRootElementProvider +{ + private static readonly Type s_wrappedElementType = typeof(IRevitElement); + + public Type GetRootType() => s_wrappedElementType; +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/RevitRootToHostConverter.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/RevitRootToHostConverter.cs new file mode 100644 index 0000000000..f6284b930f --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/RevitRootToHostConverter.cs @@ -0,0 +1,28 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; + +namespace Speckle.Converters.RevitShared; + +public class RevitRootToHostConverter : IRootToHostConverter +{ + private readonly IConverterResolver _converterResolver; + + public RevitRootToHostConverter(IConverterResolver converterResolver) + { + _converterResolver = converterResolver; + } + + public object Convert(Base target) + { + var objectConverter = _converterResolver.GetConversionForType(target.GetType()); + + if (objectConverter == null) + { + throw new SpeckleConversionException($"No conversion found for {target.GetType().Name}"); + } + + return objectConverter.Convert(target) + ?? throw new SpeckleConversionException($"Conversion of object with type {target.GetType()} returned null"); + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/RevitRootToSpeckleConverter.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/RevitRootToSpeckleConverter.cs new file mode 100644 index 0000000000..28336c4eb3 --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/RevitRootToSpeckleConverter.cs @@ -0,0 +1,50 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; +using Speckle.Converters.RevitShared.Helpers; + +namespace Speckle.Converters.RevitShared; + +// POC: maybe possible to restrict the access so this cannot be created directly? +public class RevitRootToSpeckleConverter : IRootToSpeckleConverter +{ + private readonly IConverterResolver _toSpeckle; + private readonly ParameterValueExtractor _parameterValueExtractor; + + public RevitRootToSpeckleConverter( + IConverterResolver toSpeckle, + ParameterValueExtractor parameterValueExtractor + ) + { + _toSpeckle = toSpeckle; + _parameterValueExtractor = parameterValueExtractor; + } + + // POC: our assumption here is target is valid for conversion + // if it cannot be converted then we should throw + public Base Convert(object target) + { + var objectConverter = _toSpeckle.GetConversionForType(target.GetType()); + + if (objectConverter == null) + { + throw new SpeckleConversionException($"No conversion found for {target.GetType().Name}"); + } + + Base result = + objectConverter.Convert(target) + ?? throw new SpeckleConversionException($"Conversion of object with type {target.GetType()} returned null"); + + // POC : where should logic common to most objects go? + // shouldn't target ALWAYS be DB.Element? + if (target is DB.Element element) + { + // POC: is this the right place? + result.applicationId = element.UniqueId; + + _parameterValueExtractor.RemoveUniqueId(element.UniqueId); + } + + return result; + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Services/RevitToSpeckleUnitConverter.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Services/RevitToSpeckleUnitConverter.cs new file mode 100644 index 0000000000..e1d7a7a62b --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Services/RevitToSpeckleUnitConverter.cs @@ -0,0 +1,34 @@ +using Speckle.Converters.Common; +using Speckle.Core.Kits; +using Speckle.Core.Logging; + +namespace Speckle.Converters.RevitShared.Services; + +public sealed class RevitToSpeckleUnitConverter : IHostToSpeckleUnitConverter +{ + private readonly Dictionary _unitMapping = new(); + + public RevitToSpeckleUnitConverter() + { + _unitMapping[DB.UnitTypeId.Millimeters] = Units.Millimeters; + _unitMapping[DB.UnitTypeId.Centimeters] = Units.Centimeters; + _unitMapping[DB.UnitTypeId.Meters] = Units.Meters; + _unitMapping[DB.UnitTypeId.MetersCentimeters] = Units.Meters; + _unitMapping[DB.UnitTypeId.Inches] = Units.Inches; + _unitMapping[DB.UnitTypeId.FractionalInches] = Units.Inches; + _unitMapping[DB.UnitTypeId.Feet] = Units.Feet; + _unitMapping[DB.UnitTypeId.FeetFractionalInches] = Units.Feet; + } + + // POC: maybe just convert, it's not a Try method + public string ConvertOrThrow(DB.ForgeTypeId hostUnit) + { + if (_unitMapping.TryGetValue(hostUnit, out string value)) + { + return value; + } + + // POC: probably would prefer something more specific + throw new SpeckleException($"The Unit System \"{hostUnit}\" is unsupported."); + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Services/ScalingServiceToHost.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Services/ScalingServiceToHost.cs new file mode 100644 index 0000000000..bf6e956279 --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Services/ScalingServiceToHost.cs @@ -0,0 +1,32 @@ +using Autodesk.Revit.DB; +using Speckle.Converters.Common; + +namespace Speckle.Converters.RevitShared.Services; + +public sealed class ScalingServiceToHost +{ + public double ScaleToNative(double value, string units) + { + if (string.IsNullOrEmpty(units)) + { + return value; + } + + return UnitUtils.ConvertToInternalUnits(value, UnitsToNative(units)); + } + + public ForgeTypeId UnitsToNative(string units) + { + var u = Core.Kits.Units.GetUnitsFromString(units); + + return u switch + { + Core.Kits.Units.Millimeters => UnitTypeId.Millimeters, + Core.Kits.Units.Centimeters => UnitTypeId.Centimeters, + Core.Kits.Units.Meters => UnitTypeId.Meters, + Core.Kits.Units.Inches => UnitTypeId.Inches, + Core.Kits.Units.Feet => UnitTypeId.Feet, + _ => throw new SpeckleConversionException($"The Unit System \"{units}\" is unsupported."), + }; + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Services/ScalingServiceToSpeckle.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Services/ScalingServiceToSpeckle.cs new file mode 100644 index 0000000000..a0c2779048 --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Services/ScalingServiceToSpeckle.cs @@ -0,0 +1,39 @@ +using Autodesk.Revit.DB; +using Speckle.Converters.RevitShared.Helpers; + +namespace Speckle.Converters.RevitShared.Services; + +// POC: feels like this is a context thing and we should be calculating this occasionally? +// needs some thought as to how it could be be done, could leave as is for now +public sealed class ScalingServiceToSpeckle +{ + private readonly double _defaultLengthConversionFactor; + + // POC: this seems like the reverse relationship + public ScalingServiceToSpeckle(IRevitConversionContextStack contextStack) + { + // POC: this is accurate for the current context stack + Units documentUnits = contextStack.Current.Document.GetUnits(); + FormatOptions formatOptions = documentUnits.GetFormatOptions(SpecTypeId.Length); + var lengthUnitsTypeId = formatOptions.GetUnitTypeId(); + _defaultLengthConversionFactor = ScaleStatic(1, lengthUnitsTypeId); + } + + // POC: throughout Revit conversions there's lots of comparison to check the units are valid + // atm we seem to be expecting that this is correct and that the scaling will be fixed for the duration + // of a conversion, but... I have some concerns that the units and the conversion may change, for instance, for linked documents? + // this needs to be considered and perahps scaling should be part of the context, or at least part of the IRevitConversionContextStack + public double ScaleLength(double length) => length * _defaultLengthConversionFactor; + + // POC: not sure about this??? + public double Scale(double value, ForgeTypeId forgeTypeId) + { + return ScaleStatic(value, forgeTypeId); + } + + // POC: not sure why this is needed??? + private static double ScaleStatic(double value, ForgeTypeId forgeTypeId) + { + return UnitUtils.ConvertFromInternalUnits(value, forgeTypeId); + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Speckle.Converters.RevitShared.projitems b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Speckle.Converters.RevitShared.projitems new file mode 100644 index 0000000000..e33ba9046a --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Speckle.Converters.RevitShared.projitems @@ -0,0 +1,96 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + 9655be78-8070-4b9f-b0dc-68bb6150b52c + + + Speckle.Converters.RevitShared + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Speckle.Converters.RevitShared.shproj b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Speckle.Converters.RevitShared.shproj new file mode 100644 index 0000000000..8f0ef81afc --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/Speckle.Converters.RevitShared.shproj @@ -0,0 +1,13 @@ + + + + {E1C43415-3200-45F4-8BF9-A4DD7D7F2ED6} + 14.0 + + + + + + + + \ No newline at end of file diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToHost/Raw/Geometry/ArcConverterToHost.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToHost/Raw/Geometry/ArcConverterToHost.cs new file mode 100644 index 0000000000..6e9167c7eb --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToHost/Raw/Geometry/ArcConverterToHost.cs @@ -0,0 +1,59 @@ +using Speckle.Converters.Common.Objects; +using Speckle.Converters.RevitShared.Services; +using Speckle.Core.Common; + +namespace Speckle.Converters.RevitShared.ToHost.Raw.Geometry; + +public class ArcConverterToHost : ITypedConverter +{ + private readonly ScalingServiceToHost _scalingService; + private readonly ITypedConverter _pointToXyzConverter; + private readonly ITypedConverter _planeConverter; + + public ArcConverterToHost( + ITypedConverter pointToXyzConverter, + ScalingServiceToHost scalingService, + ITypedConverter planeConverter + ) + { + _pointToXyzConverter = pointToXyzConverter; + _scalingService = scalingService; + _planeConverter = planeConverter; + } + + public DB.Arc Convert(SOG.Arc target) + { + double startAngle; + double endAngle; + + if (target.startAngle > target.endAngle) + { + startAngle = (double)target.endAngle; + endAngle = (double)target.startAngle; + } + else + { + startAngle = (double)target.startAngle.NotNull(); + endAngle = (double)target.endAngle.NotNull(); + } + + var plane = _planeConverter.Convert(target.plane); + + if (SOG.Point.Distance(target.startPoint, target.endPoint) < 1E-6) + { + // Endpoints coincide, it's a circle. + return DB.Arc.Create( + plane, + _scalingService.ScaleToNative(target.radius ?? 0, target.units), + startAngle, + endAngle + ); + } + + return DB.Arc.Create( + _pointToXyzConverter.Convert(target.startPoint), + _pointToXyzConverter.Convert(target.endPoint), + _pointToXyzConverter.Convert(target.midPoint) + ); + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToHost/Raw/Geometry/CircleConverterToHost.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToHost/Raw/Geometry/CircleConverterToHost.cs new file mode 100644 index 0000000000..55ba5f1974 --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToHost/Raw/Geometry/CircleConverterToHost.cs @@ -0,0 +1,28 @@ +using Speckle.Converters.Common.Objects; +using Speckle.Converters.RevitShared.Services; +using Speckle.Core.Common; + +namespace Speckle.Converters.RevitShared.ToHost.Raw.Geometry; + +public class CircleConverterToHost : ITypedConverter +{ + private readonly ScalingServiceToHost _scalingService; + private readonly ITypedConverter _planeConverter; + + public CircleConverterToHost(ScalingServiceToHost scalingService, ITypedConverter planeConverter) + { + _scalingService = scalingService; + _planeConverter = planeConverter; + } + + public DB.Arc Convert(SOG.Circle target) + { + var plane = _planeConverter.Convert(target.plane); + return DB.Arc.Create( + plane, + _scalingService.ScaleToNative((double)target.radius.NotNull(), target.units), + 0, + 2 * Math.PI + ); + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToHost/Raw/Geometry/CurveConverterToHost.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToHost/Raw/Geometry/CurveConverterToHost.cs new file mode 100644 index 0000000000..5f1f40f603 --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToHost/Raw/Geometry/CurveConverterToHost.cs @@ -0,0 +1,47 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.RevitShared.ToSpeckle; + +public class CurveConverterToHost : ITypedConverter +{ + private readonly ITypedConverter _pointConverter; + + public CurveConverterToHost(ITypedConverter pointConverter) + { + _pointConverter = pointConverter; + } + + public DB.Curve Convert(SOG.Curve target) + { + var pts = new List(); + for (int i = 0; i < target.points.Count; i += 3) + { + //use PointToNative for conversion as that takes into account the Project Base Point + var point = new SOG.Point(target.points[i], target.points[i + 1], target.points[i + 2], target.units); + pts.Add(_pointConverter.Convert(point)); + } + + if (target.knots != null && target.weights != null && target.knots.Count > 0 && target.weights.Count > 0) + { + var weights = target.weights.GetRange(0, pts.Count); + var speckleKnots = new List(target.knots); + if (speckleKnots.Count != pts.Count + target.degree + 1) + { + // Curve has rhino knots, repeat first and last. + speckleKnots.Insert(0, speckleKnots[0]); + speckleKnots.Add(speckleKnots[^1]); + } + + //var knots = speckleKnots.GetRange(0, pts.Count + speckleCurve.degree + 1); + var curve = DB.NurbSpline.CreateCurve(target.degree, speckleKnots, pts, weights); + return curve; + } + else + { + var weights = target.weights.NotNull().GetRange(0, pts.Count); + var curve = DB.NurbSpline.CreateCurve(pts, weights); + return curve; + } + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToHost/Raw/Geometry/EllipseConverterToHost.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToHost/Raw/Geometry/EllipseConverterToHost.cs new file mode 100644 index 0000000000..f2c86ef7c8 --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToHost/Raw/Geometry/EllipseConverterToHost.cs @@ -0,0 +1,40 @@ +using Speckle.Converters.Common.Objects; +using Speckle.Converters.RevitShared.Services; +using Speckle.Core.Common; + +namespace Speckle.Converters.RevitShared.ToHost.Raw.Geometry; + +public class EllipseConverterToHost : ITypedConverter +{ + private readonly ScalingServiceToHost _scalingService; + private readonly ITypedConverter _pointToXyzConverter; + private readonly ITypedConverter _planeConverter; + + public EllipseConverterToHost( + ITypedConverter pointToXyzConverter, + ScalingServiceToHost scalingService, + ITypedConverter planeConverter + ) + { + _pointToXyzConverter = pointToXyzConverter; + _scalingService = scalingService; + _planeConverter = planeConverter; + } + + public DB.Curve Convert(SOG.Ellipse target) + { + using DB.Plane basePlane = _planeConverter.Convert(target.plane); + + var e = DB.Ellipse.CreateCurve( + _pointToXyzConverter.Convert(target.plane.origin), + _scalingService.ScaleToNative((double)target.firstRadius.NotNull(), target.units), + _scalingService.ScaleToNative((double)target.secondRadius.NotNull(), target.units), + basePlane.XVec.Normalize(), + basePlane.YVec.Normalize(), + 0, + 2 * Math.PI + ); + e.MakeBound(target.trimDomain?.start ?? 0, target.trimDomain?.end ?? 2 * Math.PI); + return e; + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToHost/Raw/Geometry/ICurveConverterToHost.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToHost/Raw/Geometry/ICurveConverterToHost.cs new file mode 100644 index 0000000000..2b3eedcb15 --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToHost/Raw/Geometry/ICurveConverterToHost.cs @@ -0,0 +1,129 @@ +using Objects; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; + +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; + private readonly ITypedConverter _ellipseConverter; + private readonly ITypedConverter _polylineConverter; + private readonly ITypedConverter _curveConverter; + + public ICurveConverterToHost( + ITypedConverter pointConverter, + ITypedConverter vectorConverter, + ITypedConverter arcConverter, + ITypedConverter lineConverter, + ITypedConverter circleConverter, + ITypedConverter ellipseConverter, + ITypedConverter polylineConverter, + ITypedConverter curveConverter + ) + { + _pointConverter = pointConverter; + _vectorConverter = vectorConverter; + _arcConverter = arcConverter; + _lineConverter = lineConverter; + _circleConverter = circleConverter; + _ellipseConverter = ellipseConverter; + _polylineConverter = polylineConverter; + _curveConverter = curveConverter; + } + + public DB.CurveArray Convert(ICurve target) + { + DB.CurveArray curveArray = new(); + switch (target) + { + case SOG.Line line: + curveArray.Append(_lineConverter.Convert(line)); + return curveArray; + + case SOG.Arc arc: + curveArray.Append(_arcConverter.Convert(arc)); + return curveArray; + + case SOG.Circle circle: + curveArray.Append(_circleConverter.Convert(circle)); + return curveArray; + + case SOG.Ellipse ellipse: + curveArray.Append(_ellipseConverter.Convert(ellipse)); + return curveArray; + + case SOG.Spiral spiral: + return _polylineConverter.Convert(spiral.displayValue); + + case SOG.Curve nurbs: + var n = _curveConverter.Convert(nurbs); + + // poc : in original converter, we were passing a bool into this method 'splitIfClosed'. + // https://spockle.atlassian.net/browse/DUI3-462 + // I'm not entirely sure why we need to split curves, but there are several occurances + // of the method being called and overriding the bool to be true. + + //if (IsCurveClosed(n) && splitIfClosed) + //{ + // var split = SplitCurveInTwoHalves(n); + // curveArray.Append(split.Item1); + // curveArray.Append(split.Item2); + //} + //else + //{ + // curveArray.Append(n); + //} + curveArray.Append(n); + return curveArray; + + case SOG.Polyline poly: + return _polylineConverter.Convert(poly); + + case SOG.Polycurve plc: + foreach (var seg in plc.segments) + { + // Enumerate all curves in the array to ensure polylines get fully converted. + using var subCurves = Convert(seg); + var crvEnumerator = subCurves.GetEnumerator(); + while (crvEnumerator.MoveNext() && crvEnumerator.Current != null) + { + curveArray.Append(crvEnumerator.Current as DB.Curve); + } + } + return curveArray; + default: + throw new SpeckleConversionException($"The provided geometry of type {target.GetType()} is not a supported"); + } + } + + public bool IsCurveClosed(DB.Curve nativeCurve, double tol = 1E-6) + { + var endPoint = nativeCurve.GetEndPoint(0); + var source = nativeCurve.GetEndPoint(1); + var distanceTo = endPoint.DistanceTo(source); + return distanceTo < tol; + } + + public (DB.Curve, DB.Curve) SplitCurveInTwoHalves(DB.Curve nativeCurve) + { + using var curveArray = new DB.CurveArray(); + // Revit does not like single curve loop edges, so we split them in two. + var start = nativeCurve.GetEndParameter(0); + var end = nativeCurve.GetEndParameter(1); + var mid = start + ((end - start) / 2); + + var a = nativeCurve.Clone(); + a.MakeBound(start, mid); + curveArray.Append(a); + var b = nativeCurve.Clone(); + b.MakeBound(mid, end); + curveArray.Append(b); + + return (a, b); + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToHost/Raw/Geometry/LineConverterToHost.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToHost/Raw/Geometry/LineConverterToHost.cs new file mode 100644 index 0000000000..de8237e3ce --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToHost/Raw/Geometry/LineConverterToHost.cs @@ -0,0 +1,16 @@ +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.RevitShared.ToSpeckle; + +public class LineConverterToHost : ITypedConverter +{ + private readonly ITypedConverter _pointToXyzConverter; + + public LineConverterToHost(ITypedConverter pointToXyzConverter) + { + _pointToXyzConverter = pointToXyzConverter; + } + + public DB.Line Convert(SOG.Line target) => + DB.Line.CreateBound(_pointToXyzConverter.Convert(target.start), _pointToXyzConverter.Convert(target.end)); +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToHost/Raw/Geometry/PlaneConverterToHost.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToHost/Raw/Geometry/PlaneConverterToHost.cs new file mode 100644 index 0000000000..c789aba066 --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToHost/Raw/Geometry/PlaneConverterToHost.cs @@ -0,0 +1,27 @@ +using Autodesk.Revit.DB; +using Objects.Geometry; +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.RevitShared.ToSpeckle; + +public class PlaneConverterToHost : ITypedConverter +{ + private readonly ITypedConverter _pointConverter; + private readonly ITypedConverter _vectorConverter; + + public PlaneConverterToHost( + ITypedConverter pointConverter, + ITypedConverter vectorConverter + ) + { + _pointConverter = pointConverter; + _vectorConverter = vectorConverter; + } + + public DB.Plane Convert(SOG.Plane target) => + DB.Plane.CreateByOriginAndBasis( + _pointConverter.Convert(target.origin), + _vectorConverter.Convert(target.xdir).Normalize(), + _vectorConverter.Convert(target.ydir).Normalize() + ); +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToHost/Raw/Geometry/PointConverterToHost.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToHost/Raw/Geometry/PointConverterToHost.cs new file mode 100644 index 0000000000..66b8b5488b --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToHost/Raw/Geometry/PointConverterToHost.cs @@ -0,0 +1,27 @@ +using Autodesk.Revit.DB; +using Speckle.Converters.Common.Objects; +using Speckle.Converters.RevitShared.Services; + +namespace Speckle.Converters.RevitShared.ToSpeckle; + +public class PointConverterToHost : ITypedConverter +{ + private readonly ScalingServiceToHost _scalingService; + private readonly IReferencePointConverter _referencePointConverter; + + public PointConverterToHost(ScalingServiceToHost scalingService, IReferencePointConverter referencePointConverter) + { + _scalingService = scalingService; + _referencePointConverter = referencePointConverter; + } + + public XYZ Convert(SOG.Point target) + { + var revitPoint = new XYZ( + _scalingService.ScaleToNative(target.x, target.units), + _scalingService.ScaleToNative(target.y, target.units), + _scalingService.ScaleToNative(target.z, target.units) + ); + return _referencePointConverter.ToInternalCoordinates(revitPoint, true); + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToHost/Raw/Geometry/PolylineConverterToHost.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToHost/Raw/Geometry/PolylineConverterToHost.cs new file mode 100644 index 0000000000..544df8254f --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToHost/Raw/Geometry/PolylineConverterToHost.cs @@ -0,0 +1,88 @@ +using Autodesk.Revit.DB; +using Objects.Geometry; +using Speckle.Converters.Common.Objects; +using Speckle.Converters.RevitShared.Helpers; +using Speckle.Converters.RevitShared.Services; + +namespace Speckle.Converters.RevitShared.ToSpeckle; + +public class PolylineConverterToHost : ITypedConverter +{ + private readonly ITypedConverter _lineConverter; + private readonly ScalingServiceToHost _scalingService; + private readonly IRevitConversionContextStack _contextStack; + + public PolylineConverterToHost( + ITypedConverter lineConverter, + ScalingServiceToHost scalingService, + IRevitConversionContextStack contextStack + ) + { + _lineConverter = lineConverter; + _scalingService = scalingService; + _contextStack = contextStack; + } + + public CurveArray Convert(Polyline target) + { + var curveArray = new CurveArray(); + if (target.value.Count == 6) + { + // 6 coordinate values (two sets of 3), so polyline is actually a single line + curveArray.Append(_lineConverter.Convert(new SOG.Line(target.value, target.units))); + } + else + { + var pts = target.GetPoints(); + var lastPt = pts[0]; + for (var i = 1; i < pts.Count; i++) + { + var success = TryAppendLineSafely(curveArray, new SOG.Line(lastPt, pts[i], target.units)); + if (success) + { + lastPt = pts[i]; + } + } + + if (target.closed) + { + TryAppendLineSafely(curveArray, new SOG.Line(pts[^1], pts[0], target.units)); + } + } + return curveArray; + } + + /// + /// Checks if a Speckle is too sort to be created in Revit. + /// + /// + /// The length of the line will be computed on the spot to ensure it is accurate. + /// + /// The to be tested. + /// true if the line is too short, false otherwise. + public bool IsLineTooShort(SOG.Line line) + { + var scaleToNative = _scalingService.ScaleToNative(SOG.Point.Distance(line.start, line.end), line.units); + return scaleToNative < _contextStack.Current.Document.Application.ShortCurveTolerance; + } + + /// + /// Attempts to append a Speckle onto a Revit . + /// This method ensures the line is long enough to be supported. + /// It will also convert the line to Revit before appending it to the . + /// + /// The revit to add the line to. + /// The to be added. + /// True if the line was added, false otherwise. + public bool TryAppendLineSafely(CurveArray curveArray, SOG.Line line) + { + if (IsLineTooShort(line)) + { + // poc : logging "Some lines in the CurveArray where ignored due to being smaller than the allowed curve length." + return false; + } + + curveArray.Append(_lineConverter.Convert(line)); + return true; + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToHost/Raw/Geometry/VectorConverterToHost.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToHost/Raw/Geometry/VectorConverterToHost.cs new file mode 100644 index 0000000000..04f589d2cf --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToHost/Raw/Geometry/VectorConverterToHost.cs @@ -0,0 +1,28 @@ +using Autodesk.Revit.DB; +using Objects.Geometry; +using Speckle.Converters.Common.Objects; +using Speckle.Converters.RevitShared.Services; + +namespace Speckle.Converters.RevitShared.ToSpeckle; + +public class VectorConverterToHost : ITypedConverter +{ + private readonly ScalingServiceToHost _scalingService; + private readonly IReferencePointConverter _referencePointConverter; + + public VectorConverterToHost(ScalingServiceToHost scalingService, IReferencePointConverter referencePointConverter) + { + _scalingService = scalingService; + _referencePointConverter = referencePointConverter; + } + + public XYZ Convert(Vector target) + { + var revitVector = new XYZ( + _scalingService.ScaleToNative(target.x, target.units), + _scalingService.ScaleToNative(target.y, target.units), + _scalingService.ScaleToNative(target.z, target.units) + ); + return _referencePointConverter.ToInternalCoordinates(revitVector, false); + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToHost/TopLevel/BaseTopLevelConverterToHost.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToHost/TopLevel/BaseTopLevelConverterToHost.cs new file mode 100644 index 0000000000..4ea35a7980 --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToHost/TopLevel/BaseTopLevelConverterToHost.cs @@ -0,0 +1,17 @@ +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; + +namespace Speckle.Converters.RevitShared.ToSpeckle; + +public abstract class BaseTopLevelConverterToHost : IToHostTopLevelConverter + where TSpeckle : Base + where THost : notnull +{ + public abstract THost Convert(TSpeckle target); + + public object Convert(Base target) + { + var result = Convert((TSpeckle)target); + return result; + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToHost/TopLevel/GridlineToHostTopLevelConverter.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToHost/TopLevel/GridlineToHostTopLevelConverter.cs new file mode 100644 index 0000000000..24080b9516 --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToHost/TopLevel/GridlineToHostTopLevelConverter.cs @@ -0,0 +1,56 @@ +using Objects; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Converters.RevitShared.Helpers; +using Speckle.Converters.RevitShared.ToSpeckle; + +namespace Speckle.Converters.RevitShared.ToHost.TopLevel; + +[NameAndRankValue(nameof(SOBE.GridLine), 0)] +internal class GridlineToHostTopLevelConverter : BaseTopLevelConverterToHost +{ + private readonly ITypedConverter _curveConverter; + private readonly IRevitConversionContextStack _contextStack; + + public GridlineToHostTopLevelConverter( + ITypedConverter curveConverter, + IRevitConversionContextStack contextStack + ) + { + _curveConverter = curveConverter; + _contextStack = contextStack; + } + + public override DB.Grid Convert(SOBE.GridLine target) + { + DB.Curve curve = _curveConverter.Convert(target.baseLine).get_Item(0); + + using DB.Grid revitGrid = curve switch + { + DB.Arc arc => DB.Grid.Create(_contextStack.Current.Document, arc), + DB.Line line => DB.Grid.Create(_contextStack.Current.Document, line), + _ => throw new SpeckleConversionException($"Grid line curve is of type {curve.GetType()} which is not supported") + }; + + if (!string.IsNullOrEmpty(target.label) && !GridNameIsTaken(target.label)) + { + revitGrid.Name = target.label; + } + + return revitGrid; + } + + private bool GridNameIsTaken(string gridName) + { + using var collector = new DB.FilteredElementCollector(_contextStack.Current.Document); + + IEnumerable gridNames = collector + .WhereElementIsNotElementType() + .OfClass(typeof(DB.Grid)) + .ToElements() + .Cast() + .Select(grid => grid.Name); + + return gridNames.Contains(gridName); + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToHost/TopLevel/LevelToHostTopLevelConverter.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToHost/TopLevel/LevelToHostTopLevelConverter.cs new file mode 100644 index 0000000000..69f72e4f18 --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToHost/TopLevel/LevelToHostTopLevelConverter.cs @@ -0,0 +1,77 @@ +using Speckle.Converters.Common; +using Speckle.Converters.RevitShared.Helpers; +using Speckle.Converters.RevitShared.ToSpeckle; +using Speckle.Converters.RevitShared.Services; + +namespace Speckle.Converters.RevitShared.ToHost.ToLevel; + +[NameAndRankValue(nameof(SOBE.Level), 0)] +public class LevelToHostTopLevelConverter : BaseTopLevelConverterToHost +{ + private readonly IRevitConversionContextStack _contextStack; + private readonly ScalingServiceToHost _scalingService; + + public LevelToHostTopLevelConverter(IRevitConversionContextStack contextStack, ScalingServiceToHost scalingService) + { + _contextStack = contextStack; + _scalingService = scalingService; + } + + public override DB.Level Convert(SOBE.Level target) + { + using var documentLevelCollector = new DB.FilteredElementCollector(_contextStack.Current.Document); + var docLevels = documentLevelCollector.OfClass(typeof(DB.Level)).ToElements().Cast(); + + // POC : I'm not really understanding the linked use case for this. Do we want to bring this over? + + //bool elevationMatch = true; + ////level by name component + //if (target is RevitLevel speckleRevitLevel && speckleRevitLevel.referenceOnly) + //{ + // //see: https://speckle.community/t/revit-connector-levels-and-spaces/2824/5 + // elevationMatch = false; + // if (GetExistingLevelByName(docLevels, target.name) is DB.Level existingLevelWithSameName) + // { + // return existingLevelWithSameName; + // } + //} + + DB.Level revitLevel; + var targetElevation = _scalingService.ScaleToNative(target.elevation, target.units); + + if (GetExistingLevelByElevation(docLevels, targetElevation) is DB.Level existingLevel) + { + revitLevel = existingLevel; + } + else + { + revitLevel = DB.Level.Create(_contextStack.Current.Document, targetElevation); + revitLevel.Name = target.name; + + if (target is SOBR.RevitLevel rl && rl.createView) + { + using var viewPlan = CreateViewPlan(target.name, revitLevel.Id); + } + } + + return revitLevel; + } + + private static DB.Level GetExistingLevelByElevation(IEnumerable docLevels, double elevation) + { + return docLevels.FirstOrDefault(l => Math.Abs(l.Elevation - elevation) < RevitConversionContextStack.TOLERANCE); + } + + private DB.ViewPlan CreateViewPlan(string name, DB.ElementId levelId) + { + using var collector = new DB.FilteredElementCollector(_contextStack.Current.Document); + var vt = collector + .OfClass(typeof(DB.ViewFamilyType)) + .First(el => ((DB.ViewFamilyType)el).ViewFamily == DB.ViewFamily.FloorPlan); + + var view = DB.ViewPlan.Create(_contextStack.Current.Document, vt.Id, levelId); + view.Name = name; + + return view; + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToHost/TopLevel/ModelCurveToSpeckleTopLevelConverter.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToHost/TopLevel/ModelCurveToSpeckleTopLevelConverter.cs new file mode 100644 index 0000000000..e037bafd9e --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToHost/TopLevel/ModelCurveToSpeckleTopLevelConverter.cs @@ -0,0 +1,107 @@ +using Objects; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Converters.RevitShared.Helpers; +using System.Collections; + +namespace Speckle.Converters.RevitShared.ToSpeckle; + +[NameAndRankValue(nameof(SOBR.Curve.ModelCurve), 0)] +public class ModelCurveToHostTopLevelConverter : BaseTopLevelConverterToHost +{ + private readonly ITypedConverter _curveConverter; + private readonly IRevitConversionContextStack _contextStack; + + public ModelCurveToHostTopLevelConverter( + ITypedConverter curveConverter, + IRevitConversionContextStack conversionContext + ) + { + _curveConverter = curveConverter; + _contextStack = conversionContext; + } + + public override DB.ModelCurve[] Convert(SOBR.Curve.ModelCurve target) => + ModelCurvesFromEnumerator(_curveConverter.Convert(target.baseCurve).GetEnumerator(), target.baseCurve).ToArray(); + + private IEnumerable ModelCurvesFromEnumerator(IEnumerator curveEnum, ICurve speckleLine) + { + while (curveEnum.MoveNext() && curveEnum.Current != null) + { + var curve = (DB.Curve)curveEnum.Current; + // Curves must be bound in order to be valid model curves + if (!curve.IsBound) + { + curve.MakeBound(speckleLine.domain.start ?? 0, speckleLine.domain.end ?? Math.PI * 2); + } + + if (_contextStack.Current.Document.IsFamilyDocument) + { + yield return _contextStack.Current.Document.FamilyCreate.NewModelCurve( + curve, + NewSketchPlaneFromCurve(curve, _contextStack.Current.Document) + ); + } + else + { + yield return _contextStack.Current.Document.Create.NewModelCurve( + curve, + NewSketchPlaneFromCurve(curve, _contextStack.Current.Document) + ); + } + } + } + + /// + /// Credits: Grevit + /// Creates a new Sketch Plane from a Curve + /// https://github.com/grevit-dev/Grevit/blob/3c7a5cc198e00dfa4cc1e892edba7c7afd1a3f84/Grevit.Revit/Utilities.cs#L402 + /// + /// Curve to get plane from + /// Plane of the curve + private DB.SketchPlane NewSketchPlaneFromCurve(DB.Curve curve, DB.Document doc) + { + DB.XYZ startPoint = curve.GetEndPoint(0); + DB.XYZ endPoint = curve.GetEndPoint(1); + + // If Start end Endpoint are the same check further points. + int i = 2; + while (startPoint == endPoint && endPoint != null) + { + endPoint = curve.GetEndPoint(i); + i++; + } + + // Plane to return + DB.Plane plane; + + // If Z Values are equal the Plane is XY + if (startPoint.Z == endPoint.NotNull().Z) + { + plane = DB.Plane.CreateByNormalAndOrigin(DB.XYZ.BasisZ, startPoint); + } + // If X Values are equal the Plane is YZ + else if (startPoint.X == endPoint.X) + { + plane = DB.Plane.CreateByNormalAndOrigin(DB.XYZ.BasisX, startPoint); + } + // If Y Values are equal the Plane is XZ + else if (startPoint.Y == endPoint.Y) + { + plane = DB.Plane.CreateByNormalAndOrigin(DB.XYZ.BasisY, startPoint); + } + // Otherwise the Planes Normal Vector is not X,Y or Z. + // We draw lines from the Origin to each Point and use the Plane this one spans up. + else + { + using DB.CurveArray curves = new(); + curves.Append(curve); + curves.Append(DB.Line.CreateBound(new DB.XYZ(0, 0, 0), startPoint)); + curves.Append(DB.Line.CreateBound(endPoint, new DB.XYZ(0, 0, 0))); + + plane = DB.Plane.CreateByThreePoints(startPoint, new DB.XYZ(0, 0, 0), endPoint); + } + + return DB.SketchPlane.Create(doc, plane); + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/BeamConversionToSpeckle.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/BeamConversionToSpeckle.cs new file mode 100644 index 0000000000..d6ee746e43 --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/BeamConversionToSpeckle.cs @@ -0,0 +1,67 @@ +using Objects; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Converters.RevitShared.Helpers; +using Speckle.Core.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 ParameterObjectAssigner _parameterObjectAssigner; + + public BeamConversionToSpeckle( + ITypedConverter locationConverter, + ITypedConverter levelConverter, + ParameterValueExtractor parameterValueExtractor, + DisplayValueExtractor displayValueExtractor, + ParameterObjectAssigner parameterObjectAssigner + ) + { + _locationConverter = locationConverter; + _levelConverter = levelConverter; + _parameterValueExtractor = parameterValueExtractor; + _displayValueExtractor = displayValueExtractor; + _parameterObjectAssigner = parameterObjectAssigner; + } + + public SOBR.RevitBeam Convert(DB.FamilyInstance target) + { + var baseGeometry = _locationConverter.Convert(target.Location); + if (baseGeometry is not ICurve baseCurve) + { + throw new SpeckleConversionException( + $"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()); + + SOBR.RevitBeam speckleBeam = + new() + { + family = symbol.FamilyName, + type = target.Document.GetElement(target.GetTypeId()).Name, + baseLine = baseCurve + }; + + var level = _parameterValueExtractor.GetValueAsDocumentObject( + target, + DB.BuiltInParameter.INSTANCE_REFERENCE_LEVEL_PARAM + ); + speckleBeam.level = _levelConverter.Convert(level); + + speckleBeam.displayValue = _displayValueExtractor.GetDisplayValue(target); + + _parameterObjectAssigner.AssignParametersToBase(target, speckleBeam); + + return speckleBeam; + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/BoundarySegmentConversionToSpeckle.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/BoundarySegmentConversionToSpeckle.cs new file mode 100644 index 0000000000..8f02d4c345 --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/BoundarySegmentConversionToSpeckle.cs @@ -0,0 +1,36 @@ +using Objects; +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.RevitShared.ToSpeckle; + +public class BoundarySegmentConversionToSpeckle : ITypedConverter, SOG.Polycurve> +{ + private readonly ITypedConverter _curveConverter; + + public BoundarySegmentConversionToSpeckle(ITypedConverter curveConverter) + { + _curveConverter = curveConverter; + } + + public SOG.Polycurve Convert(IList target) + { + if (target.Count == 0) + { + throw new ArgumentException("Input Boundary segment list must at least have 1 segment"); + } + + var poly = new SOG.Polycurve(); + 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. + + poly.segments.Add(curve); + } + + return poly; + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/BraceToSpeckleConverter.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/BraceToSpeckleConverter.cs new file mode 100644 index 0000000000..ccae249fe5 --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/BraceToSpeckleConverter.cs @@ -0,0 +1,47 @@ +using Speckle.Converters.Common.Objects; +using Speckle.Core.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; + + public BraceToSpeckleConverter(ITypedConverter beamConverter) + { + _beamConverter = beamConverter; + } + + 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, + }; + + var dynamicProps = beam.GetMembers(DynamicBaseMemberType.Dynamic); + + foreach (var dp in dynamicProps) + { + brace[dp.Key] = dp.Value; + } + + return brace; + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/ColumnConversionToSpeckle.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/ColumnConversionToSpeckle.cs new file mode 100644 index 0000000000..1786febd39 --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/ColumnConversionToSpeckle.cs @@ -0,0 +1,139 @@ +using Autodesk.Revit.DB; +using Objects; +using Objects.BuiltElements.Revit; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Converters.RevitShared.Helpers; +using Speckle.Core.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 IRevitConversionContextStack _contextStack; + private readonly ParameterObjectAssigner _parameterObjectAssigner; + + public ColumnConversionToSpeckle( + ITypedConverter locationConverter, + ITypedConverter levelConverter, + ParameterValueExtractor parameterValueExtractor, + DisplayValueExtractor displayValueExtractor, + IRevitConversionContextStack contextStack, + ParameterObjectAssigner parameterObjectAssigner + ) + { + _locationConverter = locationConverter; + _levelConverter = levelConverter; + _parameterValueExtractor = parameterValueExtractor; + _displayValueExtractor = displayValueExtractor; + _contextStack = contextStack; + _parameterObjectAssigner = parameterObjectAssigner; + } + + public RevitColumn Convert(DB.FamilyInstance target) + { + FamilySymbol symbol = (FamilySymbol)target.Document.GetElement(target.GetTypeId()); + + RevitColumn speckleColumn = + new() { family = symbol.FamilyName, type = target.Document.GetElement(target.GetTypeId()).Name }; + + if ( + _parameterValueExtractor.TryGetValueAsDocumentObject( + target, + BuiltInParameter.FAMILY_BASE_LEVEL_PARAM, + out var level + ) + ) + { + speckleColumn.level = _levelConverter.Convert(level.NotNull()); + } + if ( + _parameterValueExtractor.TryGetValueAsDocumentObject( + target, + BuiltInParameter.FAMILY_TOP_LEVEL_PARAM, + out var topLevel + ) + ) + { + speckleColumn.topLevel = _levelConverter.Convert(topLevel.NotNull()); + } + + 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.facingFlipped = target.FacingFlipped; + speckleColumn.handFlipped = target.HandFlipped; + speckleColumn.isSlanted = target.IsSlantedColumn; + + if (target.Location is LocationPoint locationPoint) + { + speckleColumn.rotation = locationPoint.Rotation; + } + + speckleColumn.baseLine = + GetBaseCurve(target, speckleColumn.topLevel?.elevation ?? -1, speckleColumn.topOffset) + ?? throw new SpeckleConversionException("Unable to find a valid baseCurve for column"); + + speckleColumn.displayValue = _displayValueExtractor.GetDisplayValue(target); + + _parameterObjectAssigner.AssignParametersToBase(target, speckleColumn); + + 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( + basePoint, + new SOG.Point(basePoint.x, basePoint.y, topLevelElevation + topLevelOffset, _contextStack.Current.SpeckleUnits), + _contextStack.Current.SpeckleUnits + ); + } + + return null; + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/ArcToSpeckleConverter.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/ArcToSpeckleConverter.cs new file mode 100644 index 0000000000..d6774a710b --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/ArcToSpeckleConverter.cs @@ -0,0 +1,59 @@ +using Objects.Primitive; +using Speckle.Converters.Common.Objects; +using Speckle.Converters.RevitShared.Helpers; +using Speckle.Converters.RevitShared.Services; + +namespace Speckle.Converters.RevitShared.ToSpeckle; + +public class ArcToSpeckleConverter : ITypedConverter +{ + private readonly IRevitConversionContextStack _contextStack; + private readonly ITypedConverter _xyzToPointConverter; + private readonly ITypedConverter _planeConverter; + private readonly ScalingServiceToSpeckle _scalingService; + + public ArcToSpeckleConverter( + IRevitConversionContextStack contextStack, + ITypedConverter xyzToPointConverter, + ITypedConverter planeConverter, + ScalingServiceToSpeckle scalingService + ) + { + _contextStack = contextStack; + _xyzToPointConverter = xyzToPointConverter; + _planeConverter = planeConverter; + _scalingService = scalingService; + } + + public SOG.Arc Convert(DB.Arc target) + { + // see https://forums.autodesk.com/t5/revit-api-forum/how-to-retrieve-startangle-and-endangle-of-arc-object/td-p/7637128 + var arcPlane = DB.Plane.CreateByOriginAndBasis(target.Center, target.XDirection, target.YDirection); + DB.XYZ center = target.Center; + + DB.XYZ dir0 = (target.GetEndPoint(0) - center).Normalize(); + DB.XYZ dir1 = (target.GetEndPoint(1) - center).Normalize(); + + DB.XYZ start = target.Evaluate(0, true); + DB.XYZ end = target.Evaluate(1, true); + DB.XYZ mid = target.Evaluate(0.5, true); + + double startAngle = target.XDirection.AngleOnPlaneTo(dir0, target.Normal); + double endAngle = target.XDirection.AngleOnPlaneTo(dir1, target.Normal); + + return new SOG.Arc() + { + plane = _planeConverter.Convert(arcPlane), + radius = _scalingService.ScaleLength(target.Radius), + startAngle = startAngle, + endAngle = endAngle, + angleRadians = endAngle - startAngle, + units = _contextStack.Current.SpeckleUnits, + endPoint = _xyzToPointConverter.Convert(end), + startPoint = _xyzToPointConverter.Convert(start), + midPoint = _xyzToPointConverter.Convert(mid), + length = _scalingService.ScaleLength(target.Length), + domain = new Interval(target.GetEndParameter(0), target.GetEndParameter(1)) + }; + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/BoundingBoxXYZToSpeckleConverter.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/BoundingBoxXYZToSpeckleConverter.cs new file mode 100644 index 0000000000..a891579ad5 --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/BoundingBoxXYZToSpeckleConverter.cs @@ -0,0 +1,49 @@ +using Objects.Primitive; +using Speckle.Converters.Common.Objects; +using Speckle.Converters.RevitShared.Helpers; + +namespace Speckle.Converters.RevitShared.ToSpeckle; + +public class BoundingBoxXYZToSpeckleConverter : ITypedConverter +{ + private readonly IRevitConversionContextStack _contextStack; + private readonly ITypedConverter _xyzToPointConverter; + private readonly ITypedConverter _planeConverter; + + public BoundingBoxXYZToSpeckleConverter( + IRevitConversionContextStack contextStack, + ITypedConverter xyzToPointConverter, + ITypedConverter planeConverter + ) + { + _contextStack = contextStack; + _xyzToPointConverter = xyzToPointConverter; + _planeConverter = planeConverter; + } + + public SOG.Box Convert(DB.BoundingBoxXYZ target) + { + // convert min and max pts to speckle first + var min = _xyzToPointConverter.Convert(target.Min); + var max = _xyzToPointConverter.Convert(target.Max); + + // get the base plane of the bounding box from the transform + var transform = target.Transform; + var plane = DB.Plane.CreateByOriginAndBasis( + transform.Origin, + transform.BasisX.Normalize(), + transform.BasisY.Normalize() + ); + + var box = new SOG.Box() + { + xSize = new Interval(min.x, max.x), + ySize = new Interval(min.y, max.y), + zSize = new Interval(min.z, max.z), + basePlane = _planeConverter.Convert(plane), + units = _contextStack.Current.SpeckleUnits + }; + + return box; + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/CircleToSpeckleConverter.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/CircleToSpeckleConverter.cs new file mode 100644 index 0000000000..7abdc08cfc --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/CircleToSpeckleConverter.cs @@ -0,0 +1,40 @@ +using Speckle.Converters.Common.Objects; +using Speckle.Converters.RevitShared.Helpers; +using Speckle.Converters.RevitShared.Services; + +namespace Speckle.Converters.RevitShared.ToSpeckle; + +public class CircleToSpeckleConverter : ITypedConverter +{ + private readonly IRevitConversionContextStack _contextStack; + private readonly ITypedConverter _planeConverter; + private readonly ScalingServiceToSpeckle _scalingService; + + public CircleToSpeckleConverter( + IRevitConversionContextStack contextStack, + ITypedConverter planeConverter, + ScalingServiceToSpeckle scalingService + ) + { + _contextStack = contextStack; + _planeConverter = planeConverter; + _scalingService = scalingService; + } + + public SOG.Circle Convert(DB.Arc target) + { + // POC: should we check for arc of 360 and throw? Original CircleToSpeckle did not do this. + + // see https://forums.autodesk.com/t5/revit-api-forum/how-to-retrieve-startangle-and-endangle-of-arc-object/td-p/7637128 + var arcPlane = DB.Plane.CreateByNormalAndOrigin(target.Normal, target.Center); + var c = new SOG.Circle() + { + plane = _planeConverter.Convert(arcPlane), + radius = _scalingService.ScaleLength(target.Radius), + units = _contextStack.Current.SpeckleUnits, + length = _scalingService.ScaleLength(target.Length) + }; + + return c; + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/CurveArrArrayToSpecklePolycurveConverter.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/CurveArrArrayToSpecklePolycurveConverter.cs new file mode 100644 index 0000000000..4644bc1435 --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/CurveArrArrayToSpecklePolycurveConverter.cs @@ -0,0 +1,34 @@ +using Autodesk.Revit.DB; +using Objects.Geometry; +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.RevitShared.Raw; + +internal sealed class CurveArrArrayToSpecklePolycurveConverter : ITypedConverter> +{ + private readonly ITypedConverter _curveArrayConverter; + + public CurveArrArrayToSpecklePolycurveConverter(ITypedConverter curveArrayConverter) + { + _curveArrayConverter = curveArrayConverter; + } + + public List Convert(CurveArrArray target) + { + List polycurves = new(); + foreach (var curveArray in GetCurveArrays(target)) + { + polycurves.Add(_curveArrayConverter.Convert(curveArray)); + } + + return polycurves; + } + + private IEnumerable GetCurveArrays(DB.CurveArrArray curveArrArray) + { + for (var i = 0; i < curveArrArray.Size; i++) + { + yield return curveArrArray.get_Item(i); + } + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/CurveArrayConversionToSpeckle.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/CurveArrayConversionToSpeckle.cs new file mode 100644 index 0000000000..8ec80fe9f7 --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/CurveArrayConversionToSpeckle.cs @@ -0,0 +1,40 @@ +using Autodesk.Revit.DB; +using Objects; +using Objects.Geometry; +using Speckle.Converters.Common.Objects; +using Speckle.Converters.RevitShared.Helpers; +using Speckle.Converters.RevitShared.Services; + +namespace Speckle.Converters.RevitShared.ToSpeckle; + +public sealed class CurveArrayConversionToSpeckle : ITypedConverter +{ + private readonly IRevitConversionContextStack _contextStack; + private readonly ScalingServiceToSpeckle _scalingService; + private readonly ITypedConverter _curveConverter; + + public CurveArrayConversionToSpeckle( + IRevitConversionContextStack contextStack, + ScalingServiceToSpeckle scalingService, + ITypedConverter curveConverter + ) + { + _contextStack = contextStack; + _scalingService = scalingService; + _curveConverter = curveConverter; + } + + public Polycurve Convert(CurveArray target) + { + List curves = target.Cast().ToList(); + + return new Polycurve() + { + units = _contextStack.Current.SpeckleUnits, + closed = + curves.First().GetEndPoint(0).DistanceTo(curves.Last().GetEndPoint(1)) < RevitConversionContextStack.TOLERANCE, + length = _scalingService.ScaleLength(curves.Sum(x => x.Length)), + segments = curves.Select(x => _curveConverter.Convert(x)).ToList() + }; + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/CurveConversionToSpeckle.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/CurveConversionToSpeckle.cs new file mode 100644 index 0000000000..0b034a779c --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/CurveConversionToSpeckle.cs @@ -0,0 +1,47 @@ +using Objects; +using Speckle.Converters.Common.Objects; +using Speckle.Converters.Common; + +namespace Speckle.Converters.RevitShared.ToSpeckle; + +public class CurveConversionToSpeckle : ITypedConverter +{ + private readonly ITypedConverter _lineConverter; + private readonly ITypedConverter _arcConverter; + private readonly ITypedConverter _circleConverter; + private readonly ITypedConverter _ellipseConverter; + private readonly ITypedConverter _nurbsConverter; + private readonly ITypedConverter _hermiteConverter; // POC: should this be ICurve? + + public CurveConversionToSpeckle( + ITypedConverter lineConverter, + ITypedConverter arcConverter, + ITypedConverter circleConverter, + ITypedConverter ellipseConverter, + ITypedConverter nurbsConverter, + ITypedConverter hermiteConverter + ) + { + _lineConverter = lineConverter; + _arcConverter = arcConverter; + _circleConverter = circleConverter; + _ellipseConverter = ellipseConverter; + _nurbsConverter = nurbsConverter; + _hermiteConverter = hermiteConverter; + } + + public ICurve Convert(DB.Curve target) + { + return target switch + { + DB.Line line => _lineConverter.Convert(line), + // POC: are maybe arc.IsCyclic ? + DB.Arc arc => arc.IsClosed ? _circleConverter.Convert(arc) : _arcConverter.Convert(arc), + DB.Ellipse ellipse => _ellipseConverter.Convert(ellipse), + DB.NurbSpline nurbs => _nurbsConverter.Convert(nurbs), + DB.HermiteSpline hermite => _hermiteConverter.Convert(hermite), + + _ => throw new SpeckleConversionException($"Unsupported curve type {target.GetType()}") + }; + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/EllipseToSpeckleConverter.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/EllipseToSpeckleConverter.cs new file mode 100644 index 0000000000..96ca62f8f1 --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/EllipseToSpeckleConverter.cs @@ -0,0 +1,45 @@ +using Objects.Primitive; +using Speckle.Converters.Common.Objects; +using Speckle.Converters.RevitShared.Helpers; +using Speckle.Converters.RevitShared.Services; + +namespace Speckle.Converters.RevitShared.ToSpeckle; + +public class EllipseToSpeckleConverter : ITypedConverter +{ + private readonly IRevitConversionContextStack _contextStack; + private readonly ITypedConverter _planeConverter; + private readonly ScalingServiceToSpeckle _scalingService; + + public EllipseToSpeckleConverter( + IRevitConversionContextStack contextStack, + ITypedConverter planeConverter, + ScalingServiceToSpeckle scalingService + ) + { + _contextStack = contextStack; + _planeConverter = planeConverter; + _scalingService = scalingService; + } + + public SOG.Ellipse Convert(DB.Ellipse target) + { + using (DB.Plane basePlane = DB.Plane.CreateByOriginAndBasis(target.Center, target.XDirection, target.YDirection)) + { + var trim = target.IsBound ? new Interval(target.GetEndParameter(0), target.GetEndParameter(1)) : null; + + return new SOG.Ellipse() + { + plane = _planeConverter.Convert(basePlane), + // POC: scale length correct? seems right? + firstRadius = _scalingService.ScaleLength(target.RadiusX), + secondRadius = _scalingService.ScaleLength(target.RadiusY), + // POC: original EllipseToSpeckle() method was setting this twice + domain = new Interval(0, 1), + trimDomain = trim, + length = _scalingService.ScaleLength(target.Length), + units = _contextStack.Current.SpeckleUnits + }; + } + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/HermiteSplineToSpeckleConverter.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/HermiteSplineToSpeckleConverter.cs new file mode 100644 index 0000000000..5aa2a07ed2 --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/HermiteSplineToSpeckleConverter.cs @@ -0,0 +1,19 @@ +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.RevitShared.ToSpeckle; + +public class HerminteSplitToSpeckleConverter : ITypedConverter +{ + private readonly ITypedConverter _splineConverter; + + public HerminteSplitToSpeckleConverter(ITypedConverter splineConverter) + { + _splineConverter = splineConverter; + } + + public SOG.Curve Convert(DB.HermiteSpline target) + { + var nurbs = DB.NurbSpline.Create(target); + return _splineConverter.Convert(nurbs); + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/LineConversionToSpeckle.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/LineConversionToSpeckle.cs new file mode 100644 index 0000000000..2405cf0b22 --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/LineConversionToSpeckle.cs @@ -0,0 +1,34 @@ +using Objects.Primitive; +using Speckle.Converters.Common.Objects; +using Speckle.Converters.RevitShared.Helpers; +using Speckle.Converters.RevitShared.Services; + +namespace Speckle.Converters.RevitShared.ToSpeckle; + +public class LineConversionToSpeckle : ITypedConverter +{ + private readonly IRevitConversionContextStack _contextStack; + private readonly ITypedConverter _xyzToPointConverter; + private readonly ScalingServiceToSpeckle _scalingService; + + public LineConversionToSpeckle( + IRevitConversionContextStack contextStack, + ITypedConverter xyzToPointConverter, + ScalingServiceToSpeckle scalingService + ) + { + _contextStack = contextStack; + _xyzToPointConverter = xyzToPointConverter; + _scalingService = scalingService; + } + + public SOG.Line Convert(DB.Line target) => + new() + { + units = _contextStack.Current.SpeckleUnits, + start = _xyzToPointConverter.Convert(target.GetEndPoint(0)), + end = _xyzToPointConverter.Convert(target.GetEndPoint(1)), + domain = new Interval(target.GetEndParameter(0), target.GetEndParameter(1)), + length = _scalingService.ScaleLength(target.Length) + }; +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/MeshByMaterialDictionaryToSpeckle.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/MeshByMaterialDictionaryToSpeckle.cs new file mode 100644 index 0000000000..29b0bd9f98 --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/MeshByMaterialDictionaryToSpeckle.cs @@ -0,0 +1,115 @@ +using Objects.Other; +using Speckle.Converters.Common.Objects; +using Speckle.Converters.RevitShared.Helpers; + +namespace Speckle.Converters.RevitShared.ToSpeckle; + +public class MeshByMaterialDictionaryToSpeckle + : ITypedConverter>, List> +{ + private readonly IRevitConversionContextStack _contextStack; + private readonly ITypedConverter _xyzToPointConverter; + private readonly ITypedConverter _materialConverter; + + public MeshByMaterialDictionaryToSpeckle( + ITypedConverter materialConverter, + IRevitConversionContextStack contextStack, + ITypedConverter xyzToPointConverter + ) + { + _materialConverter = materialConverter; + _contextStack = contextStack; + _xyzToPointConverter = xyzToPointConverter; + } + + /// + /// Converts a dictionary of Revit meshes, where key is MaterialId, into a list of Speckle meshes. + /// + /// A dictionary with DB.ElementId keys and List of DB.Mesh values. + /// + /// Returns a list of objects where each mesh represents one unique material in the input dictionary. + /// + /// + /// Be aware that this method internally creates a new instance of for each unique material in the input dictionary. + /// These meshes are created with an initial capacity based on the size of the vertex and face arrays to avoid unnecessary resizing. + /// Also note that, for each unique material, the method tries to retrieve the related DB.Material from the current document and convert it. If the conversion is successful, + /// the material is added to the corresponding Speckle mesh. If the conversion fails, the operation simply continues without the material. + /// + public List Convert(Dictionary> target) + { + var result = new List(target.Keys.Count); + + foreach (var meshData in target) + { + DB.ElementId materialId = meshData.Key; + List meshes = meshData.Value; + + // We compute the final size of the arrays to prevent unnecessary resizing. + (int verticesSize, int facesSize) = GetVertexAndFaceListSize(meshes); + + // Initialise a new empty mesh with units and material + var speckleMesh = new SOG.Mesh( + new List(verticesSize), + new List(facesSize), + units: _contextStack.Current.SpeckleUnits + ); + + var doc = _contextStack.Current.Document; + if (doc.GetElement(materialId) is DB.Material material) + { + speckleMesh["renderMaterial"] = _materialConverter.Convert(material); + } + + // Append the revit mesh data to the speckle mesh + foreach (var mesh in meshes) + { + AppendToSpeckleMesh(mesh, speckleMesh); + } + + result.Add(speckleMesh); + } + + return result; + } + + private void AppendToSpeckleMesh(DB.Mesh mesh, SOG.Mesh speckleMesh) + { + int faceIndexOffset = speckleMesh.vertices.Count / 3; + + foreach (var vert in mesh.Vertices) + { + var (x, y, z) = _xyzToPointConverter.Convert(vert); + speckleMesh.vertices.Add(x); + speckleMesh.vertices.Add(y); + speckleMesh.vertices.Add(z); + } + + for (int i = 0; i < mesh.NumTriangles; i++) + { + var triangle = mesh.get_Triangle(i); + + speckleMesh.faces.Add(3); // TRIANGLE flag + speckleMesh.faces.Add((int)triangle.get_Index(0) + faceIndexOffset); + speckleMesh.faces.Add((int)triangle.get_Index(1) + faceIndexOffset); + speckleMesh.faces.Add((int)triangle.get_Index(2) + faceIndexOffset); + } + } + + private static (int vertexCount, int) GetVertexAndFaceListSize(List meshes) + { + int numberOfVertices = 0; + int numberOfFaces = 0; + foreach (var mesh in meshes) + { + if (mesh == null) + { + continue; + } + + numberOfVertices += mesh.Vertices.Count * 3; + numberOfFaces += mesh.NumTriangles * 4; + } + + return (numberOfVertices, numberOfFaces); + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/MeshConversionToSpeckle.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/MeshConversionToSpeckle.cs new file mode 100644 index 0000000000..2f6c494ba0 --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/MeshConversionToSpeckle.cs @@ -0,0 +1,84 @@ +using Objects.Other; +using Speckle.Converters.Common.Objects; +using Speckle.Converters.RevitShared.Helpers; + +namespace Speckle.Converters.RevitShared.ToSpeckle; + +public class MeshConversionToSpeckle : ITypedConverter +{ + private readonly ITypedConverter _xyzToPointConverter; + private readonly ITypedConverter _materialConverter; + private readonly IRevitConversionContextStack _contextStack; + + public MeshConversionToSpeckle( + IRevitConversionContextStack contextStack, + ITypedConverter xyzToPointConverter, + ITypedConverter materialConverter + ) + { + _contextStack = contextStack; + _xyzToPointConverter = xyzToPointConverter; + _materialConverter = materialConverter; + } + + public SOG.Mesh Convert(DB.Mesh target) + { + var doc = _contextStack.Current.Document; + + List vertices = GetSpeckleMeshVertexData(target); + List faces = GetSpeckleMeshFaceData(target); + + RenderMaterial? speckleMaterial = null; + if (doc.GetElement(target.MaterialElementId) is DB.Material revitMaterial) + { + speckleMaterial = _materialConverter.Convert(revitMaterial); + } + + return new SOG.Mesh(vertices, faces, units: _contextStack.Current.SpeckleUnits) + { + ["renderMaterial"] = speckleMaterial + }; + } + + private List GetSpeckleMeshVertexData(DB.Mesh target) + { + var vertices = new List(target.Vertices.Count * 3); + + foreach (var vert in target.Vertices) + { + vertices.AddRange(_xyzToPointConverter.Convert(vert).ToList()); + } + + return vertices; + } + + private List GetSpeckleMeshFaceData(DB.Mesh target) + { + var faces = new List(target.NumTriangles * 4); + for (int i = 0; i < target.NumTriangles; i++) + { + var triangle = target.get_Triangle(i); + faces.AddRange(GetMeshTriangleData(triangle)); + } + + return faces; + } + + /// + /// Retrieves the triangle data of a mesh to be stored in a Speckle Mesh faces property. + /// + /// The mesh triangle object. + /// A list of integers representing the triangle data. + /// + /// Output format is a 4 item list with format [3, v1, v2, v3]; where the first item is the triangle flag (for speckle) + /// and the 3 following numbers are the indices of each vertex in the vertex list. + /// + private IReadOnlyList GetMeshTriangleData(DB.MeshTriangle triangle) => + new[] + { + 3, // The TRIANGLE flag in speckle + (int)triangle.get_Index(0), + (int)triangle.get_Index(1), + (int)triangle.get_Index(2) + }; +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/NurbsSplineToSpeckleConverter.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/NurbsSplineToSpeckleConverter.cs new file mode 100644 index 0000000000..7229f639e3 --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/NurbsSplineToSpeckleConverter.cs @@ -0,0 +1,56 @@ +using Objects.Primitive; +using Speckle.Converters.Common.Objects; +using Speckle.Converters.RevitShared.Helpers; +using Speckle.Converters.RevitShared.Services; + +namespace Speckle.Converters.RevitShared.ToSpeckle; + +public class NurbsSplineToSpeckleConverter : ITypedConverter +{ + private readonly IRevitVersionConversionHelper _conversionHelper; + private readonly IRevitConversionContextStack _contextStack; + private readonly ITypedConverter _xyzToPointConverter; + private readonly ScalingServiceToSpeckle _scalingService; + + public NurbsSplineToSpeckleConverter( + IRevitVersionConversionHelper conversionHelper, + IRevitConversionContextStack contextStack, + ITypedConverter xyzToPointConverter, + ScalingServiceToSpeckle scalingService + ) + { + _conversionHelper = conversionHelper; + _contextStack = contextStack; + _xyzToPointConverter = xyzToPointConverter; + _scalingService = scalingService; + } + + public SOG.Curve Convert(DB.NurbSpline target) + { + var units = _contextStack.Current.SpeckleUnits; + + var points = new List(); + foreach (var p in target.CtrlPoints) + { + var point = _xyzToPointConverter.Convert(p); + points.AddRange(new List { point.x, point.y, point.z }); + } + + var coords = target.Tessellate().SelectMany(xyz => _xyzToPointConverter.Convert(xyz).ToList()).ToList(); + + return new SOG.Curve() + { + weights = target.Weights.Cast().ToList(), + points = points, + knots = target.Knots.Cast().ToList(), + degree = target.Degree, + //speckleCurve.periodic = revitCurve.Period; // POC: already commented out, remove? + rational = target.isRational, + closed = _conversionHelper.IsCurveClosed(target), + units = units, + domain = new Interval(target.GetEndParameter(0), target.GetEndParameter(1)), + length = _scalingService.ScaleLength(target.Length), + displayValue = new SOG.Polyline(coords, units) + }; + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/PlaneToSpeckleConverter.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/PlaneToSpeckleConverter.cs new file mode 100644 index 0000000000..27cef7bab6 --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/PlaneToSpeckleConverter.cs @@ -0,0 +1,32 @@ +using Speckle.Converters.Common.Objects; +using Speckle.Converters.RevitShared.Helpers; + +namespace Speckle.Converters.RevitShared.ToSpeckle; + +public class PlaneToSpeckleConverter : ITypedConverter +{ + private readonly IRevitConversionContextStack _contextStack; + private readonly ITypedConverter _xyzToPointConverter; + private readonly ITypedConverter _xyzToVectorConverter; + + public PlaneToSpeckleConverter( + IRevitConversionContextStack contextStack, + ITypedConverter xyzToPointConverter, + ITypedConverter xyzToVectorConverter + ) + { + _contextStack = contextStack; + _xyzToPointConverter = xyzToPointConverter; + _xyzToVectorConverter = xyzToVectorConverter; + } + + public SOG.Plane Convert(DB.Plane target) + { + var origin = _xyzToPointConverter.Convert(target.Origin); + var normal = _xyzToVectorConverter.Convert(target.Normal); + var xdir = _xyzToVectorConverter.Convert(target.XVec); + var ydir = _xyzToVectorConverter.Convert(target.YVec); + + return new SOG.Plane(origin, normal, xdir, ydir, _contextStack.Current.SpeckleUnits); + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/PointCloudToSpeckleConverter.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/PointCloudToSpeckleConverter.cs new file mode 100644 index 0000000000..ab86301cd2 --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/PointCloudToSpeckleConverter.cs @@ -0,0 +1,47 @@ +using Speckle.Converters.Common.Objects; +using Speckle.Converters.RevitShared.Helpers; + +namespace Speckle.Converters.RevitShared.ToSpeckle; + +public class PointCloudToSpeckleConverter : ITypedConverter +{ + private readonly IRevitConversionContextStack _contextStack; + private readonly ITypedConverter _xyzToPointConverter; + private readonly ITypedConverter _boundingBoxConverter; + + public PointCloudToSpeckleConverter( + IRevitConversionContextStack contextStack, + ITypedConverter xyzToPointConverter, + ITypedConverter boundingBoxConverter + ) + { + _contextStack = contextStack; + _xyzToPointConverter = xyzToPointConverter; + _boundingBoxConverter = boundingBoxConverter; + } + + public SOG.Pointcloud Convert(DB.PointCloudInstance target) + { + var boundingBox = target.get_BoundingBox(null); + 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))) + .SelectMany(o => new List() { o.x, o.y, o.z }) + .ToList(), + colors = points.Select(o => o.Color).ToList(), + units = _contextStack.Current.SpeckleUnits, + bbox = _boundingBoxConverter.Convert(boundingBox) + }; + + return specklePointCloud; + } + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/PointConversionToSpeckle.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/PointConversionToSpeckle.cs new file mode 100644 index 0000000000..8f6ca703db --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/PointConversionToSpeckle.cs @@ -0,0 +1,15 @@ +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.RevitShared.ToSpeckle; + +public class PointConversionToSpeckle : ITypedConverter +{ + private readonly ITypedConverter _xyzToPointConverter; + + public PointConversionToSpeckle(ITypedConverter xyzToPointConverter) + { + _xyzToPointConverter = xyzToPointConverter; + } + + public SOG.Point Convert(DB.Point target) => _xyzToPointConverter.Convert(target.Coord); +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/PolylineToSpeckleConverter.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/PolylineToSpeckleConverter.cs new file mode 100644 index 0000000000..5efb22e278 --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/PolylineToSpeckleConverter.cs @@ -0,0 +1,25 @@ +using Speckle.Converters.Common.Objects; +using Speckle.Converters.RevitShared.Helpers; + +namespace Speckle.Converters.RevitShared.ToSpeckle; + +public class PolylineToSpeckleConverter : ITypedConverter +{ + private readonly IRevitConversionContextStack _contextStack; + private readonly ITypedConverter _xyzToPointConverter; + + public PolylineToSpeckleConverter( + IRevitConversionContextStack contextStack, + ITypedConverter xyzToPointConverter + ) + { + _contextStack = contextStack; + _xyzToPointConverter = xyzToPointConverter; + } + + public SOG.Polyline Convert(DB.PolyLine target) + { + var coords = target.GetCoordinates().SelectMany(coord => _xyzToPointConverter.Convert(coord).ToList()).ToList(); + return new SOG.Polyline(coords, _contextStack.Current.SpeckleUnits); + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/SolidConversionToSpeckle.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/SolidConversionToSpeckle.cs new file mode 100644 index 0000000000..e848d3a843 --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/SolidConversionToSpeckle.cs @@ -0,0 +1,55 @@ +using Speckle.Converters.Common.Objects; +using Speckle.Converters.RevitShared.Helpers; + +namespace Speckle.Converters.RevitShared.ToSpeckle; + +/// +/// Solid conversion is a one->many. For each material used in the solid, a mesh will be returned to reduce the amount of instances created. +/// +public class SolidConversionToSpeckle : IRawConversion> +{ + private readonly RevitConversionContextStack _contextStack; + private readonly IRawConversion>, List> _meshByMaterialConverter; + + public SolidConversionToSpeckle( + RevitConversionContextStack contextStack, + IRawConversion>, List> meshByMaterialConverter + ) + { + _contextStack = contextStack; + _meshByMaterialConverter = meshByMaterialConverter; + } + + /// + /// Converts the input object into a list of . + /// + /// The input object to be converted. + /// + /// A list of objects that represent the input object. Each mesh in the list corresponds to a different material in the original solid. + /// + /// + /// This conversion process first triangulates the input solid by material, and then converts the result to raw meshes individually. + /// Be aware that this operation might be computationally intensive for complex solids, due to the need for triangulation. + /// + public List RawConvert(DB.Solid target) + { + var meshesByMaterial = GetTriangulatedMeshesFromSolidByMaterial(target); + return _meshByMaterialConverter.RawConvert(meshesByMaterial); + } + + private Dictionary> GetTriangulatedMeshesFromSolidByMaterial(DB.Solid solid) + { + var result = new Dictionary>(); + foreach (DB.Face face in solid.Faces) + { + if (!result.ContainsKey(face.MaterialElementId)) + { + result[face.MaterialElementId] = new List(); + } + + result[face.MaterialElementId].Add(face.Triangulate()); + } + + return result; + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/VectorToSpeckleConverter.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/VectorToSpeckleConverter.cs new file mode 100644 index 0000000000..e97cf962f4 --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/VectorToSpeckleConverter.cs @@ -0,0 +1,33 @@ +using Speckle.Converters.Common.Objects; +using Speckle.Converters.RevitShared.Services; + +namespace Speckle.Converters.RevitShared.ToSpeckle; + +public class VectorToSpeckleConverter : ITypedConverter +{ + private readonly IReferencePointConverter _referencePointConverter; + private readonly ScalingServiceToSpeckle _scalingService; + + public VectorToSpeckleConverter( + IReferencePointConverter referencePointConverter, + ScalingServiceToSpeckle scalingService + ) + { + _referencePointConverter = referencePointConverter; + _scalingService = scalingService; + } + + public SOG.Vector Convert(DB.XYZ target) + { + // POC: originally had a concept of not transforming, but this was + // optional arg defaulting to false - removing the argument appeared to break nothing + DB.XYZ extPt = _referencePointConverter.ConvertToExternalCoordindates(target, false); + var pointToSpeckle = new SOG.Vector( + _scalingService.ScaleLength(extPt.X), + _scalingService.ScaleLength(extPt.Y), + _scalingService.ScaleLength(extPt.Z) + ); + + return pointToSpeckle; + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/XyzConversionToPoint.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/XyzConversionToPoint.cs new file mode 100644 index 0000000000..bf250d06c3 --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/XyzConversionToPoint.cs @@ -0,0 +1,31 @@ +using Speckle.Converters.Common.Objects; +using Speckle.Converters.RevitShared.Helpers; +using Speckle.Converters.RevitShared.Services; + +namespace Speckle.Converters.RevitShared.ToSpeckle; + +public class XyzConversionToPoint : ITypedConverter +{ + private readonly ScalingServiceToSpeckle _toSpeckleScalingService; + private readonly IRevitConversionContextStack _contextStack; + + public XyzConversionToPoint( + ScalingServiceToSpeckle toSpeckleScalingService, + IRevitConversionContextStack contextStack + ) + { + _toSpeckleScalingService = toSpeckleScalingService; + _contextStack = contextStack; + } + + public SOG.Point Convert(DB.XYZ target) + { + var pointToSpeckle = new SOG.Point( + _toSpeckleScalingService.ScaleLength(target.X), + _toSpeckleScalingService.ScaleLength(target.Y), + _toSpeckleScalingService.ScaleLength(target.Z), + _contextStack.Current.SpeckleUnits + ); + return pointToSpeckle; + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/LocationConversionToSpeckle.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/LocationConversionToSpeckle.cs new file mode 100644 index 0000000000..29d3378103 --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/LocationConversionToSpeckle.cs @@ -0,0 +1,34 @@ +using Objects; +using Speckle.Converters.Common.Objects; +using Speckle.Converters.Common; +using Speckle.Core.Models; + +namespace Speckle.Converters.RevitShared.ToSpeckle; + +public class LocationConversionToSpeckle : 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( + ITypedConverter curveConverter, + ITypedConverter xyzConverter + ) + { + _curveConverter = curveConverter; + _xyzConverter = xyzConverter; + } + + public Base Convert(DB.Location target) + { + return target switch + { + DB.LocationCurve curve => (_curveConverter.Convert(curve.Curve) as Base)!, // POC: ICurve and Base are not related but we know they must be, had to soft cast and then !. + DB.LocationPoint point => _xyzConverter.Convert(point.Point), + _ => throw new SpeckleConversionException($"Unexpected location type {target.GetType()}") + }; + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/MaterialConversionToSpeckle.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/MaterialConversionToSpeckle.cs new file mode 100644 index 0000000000..f7f48974f7 --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/MaterialConversionToSpeckle.cs @@ -0,0 +1,20 @@ +using Objects.Other; +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.RevitShared.ToSpeckle; + +public class MaterialConversionToSpeckle : ITypedConverter +{ + public RenderMaterial Convert(DB.Material target) => + // POC: not sure we should be pulling in System.Drawing - + // maybe this isn't a problem as it's part of the netstandard Fwk + // ideally we'd have serialiser of our own colour class, i.e. to serialise to an uint + new() + { + name = target.Name, + opacity = 1 - target.Transparency / 100d, + diffuse = System.Drawing.Color.FromArgb(target.Color.Red, target.Color.Green, target.Color.Blue).ToArgb() + //metalness = revitMaterial.Shininess / 128d, //Looks like these are not valid conversions + //roughness = 1 - (revitMaterial.Smoothness / 100d) + }; +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/ModelCurveArrArrayToSpeckleConverter.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/ModelCurveArrArrayToSpeckleConverter.cs new file mode 100644 index 0000000000..d294360b2d --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/ModelCurveArrArrayToSpeckleConverter.cs @@ -0,0 +1,28 @@ +using Autodesk.Revit.DB; +using Objects.Geometry; +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.RevitShared.Raw; + +internal sealed class ModelCurveArrArrayConverterToSpeckle : ITypedConverter +{ + private readonly ITypedConverter _modelCurveArrayConverter; + + public ModelCurveArrArrayConverterToSpeckle(ITypedConverter modelCurveArrayConverter) + { + _modelCurveArrayConverter = modelCurveArrayConverter; + } + + public SOG.Polycurve[] Convert(ModelCurveArrArray target) + { + var polycurves = new Polycurve[target.Size]; + var revitArrays = target.Cast().ToArray(); + + for (int i = 0; i < polycurves.Length; i++) + { + polycurves[i] = _modelCurveArrayConverter.Convert(revitArrays[i]); + } + + return polycurves; + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/ModelCurveArrayToSpeckleConverter.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/ModelCurveArrayToSpeckleConverter.cs new file mode 100644 index 0000000000..5bfd4d11bb --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/ModelCurveArrayToSpeckleConverter.cs @@ -0,0 +1,46 @@ +using Objects; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Converters.RevitShared.Helpers; +using Speckle.Converters.RevitShared.Services; + +namespace Speckle.Converters.RevitShared.Raw; + +internal sealed class ModelCurveArrayToSpeckleConverter : ITypedConverter +{ + private readonly IRevitConversionContextStack _contextStack; + private readonly ScalingServiceToSpeckle _scalingService; + private readonly ITypedConverter _curveConverter; + + public ModelCurveArrayToSpeckleConverter( + IRevitConversionContextStack contextStack, + ScalingServiceToSpeckle scalingService, + ITypedConverter curveConverter + ) + { + _contextStack = contextStack; + _scalingService = scalingService; + _curveConverter = curveConverter; + } + + public SOG.Polycurve Convert(DB.ModelCurveArray target) + { + SOG.Polycurve polycurve = new(); + var curves = target.Cast().Select(mc => mc.GeometryCurve).ToArray(); + + if (curves.Length == 0) + { + throw new SpeckleConversionException($"Expected {target} to have at least 1 curve"); + } + + var start = curves[0].GetEndPoint(0); + var end = curves[^1].GetEndPoint(1); + polycurve.units = _contextStack.Current.SpeckleUnits; + polycurve.closed = start.DistanceTo(end) < RevitConversionContextStack.TOLERANCE; + polycurve.length = _scalingService.ScaleLength(curves.Sum(x => x.Length)); + + polycurve.segments.AddRange(curves.Select(x => _curveConverter.Convert(x))); + + return polycurve; + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/ParameterConversionToSpeckle.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/ParameterConversionToSpeckle.cs new file mode 100644 index 0000000000..a0b1f01da5 --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/ParameterConversionToSpeckle.cs @@ -0,0 +1,40 @@ +using Autodesk.Revit.DB; +using Speckle.Converters.Common.Objects; +using Speckle.Converters.RevitShared.Extensions; +using Speckle.Converters.RevitShared.Helpers; + +namespace Speckle.Converters.RevitShared.ToSpeckle; + +public class ParameterConversionToSpeckle : ITypedConverter +{ + private readonly ParameterValueExtractor _valueExtractor; + + public ParameterConversionToSpeckle(ParameterValueExtractor valueExtractor) + { + _valueExtractor = valueExtractor; + } + + public SOBR.Parameter Convert(Parameter target) + { + string internalName = target.GetInternalName(); + ForgeTypeId? unitTypeId = null; + if (target.StorageType is StorageType.Double) + { + // according to the api documentation, this method will throw if the storage type is not a VALUE type + // however, I've found that it will still throw if StorageType == StorageType.Integer + unitTypeId = target.GetUnitTypeId(); + } + Definition definition = target.Definition; + + return new SOBR.Parameter() + { + applicationInternalName = internalName, + applicationUnit = unitTypeId?.ToUniqueString() ?? "None", + isShared = target.IsShared, + isReadOnly = target.IsReadOnly, + name = definition.Name, + units = unitTypeId?.GetSymbol() ?? "None", + value = _valueExtractor.GetValue(target) + }; + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/BaseTopLevelConverterToSpeckle.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/BaseTopLevelConverterToSpeckle.cs new file mode 100644 index 0000000000..f7a94c8d1f --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/BaseTopLevelConverterToSpeckle.cs @@ -0,0 +1,32 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.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 SpeckleConversionException( + $"Expected resulting object to be {typeof(Base)} but was {result.GetType()}" + ); + } + + return @base; + } + + public abstract TSpeckle Convert(THost target); +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/CeilingTopLevelConverterToSpeckle.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/CeilingTopLevelConverterToSpeckle.cs new file mode 100644 index 0000000000..c3de2636aa --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/CeilingTopLevelConverterToSpeckle.cs @@ -0,0 +1,72 @@ +using Autodesk.Revit.DB; +using Objects; +using Objects.BuiltElements.Revit; +using Objects.Geometry; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Converters.RevitShared.Helpers; + +namespace Speckle.Converters.RevitShared.ToSpeckle; + +[NameAndRankValue(nameof(DB.Ceiling), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +internal sealed class CeilingTopLevelConverterToSpeckle : BaseTopLevelConverterToSpeckle +{ + private readonly ITypedConverter> _curveArrArrayConverter; + private readonly ITypedConverter _levelConverter; + private readonly ParameterValueExtractor _parameterValueExtractor; + private readonly ParameterObjectAssigner _parameterObjectAssigner; + private readonly DisplayValueExtractor _displayValueExtractor; + + //private readonly HostedElementConversionToSpeckle _hostedElementConverter; + + public CeilingTopLevelConverterToSpeckle( + ITypedConverter> curveArrArrayConverter, + ITypedConverter levelConverter, + ParameterValueExtractor parameterValueExtractor, + ParameterObjectAssigner parameterObjectAssigner, + DisplayValueExtractor displayValueExtractor + ) + { + _curveArrArrayConverter = curveArrArrayConverter; + _levelConverter = levelConverter; + _parameterValueExtractor = parameterValueExtractor; + _parameterObjectAssigner = parameterObjectAssigner; + _displayValueExtractor = displayValueExtractor; + } + + public override RevitCeiling Convert(DB.Ceiling target) + { + var sketch = (Sketch)target.Document.GetElement(target.SketchId); + List profiles = _curveArrArrayConverter.Convert(sketch.Profile); + + var speckleCeiling = new RevitCeiling(); + + var elementType = (ElementType)target.Document.GetElement(target.GetTypeId()); + speckleCeiling.type = elementType.Name; + speckleCeiling.family = elementType.FamilyName; + + // 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(); + } + + // 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, DB.BuiltInParameter.LEVEL_PARAM); + speckleCeiling.level = _levelConverter.Convert(level); + + _parameterObjectAssigner.AssignParametersToBase(target, speckleCeiling); + speckleCeiling.displayValue = _displayValueExtractor.GetDisplayValue(target); + + // POC: hosted elements OOS for alpha, but this exists in existing connector + //_hostedElementConverter.AssignHostedElements(target, speckleCeiling); + + return speckleCeiling; + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/DirectShapeTopLevelConverterToSpeckle.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/DirectShapeTopLevelConverterToSpeckle.cs new file mode 100644 index 0000000000..c9c9ca5e14 --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/DirectShapeTopLevelConverterToSpeckle.cs @@ -0,0 +1,48 @@ +using Speckle.Converters.Common; +using Speckle.Converters.RevitShared.Extensions; +using Speckle.Converters.RevitShared.Helpers; +using Speckle.Converters.RevitShared.ToSpeckle; +using Speckle.Core.Models; + +namespace Speckle.Converters.Revit2023.ToSpeckle; + +[NameAndRankValue(nameof(DB.DirectShape), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class DirectShapeTopLevelConverterToSpeckle : BaseTopLevelConverterToSpeckle +{ + private readonly IRevitConversionContextStack _contextStack; + private readonly ParameterObjectAssigner _parameterObjectAssigner; + private readonly DisplayValueExtractor _displayValueExtractor; + + public DirectShapeTopLevelConverterToSpeckle( + ParameterObjectAssigner parameterObjectAssigner, + IRevitConversionContextStack contextStack, + DisplayValueExtractor displayValueExtractor + ) + { + _parameterObjectAssigner = parameterObjectAssigner; + _contextStack = contextStack; + _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(target.Name, category, geometries) + { + displayValue = geometries, + units = _contextStack.Current.SpeckleUnits, + elementId = target.Id.ToString() + }; + + _parameterObjectAssigner.AssignParametersToBase(target, result); + + result["type"] = target.Name; + + return result; + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/ElementTopLevelConverterToSpeckle.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/ElementTopLevelConverterToSpeckle.cs new file mode 100644 index 0000000000..c3b96c9001 --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/ElementTopLevelConverterToSpeckle.cs @@ -0,0 +1,66 @@ +using Speckle.Converters.Common; +using Objects.BuiltElements.Revit; +using Speckle.Converters.RevitShared.Helpers; + +namespace Speckle.Converters.RevitShared.ToSpeckle; + +// POC: not currently used? clearly some missing pieces +[NameAndRankValue(nameof(DB.Element), 0)] +public class ElementTopLevelConverterToSpeckle : BaseTopLevelConverterToSpeckle +{ + private readonly DisplayValueExtractor _displayValueExtractor; + + public ElementTopLevelConverterToSpeckle(DisplayValueExtractor displayValueExtractor) + { + _displayValueExtractor = displayValueExtractor; + } + + public override RevitElement Convert(DB.Element target) + { + RevitElement speckleElement = new(); + + if (target.Document.GetElement(target.GetTypeId()) is DB.FamilySymbol symbol) + { + speckleElement.family = symbol.FamilyName; + speckleElement.type = symbol.Name; + } + else + { + speckleElement.type = target.Name; + } + speckleElement.type = target.Name; + + //var baseGeometry = LocationToSpeckle(target); + //if (baseGeometry is Geometry.Point point) + //{ + // speckleElement["basePoint"] = point; + //} + //else if (baseGeometry is Geometry.Line line) + //{ + // speckleElement["baseLine"] = line; + //} + + speckleElement.category = target.Category.Name; + + speckleElement.displayValue = _displayValueExtractor.GetDisplayValue(target); + + //GetHostedElements(speckleElement, target, out notes); + + //var displayValue = GetElementDisplayValue(target); + + //if (!displayValue.Any()) + //{ + // notes.Add( + // "Element does not have visible geometry. It will be sent to Speckle but won't be visible in the viewer." + // ); + //} + //else + //{ + // speckleElement.displayValue = displayValue; + //} + + //GetAllRevitParamsAndIds(speckleElement, target); + + return speckleElement; + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/ExtrusionRoofToSpeckleTopLevelConverter.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/ExtrusionRoofToSpeckleTopLevelConverter.cs new file mode 100644 index 0000000000..d46c93794f --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/ExtrusionRoofToSpeckleTopLevelConverter.cs @@ -0,0 +1,72 @@ +using Objects.BuiltElements.Revit.RevitRoof; +using Speckle.Converters.Common.Objects; +using Speckle.Converters.Common; +using Speckle.Converters.RevitShared.Helpers; + +using Speckle.Converters.RevitShared.Extensions; + +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 HostedElementConversionToSpeckle _hostedElementConverter; + private readonly ParameterObjectAssigner _parameterObjectAssigner; + + public ExtrusionRoofToSpeckleTopLevelConverter( + ITypedConverter levelConverter, + ITypedConverter modelCurveArrayConverter, + ITypedConverter pointConverter, + ParameterValueExtractor parameterValueExtractor, + DisplayValueExtractor displayValueExtractor, + HostedElementConversionToSpeckle hostedElementConverter, + ParameterObjectAssigner parameterObjectAssigner + ) + { + _levelConverter = levelConverter; + _modelCurveArrayConverter = modelCurveArrayConverter; + _pointConverter = pointConverter; + _parameterValueExtractor = parameterValueExtractor; + _displayValueExtractor = displayValueExtractor; + _hostedElementConverter = hostedElementConverter; + _parameterObjectAssigner = parameterObjectAssigner; + } + + public override RevitExtrusionRoof Convert(DB.ExtrusionRoof target) + { + var speckleExtrusionRoof = new RevitExtrusionRoof + { + start = _parameterValueExtractor.GetValueAsDouble(target, DB.BuiltInParameter.EXTRUSION_START_PARAM), + end = _parameterValueExtractor.GetValueAsDouble(target, DB.BuiltInParameter.EXTRUSION_END_PARAM) + }; + var plane = target.GetProfile().get_Item(0).SketchPlane.GetPlane(); + speckleExtrusionRoof.referenceLine = new SOG.Line( + _pointConverter.Convert(plane.Origin.Add(plane.XVec.Normalize().Negate())), + _pointConverter.Convert(plane.Origin) + ); + var level = _parameterValueExtractor.GetValueAsDocumentObject( + target, + DB.BuiltInParameter.ROOF_CONSTRAINT_LEVEL_PARAM + ); + speckleExtrusionRoof.level = _levelConverter.Convert(level); + speckleExtrusionRoof.outline = _modelCurveArrayConverter.Convert(target.GetProfile()); + + var elementType = (DB.ElementType)target.Document.GetElement(target.GetTypeId()); + speckleExtrusionRoof.type = elementType.Name; + speckleExtrusionRoof.family = elementType.FamilyName; + + _parameterObjectAssigner.AssignParametersToBase(target, speckleExtrusionRoof); + speckleExtrusionRoof.displayValue = _displayValueExtractor.GetDisplayValue(target); + speckleExtrusionRoof.elements = _hostedElementConverter + .ConvertHostedElements(target.GetHostedElementIds()) + .ToList(); + + return speckleExtrusionRoof; + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/FamilyInstanceTopLevelConverterToSpeckle.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/FamilyInstanceTopLevelConverterToSpeckle.cs new file mode 100644 index 0000000000..446dcd4383 --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/FamilyInstanceTopLevelConverterToSpeckle.cs @@ -0,0 +1,46 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.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/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/FloorTopLevelConverterToSpeckle.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/FloorTopLevelConverterToSpeckle.cs new file mode 100644 index 0000000000..13d0d3086f --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/FloorTopLevelConverterToSpeckle.cs @@ -0,0 +1,111 @@ +using Objects; +using Speckle.Converters.Common.Objects; +using Speckle.Converters.RevitShared.Helpers; +using Speckle.Converters.RevitShared.ToSpeckle; +using Speckle.Core.Models; + +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 ParameterObjectAssigner _parameterObjectAssigner; + private readonly DisplayValueExtractor _displayValueExtractor; + private readonly ISlopeArrowExtractor _slopeArrowExtractor; + + public FloorTopLevelConverterToSpeckle( + ITypedConverter> curveArrArrayConverter, + ITypedConverter levelConverter, + ParameterValueExtractor parameterValueExtractor, + ParameterObjectAssigner parameterObjectAssigner, + DisplayValueExtractor displayValueExtractor, + ISlopeArrowExtractor slopeArrowExtractor + ) + { + _curveArrArrayConverter = curveArrArrayConverter; + _levelConverter = levelConverter; + _parameterValueExtractor = parameterValueExtractor; + _parameterObjectAssigner = parameterObjectAssigner; + _displayValueExtractor = displayValueExtractor; + _slopeArrowExtractor = slopeArrowExtractor; + } + + public override SOBR.RevitFloor Convert(DB.Floor target) + { + SOBR.RevitFloor speckleFloor = new(); + + 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()); + + speckleFloor.family = type.FamilyName; + speckleFloor.type = type.Name; + + // 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(); + } + + var level = _parameterValueExtractor.GetValueAsDocumentObject(target, DB.BuiltInParameter.LEVEL_PARAM); + speckleFloor.level = _levelConverter.Convert(level); + speckleFloor.structural = + _parameterValueExtractor.GetValueAsBool(target, DB.BuiltInParameter.FLOOR_PARAM_IS_STRUCTURAL) ?? false; + + 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; + } + + _parameterObjectAssigner.AssignParametersToBase(target, speckleFloor); + TryAssignSlopeFromSlopeArrow(target, speckleFloor, slopeParam); + + speckleFloor.displayValue = _displayValueExtractor.GetDisplayValue(target); + // POC: hosted elements OOS for alpha, but this exists in existing connector + //_hostedElementConverter.AssignHostedElements(target, speckleCeiling); + + 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(tail, head); + 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/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/FootPrintRoofToSpeckleTopLevelConverter.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/FootPrintRoofToSpeckleTopLevelConverter.cs new file mode 100644 index 0000000000..1c8e74bb04 --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/FootPrintRoofToSpeckleTopLevelConverter.cs @@ -0,0 +1,89 @@ +using Autodesk.Revit.DB; +using Objects; +using Objects.BuiltElements.Revit; +using Objects.BuiltElements.Revit.RevitRoof; +using Objects.Geometry; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Converters.RevitShared.Extensions; +using Speckle.Converters.RevitShared.Helpers; + +namespace Speckle.Converters.RevitShared.ToSpeckle; + +[NameAndRankValue(nameof(DB.FootPrintRoof), 0)] +public class FootPrintRoofToSpeckleTopLevelConverter + : BaseTopLevelConverterToSpeckle +{ + private readonly ITypedConverter _levelConverter; + private readonly ITypedConverter _modelCurveArrArrayConverter; + private readonly ParameterValueExtractor _parameterValueExtractor; + private readonly DisplayValueExtractor _displayValueExtractor; + private readonly HostedElementConversionToSpeckle _hostedElementConverter; + private readonly ParameterObjectAssigner _parameterObjectAssigner; + + public FootPrintRoofToSpeckleTopLevelConverter( + ITypedConverter levelConverter, + ITypedConverter modelCurveArrArrayConverter, + ParameterValueExtractor parameterValueExtractor, + DisplayValueExtractor displayValueExtractor, + HostedElementConversionToSpeckle hostedElementConverter, + ParameterObjectAssigner parameterObjectAssigner + ) + { + _levelConverter = levelConverter; + _modelCurveArrArrayConverter = modelCurveArrArrayConverter; + _parameterValueExtractor = parameterValueExtractor; + _displayValueExtractor = displayValueExtractor; + _hostedElementConverter = hostedElementConverter; + _parameterObjectAssigner = parameterObjectAssigner; + } + + public override RevitFootprintRoof Convert(FootPrintRoof target) + { + var baseLevel = _parameterValueExtractor.GetValueAsDocumentObject( + target, + DB.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, + DB.BuiltInParameter.ROOF_UPTO_LEVEL_PARAM, + out var topLevel + ); + + //POC: CNX-9403 can be null if the sides have different slopes. + //We currently don't validate the success or failure of this TryGet as it's not necessary, but will be once we start the above ticket. + _parameterValueExtractor.TryGetValueAsDouble(target, DB.BuiltInParameter.ROOF_SLOPE, out var slope); + + RevitFootprintRoof speckleFootprintRoof = + new() + { + level = _levelConverter.Convert(baseLevel), + cutOffLevel = topLevel is not null ? _levelConverter.Convert(topLevel) : null, + slope = slope + }; + + // 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(); + speckleFootprintRoof.voids = profiles.Skip(1).ToList(); + + var elementType = (ElementType)target.Document.GetElement(target.GetTypeId()); + speckleFootprintRoof.type = elementType.Name; + speckleFootprintRoof.family = elementType.FamilyName; + + // POC: we are starting to see logic that is happening in all converters. We should definitely consider some + // conversion pipeline behavior. Would probably require adding interfaces into objects kit + _parameterObjectAssigner.AssignParametersToBase(target, speckleFootprintRoof); + speckleFootprintRoof.displayValue = _displayValueExtractor.GetDisplayValue(target); + speckleFootprintRoof.elements = _hostedElementConverter + .ConvertHostedElements(target.GetHostedElementIds()) + .ToList(); + + return speckleFootprintRoof; + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/HostedElementConversionToSpeckle.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/HostedElementConversionToSpeckle.cs new file mode 100644 index 0000000000..f9643119e0 --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/HostedElementConversionToSpeckle.cs @@ -0,0 +1,43 @@ +using Autodesk.Revit.DB; +using Speckle.Converters.Common; +using Speckle.Converters.RevitShared.Helpers; +using Speckle.Core.Models; + +namespace Speckle.Converters.RevitShared.ToSpeckle; + +// POC: do we need to see the blocks investigation outcome? Does the current logic hold? +// opportunity to rethink or confirm hosted element handling? Should this be a connector responsibiliy? +// No interfacing out however... +// CNX-9414 Re-evaluate hosted element conversions +public class HostedElementConversionToSpeckle +{ + private readonly IRootToSpeckleConverter _converter; + private readonly IRevitConversionContextStack _contextStack; + + public HostedElementConversionToSpeckle(IRootToSpeckleConverter converter, IRevitConversionContextStack contextStack) + { + _converter = converter; + _contextStack = contextStack; + } + + public IEnumerable ConvertHostedElements(IEnumerable hostedElementIds) + { + foreach (var elemId in hostedElementIds) + { + Element element = _contextStack.Current.Document.GetElement(elemId); + + Base @base; + try + { + @base = _converter.Convert(element); + } + catch (SpeckleConversionException) + { + // POC: logging + continue; + } + + yield return @base; + } + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/LevelTopLevelConverterToSpeckle.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/LevelTopLevelConverterToSpeckle.cs new file mode 100644 index 0000000000..2916415bb3 --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/LevelTopLevelConverterToSpeckle.cs @@ -0,0 +1,28 @@ +using Speckle.Converters.Common; +using Speckle.Converters.RevitShared.Services; + +namespace Speckle.Converters.RevitShared.ToSpeckle; + +[NameAndRankValue(nameof(DB.Level), 0)] +public class LevelConversionToSpeckle : BaseTopLevelConverterToSpeckle +{ + private readonly ScalingServiceToSpeckle _scalingService; + + public LevelConversionToSpeckle(ScalingServiceToSpeckle scalingService) + { + _scalingService = scalingService; + } + + public override SOBR.RevitLevel Convert(DB.Level target) + { + SOBR.RevitLevel level = + new() + { + elevation = _scalingService.ScaleLength(target.Elevation), + name = target.Name, + createView = true + }; + + return level; + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/ModelCurveToSpeckleTopLevelConverter.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/ModelCurveToSpeckleTopLevelConverter.cs new file mode 100644 index 0000000000..240cbe4713 --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/ModelCurveToSpeckleTopLevelConverter.cs @@ -0,0 +1,41 @@ +using Objects; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Converters.RevitShared.Helpers; + +namespace Speckle.Converters.RevitShared.ToSpeckle; + +// POC: ModelCurve looks a bit bogus and we may wish to revise what that is and how it inherits +// see https://spockle.atlassian.net/browse/CNX-9381 +[NameAndRankValue(nameof(DB.ModelCurve), 0)] +public class ModelCurveToSpeckleTopLevelConverter : BaseTopLevelConverterToSpeckle +{ + private readonly ITypedConverter _curveConverter; + private readonly IRevitConversionContextStack _conversionContext; + + public ModelCurveToSpeckleTopLevelConverter( + ITypedConverter curveConverter, + IRevitConversionContextStack conversionContext + ) + { + _curveConverter = curveConverter; + _conversionContext = conversionContext; + } + + public override SOBR.Curve.ModelCurve Convert(DB.ModelCurve target) + { + var modelCurve = new SOBR.Curve.ModelCurve() + { + baseCurve = _curveConverter.Convert(target.GeometryCurve), + lineStyle = target.LineStyle.Name, + elementId = target.Id.ToString(), + units = _conversionContext.Current.SpeckleUnits + }; + + // POC: check this is not going to set the display value to anything we cannot actually display - i.e. polycurve + // also we have a class for doing this, but probably this is fine for now. see https://spockle.atlassian.net/browse/CNX-9381 + modelCurve["@displayValue"] = modelCurve.baseCurve; + + return modelCurve; + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/RoofBaseToSpeckleTopLevelTopLevelConverter.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/RoofBaseToSpeckleTopLevelTopLevelConverter.cs new file mode 100644 index 0000000000..77f9ce831c --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/RoofBaseToSpeckleTopLevelTopLevelConverter.cs @@ -0,0 +1,41 @@ +using Autodesk.Revit.DB; +using Objects.BuiltElements.Revit.RevitRoof; +using Speckle.Converters.Common; +using Speckle.Converters.RevitShared.Extensions; +using Speckle.Converters.RevitShared.Helpers; + +namespace Speckle.Converters.RevitShared.ToSpeckle; + +[NameAndRankValue(nameof(DB.RoofBase), 0)] +internal sealed class RoofBaseToSpeckleTopLevelTopLevelConverter + : BaseTopLevelConverterToSpeckle +{ + private readonly DisplayValueExtractor _displayValueExtractor; + private readonly HostedElementConversionToSpeckle _hostedElementConverter; + private readonly ParameterObjectAssigner _parameterObjectAssigner; + + public RoofBaseToSpeckleTopLevelTopLevelConverter( + DisplayValueExtractor displayValueExtractor, + HostedElementConversionToSpeckle hostedElementConverter, + ParameterObjectAssigner parameterObjectAssigner + ) + { + _displayValueExtractor = displayValueExtractor; + _hostedElementConverter = hostedElementConverter; + _parameterObjectAssigner = parameterObjectAssigner; + } + + public override RevitRoof Convert(RoofBase target) + { + RevitRoof revitRoof = new(); + var elementType = (ElementType)target.Document.GetElement(target.GetTypeId()); + revitRoof.type = elementType.Name; + revitRoof.family = elementType.FamilyName; + + _parameterObjectAssigner.AssignParametersToBase(target, revitRoof); + revitRoof.displayValue = _displayValueExtractor.GetDisplayValue(target); + revitRoof.elements = _hostedElementConverter.ConvertHostedElements(target.GetHostedElementIds()).ToList(); + + return revitRoof; + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/RoomTopLevelConverterToSpeckle.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/RoomTopLevelConverterToSpeckle.cs new file mode 100644 index 0000000000..6e4e1f91cb --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/RoomTopLevelConverterToSpeckle.cs @@ -0,0 +1,68 @@ +using Objects; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Converters.RevitShared.Helpers; +using Speckle.Core.Models; + +namespace Speckle.Converters.RevitShared.ToSpeckle; + +[NameAndRankValue(nameof(DBA.Room), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class RoomTopLevelConverterToSpeckle : BaseTopLevelConverterToSpeckle +{ + private readonly DisplayValueExtractor _displayValueExtractor; + private readonly ParameterObjectAssigner _parameterObjectAssigner; + private readonly ITypedConverter _levelConverter; + private readonly ParameterValueExtractor _parameterValueExtractor; + private readonly ITypedConverter _locationConverter; + private readonly ITypedConverter, SOG.Polycurve> _boundarySegmentConverter; + + public RoomTopLevelConverterToSpeckle( + DisplayValueExtractor displayValueExtractor, + ParameterObjectAssigner parameterObjectAssigner, + ITypedConverter levelConverter, + ParameterValueExtractor parameterValueExtractor, + ITypedConverter locationConverter, + ITypedConverter, SOG.Polycurve> boundarySegmentConverter + ) + { + _displayValueExtractor = displayValueExtractor; + _parameterObjectAssigner = parameterObjectAssigner; + _levelConverter = levelConverter; + _parameterValueExtractor = parameterValueExtractor; + _locationConverter = locationConverter; + _boundarySegmentConverter = boundarySegmentConverter; + } + + 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(); + + var speckleRoom = new SOBE.Room(name ?? "-", number, level, basePoint) + { + displayValue = displayValue, + area = area, + outline = outline, + voids = voids + }; + + _parameterObjectAssigner.AssignParametersToBase(target, speckleRoom); + + // POC: Removed dynamic property `phaseCreated` as it seems the info is included in the parameters already + + return speckleRoom; + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/TopographyTopLevelConverterToSpeckle.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/TopographyTopLevelConverterToSpeckle.cs new file mode 100644 index 0000000000..eea4ea51c9 --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/TopographyTopLevelConverterToSpeckle.cs @@ -0,0 +1,37 @@ +using Speckle.Converters.Common; +using Speckle.Converters.RevitShared.Helpers; + +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 ParameterObjectAssigner _parameterObjectAssigner; + + public TopographyTopLevelConverterToSpeckle( + DisplayValueExtractor displayValueExtractor, + ParameterObjectAssigner parameterObjectAssigner + ) + { + _displayValueExtractor = displayValueExtractor; + _parameterObjectAssigner = parameterObjectAssigner; + } + + public override SOBR.RevitTopography Convert(DBA.TopographySurface target) + { + var speckleTopo = new SOBR.RevitTopography + { + displayValue = _displayValueExtractor.GetDisplayValue(target), + elementId = target.Id.ToString() + }; + + // POC: shouldn't we just do this in the RevitConverter ? + _parameterObjectAssigner.AssignParametersToBase(target, speckleTopo); + + return speckleTopo; + } +} diff --git a/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/WallTopLevelConverterToSpeckle.cs b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/WallTopLevelConverterToSpeckle.cs new file mode 100644 index 0000000000..8e0cce3ba9 --- /dev/null +++ b/DUI3-DX/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/WallTopLevelConverterToSpeckle.cs @@ -0,0 +1,180 @@ +using Speckle.Converters.Common.Objects; +using Speckle.Converters.Common; +using Objects; +using Speckle.Converters.RevitShared.Helpers; +using Speckle.Core.Models; +using Speckle.Core.Models.Extensions; +using Speckle.Converters.RevitShared.Extensions; +using Objects.BuiltElements.Revit; + +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 IRevitConversionContextStack _contextStack; + private readonly DisplayValueExtractor _displayValueExtractor; + private readonly ParameterObjectAssigner _parameterObjectAssigner; + private readonly IRootToSpeckleConverter _converter; + + public WallTopLevelConverterToSpeckle( + ITypedConverter curveConverter, + ITypedConverter levelConverter, + ITypedConverter> curveArrArrayConverter, + IRevitConversionContextStack contextStack, + ParameterValueExtractor parameterValueExtractor, + DisplayValueExtractor displayValueExtractor, + ParameterObjectAssigner parameterObjectAssigner, + IRootToSpeckleConverter converter + ) + { + _curveConverter = curveConverter; + _levelConverter = levelConverter; + _curveArrArrayConverter = curveArrArrayConverter; + _contextStack = contextStack; + _parameterValueExtractor = parameterValueExtractor; + _displayValueExtractor = displayValueExtractor; + _parameterObjectAssigner = parameterObjectAssigner; + _converter = converter; + } + + public override SOBR.RevitWall Convert(DB.Wall target) + { + SOBR.RevitWall speckleWall = new() { family = target.WallType.FamilyName.ToString(), type = target.WallType.Name }; + + AssignSpecificParameters(target, speckleWall); + AssignVoids(target, speckleWall); + AssignHostedElements(speckleWall, GetChildElements(target)); + AssignDisplayValue(target, speckleWall); + _parameterObjectAssigner.AssignParametersToBase(target, speckleWall); + + return speckleWall; + } + + private void AssignSpecificParameters(DB.Wall target, RevitWall speckleWall) + { + if (target.Location is not DB.LocationCurve locationCurve) + { + throw new SpeckleConversionException( + "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())); + } + wallChildren.AddRange(ConvertElements(target.GetHostedElementIds())); + return wallChildren; + } + + private IEnumerable ConvertElements(IEnumerable elementIds) + { + foreach (DB.ElementId elementId in elementIds) + { + yield return _converter.Convert(_contextStack.Current.Document.GetElement(elementId)); + } + } + + private void AssignDisplayValue(DB.Wall target, RevitWall speckleWall) + { + if (target.CurtainGrid is null) + { + speckleWall.displayValue = _displayValueExtractor.GetDisplayValue(target); + } + else + { + // POC: I have no why previously we were setting the display value, and then unsetting it. + // Probably curtain walls need a special case/etc.? + 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/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7.DependencyInjection/RhinoConverterModule.cs b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7.DependencyInjection/RhinoConverterModule.cs new file mode 100644 index 0000000000..6cfca7fa5e --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7.DependencyInjection/RhinoConverterModule.cs @@ -0,0 +1,19 @@ +using Speckle.Autofac.DependencyInjection; +using Speckle.Converters.Common; +using Rhino; +using Speckle.Converters.Common.DependencyInjection; + +namespace Speckle.Converters.Rhino7.DependencyInjection; + +public class RhinoConverterModule : ISpeckleModule +{ + public void Load(SpeckleContainerBuilder builder) + { + // Register single root + builder.AddRootCommon(); + + // register all application converters and context stacks + builder.AddApplicationConverters(); + builder.AddScoped, RhinoConversionContextStack>(); + } +} diff --git a/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7.DependencyInjection/Speckle.Converters.Rhino7.DependencyInjection.csproj b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7.DependencyInjection/Speckle.Converters.Rhino7.DependencyInjection.csproj new file mode 100644 index 0000000000..f92549ccd3 --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7.DependencyInjection/Speckle.Converters.Rhino7.DependencyInjection.csproj @@ -0,0 +1,16 @@ + + + net48 + false + + + + + + + + + + + + diff --git a/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7.DependencyInjection/packages.lock.json b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7.DependencyInjection/packages.lock.json new file mode 100644 index 0000000000..8094f14468 --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7.DependencyInjection/packages.lock.json @@ -0,0 +1,429 @@ +{ + "version": 2, + "dependencies": { + ".NETFramework,Version=v4.8": { + "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==" + }, + "RhinoCommon": { + "type": "Direct", + "requested": "[7.13.21348.13001, )", + "resolved": "7.13.21348.13001", + "contentHash": "JQdaNw61ddBqIe08E9O4N/grwrN1hjDHcYW7tWylwCZyFR7SepoCD4NS+6LN6+oSQhNbhLi9Bf+hQOFYFdRAEA==" + }, + "Speckle.InterfaceGenerator": { + "type": "Direct", + "requested": "[0.9.5, )", + "resolved": "0.9.5", + "contentHash": "oU/L7pN1R7q8KkbrpQ3WJnHirPHqn+9DEA7asOcUiggV5dzVg1A/VYs7GOSusD24njxXh03tE3a2oTLOjt3cVg==" + }, + "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.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "Polly": { + "type": "Transitive", + "resolved": "7.2.3", + "contentHash": "DeCY0OFbNdNxsjntr1gTXHJ5pKUwYzp04Er2LLeN3g6pWhffsGuKVfMBLe1lw7x76HrPkLxKEFxBlpRxS2nDEQ==" + }, + "Polly.Contrib.WaitAndRetry": { + "type": "Transitive", + "resolved": "1.1.1", + "contentHash": "1MUQLiSo4KDkQe6nzQRhIU05lm9jlexX5BVsbuw0SL82ynZ+GzAHQxJVDPVBboxV37Po3SG077aX8DuSy8TkaA==" + }, + "Polly.Extensions.Http": { + "type": "Transitive", + "resolved": "3.0.0", + "contentHash": "drrG+hB3pYFY7w1c3BD+lSGYvH2oIclH8GRSehgfyP5kjnFnHKQuuBhuHLv+PWyFuaTDyk/vfRpnxOzd11+J8g==", + "dependencies": { + "Polly": "7.1.0" + } + }, + "Sentry": { + "type": "Transitive", + "resolved": "3.33.0", + "contentHash": "8vbD2o6IR2wrRrkSiRbnodWGWUOqIlwYtzpjvPNOb5raJdOf+zxMwfS8f6nx9bmrTTfDj7KrCB8C/5OuicAc8A==", + "dependencies": { + "System.Reflection.Metadata": "5.0.0", + "System.Runtime.InteropServices.RuntimeInformation": "4.3.0", + "System.Text.Json": "5.0.2" + } + }, + "Sentry.Serilog": { + "type": "Transitive", + "resolved": "3.33.0", + "contentHash": "V8BU7QGWg2qLYfNPqtuTBhC1opysny5l+Ifp6J6PhOeAxU0FssR7nYfbJVetrnLIoh2rd3DlJ6hHYYQosQYcUQ==", + "dependencies": { + "Sentry": "3.33.0", + "Serilog": "2.7.1" + } + }, + "Serilog": { + "type": "Transitive", + "resolved": "2.12.0", + "contentHash": "xaiJLIdu6rYMKfQMYUZgTy8YK7SMZjB4Yk50C/u//Z4OsvxkUfSPJy4nknfvwAC34yr13q7kcyh4grbwhSxyZg==" + }, + "Serilog.Enrichers.ClientInfo": { + "type": "Transitive", + "resolved": "1.3.0", + "contentHash": "mTc7PM+wC9Hr7LWSwqt5mmnlAr7RJs+eTb3PGPRhwdOackk95MkhUZognuxXEdlW19HAFNmEBTSBY5DfLwM8jQ==", + "dependencies": { + "Serilog": "2.4.0" + } + }, + "Serilog.Exceptions": { + "type": "Transitive", + "resolved": "8.4.0", + "contentHash": "nc/+hUw3lsdo0zCj0KMIybAu7perMx79vu72w0za9Nsi6mWyNkGXxYxakAjWB7nEmYL6zdmhEQRB4oJ2ALUeug==", + "dependencies": { + "Serilog": "2.8.0" + } + }, + "Serilog.Formatting.Compact": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "pNroKVjo+rDqlxNG5PXkRLpfSCuDOBY0ri6jp9PLe505ljqwhwZz8ospy2vWhQlFu5GkIesh3FcDs4n7sWZODA==", + "dependencies": { + "Serilog": "2.8.0" + } + }, + "Serilog.Sinks.Console": { + "type": "Transitive", + "resolved": "4.1.0", + "contentHash": "K6N5q+5fetjnJPvCmkWOpJ/V8IEIoMIB1s86OzBrbxwTyHxdx3pmz4H+8+O/Dc/ftUX12DM1aynx/dDowkwzqg==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "Serilog.Sinks.File": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "uwV5hdhWPwUH1szhO8PJpFiahqXmzPzJT/sOijH/kFgUx+cyoDTMM8MHD0adw9+Iem6itoibbUXHYslzXsLEAg==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "Serilog.Sinks.PeriodicBatching": { + "type": "Transitive", + "resolved": "3.1.0", + "contentHash": "NDWR7m3PalVlGEq3rzoktrXikjFMLmpwF0HI4sowo8YDdU+gqPlTHlDQiOGxHfB0sTfjPA9JjA7ctKG9zqjGkw==", + "dependencies": { + "Serilog": "2.0.0" + } + }, + "Serilog.Sinks.Seq": { + "type": "Transitive", + "resolved": "5.2.2", + "contentHash": "1Csmo5ua7NKUe0yXUx+zsRefjAniPWcXFhUXxXG8pwo0iMiw2gjn9SOkgYnnxbgWqmlGv236w0N/dHc2v5XwMg==", + "dependencies": { + "Serilog": "2.12.0", + "Serilog.Formatting.Compact": "1.1.0", + "Serilog.Sinks.File": "5.0.0", + "Serilog.Sinks.PeriodicBatching": "3.1.0" + } + }, + "SerilogTimings": { + "type": "Transitive", + "resolved": "3.0.1", + "contentHash": "Zs28eTgszAMwpIrbBnWHBI50yuxL50p/dmAUWmy75+axdZYK/Sjm5/5m1N/CisR8acJUhTVcjPZrsB1P5iv0Uw==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "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.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, + "System.Collections.Immutable": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "FXkLXiK0sVVewcso0imKQoOxjoPAj42R8HtjjbSjVPAzwDfzoyoznWxgA3c38LDbN9SJux1xXoXYAhz98j7r2g==", + "dependencies": { + "System.Memory": "4.5.4" + } + }, + "System.DoubleNumerics": { + "type": "Transitive", + "resolved": "3.1.3", + "contentHash": "KRKEM/L3KBodjA9VOg3EifFVWUY6EOqaMB05UvPEDm7Zeby/kZW+4kdWUEPzW6xtkwf46p661L9NrbeeQhtLzw==", + "dependencies": { + "NETStandard.Library": "1.6.1" + } + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Numerics.Vectors": "4.5.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + }, + "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.5.0", + "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" + }, + "System.Reactive": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "System.Reflection.Metadata": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "5NecZgXktdGg34rh1OenY1rFNDCI8xSjFr+Z4OU4cU06AQHUdRnIIEeWENu3Wl4YowbzkymAIMvi3WyK9U53pQ==", + "dependencies": { + "System.Collections.Immutable": "5.0.0" + } + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "ZD9TMpsmYJLrxbbmdvhwt9YEgG5WntEnZ/d1eH8JBX9LBp+Ju8BSBhUGbZMNVHHomWo2KVImJhTDl2hIgw/6MA==" + }, + "System.Runtime.InteropServices.RuntimeInformation": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==" + }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "5.0.1", + "contentHash": "KmJ+CJXizDofbq6mpqDoRRLcxgOd2z9X3XoFNULSbvbqVRZkFX3istvr+MUjL6Zw1RT+RNdoI4GYidIINtgvqQ==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Memory": "4.5.4" + } + }, + "System.Text.Json": { + "type": "Transitive", + "resolved": "5.0.2", + "contentHash": "I47dVIGiV6SfAyppphxqupertT/5oZkYLDCX6vC3HpOI4ZLjyoKAreUoem2ie6G0RbRuFrlqz/PcTQjfb2DOfQ==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "5.0.0", + "System.Buffers": "4.5.1", + "System.Memory": "4.5.4", + "System.Numerics.Vectors": "4.5.0", + "System.Runtime.CompilerServices.Unsafe": "5.0.0", + "System.Text.Encodings.Web": "5.0.1", + "System.Threading.Tasks.Extensions": "4.5.4", + "System.ValueTuple": "4.5.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" + } + }, + "System.ValueTuple": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "okurQJO6NRE/apDIP23ajJ0hpiNmJ+f0BwOlB/cSqTLQlw5upkf+5+96+iG2Jw40G1fCVCyPz/FhIABUjMR+RQ==" + }, + "speckle.autofac": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "[7.0.0, )" + } + }, + "speckle.converters.common": { + "type": "Project", + "dependencies": { + "Speckle.Autofac": "[2.0.999-local, )", + "Speckle.Objects": "[3.0.1-alpha.14, )" + } + }, + "speckle.converters.common.dependencyinjection": { + "type": "Project", + "dependencies": { + "Speckle.Autofac": "[2.0.999-local, )", + "Speckle.Converters.Common": "[2.0.999-local, )" + } + }, + "speckle.converters.rhino7": { + "type": "Project", + "dependencies": { + "RhinoCommon": "[7.13.21348.13001, )", + "Speckle.Converters.Common": "[2.0.999-local, )" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "CentralTransitive", + "requested": "[7.0.0, )", + "resolved": "7.0.0", + "contentHash": "kmn78+LPVMOWeITUjIlfxUPDsI0R6G0RkeAMBmQxAJ7vBJn4q2dTva7pWi65ceN5vPGjJ9q/Uae2WKgvfktJAw==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Memory": "4.5.5" + } + }, + "Speckle.Core": { + "type": "CentralTransitive", + "requested": "[3.0.1-alpha.14, )", + "resolved": "3.0.1-alpha.14", + "contentHash": "RzQPVIGFFkKvG56YLr8ACtiwdWJE6IJ9vCQ4qHa0PIsUEpfzAIAi59jnzqtByOFC0FiFrFPow9bkfzylaZorAA==", + "dependencies": { + "GraphQL.Client": "6.0.0", + "Microsoft.CSharp": "4.7.0", + "Microsoft.Data.Sqlite": "7.0.5", + "Polly": "7.2.3", + "Polly.Contrib.WaitAndRetry": "1.1.1", + "Polly.Extensions.Http": "3.0.0", + "Sentry": "3.33.0", + "Sentry.Serilog": "3.33.0", + "Serilog": "2.12.0", + "Serilog.Enrichers.ClientInfo": "1.3.0", + "Serilog.Exceptions": "8.4.0", + "Serilog.Sinks.Console": "4.1.0", + "Serilog.Sinks.Seq": "5.2.2", + "SerilogTimings": "3.0.1", + "Speckle.Newtonsoft.Json": "13.0.2", + "System.DoubleNumerics": "3.1.3" + } + }, + "Speckle.Objects": { + "type": "CentralTransitive", + "requested": "[3.0.1-alpha.14, )", + "resolved": "3.0.1-alpha.14", + "contentHash": "z38LGryMvh7iU1uBW+4uo5DwsB3CwRgLt2uFexWFx3mPSid+A0l5XcJzOgLwgFhNl6B42Ryz4ezBsddTp1Uc/g==", + "dependencies": { + "Speckle.Core": "3.0.1-alpha.14" + } + } + } + } +} \ No newline at end of file diff --git a/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/GlobalUsings.cs b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/GlobalUsings.cs new file mode 100644 index 0000000000..baa44c188e --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/GlobalUsings.cs @@ -0,0 +1,3 @@ +global using RG = Rhino.Geometry; +global using SOG = Objects.Geometry; +global using SOP = Objects.Primitive; diff --git a/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/RhinoConversionContextStack.cs b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/RhinoConversionContextStack.cs new file mode 100644 index 0000000000..79ac2f77a4 --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/RhinoConversionContextStack.cs @@ -0,0 +1,17 @@ +using System.Diagnostics.CodeAnalysis; +using Rhino; +using Speckle.Converters.Common; + +namespace Speckle.Converters.Rhino7; + +// POC: CNX-9268 Suppressed naming warning for now, but we should evaluate if we should follow this or disable it. +[SuppressMessage( + "Naming", + "CA1711:Identifiers should not have incorrect suffix", + Justification = "Name ends in Stack but it is in fact a Stack, just not inheriting from `System.Collections.Stack`" +)] +public class RhinoConversionContextStack : ConversionContextStack +{ + public RhinoConversionContextStack(IHostToSpeckleUnitConverter unitConverter) + : base(RhinoDoc.ActiveDoc, RhinoDoc.ActiveDoc.ModelUnitSystem, unitConverter) { } +} diff --git a/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/RhinoToSpeckleUnitConverter.cs b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/RhinoToSpeckleUnitConverter.cs new file mode 100644 index 0000000000..4f4f9c77e4 --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/RhinoToSpeckleUnitConverter.cs @@ -0,0 +1,35 @@ +using Rhino; +using Speckle.Converters.Common; +using Speckle.Core.Kits; + +namespace Speckle.Converters.Rhino7; + +public class RhinoToSpeckleUnitConverter : IHostToSpeckleUnitConverter +{ + private readonly Dictionary _unitMapping = new(); + + public RhinoToSpeckleUnitConverter() + { + // POC: CNX-9269 Add unit test to ensure these don't change. + _unitMapping[UnitSystem.None] = Units.Meters; + _unitMapping[UnitSystem.Millimeters] = Units.Millimeters; + _unitMapping[UnitSystem.Centimeters] = Units.Centimeters; + _unitMapping[UnitSystem.Meters] = Units.Meters; + _unitMapping[UnitSystem.Kilometers] = Units.Kilometers; + _unitMapping[UnitSystem.Inches] = Units.Inches; + _unitMapping[UnitSystem.Feet] = Units.Feet; + _unitMapping[UnitSystem.Yards] = Units.Yards; + _unitMapping[UnitSystem.Miles] = Units.Miles; + _unitMapping[UnitSystem.Unset] = Units.Meters; + } + + public string ConvertOrThrow(UnitSystem hostUnit) + { + if (_unitMapping.TryGetValue(hostUnit, out string value)) + { + return value; + } + + throw new SpeckleConversionException($"The Unit System \"{hostUnit}\" is unsupported."); + } +} diff --git a/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/Speckle.Converters.Rhino7.csproj b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/Speckle.Converters.Rhino7.csproj new file mode 100644 index 0000000000..19094744c9 --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/Speckle.Converters.Rhino7.csproj @@ -0,0 +1,14 @@ + + + + net48 + + + + + + + + + + diff --git a/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/SpeckleToHostGeometryBaseTopLevelConverter.cs b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/SpeckleToHostGeometryBaseTopLevelConverter.cs new file mode 100644 index 0000000000..75ff2162c2 --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/SpeckleToHostGeometryBaseTopLevelConverter.cs @@ -0,0 +1,44 @@ +using Rhino; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Kits; +using Speckle.Core.Models; + +namespace Speckle.Converters.Rhino7; + +public abstract class SpeckleToHostGeometryBaseTopLevelConverter : IToHostTopLevelConverter + where TIn : Base + where TOut : RG.GeometryBase +{ + protected IConversionContextStack ContextStack { get; private set; } + private readonly ITypedConverter _geometryBaseConverter; + + protected SpeckleToHostGeometryBaseTopLevelConverter( + IConversionContextStack contextStack, + ITypedConverter geometryBaseConverter + ) + { + ContextStack = contextStack; + _geometryBaseConverter = geometryBaseConverter; + } + + public object Convert(Base target) + { + var castedBase = (TIn)target; + var result = _geometryBaseConverter.Convert(castedBase); + + /* + * 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 (castedBase["units"] is string units) + { + var scaleFactor = Units.GetConversionFactor(units, ContextStack.Current.SpeckleUnits); + var scale = RG.Transform.Scale(RG.Point3d.Origin, scaleFactor); + result.Transform(scale); + } + + return result; + } +} diff --git a/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/Raw/ArcToHostConverter.cs b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/Raw/ArcToHostConverter.cs new file mode 100644 index 0000000000..0068c38ef2 --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/Raw/ArcToHostConverter.cs @@ -0,0 +1,54 @@ +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.Rhino7.ToHost.Raw; + +/// +/// Converts a SpeckleArcRaw object to a Rhino.Geometry.Arc object or Rhino.Geometry.ArcCurve object. +/// +public class ArcToHostConverter : ITypedConverter, ITypedConverter +{ + private readonly ITypedConverter _pointConverter; + private readonly ITypedConverter _intervalConverter; + + public ArcToHostConverter( + ITypedConverter pointConverter, + ITypedConverter intervalConverter + ) + { + _pointConverter = pointConverter; + this._intervalConverter = intervalConverter; + } + + /// + /// Converts a object to a object. + /// + /// The Speckle Arc object to convert. + /// The converted object. + /// ⚠️ This conversion does NOT perform scaling. + ///
⚠️ This method does not preserve the original curve domain
+ public RG.Arc Convert(SOG.Arc target) + { + var rhinoArc = new RG.Arc( + _pointConverter.Convert(target.startPoint), + _pointConverter.Convert(target.midPoint), + _pointConverter.Convert(target.endPoint) + ); + return rhinoArc; + } + + // POC: CNX-9271 Potential code-smell by directly implementing the interface. We should discuss this further but + // since we're using the interfaces instead of the direct type, this may not be an issue. + /// + /// Converts a object to a object. + /// + /// The object to convert. + /// The converted object. + /// ⚠️ This conversion does NOT perform scaling. + ///
⚠️ Converting to instead of preserves the domain of the curve.
+ RG.ArcCurve ITypedConverter.Convert(SOG.Arc target) + { + var rhinoArc = Convert(target); + var arcCurve = new RG.ArcCurve(rhinoArc) { Domain = _intervalConverter.Convert(target.domain) }; + return arcCurve; + } +} diff --git a/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/Raw/BrepToHostConverter.cs b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/Raw/BrepToHostConverter.cs new file mode 100644 index 0000000000..db94e2f7f0 --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/Raw/BrepToHostConverter.cs @@ -0,0 +1,170 @@ +using Objects; +using Rhino; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.Rhino7.ToHost.Raw; + +public class BrepToHostConverter : ITypedConverter +{ + private readonly IConversionContextStack _contextStack; + private readonly ITypedConverter _curveConverter; + private readonly ITypedConverter _surfaceConverter; + private readonly ITypedConverter _pointConverter; + private readonly ITypedConverter _intervalConverter; + + public BrepToHostConverter( + IConversionContextStack contextStack, + ITypedConverter curveConverter, + ITypedConverter surfaceConverter, + ITypedConverter pointConverter, + ITypedConverter intervalConverter + ) + { + _contextStack = contextStack; + _curveConverter = curveConverter; + _surfaceConverter = surfaceConverter; + _pointConverter = pointConverter; + _intervalConverter = intervalConverter; + } + + /// + /// Converts a Speckle to a Rhino . + /// + /// + /// This method converts a Speckle Brep object to its equivalent Rhino Brep representation. + /// The conversion process includes converting individual curves, trims, surfaces, and vertices. + /// The resulting Rhino Brep is returned. + /// Note that the conversion does not cover all edge cases in Brep structures, therefore it is recommended to review the resulting Brep for robustness improvement. + /// + /// The Speckle Brep object to be converted. + /// The equivalent Rhino Brep object. + /// ⚠️ This conversion does NOT perform scaling. + public RG.Brep Convert(SOG.Brep target) + { + var tolerance = _contextStack.Current.Document.ModelAbsoluteTolerance; + + var rhinoBrep = new RG.Brep(); + + // Geometry goes in first, always. Order doesn't matter. + target.Curve3D.ForEach(curve => rhinoBrep.AddEdgeCurve(_curveConverter.Convert(curve))); + target.Curve2D.ForEach(curve => rhinoBrep.AddTrimCurve(_curveConverter.Convert(curve))); + target.Surfaces.ForEach(surface => rhinoBrep.AddSurface(_surfaceConverter.Convert(surface))); + target.Vertices.ForEach(vertex => rhinoBrep.Vertices.Add(_pointConverter.Convert(vertex), tolerance)); + + // Order matters, first edges, then faces, finally loops. + target.Edges.ForEach(edge => ConvertSpeckleBrepEdge(rhinoBrep, edge, tolerance)); + target.Faces.ForEach(face => ConvertSpeckleBrepFace(rhinoBrep, face)); + target.Loops.ForEach(loop => ConvertSpeckleBrepLoop(rhinoBrep, loop, tolerance)); + + // POC: Massive performance bottleneck, but a qualtiy assurance move. Fixes 99% of Brep fuckups. + + rhinoBrep.Repair(tolerance); // Repair fixes tolerance issues with the Brep if the scaling lead to some rounding error. + + if (!rhinoBrep.IsValidWithLog(out string reason)) + { + throw new SpeckleConversionException($"Resulting BREP was invalid: {reason}"); + } + + return rhinoBrep; + } + + /// + /// Converts a Speckle to a Rhino and adds it to the provided . + /// + /// + /// A consists of individual trims. There are special cases for singular trims and trims with defined edge indices. + /// Note that edge cases in Brep structures are not fully covered by this method and should be reviewed for robustness improvement. + /// This operation alters the state of the provided by adding a new loop. + /// + /// The where the new loop will be added. + /// The to be converted and added to . + /// The tolerance factor used when adding trims and setting their tolerances. + private void ConvertSpeckleBrepLoop(RG.Brep rhinoBrep, SOG.BrepLoop speckleLoop, double tol) + { + var f = rhinoBrep.Faces[speckleLoop.FaceIndex]; + + rhinoBrep.Loops.Add((RG.BrepLoopType)speckleLoop.Type, f); + + // POC: This works but it doesn't fully cover all Brep edge cases and could be the cause of some of our failed Rhino->Rhino breps. + // We should check Rhino.Inside as they have similar code structure. + speckleLoop.Trims + .ToList() + .ForEach(trim => + { + RG.BrepTrim rhTrim; + if (trim.EdgeIndex != -1) + { + rhTrim = rhinoBrep.Trims.Add( + rhinoBrep.Edges[trim.EdgeIndex], + trim.IsReversed, + rhinoBrep.Loops[trim.LoopIndex], + trim.CurveIndex + ); + } + else if (trim.TrimType == SOG.BrepTrimType.Singular) + { + rhTrim = rhinoBrep.Trims.AddSingularTrim( + rhinoBrep.Vertices[trim.EndIndex], + rhinoBrep.Loops[trim.LoopIndex], + (RG.IsoStatus)trim.IsoStatus, + trim.CurveIndex + ); + } + else + { + rhTrim = rhinoBrep.Trims.Add(trim.IsReversed, rhinoBrep.Loops[trim.LoopIndex], trim.CurveIndex); + } + + rhTrim.IsoStatus = (RG.IsoStatus)trim.IsoStatus; + rhTrim.TrimType = (RG.BrepTrimType)trim.TrimType; + rhTrim.SetTolerances(tol, tol); + }); + } + + /// + /// Converts a Speckle BrepEdge into a Rhino BrepEdge within a Rhino Brep. + /// + /// The Rhino Brep to which the converted BrepEdge will be added. + /// The Speckle BrepEdge to convert. + /// The tolerance for the conversion. + /// + /// If the domain of the Speckle BrepEdge is null or matches the curve's domain, it is assumed that the edge + /// is untrimmed, and hence added directly as a reference to the curve it points to. + /// If the edge is trimmed, it is added based on vertices and subdomain using the supplied tolerance + /// + private void ConvertSpeckleBrepEdge(RG.Brep rhinoBrep, SOG.BrepEdge speckleEdge, double tolerance) + { + if ( + speckleEdge.Domain == null + || speckleEdge.Domain.start == speckleEdge.Curve.domain.start + && speckleEdge.Domain.end == speckleEdge.Curve.domain.end + ) + { + // The edge is untrimmed, we can add it directly as a reference to the curve it points to. + rhinoBrep.Edges.Add(speckleEdge.Curve3dIndex); + } + else + { + // The edge is trimmed, must be added based on vertices and subdomain + rhinoBrep.Edges.Add( + speckleEdge.StartIndex, + speckleEdge.EndIndex, + speckleEdge.Curve3dIndex, + _intervalConverter.Convert(speckleEdge.Domain), + tolerance + ); + } + } + + /// + /// Converts a into a and adds it to the provided . + /// + /// The Rhinoceros brep geometry to which the converted face is added. + /// The Speckle brep face to be converted and added. + private void ConvertSpeckleBrepFace(RG.Brep rhinoBrep, SOG.BrepFace speckleFace) + { + var f = rhinoBrep.Faces.Add(speckleFace.SurfaceIndex); + f.OrientationIsReversed = speckleFace.OrientationReversed; + } +} diff --git a/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/Raw/CircleToHostConverter.cs b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/Raw/CircleToHostConverter.cs new file mode 100644 index 0000000000..1431d3fc1a --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/Raw/CircleToHostConverter.cs @@ -0,0 +1,58 @@ +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.Rhino7.ToHost.Raw; + +/// +/// This class is responsible for converting a into and objects. +/// Implements the interface, +/// providing implementation for to and conversion. +/// +public class CircleToHostConverter : ITypedConverter, ITypedConverter +{ + private readonly ITypedConverter _planeConverter; + private readonly ITypedConverter _intervalConverter; + + /// + /// Constructs a new instance of the class. + /// + /// + /// An implementation of used to convert into . + /// + /// + /// An implementation of used to convert into . + /// + public CircleToHostConverter( + ITypedConverter intervalConverter, + ITypedConverter planeConverter + ) + { + _intervalConverter = intervalConverter; + _planeConverter = planeConverter; + } + + /// + /// Converts the given object into a object. + /// + /// The object to convert. + /// The resulting object. + /// + /// Thrown when the radius of the given object is null. + /// + /// ⚠️ This conversion does NOT perform scaling. + ///
⚠️ This conversion does not preserve the curve domain. If you need it preserved you must request a conversion to conversion instead
+ public RG.Circle Convert(SOG.Circle target) + { + if (target.radius == null) + { + // POC: CNX-9272 Circle radius being nullable makes no sense + throw new ArgumentNullException(nameof(target), "Circle radius cannot be null"); + } + + var plane = _planeConverter.Convert(target.plane); + var radius = target.radius.Value; + return new RG.Circle(plane, radius); + } + + RG.ArcCurve ITypedConverter.Convert(SOG.Circle target) => + new(Convert(target)) { Domain = _intervalConverter.Convert(target.domain) }; +} diff --git a/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/Raw/CurveToHostConverter.cs b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/Raw/CurveToHostConverter.cs new file mode 100644 index 0000000000..a61425dc2a --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/Raw/CurveToHostConverter.cs @@ -0,0 +1,58 @@ +using Objects; +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.Rhino7.ToHost.Raw; + +public class CurveToHostConverter : ITypedConverter +{ + private readonly ITypedConverter _lineConverter; + private readonly ITypedConverter _arcConverter; + private readonly ITypedConverter _ellipseConverter; + private readonly ITypedConverter _spiralConverter; + private readonly ITypedConverter _circleConverter; + private readonly ITypedConverter _polylineConverter; + private readonly ITypedConverter _polyCurveConverter; + private readonly ITypedConverter _nurbsCurveConverter; + + public CurveToHostConverter( + ITypedConverter lineConverter, + ITypedConverter arcConverter, + ITypedConverter ellipseConverter, + ITypedConverter spiralConverter, + ITypedConverter circleConverter, + ITypedConverter polylineConverter, + ITypedConverter polyCurveConverter, + ITypedConverter nurbsCurveConverter + ) + { + _lineConverter = lineConverter; + _arcConverter = arcConverter; + _ellipseConverter = ellipseConverter; + _spiralConverter = spiralConverter; + _circleConverter = circleConverter; + _polylineConverter = polylineConverter; + _polyCurveConverter = polyCurveConverter; + _nurbsCurveConverter = nurbsCurveConverter; + } + + /// + /// Converts a given ICurve object to an RG.Curve object. + /// + /// The ICurve object to convert. + /// The converted RG.Curve object. + /// Thrown when the conversion is not supported for the given type of curve. + /// ⚠️ This conversion does NOT perform scaling. + public RG.Curve Convert(ICurve target) => + target switch + { + SOG.Line line => _lineConverter.Convert(line), + SOG.Arc arc => _arcConverter.Convert(arc), + SOG.Circle circle => _circleConverter.Convert(circle), + SOG.Ellipse ellipse => _ellipseConverter.Convert(ellipse), + SOG.Spiral spiral => _spiralConverter.Convert(spiral), + SOG.Polyline polyline => _polylineConverter.Convert(polyline), + SOG.Curve curve => _nurbsCurveConverter.Convert(curve), + SOG.Polycurve polyCurve => _polyCurveConverter.Convert(polyCurve), + _ => throw new NotSupportedException($"Unable to convert curves of type {target.GetType().Name}") + }; +} diff --git a/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/Raw/EllipseToHostConverter.cs b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/Raw/EllipseToHostConverter.cs new file mode 100644 index 0000000000..74f309a61f --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/Raw/EllipseToHostConverter.cs @@ -0,0 +1,59 @@ +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.Rhino7.ToHost.Raw; + +public class EllipseToHostConverter + : ITypedConverter, + ITypedConverter +{ + private readonly ITypedConverter _planeConverter; + private readonly ITypedConverter _intervalConverter; + + public EllipseToHostConverter( + ITypedConverter planeConverter, + ITypedConverter intervalConverter + ) + { + _planeConverter = planeConverter; + _intervalConverter = intervalConverter; + } + + /// + /// Converts an instance of to an while preserving geometric properties. + /// + /// The instance to be converted. + /// The resulting after conversion. + /// Thrown when or properties are null. + /// ⚠️ This conversion does NOT perform scaling. + ///
⚠️ This conversion does not preserve the curve domain. If you need it preserved you must request a conversion to conversion instead
+ public RG.Ellipse Convert(SOG.Ellipse target) + { + if (!target.firstRadius.HasValue || !target.secondRadius.HasValue) + { + throw new InvalidOperationException($"Ellipses cannot have null radii"); + } + + return new RG.Ellipse(_planeConverter.Convert(target.plane), target.firstRadius.Value, target.secondRadius.Value); + } + + /// + /// Converts the provided into a representation. + /// + /// The to convert. + /// + /// A that represents the provided . + /// + RG.NurbsCurve ITypedConverter.Convert(SOG.Ellipse target) + { + var rhinoEllipse = Convert(target); + var rhinoNurbsEllipse = rhinoEllipse.ToNurbsCurve(); + rhinoNurbsEllipse.Domain = _intervalConverter.Convert(target.domain); + + if (target.trimDomain != null) + { + rhinoNurbsEllipse = rhinoNurbsEllipse.Trim(_intervalConverter.Convert(target.trimDomain)).ToNurbsCurve(); + } + + return rhinoNurbsEllipse; + } +} diff --git a/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/Raw/FlatPointListToHostConverter.cs b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/Raw/FlatPointListToHostConverter.cs new file mode 100644 index 0000000000..f0363a387d --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/Raw/FlatPointListToHostConverter.cs @@ -0,0 +1,38 @@ +using Rhino.Collections; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Logging; + +namespace Speckle.Converters.Rhino7.ToHost.Raw; + +/// +/// Converts a flat list of raw double values to a Point3dList. +/// +public class FlatPointListToHostConverter : ITypedConverter, Point3dList> +{ + /// + /// Converts a flat list of raw double values to a Point3dList. + /// + /// The flat list of raw double values + /// A Point3dList object that represents the converted points + /// + /// Assumes that the amount of numbers contained on the list is a multiple of 3, + /// with the numbers being coordinates of each point in the format {x1, y1, z1, x2, y2, z2, ..., xN, yN, zN} + /// + /// Throws when the input list count is not a multiple of 3. + public Point3dList Convert(IReadOnlyList target) + { + if (target.Count % 3 != 0) + { + throw new SpeckleException("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])); + } + + return new Point3dList(points); + } +} diff --git a/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/Raw/IntervalToHostConverter.cs b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/Raw/IntervalToHostConverter.cs new file mode 100644 index 0000000000..f201283ebd --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/Raw/IntervalToHostConverter.cs @@ -0,0 +1,23 @@ +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.Rhino7.ToHost.Raw; + +public class IntervalToHostConverter : ITypedConverter +{ + /// + /// Converts a Speckle Interval object to a Rhino.Geometry.Interval object. + /// + /// The Speckle Interval to convert. + /// The converted Rhino.Geometry.Interval object. + /// Thrown when the start or end value of the Interval is null. + /// ⚠️ This conversion does NOT perform scaling. + public RG.Interval Convert(SOP.Interval target) + { + if (!target.start.HasValue || !target.end.HasValue) // POC: CNX-9272 Interval start and end being nullable makes no sense. + { + throw new ArgumentException("Interval start/end cannot be null"); + } + + return new RG.Interval(target.start.Value, target.end.Value); + } +} diff --git a/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/Raw/LineToHostConverter.cs b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/Raw/LineToHostConverter.cs new file mode 100644 index 0000000000..c549e96741 --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/Raw/LineToHostConverter.cs @@ -0,0 +1,34 @@ +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.Rhino7.ToHost.Raw; + +public class LineToHostConverter : ITypedConverter, ITypedConverter +{ + private readonly ITypedConverter _pointConverter; + + public LineToHostConverter(ITypedConverter pointConverter) + { + _pointConverter = pointConverter; + } + + /// + /// Converts a Speckle Line object to a Rhino Line object. + /// + /// The Speckle Line object to convert. + /// Returns the resulting Rhino Line object. + /// ⚠️ This conversion does NOT perform scaling. + /// + ///
⚠️ This conversion does not preserve the curve domain. + /// If you need it preserved you must request a conversion to + /// conversion instead + ///
+ public RG.Line Convert(SOG.Line target) => + new(_pointConverter.Convert(target.start), _pointConverter.Convert(target.end)); + + /// + /// Converts a Speckle Line object to a Rhino LineCurve object. + /// + /// The Speckle Line object to convert. + /// Returns the resulting Rhino LineCurve object. + RG.LineCurve ITypedConverter.Convert(SOG.Line target) => new(Convert(target)); +} diff --git a/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/Raw/MeshToHostConverter.cs b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/Raw/MeshToHostConverter.cs new file mode 100644 index 0000000000..d3917dafd3 --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/Raw/MeshToHostConverter.cs @@ -0,0 +1,102 @@ +using System.Drawing; +using Objects.Utils; +using Rhino.Collections; +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.Rhino7.ToHost.Raw; + +public class MeshToHostConverter : ITypedConverter +{ + private readonly ITypedConverter, Point3dList> _pointListConverter; + + public MeshToHostConverter(ITypedConverter, Point3dList> pointListConverter) + { + _pointListConverter = pointListConverter; + } + + /// + /// Converts a Speckle mesh object to a Rhino mesh object. + /// + /// The Speckle mesh object to convert. + /// A Rhino mesh object converted from the Speckle mesh. + /// ⚠️ 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(); + + m.Vertices.AddVertices(vertices); + m.VertexColors.SetColors(colors); + + 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) + { + 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) + { + n += 3; // 0 -> 3, 1 -> 4 + } + + if (n == 3) + { + // 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) + { + var face = new RG.MeshFace(triangles[t], triangles[t + 1], triangles[t + 2]); + faceIndices.Add(m.Faces.AddFace(face)); + } + + RG.MeshNgon ngon = RG.MeshNgon.Create(target.faces.GetRange(i + 1, n), faceIndices); + m.Ngons.AddNgon(ngon); + } + + i += n + 1; + } + m.Faces.CullDegenerateFaces(); + } + + private void AssignTextureCoordinates(SOG.Mesh target, RG.Mesh m) + { + var textureCoordinates = new RG.Point2f[target.TextureCoordinatesCount]; + for (int ti = 0; ti < target.TextureCoordinatesCount; ti++) + { + var (u, v) = target.GetTextureCoordinate(ti); + textureCoordinates[ti] = new RG.Point2f(u, v); + } + m.TextureCoordinates.SetTextureCoordinates(textureCoordinates); + } +} diff --git a/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/Raw/NurbsCurveToHostConverter.cs b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/Raw/NurbsCurveToHostConverter.cs new file mode 100644 index 0000000000..9f9bf52811 --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/Raw/NurbsCurveToHostConverter.cs @@ -0,0 +1,51 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.Rhino7.ToHost.Raw; + +public class NurbsCurveToHostConverter : ITypedConverter +{ + private readonly ITypedConverter _intervalConverter; + + public NurbsCurveToHostConverter(ITypedConverter intervalConverter) + { + _intervalConverter = intervalConverter; + } + + /// + /// Converts a Speckle NurbsCurve object to a Rhino NurbsCurve object. + /// + /// The Speckle NurbsCurve object to be converted. + /// The converted Rhino NurbsCurve object. + /// Thrown when the conversion fails. + /// ⚠️ This conversion does NOT perform scaling. + public RG.NurbsCurve Convert(SOG.Curve target) + { + RG.NurbsCurve nurbsCurve = new(target.degree, target.points.Count / 3); + + // Hyper optimised curve control point conversion + for (int i = 2, j = 0; i < target.points.Count; i += 3, j++) + { + var pt = new RG.Point3d(target.points[i - 2], target.points[i - 1], target.points[i]); // Skip the point converter for performance + nurbsCurve.Points.SetPoint(j, pt, target.weights[j]); + } + + // check knot multiplicity to match Rhino's standard of (# control points + degree - 1) + // skip extra knots at start & end if knot multiplicity is (# control points + degree + 1) + int extraKnots = target.knots.Count - nurbsCurve.Knots.Count; + for (int j = 0; j < nurbsCurve.Knots.Count; j++) + { + if (extraKnots == 2) + { + nurbsCurve.Knots[j] = target.knots[j + 1]; + } + else + { + nurbsCurve.Knots[j] = target.knots[j]; + } + } + + nurbsCurve.Domain = _intervalConverter.Convert(target.domain); + return nurbsCurve; + } +} diff --git a/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/Raw/PlaneToHostConverter.cs b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/Raw/PlaneToHostConverter.cs new file mode 100644 index 0000000000..49e10ab69e --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/Raw/PlaneToHostConverter.cs @@ -0,0 +1,31 @@ +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.Rhino7.ToHost.Raw; + +public class PlaneToHostConverter : ITypedConverter +{ + private readonly ITypedConverter _pointConverter; + private readonly ITypedConverter _vectorConverter; + + public PlaneToHostConverter( + ITypedConverter pointConverter, + ITypedConverter vectorConverter + ) + { + _pointConverter = pointConverter; + _vectorConverter = vectorConverter; + } + + /// + /// Converts a Speckle Plane object to a Rhino Plane object. + /// + /// The Speckle Plane object to be converted. + /// The converted Rhino Plane object. + /// ⚠️ This conversion does NOT perform scaling. + public RG.Plane Convert(SOG.Plane target) => + new( + _pointConverter.Convert(target.origin), + _vectorConverter.Convert(target.xdir), + _vectorConverter.Convert(target.ydir) + ); +} diff --git a/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/Raw/PointCloudToHostConverter.cs b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/Raw/PointCloudToHostConverter.cs new file mode 100644 index 0000000000..a6d749b15c --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/Raw/PointCloudToHostConverter.cs @@ -0,0 +1,37 @@ +using System.Drawing; +using Rhino.Collections; +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.Rhino7.ToHost.Raw; + +public class PointCloudToHostConverter : ITypedConverter +{ + private readonly ITypedConverter, Point3dList> _pointListConverter; + + public PointCloudToHostConverter(ITypedConverter, Point3dList> pointListConverter) + { + _pointListConverter = pointListConverter; + } + + /// + /// Converts raw Speckle point cloud data to Rhino PointCloud object. + /// + /// The raw Speckle Pointcloud object to convert. + /// The converted Rhino PointCloud object. + /// ⚠️ This conversion does NOT perform scaling. + public RG.PointCloud Convert(SOG.Pointcloud target) + { + var rhinoPoints = _pointListConverter.Convert(target.points); + var rhinoPointCloud = new RG.PointCloud(rhinoPoints); + + if (target.colors.Count == rhinoPoints.Count) + { + for (int i = 0; i < rhinoPoints.Count; i++) + { + rhinoPointCloud[i].Color = Color.FromArgb(target.colors[i]); + } + } + + return rhinoPointCloud; + } +} diff --git a/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/Raw/PointToHostConverter.cs b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/Raw/PointToHostConverter.cs new file mode 100644 index 0000000000..8ce40bcb59 --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/Raw/PointToHostConverter.cs @@ -0,0 +1,22 @@ +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.Rhino7.ToHost.Raw; + +public class PointToHostConverter : ITypedConverter, ITypedConverter +{ + /// + /// Converts a Speckle Point object to a Rhino Point3d object. + /// + /// The Speckle Point object to convert. + /// The converted Rhino Point3d object. + /// ⚠️ This conversion does NOT perform scaling. + public RG.Point3d Convert(SOG.Point target) => new(target.x, target.y, target.z); + + /// + /// Converts a Speckle Point object to a Rhino Point object. + /// + /// The Speckle Point object to convert. + /// The converted Rhino Point object. + /// ⚠️ This conversion does NOT perform scaling. + RG.Point ITypedConverter.Convert(SOG.Point target) => new(Convert(target)); +} diff --git a/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/Raw/PolyCurveToHostConverter.cs b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/Raw/PolyCurveToHostConverter.cs new file mode 100644 index 0000000000..3f35905090 --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/Raw/PolyCurveToHostConverter.cs @@ -0,0 +1,43 @@ +using Objects; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Kits; + +namespace Speckle.Converters.Rhino7.ToHost.Raw; + +public class PolyCurveToHostConverter : ITypedConverter +{ + public ITypedConverter? CurveConverter { get; set; } // POC: CNX-9311 Circular dependency injected by the container using property. + + private readonly ITypedConverter _intervalConverter; + + public PolyCurveToHostConverter(ITypedConverter intervalConverter) + { + _intervalConverter = intervalConverter; + } + + /// + /// Converts a SpecklePolyCurve object to a Rhino PolyCurve object. + /// + /// The SpecklePolyCurve object to convert. + /// The converted Rhino PolyCurve object. + /// ⚠️ This conversion does NOT perform scaling. + public RG.PolyCurve Convert(SOG.Polycurve target) + { + RG.PolyCurve result = new(); + + foreach (var segment in target.segments) + { + var childCurve = CurveConverter.NotNull().Convert(segment); + bool success = result.AppendSegment(childCurve); + if (!success) + { + throw new ConversionException($"Failed to append segment {segment}"); + } + } + + result.Domain = _intervalConverter.Convert(target.domain); + + return result; + } +} diff --git a/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/Raw/PolylineToHostConverter.cs b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/Raw/PolylineToHostConverter.cs new file mode 100644 index 0000000000..5abe48fed5 --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/Raw/PolylineToHostConverter.cs @@ -0,0 +1,61 @@ +using Rhino.Collections; +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.Rhino7.ToHost.Raw; + +public class PolylineToHostConverter + : ITypedConverter, + ITypedConverter +{ + private readonly ITypedConverter, Point3dList> _pointListConverter; + private readonly ITypedConverter _intervalConverter; + + public PolylineToHostConverter( + ITypedConverter, Point3dList> pointListConverter, + ITypedConverter intervalConverter + ) + { + _pointListConverter = pointListConverter; + _intervalConverter = intervalConverter; + } + + /// + /// Converts a Speckle polyline object to a Rhino Polyline object. + /// + /// The Speckle polyline object to be converted. + /// The converted Rhino Polyline object. + /// ⚠️ This conversion does NOT perform scaling. + /// + ///
⚠️ This conversion does not preserve the curve domain. + /// If you need it preserved you must request a conversion to + /// conversion instead + ///
+ public RG.Polyline Convert(SOG.Polyline target) + { + var points = _pointListConverter.Convert(target.value); + + if (target.closed) + { + points.Add(points[0]); + } + + var poly = new RG.Polyline(points); + + return poly; + } + + // POC: CNX-9271 Potential code-smell by directly implementing the interface. We should discuss this further but + // since we're using the interfaces instead of the direct type, this may not be an issue. + /// + /// Converts a Speckle polyline object to a Rhino Polyline object. + /// + /// The Speckle polyline object to be converted. + /// The converted Rhino Polyline object. + /// ⚠️ This conversion does NOT perform scaling. + RG.PolylineCurve ITypedConverter.Convert(SOG.Polyline target) + { + var poly = Convert(target).ToPolylineCurve(); + poly.Domain = _intervalConverter.Convert(target.domain); + return poly; + } +} diff --git a/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/Raw/SpiralToHostConverter.cs b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/Raw/SpiralToHostConverter.cs new file mode 100644 index 0000000000..cb7f5a8745 --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/Raw/SpiralToHostConverter.cs @@ -0,0 +1,31 @@ +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.Rhino7.ToHost.Raw; + +public class SpiralToHostConverter : ITypedConverter +{ + private readonly ITypedConverter _polylineConverter; + private readonly ITypedConverter _intervalConverter; + + public SpiralToHostConverter( + ITypedConverter polylineConverter, + ITypedConverter intervalConverter + ) + { + _polylineConverter = polylineConverter; + _intervalConverter = intervalConverter; + } + + /// + /// Converts a Speckle Spiral object to a Rhino PolylineCurve object. + /// + /// The Speckle Spiral object to be converted. + /// A Rhino PolylineCurve object. + /// ⚠️ This conversion does NOT perform scaling. + public RG.PolylineCurve Convert(SOG.Spiral target) + { + var result = _polylineConverter.Convert(target.displayValue); + result.Domain = _intervalConverter.Convert(target.domain); + return result; + } +} diff --git a/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/Raw/SurfaceToHostConverter.cs b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/Raw/SurfaceToHostConverter.cs new file mode 100644 index 0000000000..91da44ec59 --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/Raw/SurfaceToHostConverter.cs @@ -0,0 +1,66 @@ +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.Rhino7.ToHost.Raw; + +public class SurfaceToHostConverter : ITypedConverter +{ + /// + /// Converts a raw Speckle surface to a Rhino NURBS surface. + /// + /// The raw Speckle surface to convert. + /// The converted Rhino NURBS surface. + /// ⚠️ This conversion does NOT perform scaling. + public RG.NurbsSurface Convert(SOG.Surface target) + { + // Create rhino surface + var points = target.GetControlPoints().ToList(); + + var result = RG.NurbsSurface.Create( + 3, + target.rational, + target.degreeU + 1, + target.degreeV + 1, + points.Count, + points[0].Count + ); + + // Set knot vectors + var correctUKnots = GetCorrectKnots(target.knotsU, target.countU, target.degreeU); + for (int i = 0; i < correctUKnots.Count; i++) + { + result.KnotsU[i] = correctUKnots[i]; + } + + var correctVKnots = GetCorrectKnots(target.knotsV, target.countV, target.degreeV); + for (int i = 0; i < correctVKnots.Count; i++) + { + result.KnotsV[i] = correctVKnots[i]; + } + + // Set control points + for (var i = 0; i < points.Count; i++) + { + for (var j = 0; j < points[i].Count; j++) + { + var pt = points[i][j]; + result.Points.SetPoint(i, j, pt.x * pt.weight, pt.y * pt.weight, pt.z * pt.weight); + result.Points.SetWeight(i, j, pt.weight); + } + } + + // Return surface + return result; + } + + private List GetCorrectKnots(List knots, int controlPointCount, int degree) + { + var correctKnots = knots; + if (knots.Count == controlPointCount + degree + 1) + { + correctKnots.RemoveAt(0); + correctKnots.RemoveAt(correctKnots.Count - 1); + } + + return correctKnots; + } +} diff --git a/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/Raw/VectorToHostConverter.cs b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/Raw/VectorToHostConverter.cs new file mode 100644 index 0000000000..77e017f0fd --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/Raw/VectorToHostConverter.cs @@ -0,0 +1,14 @@ +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.Rhino7.ToHost.Raw; + +public class VectorToHostConverter : ITypedConverter +{ + /// + /// Converts a Speckle.Vector object to a Rhino Vector3d object. + /// + /// The Speckle.Vector to be converted. + /// The converted Rhino Vector3d object. + /// ⚠️ This conversion does NOT perform scaling. + public RG.Vector3d Convert(SOG.Vector target) => new(target.x, target.y, target.z); +} diff --git a/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/TopLevel/ArcToHostTopLevelConverter.cs b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/TopLevel/ArcToHostTopLevelConverter.cs new file mode 100644 index 0000000000..12c4420914 --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/TopLevel/ArcToHostTopLevelConverter.cs @@ -0,0 +1,15 @@ +using Rhino; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.Rhino7.ToHost.TopLevel; + +[NameAndRankValue(nameof(SOG.Arc), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class ArcToHostTopLevelConverter : SpeckleToHostGeometryBaseTopLevelConverter +{ + public ArcToHostTopLevelConverter( + IConversionContextStack contextStack, + ITypedConverter geometryBaseConverter + ) + : base(contextStack, geometryBaseConverter) { } +} diff --git a/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/TopLevel/BrepToHostTopLevelConverter.cs b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/TopLevel/BrepToHostTopLevelConverter.cs new file mode 100644 index 0000000000..6ca70d3ad1 --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/TopLevel/BrepToHostTopLevelConverter.cs @@ -0,0 +1,15 @@ +using Rhino; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.Rhino7.ToHost.TopLevel; + +[NameAndRankValue(nameof(SOG.Brep), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class BrepToHostTopLevelConverter : SpeckleToHostGeometryBaseTopLevelConverter +{ + public BrepToHostTopLevelConverter( + IConversionContextStack contextStack, + ITypedConverter geometryBaseConverter + ) + : base(contextStack, geometryBaseConverter) { } +} diff --git a/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/TopLevel/CircleToHostTopLevelConverter.cs b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/TopLevel/CircleToHostTopLevelConverter.cs new file mode 100644 index 0000000000..1493120e57 --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/TopLevel/CircleToHostTopLevelConverter.cs @@ -0,0 +1,15 @@ +using Rhino; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.Rhino7.ToHost.TopLevel; + +[NameAndRankValue(nameof(SOG.Circle), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class CircleToHostTopLevelConverter : SpeckleToHostGeometryBaseTopLevelConverter +{ + public CircleToHostTopLevelConverter( + IConversionContextStack contextStack, + ITypedConverter geometryBaseConverter + ) + : base(contextStack, geometryBaseConverter) { } +} diff --git a/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/TopLevel/EllipseToHostTopLevelConverter.cs b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/TopLevel/EllipseToHostTopLevelConverter.cs new file mode 100644 index 0000000000..e6a4b03a41 --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/TopLevel/EllipseToHostTopLevelConverter.cs @@ -0,0 +1,15 @@ +using Rhino; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.Rhino7.ToHost.TopLevel; + +[NameAndRankValue(nameof(SOG.Ellipse), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class EllipseToHostTopLevelConverter : SpeckleToHostGeometryBaseTopLevelConverter +{ + public EllipseToHostTopLevelConverter( + IConversionContextStack contextStack, + ITypedConverter geometryBaseConverter + ) + : base(contextStack, geometryBaseConverter) { } +} diff --git a/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/TopLevel/FallbackToHostTopLevelConverter.cs b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/TopLevel/FallbackToHostTopLevelConverter.cs new file mode 100644 index 0000000000..811433890c --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/TopLevel/FallbackToHostTopLevelConverter.cs @@ -0,0 +1,69 @@ +using Rhino; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Kits; +using Speckle.Core.Models; + +namespace Speckle.Converters.Rhino7.ToHost.TopLevel; + +[NameAndRankValue(nameof(DisplayableObject), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class FallbackToHostTopLevelConverter + : IToHostTopLevelConverter, + ITypedConverter> +{ + private readonly ITypedConverter _lineConverter; + private readonly ITypedConverter _polylineConverter; + private readonly ITypedConverter _meshConverter; + private readonly IConversionContextStack _contextStack; + + public FallbackToHostTopLevelConverter( + ITypedConverter lineConverter, + ITypedConverter polylineConverter, + ITypedConverter meshConverter, + IConversionContextStack contextStack + ) + { + _lineConverter = lineConverter; + _polylineConverter = polylineConverter; + _meshConverter = meshConverter; + _contextStack = contextStack; + } + + public object Convert(Base target) => Convert((DisplayableObject)target); + + public List Convert(DisplayableObject target) + { + var result = new List(); + foreach (var item in target.displayValue) + { + RG.GeometryBase x = item switch + { + SOG.Line line => _lineConverter.Convert(line), + SOG.Polyline polyline => _polylineConverter.Convert(polyline), + SOG.Mesh mesh => _meshConverter.Convert(mesh), + _ => throw new NotSupportedException($"Found unsupported fallback geometry: {item.GetType()}") + }; + x.Transform(GetUnitsTransform(item)); + result.Add(x); + } + + return result; + } + + 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, _contextStack.Current.SpeckleUnits); + var scale = RG.Transform.Scale(RG.Point3d.Origin, scaleFactor); + return scale; + } + + return RG.Transform.Identity; + } +} diff --git a/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/TopLevel/LineToHostTopLevelConverter.cs b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/TopLevel/LineToHostTopLevelConverter.cs new file mode 100644 index 0000000000..5a64a3ebab --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/TopLevel/LineToHostTopLevelConverter.cs @@ -0,0 +1,15 @@ +using Rhino; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.Rhino7.ToHost.TopLevel; + +[NameAndRankValue(nameof(SOG.Line), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class LineToHostTopLevelConverter : SpeckleToHostGeometryBaseTopLevelConverter +{ + public LineToHostTopLevelConverter( + IConversionContextStack contextStack, + ITypedConverter geometryBaseConverter + ) + : base(contextStack, geometryBaseConverter) { } +} diff --git a/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/TopLevel/MeshToHostTopLevelConverter.cs b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/TopLevel/MeshToHostTopLevelConverter.cs new file mode 100644 index 0000000000..c7d047df3b --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/TopLevel/MeshToHostTopLevelConverter.cs @@ -0,0 +1,15 @@ +using Rhino; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.Rhino7.ToHost.TopLevel; + +[NameAndRankValue(nameof(SOG.Mesh), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class MeshToHostTopLevelConverter : SpeckleToHostGeometryBaseTopLevelConverter +{ + public MeshToHostTopLevelConverter( + IConversionContextStack contextStack, + ITypedConverter geometryBaseConverter + ) + : base(contextStack, geometryBaseConverter) { } +} diff --git a/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/TopLevel/NurbsCurveToHostTopLevelConverter.cs b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/TopLevel/NurbsCurveToHostTopLevelConverter.cs new file mode 100644 index 0000000000..987b46161f --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/TopLevel/NurbsCurveToHostTopLevelConverter.cs @@ -0,0 +1,15 @@ +using Rhino; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.Rhino7.ToHost.TopLevel; + +[NameAndRankValue(nameof(SOG.Curve), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class NurbsCurveToHostTopLevelConverter : SpeckleToHostGeometryBaseTopLevelConverter +{ + public NurbsCurveToHostTopLevelConverter( + IConversionContextStack contextStack, + ITypedConverter geometryBaseConverter + ) + : base(contextStack, geometryBaseConverter) { } +} diff --git a/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/TopLevel/PointCloudToHostTopLevelConverter.cs b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/TopLevel/PointCloudToHostTopLevelConverter.cs new file mode 100644 index 0000000000..51a2613e90 --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/TopLevel/PointCloudToHostTopLevelConverter.cs @@ -0,0 +1,16 @@ +using Rhino; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.Rhino7.ToHost.TopLevel; + +[NameAndRankValue(nameof(SOG.Pointcloud), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class PointCloudToHostTopLevelConverter + : SpeckleToHostGeometryBaseTopLevelConverter +{ + public PointCloudToHostTopLevelConverter( + IConversionContextStack contextStack, + ITypedConverter geometryBaseConverter + ) + : base(contextStack, geometryBaseConverter) { } +} diff --git a/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/TopLevel/PointToHostTopLevelConverter.cs b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/TopLevel/PointToHostTopLevelConverter.cs new file mode 100644 index 0000000000..de10053ce9 --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/TopLevel/PointToHostTopLevelConverter.cs @@ -0,0 +1,15 @@ +using Rhino; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.Rhino7.ToHost.TopLevel; + +[NameAndRankValue(nameof(SOG.Point), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class PointToHostTopLevelConverter : SpeckleToHostGeometryBaseTopLevelConverter +{ + public PointToHostTopLevelConverter( + IConversionContextStack contextStack, + ITypedConverter geometryBaseConverter + ) + : base(contextStack, geometryBaseConverter) { } +} diff --git a/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/TopLevel/PolycurveToHostTopLevelConverter.cs b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/TopLevel/PolycurveToHostTopLevelConverter.cs new file mode 100644 index 0000000000..d6f8d2f14b --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/TopLevel/PolycurveToHostTopLevelConverter.cs @@ -0,0 +1,15 @@ +using Rhino; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.Rhino7.ToHost.TopLevel; + +[NameAndRankValue(nameof(SOG.Polycurve), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class PolycurveToHostTopLevelConverter : SpeckleToHostGeometryBaseTopLevelConverter +{ + public PolycurveToHostTopLevelConverter( + IConversionContextStack contextStack, + ITypedConverter geometryBaseConverter + ) + : base(contextStack, geometryBaseConverter) { } +} diff --git a/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/TopLevel/PolylineToHostTopLevelConverter.cs b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/TopLevel/PolylineToHostTopLevelConverter.cs new file mode 100644 index 0000000000..081f217f04 --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToHost/TopLevel/PolylineToHostTopLevelConverter.cs @@ -0,0 +1,16 @@ +using Rhino; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.Rhino7.ToHost.TopLevel; + +[NameAndRankValue(nameof(SOG.Polyline), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class PolylineToHostTopLevelConverter + : SpeckleToHostGeometryBaseTopLevelConverter +{ + public PolylineToHostTopLevelConverter( + IConversionContextStack contextStack, + ITypedConverter geometryBaseConverter + ) + : base(contextStack, geometryBaseConverter) { } +} diff --git a/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/Raw/ArcCurveToSpeckleConverter.cs b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/Raw/ArcCurveToSpeckleConverter.cs new file mode 100644 index 0000000000..8a3eee9cb7 --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/Raw/ArcCurveToSpeckleConverter.cs @@ -0,0 +1,61 @@ +using Objects; +using Rhino; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; + +namespace Speckle.Converters.Rhino7.ToSpeckle.Raw; + +public class ArcCurveToSpeckleConverter : ITypedConverter, ITypedConverter +{ + private readonly ITypedConverter _circleConverter; + private readonly ITypedConverter _arcConverter; + private readonly ITypedConverter _intervalConverter; + private readonly IConversionContextStack _contextStack; + + public ArcCurveToSpeckleConverter( + ITypedConverter circleConverter, + ITypedConverter arcConverter, + ITypedConverter intervalConverter, + IConversionContextStack contextStack + ) + { + _circleConverter = circleConverter; + _arcConverter = arcConverter; + _intervalConverter = intervalConverter; + _contextStack = contextStack; + } + + /// + /// Converts an RG.ArcCurve to an ICurve. + /// + /// The RG.ArcCurve to convert. + /// The converted ICurve. + /// + /// ⚠️ If the provided ArcCurve is a complete circle, a Speckle Circle will be returned. + /// Otherwise, the output will be a Speckle Arc.
+ /// ✅ This method preserves the domain of the original ArcCurve.
+ ///
+ public ICurve Convert(RG.ArcCurve target) + { + var tolerance = _contextStack.Current.Document.ModelAbsoluteTolerance; + + if (target.IsCompleteCircle) + { + target.TryGetCircle(out var getObj, tolerance); + var cir = _circleConverter.Convert(getObj); + cir.domain = _intervalConverter.Convert(target.Domain); + return cir; + } + + var arc = _arcConverter.Convert(target.Arc); + arc.domain = _intervalConverter.Convert(target.Domain); + return arc; + } + + // POC: CNX-9275 Need to implement this because ICurve and Base are not related, this one is needed at the top-level, the other is for better typed experience. + // This also causes us to have to force cast ICurve to Base as a result, which is expected to always succeed but not nice. + /// + /// The converted ICurve with a cast to object + Base ITypedConverter.Convert(RG.ArcCurve target) => (Base)Convert(target); +} diff --git a/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/Raw/ArcToSpeckleConverter.cs b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/Raw/ArcToSpeckleConverter.cs new file mode 100644 index 0000000000..82c89c2551 --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/Raw/ArcToSpeckleConverter.cs @@ -0,0 +1,52 @@ +using Rhino; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.Rhino7.ToSpeckle.Raw; + +public class ArcToSpeckleConverter : ITypedConverter +{ + private readonly ITypedConverter _pointConverter; + private readonly ITypedConverter _planeConverter; + private readonly ITypedConverter _boxConverter; + private readonly IConversionContextStack _contextStack; + + public ArcToSpeckleConverter( + ITypedConverter pointConverter, + ITypedConverter planeConverter, + ITypedConverter boxConverter, + IConversionContextStack contextStack + ) + { + _pointConverter = pointConverter; + _planeConverter = planeConverter; + _boxConverter = boxConverter; + _contextStack = contextStack; + } + + /// + /// Converts a Rhino Arc object to a Speckle Arc object. + /// + /// The Rhino Arc object to convert. + /// The converted Speckle Arc object. + /// + /// This method assumes the domain of the arc is (0,1) as Arc types in Rhino do not have domain. You may want to request a conversion from ArcCurve instead. + /// + public SOG.Arc Convert(RG.Arc target) => + new( + _planeConverter.Convert(target.Plane), + target.Radius, + target.StartAngle, + target.EndAngle, + target.Angle, + _contextStack.Current.SpeckleUnits + ) + { + startPoint = _pointConverter.Convert(target.StartPoint), + midPoint = _pointConverter.Convert(target.MidPoint), + endPoint = _pointConverter.Convert(target.EndPoint), + domain = new SOP.Interval(0, 1), + length = target.Length, + bbox = _boxConverter.Convert(new RG.Box(target.BoundingBox())) + }; +} diff --git a/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/Raw/BoxToSpeckleConverter.cs b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/Raw/BoxToSpeckleConverter.cs new file mode 100644 index 0000000000..cebd6e5d70 --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/Raw/BoxToSpeckleConverter.cs @@ -0,0 +1,41 @@ +using Rhino; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.Rhino7.ToSpeckle.Raw; + +public class BoxToSpeckleConverter : ITypedConverter +{ + private readonly ITypedConverter _planeConverter; + private readonly ITypedConverter _intervalConverter; + private readonly IConversionContextStack _contextStack; + + public BoxToSpeckleConverter( + ITypedConverter planeConverter, + ITypedConverter intervalConverter, + IConversionContextStack contextStack + ) + { + _planeConverter = planeConverter; + _intervalConverter = intervalConverter; + _contextStack = contextStack; + } + + /// + /// Converts a Rhino Box object to a Speckle Box object. + /// + /// The Rhino Box object to convert. + /// The converted Speckle Box object. + public SOG.Box Convert(RG.Box target) => + new( + _planeConverter.Convert(target.Plane), + _intervalConverter.Convert(target.X), + _intervalConverter.Convert(target.Y), + _intervalConverter.Convert(target.Z), + _contextStack.Current.SpeckleUnits + ) + { + area = target.Area, + volume = target.Volume + }; +} diff --git a/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/Raw/BrepToSpeckleConverter.cs b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/Raw/BrepToSpeckleConverter.cs new file mode 100644 index 0000000000..e74bb2f374 --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/Raw/BrepToSpeckleConverter.cs @@ -0,0 +1,205 @@ +using Objects; +using Rhino; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Kits; +using Speckle.Core.Logging; + +namespace Speckle.Converters.Rhino7.ToSpeckle.Raw; + +public class BrepToSpeckleConverter : ITypedConverter +{ + private readonly ITypedConverter _pointConverter; + private readonly ITypedConverter _curveConverter; + private readonly ITypedConverter _surfaceConverter; + private readonly ITypedConverter _meshConverter; + private readonly ITypedConverter _boxConverter; + private readonly ITypedConverter _intervalConverter; + private readonly IConversionContextStack _contextStack; + + public BrepToSpeckleConverter( + ITypedConverter pointConverter, + ITypedConverter curveConverter, + ITypedConverter surfaceConverter, + ITypedConverter meshConverter, + ITypedConverter boxConverter, + ITypedConverter intervalConverter, + IConversionContextStack contextStack + ) + { + _pointConverter = pointConverter; + _curveConverter = curveConverter; + _surfaceConverter = surfaceConverter; + _meshConverter = meshConverter; + _boxConverter = boxConverter; + _intervalConverter = intervalConverter; + _contextStack = contextStack; + } + + /// + /// Converts a Brep object to a Speckle Brep object. + /// + /// The Brep object to convert. + /// The converted Speckle Brep object. + public SOG.Brep Convert(RG.Brep target) + { + var tol = _contextStack.Current.Document.ModelAbsoluteTolerance; + target.Repair(tol); + + // POC: CNX-9276 This should come as part of the user settings in the context object. + // if (PreprocessGeometry) + // { + // brep = BrepEncoder.ToRawBrep(brep, 1.0, Doc.ModelAngleToleranceRadians, Doc.ModelRelativeTolerance); + // } + + // get display mesh and attach render material to it if it exists + var displayMesh = GetBrepDisplayMesh(target); + var displayValue = new List(); + if (displayMesh != null) + { + displayValue.Add(_meshConverter.Convert(displayMesh)); + } + + // POC: CNX-9277 Swap input material for something coming from the context. + // if (displayValue != null && mat != null) + // { + // displayValue["renderMaterial"] = mat; + // } + + // Vertices, uv curves, 3d curves and surfaces + var vertices = target.Vertices.Select(vertex => _pointConverter.Convert(vertex.Location)).ToList(); + var curves3d = target.Curves3D.Select(curve3d => _curveConverter.Convert(curve3d)).ToList(); + var surfaces = target.Surfaces.Select(srf => _surfaceConverter.Convert(srf.ToNurbsSurface())).ToList(); + + List curves2d; + using (_contextStack.Push(Units.None)) + { + // Curves2D are unitless, so we convert them within a new pushed context with None units. + curves2d = target.Curves2D.Select(curve2d => _curveConverter.Convert(curve2d)).ToList(); + } + + var speckleBrep = new SOG.Brep + { + Vertices = vertices, + Curve3D = curves3d, + Curve2D = curves2d, + Surfaces = surfaces, + displayValue = displayValue, + IsClosed = target.IsSolid, + Orientation = (SOG.BrepOrientation)target.SolidOrientation, + volume = target.IsSolid ? target.GetVolume() : 0, + area = target.GetArea(), + bbox = _boxConverter.Convert(new RG.Box(target.GetBoundingBox(false))), + units = _contextStack.Current.SpeckleUnits + }; + + // Brep non-geometry types + var faces = ConvertBrepFaces(target, speckleBrep); + var edges = ConvertBrepEdges(target, speckleBrep); + var loops = ConvertBrepLoops(target, speckleBrep); + var trims = ConvertBrepTrims(target, speckleBrep); + + speckleBrep.Faces = faces; + speckleBrep.Edges = edges; + speckleBrep.Loops = loops; + speckleBrep.Trims = trims; + return speckleBrep; + } + + private static List ConvertBrepFaces(RG.Brep brep, SOG.Brep speckleParent) => + brep.Faces + .Select( + f => + new SOG.BrepFace( + speckleParent, + f.SurfaceIndex, + f.Loops.Select(l => l.LoopIndex).ToList(), + f.OuterLoop.LoopIndex, + f.OrientationIsReversed + ) + ) + .ToList(); + + private List ConvertBrepEdges(RG.Brep brep, SOG.Brep speckleParent) => + brep.Edges + .Select( + edge => + new SOG.BrepEdge( + speckleParent, + edge.EdgeCurveIndex, + edge.TrimIndices(), + edge.StartVertex.VertexIndex, + edge.EndVertex.VertexIndex, + edge.ProxyCurveIsReversed, + _intervalConverter.Convert(edge.Domain) + ) + ) + .ToList(); + + private List ConvertBrepTrims(RG.Brep brep, SOG.Brep speckleParent) => + brep.Trims + .Select(trim => + { + var t = new SOG.BrepTrim( + speckleParent, + trim.Edge.EdgeIndex, + trim.Face.FaceIndex, + trim.Loop.LoopIndex, + trim.TrimCurveIndex, + (int)trim.IsoStatus, + (SOG.BrepTrimType)trim.TrimType, + trim.IsReversed(), + trim.StartVertex.VertexIndex, + trim.EndVertex.VertexIndex + ) + { + Domain = _intervalConverter.Convert(trim.Domain) + }; + + return t; + }) + .ToList(); + + private List ConvertBrepLoops(RG.Brep brep, SOG.Brep speckleParent) => + brep.Loops + .Select( + loop => + new SOG.BrepLoop( + speckleParent, + loop.Face.FaceIndex, + loop.Trims.Select(t => t.TrimIndex).ToList(), + (SOG.BrepLoopType)loop.LoopType + ) + ) + .ToList(); + + private RG.Mesh? GetBrepDisplayMesh(RG.Brep brep) + { + var joinedMesh = new RG.Mesh(); + + // get from settings + //Settings.TryGetValue("sendMeshSetting", out string meshSetting); + + RG.MeshingParameters mySettings = new(0.05, 0.05); + // switch (SelectedMeshSettings) + // { + // case MeshSettings.CurrentDoc: + // mySettings = RH.MeshingParameters.DocumentCurrentSetting(Doc); + // break; + // case MeshSettings.Default: + // default: + // mySettings = new RH.MeshingParameters(0.05, 0.05); + // break; + // } + + try + { + joinedMesh.Append(RG.Mesh.CreateFromBrep(brep, mySettings)); + return joinedMesh; + } + catch (Exception ex) when (!ex.IsFatal()) + { + return null; + } + } +} diff --git a/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/Raw/CircleToSpeckleConverter.cs b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/Raw/CircleToSpeckleConverter.cs new file mode 100644 index 0000000000..0e3f91e3bf --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/Raw/CircleToSpeckleConverter.cs @@ -0,0 +1,39 @@ +using Rhino; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; + +namespace Speckle.Converters.Rhino7.ToSpeckle.Raw; + +public class CircleToSpeckleConverter : ITypedConverter +{ + private readonly ITypedConverter _planeConverter; + private readonly IConversionContextStack _contextStack; + + public CircleToSpeckleConverter( + ITypedConverter planeConverter, + IConversionContextStack contextStack + ) + { + _planeConverter = planeConverter; + _contextStack = contextStack; + } + + public Base Convert(object target) => Convert((RG.Circle)target); + + /// + /// Converts a RG.Circle object to a SOG.Circle object. + /// + /// The RG.Circle object to convert. + /// The converted SOG.Circle object. + /// + /// ⚠️ This conversion assumes the domain of a circle is (0,1) as Rhino Circle types do not have a domain. If you want to preserve the domain use ArcCurve conversion instead. + /// + public SOG.Circle Convert(RG.Circle target) => + new(_planeConverter.Convert(target.Plane), target.Radius, _contextStack.Current.SpeckleUnits) + { + domain = new SOP.Interval(0, 1), + length = 2 * Math.PI * target.Radius, + area = Math.PI * Math.Pow(target.Radius, 2) + }; +} diff --git a/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/Raw/ControlPointToSpeckleConverter.cs b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/Raw/ControlPointToSpeckleConverter.cs new file mode 100644 index 0000000000..d3747e2faf --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/Raw/ControlPointToSpeckleConverter.cs @@ -0,0 +1,26 @@ +using Rhino; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; + +namespace Speckle.Converters.Rhino7.ToSpeckle.Raw; + +public class ControlPointToSpeckleConverter : ITypedConverter +{ + private readonly IConversionContextStack _contextStack; + + public ControlPointToSpeckleConverter(IConversionContextStack contextStack) + { + _contextStack = contextStack; + } + + /// + /// Converts a ControlPoint object to a Speckle ControlPoint object. + /// + /// The ControlPoint object to convert. + /// The converted Speckle ControlPoint object. + public SOG.ControlPoint Convert(RG.ControlPoint target) => + new(target.Location.X, target.Location.Y, target.Location.Z, target.Weight, _contextStack.Current.SpeckleUnits); + + public Base Convert(object target) => Convert((RG.ControlPoint)target); +} diff --git a/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/Raw/CurveToSpeckleConverter.cs b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/Raw/CurveToSpeckleConverter.cs new file mode 100644 index 0000000000..5c792ebaf8 --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/Raw/CurveToSpeckleConverter.cs @@ -0,0 +1,51 @@ +using Objects; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; + +namespace Speckle.Converters.Rhino7.ToSpeckle.Raw; + +// POC: CNX-9278 This converter decides which specific curve conversion to use. IIndex may be a better choice. +public class CurveToSpeckleConverter : ITypedConverter, ITypedConverter +{ + private readonly ITypedConverter _polyCurveConverter; + private readonly ITypedConverter _arcCurveConverter; + private readonly ITypedConverter _polylineConverter; + private readonly ITypedConverter _nurbsCurveConverter; + private readonly ITypedConverter _lineCurveConverter; + + public CurveToSpeckleConverter( + ITypedConverter polyCurveConverter, + ITypedConverter arcCurveConverter, + ITypedConverter polylineConverter, + ITypedConverter nurbsCurveConverter, + ITypedConverter lineCurveConverter + ) + { + _polyCurveConverter = polyCurveConverter; + _arcCurveConverter = arcCurveConverter; + _polylineConverter = polylineConverter; + _nurbsCurveConverter = nurbsCurveConverter; + _lineCurveConverter = lineCurveConverter; + } + + /// + /// Converts a Rhino curve to a Speckle ICurve. + /// + /// The Rhino curve to convert. + /// The Speckle curve. + /// + /// This is the main converter when the type of curve you input or output does not matter to the caller.
+ /// ⚠️ If an unsupported type of Curve is input, it will be converted to NURBS. + ///
+ public ICurve Convert(RG.Curve target) => + target switch + { + RG.PolyCurve polyCurve => _polyCurveConverter.Convert(polyCurve), + RG.ArcCurve arcCurve => _arcCurveConverter.Convert(arcCurve), + RG.PolylineCurve polylineCurve => _polylineConverter.Convert(polylineCurve), + RG.LineCurve lineCurve => _lineCurveConverter.Convert(lineCurve), + _ => _nurbsCurveConverter.Convert(target.ToNurbsCurve()) + }; + + Base ITypedConverter.Convert(RG.Curve target) => (Base)Convert(target); +} diff --git a/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/Raw/EllipseToSpeckleConverter.cs b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/Raw/EllipseToSpeckleConverter.cs new file mode 100644 index 0000000000..fc6b52cc8f --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/Raw/EllipseToSpeckleConverter.cs @@ -0,0 +1,48 @@ +using Rhino; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.Rhino7.ToSpeckle.Raw; + +public class EllipseToSpeckleConverter : ITypedConverter +{ + private readonly ITypedConverter _planeConverter; + private readonly ITypedConverter _boxConverter; + private readonly IConversionContextStack _contextStack; + + public EllipseToSpeckleConverter( + ITypedConverter planeConverter, + ITypedConverter boxConverter, + IConversionContextStack contextStack + ) + { + _planeConverter = planeConverter; + _boxConverter = boxConverter; + _contextStack = contextStack; + } + + /// + /// Converts a Rhino Ellipse to a Speckle Ellipse. + /// + /// The Rhino Ellipse to convert. + /// The converted Speckle Ellipse. + /// + /// ⚠️ Rhino ellipses are not curves. The result is a mathematical representation of an ellipse that can be converted into NURBS for display. + /// + public SOG.Ellipse Convert(RG.Ellipse target) + { + var nurbsCurve = target.ToNurbsCurve(); + return new( + _planeConverter.Convert(target.Plane), + target.Radius1, + target.Radius2, + _contextStack.Current.SpeckleUnits + ) + { + domain = new SOP.Interval(0, 1), + length = nurbsCurve.GetLength(), + area = Math.PI * target.Radius1 * target.Radius2, + bbox = _boxConverter.Convert(new RG.Box(nurbsCurve.GetBoundingBox(true))) + }; + } +} diff --git a/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/Raw/IntervalToSpeckleConverter.cs b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/Raw/IntervalToSpeckleConverter.cs new file mode 100644 index 0000000000..272fecafdb --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/Raw/IntervalToSpeckleConverter.cs @@ -0,0 +1,13 @@ +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.Rhino7.ToSpeckle.Raw; + +public class IntervalToSpeckleConverter : ITypedConverter +{ + /// + /// Converts a Rhino Interval object to a Speckle Interval object. + /// + /// The Rhino Interval object to be converted. + /// The converted Speckle Interval object. + public SOP.Interval Convert(RG.Interval target) => new(target.T0, target.T1); +} diff --git a/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/Raw/LineToSpeckleConverter.cs b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/Raw/LineToSpeckleConverter.cs new file mode 100644 index 0000000000..f40620f78d --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/Raw/LineToSpeckleConverter.cs @@ -0,0 +1,41 @@ +using Rhino; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.Rhino7.ToSpeckle.Raw; + +public class LineToSpeckleConverter : ITypedConverter, ITypedConverter +{ + private readonly ITypedConverter _pointConverter; + private readonly ITypedConverter _boxConverter; + private readonly IConversionContextStack _contextStack; + + public LineToSpeckleConverter( + ITypedConverter pointConverter, + ITypedConverter boxConverter, + IConversionContextStack contextStack + ) + { + _pointConverter = pointConverter; + _boxConverter = boxConverter; + _contextStack = contextStack; + } + + /// + /// Converts a Rhino Line object to a Speckle Line object. + /// + /// The Rhino Line object to convert. + /// The converted Speckle Line object. + /// + /// ⚠️ This conversion assumes the domain of a line is (0, LENGTH), as Rhino Lines do not have domain. If you want the domain preserved use LineCurve conversions instead. + /// + public SOG.Line Convert(RG.Line target) => + new(_pointConverter.Convert(target.From), _pointConverter.Convert(target.To), _contextStack.Current.SpeckleUnits) + { + length = target.Length, + domain = new SOP.Interval(0, target.Length), + bbox = _boxConverter.Convert(new RG.Box(target.BoundingBox)) + }; + + public SOG.Line Convert(RG.LineCurve target) => Convert(target.Line); +} diff --git a/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/Raw/MeshToSpeckleConverter.cs b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/Raw/MeshToSpeckleConverter.cs new file mode 100644 index 0000000000..73aa3f4a8a --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/Raw/MeshToSpeckleConverter.cs @@ -0,0 +1,66 @@ +using Rhino; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.Rhino7.ToSpeckle.Raw; + +[NameAndRankValue(nameof(RG.Mesh), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class MeshToSpeckleConverter : ITypedConverter +{ + private readonly ITypedConverter _pointConverter; + private readonly ITypedConverter _boxConverter; + private readonly IConversionContextStack _contextStack; + + public MeshToSpeckleConverter( + ITypedConverter pointConverter, + ITypedConverter boxConverter, + IConversionContextStack contextStack + ) + { + _pointConverter = pointConverter; + _boxConverter = boxConverter; + _contextStack = contextStack; + } + + /// + /// Converts a Rhino Mesh to a Speckle Mesh. + /// + /// The Rhino Mesh to be converted. + /// The converted Speckle Mesh. + /// Thrown when the Rhino Mesh has 0 vertices or faces. + public SOG.Mesh Convert(RG.Mesh target) + { + if (target.Vertices.Count == 0 || target.Faces.Count == 0) + { + throw new SpeckleConversionException("Cannot convert a mesh with 0 vertices/faces"); + } + + var vertexCoordinates = target.Vertices.ToPoint3dArray().SelectMany(pt => new[] { pt.X, pt.Y, pt.Z }).ToList(); + var faces = new List(); + + 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)); + } + + var textureCoordinates = new List(target.TextureCoordinates.Count * 2); + foreach (var textureCoord in target.TextureCoordinates) + { + textureCoordinates.Add(textureCoord.X); + textureCoordinates.Add(textureCoord.Y); + } + + var colors = target.VertexColors.Select(cl => cl.ToArgb()).ToList(); + var volume = target.IsClosed ? target.Volume() : 0; + var bbox = _boxConverter.Convert(new RG.Box(target.GetBoundingBox(false))); + + return new SOG.Mesh(vertexCoordinates, faces, colors, textureCoordinates, _contextStack.Current.SpeckleUnits) + { + volume = volume, + bbox = bbox + }; + } +} diff --git a/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/Raw/NurbsCurveConverter.cs b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/Raw/NurbsCurveConverter.cs new file mode 100644 index 0000000000..6cf8dca53d --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/Raw/NurbsCurveConverter.cs @@ -0,0 +1,71 @@ +using Rhino; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.Rhino7.ToSpeckle.Raw; + +public class NurbsCurveConverter : ITypedConverter +{ + private readonly ITypedConverter _polylineConverter; + private readonly ITypedConverter _intervalConverter; + private readonly ITypedConverter _boxConverter; + private readonly IConversionContextStack _contextStack; + + public NurbsCurveConverter( + ITypedConverter polylineConverter, + ITypedConverter intervalConverter, + ITypedConverter boxConverter, + IConversionContextStack contextStack + ) + { + _polylineConverter = polylineConverter; + _intervalConverter = intervalConverter; + _boxConverter = boxConverter; + _contextStack = contextStack; + } + + /// + /// Converts a NurbsCurve object to a Speckle Curve object. + /// + /// The NurbsCurve object to convert. + /// The converted Speckle Curve object. + /// + /// ⚠️ This conversion does not respect Rhino knot vector structure. + /// It adds 1 extra knot at the start and end of the vector by repeating the first and last value. + /// This is because Rhino's standard of (controlPoints + degree + 1) wasn't followed on other software. + /// + public SOG.Curve Convert(RG.NurbsCurve target) + { + target.ToPolyline(0, 1, 0, 0, 0, 0.1, 0, 0, true).TryGetPolyline(out var poly); + if (target.IsClosed) + { + poly.Add(poly[0]); + } + + SOG.Polyline displayValue = _polylineConverter.Convert(poly); + + var nurbsCurve = target.ToNurbsCurve(); + + // increase knot multiplicity to (# control points + degree + 1) + // add extra knots at start & end because Rhino's knot multiplicity standard is (# control points + degree - 1) + var knots = nurbsCurve.Knots.ToList(); + knots.Insert(0, knots[0]); + knots.Insert(knots.Count - 1, knots[^1]); + + var myCurve = new SOG.Curve(displayValue, _contextStack.Current.SpeckleUnits) + { + weights = nurbsCurve.Points.Select(ctp => ctp.Weight).ToList(), + points = nurbsCurve.Points.SelectMany(ctp => new[] { ctp.Location.X, ctp.Location.Y, ctp.Location.Z }).ToList(), + knots = knots, + degree = nurbsCurve.Degree, + periodic = nurbsCurve.IsPeriodic, + rational = nurbsCurve.IsRational, + domain = _intervalConverter.Convert(nurbsCurve.Domain), + closed = nurbsCurve.IsClosed, + length = nurbsCurve.GetLength(), + bbox = _boxConverter.Convert(new RG.Box(nurbsCurve.GetBoundingBox(true))) + }; + + return myCurve; + } +} diff --git a/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/Raw/NurbsSurfaceToSpeckleConverter.cs b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/Raw/NurbsSurfaceToSpeckleConverter.cs new file mode 100644 index 0000000000..0d663ff520 --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/Raw/NurbsSurfaceToSpeckleConverter.cs @@ -0,0 +1,72 @@ +using Rhino; +using Rhino.Geometry.Collections; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.Rhino7.ToSpeckle.Raw; + +public class NurbsSurfaceToSpeckleConverter : ITypedConverter +{ + private readonly ITypedConverter _boxConverter; + private readonly ITypedConverter _intervalConverter; + private readonly ITypedConverter _controlPointConverter; + private readonly IConversionContextStack _contextStack; + + public NurbsSurfaceToSpeckleConverter( + ITypedConverter boxConverter, + ITypedConverter intervalConverter, + ITypedConverter controlPointConverter, + IConversionContextStack contextStack + ) + { + _boxConverter = boxConverter; + _intervalConverter = intervalConverter; + _controlPointConverter = controlPointConverter; + _contextStack = contextStack; + } + + /// + /// 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 result = new SOG.Surface + { + 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 = _contextStack.Current.SpeckleUnits, + bbox = _boxConverter.Convert(new RG.Box(target.GetBoundingBox(true))) + }; + + result.SetControlPoints(ControlPointsToSpeckle(target.Points)); + + 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/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/Raw/PlaneToSpeckleConverter.cs b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/Raw/PlaneToSpeckleConverter.cs new file mode 100644 index 0000000000..40d15be50c --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/Raw/PlaneToSpeckleConverter.cs @@ -0,0 +1,37 @@ +using Rhino; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.Rhino7.ToSpeckle.Raw; + +public class PlaneToSpeckleConverter : ITypedConverter +{ + private readonly ITypedConverter _vectorConverter; + private readonly ITypedConverter _pointConverter; + private readonly IConversionContextStack _contextStack; + + public PlaneToSpeckleConverter( + ITypedConverter vectorConverter, + ITypedConverter pointConverter, + IConversionContextStack contextStack + ) + { + _vectorConverter = vectorConverter; + _pointConverter = pointConverter; + _contextStack = contextStack; + } + + /// + /// Converts an instance of Rhino Plane to Speckle Plane. + /// + /// The instance of Rhino Plane to convert. + /// The converted instance of Speckle Plane. + public SOG.Plane Convert(RG.Plane target) => + new( + _pointConverter.Convert(target.Origin), + _vectorConverter.Convert(target.ZAxis), + _vectorConverter.Convert(target.XAxis), + _vectorConverter.Convert(target.YAxis), + _contextStack.Current.SpeckleUnits + ); +} diff --git a/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/Raw/PointToSpeckleConverter.cs b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/Raw/PointToSpeckleConverter.cs new file mode 100644 index 0000000000..2bfdfdae1c --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/Raw/PointToSpeckleConverter.cs @@ -0,0 +1,24 @@ +using Rhino; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.Rhino7.ToSpeckle.Raw; + +public class PointToSpeckleConverter : ITypedConverter, ITypedConverter +{ + private readonly IConversionContextStack _contextStack; + + public PointToSpeckleConverter(IConversionContextStack contextStack) + { + _contextStack = contextStack; + } + + /// + /// Converts a Rhino 3D point to a Speckle point. + /// + /// The Rhino 3D point to convert. + /// The converted Speckle point. + public SOG.Point Convert(RG.Point3d target) => new(target.X, target.Y, target.Z, _contextStack.Current.SpeckleUnits); + + public SOG.Point Convert(RG.Point target) => Convert(target.Location); +} diff --git a/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/Raw/PolyCurveToSpeckleConverter.cs b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/Raw/PolyCurveToSpeckleConverter.cs new file mode 100644 index 0000000000..6937d6b007 --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/Raw/PolyCurveToSpeckleConverter.cs @@ -0,0 +1,48 @@ +using Objects; +using Rhino; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.Rhino7.ToSpeckle.Raw; + +public class PolyCurveToSpeckleConverter : ITypedConverter +{ + public ITypedConverter? CurveConverter { get; set; } // POC: CNX-9279 This created a circular dependency on the constructor, making it a property allows for the container to resolve it correctly + private readonly ITypedConverter _intervalConverter; + private readonly ITypedConverter _boxConverter; + private readonly IConversionContextStack _contextStack; + + public PolyCurveToSpeckleConverter( + ITypedConverter intervalConverter, + ITypedConverter boxConverter, + IConversionContextStack contextStack + ) + { + _intervalConverter = intervalConverter; + _boxConverter = boxConverter; + _contextStack = contextStack; + } + + /// + /// Converts a Rhino PolyCurve to a Speckle Polycurve. + /// + /// The Rhino PolyCurve to convert. + /// The converted Speckle Polycurve. + /// + /// This method removes the nesting of the PolyCurve by duplicating the segments at a granular level. + /// All PolyLIne, PolyCurve and NURBS curves with G1 discontinuities will be broken down. + /// + public SOG.Polycurve Convert(RG.PolyCurve target) + { + var myPoly = new SOG.Polycurve + { + closed = target.IsClosed, + domain = _intervalConverter.Convert(target.Domain), + length = target.GetLength(), + bbox = _boxConverter.Convert(new RG.Box(target.GetBoundingBox(true))), + segments = target.DuplicateSegments().Select(x => CurveConverter.NotNull().Convert(x)).ToList(), + units = _contextStack.Current.SpeckleUnits + }; + return myPoly; + } +} diff --git a/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/Raw/PolylineToSpeckleConverter.cs b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/Raw/PolylineToSpeckleConverter.cs new file mode 100644 index 0000000000..1ee241e7d1 --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/Raw/PolylineToSpeckleConverter.cs @@ -0,0 +1,69 @@ +using Rhino; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.Rhino7.ToSpeckle.Raw; + +public class PolylineToSpeckleConverter + : ITypedConverter, + ITypedConverter +{ + private readonly ITypedConverter _pointConverter; + private readonly ITypedConverter _boxConverter; + private readonly ITypedConverter _intervalConverter; + private readonly IConversionContextStack _contextStack; + + public PolylineToSpeckleConverter( + ITypedConverter pointConverter, + ITypedConverter boxConverter, + IConversionContextStack contextStack, + ITypedConverter intervalConverter + ) + { + _pointConverter = pointConverter; + _boxConverter = boxConverter; + _contextStack = contextStack; + _intervalConverter = intervalConverter; + } + + /// + /// Converts the given Rhino polyline to a Speckle polyline. + /// + /// The Rhino polyline to be converted. + /// The converted Speckle polyline. + /// ⚠️ This conversion assumes domain interval is (0,LENGTH) as Rhino Polylines have no domain. If needed, you may want to use PolylineCurve conversion instead. + public SOG.Polyline Convert(RG.Polyline target) + { + var box = _boxConverter.Convert(new RG.Box(target.BoundingBox)); + var points = target.Select(pt => _pointConverter.Convert(pt)).ToList(); + + if (target.IsClosed) + { + points.RemoveAt(points.Count - 1); + } + + return new SOG.Polyline( + points.SelectMany(pt => new[] { pt.x, pt.y, pt.z }).ToList(), + _contextStack.Current.SpeckleUnits + ) + { + bbox = box, + length = target.Length, + domain = new(0, target.Length), + closed = target.IsClosed + }; + } + + /// + /// Converts the given Rhino PolylineCurve to a Speckle polyline. + /// + /// The Rhino PolylineCurve to be converted. + /// The converted Speckle polyline. + /// ✅ This conversion respects the domain of the original PolylineCurve + public SOG.Polyline Convert(RG.PolylineCurve target) + { + var result = Convert(target.ToPolyline()); + result.domain = _intervalConverter.Convert(target.Domain); + return result; + } +} diff --git a/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/Raw/RawPointCloudToSpeckle.cs b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/Raw/RawPointCloudToSpeckle.cs new file mode 100644 index 0000000000..5e4a17e6cb --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/Raw/RawPointCloudToSpeckle.cs @@ -0,0 +1,34 @@ +using Rhino; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.Rhino7.ToSpeckle.Raw; + +public class RawPointCloudToSpeckle : ITypedConverter +{ + private readonly IConversionContextStack _contextStack; + private readonly ITypedConverter _boxConverter; + + public RawPointCloudToSpeckle( + IConversionContextStack contextStack, + ITypedConverter boxConverter + ) + { + _contextStack = contextStack; + _boxConverter = boxConverter; + } + + /// + /// Converts a Rhino PointCloud object to a Speckle Pointcloud object. + /// + /// The Rhino PointCloud object to convert. + /// The converted Speckle Pointcloud object. + public SOG.Pointcloud Convert(RG.PointCloud target) => + new() + { + points = target.GetPoints().SelectMany(pt => new[] { pt.X, pt.Y, pt.Z }).ToList(), + colors = target.GetColors().Select(o => o.ToArgb()).ToList(), + bbox = _boxConverter.Convert(new RG.Box(target.GetBoundingBox(true))), + units = _contextStack.Current.SpeckleUnits + }; +} diff --git a/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/Raw/VectorToSpeckleConverter.cs b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/Raw/VectorToSpeckleConverter.cs new file mode 100644 index 0000000000..489eddedc4 --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/Raw/VectorToSpeckleConverter.cs @@ -0,0 +1,23 @@ +using Rhino; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.Rhino7.ToSpeckle.Raw; + +public class VectorToSpeckleConverter : ITypedConverter +{ + private readonly IConversionContextStack _contextStack; + + public VectorToSpeckleConverter(IConversionContextStack contextStack) + { + _contextStack = contextStack; + } + + /// + /// Converts a Rhino Vector3d object to a Speckle Vector object. + /// + /// The Rhino Vector3d object to convert. + /// The converted Speckle Vector object. + public SOG.Vector Convert(RG.Vector3d target) => + new(target.X, target.Y, target.Z, _contextStack.Current.SpeckleUnits); +} diff --git a/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/TopLevel/BrepObjectToSpeckleTopLevelConverter.cs b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/TopLevel/BrepObjectToSpeckleTopLevelConverter.cs new file mode 100644 index 0000000000..7520c1bd83 --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/TopLevel/BrepObjectToSpeckleTopLevelConverter.cs @@ -0,0 +1,24 @@ +using Rhino.DocObjects; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; + +namespace Speckle.Converters.Rhino7.ToSpeckle.TopLevel; + +[NameAndRankValue(nameof(BrepObject), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class BrepObjectToSpeckleTopLevelConverter : IToSpeckleTopLevelConverter +{ + private readonly ITypedConverter _curveConverter; + + public BrepObjectToSpeckleTopLevelConverter(ITypedConverter curveConverter) + { + _curveConverter = curveConverter; + } + + public Base Convert(object target) + { + var curveObject = (BrepObject)target; + var speckleCurve = _curveConverter.Convert(curveObject.BrepGeometry); + return speckleCurve; + } +} diff --git a/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/TopLevel/CurveObjectToSpeckleTopLevelConverter.cs b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/TopLevel/CurveObjectToSpeckleTopLevelConverter.cs new file mode 100644 index 0000000000..a8214a0c97 --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/TopLevel/CurveObjectToSpeckleTopLevelConverter.cs @@ -0,0 +1,15 @@ +using Rhino.DocObjects; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; + +namespace Speckle.Converters.Rhino7.ToSpeckle.TopLevel; + +[NameAndRankValue(nameof(CurveObject), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class CurveObjectToSpeckleTopLevelConverter : RhinoObjectToSpeckleTopLevelConverter +{ + public CurveObjectToSpeckleTopLevelConverter(ITypedConverter conversion) + : base(conversion) { } + + protected override RG.Curve GetTypedGeometry(CurveObject input) => input.CurveGeometry; +} diff --git a/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/TopLevel/ExtrusionObjectToSpeckleTopLevelConverter.cs b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/TopLevel/ExtrusionObjectToSpeckleTopLevelConverter.cs new file mode 100644 index 0000000000..46d2c27586 --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/TopLevel/ExtrusionObjectToSpeckleTopLevelConverter.cs @@ -0,0 +1,24 @@ +using Rhino.DocObjects; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; + +namespace Speckle.Converters.Rhino7.ToSpeckle.TopLevel; + +[NameAndRankValue(nameof(ExtrusionObject), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class ExtrusionObjectToSpeckleTopLevelConverter : IToSpeckleTopLevelConverter +{ + private readonly ITypedConverter _curveConverter; + + public ExtrusionObjectToSpeckleTopLevelConverter(ITypedConverter curveConverter) + { + _curveConverter = curveConverter; + } + + public Base Convert(object target) + { + var curveObject = (ExtrusionObject)target; + var speckleCurve = _curveConverter.Convert(curveObject.ExtrusionGeometry.ToBrep()); + return speckleCurve; + } +} diff --git a/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/TopLevel/MeshObjectToSpeckleTopLevelConverter.cs b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/TopLevel/MeshObjectToSpeckleTopLevelConverter.cs new file mode 100644 index 0000000000..f1c9675a04 --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/TopLevel/MeshObjectToSpeckleTopLevelConverter.cs @@ -0,0 +1,14 @@ +using Rhino.DocObjects; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.Rhino7.ToSpeckle.TopLevel; + +[NameAndRankValue(nameof(MeshObject), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class MeshObjectToSpeckleTopLevelConverter : RhinoObjectToSpeckleTopLevelConverter +{ + public MeshObjectToSpeckleTopLevelConverter(ITypedConverter conversion) + : base(conversion) { } + + protected override RG.Mesh GetTypedGeometry(MeshObject input) => input.MeshGeometry; +} diff --git a/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/TopLevel/PointCloudObjectToSpeckleTopLevelConverter.cs b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/TopLevel/PointCloudObjectToSpeckleTopLevelConverter.cs new file mode 100644 index 0000000000..e741e38e49 --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/TopLevel/PointCloudObjectToSpeckleTopLevelConverter.cs @@ -0,0 +1,15 @@ +using Rhino.DocObjects; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.Rhino7.ToSpeckle.TopLevel; + +[NameAndRankValue(nameof(PointCloudObject), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class PointCloudObjectToSpeckleTopLevelConverter + : RhinoObjectToSpeckleTopLevelConverter +{ + public PointCloudObjectToSpeckleTopLevelConverter(ITypedConverter conversion) + : base(conversion) { } + + protected override RG.PointCloud GetTypedGeometry(PointCloudObject input) => input.PointCloudGeometry; +} diff --git a/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/TopLevel/PointObjectToSpeckleTopLevelConverter.cs b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/TopLevel/PointObjectToSpeckleTopLevelConverter.cs new file mode 100644 index 0000000000..7d9c750b64 --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/TopLevel/PointObjectToSpeckleTopLevelConverter.cs @@ -0,0 +1,15 @@ +using Rhino.DocObjects; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.Rhino7.ToSpeckle.TopLevel; + +[NameAndRankValue(nameof(PointObject), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class PointObjectToSpeckleTopLevelConverter + : RhinoObjectToSpeckleTopLevelConverter +{ + public PointObjectToSpeckleTopLevelConverter(ITypedConverter conversion) + : base(conversion) { } + + protected override RG.Point GetTypedGeometry(PointObject input) => input.PointGeometry; +} diff --git a/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/TopLevel/RhinoObjectToSpeckleTopLevelConverter.cs b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/TopLevel/RhinoObjectToSpeckleTopLevelConverter.cs new file mode 100644 index 0000000000..edff24473a --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/ToSpeckle/TopLevel/RhinoObjectToSpeckleTopLevelConverter.cs @@ -0,0 +1,33 @@ +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; + +namespace Speckle.Converters.Rhino7.ToSpeckle.TopLevel; + +public abstract class RhinoObjectToSpeckleTopLevelConverter : IToSpeckleTopLevelConverter + where TTopLevelIn : Rhino.DocObjects.RhinoObject + where TInRaw : RG.GeometryBase + where TOutRaw : Base +{ + public ITypedConverter Conversion { get; } + + protected RhinoObjectToSpeckleTopLevelConverter(ITypedConverter conversion) + { + Conversion = conversion; + } + + // POC: IIndex would fix this as I would just request the type from `RhinoObject.Geometry` directly. + protected abstract TInRaw GetTypedGeometry(TTopLevelIn input); + + public virtual Base Convert(object target) + { + var typedTarget = (TTopLevelIn)target; + var typedGeometry = GetTypedGeometry(typedTarget); + + var result = Conversion.Convert(typedGeometry); + + // POC: Any common operations for all RhinoObjects should be done here, not on the specific implementer + // Things like user-dictionaries and other user-defined metadata. + + return result; + } +} diff --git a/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/packages.lock.json b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/packages.lock.json new file mode 100644 index 0000000000..98fcfe0158 --- /dev/null +++ b/DUI3-DX/Converters/Rhino/Speckle.Converters.Rhino7/packages.lock.json @@ -0,0 +1,415 @@ +{ + "version": 2, + "dependencies": { + ".NETFramework,Version=v4.8": { + "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==" + }, + "RhinoCommon": { + "type": "Direct", + "requested": "[7.13.21348.13001, )", + "resolved": "7.13.21348.13001", + "contentHash": "JQdaNw61ddBqIe08E9O4N/grwrN1hjDHcYW7tWylwCZyFR7SepoCD4NS+6LN6+oSQhNbhLi9Bf+hQOFYFdRAEA==" + }, + "Speckle.InterfaceGenerator": { + "type": "Direct", + "requested": "[0.9.5, )", + "resolved": "0.9.5", + "contentHash": "oU/L7pN1R7q8KkbrpQ3WJnHirPHqn+9DEA7asOcUiggV5dzVg1A/VYs7GOSusD24njxXh03tE3a2oTLOjt3cVg==" + }, + "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.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "Polly": { + "type": "Transitive", + "resolved": "7.2.3", + "contentHash": "DeCY0OFbNdNxsjntr1gTXHJ5pKUwYzp04Er2LLeN3g6pWhffsGuKVfMBLe1lw7x76HrPkLxKEFxBlpRxS2nDEQ==" + }, + "Polly.Contrib.WaitAndRetry": { + "type": "Transitive", + "resolved": "1.1.1", + "contentHash": "1MUQLiSo4KDkQe6nzQRhIU05lm9jlexX5BVsbuw0SL82ynZ+GzAHQxJVDPVBboxV37Po3SG077aX8DuSy8TkaA==" + }, + "Polly.Extensions.Http": { + "type": "Transitive", + "resolved": "3.0.0", + "contentHash": "drrG+hB3pYFY7w1c3BD+lSGYvH2oIclH8GRSehgfyP5kjnFnHKQuuBhuHLv+PWyFuaTDyk/vfRpnxOzd11+J8g==", + "dependencies": { + "Polly": "7.1.0" + } + }, + "Sentry": { + "type": "Transitive", + "resolved": "3.33.0", + "contentHash": "8vbD2o6IR2wrRrkSiRbnodWGWUOqIlwYtzpjvPNOb5raJdOf+zxMwfS8f6nx9bmrTTfDj7KrCB8C/5OuicAc8A==", + "dependencies": { + "System.Reflection.Metadata": "5.0.0", + "System.Runtime.InteropServices.RuntimeInformation": "4.3.0", + "System.Text.Json": "5.0.2" + } + }, + "Sentry.Serilog": { + "type": "Transitive", + "resolved": "3.33.0", + "contentHash": "V8BU7QGWg2qLYfNPqtuTBhC1opysny5l+Ifp6J6PhOeAxU0FssR7nYfbJVetrnLIoh2rd3DlJ6hHYYQosQYcUQ==", + "dependencies": { + "Sentry": "3.33.0", + "Serilog": "2.7.1" + } + }, + "Serilog": { + "type": "Transitive", + "resolved": "2.12.0", + "contentHash": "xaiJLIdu6rYMKfQMYUZgTy8YK7SMZjB4Yk50C/u//Z4OsvxkUfSPJy4nknfvwAC34yr13q7kcyh4grbwhSxyZg==" + }, + "Serilog.Enrichers.ClientInfo": { + "type": "Transitive", + "resolved": "1.3.0", + "contentHash": "mTc7PM+wC9Hr7LWSwqt5mmnlAr7RJs+eTb3PGPRhwdOackk95MkhUZognuxXEdlW19HAFNmEBTSBY5DfLwM8jQ==", + "dependencies": { + "Serilog": "2.4.0" + } + }, + "Serilog.Exceptions": { + "type": "Transitive", + "resolved": "8.4.0", + "contentHash": "nc/+hUw3lsdo0zCj0KMIybAu7perMx79vu72w0za9Nsi6mWyNkGXxYxakAjWB7nEmYL6zdmhEQRB4oJ2ALUeug==", + "dependencies": { + "Serilog": "2.8.0" + } + }, + "Serilog.Formatting.Compact": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "pNroKVjo+rDqlxNG5PXkRLpfSCuDOBY0ri6jp9PLe505ljqwhwZz8ospy2vWhQlFu5GkIesh3FcDs4n7sWZODA==", + "dependencies": { + "Serilog": "2.8.0" + } + }, + "Serilog.Sinks.Console": { + "type": "Transitive", + "resolved": "4.1.0", + "contentHash": "K6N5q+5fetjnJPvCmkWOpJ/V8IEIoMIB1s86OzBrbxwTyHxdx3pmz4H+8+O/Dc/ftUX12DM1aynx/dDowkwzqg==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "Serilog.Sinks.File": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "uwV5hdhWPwUH1szhO8PJpFiahqXmzPzJT/sOijH/kFgUx+cyoDTMM8MHD0adw9+Iem6itoibbUXHYslzXsLEAg==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "Serilog.Sinks.PeriodicBatching": { + "type": "Transitive", + "resolved": "3.1.0", + "contentHash": "NDWR7m3PalVlGEq3rzoktrXikjFMLmpwF0HI4sowo8YDdU+gqPlTHlDQiOGxHfB0sTfjPA9JjA7ctKG9zqjGkw==", + "dependencies": { + "Serilog": "2.0.0" + } + }, + "Serilog.Sinks.Seq": { + "type": "Transitive", + "resolved": "5.2.2", + "contentHash": "1Csmo5ua7NKUe0yXUx+zsRefjAniPWcXFhUXxXG8pwo0iMiw2gjn9SOkgYnnxbgWqmlGv236w0N/dHc2v5XwMg==", + "dependencies": { + "Serilog": "2.12.0", + "Serilog.Formatting.Compact": "1.1.0", + "Serilog.Sinks.File": "5.0.0", + "Serilog.Sinks.PeriodicBatching": "3.1.0" + } + }, + "SerilogTimings": { + "type": "Transitive", + "resolved": "3.0.1", + "contentHash": "Zs28eTgszAMwpIrbBnWHBI50yuxL50p/dmAUWmy75+axdZYK/Sjm5/5m1N/CisR8acJUhTVcjPZrsB1P5iv0Uw==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "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.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, + "System.Collections.Immutable": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "FXkLXiK0sVVewcso0imKQoOxjoPAj42R8HtjjbSjVPAzwDfzoyoznWxgA3c38LDbN9SJux1xXoXYAhz98j7r2g==", + "dependencies": { + "System.Memory": "4.5.4" + } + }, + "System.DoubleNumerics": { + "type": "Transitive", + "resolved": "3.1.3", + "contentHash": "KRKEM/L3KBodjA9VOg3EifFVWUY6EOqaMB05UvPEDm7Zeby/kZW+4kdWUEPzW6xtkwf46p661L9NrbeeQhtLzw==", + "dependencies": { + "NETStandard.Library": "1.6.1" + } + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Numerics.Vectors": "4.5.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + }, + "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.5.0", + "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" + }, + "System.Reactive": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "System.Reflection.Metadata": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "5NecZgXktdGg34rh1OenY1rFNDCI8xSjFr+Z4OU4cU06AQHUdRnIIEeWENu3Wl4YowbzkymAIMvi3WyK9U53pQ==", + "dependencies": { + "System.Collections.Immutable": "5.0.0" + } + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "ZD9TMpsmYJLrxbbmdvhwt9YEgG5WntEnZ/d1eH8JBX9LBp+Ju8BSBhUGbZMNVHHomWo2KVImJhTDl2hIgw/6MA==" + }, + "System.Runtime.InteropServices.RuntimeInformation": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==" + }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "5.0.1", + "contentHash": "KmJ+CJXizDofbq6mpqDoRRLcxgOd2z9X3XoFNULSbvbqVRZkFX3istvr+MUjL6Zw1RT+RNdoI4GYidIINtgvqQ==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Memory": "4.5.4" + } + }, + "System.Text.Json": { + "type": "Transitive", + "resolved": "5.0.2", + "contentHash": "I47dVIGiV6SfAyppphxqupertT/5oZkYLDCX6vC3HpOI4ZLjyoKAreUoem2ie6G0RbRuFrlqz/PcTQjfb2DOfQ==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "5.0.0", + "System.Buffers": "4.5.1", + "System.Memory": "4.5.4", + "System.Numerics.Vectors": "4.5.0", + "System.Runtime.CompilerServices.Unsafe": "5.0.0", + "System.Text.Encodings.Web": "5.0.1", + "System.Threading.Tasks.Extensions": "4.5.4", + "System.ValueTuple": "4.5.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" + } + }, + "System.ValueTuple": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "okurQJO6NRE/apDIP23ajJ0hpiNmJ+f0BwOlB/cSqTLQlw5upkf+5+96+iG2Jw40G1fCVCyPz/FhIABUjMR+RQ==" + }, + "speckle.autofac": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "[7.0.0, )" + } + }, + "speckle.converters.common": { + "type": "Project", + "dependencies": { + "Speckle.Autofac": "[2.0.999-local, )", + "Speckle.Objects": "[3.0.1-alpha.14, )" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "CentralTransitive", + "requested": "[7.0.0, )", + "resolved": "7.0.0", + "contentHash": "kmn78+LPVMOWeITUjIlfxUPDsI0R6G0RkeAMBmQxAJ7vBJn4q2dTva7pWi65ceN5vPGjJ9q/Uae2WKgvfktJAw==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Memory": "4.5.5" + } + }, + "Speckle.Core": { + "type": "CentralTransitive", + "requested": "[3.0.1-alpha.14, )", + "resolved": "3.0.1-alpha.14", + "contentHash": "RzQPVIGFFkKvG56YLr8ACtiwdWJE6IJ9vCQ4qHa0PIsUEpfzAIAi59jnzqtByOFC0FiFrFPow9bkfzylaZorAA==", + "dependencies": { + "GraphQL.Client": "6.0.0", + "Microsoft.CSharp": "4.7.0", + "Microsoft.Data.Sqlite": "7.0.5", + "Polly": "7.2.3", + "Polly.Contrib.WaitAndRetry": "1.1.1", + "Polly.Extensions.Http": "3.0.0", + "Sentry": "3.33.0", + "Sentry.Serilog": "3.33.0", + "Serilog": "2.12.0", + "Serilog.Enrichers.ClientInfo": "1.3.0", + "Serilog.Exceptions": "8.4.0", + "Serilog.Sinks.Console": "4.1.0", + "Serilog.Sinks.Seq": "5.2.2", + "SerilogTimings": "3.0.1", + "Speckle.Newtonsoft.Json": "13.0.2", + "System.DoubleNumerics": "3.1.3" + } + }, + "Speckle.Objects": { + "type": "CentralTransitive", + "requested": "[3.0.1-alpha.14, )", + "resolved": "3.0.1-alpha.14", + "contentHash": "z38LGryMvh7iU1uBW+4uo5DwsB3CwRgLt2uFexWFx3mPSid+A0l5XcJzOgLwgFhNl6B42Ryz4ezBsddTp1Uc/g==", + "dependencies": { + "Speckle.Core": "3.0.1-alpha.14" + } + } + } + } +} \ No newline at end of file diff --git a/DUI3-DX/DUI3/Speckle.Connectors.DUI.WebView/ContainerRegistration.cs b/DUI3-DX/DUI3/Speckle.Connectors.DUI.WebView/ContainerRegistration.cs new file mode 100644 index 0000000000..6d4d2de007 --- /dev/null +++ b/DUI3-DX/DUI3/Speckle.Connectors.DUI.WebView/ContainerRegistration.cs @@ -0,0 +1,12 @@ +using Speckle.Autofac.DependencyInjection; + +namespace Speckle.Connectors.DUI.WebView; + +public static class ContainerRegistration +{ + public static void AddDUIView(this SpeckleContainerBuilder speckleContainerBuilder) + { + // send operation and dependencies + speckleContainerBuilder.AddSingleton(); + } +} diff --git a/DUI3-DX/DUI3/Speckle.Connectors.DUI.WebView/DUI3ControlWebView.xaml b/DUI3-DX/DUI3/Speckle.Connectors.DUI.WebView/DUI3ControlWebView.xaml new file mode 100644 index 0000000000..c2a5e9354e --- /dev/null +++ b/DUI3-DX/DUI3/Speckle.Connectors.DUI.WebView/DUI3ControlWebView.xaml @@ -0,0 +1,19 @@ + + + + + + + + diff --git a/DUI3-DX/DUI3/Speckle.Connectors.DUI.WebView/DUI3ControlWebView.xaml.cs b/DUI3-DX/DUI3/Speckle.Connectors.DUI.WebView/DUI3ControlWebView.xaml.cs new file mode 100644 index 0000000000..c3153a0d5f --- /dev/null +++ b/DUI3-DX/DUI3/Speckle.Connectors.DUI.WebView/DUI3ControlWebView.xaml.cs @@ -0,0 +1,56 @@ +using System.Windows.Controls; +using System.Windows.Threading; +using Microsoft.Web.WebView2.Core; +using Speckle.Connectors.DUI.Bindings; +using Speckle.Connectors.DUI.Bridge; + +namespace Speckle.Connectors.DUI.WebView; + +public sealed partial class DUI3ControlWebView : UserControl +{ + private readonly IEnumerable> _bindings; + private readonly ITopLevelExceptionHandler _topLevelExceptionHandler; + + public DUI3ControlWebView(IEnumerable> bindings, ITopLevelExceptionHandler topLevelExceptionHandler) + { + _bindings = bindings; + _topLevelExceptionHandler = topLevelExceptionHandler; + InitializeComponent(); + + Browser.CoreWebView2InitializationCompleted += (sender, args) => + _topLevelExceptionHandler.CatchUnhandled(() => OnInitialized(sender, args)); + } + + private void ShowDevToolsMethod() => Browser.CoreWebView2.OpenDevToolsWindow(); + + private void ExecuteScriptAsyncMethod(string script) + { + if (!Browser.IsInitialized) + { + throw new InvalidOperationException("Failed to execute script, Webview2 is not initialized yet."); + } + + Browser.Dispatcher.Invoke(() => Browser.ExecuteScriptAsync(script), DispatcherPriority.Background); + } + + private void OnInitialized(object? sender, CoreWebView2InitializationCompletedEventArgs e) + { + if (!e.IsSuccess) + { + throw new InvalidOperationException("Webview Failed to initialize", e.InitializationException); + } + + // We use Lazy here to delay creating the binding until after the Browser is fully initialized. + // Otherwise the Browser cannot respond to any requests to ExecuteScriptAsyncMethod + foreach (Lazy lazyBinding in _bindings) + { + SetupBinding(lazyBinding.Value); + } + } + + private void SetupBinding(IBinding binding) + { + binding.Parent.AssociateWithBinding(binding, ExecuteScriptAsyncMethod, Browser, ShowDevToolsMethod); + Browser.CoreWebView2.AddHostObjectToScript(binding.Name, binding.Parent); + } +} diff --git a/DUI3-DX/DUI3/Speckle.Connectors.DUI.WebView/Speckle.Connectors.DUI.WebView.csproj b/DUI3-DX/DUI3/Speckle.Connectors.DUI.WebView/Speckle.Connectors.DUI.WebView.csproj new file mode 100644 index 0000000000..4f80899e94 --- /dev/null +++ b/DUI3-DX/DUI3/Speckle.Connectors.DUI.WebView/Speckle.Connectors.DUI.WebView.csproj @@ -0,0 +1,16 @@ + + + + net48;net6.0-windows + true + true + + + + + + + + + + diff --git a/DUI3-DX/DUI3/Speckle.Connectors.DUI.WebView/packages.lock.json b/DUI3-DX/DUI3/Speckle.Connectors.DUI.WebView/packages.lock.json new file mode 100644 index 0000000000..ff084cdbee --- /dev/null +++ b/DUI3-DX/DUI3/Speckle.Connectors.DUI.WebView/packages.lock.json @@ -0,0 +1,928 @@ +{ + "version": 2, + "dependencies": { + ".NETFramework,Version=v4.8": { + "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.1823.32, )", + "resolved": "1.0.1823.32", + "contentHash": "ppRcWBUNggFIqyJp7PfgS4Oe8/7yli/mHcTNDOaqo3ZzJWnv9AOr0WE9ceEP5SPs1M7CQ3QvdGMR7KNz0v09EA==" + }, + "PolySharp": { + "type": "Direct", + "requested": "[1.14.1, )", + "resolved": "1.14.1", + "contentHash": "mOOmFYwad3MIOL14VCjj02LljyF1GNw1wP0YVlxtcPvqdxjGGMNdNJJxHptlry3MOd8b40Flm8RPOM8JOlN2sQ==" + }, + "Speckle.InterfaceGenerator": { + "type": "Direct", + "requested": "[0.9.5, )", + "resolved": "0.9.5", + "contentHash": "oU/L7pN1R7q8KkbrpQ3WJnHirPHqn+9DEA7asOcUiggV5dzVg1A/VYs7GOSusD24njxXh03tE3a2oTLOjt3cVg==" + }, + "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": "7.0.0", + "contentHash": "3aeMZ1N0lJoSyzqiP03hqemtb1BijhsJADdobn/4nsMJ8V1H+CrpuduUe4hlRdx+ikBQju1VGjMD1GJ3Sk05Eg==", + "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.DependencyInjection": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "elNeOmkeX3eDVG6pYVeV82p29hr+UKDaBhrZyWvWLw/EVZSYEkZlQdkp0V39k/Xehs2Qa0mvoCvkVj3eQxNQ1Q==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "7.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0", + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "h3j/QfmFN4S0w4C2A6X7arXij/M/OVw3uQHSOFxnND4DyAzO1F9eMX7Eti7lU/OkSthEE0WzRsfT/Dmx86jzCw==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "7.0.0", + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "Microsoft.Extensions.Logging": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "Nw2muoNrOG5U5qa2ZekXwudUn2BJcD41e65zwmDHb1fQegTX66UokLWZkJRpqSSHXDOWZ5V0iqhbxOEky91atA==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "7.0.0", + "Microsoft.Extensions.DependencyInjection": "7.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0", + "Microsoft.Extensions.Logging.Abstractions": "7.0.0", + "Microsoft.Extensions.Options": "7.0.0", + "System.Diagnostics.DiagnosticSource": "7.0.0", + "System.ValueTuple": "4.5.0" + } + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "lP1yBnTTU42cKpMozuafbvNtQ7QcBjr/CcK3bYOGEMH55Fjt+iecXjT6chR7vbgCMqy3PG3aNQSZgo/EuY/9qQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0", + "Microsoft.Extensions.Primitives": "7.0.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "um1KU5kxcRp3CNuI8o/GrZtD4AIOXDk+RLsytjZ9QPok3ttLUelLKpilVPuaFT3TFjOhSibUAso0odbOaCDj3Q==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "Polly": { + "type": "Transitive", + "resolved": "7.2.3", + "contentHash": "DeCY0OFbNdNxsjntr1gTXHJ5pKUwYzp04Er2LLeN3g6pWhffsGuKVfMBLe1lw7x76HrPkLxKEFxBlpRxS2nDEQ==" + }, + "Polly.Contrib.WaitAndRetry": { + "type": "Transitive", + "resolved": "1.1.1", + "contentHash": "1MUQLiSo4KDkQe6nzQRhIU05lm9jlexX5BVsbuw0SL82ynZ+GzAHQxJVDPVBboxV37Po3SG077aX8DuSy8TkaA==" + }, + "Polly.Extensions.Http": { + "type": "Transitive", + "resolved": "3.0.0", + "contentHash": "drrG+hB3pYFY7w1c3BD+lSGYvH2oIclH8GRSehgfyP5kjnFnHKQuuBhuHLv+PWyFuaTDyk/vfRpnxOzd11+J8g==", + "dependencies": { + "Polly": "7.1.0" + } + }, + "Sentry": { + "type": "Transitive", + "resolved": "3.33.0", + "contentHash": "8vbD2o6IR2wrRrkSiRbnodWGWUOqIlwYtzpjvPNOb5raJdOf+zxMwfS8f6nx9bmrTTfDj7KrCB8C/5OuicAc8A==", + "dependencies": { + "System.Reflection.Metadata": "5.0.0", + "System.Runtime.InteropServices.RuntimeInformation": "4.3.0", + "System.Text.Json": "5.0.2" + } + }, + "Sentry.Serilog": { + "type": "Transitive", + "resolved": "3.33.0", + "contentHash": "V8BU7QGWg2qLYfNPqtuTBhC1opysny5l+Ifp6J6PhOeAxU0FssR7nYfbJVetrnLIoh2rd3DlJ6hHYYQosQYcUQ==", + "dependencies": { + "Sentry": "3.33.0", + "Serilog": "2.7.1" + } + }, + "Serilog": { + "type": "Transitive", + "resolved": "2.12.0", + "contentHash": "xaiJLIdu6rYMKfQMYUZgTy8YK7SMZjB4Yk50C/u//Z4OsvxkUfSPJy4nknfvwAC34yr13q7kcyh4grbwhSxyZg==" + }, + "Serilog.Enrichers.ClientInfo": { + "type": "Transitive", + "resolved": "1.3.0", + "contentHash": "mTc7PM+wC9Hr7LWSwqt5mmnlAr7RJs+eTb3PGPRhwdOackk95MkhUZognuxXEdlW19HAFNmEBTSBY5DfLwM8jQ==", + "dependencies": { + "Serilog": "2.4.0" + } + }, + "Serilog.Exceptions": { + "type": "Transitive", + "resolved": "8.4.0", + "contentHash": "nc/+hUw3lsdo0zCj0KMIybAu7perMx79vu72w0za9Nsi6mWyNkGXxYxakAjWB7nEmYL6zdmhEQRB4oJ2ALUeug==", + "dependencies": { + "Serilog": "2.8.0" + } + }, + "Serilog.Formatting.Compact": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "pNroKVjo+rDqlxNG5PXkRLpfSCuDOBY0ri6jp9PLe505ljqwhwZz8ospy2vWhQlFu5GkIesh3FcDs4n7sWZODA==", + "dependencies": { + "Serilog": "2.8.0" + } + }, + "Serilog.Sinks.Console": { + "type": "Transitive", + "resolved": "4.1.0", + "contentHash": "K6N5q+5fetjnJPvCmkWOpJ/V8IEIoMIB1s86OzBrbxwTyHxdx3pmz4H+8+O/Dc/ftUX12DM1aynx/dDowkwzqg==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "Serilog.Sinks.File": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "uwV5hdhWPwUH1szhO8PJpFiahqXmzPzJT/sOijH/kFgUx+cyoDTMM8MHD0adw9+Iem6itoibbUXHYslzXsLEAg==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "Serilog.Sinks.PeriodicBatching": { + "type": "Transitive", + "resolved": "3.1.0", + "contentHash": "NDWR7m3PalVlGEq3rzoktrXikjFMLmpwF0HI4sowo8YDdU+gqPlTHlDQiOGxHfB0sTfjPA9JjA7ctKG9zqjGkw==", + "dependencies": { + "Serilog": "2.0.0" + } + }, + "Serilog.Sinks.Seq": { + "type": "Transitive", + "resolved": "5.2.2", + "contentHash": "1Csmo5ua7NKUe0yXUx+zsRefjAniPWcXFhUXxXG8pwo0iMiw2gjn9SOkgYnnxbgWqmlGv236w0N/dHc2v5XwMg==", + "dependencies": { + "Serilog": "2.12.0", + "Serilog.Formatting.Compact": "1.1.0", + "Serilog.Sinks.File": "5.0.0", + "Serilog.Sinks.PeriodicBatching": "3.1.0" + } + }, + "SerilogTimings": { + "type": "Transitive", + "resolved": "3.0.1", + "contentHash": "Zs28eTgszAMwpIrbBnWHBI50yuxL50p/dmAUWmy75+axdZYK/Sjm5/5m1N/CisR8acJUhTVcjPZrsB1P5iv0Uw==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "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.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, + "System.Collections.Immutable": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "FXkLXiK0sVVewcso0imKQoOxjoPAj42R8HtjjbSjVPAzwDfzoyoznWxgA3c38LDbN9SJux1xXoXYAhz98j7r2g==", + "dependencies": { + "System.Memory": "4.5.4" + } + }, + "System.Diagnostics.DiagnosticSource": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "9W0ewWDuAyDqS2PigdTxk6jDKonfgscY/hP8hm7VpxYhNHZHKvZTdRckberlFk3VnCmr3xBUyMBut12Q+T2aOw==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "System.DoubleNumerics": { + "type": "Transitive", + "resolved": "3.1.3", + "contentHash": "KRKEM/L3KBodjA9VOg3EifFVWUY6EOqaMB05UvPEDm7Zeby/kZW+4kdWUEPzW6xtkwf46p661L9NrbeeQhtLzw==", + "dependencies": { + "NETStandard.Library": "1.6.1" + } + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Numerics.Vectors": "4.5.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + }, + "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.5.0", + "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" + }, + "System.Reactive": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "System.Reflection.Metadata": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "5NecZgXktdGg34rh1OenY1rFNDCI8xSjFr+Z4OU4cU06AQHUdRnIIEeWENu3Wl4YowbzkymAIMvi3WyK9U53pQ==", + "dependencies": { + "System.Collections.Immutable": "5.0.0" + } + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + }, + "System.Runtime.InteropServices.RuntimeInformation": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==" + }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "5.0.1", + "contentHash": "KmJ+CJXizDofbq6mpqDoRRLcxgOd2z9X3XoFNULSbvbqVRZkFX3istvr+MUjL6Zw1RT+RNdoI4GYidIINtgvqQ==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Memory": "4.5.4" + } + }, + "System.Text.Json": { + "type": "Transitive", + "resolved": "5.0.2", + "contentHash": "I47dVIGiV6SfAyppphxqupertT/5oZkYLDCX6vC3HpOI4ZLjyoKAreUoem2ie6G0RbRuFrlqz/PcTQjfb2DOfQ==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "5.0.0", + "System.Buffers": "4.5.1", + "System.Memory": "4.5.4", + "System.Numerics.Vectors": "4.5.0", + "System.Runtime.CompilerServices.Unsafe": "5.0.0", + "System.Text.Encodings.Web": "5.0.1", + "System.Threading.Tasks.Extensions": "4.5.4", + "System.ValueTuple": "4.5.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" + } + }, + "System.ValueTuple": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "okurQJO6NRE/apDIP23ajJ0hpiNmJ+f0BwOlB/cSqTLQlw5upkf+5+96+iG2Jw40G1fCVCyPz/FhIABUjMR+RQ==" + }, + "speckle.autofac": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "[7.0.0, )" + } + }, + "speckle.connectors.dui": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "[7.0.0, )", + "Speckle.Autofac": "[2.0.999-local, )", + "Speckle.Connectors.Utils": "[2.0.999-local, )", + "Speckle.Core": "[3.0.1-alpha.14, )", + "System.Threading.Tasks.Dataflow": "[6.0.0, )" + } + }, + "speckle.connectors.utils": { + "type": "Project", + "dependencies": { + "Serilog.Extensions.Logging": "[7.0.0, )", + "Speckle.Autofac": "[2.0.999-local, )", + "Speckle.Core": "[3.0.1-alpha.14, )" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "CentralTransitive", + "requested": "[7.0.0, )", + "resolved": "7.0.0", + "contentHash": "kmn78+LPVMOWeITUjIlfxUPDsI0R6G0RkeAMBmQxAJ7vBJn4q2dTva7pWi65ceN5vPGjJ9q/Uae2WKgvfktJAw==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Memory": "4.5.5" + } + }, + "Serilog.Extensions.Logging": { + "type": "CentralTransitive", + "requested": "[7.0.0, )", + "resolved": "7.0.0", + "contentHash": "9faU0zNQqU7I6soVhLUMYaGNpgWv6cKlKb2S5AnS8gXxzW/em5Ladm/6FMrWTnX41cdbdGPOWNAo6adi4WaJ6A==", + "dependencies": { + "Microsoft.Extensions.Logging": "7.0.0", + "Serilog": "2.12.0" + } + }, + "Speckle.Core": { + "type": "CentralTransitive", + "requested": "[3.0.1-alpha.14, )", + "resolved": "3.0.1-alpha.14", + "contentHash": "RzQPVIGFFkKvG56YLr8ACtiwdWJE6IJ9vCQ4qHa0PIsUEpfzAIAi59jnzqtByOFC0FiFrFPow9bkfzylaZorAA==", + "dependencies": { + "GraphQL.Client": "6.0.0", + "Microsoft.CSharp": "4.7.0", + "Microsoft.Data.Sqlite": "7.0.5", + "Polly": "7.2.3", + "Polly.Contrib.WaitAndRetry": "1.1.1", + "Polly.Extensions.Http": "3.0.0", + "Sentry": "3.33.0", + "Sentry.Serilog": "3.33.0", + "Serilog": "2.12.0", + "Serilog.Enrichers.ClientInfo": "1.3.0", + "Serilog.Exceptions": "8.4.0", + "Serilog.Sinks.Console": "4.1.0", + "Serilog.Sinks.Seq": "5.2.2", + "SerilogTimings": "3.0.1", + "Speckle.Newtonsoft.Json": "13.0.2", + "System.DoubleNumerics": "3.1.3" + } + }, + "System.Threading.Tasks.Dataflow": { + "type": "CentralTransitive", + "requested": "[6.0.0, )", + "resolved": "6.0.0", + "contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA==" + } + }, + "net6.0-windows7.0": { + "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.1823.32, )", + "resolved": "1.0.1823.32", + "contentHash": "ppRcWBUNggFIqyJp7PfgS4Oe8/7yli/mHcTNDOaqo3ZzJWnv9AOr0WE9ceEP5SPs1M7CQ3QvdGMR7KNz0v09EA==" + }, + "PolySharp": { + "type": "Direct", + "requested": "[1.14.1, )", + "resolved": "1.14.1", + "contentHash": "mOOmFYwad3MIOL14VCjj02LljyF1GNw1wP0YVlxtcPvqdxjGGMNdNJJxHptlry3MOd8b40Flm8RPOM8JOlN2sQ==" + }, + "Speckle.InterfaceGenerator": { + "type": "Direct", + "requested": "[0.9.5, )", + "resolved": "0.9.5", + "contentHash": "oU/L7pN1R7q8KkbrpQ3WJnHirPHqn+9DEA7asOcUiggV5dzVg1A/VYs7GOSusD24njxXh03tE3a2oTLOjt3cVg==" + }, + "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.AspNetCore.Http": { + "type": "Transitive", + "resolved": "2.2.2", + "contentHash": "BAibpoItxI5puk7YJbIGj95arZueM8B8M5xT1fXBn3hb3L2G3ucrZcYXv1gXdaroLbntUs8qeV8iuBrpjQsrKw==", + "dependencies": { + "Microsoft.AspNetCore.Http.Abstractions": "2.2.0", + "Microsoft.AspNetCore.WebUtilities": "2.2.0", + "Microsoft.Extensions.ObjectPool": "2.2.0", + "Microsoft.Extensions.Options": "2.2.0", + "Microsoft.Net.Http.Headers": "2.2.0" + } + }, + "Microsoft.AspNetCore.Http.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "Nxs7Z1q3f1STfLYKJSVXCs1iBl+Ya6E8o4Oy1bCxJ/rNI44E/0f6tbsrVqAWfB7jlnJfyaAtIalBVxPKUPQb4Q==", + "dependencies": { + "Microsoft.AspNetCore.Http.Features": "2.2.0", + "System.Text.Encodings.Web": "4.5.0" + } + }, + "Microsoft.AspNetCore.Http.Features": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "ziFz5zH8f33En4dX81LW84I6XrYXKf9jg6aM39cM+LffN9KJahViKZ61dGMSO2gd3e+qe5yBRwsesvyqlZaSMg==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.2.0" + } + }, + "Microsoft.AspNetCore.WebUtilities": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "9ErxAAKaDzxXASB/b5uLEkLgUWv1QbeVxyJYEHQwMaxXOeFFVkQxiq8RyfVcifLU7NR0QY0p3acqx4ZpYfhHDg==", + "dependencies": { + "Microsoft.Net.Http.Headers": "2.2.0", + "System.Text.Encodings.Web": "4.5.0" + } + }, + "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.DependencyInjection": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "elNeOmkeX3eDVG6pYVeV82p29hr+UKDaBhrZyWvWLw/EVZSYEkZlQdkp0V39k/Xehs2Qa0mvoCvkVj3eQxNQ1Q==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "h3j/QfmFN4S0w4C2A6X7arXij/M/OVw3uQHSOFxnND4DyAzO1F9eMX7Eti7lU/OkSthEE0WzRsfT/Dmx86jzCw==" + }, + "Microsoft.Extensions.Logging": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "Nw2muoNrOG5U5qa2ZekXwudUn2BJcD41e65zwmDHb1fQegTX66UokLWZkJRpqSSHXDOWZ5V0iqhbxOEky91atA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "7.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0", + "Microsoft.Extensions.Logging.Abstractions": "7.0.0", + "Microsoft.Extensions.Options": "7.0.0" + } + }, + "Microsoft.Extensions.ObjectPool": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "gA8H7uQOnM5gb+L0uTNjViHYr+hRDqCdfugheGo/MxQnuHzmhhzCBTIPm19qL1z1Xe0NEMabfcOBGv9QghlZ8g==" + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "lP1yBnTTU42cKpMozuafbvNtQ7QcBjr/CcK3bYOGEMH55Fjt+iecXjT6chR7vbgCMqy3PG3aNQSZgo/EuY/9qQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0", + "Microsoft.Extensions.Primitives": "7.0.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "um1KU5kxcRp3CNuI8o/GrZtD4AIOXDk+RLsytjZ9QPok3ttLUelLKpilVPuaFT3TFjOhSibUAso0odbOaCDj3Q==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "Microsoft.Net.Http.Headers": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "iZNkjYqlo8sIOI0bQfpsSoMTmB/kyvmV2h225ihyZT33aTp48ZpF6qYnXxzSXmHt8DpBAwBTX+1s1UFLbYfZKg==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.2.0", + "System.Buffers": "4.5.0" + } + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "Polly": { + "type": "Transitive", + "resolved": "7.2.3", + "contentHash": "DeCY0OFbNdNxsjntr1gTXHJ5pKUwYzp04Er2LLeN3g6pWhffsGuKVfMBLe1lw7x76HrPkLxKEFxBlpRxS2nDEQ==" + }, + "Polly.Contrib.WaitAndRetry": { + "type": "Transitive", + "resolved": "1.1.1", + "contentHash": "1MUQLiSo4KDkQe6nzQRhIU05lm9jlexX5BVsbuw0SL82ynZ+GzAHQxJVDPVBboxV37Po3SG077aX8DuSy8TkaA==" + }, + "Polly.Extensions.Http": { + "type": "Transitive", + "resolved": "3.0.0", + "contentHash": "drrG+hB3pYFY7w1c3BD+lSGYvH2oIclH8GRSehgfyP5kjnFnHKQuuBhuHLv+PWyFuaTDyk/vfRpnxOzd11+J8g==", + "dependencies": { + "Polly": "7.1.0" + } + }, + "Sentry": { + "type": "Transitive", + "resolved": "3.33.0", + "contentHash": "8vbD2o6IR2wrRrkSiRbnodWGWUOqIlwYtzpjvPNOb5raJdOf+zxMwfS8f6nx9bmrTTfDj7KrCB8C/5OuicAc8A==" + }, + "Sentry.Serilog": { + "type": "Transitive", + "resolved": "3.33.0", + "contentHash": "V8BU7QGWg2qLYfNPqtuTBhC1opysny5l+Ifp6J6PhOeAxU0FssR7nYfbJVetrnLIoh2rd3DlJ6hHYYQosQYcUQ==", + "dependencies": { + "Sentry": "3.33.0", + "Serilog": "2.10.0" + } + }, + "Serilog": { + "type": "Transitive", + "resolved": "2.12.0", + "contentHash": "xaiJLIdu6rYMKfQMYUZgTy8YK7SMZjB4Yk50C/u//Z4OsvxkUfSPJy4nknfvwAC34yr13q7kcyh4grbwhSxyZg==" + }, + "Serilog.Enrichers.ClientInfo": { + "type": "Transitive", + "resolved": "1.3.0", + "contentHash": "mTc7PM+wC9Hr7LWSwqt5mmnlAr7RJs+eTb3PGPRhwdOackk95MkhUZognuxXEdlW19HAFNmEBTSBY5DfLwM8jQ==", + "dependencies": { + "Microsoft.AspNetCore.Http": "2.2.2", + "Serilog": "2.9.0" + } + }, + "Serilog.Exceptions": { + "type": "Transitive", + "resolved": "8.4.0", + "contentHash": "nc/+hUw3lsdo0zCj0KMIybAu7perMx79vu72w0za9Nsi6mWyNkGXxYxakAjWB7nEmYL6zdmhEQRB4oJ2ALUeug==", + "dependencies": { + "Serilog": "2.8.0", + "System.Reflection.TypeExtensions": "4.7.0" + } + }, + "Serilog.Formatting.Compact": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "pNroKVjo+rDqlxNG5PXkRLpfSCuDOBY0ri6jp9PLe505ljqwhwZz8ospy2vWhQlFu5GkIesh3FcDs4n7sWZODA==", + "dependencies": { + "Serilog": "2.8.0" + } + }, + "Serilog.Sinks.Console": { + "type": "Transitive", + "resolved": "4.1.0", + "contentHash": "K6N5q+5fetjnJPvCmkWOpJ/V8IEIoMIB1s86OzBrbxwTyHxdx3pmz4H+8+O/Dc/ftUX12DM1aynx/dDowkwzqg==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "Serilog.Sinks.File": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "uwV5hdhWPwUH1szhO8PJpFiahqXmzPzJT/sOijH/kFgUx+cyoDTMM8MHD0adw9+Iem6itoibbUXHYslzXsLEAg==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "Serilog.Sinks.PeriodicBatching": { + "type": "Transitive", + "resolved": "3.1.0", + "contentHash": "NDWR7m3PalVlGEq3rzoktrXikjFMLmpwF0HI4sowo8YDdU+gqPlTHlDQiOGxHfB0sTfjPA9JjA7ctKG9zqjGkw==", + "dependencies": { + "Serilog": "2.0.0" + } + }, + "Serilog.Sinks.Seq": { + "type": "Transitive", + "resolved": "5.2.2", + "contentHash": "1Csmo5ua7NKUe0yXUx+zsRefjAniPWcXFhUXxXG8pwo0iMiw2gjn9SOkgYnnxbgWqmlGv236w0N/dHc2v5XwMg==", + "dependencies": { + "Serilog": "2.12.0", + "Serilog.Formatting.Compact": "1.1.0", + "Serilog.Sinks.File": "5.0.0", + "Serilog.Sinks.PeriodicBatching": "3.1.0" + } + }, + "SerilogTimings": { + "type": "Transitive", + "resolved": "3.0.1", + "contentHash": "Zs28eTgszAMwpIrbBnWHBI50yuxL50p/dmAUWmy75+axdZYK/Sjm5/5m1N/CisR8acJUhTVcjPZrsB1P5iv0Uw==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "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.Buffers": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "pL2ChpaRRWI/p4LXyy4RgeWlYF2sgfj/pnVMvBqwNFr5cXg7CXNnWZWxrOONLg8VGdFB8oB+EG2Qw4MLgTOe+A==" + }, + "System.DoubleNumerics": { + "type": "Transitive", + "resolved": "3.1.3", + "contentHash": "KRKEM/L3KBodjA9VOg3EifFVWUY6EOqaMB05UvPEDm7Zeby/kZW+4kdWUEPzW6xtkwf46p661L9NrbeeQhtLzw==", + "dependencies": { + "NETStandard.Library": "1.6.1" + } + }, + "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.TypeExtensions": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "VybpaOQQhqE6siHppMktjfGBw1GCwvCqiufqmP8F1nj7fTUNtW35LOEt3UZTEsECfo+ELAl/9o9nJx3U91i7vA==" + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "Xg4G4Indi4dqP1iuAiMSwpiWS54ZghzR644OtsRCm/m/lBMG8dUBhLVN7hLm8NNrNTR+iGbshCPTwrvxZPlm4g==" + }, + "speckle.autofac": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "[7.0.0, )" + } + }, + "speckle.connectors.dui": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "[7.0.0, )", + "Speckle.Autofac": "[2.0.999-local, )", + "Speckle.Connectors.Utils": "[2.0.999-local, )", + "Speckle.Core": "[3.0.1-alpha.14, )", + "System.Threading.Tasks.Dataflow": "[6.0.0, )" + } + }, + "speckle.connectors.utils": { + "type": "Project", + "dependencies": { + "Serilog.Extensions.Logging": "[7.0.0, )", + "Speckle.Autofac": "[2.0.999-local, )", + "Speckle.Core": "[3.0.1-alpha.14, )" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "CentralTransitive", + "requested": "[7.0.0, )", + "resolved": "7.0.0", + "contentHash": "kmn78+LPVMOWeITUjIlfxUPDsI0R6G0RkeAMBmQxAJ7vBJn4q2dTva7pWi65ceN5vPGjJ9q/Uae2WKgvfktJAw==" + }, + "Serilog.Extensions.Logging": { + "type": "CentralTransitive", + "requested": "[7.0.0, )", + "resolved": "7.0.0", + "contentHash": "9faU0zNQqU7I6soVhLUMYaGNpgWv6cKlKb2S5AnS8gXxzW/em5Ladm/6FMrWTnX41cdbdGPOWNAo6adi4WaJ6A==", + "dependencies": { + "Microsoft.Extensions.Logging": "7.0.0", + "Serilog": "2.12.0" + } + }, + "Speckle.Core": { + "type": "CentralTransitive", + "requested": "[3.0.1-alpha.14, )", + "resolved": "3.0.1-alpha.14", + "contentHash": "RzQPVIGFFkKvG56YLr8ACtiwdWJE6IJ9vCQ4qHa0PIsUEpfzAIAi59jnzqtByOFC0FiFrFPow9bkfzylaZorAA==", + "dependencies": { + "GraphQL.Client": "6.0.0", + "Microsoft.CSharp": "4.7.0", + "Microsoft.Data.Sqlite": "7.0.5", + "Polly": "7.2.3", + "Polly.Contrib.WaitAndRetry": "1.1.1", + "Polly.Extensions.Http": "3.0.0", + "Sentry": "3.33.0", + "Sentry.Serilog": "3.33.0", + "Serilog": "2.12.0", + "Serilog.Enrichers.ClientInfo": "1.3.0", + "Serilog.Exceptions": "8.4.0", + "Serilog.Sinks.Console": "4.1.0", + "Serilog.Sinks.Seq": "5.2.2", + "SerilogTimings": "3.0.1", + "Speckle.Newtonsoft.Json": "13.0.2", + "System.DoubleNumerics": "3.1.3" + } + }, + "System.Threading.Tasks.Dataflow": { + "type": "CentralTransitive", + "requested": "[6.0.0, )", + "resolved": "6.0.0", + "contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA==" + } + } + } +} \ No newline at end of file diff --git a/DUI3-DX/DUI3/Speckle.Connectors.DUI/Bindings/AccountBinding.cs b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Bindings/AccountBinding.cs new file mode 100644 index 0000000000..6357195a43 --- /dev/null +++ b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Bindings/AccountBinding.cs @@ -0,0 +1,17 @@ +using Speckle.Connectors.DUI.Bridge; +using Speckle.Core.Credentials; + +namespace Speckle.Connectors.DUI.Bindings; + +public class AccountBinding : IBinding +{ + public string Name => "accountsBinding"; + public IBridge Parent { get; } + + public AccountBinding(IBridge bridge) + { + Parent = bridge; + } + + public Account[] GetAccounts() => AccountManager.GetAccounts().ToArray(); +} diff --git a/DUI3-DX/DUI3/Speckle.Connectors.DUI/Bindings/ConfigBinding.cs b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Bindings/ConfigBinding.cs new file mode 100644 index 0000000000..ca2aed10fe --- /dev/null +++ b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Bindings/ConfigBinding.cs @@ -0,0 +1,75 @@ +using System.Runtime.Serialization; +using Speckle.Connectors.DUI.Bridge; +using Speckle.Core.Transports; +using Speckle.Newtonsoft.Json; + +namespace Speckle.Connectors.DUI.Bindings; + +/// +/// POC: Simple config binding, as it was driving Dim nuts he couldn't swap to a dark theme. +/// How does it store configs? In a sqlite db called 'DUI3Config', we create a row for each host application: +/// [ hash, contents ] +/// ['Rhino', serialised config] +/// ['Revit', serialised config] +/// +public class ConfigBinding : IBinding +{ + public string Name => "configBinding"; + public IBridge Parent { get; } + private SQLiteTransport ConfigStorage { get; } + private readonly string _connectorName; + private readonly JsonSerializerSettings _serializerOptions; + + public ConfigBinding(IBridge bridge, JsonSerializerSettings serializerOptions, string connectorName) + { + 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) + _connectorName = connectorName; + _serializerOptions = serializerOptions; + } + + public ConnectorConfig GetConfig() + { + var rawConfig = ConfigStorage.GetObject(_connectorName); + if (rawConfig is null) + { + return SeedConfig(); + } + + try + { + var config = JsonConvert.DeserializeObject(rawConfig, _serializerOptions); + if (config is null) + { + throw new SerializationException("Failed to deserialize config"); + } + + return config; + } + catch (SerializationException) + { + return SeedConfig(); + } + } + + private ConnectorConfig SeedConfig() + { + var cfg = new ConnectorConfig(); + UpdateConfig(cfg); + return cfg; + } + + public void UpdateConfig(ConnectorConfig config) + { + var str = JsonConvert.SerializeObject(config, _serializerOptions); + ConfigStorage.UpdateObject(_connectorName, str); + } +} + +/// +/// POC: A simple POCO for keeping track of settings. I see this as extensible in the future by each host application if and when we will need global per-app connector settings. +/// +public class ConnectorConfig +{ + public bool DarkTheme { get; set; } = true; +} diff --git a/DUI3-DX/DUI3/Speckle.Connectors.DUI/Bindings/IBasicConnectorBinding.cs b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Bindings/IBasicConnectorBinding.cs new file mode 100644 index 0000000000..c88da91a81 --- /dev/null +++ b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Bindings/IBasicConnectorBinding.cs @@ -0,0 +1,91 @@ +using Speckle.Connectors.DUI.Bridge; +using Speckle.Connectors.DUI.Models; +using Speckle.Connectors.DUI.Models.Card; + +namespace Speckle.Connectors.DUI.Bindings; + +public interface IBasicConnectorBinding : IBinding +{ + public string GetSourceApplicationName(); + public string GetSourceApplicationVersion(); + public string GetConnectorVersion(); + public DocumentInfo? GetDocumentInfo(); + public DocumentModelStore GetDocumentState(); + public void AddModel(ModelCard model); + public void UpdateModel(ModelCard model); + public void RemoveModel(ModelCard model); + + /// + /// Highlights the objects attached to this sender in the host application. + /// + /// + public void HighlightModel(string modelCardId); + + public void HighlightObjects(List objectIds); + + public BasicConnectorBindingCommands Commands { get; } +} + +public static class BasicConnectorBindingEvents +{ + public const string DISPLAY_TOAST_NOTIFICATION = "DisplayToastNotification"; + public const string DOCUMENT_CHANGED = "documentChanged"; +} + +public enum ToastNotificationType +{ + SUCCESS, + WARNING, + DANGER, + INFO +} + +public class BasicConnectorBindingCommands +{ + private const string NOTIFY_DOCUMENT_CHANGED_EVENT_NAME = "documentChanged"; + private const string SET_MODEL_PROGRESS_UI_COMMAND_NAME = "setModelProgress"; + private const string SET_MODEL_ERROR_UI_COMMAND_NAME = "setModelError"; + internal const string SET_GLOBAL_NOTIFICATION = "setGlobalNotification"; + + protected IBridge Bridge { get; } + + public BasicConnectorBindingCommands(IBridge bridge) + { + Bridge = bridge; + } + + public void NotifyDocumentChanged() => Bridge.Send(NOTIFY_DOCUMENT_CHANGED_EVENT_NAME); + + /// + /// Use it whenever you want to send global toast notification to UI. + /// + /// Level of notification, see for types + /// Title of the notification + /// Message in the toast notification. + /// Closes toast notification in set timeout in UI. Default is true. + public void SetGlobalNotification(ToastNotificationType type, string title, string message, bool autoClose = true) => + Bridge.Send( + SET_GLOBAL_NOTIFICATION, + new + { + type, + title, + description = message, + autoClose + } + ); + + public void SetModelProgress(string modelCardId, ModelCardProgress progress, CancellationTokenSource cts) + { + // NOTE: To prevent potential race condition + // After cancelling operation some parts could still send last progress update which was set progress on UI + // after it forced to be undefined. This is the safest way to prevent any case like this. + if (!cts.IsCancellationRequested) + { + Bridge.Send(SET_MODEL_PROGRESS_UI_COMMAND_NAME, new { modelCardId, progress }); + } + } + + public void SetModelError(string modelCardId, Exception error) => + Bridge.Send(SET_MODEL_ERROR_UI_COMMAND_NAME, new { modelCardId, error = error.Message }); +} diff --git a/DUI3-DX/DUI3/Speckle.Connectors.DUI/Bindings/IBinding.cs b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Bindings/IBinding.cs new file mode 100644 index 0000000000..bfaacb7c7c --- /dev/null +++ b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Bindings/IBinding.cs @@ -0,0 +1,23 @@ +using Speckle.Connectors.DUI.Bridge; + +namespace Speckle.Connectors.DUI.Bindings; + +/// +/// Describes the most basic binding. +/// +public interface IBinding +{ + /// + /// This will be the name under which it will be available in the Frontend, e.g. + /// window.superBinding, or window.mapperBinding. Please use camelCase even if + /// it hurts. + /// + public string Name { get; } + + /// + /// Bindings will be wrapped by a browser specific bridge, and they will need + /// to use that bridge to send events to the Frontend, via SendToBrowser(IHostAppEvent). + /// TODO: we'll probably need a factory class of sorts to handle the proper wrapping. Currently, on bridge instantiation the parent is set in the bindings class that has been wrapped around. Not vvv elegant. + /// + public IBridge Parent { get; } +} diff --git a/DUI3-DX/DUI3/Speckle.Connectors.DUI/Bindings/IReceiveBinding.cs b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Bindings/IReceiveBinding.cs new file mode 100644 index 0000000000..fc7dd6a2ca --- /dev/null +++ b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Bindings/IReceiveBinding.cs @@ -0,0 +1,16 @@ +namespace Speckle.Connectors.DUI.Bindings; + +public interface IReceiveBinding : IBinding +{ + /// + /// Instructs the host app to start receiving this model version. + /// + /// Model card id + public Task Receive(string modelCardId); + + /// + /// Instructs the host app to cancel the receiving for a given model. + /// + /// + public void CancelReceive(string modelCardId); +} diff --git a/DUI3-DX/DUI3/Speckle.Connectors.DUI/Bindings/ISelectionBinding.cs b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Bindings/ISelectionBinding.cs new file mode 100644 index 0000000000..1bef4f8815 --- /dev/null +++ b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Bindings/ISelectionBinding.cs @@ -0,0 +1,13 @@ +namespace Speckle.Connectors.DUI.Bindings; + +public interface ISelectionBinding : IBinding +{ + public SelectionInfo GetSelection(); +} + +public static class SelectionBindingEvents +{ + public const string SET_SELECTION = "setSelection"; +} + +public record SelectionInfo(List SelectedObjectIds, string Summary); diff --git a/DUI3-DX/DUI3/Speckle.Connectors.DUI/Bindings/ISendBinding.cs b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Bindings/ISendBinding.cs new file mode 100644 index 0000000000..fb4891792a --- /dev/null +++ b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Bindings/ISendBinding.cs @@ -0,0 +1,22 @@ +using Speckle.Connectors.DUI.Models.Card.SendFilter; + +namespace Speckle.Connectors.DUI.Bindings; + +public interface ISendBinding : IBinding +{ + public List GetSendFilters(); + + /// + /// Instructs the host app to start sending this model. + /// + /// + public Task Send(string modelCardId); + + /// + /// Instructs the host app to cancel the sending for a given model. + /// + /// + public void CancelSend(string modelCardId); + + public SendBindingUICommands Commands { get; } +} diff --git a/DUI3-DX/DUI3/Speckle.Connectors.DUI/Bindings/ISendBindingUICommands.cs b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Bindings/ISendBindingUICommands.cs new file mode 100644 index 0000000000..13848f1a71 --- /dev/null +++ b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Bindings/ISendBindingUICommands.cs @@ -0,0 +1,10 @@ +namespace Speckle.Connectors.DUI.Bindings; + +public interface ISendBindingUICommands +{ + public void RefreshSendFilters(string frontEndName); + + public void SetModelsExpired(string frontEndName, IEnumerable expiredModelIds); + + public void SetModelCreatedVersionId(string frontEndName, string modelCardId, string versionId); +} diff --git a/DUI3-DX/DUI3/Speckle.Connectors.DUI/Bindings/ReceiveBindingUICommands.cs b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Bindings/ReceiveBindingUICommands.cs new file mode 100644 index 0000000000..62bcc2813d --- /dev/null +++ b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Bindings/ReceiveBindingUICommands.cs @@ -0,0 +1,30 @@ +using Speckle.Connectors.DUI.Bridge; +using Speckle.Connectors.Utils.Conversion; + +namespace Speckle.Connectors.DUI.Bindings; + +public class ReceiveBindingUICommands : BasicConnectorBindingCommands +{ + // POC: put here events once we needed for receive specific + private const string SET_MODEL_RECEIVE_RESULT_UI_COMMAND_NAME = "setModelReceiveResult"; + + public ReceiveBindingUICommands(IBridge bridge) + : base(bridge) { } + + public void SetModelReceiveResult( + string modelCardId, + IEnumerable bakedObjectIds, + IEnumerable conversionResults + ) + { + Bridge.Send( + SET_MODEL_RECEIVE_RESULT_UI_COMMAND_NAME, + new + { + ModelCardId = modelCardId, + bakedObjectIds, + conversionResults + } + ); + } +} diff --git a/DUI3-DX/DUI3/Speckle.Connectors.DUI/Bindings/SendBindingUICommands.cs b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Bindings/SendBindingUICommands.cs new file mode 100644 index 0000000000..7652fe9e5a --- /dev/null +++ b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Bindings/SendBindingUICommands.cs @@ -0,0 +1,36 @@ +using Speckle.Connectors.DUI.Bridge; +using Speckle.Connectors.Utils.Conversion; + +namespace Speckle.Connectors.DUI.Bindings; + +// POC: Send Commands share all commands from BasicBindings + some, this pattern should be revised +public class SendBindingUICommands : BasicConnectorBindingCommands +{ + private const string REFRESH_SEND_FILTERS_UI_COMMAND_NAME = "refreshSendFilters"; + private const string SET_MODELS_EXPIRED_UI_COMMAND_NAME = "setModelsExpired"; + private const string SET_MODEL_SEND_RESULT_UI_COMMAND_NAME = "setModelSendResult"; + + public SendBindingUICommands(IBridge bridge) + : base(bridge) { } + + // POC.. the only reasons this needs the bridge is to send? realtionship to these messages and the bridge is unclear + public void RefreshSendFilters() => Bridge.Send(REFRESH_SEND_FILTERS_UI_COMMAND_NAME); + + public void SetModelsExpired(IEnumerable expiredModelIds) => + Bridge.Send(SET_MODELS_EXPIRED_UI_COMMAND_NAME, expiredModelIds); + + public void SetModelSendResult( + string modelCardId, + string versionId, + IEnumerable sendConversionResults + ) => + Bridge.Send( + SET_MODEL_SEND_RESULT_UI_COMMAND_NAME, + new + { + modelCardId, + versionId, + sendConversionResults + } + ); +} diff --git a/DUI3-DX/DUI3/Speckle.Connectors.DUI/Bindings/TestBinding.cs b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Bindings/TestBinding.cs new file mode 100644 index 0000000000..f0523db541 --- /dev/null +++ b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Bindings/TestBinding.cs @@ -0,0 +1,65 @@ +using System.Diagnostics; +using Speckle.Connectors.DUI.Bridge; +using Speckle.Core.Logging; + +namespace Speckle.Connectors.DUI.Bindings; + +/// +/// POC: This is a class that sanity checks basic bridge functionality. It is required by the frontend's tests page. +/// +public class TestBinding : IBinding +{ + public string Name => "testBinding"; + public IBridge Parent { get; } + + public TestBinding(IBridge bridge) + { + Parent = bridge; + } + + public string SayHi(string name, int count, bool sayHelloNotHi) + { + var baseGreeting = $"{(sayHelloNotHi ? "Hello" : "Hi")} {name}!"; + var finalGreeting = ""; + for (int i = 0; i < Math.Max(1, Math.Abs(count)); i++) + { + finalGreeting += baseGreeting + Environment.NewLine; + } + + return finalGreeting; + } + + public void ShouldThrow() => throw new SpeckleException("I am supposed to throw."); + + public void GoAway() => Debug.WriteLine("Okay, going away."); + + public object GetComplexType() => + new + { + Id = GetHashCode() + " - I am a string", + count = GetHashCode(), + thisIsABoolean = false + }; + + public void TriggerEvent(string eventName) + { + switch (eventName) + { + case "emptyTestEvent": + Parent.Send("emptyTestEvent"); + break; + case "testEvent": + default: + Parent.Send( + "testEvent", + new + { + IsOk = true, + Name = "foo", + Count = 42 + } + ); + break; + } + } +} diff --git a/DUI3-DX/DUI3/Speckle.Connectors.DUI/Bridge/BrowserBridge.cs b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Bridge/BrowserBridge.cs new file mode 100644 index 0000000000..aa26374558 --- /dev/null +++ b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Bridge/BrowserBridge.cs @@ -0,0 +1,343 @@ +using System.Collections.Concurrent; +using System.Reflection; +using System.Runtime.InteropServices; +using Speckle.Newtonsoft.Json; +using Speckle.Connectors.DUI.Bindings; +using System.Threading.Tasks.Dataflow; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using Microsoft.Extensions.Logging; +using Speckle.Connectors.Utils; +using Speckle.Core.Models.Extensions; + +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 +/// +[ClassInterface(ClassInterfaceType.AutoDual)] +[ComVisible(true)] +public sealed class BrowserBridge : IBridge +{ + /// + /// The name under which we expect the frontend to hoist this bindings class to the global scope. + /// e.g., `receiveBindings` should be available as `window.receiveBindings`. + /// + + private readonly JsonSerializerSettings _serializerOptions; + private readonly ConcurrentDictionary _resultsStore = new(); + private readonly SynchronizationContext _mainThreadContext; + private readonly ITopLevelExceptionHandler _topLevelExceptionHandler; + + private IReadOnlyDictionary _bindingMethodCache = new Dictionary(); + + private ActionBlock? _actionBlock; + private Action? _scriptMethod; + + private IBinding? _binding; + private Type? _bindingType; + + private readonly ILogger _logger; + + /// + /// Action that opens up the developer tools of the respective browser we're using. While webview2 allows for "right click, inspect", cefsharp does not - hence the need for this. + /// + public Action? ShowDevToolsAction { get; set; } + + public string FrontendBoundName { get; private set; } = "Unknown"; + + public object? Browser { get; private set; } + + public IBinding? Binding + { + get => _binding; + private set + { + if (_binding != null || this != value?.Parent) + { + throw new ArgumentException($"Binding: {FrontendBoundName} is already bound or does not match bridge"); + } + + _binding = value; + } + } + + private struct RunMethodArgs + { + public string MethodName; + public string RequestId; + public string MethodArgs; + } + + /// + /// Initializes a new instance of the class. + /// + /// The settings to use for JSON serialization and deserialization. + /// The factory to create a logger for . + public BrowserBridge(JsonSerializerSettings jsonSerializerSettings, ILoggerFactory loggerFactory) + { + _serializerOptions = jsonSerializerSettings; + _logger = loggerFactory.CreateLogger(); + _topLevelExceptionHandler = new TopLevelExceptionHandler(loggerFactory, this); //TODO: Probably we could inject this with a Lazy somewhere + // Capture the main thread's SynchronizationContext + _mainThreadContext = SynchronizationContext.Current; + } + + public void AssociateWithBinding( + IBinding binding, + Action scriptMethod, + object browser, + Action showDevToolsAction + ) + { + // set via binding property to ensure explosion if already bound + Binding = binding; + FrontendBoundName = binding.Name; + Browser = browser; + _scriptMethod = scriptMethod; + + _bindingType = binding.GetType(); + ShowDevToolsAction = showDevToolsAction; + + // Note: we need to filter out getter and setter methods here because they are not really nicely + // supported across browsers, hence the !method.IsSpecialName. + var bindingMethodCache = new Dictionary(); + foreach (var m in _bindingType.GetMethods().Where(method => !method.IsSpecialName)) + { + 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, + CancellationToken = new CancellationTokenSource(TimeSpan.FromHours(3)).Token // Not sure we need such a long time. //TODO: This token source is not disposed.... + } + ); + + _logger.LogInformation("Bridge bound to front end name {FrontEndName}", binding.Name); + } + + private async Task OnActionBlock(RunMethodArgs args) + { + Result result = await _topLevelExceptionHandler + .CatchUnhandled(async () => await ExecuteMethod(args.MethodName, args.MethodArgs).ConfigureAwait(false)) + .ConfigureAwait(false); + + string resultJson = result.IsSuccess + ? JsonConvert.SerializeObject(result.Value, _serializerOptions) + : SerializeFormattedException(result.Exception); + + NotifyUIMethodCallResultReady(args.RequestId, resultJson); + } + + /// + /// Used by the Frontend bridge logic to understand which methods are available. + /// + /// + public string[] GetBindingsMethodNames() + { + var bindingNames = _bindingMethodCache.Keys.ToArray(); + Debug.WriteLine($"{FrontendBoundName}: " + JsonConvert.SerializeObject(bindingNames, Formatting.Indented)); + 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 + { + MethodName = methodName, + RequestId = requestId, + MethodArgs = args + } + ); + if (!wasAccepted) + { + throw new InvalidOperationException($"Action block declined to Post ({methodName} {requestId} {args})"); + } + } + } + + /// + /// Run actions on main thread. + /// + /// Action to run on main thread. + public void RunOnMainThread(Action action) + { + _mainThreadContext.Post( + _ => + { + // Execute the action on the main thread + action.Invoke(); + }, + null + ); + } + + /// + /// Used by the action block to invoke the actual method called by the UI. + /// + /// + /// + /// The was not found or the given were not valid for the method call + /// The invoked method throws an exception + /// The Json + private async Task ExecuteMethod(string methodName, string args) + { + if (!_bindingMethodCache.TryGetValue(methodName, out MethodInfo method)) + { + throw new ArgumentException( + $"Cannot find method {methodName} in bindings class {_bindingType?.AssemblyQualifiedName}.", + nameof(methodName) + ); + } + + var parameters = method.GetParameters(); + var jsonArgsArray = JsonConvert.DeserializeObject(args); + if (parameters.Length != jsonArgsArray?.Length) + { + throw new ArgumentException( + $"Wrong number of arguments when invoking binding function {methodName}, expected {parameters.Length}, but got {jsonArgsArray?.Length}.", + nameof(args) + ); + } + + var typedArgs = new object?[jsonArgsArray.Length]; + + for (int i = 0; i < typedArgs.Length; i++) + { + var ccc = JsonConvert.DeserializeObject(jsonArgsArray[i], parameters[i].ParameterType, _serializerOptions); + typedArgs[i] = ccc; + } + + object? resultTyped; + try + { + resultTyped = method.Invoke(Binding, typedArgs); + } + catch (TargetInvocationException ex) + { + throw new TargetInvocationException($"Unhandled exception while executing {methodName}", ex.InnerException); + } + + // Was the method called async? + if (resultTyped is not Task resultTypedTask) + { + // Regular method: no need to await things + return resultTyped; + } + + // It's an async call + await resultTypedTask.ConfigureAwait(false); + + // If has a "Result" property return the value otherwise null (Task etc) + PropertyInfo? resultProperty = resultTypedTask.GetType().GetProperty(nameof(Task.Result)); + object? taskResult = resultProperty?.GetValue(resultTypedTask); + return taskResult; + } + + /// + /// Errors that not handled on bindings. + /// + private string SerializeFormattedException(Exception e) + { + //TODO: I'm not sure we still require this... the top level handler is already displaying the toast + var errorDetails = new + { + Message = e.Message, // Topmost message + Error = e.ToFormattedString(), // All messages from exceptions + StackTrace = e.ToString(), + }; + + return JsonConvert.SerializeObject(errorDetails, _serializerOptions); + } + + /// + /// Notifies the UI that the method call is ready. We do not give the result back to the ui here via ExecuteScriptAsync + /// because of limitations we discovered along the way (e.g, / chars need to be escaped). + /// + /// + /// + private void NotifyUIMethodCallResultReady(string requestId, string? serializedData = null) + { + _resultsStore[requestId] = serializedData; + string script = $"{FrontendBoundName}.responseReady('{requestId}')"; + _scriptMethod.NotNull().Invoke(script); + } + + /// + /// Called by the ui to get back the serialized result of the method. See comments above for why. + /// + /// + /// + public string? GetCallResult(string requestId) + { + bool isFound = _resultsStore.TryRemove(requestId, out string? res); + if (!isFound) + { + throw new ArgumentException($"No result for the given request id was found: {requestId}", nameof(requestId)); + } + return res; + } + + /// + /// Shows the dev tools. This is currently only needed for CefSharp - other browser + /// controls allow for right click + inspect. + /// + public void ShowDevTools() + { + ShowDevToolsAction?.Invoke(); + } + + [SuppressMessage("Design", "CA1054:URI-like parameters should not be strings", Justification = "Url run as process")] + public void OpenUrl(string url) + { + Process.Start(new ProcessStartInfo { FileName = url, UseShellExecute = true }); + } + + public void Send(string eventName) + { + if (_binding is null) + { + throw new InvalidOperationException("Bridge was not Initialized"); + } + + var script = $"{FrontendBoundName}.emit('{eventName}')"; + + _scriptMethod.NotNull().Invoke(script); + } + + public void Send(string eventName, T data) + where T : class + { + if (_binding is null) + { + throw new InvalidOperationException("Bridge was not associated with a binding"); + } + + string payload = JsonConvert.SerializeObject(data, _serializerOptions); + string requestId = $"{Guid.NewGuid()}_{eventName}"; + _resultsStore[requestId] = payload; + var script = $"{FrontendBoundName}.emitResponseReady('{eventName}', '{requestId}')"; + _scriptMethod.NotNull().Invoke(script); + } +} diff --git a/DUI3-DX/DUI3/Speckle.Connectors.DUI/Bridge/IBridge.cs b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Bridge/IBridge.cs new file mode 100644 index 0000000000..3b26e58d82 --- /dev/null +++ b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Bridge/IBridge.cs @@ -0,0 +1,48 @@ +using Speckle.Connectors.DUI.Bindings; + +namespace Speckle.Connectors.DUI.Bridge; + +/// +/// Describes a bridge - a wrapper class around a specific browser host. Not needed right now, +/// but if in the future we will have other bridge classes (e.g, ones that wrap around other browsers), +/// it just might be useful. +/// +public interface IBridge +{ + // POC: documnetation comments + string FrontendBoundName { get; } + + void AssociateWithBinding(IBinding binding, Action scriptMethod, object browser, Action showDevToolsAction); + + /// + /// This method is called by the Frontend bridge to understand what it can actually call. It should return the method names of the bindings that this bridge wraps around. + /// + /// + public string[] GetBindingsMethodNames(); + + /// + /// This method is called by the Frontend bridge when invoking any of the wrapped binding's methods. + /// + /// + /// + /// + /// + public void RunMethod(string methodName, string requestId, string args); + + /// + /// Run actions on main thread. + /// Some applications might need to run some operations on main thread as deferred actions. + /// + /// Action to run on main thread. + public void RunOnMainThread(Action action); + + /// + /// Bridge was not associated with a binding + public void Send(string eventName); + + /// + /// data to store + /// + public void Send(string eventName, T data) + where T : class; +} diff --git a/DUI3-DX/DUI3/Speckle.Connectors.DUI/Bridge/SyncToCurrentThread.cs b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Bridge/SyncToCurrentThread.cs new file mode 100644 index 0000000000..820d49fd0c --- /dev/null +++ b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Bridge/SyncToCurrentThread.cs @@ -0,0 +1,17 @@ +using Speckle.Connectors.Utils.Operations; + +namespace Speckle.Connectors.DUI.Bridge; + +/// +/// Implements the interface and runs a given function on the current thread using Task.Run. +/// +public class SyncToCurrentThread : ISyncToThread +{ + /// + /// Executes a given function on the current thread using Task.Run. + /// + /// The return type of the function. + /// The function to execute. + /// A Task object representing the asynchronous operation. + public Task RunOnThread(Func func) => Task.FromResult(func.Invoke()); +} diff --git a/DUI3-DX/DUI3/Speckle.Connectors.DUI/Bridge/SyncToUIThread.cs b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Bridge/SyncToUIThread.cs new file mode 100644 index 0000000000..90a52df570 --- /dev/null +++ b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Bridge/SyncToUIThread.cs @@ -0,0 +1,35 @@ +using System.Diagnostics.CodeAnalysis; +using Speckle.Connectors.Utils.Operations; + +namespace Speckle.Connectors.DUI.Bridge; + +public class SyncToUIThread : ISyncToThread +{ + private readonly IBridge _bridge; + + public SyncToUIThread(IBridge bridge) + { + _bridge = bridge; + } + + [SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Task Completion Source")] + public Task RunOnThread(Func func) + { + TaskCompletionSource tcs = new(); + + _bridge.RunOnMainThread(() => + { + try + { + T result = func.Invoke(); + tcs.SetResult(result); + } + catch (Exception ex) + { + tcs.SetException(ex); + } + }); + + return tcs.Task; + } +} diff --git a/DUI3-DX/DUI3/Speckle.Connectors.DUI/Bridge/TopLevelExceptionHandler.cs b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Bridge/TopLevelExceptionHandler.cs new file mode 100644 index 0000000000..679303cfe9 --- /dev/null +++ b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Bridge/TopLevelExceptionHandler.cs @@ -0,0 +1,135 @@ +using System.Diagnostics.CodeAnalysis; +using Microsoft.Extensions.Logging; +using Speckle.Connectors.DUI.Bindings; +using Speckle.Connectors.Utils; +using Speckle.Core.Logging; +using Speckle.Core.Models.Extensions; +using Speckle.InterfaceGenerator; + +namespace Speckle.Connectors.DUI.Bridge; + +/// +/// Result Pattern struct +/// +/// +public readonly struct Result +{ + //Don't add new members to this struct, it is perfect. + public T? Value { get; } + public Exception? Exception { get; } + + [MemberNotNullWhen(false, nameof(Exception))] + public bool IsSuccess => Exception is null; + + /// + /// Create a successful result + /// + /// + public Result(T result) + { + Value = result; + } + + /// + /// Create a non-sucessful result + /// + /// + /// was null + public Result([NotNull] Exception? result) + { + Exception = result.NotNull(); + } +} + +/// +/// The functions provided by this class are designed to be used in all "top level" scenarios (e.g. Plugin, UI, and Event callbacks) +/// To provide "last ditch effort" handling of unexpected exceptions that have not been handled. +/// 1. Log events to the injected +/// 2. Display a toast notification with exception details +///
+///
+/// +/// exceptions cannot be recovered from. +/// They will be rethrown to allow the host app to run its handlers
+/// Depending on the host app, this may trigger windows event logging, and recovery snapshots before ultimately terminating the process
+/// Attempting to swallow them may lead to data corruption, deadlocking, or things worse than a managed host app crash. +///
+[GenerateAutoInterface] +public sealed class TopLevelExceptionHandler : ITopLevelExceptionHandler +{ + private readonly ILogger _logger; + private readonly IBridge _bridge; + + private const string UNHANDLED_LOGGER_TEMPLATE = "An unhandled Exception occured"; + + public TopLevelExceptionHandler(ILoggerFactory loggerFactory, IBridge bridge) + { + _logger = loggerFactory.CreateLogger(); + _bridge = bridge; + } + + /// + /// Invokes the given function within a / block, + /// and provides exception handling for unexpected exceptions that have not been handled.
+ ///
+ /// 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) + { + CatchUnhandled(() => + { + function.Invoke(); + return (object?)null; + }); + } + + /// + /// return type + /// A result pattern struct (where exceptions have been handled) + public Result CatchUnhandled(Func function) + { + return CatchUnhandled(() => Task.FromResult(function.Invoke())).Result; + } + + /// + public async Task> CatchUnhandled(Func> function) + { + try + { + try + { + return new(await function.Invoke().ConfigureAwait(false)); + } + catch (Exception ex) when (!ex.IsFatal()) + { + _logger.LogError(ex, UNHANDLED_LOGGER_TEMPLATE); + + SetGlobalNotification( + ToastNotificationType.DANGER, + "Unhandled Exception Occured", + ex.ToFormattedString(), + false + ); + return new(ex); + } + } + catch (Exception ex) + { + _logger.LogCritical(ex, UNHANDLED_LOGGER_TEMPLATE); + throw; + } + } + + private void SetGlobalNotification(ToastNotificationType type, string title, string message, bool autoClose) => + _bridge.Send( + BasicConnectorBindingCommands.SET_GLOBAL_NOTIFICATION, //TODO: We could move these constants into a DUI3 constants static class + new + { + type, + title, + description = message, + autoClose + } + ); +} diff --git a/DUI3-DX/DUI3/Speckle.Connectors.DUI/ContainerRegistration.cs b/DUI3-DX/DUI3/Speckle.Connectors.DUI/ContainerRegistration.cs new file mode 100644 index 0000000000..affb3e031b --- /dev/null +++ b/DUI3-DX/DUI3/Speckle.Connectors.DUI/ContainerRegistration.cs @@ -0,0 +1,44 @@ +using Speckle.Autofac.DependencyInjection; +using Speckle.Connectors.DUI.Bridge; +using Speckle.Connectors.DUI.Models.Card.SendFilter; +using Speckle.Connectors.DUI.Utils; +using Speckle.Connectors.Utils.Operations; +using Speckle.Core.Transports; +using Speckle.Newtonsoft.Json; +using Speckle.Newtonsoft.Json.Serialization; + +namespace Speckle.Connectors.DUI; + +public static class ContainerRegistration +{ + public static void AddDUI(this SpeckleContainerBuilder speckleContainerBuilder) + { + // send operation and dependencies + speckleContainerBuilder.AddSingletonInstance(); + speckleContainerBuilder.AddTransient(); + speckleContainerBuilder.AddSingleton(); + speckleContainerBuilder.AddTransient(); // POC: Each binding should have it's own bridge instance + speckleContainerBuilder.AddSingleton(); + speckleContainerBuilder.AddSingleton(GetJsonSerializerSettings()); + } + + private static JsonSerializerSettings GetJsonSerializerSettings() + { + // Register WebView2 panel stuff + JsonSerializerSettings settings = + new() + { + Error = (_, args) => + { + // POC: we should probably do a bit more than just swallowing this! + Console.WriteLine("*** JSON ERROR: " + args.ErrorContext); + }, + ContractResolver = new CamelCasePropertyNamesContractResolver(), + NullValueHandling = NullValueHandling.Ignore, + ReferenceLoopHandling = ReferenceLoopHandling.Ignore, + DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate, + Converters = { new DiscriminatedObjectConverter(), new AbstractConverter() } + }; + return settings; + } +} diff --git a/DUI3-DX/DUI3/Speckle.Connectors.DUI/Exceptions/SpeckleSendFilterException.cs b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Exceptions/SpeckleSendFilterException.cs new file mode 100644 index 0000000000..f5ef115b07 --- /dev/null +++ b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Exceptions/SpeckleSendFilterException.cs @@ -0,0 +1,14 @@ +using Speckle.Core.Logging; + +namespace Speckle.Connectors.DUI.Exceptions; + +public class SpeckleSendFilterException : SpeckleException +{ + public SpeckleSendFilterException() { } + + public SpeckleSendFilterException(string message) + : base(message) { } + + public SpeckleSendFilterException(string message, Exception innerException) + : base(message, innerException) { } +} diff --git a/DUI3-DX/DUI3/Speckle.Connectors.DUI/GlobalSuppressions.cs b/DUI3-DX/DUI3/Speckle.Connectors.DUI/GlobalSuppressions.cs new file mode 100644 index 0000000000..1e481a130a --- /dev/null +++ b/DUI3-DX/DUI3/Speckle.Connectors.DUI/GlobalSuppressions.cs @@ -0,0 +1,14 @@ +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. + +using System.Diagnostics.CodeAnalysis; + +[assembly: SuppressMessage( + "Performance", + "CA1848:Use the LoggerMessage delegates", + Justification = "", + Scope = "member", + Target = "~M:Speckle.Connectors.DUI.Bridge.BrowserBridge.AssociateWithBinding(Speckle.Connectors.DUI.Bindings.IBinding,System.Action{System.String},System.Object)" +)] diff --git a/DUI3-DX/DUI3/Speckle.Connectors.DUI/Models/Card/CreateVersionArgs.cs b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Models/Card/CreateVersionArgs.cs new file mode 100644 index 0000000000..e99ee583fe --- /dev/null +++ b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Models/Card/CreateVersionArgs.cs @@ -0,0 +1,3 @@ +namespace Speckle.Connectors.DUI.Models.Card; + +public record CreateVersionArgs(string ModelCardId, string ObjectId); diff --git a/DUI3-DX/DUI3/Speckle.Connectors.DUI/Models/Card/ModelCard.cs b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Models/Card/ModelCard.cs new file mode 100644 index 0000000000..51aae18fb2 --- /dev/null +++ b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Models/Card/ModelCard.cs @@ -0,0 +1,26 @@ +using Speckle.Connectors.DUI.Settings; +using Speckle.Connectors.DUI.Utils; + +namespace Speckle.Connectors.DUI.Models.Card; + +public class ModelCard : DiscriminatedObject +{ + /// + /// This is a unique id generated by the ui to make model cards easier to reference around. + /// It's not the actual model (branch) id. + /// + public string? ModelCardId { get; set; } + + /// + /// Model id. FKA branch id. + /// + public string? ModelId { get; set; } + + /// + /// Project id. FKA stream id. + /// + public string? ProjectId { get; set; } + public string? AccountId { get; set; } + + public List? Settings { get; set; } +} diff --git a/DUI3-DX/DUI3/Speckle.Connectors.DUI/Models/Card/ModelCardError.cs b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Models/Card/ModelCardError.cs new file mode 100644 index 0000000000..dccfe792e8 --- /dev/null +++ b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Models/Card/ModelCardError.cs @@ -0,0 +1,3 @@ +namespace Speckle.Connectors.DUI.Models.Card; + +public record ModelCardError(string ModelCardId, Exception Error); diff --git a/DUI3-DX/DUI3/Speckle.Connectors.DUI/Models/Card/ModelCardNotification.cs b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Models/Card/ModelCardNotification.cs new file mode 100644 index 0000000000..0a7b60c9d6 --- /dev/null +++ b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Models/Card/ModelCardNotification.cs @@ -0,0 +1,10 @@ +namespace Speckle.Connectors.DUI.Models.Card; + +public class ModelCardNotification +{ + public string? ModelCardId { get; set; } + public string? Text { get; set; } + public string? Level { get; set; } + public int Timeout { get; set; } + public bool Dismissible { get; set; } = true; +} diff --git a/DUI3-DX/DUI3/Speckle.Connectors.DUI/Models/Card/ModelCardProgress.cs b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Models/Card/ModelCardProgress.cs new file mode 100644 index 0000000000..1d2dbdeb7f --- /dev/null +++ b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Models/Card/ModelCardProgress.cs @@ -0,0 +1,7 @@ +namespace Speckle.Connectors.DUI.Models.Card; + +/// +/// 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); diff --git a/DUI3-DX/DUI3/Speckle.Connectors.DUI/Models/Card/ReceiveResult.cs b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Models/Card/ReceiveResult.cs new file mode 100644 index 0000000000..c4473e7f38 --- /dev/null +++ b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Models/Card/ReceiveResult.cs @@ -0,0 +1,5 @@ +// using Speckle.Connectors.Utils.Builders; +// +// namespace Speckle.Connectors.DUI.Models.Card; +// +// public record ReceiveResult(bool Display, HostObjectBuilderResult ReceiveConversionResults); diff --git a/DUI3-DX/DUI3/Speckle.Connectors.DUI/Models/Card/ReceiverModelCard.cs b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Models/Card/ReceiverModelCard.cs new file mode 100644 index 0000000000..143a646709 --- /dev/null +++ b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Models/Card/ReceiverModelCard.cs @@ -0,0 +1,11 @@ +namespace Speckle.Connectors.DUI.Models.Card; + +public class ReceiverModelCard : ModelCard +{ + public string? ProjectName { get; set; } + public string? ModelName { get; set; } + public string? SelectedVersionId { get; set; } + public string? LatestVersionId { get; set; } + public bool HasDismissedUpdateWarning { get; set; } + public List? BakedObjectIds { get; set; } +} diff --git a/DUI3-DX/DUI3/Speckle.Connectors.DUI/Models/Card/ReceiverModelCardResult.cs b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Models/Card/ReceiverModelCardResult.cs new file mode 100644 index 0000000000..a1bb275dd0 --- /dev/null +++ b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Models/Card/ReceiverModelCardResult.cs @@ -0,0 +1,7 @@ +namespace Speckle.Connectors.DUI.Models.Card; + +public class ReceiverModelCardResult +{ + public string? ModelCardId { get; set; } + public List BakedObjectIds { get; set; } = new(); +} diff --git a/DUI3-DX/DUI3/Speckle.Connectors.DUI/Models/Card/SendFilter/DirectSelectionSendFilter.cs b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Models/Card/SendFilter/DirectSelectionSendFilter.cs new file mode 100644 index 0000000000..97bce1ef6b --- /dev/null +++ b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Models/Card/SendFilter/DirectSelectionSendFilter.cs @@ -0,0 +1,13 @@ +using Speckle.Connectors.DUI.Utils; + +namespace Speckle.Connectors.DUI.Models.Card.SendFilter; + +public abstract class DirectSelectionSendFilter : DiscriminatedObject, ISendFilter +{ + public string Name { get; set; } = "Selection"; + public string? Summary { get; set; } + public bool IsDefault { get; set; } + public List SelectedObjectIds { get; set; } = new(); + public abstract List GetObjectIds(); + public abstract bool CheckExpiry(string[] changedObjectIds); +} diff --git a/DUI3-DX/DUI3/Speckle.Connectors.DUI/Models/Card/SendFilter/EverythingSendFilter.cs b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Models/Card/SendFilter/EverythingSendFilter.cs new file mode 100644 index 0000000000..a5c83f77df --- /dev/null +++ b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Models/Card/SendFilter/EverythingSendFilter.cs @@ -0,0 +1,12 @@ +using Speckle.Connectors.DUI.Utils; + +namespace Speckle.Connectors.DUI.Models.Card.SendFilter; + +public abstract class EverythingSendFilter : DiscriminatedObject, ISendFilter +{ + public string Name { get; set; } = "Everything"; + public string? Summary { get; set; } = "All supported objects in the file."; + public bool IsDefault { get; set; } + public abstract List GetObjectIds(); + public abstract bool CheckExpiry(string[] changedObjectIds); +} diff --git a/DUI3-DX/DUI3/Speckle.Connectors.DUI/Models/Card/SendFilter/ISendFilter.cs b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Models/Card/SendFilter/ISendFilter.cs new file mode 100644 index 0000000000..4e025c43b9 --- /dev/null +++ b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Models/Card/SendFilter/ISendFilter.cs @@ -0,0 +1,21 @@ +namespace Speckle.Connectors.DUI.Models.Card.SendFilter; + +public interface ISendFilter +{ + public string Name { get; set; } + public string? Summary { get; set; } + public bool IsDefault { get; set; } + + /// + /// Gets the ids of the objects targeted by the filter from the host application. + /// + /// + public List GetObjectIds(); + + /// + /// Checks whether any of the targeted objects are affected by changes from the host application. + /// + /// + /// + public bool CheckExpiry(string[] changedObjectIds); +} diff --git a/DUI3-DX/DUI3/Speckle.Connectors.DUI/Models/Card/SenderModelCard.cs b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Models/Card/SenderModelCard.cs new file mode 100644 index 0000000000..1bee9faabc --- /dev/null +++ b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Models/Card/SenderModelCard.cs @@ -0,0 +1,11 @@ +using Speckle.Connectors.DUI.Models.Card.SendFilter; + +namespace Speckle.Connectors.DUI.Models.Card; + +public class SenderModelCard : ModelCard +{ + public ISendFilter? SendFilter { get; set; } + + // [JsonIgnore] + // public HashSet ChangedObjectIds { get; set; } = new(); +} diff --git a/DUI3-DX/DUI3/Speckle.Connectors.DUI/Models/DocumentInfo.cs b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Models/DocumentInfo.cs new file mode 100644 index 0000000000..b15a76c278 --- /dev/null +++ b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Models/DocumentInfo.cs @@ -0,0 +1,9 @@ +namespace Speckle.Connectors.DUI.Models; + +public record DocumentInfo(string Location, string Name, string Id) +{ + //?.Replace("\\", "\\\\"); // for some reason, when returning variables from a direct binding call + //we don't need this. nevertheless, after switching to a post response back to the ui, + //we need this to ensure deserialization in js doesn't throw. it's frustrating! + public string? Message { get; set; } +} diff --git a/DUI3-DX/DUI3/Speckle.Connectors.DUI/Models/DocumentModelStore.cs b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Models/DocumentModelStore.cs new file mode 100644 index 0000000000..c43cb83ae3 --- /dev/null +++ b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Models/DocumentModelStore.cs @@ -0,0 +1,110 @@ +using System.Collections.ObjectModel; +using Speckle.Connectors.DUI.Utils; +using Speckle.Newtonsoft.Json; +using Speckle.Connectors.DUI.Models.Card; + +namespace Speckle.Connectors.DUI.Models; + +/// +/// Encapsulates the state Speckle needs to persist in the host app's document. +/// +public abstract class DocumentModelStore +{ + 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 JsonSerializerSettings _serializerOptions; + + private readonly bool _writeToFileOnChange; + + /// + /// Base host app state class that controls the storage of the models in the file. + /// + /// our custom serialiser that should be globally DI'ed in. + /// Whether to store the models state in the file on any change. Defaults to false out of caution, but it's recommended to set to true, unless severe host app limitations. + protected DocumentModelStore(JsonSerializerSettings serializerOptions, bool writeToFileOnChange) + { + _serializerOptions = serializerOptions; + _writeToFileOnChange = writeToFileOnChange; + + RegisterWriteOnChangeEvent(); + } + + private void RegisterWriteOnChangeEvent() + { + if (_writeToFileOnChange) + { + _models.CollectionChanged += (_, _) => WriteToFile(); + } + } + + /// + /// This event is triggered by each specific host app implementation of the document model store. + /// + // POC: unsure about the PublicAPI annotation, unsure if this changed handle should live here on the store... :/ + public event EventHandler? DocumentChanged; + + public virtual bool IsDocumentInit { get; set; } + + // TODO: not sure about this, throwing an exception, needs some thought... + // Further note (dim): If we reach to the stage of throwing an exception here because a model is not found, there's a huge misalignment between the UI's list of model cards and the host app's. + // 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; + } + + public void UpdateModel(ModelCard model) + { + int idx = Models.ToList().FindIndex(m => model.ModelCardId == m.ModelCardId); + Models[idx] = model; + } + + public void RemoveModel(ModelCard model) + { + int index = Models.ToList().FindIndex(m => m.ModelCardId == model.ModelCardId); + Models.RemoveAt(index); + } + + protected void OnDocumentChanged() => DocumentChanged?.Invoke(this, EventArgs.Empty); + + 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() + { + return JsonConvert.SerializeObject(Models, _serializerOptions); + } + + // POC: this seemms more like a IModelsDeserializer?, seems disconnected from this class + protected ObservableCollection? Deserialize(string models) + { + return JsonConvert.DeserializeObject>(models, _serializerOptions); + } + + /// + /// Implement this method according to the host app's specific ways of storing custom data in its file. + /// + public abstract void WriteToFile(); + + /// + /// Implement this method according to the host app's specific ways of reading custom data from its file. + /// + public abstract void ReadFromFile(); +} diff --git a/DUI3-DX/DUI3/Speckle.Connectors.DUI/Objects/ISpeckleHostObject.cs b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Objects/ISpeckleHostObject.cs new file mode 100644 index 0000000000..e3637ceabf --- /dev/null +++ b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Objects/ISpeckleHostObject.cs @@ -0,0 +1,8 @@ +namespace Speckle.Connectors.DUI.Objects; + +public interface ISpeckleHostObject +{ + public string ApplicationId { get; } + public string SpeckleId { get; } + public bool IsExpired { get; } +} diff --git a/DUI3-DX/DUI3/Speckle.Connectors.DUI/Settings/CardSetting.cs b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Settings/CardSetting.cs new file mode 100644 index 0000000000..6945a3d4af --- /dev/null +++ b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Settings/CardSetting.cs @@ -0,0 +1,12 @@ +using Speckle.Connectors.DUI.Utils; + +namespace Speckle.Connectors.DUI.Settings; + +public class CardSetting : DiscriminatedObject +{ + public string? Id { get; set; } + public string? Title { get; set; } + public string? Type { get; set; } + public object? Value { get; set; } + public List? Enum { get; set; } +} diff --git a/DUI3-DX/DUI3/Speckle.Connectors.DUI/Speckle.Connectors.DUI.csproj b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Speckle.Connectors.DUI.csproj new file mode 100644 index 0000000000..421db57bc1 --- /dev/null +++ b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Speckle.Connectors.DUI.csproj @@ -0,0 +1,17 @@ + + + + netstandard2.0 + + + + + + + + + + + + + diff --git a/DUI3-DX/DUI3/Speckle.Connectors.DUI/Url.cs b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Url.cs new file mode 100644 index 0000000000..b5f5a66f1b --- /dev/null +++ b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Url.cs @@ -0,0 +1,19 @@ +namespace Speckle.Connectors.DUI; + +// POC: XAML file accept Static only, but later we can search more is it possible to inject this? or necessary?? + +/// +/// Only place that we reference to URLs to connector UIs. +/// +/// +/// If we are on 'work in progress' branch on UI repo, +/// we can replace the netlify url with 'preview' one which is provided within each PR. +/// sample url produced by PR on `dui3` branch on `speckle-server` -> deploy-preview-2076--boisterous-douhua-e3cefb.netlify.app +/// +public static class Url +{ + public static readonly Uri Netlify = new("https://boisterous-douhua-e3cefb.netlify.app/"); + + // In CefSharp XAML file we cannot call ToString() function over URI + public static readonly string NetlifyString = Netlify.ToString(); +} diff --git a/DUI3-DX/DUI3/Speckle.Connectors.DUI/Utils/DiscriminatedObject.cs b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Utils/DiscriminatedObject.cs new file mode 100644 index 0000000000..e0d341c6b6 --- /dev/null +++ b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Utils/DiscriminatedObject.cs @@ -0,0 +1,16 @@ +namespace Speckle.Connectors.DUI.Utils; + +/// +/// Any polymorphic type base should inherit from this class in order for it to be properly deserialized. +/// - Class inheritance scenario For example, if you have a base class BaseSettings, and from it you create RhinoBaseSettings and AutocadBaseSettings, the BaseSetting class should inherit from this class. +/// - Interface scenario: you have an ISenderCard interface, which you implement as ReceiverCard and SenderCard. Both ReceiverCard and SenderCard should inherit from this class. +/// +/// POC: should probaby be changed to attribute instead of inheritence? TBC +public class DiscriminatedObject +{ + public string TypeDiscriminator + { + get => this.GetType().Name; + set { } + } +} diff --git a/DUI3-DX/DUI3/Speckle.Connectors.DUI/Utils/DiscriminatedObjectConverter.cs b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Utils/DiscriminatedObjectConverter.cs new file mode 100644 index 0000000000..2d1dfedf8f --- /dev/null +++ b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Utils/DiscriminatedObjectConverter.cs @@ -0,0 +1,130 @@ +using System.Diagnostics; +using System.Reflection; +using Speckle.Core.Serialisation; +using Speckle.Newtonsoft.Json; +using Speckle.Newtonsoft.Json.Linq; +using Speckle.Newtonsoft.Json.Serialization; + +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. +/// +// POC: automatic registration of compatible objects +public class DiscriminatedObjectConverter : JsonConverter +{ + private readonly JsonSerializer _localSerializer = + new() + { + DefaultValueHandling = DefaultValueHandling.Ignore, + ContractResolver = new CamelCasePropertyNamesContractResolver(), + NullValueHandling = NullValueHandling.Ignore + }; + + public override void WriteJson(JsonWriter writer, DiscriminatedObject? value, JsonSerializer serializer) + { + if (value is null) + { + return; + } + var jo = JObject.FromObject(value, _localSerializer); + jo.WriteTo(writer); + } + + public override DiscriminatedObject? ReadJson( + JsonReader reader, + Type objectType, + DiscriminatedObject? existingValue, + bool hasExistingValue, + JsonSerializer serializer + ) + { + JObject jsonObject = JObject.Load(reader); + + var typeName = + jsonObject.Value("typeDiscriminator") + ?? throw new SpeckleDeserializeException( + "DUI3 Discriminator converter deserialization failed: did not find a typeDiscriminator field." + ); + var type = + GetTypeByName(typeName) + ?? throw new SpeckleDeserializeException( + "DUI3 Discriminator converter deserialization failed, type not found: " + typeName + ); + var obj = Activator.CreateInstance(type, true); + serializer.Populate(jsonObject.CreateReader(), obj); + + // Store the JSON property names in the object for later comparison + if (obj is PropertyValidator pv) + { + // Capture property names from JSON + var jsonPropertyNames = jsonObject.Properties().Select(p => p.Name).ToList(); + + pv.JsonPropertyNames = jsonPropertyNames; + } + + // POC: cast? throw if null? + 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 + { + // 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) + { + _typeCache[name] = type; + 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); + } + } + + // should this throw instead? :/ + return null; + } +} + +public class AbstractConverter : JsonConverter +{ + public override bool CanConvert(Type objectType) => objectType == typeof(TAbstract); + + public override object? ReadJson( + JsonReader reader, + Type objectType, + object? existingValue, + JsonSerializer serializer + ) => serializer.Deserialize(reader); + + public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) => + serializer.Serialize(writer, value); +} diff --git a/DUI3-DX/DUI3/Speckle.Connectors.DUI/Utils/ModelNotFoundException.cs b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Utils/ModelNotFoundException.cs new file mode 100644 index 0000000000..a856d5b130 --- /dev/null +++ b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Utils/ModelNotFoundException.cs @@ -0,0 +1,16 @@ +// POC: why is SpeckleException in this namespace? :8 + +using Speckle.Core.Logging; + +namespace Speckle.Connectors.DUI.Utils; + +public class ModelNotFoundException : SpeckleException +{ + public ModelNotFoundException(string message) + : base(message) { } + + public ModelNotFoundException(string message, Exception inner) + : base(message, inner) { } + + public ModelNotFoundException() { } +} diff --git a/DUI3-DX/DUI3/Speckle.Connectors.DUI/Utils/PropertyValidator.cs b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Utils/PropertyValidator.cs new file mode 100644 index 0000000000..fd6546990c --- /dev/null +++ b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Utils/PropertyValidator.cs @@ -0,0 +1,51 @@ +using Speckle.Connectors.Utils; +using Speckle.Newtonsoft.Json; + +namespace Speckle.Connectors.DUI.Utils; + +public class PropertyValidator : DiscriminatedObject +{ + [JsonIgnore] + public List? JsonPropertyNames { get; set; } + + public bool InitializeNewProperties() + { + bool isUpdated = false; + var properties = this.GetType().GetProperties(); + + // Create a new instance of the current type to get default values + var defaultInstance = Activator.CreateInstance(this.GetType()); + + foreach (var property in properties) + { + if (property.GetValue(this) == null) + { + // Get the default value from the new instance + var defaultValue = property.GetValue(defaultInstance); + + // Set this default value to the current instance + property.SetValue(this, defaultValue); + isUpdated = true; + } + } + + return isUpdated; // Return true if any property was updated + } + + public bool CheckRemovedProperties() + { + bool removedPropertiesExist = false; + var currentPropertyNames = this.GetType().GetProperties().Select(p => p.Name).ToList(); + + foreach (var jsonPropName in JsonPropertyNames.NotNull()) + { + if (!currentPropertyNames.Contains(jsonPropName)) + { + // This property was in the JSON but not in the class + removedPropertiesExist = true; + } + } + + return removedPropertiesExist; + } +} diff --git a/DUI3-DX/DUI3/Speckle.Connectors.DUI/packages.lock.json b/DUI3-DX/DUI3/Speckle.Connectors.DUI/packages.lock.json new file mode 100644 index 0000000000..9f44602e71 --- /dev/null +++ b/DUI3-DX/DUI3/Speckle.Connectors.DUI/packages.lock.json @@ -0,0 +1,556 @@ +{ + "version": 2, + "dependencies": { + ".NETStandard,Version=v2.0": { + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Direct", + "requested": "[7.0.0, )", + "resolved": "7.0.0", + "contentHash": "kmn78+LPVMOWeITUjIlfxUPDsI0R6G0RkeAMBmQxAJ7vBJn4q2dTva7pWi65ceN5vPGjJ9q/Uae2WKgvfktJAw==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Memory": "4.5.5" + } + }, + "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" + } + }, + "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, )", + "resolved": "1.14.1", + "contentHash": "mOOmFYwad3MIOL14VCjj02LljyF1GNw1wP0YVlxtcPvqdxjGGMNdNJJxHptlry3MOd8b40Flm8RPOM8JOlN2sQ==" + }, + "Speckle.Core": { + "type": "Direct", + "requested": "[3.0.1-alpha.14, )", + "resolved": "3.0.1-alpha.14", + "contentHash": "RzQPVIGFFkKvG56YLr8ACtiwdWJE6IJ9vCQ4qHa0PIsUEpfzAIAi59jnzqtByOFC0FiFrFPow9bkfzylaZorAA==", + "dependencies": { + "GraphQL.Client": "6.0.0", + "Microsoft.CSharp": "4.7.0", + "Microsoft.Data.Sqlite": "7.0.5", + "Polly": "7.2.3", + "Polly.Contrib.WaitAndRetry": "1.1.1", + "Polly.Extensions.Http": "3.0.0", + "Sentry": "3.33.0", + "Sentry.Serilog": "3.33.0", + "Serilog": "2.12.0", + "Serilog.Enrichers.ClientInfo": "1.3.0", + "Serilog.Exceptions": "8.4.0", + "Serilog.Sinks.Console": "4.1.0", + "Serilog.Sinks.Seq": "5.2.2", + "SerilogTimings": "3.0.1", + "Speckle.Newtonsoft.Json": "13.0.2", + "System.DoubleNumerics": "3.1.3" + } + }, + "Speckle.InterfaceGenerator": { + "type": "Direct", + "requested": "[0.9.5, )", + "resolved": "0.9.5", + "contentHash": "oU/L7pN1R7q8KkbrpQ3WJnHirPHqn+9DEA7asOcUiggV5dzVg1A/VYs7GOSusD24njxXh03tE3a2oTLOjt3cVg==" + }, + "System.Threading.Tasks.Dataflow": { + "type": "Direct", + "requested": "[6.0.0, )", + "resolved": "6.0.0", + "contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA==" + }, + "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.AspNetCore.Http": { + "type": "Transitive", + "resolved": "2.1.1", + "contentHash": "pPDcCW8spnyibK3krpxrOpaFHf5fjV6k1Hsl6gfh77N/8gRYlLU7MOQDUnjpEwdlHmtxwJKQJNxZqVQOmJGRUw==", + "dependencies": { + "Microsoft.AspNetCore.Http.Abstractions": "2.1.1", + "Microsoft.AspNetCore.WebUtilities": "2.1.1", + "Microsoft.Extensions.ObjectPool": "2.1.1", + "Microsoft.Extensions.Options": "2.1.1", + "Microsoft.Net.Http.Headers": "2.1.1" + } + }, + "Microsoft.AspNetCore.Http.Abstractions": { + "type": "Transitive", + "resolved": "2.1.1", + "contentHash": "kQUEVOU4loc8CPSb2WoHFTESqwIa8Ik7ysCBfTwzHAd0moWovc9JQLmhDIHlYLjHbyexqZAlkq/FPRUZqokebw==", + "dependencies": { + "Microsoft.AspNetCore.Http.Features": "2.1.1", + "System.Text.Encodings.Web": "4.5.0" + } + }, + "Microsoft.AspNetCore.Http.Features": { + "type": "Transitive", + "resolved": "2.1.1", + "contentHash": "VklZ7hWgSvHBcDtwYYkdMdI/adlf7ebxTZ9kdzAhX+gUs5jSHE9mZlTamdgf9miSsxc1QjNazHXTDJdVPZKKTw==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.1.1" + } + }, + "Microsoft.AspNetCore.WebUtilities": { + "type": "Transitive", + "resolved": "2.1.1", + "contentHash": "PGKIZt4+412Z/XPoSjvYu/QIbTxcAQuEFNoA1Pw8a9mgmO0ZhNBmfaNyhgXFf7Rq62kP0tT/2WXpxdcQhkFUPA==", + "dependencies": { + "Microsoft.Net.Http.Headers": "2.1.1", + "System.Text.Encodings.Web": "4.5.0" + } + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "3aeMZ1N0lJoSyzqiP03hqemtb1BijhsJADdobn/4nsMJ8V1H+CrpuduUe4hlRdx+ikBQju1VGjMD1GJ3Sk05Eg==", + "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.DependencyInjection": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "elNeOmkeX3eDVG6pYVeV82p29hr+UKDaBhrZyWvWLw/EVZSYEkZlQdkp0V39k/Xehs2Qa0mvoCvkVj3eQxNQ1Q==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "7.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0", + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "h3j/QfmFN4S0w4C2A6X7arXij/M/OVw3uQHSOFxnND4DyAzO1F9eMX7Eti7lU/OkSthEE0WzRsfT/Dmx86jzCw==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "7.0.0", + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "Microsoft.Extensions.Logging": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "Nw2muoNrOG5U5qa2ZekXwudUn2BJcD41e65zwmDHb1fQegTX66UokLWZkJRpqSSHXDOWZ5V0iqhbxOEky91atA==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "7.0.0", + "Microsoft.Extensions.DependencyInjection": "7.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0", + "Microsoft.Extensions.Logging.Abstractions": "7.0.0", + "Microsoft.Extensions.Options": "7.0.0", + "System.Diagnostics.DiagnosticSource": "7.0.0" + } + }, + "Microsoft.Extensions.ObjectPool": { + "type": "Transitive", + "resolved": "2.1.1", + "contentHash": "SErON45qh4ogDp6lr6UvVmFYW0FERihW+IQ+2JyFv1PUyWktcJytFaWH5zarufJvZwhci7Rf1IyGXr9pVEadTw==" + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "lP1yBnTTU42cKpMozuafbvNtQ7QcBjr/CcK3bYOGEMH55Fjt+iecXjT6chR7vbgCMqy3PG3aNQSZgo/EuY/9qQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0", + "Microsoft.Extensions.Primitives": "7.0.0", + "System.ComponentModel.Annotations": "5.0.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "um1KU5kxcRp3CNuI8o/GrZtD4AIOXDk+RLsytjZ9QPok3ttLUelLKpilVPuaFT3TFjOhSibUAso0odbOaCDj3Q==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "Microsoft.Net.Http.Headers": { + "type": "Transitive", + "resolved": "2.1.1", + "contentHash": "lPNIphl8b2EuhOE9dMH6EZDmu7pS882O+HMi5BJNsigxHaWlBrYxZHFZgE18cyaPp6SSZcTkKkuzfjV/RRQKlA==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.1.1", + "System.Buffers": "4.5.0" + } + }, + "Microsoft.NETCore.Platforms": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==" + }, + "Microsoft.NETCore.Targets": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "Polly": { + "type": "Transitive", + "resolved": "7.2.3", + "contentHash": "DeCY0OFbNdNxsjntr1gTXHJ5pKUwYzp04Er2LLeN3g6pWhffsGuKVfMBLe1lw7x76HrPkLxKEFxBlpRxS2nDEQ==" + }, + "Polly.Contrib.WaitAndRetry": { + "type": "Transitive", + "resolved": "1.1.1", + "contentHash": "1MUQLiSo4KDkQe6nzQRhIU05lm9jlexX5BVsbuw0SL82ynZ+GzAHQxJVDPVBboxV37Po3SG077aX8DuSy8TkaA==" + }, + "Polly.Extensions.Http": { + "type": "Transitive", + "resolved": "3.0.0", + "contentHash": "drrG+hB3pYFY7w1c3BD+lSGYvH2oIclH8GRSehgfyP5kjnFnHKQuuBhuHLv+PWyFuaTDyk/vfRpnxOzd11+J8g==", + "dependencies": { + "Polly": "7.1.0" + } + }, + "Sentry": { + "type": "Transitive", + "resolved": "3.33.0", + "contentHash": "8vbD2o6IR2wrRrkSiRbnodWGWUOqIlwYtzpjvPNOb5raJdOf+zxMwfS8f6nx9bmrTTfDj7KrCB8C/5OuicAc8A==", + "dependencies": { + "System.Reflection.Metadata": "5.0.0", + "System.Text.Json": "5.0.2" + } + }, + "Sentry.Serilog": { + "type": "Transitive", + "resolved": "3.33.0", + "contentHash": "V8BU7QGWg2qLYfNPqtuTBhC1opysny5l+Ifp6J6PhOeAxU0FssR7nYfbJVetrnLIoh2rd3DlJ6hHYYQosQYcUQ==", + "dependencies": { + "Sentry": "3.33.0", + "Serilog": "2.7.1" + } + }, + "Serilog": { + "type": "Transitive", + "resolved": "2.12.0", + "contentHash": "xaiJLIdu6rYMKfQMYUZgTy8YK7SMZjB4Yk50C/u//Z4OsvxkUfSPJy4nknfvwAC34yr13q7kcyh4grbwhSxyZg==" + }, + "Serilog.Enrichers.ClientInfo": { + "type": "Transitive", + "resolved": "1.3.0", + "contentHash": "mTc7PM+wC9Hr7LWSwqt5mmnlAr7RJs+eTb3PGPRhwdOackk95MkhUZognuxXEdlW19HAFNmEBTSBY5DfLwM8jQ==", + "dependencies": { + "Microsoft.AspNetCore.Http": "2.1.1", + "Serilog": "2.7.1" + } + }, + "Serilog.Exceptions": { + "type": "Transitive", + "resolved": "8.4.0", + "contentHash": "nc/+hUw3lsdo0zCj0KMIybAu7perMx79vu72w0za9Nsi6mWyNkGXxYxakAjWB7nEmYL6zdmhEQRB4oJ2ALUeug==", + "dependencies": { + "Serilog": "2.8.0", + "System.Reflection.TypeExtensions": "4.7.0" + } + }, + "Serilog.Formatting.Compact": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "pNroKVjo+rDqlxNG5PXkRLpfSCuDOBY0ri6jp9PLe505ljqwhwZz8ospy2vWhQlFu5GkIesh3FcDs4n7sWZODA==", + "dependencies": { + "Serilog": "2.8.0" + } + }, + "Serilog.Sinks.Console": { + "type": "Transitive", + "resolved": "4.1.0", + "contentHash": "K6N5q+5fetjnJPvCmkWOpJ/V8IEIoMIB1s86OzBrbxwTyHxdx3pmz4H+8+O/Dc/ftUX12DM1aynx/dDowkwzqg==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "Serilog.Sinks.File": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "uwV5hdhWPwUH1szhO8PJpFiahqXmzPzJT/sOijH/kFgUx+cyoDTMM8MHD0adw9+Iem6itoibbUXHYslzXsLEAg==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "Serilog.Sinks.PeriodicBatching": { + "type": "Transitive", + "resolved": "3.1.0", + "contentHash": "NDWR7m3PalVlGEq3rzoktrXikjFMLmpwF0HI4sowo8YDdU+gqPlTHlDQiOGxHfB0sTfjPA9JjA7ctKG9zqjGkw==", + "dependencies": { + "Serilog": "2.0.0" + } + }, + "Serilog.Sinks.Seq": { + "type": "Transitive", + "resolved": "5.2.2", + "contentHash": "1Csmo5ua7NKUe0yXUx+zsRefjAniPWcXFhUXxXG8pwo0iMiw2gjn9SOkgYnnxbgWqmlGv236w0N/dHc2v5XwMg==", + "dependencies": { + "Serilog": "2.12.0", + "Serilog.Formatting.Compact": "1.1.0", + "Serilog.Sinks.File": "5.0.0", + "Serilog.Sinks.PeriodicBatching": "3.1.0" + } + }, + "SerilogTimings": { + "type": "Transitive", + "resolved": "3.0.1", + "contentHash": "Zs28eTgszAMwpIrbBnWHBI50yuxL50p/dmAUWmy75+axdZYK/Sjm5/5m1N/CisR8acJUhTVcjPZrsB1P5iv0Uw==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "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.Buffers": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, + "System.Collections.Immutable": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "FXkLXiK0sVVewcso0imKQoOxjoPAj42R8HtjjbSjVPAzwDfzoyoznWxgA3c38LDbN9SJux1xXoXYAhz98j7r2g==", + "dependencies": { + "System.Memory": "4.5.4" + } + }, + "System.ComponentModel.Annotations": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dMkqfy2el8A8/I76n2Hi1oBFEbG1SfxD2l5nhwXV3XjlnOmwxJlQbYpJH4W51odnU9sARCSAgv7S3CyAFMkpYg==" + }, + "System.Diagnostics.DiagnosticSource": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "9W0ewWDuAyDqS2PigdTxk6jDKonfgscY/hP8hm7VpxYhNHZHKvZTdRckberlFk3VnCmr3xBUyMBut12Q+T2aOw==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "System.DoubleNumerics": { + "type": "Transitive", + "resolved": "3.1.3", + "contentHash": "KRKEM/L3KBodjA9VOg3EifFVWUY6EOqaMB05UvPEDm7Zeby/kZW+4kdWUEPzW6xtkwf46p661L9NrbeeQhtLzw==", + "dependencies": { + "NETStandard.Library": "1.6.1" + } + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Numerics.Vectors": "4.4.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + }, + "System.Numerics.Vectors": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" + }, + "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.Reflection.Metadata": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "5NecZgXktdGg34rh1OenY1rFNDCI8xSjFr+Z4OU4cU06AQHUdRnIIEeWENu3Wl4YowbzkymAIMvi3WyK9U53pQ==", + "dependencies": { + "System.Collections.Immutable": "5.0.0" + } + }, + "System.Reflection.TypeExtensions": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "VybpaOQQhqE6siHppMktjfGBw1GCwvCqiufqmP8F1nj7fTUNtW35LOEt3UZTEsECfo+ELAl/9o9nJx3U91i7vA==" + }, + "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" + } + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + }, + "System.Runtime.InteropServices.WindowsRuntime": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "J4GUi3xZQLUBasNwZnjrffN8i5wpHrBtZoLG+OhRyGo/+YunMRWWtwoMDlUAIdmX0uRfpHIBDSV6zyr3yf00TA==", + "dependencies": { + "System.Runtime": "4.3.0" + } + }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "5.0.1", + "contentHash": "KmJ+CJXizDofbq6mpqDoRRLcxgOd2z9X3XoFNULSbvbqVRZkFX3istvr+MUjL6Zw1RT+RNdoI4GYidIINtgvqQ==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Memory": "4.5.4" + } + }, + "System.Text.Json": { + "type": "Transitive", + "resolved": "5.0.2", + "contentHash": "I47dVIGiV6SfAyppphxqupertT/5oZkYLDCX6vC3HpOI4ZLjyoKAreUoem2ie6G0RbRuFrlqz/PcTQjfb2DOfQ==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "5.0.0", + "System.Buffers": "4.5.1", + "System.Memory": "4.5.4", + "System.Numerics.Vectors": "4.5.0", + "System.Runtime.CompilerServices.Unsafe": "5.0.0", + "System.Text.Encodings.Web": "5.0.1", + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "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.autofac": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "[7.0.0, )" + } + }, + "speckle.connectors.utils": { + "type": "Project", + "dependencies": { + "Serilog.Extensions.Logging": "[7.0.0, )", + "Speckle.Autofac": "[2.0.999-local, )", + "Speckle.Core": "[3.0.1-alpha.14, )" + } + }, + "Serilog.Extensions.Logging": { + "type": "CentralTransitive", + "requested": "[7.0.0, )", + "resolved": "7.0.0", + "contentHash": "9faU0zNQqU7I6soVhLUMYaGNpgWv6cKlKb2S5AnS8gXxzW/em5Ladm/6FMrWTnX41cdbdGPOWNAo6adi4WaJ6A==", + "dependencies": { + "Microsoft.Extensions.Logging": "7.0.0", + "Serilog": "2.12.0" + } + } + } + } +} \ No newline at end of file diff --git a/DUI3-DX/Directory.Build.props b/DUI3-DX/Directory.Build.props new file mode 100644 index 0000000000..7093d49171 --- /dev/null +++ b/DUI3-DX/Directory.Build.props @@ -0,0 +1,18 @@ + + + + + enable + enable + true + false + true + true + true + + + true + true + $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb + + diff --git a/DUI3-DX/Directory.Packages.props b/DUI3-DX/Directory.Packages.props new file mode 100644 index 0000000000..06de90bb1c --- /dev/null +++ b/DUI3-DX/Directory.Packages.props @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DUI3-DX/Sdk/Speckle.Autofac/AssemblyResolver.cs b/DUI3-DX/Sdk/Speckle.Autofac/AssemblyResolver.cs new file mode 100644 index 0000000000..8f887f56ea --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Autofac/AssemblyResolver.cs @@ -0,0 +1,26 @@ +using System.Reflection; + +namespace Speckle.Autofac; + +public static class AssemblyResolver +{ + public static Assembly? OnAssemblyResolve(object? sender, ResolveEventArgs args) + { + // POC: tight binding to files + string name = args.Name.Split(',')[0]; + string? path = Path.GetDirectoryName(typeof(T).Assembly.Location); + + if (path == null) + { + return null; + } + string assemblyFile = Path.Combine(path, name + ".dll"); + + Assembly? assembly = null; + if (File.Exists(assemblyFile)) + { + assembly = Assembly.LoadFrom(assemblyFile); + } + return assembly; + } +} diff --git a/DUI3-DX/Sdk/Speckle.Autofac/ContainerRegistration.cs b/DUI3-DX/Sdk/Speckle.Autofac/ContainerRegistration.cs new file mode 100644 index 0000000000..f99b7c6728 --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Autofac/ContainerRegistration.cs @@ -0,0 +1,12 @@ +using Speckle.Autofac.DependencyInjection; + +namespace Speckle.Autofac; + +public static class ContainerRegistration +{ + public static void AddAutofac(this SpeckleContainerBuilder builder) + { + // send operation and dependencies + builder.AddScoped(); + } +} diff --git a/DUI3-DX/Sdk/Speckle.Autofac/DependencyInjection/Factory.cs b/DUI3-DX/Sdk/Speckle.Autofac/DependencyInjection/Factory.cs new file mode 100644 index 0000000000..bfd7593aae --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Autofac/DependencyInjection/Factory.cs @@ -0,0 +1,25 @@ +using Autofac.Features.Indexed; +using Speckle.InterfaceGenerator; + +namespace Speckle.Autofac.DependencyInjection; + +[GenerateAutoInterface] +public class Factory : IFactory + where TValue : class +{ + private readonly IIndex _types; + + public Factory(IIndex types) + { + _types = types; + } + + public TValue? ResolveInstance(string strongName) + { + if (_types.TryGetValue(strongName, out TValue value)) + { + return value; + } + return null; + } +} diff --git a/DUI3-DX/Sdk/Speckle.Autofac/DependencyInjection/ISpeckleContainerContext.cs b/DUI3-DX/Sdk/Speckle.Autofac/DependencyInjection/ISpeckleContainerContext.cs new file mode 100644 index 0000000000..a65698422a --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Autofac/DependencyInjection/ISpeckleContainerContext.cs @@ -0,0 +1,7 @@ +namespace Speckle.Autofac.DependencyInjection; + +public interface ISpeckleContainerContext +{ + T Resolve() + where T : notnull; +} diff --git a/DUI3-DX/Sdk/Speckle.Autofac/DependencyInjection/ISpeckleModule.cs b/DUI3-DX/Sdk/Speckle.Autofac/DependencyInjection/ISpeckleModule.cs new file mode 100644 index 0000000000..795caa576c --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Autofac/DependencyInjection/ISpeckleModule.cs @@ -0,0 +1,6 @@ +namespace Speckle.Autofac.DependencyInjection; + +public interface ISpeckleModule +{ + void Load(SpeckleContainerBuilder builder); +} diff --git a/DUI3-DX/Sdk/Speckle.Autofac/DependencyInjection/SpeckleContainer.cs b/DUI3-DX/Sdk/Speckle.Autofac/DependencyInjection/SpeckleContainer.cs new file mode 100644 index 0000000000..189481d641 --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Autofac/DependencyInjection/SpeckleContainer.cs @@ -0,0 +1,19 @@ +using Autofac; + +namespace Speckle.Autofac.DependencyInjection; + +public class SpeckleContainer +{ + private readonly IContainer _container; + + public SpeckleContainer(IContainer container) + { + _container = container; + } + + public T Resolve() + where T : class + { + return _container.Resolve(); + } +} diff --git a/DUI3-DX/Sdk/Speckle.Autofac/DependencyInjection/SpeckleContainerBuilder.cs b/DUI3-DX/Sdk/Speckle.Autofac/DependencyInjection/SpeckleContainerBuilder.cs new file mode 100644 index 0000000000..363e55cc56 --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Autofac/DependencyInjection/SpeckleContainerBuilder.cs @@ -0,0 +1,234 @@ +using System.Reflection; +using Autofac; +using Microsoft.Extensions.Logging; +using Speckle.Autofac.Files; +using Module = Autofac.Module; + +namespace Speckle.Autofac.DependencyInjection; + +public class SpeckleContainerBuilder +{ + private readonly struct SpeckleContainerContext : ISpeckleContainerContext + { + private readonly IComponentContext _componentContext; + + public SpeckleContainerContext(IComponentContext componentContext) + { + _componentContext = componentContext; + } + + public T Resolve() + where T : notnull => _componentContext.Resolve(); + } + + private static readonly Type s_moduleType = typeof(ISpeckleModule); + private readonly IStorageInfo _storageInfo; + + private SpeckleContainerBuilder(IStorageInfo storageInfo, ContainerBuilder? containerBuilder) + { + _storageInfo = storageInfo; + ContainerBuilder = containerBuilder ?? new ContainerBuilder(); + } + + public static SpeckleContainerBuilder CreateInstance() => new(new StorageInfo(), null); + + private static SpeckleContainerBuilder CreateInstanceForLoading(ContainerBuilder containerBuilder) => + new(new StorageInfo(), containerBuilder); + + // POC: HOW TO GET TYPES loaded, this feels a bit heavy handed and relies on Autofac where we can probably do something different + public SpeckleContainerBuilder LoadAutofacModules(Assembly pluginAssembly, IEnumerable dependencyPaths) + { + // look for assemblies in these paths that offer autofac modules + foreach (string path in dependencyPaths) + { + // POC: naming conventions + // find assemblies + var assembliesInPath = _storageInfo.GetFilenamesInDirectory(path, "Speckle*.dll"); + var assemblies = assembliesInPath.Select(LoadAssemblyFile).ToList(); + if (assemblies.All(x => x != pluginAssembly)) + { + LoadAssembly(pluginAssembly); + } + } + + return this; + } + + private Assembly? LoadAssemblyFile(string file) + { + try + { + // inspect the assemblies for Autofac.Module + var assembly = Assembly.LoadFrom(file); + LoadAssembly(assembly); + return assembly; + } + // POC: catch only certain exceptions + catch (Exception ex) when (!ex.IsFatal()) + { + return null; + } + } + + private void LoadAssembly(Assembly assembly) + { + var moduleClasses = assembly.GetTypes().Where(x => x.GetInterfaces().Contains(s_moduleType)).ToList(); + + // create each module + // POC: could look for some attribute here + foreach (var moduleClass in moduleClasses) + { + var module = (ISpeckleModule)Activator.CreateInstance(moduleClass); + ContainerBuilder.RegisterModule(new ModuleAdapter(module)); + } + } + + private readonly Lazy> _types = + new(() => + { + var types = new List(); + foreach ( + var asm in AppDomain.CurrentDomain + .GetAssemblies() + .Where(x => x.GetName().Name.StartsWith("Speckle", StringComparison.OrdinalIgnoreCase)) + ) + { + types.AddRange(asm.GetTypes()); + } + return types; + }); + + public IReadOnlyList SpeckleTypes => _types.Value; + public ContainerBuilder ContainerBuilder { get; } + + private class ModuleAdapter : Module + { + private readonly ISpeckleModule _speckleModule; + + public ModuleAdapter(ISpeckleModule speckleModule) + { + _speckleModule = speckleModule; + } + + protected override void Load(ContainerBuilder builder) => _speckleModule.Load(CreateInstanceForLoading(builder)); + } + + public SpeckleContainerBuilder AddModule(ISpeckleModule module) + { + ContainerBuilder.RegisterModule(new ModuleAdapter(module)); + + return this; + } + + public SpeckleContainerBuilder AddSingleton(T instance) + where T : class + { + ContainerBuilder.RegisterInstance(instance).SingleInstance(); + return this; + } + + public SpeckleContainerBuilder AddSingleton() + where T : class + { + ContainerBuilder.RegisterType().AsSelf().SingleInstance(); + return this; + } + + public SpeckleContainerBuilder AddSingletonInstance() + where T : class + { + ContainerBuilder.RegisterType().AsSelf().SingleInstance().AutoActivate(); + return this; + } + + public SpeckleContainerBuilder AddSingletonInstance() + where T : class, TInterface + where TInterface : notnull + { + ContainerBuilder.RegisterType().As().SingleInstance().AutoActivate(); + return this; + } + + public SpeckleContainerBuilder AddSingleton() + where T : class, TInterface + where TInterface : notnull + { + ContainerBuilder.RegisterType().As().SingleInstance(); + return this; + } + + public SpeckleContainerBuilder AddSingleton(string param, string value) + where T : class, TInterface + where TInterface : notnull + { + ContainerBuilder.RegisterType().As().SingleInstance().WithParameter(param, value); + return this; + } + + public SpeckleContainerBuilder AddScoped() + where T : class, TInterface + where TInterface : notnull + { + ContainerBuilder.RegisterType().As().InstancePerLifetimeScope(); + return this; + } + + public SpeckleContainerBuilder AddScoped(Func action) + where T : notnull + { + ContainerBuilder.Register(c => action(new SpeckleContainerContext(c))).InstancePerLifetimeScope(); + return this; + } + + public SpeckleContainerBuilder AddScoped() + where T : class + { + ContainerBuilder.RegisterType().AsSelf().InstancePerLifetimeScope(); + return this; + } + + public SpeckleContainerBuilder AddTransient() + where T : class, TInterface + where TInterface : notnull + { + ContainerBuilder.RegisterType().As().InstancePerDependency(); + return this; + } + + public SpeckleContainerBuilder AddTransient() + where T : class + { + ContainerBuilder.RegisterType().AsSelf().InstancePerDependency(); + return this; + } + + //Scans assembly for classes that implement the same name interface and registers as transient + public SpeckleContainerBuilder ScanAssembly(Assembly assembly) + { + ContainerBuilder + .RegisterAssemblyTypes(assembly) + .Where(t => t.IsClass) + .As(GetInterfacesWithNameName) + .InstancePerDependency(); + return this; + } + + public SpeckleContainerBuilder ScanAssemblyOfType() => ScanAssembly(typeof(T).Assembly); + + private static IEnumerable GetInterfacesWithNameName(Type type) => + type.GetInterfaces().Where(i => i.Name == "I" + type.Name); + + public SpeckleContainer Build() + { + var container = ContainerBuilder.Build(); + + // POC: we could create the factory on construction of the container and then inject that and store it + var logger = container.Resolve().CreateLogger(); + + // POC: we could probably expand on this + List assemblies = AppDomain.CurrentDomain.GetAssemblies().Select(x => x.FullName).ToList(); + logger.LogInformation("Loaded assemblies: {@Assemblies}", assemblies); + + return new SpeckleContainer(container); + } +} diff --git a/DUI3-DX/Sdk/Speckle.Autofac/DependencyInjection/UnitOfWork.cs b/DUI3-DX/Sdk/Speckle.Autofac/DependencyInjection/UnitOfWork.cs new file mode 100644 index 0000000000..43e69adf3d --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Autofac/DependencyInjection/UnitOfWork.cs @@ -0,0 +1,35 @@ +using Autofac; +using Speckle.InterfaceGenerator; + +namespace Speckle.Autofac.DependencyInjection; + +public partial interface IUnitOfWork : IDisposable + where TService : class { } + +[GenerateAutoInterface] +public sealed class UnitOfWork : IUnitOfWork + where TService : class +{ + private readonly ILifetimeScope _unitOfWorkScope; + private bool _notDisposed = true; + + public UnitOfWork(ILifetimeScope unitOfWorkScope, TService service) + { + _unitOfWorkScope = unitOfWorkScope; + Service = service; + } + + public TService Service { get; } + + [AutoInterfaceIgnore] + public void Dispose() => Disposing(true); + + private void Disposing(bool fromDispose) + { + if (_notDisposed && fromDispose) + { + _unitOfWorkScope.Dispose(); + _notDisposed = false; + } + } +} diff --git a/DUI3-DX/Sdk/Speckle.Autofac/DependencyInjection/UnitOfWorkFactory.cs b/DUI3-DX/Sdk/Speckle.Autofac/DependencyInjection/UnitOfWorkFactory.cs new file mode 100644 index 0000000000..bbd8deb59d --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Autofac/DependencyInjection/UnitOfWorkFactory.cs @@ -0,0 +1,40 @@ +using Autofac; +using Autofac.Core; +using Speckle.InterfaceGenerator; + +namespace Speckle.Autofac.DependencyInjection; + +[GenerateAutoInterface] +public class UnitOfWorkFactory : IUnitOfWorkFactory +{ + private readonly ILifetimeScope _parentScope; + + public UnitOfWorkFactory(ILifetimeScope parentScope) + { + _parentScope = parentScope; + } + + public IUnitOfWork Resolve() + where TService : class + { + ILifetimeScope? childScope = null; + + try + { + childScope = _parentScope.BeginLifetimeScope(); + var service = childScope.Resolve(); + + return new UnitOfWork(childScope, service); + } + catch (DependencyResolutionException dre) + { + childScope?.Dispose(); + + // POC: check exception and how to pass this further up + throw new DependencyResolutionException( + $"Dependency error resolving {typeof(TService)} within UnitOfWorkFactory", + dre + ); + } + } +} diff --git a/DUI3-DX/Sdk/Speckle.Autofac/ExceptionExtensions.cs b/DUI3-DX/Sdk/Speckle.Autofac/ExceptionExtensions.cs new file mode 100644 index 0000000000..626e9751c1 --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Autofac/ExceptionExtensions.cs @@ -0,0 +1,45 @@ +using System.Diagnostics.Contracts; + +namespace Speckle.Autofac; + +public static class ExceptionExtensions +{ + /// + /// Helper function for catch blocks to avoid catching and handling/wrapping of some critical exception types that are unlikely to be truly handleable + /// + /// + /// We should aim to always catch specific exception types, and have all functions document the types they may throw. + /// However, this is not always achievable. + /// e.g. when dealing with legacy code, some third-party APIs, or in cases where we want to prevent a host app crash. + /// In these cases, we often want to catch all exceptions, and opt out only of the ones that definitely shouldn't be handled + /// + /// + /// + /// try + /// { + /// SomethingSketchy(); + /// } + /// catch (Exception ex) when (!IsFatal(ex)) + /// { + /// throw new SpeckleException("Failed to do something", ex); + /// } + /// + /// + /// + /// for types that are unlikely to ever be recoverable + [Pure] + public static bool IsFatal(this Exception ex) + { + return ex switch + { + OutOfMemoryException + or ThreadAbortException + or InvalidProgramException + or AccessViolationException + or AppDomainUnloadedException + or BadImageFormatException + => true, + _ => false, + }; + } +} diff --git a/DUI3-DX/Sdk/Speckle.Autofac/Files/StorageInfo.cs b/DUI3-DX/Sdk/Speckle.Autofac/Files/StorageInfo.cs new file mode 100644 index 0000000000..a810c1c838 --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Autofac/Files/StorageInfo.cs @@ -0,0 +1,12 @@ +using Speckle.InterfaceGenerator; + +namespace Speckle.Autofac.Files; + +[GenerateAutoInterface] +public class StorageInfo : IStorageInfo +{ + public IEnumerable GetFilenamesInDirectory(string path, string pattern) + { + return Directory.GetFiles(path, pattern); + } +} diff --git a/DUI3-DX/Sdk/Speckle.Autofac/GlobalSuppressions.cs b/DUI3-DX/Sdk/Speckle.Autofac/GlobalSuppressions.cs new file mode 100644 index 0000000000..2501a9ef35 --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Autofac/GlobalSuppressions.cs @@ -0,0 +1,21 @@ +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. + +using System.Diagnostics.CodeAnalysis; + +[assembly: SuppressMessage( + "Performance", + "CA1805:Do not initialize unnecessarily", + Justification = "", + Scope = "member", + Target = "~F:Speckle.Autofac.DependencyInjection.ScopedFactory`1._disposed" +)] +[assembly: SuppressMessage( + "Performance", + "CA1848:Use the LoggerMessage delegates", + Justification = "", + Scope = "member", + Target = "~M:Speckle.Autofac.DependencyInjection.AutofacContainer.Build~Speckle.Autofac.DependencyInjection.AutofacContainer" +)] diff --git a/DUI3-DX/Sdk/Speckle.Autofac/Speckle.Autofac.csproj b/DUI3-DX/Sdk/Speckle.Autofac/Speckle.Autofac.csproj new file mode 100644 index 0000000000..c87411e8e9 --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Autofac/Speckle.Autofac.csproj @@ -0,0 +1,13 @@ + + + + true + netstandard2.0 + + + + + + + + diff --git a/DUI3-DX/Sdk/Speckle.Autofac/packages.lock.json b/DUI3-DX/Sdk/Speckle.Autofac/packages.lock.json new file mode 100644 index 0000000000..bea3fab0df --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Autofac/packages.lock.json @@ -0,0 +1,113 @@ +{ + "version": 2, + "dependencies": { + ".NETStandard,Version=v2.0": { + "Autofac": { + "type": "Direct", + "requested": "[5.2.0, )", + "resolved": "5.2.0", + "contentHash": "V8dBH0dsv75uDzl7Sw+HkhKDPUw2eXnlMjcSVMH+tLo2s67MpTKGyDj1pDcpR+IF2u4YRs0s3/x7R88YJzIWvg==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "1.1.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Direct", + "requested": "[7.0.0, )", + "resolved": "7.0.0", + "contentHash": "kmn78+LPVMOWeITUjIlfxUPDsI0R6G0RkeAMBmQxAJ7vBJn4q2dTva7pWi65ceN5vPGjJ9q/Uae2WKgvfktJAw==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Memory": "4.5.5" + } + }, + "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" + } + }, + "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, )", + "resolved": "1.14.1", + "contentHash": "mOOmFYwad3MIOL14VCjj02LljyF1GNw1wP0YVlxtcPvqdxjGGMNdNJJxHptlry3MOd8b40Flm8RPOM8JOlN2sQ==" + }, + "Speckle.InterfaceGenerator": { + "type": "Direct", + "requested": "[0.9.5, )", + "resolved": "0.9.5", + "contentHash": "oU/L7pN1R7q8KkbrpQ3WJnHirPHqn+9DEA7asOcUiggV5dzVg1A/VYs7GOSusD24njxXh03tE3a2oTLOjt3cVg==" + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "1Am6l4Vpn3/K32daEqZI+FFr96OlZkgwK2LcT3pZ2zWubR5zTPW3/FkO1Rat9kb7oQOa4rxgl9LJHc5tspCWfg==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.2" + } + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.NETCore.Platforms": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "System.Buffers": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Numerics.Vectors": "4.4.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + }, + "System.Numerics.Vectors": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ==" + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "4.5.3", + "contentHash": "3TIsJhD1EiiT0w2CcDMN/iSSwnNnsrnbzeVHSKkaEgV85txMprmuO+Yq2AdSbeVGcg28pdNDTPK87tJhX7VFHw==" + }, + "System.Threading.Tasks.Extensions": { + "type": "Transitive", + "resolved": "4.5.2", + "contentHash": "BG/TNxDFv0svAzx8OiMXDlsHfGw623BZ8tCXw4YLhDFDvDhNUEV58jKYMGRnkbJNm7c3JNNJDiN7JBMzxRBR2w==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "4.5.2" + } + } + } + } +} \ No newline at end of file diff --git a/DUI3-DX/Sdk/Speckle.Connectors.Utils/Builders/IHostObjectBuilder.cs b/DUI3-DX/Sdk/Speckle.Connectors.Utils/Builders/IHostObjectBuilder.cs new file mode 100644 index 0000000000..853b2b3ba4 --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Connectors.Utils/Builders/IHostObjectBuilder.cs @@ -0,0 +1,32 @@ +using Speckle.Connectors.Utils.Conversion; +using Speckle.Core.Models; + +namespace Speckle.Connectors.Utils.Builders; + +// POC: We might consider to put also IRootObjectBuilder interface here in same folder and create concrete classes from it in per connector. +public interface IHostObjectBuilder +{ + /// + /// Build host application objects from root commit object. + /// + /// Commit object that received from server. + /// 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. + HostObjectBuilderResult Build( + Base rootObject, + string projectName, + string modelName, + Action? onOperationProgressed, + CancellationToken cancellationToken + ); +} + +public record HostObjectBuilderResult( + IEnumerable BakedObjectIds, + IEnumerable ConversionResults +); diff --git a/DUI3-DX/Sdk/Speckle.Connectors.Utils/Builders/IRootObjectBuilder.cs b/DUI3-DX/Sdk/Speckle.Connectors.Utils/Builders/IRootObjectBuilder.cs new file mode 100644 index 0000000000..bdf9fdf2a5 --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Connectors.Utils/Builders/IRootObjectBuilder.cs @@ -0,0 +1,17 @@ +using Speckle.Connectors.Utils.Conversion; +using Speckle.Connectors.Utils.Operations; +using Speckle.Core.Models; + +namespace Speckle.Connectors.Utils.Builders; + +public interface IRootObjectBuilder +{ + public RootObjectBuilderResult Build( + IReadOnlyList objects, + SendInfo sendInfo, + Action? onOperationProgressed = null, + CancellationToken ct = default + ); +} + +public record RootObjectBuilderResult(Base RootObject, IEnumerable ConversionResults); diff --git a/DUI3-DX/Sdk/Speckle.Connectors.Utils/Builders/TraversalExtensions.cs b/DUI3-DX/Sdk/Speckle.Connectors.Utils/Builders/TraversalExtensions.cs new file mode 100644 index 0000000000..56df5701f7 --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Connectors.Utils/Builders/TraversalExtensions.cs @@ -0,0 +1,26 @@ +using Speckle.Core.Models; +using Speckle.Core.Models.GraphTraversal; + +namespace Speckle.Connectors.Utils.Builders; + +public static class TraversalExtensions +{ + public static IEnumerable TraverseWithProgress( + this GraphTraversal traversalFunction, + Base rootObject, + Action? onOperationProgressed, + CancellationToken cancellationToken = default + ) + { + var traversalGraph = traversalFunction.Traverse(rootObject).ToArray(); + int count = 0; + foreach (var tc in traversalGraph) + { + cancellationToken.ThrowIfCancellationRequested(); + + yield return tc; + + onOperationProgressed?.Invoke("Converting", (double)++count / traversalGraph.Length); + } + } +} diff --git a/DUI3-DX/Sdk/Speckle.Connectors.Utils/Caching/ISendConversionCache.cs b/DUI3-DX/Sdk/Speckle.Connectors.Utils/Caching/ISendConversionCache.cs new file mode 100644 index 0000000000..ce5bfc41a2 --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Connectors.Utils/Caching/ISendConversionCache.cs @@ -0,0 +1,23 @@ +using Speckle.Core.Models; + +namespace Speckle.Connectors.Utils.Caching; + +/// +/// Stores object references resulting from a send operation. These can be retrieved back during a subsequent send operation to bypass conversion if +/// they have not been changed. On large sends with small changes, this makes the process much speedier! +/// Note: do not and should not persist between file opens, should just persist in memory between send operations. Always eagerly invalidate. +/// If you ever implement a different conversion cache, do remember that objects in speckle are namespaced to each project (stream). E.g., if you send A to project C and project D, A needs to exist twice in the db. As such, always namespace stored references by project id. +/// Further note: Caching is optional in the send ops; an instance of this should be injected only in applications where we know we can rely on change tracking! +/// +public interface ISendConversionCache +{ + void StoreSendResult(string projectId, Dictionary convertedReferences); + + /// + /// Call this method whenever you need to invalidate a set of objects that have changed in the host app. + /// Failure to do so correctly will result in cache poisoning and incorrect version creation (stale objects). + /// + /// + public void EvictObjects(IEnumerable objectIds); + bool TryGetValue(string projectId, string applicationId, out ObjectReference objectReference); +} diff --git a/DUI3-DX/Sdk/Speckle.Connectors.Utils/Caching/NullSendConversionCache.cs b/DUI3-DX/Sdk/Speckle.Connectors.Utils/Caching/NullSendConversionCache.cs new file mode 100644 index 0000000000..ec8e85f46c --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Connectors.Utils/Caching/NullSendConversionCache.cs @@ -0,0 +1,19 @@ +using Speckle.Core.Models; + +namespace Speckle.Connectors.Utils.Caching; + +/// +/// A null send conversion cache for future use in connectors that cannot support . It does nothing! +/// +public class NullSendConversionCache : ISendConversionCache +{ + public void StoreSendResult(string projectId, Dictionary convertedReferences) { } + + public void EvictObjects(IEnumerable objectIds) { } + + public bool TryGetValue(string projectId, string applicationId, out ObjectReference objectReference) + { + objectReference = new ObjectReference(); + return false; + } +} diff --git a/DUI3-DX/Sdk/Speckle.Connectors.Utils/Caching/SendConversionCache.cs b/DUI3-DX/Sdk/Speckle.Connectors.Utils/Caching/SendConversionCache.cs new file mode 100644 index 0000000000..94a9883927 --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Connectors.Utils/Caching/SendConversionCache.cs @@ -0,0 +1,28 @@ +using Speckle.Core.Models; + +namespace Speckle.Connectors.Utils.Caching; + +/// +public class SendConversionCache : ISendConversionCache +{ + public SendConversionCache() { } + + private Dictionary<(string applicationId, string projectId), ObjectReference> Cache { get; set; } = new(); // NOTE: as this dude's accessed from potentially more operations at the same time, it might be safer to bless him as a concurrent dictionary. + + public void StoreSendResult(string projectId, Dictionary convertedReferences) + { + foreach (var kvp in convertedReferences) + { + Cache[(kvp.Key, projectId)] = kvp.Value; + } + } + + /// + public void EvictObjects(IEnumerable objectIds) => + Cache = Cache + .Where(kvp => !objectIds.Contains(kvp.Key.applicationId)) + .ToDictionary(kvp => kvp.Key, kvp => kvp.Value); + + public bool TryGetValue(string projectId, string applicationId, out ObjectReference objectReference) => + Cache.TryGetValue((applicationId, projectId), out objectReference); +} diff --git a/DUI3-DX/Sdk/Speckle.Connectors.Utils/Cancellation/CancellationManager.cs b/DUI3-DX/Sdk/Speckle.Connectors.Utils/Cancellation/CancellationManager.cs new file mode 100644 index 0000000000..0f917fd981 --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Connectors.Utils/Cancellation/CancellationManager.cs @@ -0,0 +1,85 @@ +namespace Speckle.Connectors.Utils.Cancellation; + +/// +/// Util class to manage cancellations. +/// +public class CancellationManager +{ + /// + /// Dictionary to relate with registered id. + /// + private readonly Dictionary _operationsInProgress = new(); + + 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; + } + + /// + /// 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 void CancelAllOperations() + { + foreach (var operation in _operationsInProgress) + { + operation.Value.Cancel(); + operation.Value.Dispose(); + } + _operationsInProgress.Clear(); + } + + /// + /// Initialize a token source for cancellable operation. + /// + /// Id to register token. + /// Initialized cancellation token source. + public CancellationTokenSource InitCancellationTokenSource(string id) + { + if (IsExist(id)) + { + CancelOperation(id); + } + + var cts = new CancellationTokenSource(); + _operationsInProgress[id] = cts; + return cts; + } + + /// + /// Cancel operation. + /// + /// Id to cancel operation. + public void CancelOperation(string id) + { + if (_operationsInProgress.TryGetValue(id, out CancellationTokenSource cts)) + { + cts.Cancel(); + cts.Dispose(); + _operationsInProgress.Remove(id); + } + } + + /// + /// Whether cancellation requested already or not. + /// + /// Id to check cancellation requested already or not. + /// + public bool IsCancellationRequested(string id) + { + return _operationsInProgress[id].IsCancellationRequested; + } +} diff --git a/DUI3-DX/Sdk/Speckle.Connectors.Utils/Cancellation/ICancelable.cs b/DUI3-DX/Sdk/Speckle.Connectors.Utils/Cancellation/ICancelable.cs new file mode 100644 index 0000000000..5a9d9f7048 --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Connectors.Utils/Cancellation/ICancelable.cs @@ -0,0 +1,9 @@ +namespace Speckle.Connectors.Utils.Cancellation; + +/// +/// Provides a mechanism for cancelling operations. +/// +public interface ICancelable +{ + public CancellationManager CancellationManager { get; } +} diff --git a/DUI3-DX/Sdk/Speckle.Connectors.Utils/ContainerRegistration.cs b/DUI3-DX/Sdk/Speckle.Connectors.Utils/ContainerRegistration.cs new file mode 100644 index 0000000000..93a7c326dd --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Connectors.Utils/ContainerRegistration.cs @@ -0,0 +1,28 @@ +using Autofac; +using Microsoft.Extensions.Logging; +using Serilog; +using Speckle.Autofac.DependencyInjection; +using Speckle.Connectors.Utils.Cancellation; +using Speckle.Connectors.Utils.Operations; +using Speckle.Core.Logging; + +namespace Speckle.Connectors.Utils; + +public static class ContainerRegistration +{ + public static void AddConnectorUtils(this SpeckleContainerBuilder builder) + { + // send operation and dependencies + builder.AddSingleton(); + builder.AddScoped(); + + //TODO: Logger will likely be removed from Core, we'll plan to figure out the config later... + var serilogLogger = SpeckleLog.Logger; + + ILoggerFactory loggerFactory = new LoggerFactory().AddSerilog(serilogLogger); + builder.ContainerBuilder.Register(_ => loggerFactory).As().SingleInstance().AutoActivate(); + + builder.ContainerBuilder.RegisterGeneric(typeof(Logger<>)).As(typeof(ILogger<>)).SingleInstance(); + builder.AddSingleton(loggerFactory); + } +} diff --git a/DUI3-DX/Sdk/Speckle.Connectors.Utils/Conversion/ReportResult.cs b/DUI3-DX/Sdk/Speckle.Connectors.Utils/Conversion/ReportResult.cs new file mode 100644 index 0000000000..2cd6606b3d --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Connectors.Utils/Conversion/ReportResult.cs @@ -0,0 +1,112 @@ +using Speckle.Core.Models; + +namespace Speckle.Connectors.Utils.Conversion; + +public class Report : Base +{ + public required IEnumerable ConversionResults { get; set; } +} + +public enum Status +{ + NONE = 0, // Do not fucking use + SUCCESS = 1, + INFO = 2, // Not in use yet, maybe later as discussed + WARNING = 3, // Not in use yet, maybe later as discussed + ERROR = 4 +} + +public class SendConversionResult : ConversionResult +{ + public SendConversionResult( + Status status, + string sourceId, + string sourceType, + Base? result = null, + Exception? exception = null + ) + { + Status = status; + SourceId = sourceId; + SourceType = sourceType; + ResultId = result?.id; + ResultType = result?.speckle_type; + if (exception is not null) + { + Error = new ErrorWrapper() { Message = exception.Message, StackTrace = exception.StackTrace }; + } + } +} + +public class ReceiveConversionResult : ConversionResult +{ + public ReceiveConversionResult( + Status status, + Base source, + string? resultId = null, + string? resultType = null, + Exception? exception = null + ) + { + Status = status; + SourceId = source.id; + SourceType = source.speckle_type; // Note: we'll parse it nicely in FE + ResultId = resultId; + ResultType = resultType; + if (exception is not null) + { + Error = new ErrorWrapper() { Message = exception.Message, StackTrace = exception.StackTrace }; + } + } +} + +/// +/// Base class for which we inherit send or receive conversion results. Note, the properties Source* and Result* swap meaning if they are a +/// send conversion result or a receive conversion result - but i do not believe this requires fully separate classes, especially +/// for what this is meant to be at its core: a list of green or red checkmarks in the UI. To make DX easier, the two classes above embody +/// this one and provided clean constructors for each case. +/// POC: Inherits from Base so we can attach the conversion report to the root commit object. Can be revisited later (it's not a problem to not inherit from base). +/// +public abstract class ConversionResult : Base +{ + public Status Status { get; init; } + + /// + /// For receive conversion reports, this is the id of the speckle object. For send, it's the host app object id. + /// + public string? SourceId { get; init; } + + /// + /// For receive conversion reports, this is the type of the speckle object. For send, it's the host app object type. + /// + public string? SourceType { get; init; } + + /// + /// For receive conversion reports, this is the id of the host app object. For send, it's the speckle object id. + /// + public string? ResultId { get; init; } + + /// + /// For receive conversion reports, this is the type of the host app object. For send, it's the speckle object type. + /// + public string? ResultType { get; init; } + + /// + /// The exception, if any. + /// + public ErrorWrapper? Error { get; init; } + + // /// + // /// Makes it easy for the FE to discriminate (against report types, not people). + // /// + // public string Type => this.GetType().ToString(); +} + +/// +/// Wraps around exceptions to make them nicely serializable for the ui. +/// +public class ErrorWrapper : Base +{ + public required string Message { get; set; } + public required string StackTrace { get; set; } +}; diff --git a/DUI3-DX/Sdk/Speckle.Connectors.Utils/Instances/IInstanceObjectsManager.cs b/DUI3-DX/Sdk/Speckle.Connectors.Utils/Instances/IInstanceObjectsManager.cs new file mode 100644 index 0000000000..532e42ed0e --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Connectors.Utils/Instances/IInstanceObjectsManager.cs @@ -0,0 +1,53 @@ +using Speckle.Connectors.Utils.Conversion; +using Speckle.Core.Models.Instances; + +namespace Speckle.Connectors.Utils.Instances; + +/// +/// A utility class that helps manage host application blocks in send/receive operations. This expects to be a scoped dependendency per send/receive operation. +/// POC: could be split into two - instance unpacker and instance baker. +/// +/// Host application object type, e.g. RhinoObject +/// The type of the applicationIdMap values. +public interface IInstanceObjectsManager +{ + /// + /// Given a list of host application objects, it will unpack them into atomic objects, instance proxies and instance proxy definitions. + /// + /// Raw selection from the host application. + UnpackResult UnpackSelection(IEnumerable objects); + + /// + /// Will bake a set of instance components (instances and instance definitions) in the host app. + /// + /// + /// + /// + /// + /// + BakeResult BakeInstances( + List<(string[] layerPath, IInstanceComponent obj)> instanceComponents, + Dictionary applicationIdMap, + string baseLayerName, + Action? onOperationProgressed + ); + + /// + /// Cleans up previously baked instances and associated definitions containing the `namePrefix` in their name. + /// Note: this is based on the convention that all defintions have their name set to a model based prefix. + /// + /// The name prefix to search and delete by. + void PurgeInstances(string namePrefix); +} + +public record UnpackResult( + List AtomicObjects, + Dictionary InstanceProxies, + List InstanceDefinitionProxies +); + +public record BakeResult( + List CreatedInstanceIds, + List ConsumedObjectIds, + List InstanceConversionResults +); diff --git a/DUI3-DX/Sdk/Speckle.Connectors.Utils/NotNullExtensions.cs b/DUI3-DX/Sdk/Speckle.Connectors.Utils/NotNullExtensions.cs new file mode 100644 index 0000000000..59d9f2b3c0 --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Connectors.Utils/NotNullExtensions.cs @@ -0,0 +1,63 @@ +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; + +namespace Speckle.Connectors.Utils; + +public static class NotNullExtensions +{ + /// + public static async Task NotNull( + this Task task, + [CallerArgumentExpression(nameof(task))] string? message = null + ) + where T : class + { + var x = await task.ConfigureAwait(false); + if (x is null) + { + throw new ArgumentNullException(message ?? "Value is null"); + } + return x; + } + + /// + public static async Task NotNull( + this Task task, + [CallerArgumentExpression(nameof(task))] string? message = null + ) + where T : struct + { + var x = await task.ConfigureAwait(false); + if (x is null) + { + throw new ArgumentNullException(message ?? "Value is null"); + } + return x.Value; + } + + /// the object to check for null + /// see + /// type + /// A non null value + /// was null + public static T NotNull([NotNull] this T? obj, [CallerArgumentExpression(nameof(obj))] string? paramName = null) + where T : class + { + if (obj is null) + { + throw new ArgumentNullException(paramName ?? "Value is null"); + } + return obj; + } + + /// + public static T NotNull([NotNull] this T? obj, [CallerArgumentExpression(nameof(obj))] string? paramName = null) + where T : struct + { + if (obj is null) + { + throw new ArgumentNullException(paramName ?? "Value is null"); + } + return obj.Value; + } +} diff --git a/DUI3-DX/Sdk/Speckle.Connectors.Utils/Operations/IRootObjectSender.cs b/DUI3-DX/Sdk/Speckle.Connectors.Utils/Operations/IRootObjectSender.cs new file mode 100644 index 0000000000..1fed24ea95 --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Connectors.Utils/Operations/IRootObjectSender.cs @@ -0,0 +1,18 @@ +using Speckle.Core.Models; + +namespace Speckle.Connectors.Utils.Operations; + +/// +/// 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 interface IRootObjectSender +{ + public Task<(string rootObjId, Dictionary convertedReferences)> Send( + Base commitObject, + SendInfo sendInfo, + Action? onOperationProgressed = null, + CancellationToken ct = default + ); +} diff --git a/DUI3-DX/Sdk/Speckle.Connectors.Utils/Operations/ISyncToThread.cs b/DUI3-DX/Sdk/Speckle.Connectors.Utils/Operations/ISyncToThread.cs new file mode 100644 index 0000000000..7e1950a300 --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Connectors.Utils/Operations/ISyncToThread.cs @@ -0,0 +1,6 @@ +namespace Speckle.Connectors.Utils.Operations; + +public interface ISyncToThread +{ + public Task RunOnThread(Func func); +} diff --git a/DUI3-DX/Sdk/Speckle.Connectors.Utils/Operations/ReceiveOperation.cs b/DUI3-DX/Sdk/Speckle.Connectors.Utils/Operations/ReceiveOperation.cs new file mode 100644 index 0000000000..a93a2c61f7 --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Connectors.Utils/Operations/ReceiveOperation.cs @@ -0,0 +1,52 @@ +using Speckle.Connectors.Utils.Builders; +using Speckle.Core.Api; +using Speckle.Core.Credentials; +using Speckle.Core.Models; +using Speckle.Core.Transports; + +namespace Speckle.Connectors.Utils.Operations; + +public sealed class ReceiveOperation +{ + private readonly IHostObjectBuilder _hostObjectBuilder; + private readonly ISyncToThread _syncToThread; + + public ReceiveOperation(IHostObjectBuilder hostObjectBuilder, ISyncToThread syncToThread) + { + _hostObjectBuilder = hostObjectBuilder; + _syncToThread = syncToThread; + } + + public async Task Execute( + string accountId, // POC: all these string arguments exists in ModelCard but not sure to pass this dependency here, TBD! + string projectId, + string projectName, + string modelName, + string versionId, + CancellationToken cancellationToken, + Action? onOperationProgressed = null + ) + { + // 2 - Check account exist + Account account = AccountManager.GetAccount(accountId); + + // 3 - Get commit object from server + using Client apiClient = new(account); + Commit version = await apiClient.CommitGet(projectId, versionId, cancellationToken).ConfigureAwait(false); + + using ServerTransport transport = new(account, projectId); + Base commitObject = await Speckle.Core.Api.Operations + .Receive(version.referencedObject, transport, cancellationToken: cancellationToken) + .ConfigureAwait(false); + + cancellationToken.ThrowIfCancellationRequested(); + + // 4 - Convert objects + return await _syncToThread + .RunOnThread(() => + { + return _hostObjectBuilder.Build(commitObject, projectName, modelName, onOperationProgressed, cancellationToken); + }) + .ConfigureAwait(false); + } +} diff --git a/DUI3-DX/Sdk/Speckle.Connectors.Utils/Operations/RootObjectSender.cs b/DUI3-DX/Sdk/Speckle.Connectors.Utils/Operations/RootObjectSender.cs new file mode 100644 index 0000000000..0234361f66 --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Connectors.Utils/Operations/RootObjectSender.cs @@ -0,0 +1,66 @@ +using Speckle.Connectors.Utils.Caching; +using Speckle.Core.Api; +using Speckle.Core.Credentials; +using Speckle.Core.Models; +using Speckle.Core.Transports; + +namespace Speckle.Connectors.Utils.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? +public sealed class RootObjectSender : IRootObjectSender +{ + // POC: Revisit this factory pattern, I think we could solve this higher up by injecting a scoped factory for `SendOperation` in the SendBinding + private readonly ServerTransport.Factory _transportFactory; + private readonly ISendConversionCache _sendConversionCache; + + public RootObjectSender(ServerTransport.Factory transportFactory, ISendConversionCache sendConversionCache) + { + _transportFactory = transportFactory; + _sendConversionCache = sendConversionCache; + } + + public async Task<(string rootObjId, Dictionary convertedReferences)> Send( + Base commitObject, + SendInfo sendInfo, + Action? onOperationProgressed = null, + CancellationToken ct = default + ) + { + ct.ThrowIfCancellationRequested(); + + onOperationProgressed?.Invoke("Uploading...", null); + + Account account = AccountManager.GetAccount(sendInfo.AccountId); + + ITransport transport = _transportFactory(account, sendInfo.ProjectId, 60, null); + var sendResult = await SendHelper.Send(commitObject, transport, true, null, ct).ConfigureAwait(false); + + _sendConversionCache.StoreSendResult(sendInfo.ProjectId, sendResult.convertedReferences); + + ct.ThrowIfCancellationRequested(); + + onOperationProgressed?.Invoke("Linking version to model...", null); + + // 8 - Create the version (commit) + using var apiClient = new Client(account); + _ = await apiClient + .CommitCreate( + new CommitCreateInput + { + streamId = sendInfo.ProjectId, + branchName = sendInfo.ModelId, + sourceApplication = sendInfo.SourceApplication, + objectId = sendResult.rootObjId + }, + ct + ) + .ConfigureAwait(true); + + return sendResult; + } +} diff --git a/DUI3-DX/Sdk/Speckle.Connectors.Utils/Operations/Send.cs b/DUI3-DX/Sdk/Speckle.Connectors.Utils/Operations/Send.cs new file mode 100644 index 0000000000..457c3126ba --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Connectors.Utils/Operations/Send.cs @@ -0,0 +1,198 @@ +using System.Collections.Concurrent; +using System.Diagnostics; +using Serilog.Context; +using Speckle.Core.Logging; +using Speckle.Core.Models; +using Speckle.Core.Serialisation; +using Speckle.Core.Transports; +using Speckle.Newtonsoft.Json.Linq; + +namespace Speckle.Connectors.Utils.Operations; + +/// +/// NOTE: Contains copy pasted code from the OG Send operations in Core (the non-obsolete ones). +/// +public static class SendHelper +{ + /// + /// IMPORTANT: Copy pasted function from Operations.Send in Core, but this time returning the converted references from the serializer. + /// Sends a Speckle Object to the provided and (optionally) the default local cache + /// + /// + /// + /// + /// When , an additional will be included + /// + /// + /// + /// The or was + /// + /// using ServerTransport destination = new(account, streamId); + /// string objectId = await Send(mySpeckleObject, destination, true); + /// + public static async Task<(string rootObjId, Dictionary convertedReferences)> Send( + Base value, + ITransport transport, + bool useDefaultCache, + Action>? onProgressAction = null, + CancellationToken cancellationToken = default + ) + { + if (transport is null) + { + throw new ArgumentNullException(nameof(transport), "Expected a transport to be explicitly specified"); + } + + List transports = new() { transport }; + using SQLiteTransport? localCache = useDefaultCache ? new SQLiteTransport { TransportName = "LC" } : null; + if (localCache is not null) + { + transports.Add(localCache); + } + + return await Send(value, transports, onProgressAction, cancellationToken).ConfigureAwait(false); + } + + /// + /// IMPORTANT: Copy pasted function from Operations.Send in Core, but this time returning the converted references from the serializer. + /// It's marked as private as DUI3 only uses the one above. + /// Note that this should be structured better in the future - this is here to minimise core changes coming from DUI3. + /// + /// + /// + /// + /// + /// + /// + /// + /// + private static async Task<(string rootObjId, Dictionary convertedReferences)> Send( + Base value, + IReadOnlyCollection transports, + Action>? onProgressAction = null, + CancellationToken cancellationToken = default + ) + { + if (value is null) + { + throw new ArgumentNullException(nameof(value)); + } + + if (transports.Count == 0) + { + throw new ArgumentException("Expected at least on transport to be specified", nameof(transports)); + } + + var transportContext = transports.ToDictionary(t => t.TransportName, t => t.TransportContext); + + // make sure all logs in the operation have the proper context + using (LogContext.PushProperty("transportContext", transportContext)) + using (LogContext.PushProperty("correlationId", Guid.NewGuid().ToString())) + { + var sendTimer = Stopwatch.StartNew(); + SpeckleLog.Logger.Information("Starting send operation"); + + var internalProgressAction = GetInternalProgressAction(onProgressAction); + + BaseObjectSerializerV2 serializerV2 = + new(transports, internalProgressAction, trackDetachedChildren: true, cancellationToken); + + foreach (var t in transports) + { + t.OnProgressAction = internalProgressAction; + t.CancellationToken = cancellationToken; + t.BeginWrite(); + } + + (string rootObjId, Dictionary) serializerReturnValue; + try + { + serializerReturnValue = await SerializerSend(value, serializerV2, cancellationToken).ConfigureAwait(false); + } + catch (Exception ex) when (!ex.IsFatal()) + { + SpeckleLog.Logger.Information( + ex, + "Send operation failed after {elapsed} seconds", + sendTimer.Elapsed.TotalSeconds + ); + if (ex is OperationCanceledException or SpeckleException) + { + throw; + } + + throw new SpeckleException("Send operation was unsuccessful", ex); + } + finally + { + foreach (var t in transports) + { + t.EndWrite(); + } + } + + sendTimer.Stop(); + SpeckleLog.Logger + .ForContext("transportElapsedBreakdown", transports.ToDictionary(t => t.TransportName, t => t.Elapsed)) + .ForContext("note", "the elapsed summary doesn't need to add up to the total elapsed... Threading magic...") + .ForContext("serializerElapsed", serializerV2.Elapsed) + .Information( + "Finished sending {objectCount} objects after {elapsed}, result {objectId}", + transports.Max(t => t.SavedObjectCount), + sendTimer.Elapsed.TotalSeconds, + serializerReturnValue.rootObjId + ); + return serializerReturnValue; + } + } + + internal static async Task<( + string rootObjectId, + Dictionary convertedReferences + )> SerializerSend(Base value, BaseObjectSerializerV2 serializer, CancellationToken cancellationToken = default) + { + string obj = serializer.Serialize(value); + Task[] transportAwaits = serializer.WriteTransports.Select(t => t.WriteComplete()).ToArray(); + + cancellationToken.ThrowIfCancellationRequested(); + + await Task.WhenAll(transportAwaits).ConfigureAwait(false); + + var parsed = JObject.Parse(obj); + JToken? idToken = parsed.GetValue("id", StringComparison.Ordinal); + + if (idToken == null) + { + throw new SpeckleException("Failed to get id of serialized object"); + } + + return (idToken.ToString(), serializer.ObjectReferences); + } + + /// + /// Factory for progress actions used internally inside send and receive methods. + /// + /// + /// + private static Action? GetInternalProgressAction( + Action>? onProgressAction + ) + { + if (onProgressAction is null) + { + return null; + } + + var localProgressDict = new ConcurrentDictionary(); + + return (name, processed) => + { + if (!localProgressDict.TryAdd(name, processed)) + { + localProgressDict[name] += processed; + } + + onProgressAction.Invoke(localProgressDict); + }; + } +} diff --git a/DUI3-DX/Sdk/Speckle.Connectors.Utils/Operations/SendInfo.cs b/DUI3-DX/Sdk/Speckle.Connectors.Utils/Operations/SendInfo.cs new file mode 100644 index 0000000000..0b45d1161f --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Connectors.Utils/Operations/SendInfo.cs @@ -0,0 +1,17 @@ +namespace Speckle.Connectors.Utils.Operations; + +public readonly struct SendInfo +{ + public SendInfo(string accountId, string projectId, string modelId, string sourceApplication) + { + AccountId = accountId; + ProjectId = projectId; + ModelId = modelId; + SourceApplication = sourceApplication; + } + + public string AccountId { get; } + public string ProjectId { get; } + public string ModelId { get; } + public string SourceApplication { get; } +} diff --git a/DUI3-DX/Sdk/Speckle.Connectors.Utils/Operations/SendOperation.cs b/DUI3-DX/Sdk/Speckle.Connectors.Utils/Operations/SendOperation.cs new file mode 100644 index 0000000000..c7ae1f458e --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Connectors.Utils/Operations/SendOperation.cs @@ -0,0 +1,52 @@ +using Speckle.Connectors.Utils.Builders; +using Speckle.Connectors.Utils.Conversion; +using Speckle.Core.Models; + +namespace Speckle.Connectors.Utils.Operations; + +public sealed class SendOperation +{ + private readonly IRootObjectBuilder _rootObjectBuilder; + private readonly IRootObjectSender _baseObjectSender; + private readonly ISyncToThread _syncToThread; + + public SendOperation( + IRootObjectBuilder rootObjectBuilder, + IRootObjectSender baseObjectSender, + ISyncToThread syncToThread + ) + { + _rootObjectBuilder = rootObjectBuilder; + _baseObjectSender = baseObjectSender; + _syncToThread = syncToThread; + } + + public async Task Execute( + IReadOnlyList objects, + SendInfo sendInfo, + Action? onOperationProgressed = null, + CancellationToken ct = default + ) + { + var buildResult = await _syncToThread + .RunOnThread(() => _rootObjectBuilder.Build(objects, sendInfo, onOperationProgressed, ct)) + .ConfigureAwait(false); + + // POC: Jonathon asks on behalf of willow twin - let's explore how this can work + buildResult.RootObject["@report"] = new Report { ConversionResults = buildResult.ConversionResults }; + + // 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); + + return new(rootObjId, convertedReferences, buildResult.ConversionResults); + } +} + +public record SendOperationResult( + string RootObjId, + IReadOnlyDictionary ConvertedReferences, + IEnumerable ConversionResults +); diff --git a/DUI3-DX/Sdk/Speckle.Connectors.Utils/Reflection/AssemblyExtensions.cs b/DUI3-DX/Sdk/Speckle.Connectors.Utils/Reflection/AssemblyExtensions.cs new file mode 100644 index 0000000000..5e48c146a6 --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Connectors.Utils/Reflection/AssemblyExtensions.cs @@ -0,0 +1,20 @@ +using System.Reflection; + +namespace Speckle.Connectors.Utils.Reflection; + +public static class AssemblyExtensions +{ + public static string GetVersion(this Assembly assembly) + { + // this is adapted from Serilog extension method, but we might find the fallback is enough: assembly.GetName()?.Version?.ToString(); + var attribute = assembly.GetCustomAttributes().OfType().FirstOrDefault(); + if (attribute != null) + { + return attribute.InformationalVersion; + } + + // otherwise use assembly version + // POC: missing version? + return assembly.GetName()?.Version?.ToString() ?? "Missing Version"; + } +} diff --git a/DUI3-DX/Sdk/Speckle.Connectors.Utils/Speckle.Connectors.Utils.csproj b/DUI3-DX/Sdk/Speckle.Connectors.Utils/Speckle.Connectors.Utils.csproj new file mode 100644 index 0000000000..125a1a6f7a --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Connectors.Utils/Speckle.Connectors.Utils.csproj @@ -0,0 +1,14 @@ + + + + netstandard2.0 + + + + + + + + + + diff --git a/DUI3-DX/Sdk/Speckle.Connectors.Utils/packages.lock.json b/DUI3-DX/Sdk/Speckle.Connectors.Utils/packages.lock.json new file mode 100644 index 0000000000..990763d412 --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Connectors.Utils/packages.lock.json @@ -0,0 +1,551 @@ +{ + "version": 2, + "dependencies": { + ".NETStandard,Version=v2.0": { + "Autofac": { + "type": "Direct", + "requested": "[5.2.0, )", + "resolved": "5.2.0", + "contentHash": "V8dBH0dsv75uDzl7Sw+HkhKDPUw2eXnlMjcSVMH+tLo2s67MpTKGyDj1pDcpR+IF2u4YRs0s3/x7R88YJzIWvg==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "1.1.0" + } + }, + "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" + } + }, + "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, )", + "resolved": "1.14.1", + "contentHash": "mOOmFYwad3MIOL14VCjj02LljyF1GNw1wP0YVlxtcPvqdxjGGMNdNJJxHptlry3MOd8b40Flm8RPOM8JOlN2sQ==" + }, + "Serilog.Extensions.Logging": { + "type": "Direct", + "requested": "[7.0.0, )", + "resolved": "7.0.0", + "contentHash": "9faU0zNQqU7I6soVhLUMYaGNpgWv6cKlKb2S5AnS8gXxzW/em5Ladm/6FMrWTnX41cdbdGPOWNAo6adi4WaJ6A==", + "dependencies": { + "Microsoft.Extensions.Logging": "7.0.0", + "Serilog": "2.12.0" + } + }, + "Speckle.Core": { + "type": "Direct", + "requested": "[3.0.1-alpha.14, )", + "resolved": "3.0.1-alpha.14", + "contentHash": "RzQPVIGFFkKvG56YLr8ACtiwdWJE6IJ9vCQ4qHa0PIsUEpfzAIAi59jnzqtByOFC0FiFrFPow9bkfzylaZorAA==", + "dependencies": { + "GraphQL.Client": "6.0.0", + "Microsoft.CSharp": "4.7.0", + "Microsoft.Data.Sqlite": "7.0.5", + "Polly": "7.2.3", + "Polly.Contrib.WaitAndRetry": "1.1.1", + "Polly.Extensions.Http": "3.0.0", + "Sentry": "3.33.0", + "Sentry.Serilog": "3.33.0", + "Serilog": "2.12.0", + "Serilog.Enrichers.ClientInfo": "1.3.0", + "Serilog.Exceptions": "8.4.0", + "Serilog.Sinks.Console": "4.1.0", + "Serilog.Sinks.Seq": "5.2.2", + "SerilogTimings": "3.0.1", + "Speckle.Newtonsoft.Json": "13.0.2", + "System.DoubleNumerics": "3.1.3" + } + }, + "Speckle.InterfaceGenerator": { + "type": "Direct", + "requested": "[0.9.5, )", + "resolved": "0.9.5", + "contentHash": "oU/L7pN1R7q8KkbrpQ3WJnHirPHqn+9DEA7asOcUiggV5dzVg1A/VYs7GOSusD24njxXh03tE3a2oTLOjt3cVg==" + }, + "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.AspNetCore.Http": { + "type": "Transitive", + "resolved": "2.1.1", + "contentHash": "pPDcCW8spnyibK3krpxrOpaFHf5fjV6k1Hsl6gfh77N/8gRYlLU7MOQDUnjpEwdlHmtxwJKQJNxZqVQOmJGRUw==", + "dependencies": { + "Microsoft.AspNetCore.Http.Abstractions": "2.1.1", + "Microsoft.AspNetCore.WebUtilities": "2.1.1", + "Microsoft.Extensions.ObjectPool": "2.1.1", + "Microsoft.Extensions.Options": "2.1.1", + "Microsoft.Net.Http.Headers": "2.1.1" + } + }, + "Microsoft.AspNetCore.Http.Abstractions": { + "type": "Transitive", + "resolved": "2.1.1", + "contentHash": "kQUEVOU4loc8CPSb2WoHFTESqwIa8Ik7ysCBfTwzHAd0moWovc9JQLmhDIHlYLjHbyexqZAlkq/FPRUZqokebw==", + "dependencies": { + "Microsoft.AspNetCore.Http.Features": "2.1.1", + "System.Text.Encodings.Web": "4.5.0" + } + }, + "Microsoft.AspNetCore.Http.Features": { + "type": "Transitive", + "resolved": "2.1.1", + "contentHash": "VklZ7hWgSvHBcDtwYYkdMdI/adlf7ebxTZ9kdzAhX+gUs5jSHE9mZlTamdgf9miSsxc1QjNazHXTDJdVPZKKTw==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.1.1" + } + }, + "Microsoft.AspNetCore.WebUtilities": { + "type": "Transitive", + "resolved": "2.1.1", + "contentHash": "PGKIZt4+412Z/XPoSjvYu/QIbTxcAQuEFNoA1Pw8a9mgmO0ZhNBmfaNyhgXFf7Rq62kP0tT/2WXpxdcQhkFUPA==", + "dependencies": { + "Microsoft.Net.Http.Headers": "2.1.1", + "System.Text.Encodings.Web": "4.5.0" + } + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "3aeMZ1N0lJoSyzqiP03hqemtb1BijhsJADdobn/4nsMJ8V1H+CrpuduUe4hlRdx+ikBQju1VGjMD1GJ3Sk05Eg==", + "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.DependencyInjection": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "elNeOmkeX3eDVG6pYVeV82p29hr+UKDaBhrZyWvWLw/EVZSYEkZlQdkp0V39k/Xehs2Qa0mvoCvkVj3eQxNQ1Q==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "7.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0", + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "h3j/QfmFN4S0w4C2A6X7arXij/M/OVw3uQHSOFxnND4DyAzO1F9eMX7Eti7lU/OkSthEE0WzRsfT/Dmx86jzCw==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "7.0.0", + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "Microsoft.Extensions.Logging": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "Nw2muoNrOG5U5qa2ZekXwudUn2BJcD41e65zwmDHb1fQegTX66UokLWZkJRpqSSHXDOWZ5V0iqhbxOEky91atA==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "7.0.0", + "Microsoft.Extensions.DependencyInjection": "7.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0", + "Microsoft.Extensions.Logging.Abstractions": "7.0.0", + "Microsoft.Extensions.Options": "7.0.0", + "System.Diagnostics.DiagnosticSource": "7.0.0" + } + }, + "Microsoft.Extensions.ObjectPool": { + "type": "Transitive", + "resolved": "2.1.1", + "contentHash": "SErON45qh4ogDp6lr6UvVmFYW0FERihW+IQ+2JyFv1PUyWktcJytFaWH5zarufJvZwhci7Rf1IyGXr9pVEadTw==" + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "lP1yBnTTU42cKpMozuafbvNtQ7QcBjr/CcK3bYOGEMH55Fjt+iecXjT6chR7vbgCMqy3PG3aNQSZgo/EuY/9qQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0", + "Microsoft.Extensions.Primitives": "7.0.0", + "System.ComponentModel.Annotations": "5.0.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "um1KU5kxcRp3CNuI8o/GrZtD4AIOXDk+RLsytjZ9QPok3ttLUelLKpilVPuaFT3TFjOhSibUAso0odbOaCDj3Q==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "Microsoft.Net.Http.Headers": { + "type": "Transitive", + "resolved": "2.1.1", + "contentHash": "lPNIphl8b2EuhOE9dMH6EZDmu7pS882O+HMi5BJNsigxHaWlBrYxZHFZgE18cyaPp6SSZcTkKkuzfjV/RRQKlA==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.1.1", + "System.Buffers": "4.5.0" + } + }, + "Microsoft.NETCore.Platforms": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==" + }, + "Microsoft.NETCore.Targets": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "Polly": { + "type": "Transitive", + "resolved": "7.2.3", + "contentHash": "DeCY0OFbNdNxsjntr1gTXHJ5pKUwYzp04Er2LLeN3g6pWhffsGuKVfMBLe1lw7x76HrPkLxKEFxBlpRxS2nDEQ==" + }, + "Polly.Contrib.WaitAndRetry": { + "type": "Transitive", + "resolved": "1.1.1", + "contentHash": "1MUQLiSo4KDkQe6nzQRhIU05lm9jlexX5BVsbuw0SL82ynZ+GzAHQxJVDPVBboxV37Po3SG077aX8DuSy8TkaA==" + }, + "Polly.Extensions.Http": { + "type": "Transitive", + "resolved": "3.0.0", + "contentHash": "drrG+hB3pYFY7w1c3BD+lSGYvH2oIclH8GRSehgfyP5kjnFnHKQuuBhuHLv+PWyFuaTDyk/vfRpnxOzd11+J8g==", + "dependencies": { + "Polly": "7.1.0" + } + }, + "Sentry": { + "type": "Transitive", + "resolved": "3.33.0", + "contentHash": "8vbD2o6IR2wrRrkSiRbnodWGWUOqIlwYtzpjvPNOb5raJdOf+zxMwfS8f6nx9bmrTTfDj7KrCB8C/5OuicAc8A==", + "dependencies": { + "System.Reflection.Metadata": "5.0.0", + "System.Text.Json": "5.0.2" + } + }, + "Sentry.Serilog": { + "type": "Transitive", + "resolved": "3.33.0", + "contentHash": "V8BU7QGWg2qLYfNPqtuTBhC1opysny5l+Ifp6J6PhOeAxU0FssR7nYfbJVetrnLIoh2rd3DlJ6hHYYQosQYcUQ==", + "dependencies": { + "Sentry": "3.33.0", + "Serilog": "2.7.1" + } + }, + "Serilog": { + "type": "Transitive", + "resolved": "2.12.0", + "contentHash": "xaiJLIdu6rYMKfQMYUZgTy8YK7SMZjB4Yk50C/u//Z4OsvxkUfSPJy4nknfvwAC34yr13q7kcyh4grbwhSxyZg==" + }, + "Serilog.Enrichers.ClientInfo": { + "type": "Transitive", + "resolved": "1.3.0", + "contentHash": "mTc7PM+wC9Hr7LWSwqt5mmnlAr7RJs+eTb3PGPRhwdOackk95MkhUZognuxXEdlW19HAFNmEBTSBY5DfLwM8jQ==", + "dependencies": { + "Microsoft.AspNetCore.Http": "2.1.1", + "Serilog": "2.7.1" + } + }, + "Serilog.Exceptions": { + "type": "Transitive", + "resolved": "8.4.0", + "contentHash": "nc/+hUw3lsdo0zCj0KMIybAu7perMx79vu72w0za9Nsi6mWyNkGXxYxakAjWB7nEmYL6zdmhEQRB4oJ2ALUeug==", + "dependencies": { + "Serilog": "2.8.0", + "System.Reflection.TypeExtensions": "4.7.0" + } + }, + "Serilog.Formatting.Compact": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "pNroKVjo+rDqlxNG5PXkRLpfSCuDOBY0ri6jp9PLe505ljqwhwZz8ospy2vWhQlFu5GkIesh3FcDs4n7sWZODA==", + "dependencies": { + "Serilog": "2.8.0" + } + }, + "Serilog.Sinks.Console": { + "type": "Transitive", + "resolved": "4.1.0", + "contentHash": "K6N5q+5fetjnJPvCmkWOpJ/V8IEIoMIB1s86OzBrbxwTyHxdx3pmz4H+8+O/Dc/ftUX12DM1aynx/dDowkwzqg==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "Serilog.Sinks.File": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "uwV5hdhWPwUH1szhO8PJpFiahqXmzPzJT/sOijH/kFgUx+cyoDTMM8MHD0adw9+Iem6itoibbUXHYslzXsLEAg==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "Serilog.Sinks.PeriodicBatching": { + "type": "Transitive", + "resolved": "3.1.0", + "contentHash": "NDWR7m3PalVlGEq3rzoktrXikjFMLmpwF0HI4sowo8YDdU+gqPlTHlDQiOGxHfB0sTfjPA9JjA7ctKG9zqjGkw==", + "dependencies": { + "Serilog": "2.0.0" + } + }, + "Serilog.Sinks.Seq": { + "type": "Transitive", + "resolved": "5.2.2", + "contentHash": "1Csmo5ua7NKUe0yXUx+zsRefjAniPWcXFhUXxXG8pwo0iMiw2gjn9SOkgYnnxbgWqmlGv236w0N/dHc2v5XwMg==", + "dependencies": { + "Serilog": "2.12.0", + "Serilog.Formatting.Compact": "1.1.0", + "Serilog.Sinks.File": "5.0.0", + "Serilog.Sinks.PeriodicBatching": "3.1.0" + } + }, + "SerilogTimings": { + "type": "Transitive", + "resolved": "3.0.1", + "contentHash": "Zs28eTgszAMwpIrbBnWHBI50yuxL50p/dmAUWmy75+axdZYK/Sjm5/5m1N/CisR8acJUhTVcjPZrsB1P5iv0Uw==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "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.Buffers": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, + "System.Collections.Immutable": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "FXkLXiK0sVVewcso0imKQoOxjoPAj42R8HtjjbSjVPAzwDfzoyoznWxgA3c38LDbN9SJux1xXoXYAhz98j7r2g==", + "dependencies": { + "System.Memory": "4.5.4" + } + }, + "System.ComponentModel.Annotations": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dMkqfy2el8A8/I76n2Hi1oBFEbG1SfxD2l5nhwXV3XjlnOmwxJlQbYpJH4W51odnU9sARCSAgv7S3CyAFMkpYg==" + }, + "System.Diagnostics.DiagnosticSource": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "9W0ewWDuAyDqS2PigdTxk6jDKonfgscY/hP8hm7VpxYhNHZHKvZTdRckberlFk3VnCmr3xBUyMBut12Q+T2aOw==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "System.DoubleNumerics": { + "type": "Transitive", + "resolved": "3.1.3", + "contentHash": "KRKEM/L3KBodjA9VOg3EifFVWUY6EOqaMB05UvPEDm7Zeby/kZW+4kdWUEPzW6xtkwf46p661L9NrbeeQhtLzw==", + "dependencies": { + "NETStandard.Library": "1.6.1" + } + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Numerics.Vectors": "4.4.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + }, + "System.Numerics.Vectors": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" + }, + "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.Reflection.Metadata": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "5NecZgXktdGg34rh1OenY1rFNDCI8xSjFr+Z4OU4cU06AQHUdRnIIEeWENu3Wl4YowbzkymAIMvi3WyK9U53pQ==", + "dependencies": { + "System.Collections.Immutable": "5.0.0" + } + }, + "System.Reflection.TypeExtensions": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "VybpaOQQhqE6siHppMktjfGBw1GCwvCqiufqmP8F1nj7fTUNtW35LOEt3UZTEsECfo+ELAl/9o9nJx3U91i7vA==" + }, + "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" + } + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + }, + "System.Runtime.InteropServices.WindowsRuntime": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "J4GUi3xZQLUBasNwZnjrffN8i5wpHrBtZoLG+OhRyGo/+YunMRWWtwoMDlUAIdmX0uRfpHIBDSV6zyr3yf00TA==", + "dependencies": { + "System.Runtime": "4.3.0" + } + }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "5.0.1", + "contentHash": "KmJ+CJXizDofbq6mpqDoRRLcxgOd2z9X3XoFNULSbvbqVRZkFX3istvr+MUjL6Zw1RT+RNdoI4GYidIINtgvqQ==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Memory": "4.5.4" + } + }, + "System.Text.Json": { + "type": "Transitive", + "resolved": "5.0.2", + "contentHash": "I47dVIGiV6SfAyppphxqupertT/5oZkYLDCX6vC3HpOI4ZLjyoKAreUoem2ie6G0RbRuFrlqz/PcTQjfb2DOfQ==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "5.0.0", + "System.Buffers": "4.5.1", + "System.Memory": "4.5.4", + "System.Numerics.Vectors": "4.5.0", + "System.Runtime.CompilerServices.Unsafe": "5.0.0", + "System.Text.Encodings.Web": "5.0.1", + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "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.autofac": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "[7.0.0, )" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "CentralTransitive", + "requested": "[7.0.0, )", + "resolved": "7.0.0", + "contentHash": "kmn78+LPVMOWeITUjIlfxUPDsI0R6G0RkeAMBmQxAJ7vBJn4q2dTva7pWi65ceN5vPGjJ9q/Uae2WKgvfktJAw==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Memory": "4.5.5" + } + } + } + } +} \ No newline at end of file diff --git a/DUI3-DX/Sdk/Speckle.Converters.Common.DependencyInjection/ContainerRegistration.cs b/DUI3-DX/Sdk/Speckle.Converters.Common.DependencyInjection/ContainerRegistration.cs new file mode 100644 index 0000000000..84ab8defe1 --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Converters.Common.DependencyInjection/ContainerRegistration.cs @@ -0,0 +1,40 @@ +using Speckle.Autofac.DependencyInjection; +using Speckle.Converters.Common.DependencyInjection.ToHost; +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.Common.DependencyInjection; + +public static class ContainerRegistration +{ + public static void AddRootCommon(this SpeckleContainerBuilder builder) + where TRootToSpeckleConverter : class, IRootToSpeckleConverter + { + builder.AddScoped(); + /* + POC: CNX-9267 Moved the Injection of converters into the converter module. Not sure if this is 100% right, as this doesn't just register the conversions within this converter, but any conversions found in any Speckle.*.dll file. + This will require consolidating across other connectors. + */ + builder.AddScoped, Factory>(); + builder.AddScoped< + IConverterResolver, + ConverterResolver + >(); + + builder.AddScoped, Factory>(); + builder.AddScoped, ConverterResolver>(); + + builder.AddScoped(); + + builder.InjectNamedTypes(); + builder.InjectNamedTypes(); + } + + public static void AddApplicationConverters( + this SpeckleContainerBuilder builder + ) + where THostToSpeckleUnitConverter : class, IHostToSpeckleUnitConverter + { + builder.AddScoped, THostToSpeckleUnitConverter>(); + builder.RegisterRawConversions(); + } +} diff --git a/DUI3-DX/Sdk/Speckle.Converters.Common.DependencyInjection/NamedTypeInjector.cs b/DUI3-DX/Sdk/Speckle.Converters.Common.DependencyInjection/NamedTypeInjector.cs new file mode 100644 index 0000000000..c4a0800ce7 --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Converters.Common.DependencyInjection/NamedTypeInjector.cs @@ -0,0 +1,79 @@ +using System.Reflection; +using Autofac; +using Speckle.Autofac.DependencyInjection; +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.Common.DependencyInjection; + +public static class ConversionTypesInjector +{ + public static void InjectNamedTypes(this SpeckleContainerBuilder containerBuilder) + where T : notnull + { + var types = containerBuilder.SpeckleTypes.Where(x => x.GetInterfaces().Contains(typeof(T))); + + // we only care about named types + var byName = types + .Where(x => x.GetCustomAttribute() != null) + .Select(x => + { + var nameAndRank = x.GetCustomAttribute(); + + return (name: nameAndRank.Name, rank: nameAndRank.Rank, type: x); + }) + .ToList(); + + // we'll register the types accordingly + var names = byName.Select(x => x.name).Distinct(); + foreach (string name in names) + { + var namedTypes = byName.Where(x => x.name == name).OrderByDescending(y => y.rank).ToList(); + + // first type found + var first = namedTypes[0]; + + // POC: may need to be instance per lifecycle scope + containerBuilder.ContainerBuilder.RegisterType(first.type).Keyed(first.name).InstancePerLifetimeScope(); + + // 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); + // POC: should we explode if no found? + if (secondaryType != null) + { + containerBuilder.ContainerBuilder + .RegisterType(first.type) + .As(secondaryType) + .PropertiesAutowired(PropertyWiringOptions.AllowCircularDependencies) + .InstancePerLifetimeScope(); + } + + // register subsequent types with rank + namedTypes.RemoveAt(0); + foreach (var other in namedTypes) + { + // POC: is this the right scope? + containerBuilder.ContainerBuilder + .RegisterType(other.type) + .Keyed($"{other.name}|{other.rank}") + .InstancePerLifetimeScope(); + + // 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() + // NOT very DRY + secondaryType = first.type.GetInterface(typeof(ITypedConverter<,>).Name); + // POC: should we explode if no found? + if (secondaryType != null) + { + containerBuilder.ContainerBuilder.RegisterType(first.type).As(secondaryType).InstancePerLifetimeScope(); + } + } + } + } +} diff --git a/DUI3-DX/Sdk/Speckle.Converters.Common.DependencyInjection/RawConversionRegisterer.cs b/DUI3-DX/Sdk/Speckle.Converters.Common.DependencyInjection/RawConversionRegisterer.cs new file mode 100644 index 0000000000..73745af1f7 --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Converters.Common.DependencyInjection/RawConversionRegisterer.cs @@ -0,0 +1,39 @@ +using Autofac; +using Speckle.Autofac.DependencyInjection; +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.Common.DependencyInjection; + +// POC: review and see if it can be made more generic, related to the +// NameAndRankAttribute work that needs doing +public static class RawConversionRegisterer +{ + public static void RegisterRawConversions(this SpeckleContainerBuilder containerBuilder) + { + // POC: hard-coding speckle... :/ + foreach (Type speckleType in containerBuilder.SpeckleTypes) + { + RegisterRawConversionsForType(containerBuilder.ContainerBuilder, speckleType); + } + } + + private static void RegisterRawConversionsForType(ContainerBuilder containerBuilder, Type type) + { + if (!type.IsClass || type.IsAbstract) + { + return; + } + + var rawConversionInterfaces = type.GetInterfaces() + .Where(it => it.IsGenericType && it.GetGenericTypeDefinition() == typeof(ITypedConverter<,>)); + + foreach (var conversionInterface in rawConversionInterfaces) + { + containerBuilder + .RegisterType(type) + .As(conversionInterface) + .PropertiesAutowired(PropertyWiringOptions.AllowCircularDependencies) + .InstancePerLifetimeScope(); + } + } +} diff --git a/DUI3-DX/Sdk/Speckle.Converters.Common.DependencyInjection/Speckle.Converters.Common.DependencyInjection.csproj b/DUI3-DX/Sdk/Speckle.Converters.Common.DependencyInjection/Speckle.Converters.Common.DependencyInjection.csproj new file mode 100644 index 0000000000..b7e5a37e54 --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Converters.Common.DependencyInjection/Speckle.Converters.Common.DependencyInjection.csproj @@ -0,0 +1,16 @@ + + + + netstandard2.0 + + + + + + + + + + + + diff --git a/DUI3-DX/Sdk/Speckle.Converters.Common.DependencyInjection/ToHost/ConverterWithFallback.cs b/DUI3-DX/Sdk/Speckle.Converters.Common.DependencyInjection/ToHost/ConverterWithFallback.cs new file mode 100644 index 0000000000..1d143ef8f9 --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Converters.Common.DependencyInjection/ToHost/ConverterWithFallback.cs @@ -0,0 +1,69 @@ +using System.Collections; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; +using Speckle.Core.Models.Extensions; + +namespace Speckle.Converters.Common.DependencyInjection.ToHost; + +// POC: CNX-9394 Find a better home for this outside `DependencyInjection` project +/// +/// +///
+/// If no suitable converter conversion is found, and the target object has a displayValue property +/// a converter with strong name of is resolved for. +///
+/// +public sealed class ConverterWithFallback : IRootToHostConverter +{ + private readonly ConverterWithoutFallback _baseConverter; + + public ConverterWithFallback(IConverterResolver toHost) + { + _baseConverter = new ConverterWithoutFallback(toHost); + } + + /// + /// Converts a instance to a host object. + /// + /// The instance to convert. + /// The converted host object. + /// Fallbacks to display value if a direct conversion is not possible. + /// + /// The conversion is done in the following order of preference: + /// 1. Direct conversion using the . + /// 2. Fallback to display value using the method, if a direct conversion is not possible. + /// + /// If the direct conversion is not available and there is no displayValue, a is thrown. + /// + /// Thrown when no conversion is found for . + public object Convert(Base target) + { + Type type = target.GetType(); + + // Direct conversion if a converter is found + if (_baseConverter.TryGetConverter(type, out IToHostTopLevelConverter? result)) + { + return result.Convert(target); + } + + // Fallback to display value if it exists. + var displayValue = target.TryGetDisplayValue(); + if (displayValue != null) + { + if (displayValue is IList && !displayValue.Any()) + { + throw new NotSupportedException($"No display value found for {type}"); + } + return FallbackToDisplayValue(displayValue); + } + + throw new NotSupportedException($"No conversion found for {type}"); + } + + private object FallbackToDisplayValue(IReadOnlyList displayValue) + { + var tempDisplayableObject = new DisplayableObject(displayValue); + + return _baseConverter.Convert(tempDisplayableObject); + } +} diff --git a/DUI3-DX/Sdk/Speckle.Converters.Common.DependencyInjection/ToHost/ConverterWithoutFallback.cs b/DUI3-DX/Sdk/Speckle.Converters.Common.DependencyInjection/ToHost/ConverterWithoutFallback.cs new file mode 100644 index 0000000000..c2ec1bd9e3 --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Converters.Common.DependencyInjection/ToHost/ConverterWithoutFallback.cs @@ -0,0 +1,46 @@ +using System.Diagnostics.CodeAnalysis; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; + +namespace Speckle.Converters.Common.DependencyInjection.ToHost; + +// POC: CNX-9394 Find a better home for this outside `DependencyInjection` project +/// +/// Provides an implementation for +/// that resolves a via the injected +/// +/// +public sealed class ConverterWithoutFallback : IRootToHostConverter +{ + private readonly IConverterResolver _toHost; + + public ConverterWithoutFallback(IConverterResolver converterResolver) + { + _toHost = converterResolver; + } + + public object Convert(Base target) + { + if (!TryGetConverter(target.GetType(), out IToHostTopLevelConverter? converter)) + { + throw new NotSupportedException($"No conversion found for {target.GetType()}"); + } + + object result = converter.Convert(target); + return result; + } + + internal bool TryGetConverter(Type target, [NotNullWhen(true)] out IToHostTopLevelConverter? result) + { + // Direct conversion if a converter is found + var objectConverter = _toHost.GetConversionForType(target); + if (objectConverter != null) + { + result = objectConverter; + return true; + } + + result = null; + return false; + } +} diff --git a/DUI3-DX/Sdk/Speckle.Converters.Common.DependencyInjection/packages.lock.json b/DUI3-DX/Sdk/Speckle.Converters.Common.DependencyInjection/packages.lock.json new file mode 100644 index 0000000000..cb39bea478 --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Converters.Common.DependencyInjection/packages.lock.json @@ -0,0 +1,515 @@ +{ + "version": 2, + "dependencies": { + ".NETStandard,Version=v2.0": { + "Autofac": { + "type": "Direct", + "requested": "[5.2.0, )", + "resolved": "5.2.0", + "contentHash": "V8dBH0dsv75uDzl7Sw+HkhKDPUw2eXnlMjcSVMH+tLo2s67MpTKGyDj1pDcpR+IF2u4YRs0s3/x7R88YJzIWvg==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "1.1.0" + } + }, + "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" + } + }, + "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, )", + "resolved": "1.14.1", + "contentHash": "mOOmFYwad3MIOL14VCjj02LljyF1GNw1wP0YVlxtcPvqdxjGGMNdNJJxHptlry3MOd8b40Flm8RPOM8JOlN2sQ==" + }, + "Speckle.InterfaceGenerator": { + "type": "Direct", + "requested": "[0.9.5, )", + "resolved": "0.9.5", + "contentHash": "oU/L7pN1R7q8KkbrpQ3WJnHirPHqn+9DEA7asOcUiggV5dzVg1A/VYs7GOSusD24njxXh03tE3a2oTLOjt3cVg==" + }, + "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.AspNetCore.Http": { + "type": "Transitive", + "resolved": "2.1.1", + "contentHash": "pPDcCW8spnyibK3krpxrOpaFHf5fjV6k1Hsl6gfh77N/8gRYlLU7MOQDUnjpEwdlHmtxwJKQJNxZqVQOmJGRUw==", + "dependencies": { + "Microsoft.AspNetCore.Http.Abstractions": "2.1.1", + "Microsoft.AspNetCore.WebUtilities": "2.1.1", + "Microsoft.Extensions.ObjectPool": "2.1.1", + "Microsoft.Extensions.Options": "2.1.1", + "Microsoft.Net.Http.Headers": "2.1.1" + } + }, + "Microsoft.AspNetCore.Http.Abstractions": { + "type": "Transitive", + "resolved": "2.1.1", + "contentHash": "kQUEVOU4loc8CPSb2WoHFTESqwIa8Ik7ysCBfTwzHAd0moWovc9JQLmhDIHlYLjHbyexqZAlkq/FPRUZqokebw==", + "dependencies": { + "Microsoft.AspNetCore.Http.Features": "2.1.1", + "System.Text.Encodings.Web": "4.5.0" + } + }, + "Microsoft.AspNetCore.Http.Features": { + "type": "Transitive", + "resolved": "2.1.1", + "contentHash": "VklZ7hWgSvHBcDtwYYkdMdI/adlf7ebxTZ9kdzAhX+gUs5jSHE9mZlTamdgf9miSsxc1QjNazHXTDJdVPZKKTw==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.1.1" + } + }, + "Microsoft.AspNetCore.WebUtilities": { + "type": "Transitive", + "resolved": "2.1.1", + "contentHash": "PGKIZt4+412Z/XPoSjvYu/QIbTxcAQuEFNoA1Pw8a9mgmO0ZhNBmfaNyhgXFf7Rq62kP0tT/2WXpxdcQhkFUPA==", + "dependencies": { + "Microsoft.Net.Http.Headers": "2.1.1", + "System.Text.Encodings.Web": "4.5.0" + } + }, + "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.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "2.1.1", + "contentHash": "MgYpU5cwZohUMKKg3sbPhvGG+eAZ/59E9UwPwlrUkyXU+PGzqwZg9yyQNjhxuAWmoNoFReoemeCku50prYSGzA==" + }, + "Microsoft.Extensions.ObjectPool": { + "type": "Transitive", + "resolved": "2.1.1", + "contentHash": "SErON45qh4ogDp6lr6UvVmFYW0FERihW+IQ+2JyFv1PUyWktcJytFaWH5zarufJvZwhci7Rf1IyGXr9pVEadTw==" + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "2.1.1", + "contentHash": "V7lXCU78lAbzaulCGFKojcCyG8RTJicEbiBkPJjFqiqXwndEBBIehdXRMWEVU3UtzQ1yDvphiWUL9th6/4gJ7w==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.1.1", + "Microsoft.Extensions.Primitives": "2.1.1" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "2.1.1", + "contentHash": "scJ1GZNIxMmjpENh0UZ8XCQ6vzr/LzeF9WvEA51Ix2OQGAs9WPgPu8ABVUdvpKPLuor/t05gm6menJK3PwqOXg==", + "dependencies": { + "System.Memory": "4.5.1", + "System.Runtime.CompilerServices.Unsafe": "4.5.1" + } + }, + "Microsoft.Net.Http.Headers": { + "type": "Transitive", + "resolved": "2.1.1", + "contentHash": "lPNIphl8b2EuhOE9dMH6EZDmu7pS882O+HMi5BJNsigxHaWlBrYxZHFZgE18cyaPp6SSZcTkKkuzfjV/RRQKlA==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.1.1", + "System.Buffers": "4.5.0" + } + }, + "Microsoft.NETCore.Platforms": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==" + }, + "Microsoft.NETCore.Targets": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "Polly": { + "type": "Transitive", + "resolved": "7.2.3", + "contentHash": "DeCY0OFbNdNxsjntr1gTXHJ5pKUwYzp04Er2LLeN3g6pWhffsGuKVfMBLe1lw7x76HrPkLxKEFxBlpRxS2nDEQ==" + }, + "Polly.Contrib.WaitAndRetry": { + "type": "Transitive", + "resolved": "1.1.1", + "contentHash": "1MUQLiSo4KDkQe6nzQRhIU05lm9jlexX5BVsbuw0SL82ynZ+GzAHQxJVDPVBboxV37Po3SG077aX8DuSy8TkaA==" + }, + "Polly.Extensions.Http": { + "type": "Transitive", + "resolved": "3.0.0", + "contentHash": "drrG+hB3pYFY7w1c3BD+lSGYvH2oIclH8GRSehgfyP5kjnFnHKQuuBhuHLv+PWyFuaTDyk/vfRpnxOzd11+J8g==", + "dependencies": { + "Polly": "7.1.0" + } + }, + "Sentry": { + "type": "Transitive", + "resolved": "3.33.0", + "contentHash": "8vbD2o6IR2wrRrkSiRbnodWGWUOqIlwYtzpjvPNOb5raJdOf+zxMwfS8f6nx9bmrTTfDj7KrCB8C/5OuicAc8A==", + "dependencies": { + "System.Reflection.Metadata": "5.0.0", + "System.Text.Json": "5.0.2" + } + }, + "Sentry.Serilog": { + "type": "Transitive", + "resolved": "3.33.0", + "contentHash": "V8BU7QGWg2qLYfNPqtuTBhC1opysny5l+Ifp6J6PhOeAxU0FssR7nYfbJVetrnLIoh2rd3DlJ6hHYYQosQYcUQ==", + "dependencies": { + "Sentry": "3.33.0", + "Serilog": "2.7.1" + } + }, + "Serilog": { + "type": "Transitive", + "resolved": "2.12.0", + "contentHash": "xaiJLIdu6rYMKfQMYUZgTy8YK7SMZjB4Yk50C/u//Z4OsvxkUfSPJy4nknfvwAC34yr13q7kcyh4grbwhSxyZg==" + }, + "Serilog.Enrichers.ClientInfo": { + "type": "Transitive", + "resolved": "1.3.0", + "contentHash": "mTc7PM+wC9Hr7LWSwqt5mmnlAr7RJs+eTb3PGPRhwdOackk95MkhUZognuxXEdlW19HAFNmEBTSBY5DfLwM8jQ==", + "dependencies": { + "Microsoft.AspNetCore.Http": "2.1.1", + "Serilog": "2.7.1" + } + }, + "Serilog.Exceptions": { + "type": "Transitive", + "resolved": "8.4.0", + "contentHash": "nc/+hUw3lsdo0zCj0KMIybAu7perMx79vu72w0za9Nsi6mWyNkGXxYxakAjWB7nEmYL6zdmhEQRB4oJ2ALUeug==", + "dependencies": { + "Serilog": "2.8.0", + "System.Reflection.TypeExtensions": "4.7.0" + } + }, + "Serilog.Formatting.Compact": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "pNroKVjo+rDqlxNG5PXkRLpfSCuDOBY0ri6jp9PLe505ljqwhwZz8ospy2vWhQlFu5GkIesh3FcDs4n7sWZODA==", + "dependencies": { + "Serilog": "2.8.0" + } + }, + "Serilog.Sinks.Console": { + "type": "Transitive", + "resolved": "4.1.0", + "contentHash": "K6N5q+5fetjnJPvCmkWOpJ/V8IEIoMIB1s86OzBrbxwTyHxdx3pmz4H+8+O/Dc/ftUX12DM1aynx/dDowkwzqg==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "Serilog.Sinks.File": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "uwV5hdhWPwUH1szhO8PJpFiahqXmzPzJT/sOijH/kFgUx+cyoDTMM8MHD0adw9+Iem6itoibbUXHYslzXsLEAg==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "Serilog.Sinks.PeriodicBatching": { + "type": "Transitive", + "resolved": "3.1.0", + "contentHash": "NDWR7m3PalVlGEq3rzoktrXikjFMLmpwF0HI4sowo8YDdU+gqPlTHlDQiOGxHfB0sTfjPA9JjA7ctKG9zqjGkw==", + "dependencies": { + "Serilog": "2.0.0" + } + }, + "Serilog.Sinks.Seq": { + "type": "Transitive", + "resolved": "5.2.2", + "contentHash": "1Csmo5ua7NKUe0yXUx+zsRefjAniPWcXFhUXxXG8pwo0iMiw2gjn9SOkgYnnxbgWqmlGv236w0N/dHc2v5XwMg==", + "dependencies": { + "Serilog": "2.12.0", + "Serilog.Formatting.Compact": "1.1.0", + "Serilog.Sinks.File": "5.0.0", + "Serilog.Sinks.PeriodicBatching": "3.1.0" + } + }, + "SerilogTimings": { + "type": "Transitive", + "resolved": "3.0.1", + "contentHash": "Zs28eTgszAMwpIrbBnWHBI50yuxL50p/dmAUWmy75+axdZYK/Sjm5/5m1N/CisR8acJUhTVcjPZrsB1P5iv0Uw==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "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.Buffers": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, + "System.Collections.Immutable": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "FXkLXiK0sVVewcso0imKQoOxjoPAj42R8HtjjbSjVPAzwDfzoyoznWxgA3c38LDbN9SJux1xXoXYAhz98j7r2g==", + "dependencies": { + "System.Memory": "4.5.4" + } + }, + "System.DoubleNumerics": { + "type": "Transitive", + "resolved": "3.1.3", + "contentHash": "KRKEM/L3KBodjA9VOg3EifFVWUY6EOqaMB05UvPEDm7Zeby/kZW+4kdWUEPzW6xtkwf46p661L9NrbeeQhtLzw==", + "dependencies": { + "NETStandard.Library": "1.6.1" + } + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Numerics.Vectors": "4.4.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + }, + "System.Numerics.Vectors": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" + }, + "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.Reflection.Metadata": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "5NecZgXktdGg34rh1OenY1rFNDCI8xSjFr+Z4OU4cU06AQHUdRnIIEeWENu3Wl4YowbzkymAIMvi3WyK9U53pQ==", + "dependencies": { + "System.Collections.Immutable": "5.0.0" + } + }, + "System.Reflection.TypeExtensions": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "VybpaOQQhqE6siHppMktjfGBw1GCwvCqiufqmP8F1nj7fTUNtW35LOEt3UZTEsECfo+ELAl/9o9nJx3U91i7vA==" + }, + "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" + } + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "ZD9TMpsmYJLrxbbmdvhwt9YEgG5WntEnZ/d1eH8JBX9LBp+Ju8BSBhUGbZMNVHHomWo2KVImJhTDl2hIgw/6MA==" + }, + "System.Runtime.InteropServices.WindowsRuntime": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "J4GUi3xZQLUBasNwZnjrffN8i5wpHrBtZoLG+OhRyGo/+YunMRWWtwoMDlUAIdmX0uRfpHIBDSV6zyr3yf00TA==", + "dependencies": { + "System.Runtime": "4.3.0" + } + }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "5.0.1", + "contentHash": "KmJ+CJXizDofbq6mpqDoRRLcxgOd2z9X3XoFNULSbvbqVRZkFX3istvr+MUjL6Zw1RT+RNdoI4GYidIINtgvqQ==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Memory": "4.5.4" + } + }, + "System.Text.Json": { + "type": "Transitive", + "resolved": "5.0.2", + "contentHash": "I47dVIGiV6SfAyppphxqupertT/5oZkYLDCX6vC3HpOI4ZLjyoKAreUoem2ie6G0RbRuFrlqz/PcTQjfb2DOfQ==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "5.0.0", + "System.Buffers": "4.5.1", + "System.Memory": "4.5.4", + "System.Numerics.Vectors": "4.5.0", + "System.Runtime.CompilerServices.Unsafe": "5.0.0", + "System.Text.Encodings.Web": "5.0.1", + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "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.autofac": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "[7.0.0, )" + } + }, + "speckle.converters.common": { + "type": "Project", + "dependencies": { + "Speckle.Autofac": "[2.0.999-local, )", + "Speckle.Objects": "[3.0.1-alpha.14, )" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "CentralTransitive", + "requested": "[7.0.0, )", + "resolved": "7.0.0", + "contentHash": "kmn78+LPVMOWeITUjIlfxUPDsI0R6G0RkeAMBmQxAJ7vBJn4q2dTva7pWi65ceN5vPGjJ9q/Uae2WKgvfktJAw==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Memory": "4.5.5" + } + }, + "Speckle.Core": { + "type": "CentralTransitive", + "requested": "[3.0.1-alpha.14, )", + "resolved": "3.0.1-alpha.14", + "contentHash": "RzQPVIGFFkKvG56YLr8ACtiwdWJE6IJ9vCQ4qHa0PIsUEpfzAIAi59jnzqtByOFC0FiFrFPow9bkfzylaZorAA==", + "dependencies": { + "GraphQL.Client": "6.0.0", + "Microsoft.CSharp": "4.7.0", + "Microsoft.Data.Sqlite": "7.0.5", + "Polly": "7.2.3", + "Polly.Contrib.WaitAndRetry": "1.1.1", + "Polly.Extensions.Http": "3.0.0", + "Sentry": "3.33.0", + "Sentry.Serilog": "3.33.0", + "Serilog": "2.12.0", + "Serilog.Enrichers.ClientInfo": "1.3.0", + "Serilog.Exceptions": "8.4.0", + "Serilog.Sinks.Console": "4.1.0", + "Serilog.Sinks.Seq": "5.2.2", + "SerilogTimings": "3.0.1", + "Speckle.Newtonsoft.Json": "13.0.2", + "System.DoubleNumerics": "3.1.3" + } + }, + "Speckle.Objects": { + "type": "CentralTransitive", + "requested": "[3.0.1-alpha.14, )", + "resolved": "3.0.1-alpha.14", + "contentHash": "z38LGryMvh7iU1uBW+4uo5DwsB3CwRgLt2uFexWFx3mPSid+A0l5XcJzOgLwgFhNl6B42Ryz4ezBsddTp1Uc/g==", + "dependencies": { + "Speckle.Core": "3.0.1-alpha.14" + } + } + } + } +} \ No newline at end of file diff --git a/DUI3-DX/Sdk/Speckle.Converters.Common/ContextWrapper.cs b/DUI3-DX/Sdk/Speckle.Converters.Common/ContextWrapper.cs new file mode 100644 index 0000000000..02088f06bb --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Converters.Common/ContextWrapper.cs @@ -0,0 +1,32 @@ +namespace Speckle.Converters.Common; + +public class ContextWrapper : IDisposable + where TDocument : class +{ + private IConversionContextStack? _stack; + + public ConversionContext? Context { get; private set; } + + public ContextWrapper(IConversionContextStack stack) + { + _stack = stack; + Context = _stack.Current; + } + + protected virtual void Dispose(bool disposing) + { + if (disposing && _stack != null) + { + // technically we could be popping something not this but throwing in dispose is bad + _stack.Pop(); + _stack = null; + Context = null; + } + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } +} diff --git a/DUI3-DX/Sdk/Speckle.Converters.Common/ConversionContext.cs b/DUI3-DX/Sdk/Speckle.Converters.Common/ConversionContext.cs new file mode 100644 index 0000000000..723877d0b9 --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Converters.Common/ConversionContext.cs @@ -0,0 +1,15 @@ +namespace Speckle.Converters.Common; + +// POC: record? +public class ConversionContext + where TDocument : class +{ + public ConversionContext(TDocument doc, string speckleUnits) + { + Document = doc; + SpeckleUnits = speckleUnits; + } + + public TDocument Document { get; } + public string SpeckleUnits { get; private set; } +} diff --git a/DUI3-DX/Sdk/Speckle.Converters.Common/ConversionContextStack.cs b/DUI3-DX/Sdk/Speckle.Converters.Common/ConversionContextStack.cs new file mode 100644 index 0000000000..918a29d3c1 --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Converters.Common/ConversionContextStack.cs @@ -0,0 +1,48 @@ +using System.Diagnostics.CodeAnalysis; +using Speckle.InterfaceGenerator; + +namespace Speckle.Converters.Common; + +// POC: Suppressed naming warning for now, but we should evaluate if we should follow this or disable it. +[SuppressMessage( + "Naming", + "CA1711:Identifiers should not have incorrect suffix", + Justification = "Name ends in Stack but it is in fact a Stack, just not inheriting from `System.Collections.Stack`" +)] +[GenerateAutoInterface] +public abstract class ConversionContextStack : IConversionContextStack + where TDocument : class +{ + private readonly IHostToSpeckleUnitConverter _unitConverter; + private readonly TDocument _document; + + protected ConversionContextStack( + TDocument document, + THostUnit hostUnit, + IHostToSpeckleUnitConverter unitConverter + ) + { + _document = document; + _unitConverter = unitConverter; + + _stack.Push(new ConversionContext(_document, _unitConverter.ConvertOrThrow(hostUnit))); + } + + private readonly Stack> _stack = new(); + + public ConversionContext Current => _stack.Peek(); + + public ContextWrapper Push(string speckleUnit) + { + var context = new ConversionContext(_document, speckleUnit); + _stack.Push(context); + return new ContextWrapper(this); + } + + public ContextWrapper Push(THostUnit hostUnit) + { + return Push(_unitConverter.ConvertOrThrow(hostUnit)); + } + + public void Pop() => _stack.Pop(); +} diff --git a/DUI3-DX/Sdk/Speckle.Converters.Common/DisplayableObject.cs b/DUI3-DX/Sdk/Speckle.Converters.Common/DisplayableObject.cs new file mode 100644 index 0000000000..a1f2b91d5b --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Converters.Common/DisplayableObject.cs @@ -0,0 +1,29 @@ +using Objects; +using Objects.Geometry; +using Speckle.Core.Models; + +namespace Speckle.Converters.Common; + +// POC: This object was created with the purpose of defining a specific subtype of base type the fallback conversion. +// This will never be serialised and is just used to provide a type to register as the fallback conversion. +// We did this because providing a conversion from List seemed too generic and potentially conflicting. +// This also allows us to treat any implementation of IDisplayValue without caring about it's specific T type. + +public sealed class DisplayableObject : Base, IDisplayValue> +{ + public DisplayableObject(IReadOnlyList displayValue) + { + var invalidGeometry = displayValue.FirstOrDefault(b => b is not (Line or Polyline or Mesh or Arc or Point)); + + if (invalidGeometry != null) + { + throw new ArgumentException( + $"Displayable objects should only contain simple geometries (lines, polylines, meshes) but contained {invalidGeometry}" + ); + } + + this.displayValue = displayValue; + } + + public IReadOnlyList displayValue { get; } +} diff --git a/DUI3-DX/Sdk/Speckle.Converters.Common/IHostToSpeckleUnitConverter.cs b/DUI3-DX/Sdk/Speckle.Converters.Common/IHostToSpeckleUnitConverter.cs new file mode 100644 index 0000000000..f4413a9ea7 --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Converters.Common/IHostToSpeckleUnitConverter.cs @@ -0,0 +1,6 @@ +namespace Speckle.Converters.Common; + +public interface IHostToSpeckleUnitConverter +{ + string ConvertOrThrow(THostUnit hostUnit); +} diff --git a/DUI3-DX/Sdk/Speckle.Converters.Common/IRootElementProvider.cs b/DUI3-DX/Sdk/Speckle.Converters.Common/IRootElementProvider.cs new file mode 100644 index 0000000000..e183c154af --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Converters.Common/IRootElementProvider.cs @@ -0,0 +1,6 @@ +namespace Speckle.Converters.Common; + +public interface IRootElementProvider +{ + Type GetRootType(); +} diff --git a/DUI3-DX/Sdk/Speckle.Converters.Common/IRootToHostConverter.cs b/DUI3-DX/Sdk/Speckle.Converters.Common/IRootToHostConverter.cs new file mode 100644 index 0000000000..2f11b6e5c4 --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Converters.Common/IRootToHostConverter.cs @@ -0,0 +1,8 @@ +using Speckle.Core.Models; + +namespace Speckle.Converters.Common; + +public interface IRootToHostConverter +{ + object Convert(Base target); +} diff --git a/DUI3-DX/Sdk/Speckle.Converters.Common/NameAndRankValueAttribute.cs b/DUI3-DX/Sdk/Speckle.Converters.Common/NameAndRankValueAttribute.cs new file mode 100644 index 0000000000..54f380eeb0 --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Converters.Common/NameAndRankValueAttribute.cs @@ -0,0 +1,18 @@ +namespace Speckle.Converters.Common; + +// POC: maybe better to put in utils/reflection +[AttributeUsage(AttributeTargets.Class)] +public sealed class NameAndRankValueAttribute : 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; + } +} diff --git a/DUI3-DX/Sdk/Speckle.Converters.Common/NotNullExtensions.cs b/DUI3-DX/Sdk/Speckle.Converters.Common/NotNullExtensions.cs new file mode 100644 index 0000000000..50e9224965 --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Converters.Common/NotNullExtensions.cs @@ -0,0 +1,55 @@ +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; + +namespace Speckle.Converters.Common; + +public static class NotNullExtensions +{ + public static async Task NotNull( + this Task task, + [CallerArgumentExpression(nameof(task))] string? message = null + ) + where T : class + { + var x = await task.ConfigureAwait(false); + if (x is null) + { + throw new ArgumentNullException(message ?? "Value is null"); + } + return x; + } + + public static async Task NotNull( + this Task task, + [CallerArgumentExpression(nameof(task))] string? message = null + ) + where T : struct + { + var x = await task.ConfigureAwait(false); + if (x is null) + { + throw new ArgumentNullException(message ?? "Value is null"); + } + return x.Value; + } + + public static T NotNull([NotNull] this T? obj, [CallerArgumentExpression(nameof(obj))] string? paramName = null) + where T : class + { + if (obj is null) + { + throw new ArgumentNullException(paramName ?? "Value is null"); + } + return obj; + } + + public static T NotNull([NotNull] this T? obj, [CallerArgumentExpression(nameof(obj))] string? paramName = null) + where T : struct + { + if (obj is null) + { + throw new ArgumentNullException(paramName ?? "Value is null"); + } + return obj.Value; + } +} diff --git a/DUI3-DX/Sdk/Speckle.Converters.Common/Objects/IToHostTopLevelConverter.cs b/DUI3-DX/Sdk/Speckle.Converters.Common/Objects/IToHostTopLevelConverter.cs new file mode 100644 index 0000000000..59869df951 --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Converters.Common/Objects/IToHostTopLevelConverter.cs @@ -0,0 +1,12 @@ +using Speckle.Core.Models; + +namespace Speckle.Converters.Common.Objects; + +// POC: NEXT UP +// * begin scope: https://stackoverflow.com/questions/49595198/autofac-resolving-through-factory-methods +// Interceptors? + +public interface IToHostTopLevelConverter +{ + object Convert(Base target); +} diff --git a/DUI3-DX/Sdk/Speckle.Converters.Common/Objects/IToSpeckleTopLevelConverter.cs b/DUI3-DX/Sdk/Speckle.Converters.Common/Objects/IToSpeckleTopLevelConverter.cs new file mode 100644 index 0000000000..b0d7525e6e --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Converters.Common/Objects/IToSpeckleTopLevelConverter.cs @@ -0,0 +1,12 @@ +using Speckle.Core.Models; + +namespace Speckle.Converters.Common.Objects; + +// POC: NEXT UP +// * begin scope: https://stackoverflow.com/questions/49595198/autofac-resolving-through-factory-methods +// Interceptors? + +public interface IToSpeckleTopLevelConverter +{ + Base Convert(object target); +} diff --git a/DUI3-DX/Sdk/Speckle.Converters.Common/Objects/ITypedConverter.cs b/DUI3-DX/Sdk/Speckle.Converters.Common/Objects/ITypedConverter.cs new file mode 100644 index 0000000000..80efa37f86 --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Converters.Common/Objects/ITypedConverter.cs @@ -0,0 +1,6 @@ +namespace Speckle.Converters.Common.Objects; + +public interface ITypedConverter +{ + TOut Convert(TIn target); +} diff --git a/DUI3-DX/Sdk/Speckle.Converters.Common/ReceiveMode.cs b/DUI3-DX/Sdk/Speckle.Converters.Common/ReceiveMode.cs new file mode 100644 index 0000000000..b24701062d --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Converters.Common/ReceiveMode.cs @@ -0,0 +1,25 @@ +namespace Speckle.Converters.Common; + +// NOTE: Do not change the order of the existing ones +/// +/// Receive modes indicate what to do and not do when receiving objects +/// +public enum ReceiveMode +{ + /// + /// Attemts updating previously received objects by ID, deletes previously received objects that do not exist anymore and creates new ones + /// + // POC: these could be numbered explicitly if the order matters, gaps could be included, + // so 1000, 2000, 3000 for plenty of expansion + Update, + + /// + /// Always creates new objects + /// + Create, + + /// + /// Ignores updating previously received objects and does not attempt updating or deleting them, creates new objects + /// + Ignore +} diff --git a/DUI3-DX/Sdk/Speckle.Converters.Common/RecursiveConverterResolver.cs b/DUI3-DX/Sdk/Speckle.Converters.Common/RecursiveConverterResolver.cs new file mode 100644 index 0000000000..0c18169e5c --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Converters.Common/RecursiveConverterResolver.cs @@ -0,0 +1,27 @@ +using Speckle.Autofac.DependencyInjection; +using Speckle.InterfaceGenerator; + +namespace Speckle.Converters.Common; + +[GenerateAutoInterface] +public sealed class ConverterResolver : IConverterResolver + where TConverter : class +{ + private readonly IFactory _factory; + + public ConverterResolver(IFactory factory) + { + _factory = factory; + } + + public TConverter? GetConversionForType(Type objectType) + { + if (objectType.BaseType == null) + { + //We've reached the top of the inheritance tree + return null; + } + + return _factory.ResolveInstance(objectType.Name) ?? GetConversionForType(objectType.BaseType); + } +} diff --git a/DUI3-DX/Sdk/Speckle.Converters.Common/RootConvertManager.cs b/DUI3-DX/Sdk/Speckle.Converters.Common/RootConvertManager.cs new file mode 100644 index 0000000000..4ae930fa1c --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Converters.Common/RootConvertManager.cs @@ -0,0 +1,42 @@ +using Speckle.Autofac.DependencyInjection; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; +using Speckle.InterfaceGenerator; + +namespace Speckle.Converters.Common; + +[GenerateAutoInterface] +public class RootConvertManager : IRootConvertManager +{ + private readonly IFactory _toSpeckle; + + public RootConvertManager(IFactory toSpeckle) + { + _toSpeckle = toSpeckle; + } + + public Type GetTargetType(object target) => target.GetType(); + + public bool IsSubClass(Type baseType, Type childType) => baseType.IsAssignableFrom(childType); + + public Base Convert(Type type, object obj) + { + try + { + var objectConverter = _toSpeckle.ResolveInstance(type.Name); //poc: would be nice to have supertypes resolve + + if (objectConverter == null) + { + throw new NotSupportedException($"No conversion found for {type.Name}"); + } + var convertedObject = objectConverter.Convert(obj); + + return convertedObject; + } + catch (SpeckleConversionException e) + { + Console.WriteLine(e); + throw; // Just rethrowing for now, Logs may be needed here. + } + } +} diff --git a/DUI3-DX/Sdk/Speckle.Converters.Common/RootToSpeckleConverter.cs b/DUI3-DX/Sdk/Speckle.Converters.Common/RootToSpeckleConverter.cs new file mode 100644 index 0000000000..d1807e231f --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Converters.Common/RootToSpeckleConverter.cs @@ -0,0 +1,39 @@ +using Speckle.Autofac.DependencyInjection; +using Speckle.Converters.Common.Objects; +using Speckle.Core.Models; +using Speckle.InterfaceGenerator; + +namespace Speckle.Converters.Common; + +[GenerateAutoInterface] +public class RootToSpeckleConverter : IRootToSpeckleConverter +{ + private readonly IFactory _toSpeckle; + + public RootToSpeckleConverter(IFactory toSpeckle) + { + _toSpeckle = toSpeckle; + } + + public Base Convert(object target) + { + Type type = target.GetType(); + try + { + var objectConverter = _toSpeckle.ResolveInstance(type.Name); //poc: would be nice to have supertypes resolve + + if (objectConverter == null) + { + throw new NotSupportedException($"No conversion found for {type.Name}"); + } + var convertedObject = objectConverter.Convert(target); + + return convertedObject; + } + catch (SpeckleConversionException e) + { + Console.WriteLine(e); + throw; // Just rethrowing for now, Logs may be needed here. + } + } +} diff --git a/DUI3-DX/Sdk/Speckle.Converters.Common/Speckle.Converters.Common.csproj b/DUI3-DX/Sdk/Speckle.Converters.Common/Speckle.Converters.Common.csproj new file mode 100644 index 0000000000..4bbe1058c7 --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Converters.Common/Speckle.Converters.Common.csproj @@ -0,0 +1,15 @@ + + + + netstandard2.0 + + + + + + + + + + + diff --git a/DUI3-DX/Sdk/Speckle.Converters.Common/SpeckleConversionException.cs b/DUI3-DX/Sdk/Speckle.Converters.Common/SpeckleConversionException.cs new file mode 100644 index 0000000000..19cbd2bf01 --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Converters.Common/SpeckleConversionException.cs @@ -0,0 +1,12 @@ +namespace Speckle.Converters.Common; + +public class SpeckleConversionException : Exception +{ + public SpeckleConversionException() { } + + public SpeckleConversionException(string message) + : base(message) { } + + public SpeckleConversionException(string message, Exception innerException) + : base(message, innerException) { } +} diff --git a/DUI3-DX/Sdk/Speckle.Converters.Common/packages.lock.json b/DUI3-DX/Sdk/Speckle.Converters.Common/packages.lock.json new file mode 100644 index 0000000000..23035de679 --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Converters.Common/packages.lock.json @@ -0,0 +1,499 @@ +{ + "version": 2, + "dependencies": { + ".NETStandard,Version=v2.0": { + "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" + } + }, + "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, )", + "resolved": "1.14.1", + "contentHash": "mOOmFYwad3MIOL14VCjj02LljyF1GNw1wP0YVlxtcPvqdxjGGMNdNJJxHptlry3MOd8b40Flm8RPOM8JOlN2sQ==" + }, + "Speckle.InterfaceGenerator": { + "type": "Direct", + "requested": "[0.9.5, )", + "resolved": "0.9.5", + "contentHash": "oU/L7pN1R7q8KkbrpQ3WJnHirPHqn+9DEA7asOcUiggV5dzVg1A/VYs7GOSusD24njxXh03tE3a2oTLOjt3cVg==" + }, + "Speckle.Objects": { + "type": "Direct", + "requested": "[3.0.1-alpha.14, )", + "resolved": "3.0.1-alpha.14", + "contentHash": "z38LGryMvh7iU1uBW+4uo5DwsB3CwRgLt2uFexWFx3mPSid+A0l5XcJzOgLwgFhNl6B42Ryz4ezBsddTp1Uc/g==", + "dependencies": { + "Speckle.Core": "3.0.1-alpha.14" + } + }, + "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.AspNetCore.Http": { + "type": "Transitive", + "resolved": "2.1.1", + "contentHash": "pPDcCW8spnyibK3krpxrOpaFHf5fjV6k1Hsl6gfh77N/8gRYlLU7MOQDUnjpEwdlHmtxwJKQJNxZqVQOmJGRUw==", + "dependencies": { + "Microsoft.AspNetCore.Http.Abstractions": "2.1.1", + "Microsoft.AspNetCore.WebUtilities": "2.1.1", + "Microsoft.Extensions.ObjectPool": "2.1.1", + "Microsoft.Extensions.Options": "2.1.1", + "Microsoft.Net.Http.Headers": "2.1.1" + } + }, + "Microsoft.AspNetCore.Http.Abstractions": { + "type": "Transitive", + "resolved": "2.1.1", + "contentHash": "kQUEVOU4loc8CPSb2WoHFTESqwIa8Ik7ysCBfTwzHAd0moWovc9JQLmhDIHlYLjHbyexqZAlkq/FPRUZqokebw==", + "dependencies": { + "Microsoft.AspNetCore.Http.Features": "2.1.1", + "System.Text.Encodings.Web": "4.5.0" + } + }, + "Microsoft.AspNetCore.Http.Features": { + "type": "Transitive", + "resolved": "2.1.1", + "contentHash": "VklZ7hWgSvHBcDtwYYkdMdI/adlf7ebxTZ9kdzAhX+gUs5jSHE9mZlTamdgf9miSsxc1QjNazHXTDJdVPZKKTw==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.1.1" + } + }, + "Microsoft.AspNetCore.WebUtilities": { + "type": "Transitive", + "resolved": "2.1.1", + "contentHash": "PGKIZt4+412Z/XPoSjvYu/QIbTxcAQuEFNoA1Pw8a9mgmO0ZhNBmfaNyhgXFf7Rq62kP0tT/2WXpxdcQhkFUPA==", + "dependencies": { + "Microsoft.Net.Http.Headers": "2.1.1", + "System.Text.Encodings.Web": "4.5.0" + } + }, + "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.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "2.1.1", + "contentHash": "MgYpU5cwZohUMKKg3sbPhvGG+eAZ/59E9UwPwlrUkyXU+PGzqwZg9yyQNjhxuAWmoNoFReoemeCku50prYSGzA==" + }, + "Microsoft.Extensions.ObjectPool": { + "type": "Transitive", + "resolved": "2.1.1", + "contentHash": "SErON45qh4ogDp6lr6UvVmFYW0FERihW+IQ+2JyFv1PUyWktcJytFaWH5zarufJvZwhci7Rf1IyGXr9pVEadTw==" + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "2.1.1", + "contentHash": "V7lXCU78lAbzaulCGFKojcCyG8RTJicEbiBkPJjFqiqXwndEBBIehdXRMWEVU3UtzQ1yDvphiWUL9th6/4gJ7w==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.1.1", + "Microsoft.Extensions.Primitives": "2.1.1" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "2.1.1", + "contentHash": "scJ1GZNIxMmjpENh0UZ8XCQ6vzr/LzeF9WvEA51Ix2OQGAs9WPgPu8ABVUdvpKPLuor/t05gm6menJK3PwqOXg==", + "dependencies": { + "System.Memory": "4.5.1", + "System.Runtime.CompilerServices.Unsafe": "4.5.1" + } + }, + "Microsoft.Net.Http.Headers": { + "type": "Transitive", + "resolved": "2.1.1", + "contentHash": "lPNIphl8b2EuhOE9dMH6EZDmu7pS882O+HMi5BJNsigxHaWlBrYxZHFZgE18cyaPp6SSZcTkKkuzfjV/RRQKlA==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.1.1", + "System.Buffers": "4.5.0" + } + }, + "Microsoft.NETCore.Platforms": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==" + }, + "Microsoft.NETCore.Targets": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "Polly": { + "type": "Transitive", + "resolved": "7.2.3", + "contentHash": "DeCY0OFbNdNxsjntr1gTXHJ5pKUwYzp04Er2LLeN3g6pWhffsGuKVfMBLe1lw7x76HrPkLxKEFxBlpRxS2nDEQ==" + }, + "Polly.Contrib.WaitAndRetry": { + "type": "Transitive", + "resolved": "1.1.1", + "contentHash": "1MUQLiSo4KDkQe6nzQRhIU05lm9jlexX5BVsbuw0SL82ynZ+GzAHQxJVDPVBboxV37Po3SG077aX8DuSy8TkaA==" + }, + "Polly.Extensions.Http": { + "type": "Transitive", + "resolved": "3.0.0", + "contentHash": "drrG+hB3pYFY7w1c3BD+lSGYvH2oIclH8GRSehgfyP5kjnFnHKQuuBhuHLv+PWyFuaTDyk/vfRpnxOzd11+J8g==", + "dependencies": { + "Polly": "7.1.0" + } + }, + "Sentry": { + "type": "Transitive", + "resolved": "3.33.0", + "contentHash": "8vbD2o6IR2wrRrkSiRbnodWGWUOqIlwYtzpjvPNOb5raJdOf+zxMwfS8f6nx9bmrTTfDj7KrCB8C/5OuicAc8A==", + "dependencies": { + "System.Reflection.Metadata": "5.0.0", + "System.Text.Json": "5.0.2" + } + }, + "Sentry.Serilog": { + "type": "Transitive", + "resolved": "3.33.0", + "contentHash": "V8BU7QGWg2qLYfNPqtuTBhC1opysny5l+Ifp6J6PhOeAxU0FssR7nYfbJVetrnLIoh2rd3DlJ6hHYYQosQYcUQ==", + "dependencies": { + "Sentry": "3.33.0", + "Serilog": "2.7.1" + } + }, + "Serilog": { + "type": "Transitive", + "resolved": "2.12.0", + "contentHash": "xaiJLIdu6rYMKfQMYUZgTy8YK7SMZjB4Yk50C/u//Z4OsvxkUfSPJy4nknfvwAC34yr13q7kcyh4grbwhSxyZg==" + }, + "Serilog.Enrichers.ClientInfo": { + "type": "Transitive", + "resolved": "1.3.0", + "contentHash": "mTc7PM+wC9Hr7LWSwqt5mmnlAr7RJs+eTb3PGPRhwdOackk95MkhUZognuxXEdlW19HAFNmEBTSBY5DfLwM8jQ==", + "dependencies": { + "Microsoft.AspNetCore.Http": "2.1.1", + "Serilog": "2.7.1" + } + }, + "Serilog.Exceptions": { + "type": "Transitive", + "resolved": "8.4.0", + "contentHash": "nc/+hUw3lsdo0zCj0KMIybAu7perMx79vu72w0za9Nsi6mWyNkGXxYxakAjWB7nEmYL6zdmhEQRB4oJ2ALUeug==", + "dependencies": { + "Serilog": "2.8.0", + "System.Reflection.TypeExtensions": "4.7.0" + } + }, + "Serilog.Formatting.Compact": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "pNroKVjo+rDqlxNG5PXkRLpfSCuDOBY0ri6jp9PLe505ljqwhwZz8ospy2vWhQlFu5GkIesh3FcDs4n7sWZODA==", + "dependencies": { + "Serilog": "2.8.0" + } + }, + "Serilog.Sinks.Console": { + "type": "Transitive", + "resolved": "4.1.0", + "contentHash": "K6N5q+5fetjnJPvCmkWOpJ/V8IEIoMIB1s86OzBrbxwTyHxdx3pmz4H+8+O/Dc/ftUX12DM1aynx/dDowkwzqg==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "Serilog.Sinks.File": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "uwV5hdhWPwUH1szhO8PJpFiahqXmzPzJT/sOijH/kFgUx+cyoDTMM8MHD0adw9+Iem6itoibbUXHYslzXsLEAg==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "Serilog.Sinks.PeriodicBatching": { + "type": "Transitive", + "resolved": "3.1.0", + "contentHash": "NDWR7m3PalVlGEq3rzoktrXikjFMLmpwF0HI4sowo8YDdU+gqPlTHlDQiOGxHfB0sTfjPA9JjA7ctKG9zqjGkw==", + "dependencies": { + "Serilog": "2.0.0" + } + }, + "Serilog.Sinks.Seq": { + "type": "Transitive", + "resolved": "5.2.2", + "contentHash": "1Csmo5ua7NKUe0yXUx+zsRefjAniPWcXFhUXxXG8pwo0iMiw2gjn9SOkgYnnxbgWqmlGv236w0N/dHc2v5XwMg==", + "dependencies": { + "Serilog": "2.12.0", + "Serilog.Formatting.Compact": "1.1.0", + "Serilog.Sinks.File": "5.0.0", + "Serilog.Sinks.PeriodicBatching": "3.1.0" + } + }, + "SerilogTimings": { + "type": "Transitive", + "resolved": "3.0.1", + "contentHash": "Zs28eTgszAMwpIrbBnWHBI50yuxL50p/dmAUWmy75+axdZYK/Sjm5/5m1N/CisR8acJUhTVcjPZrsB1P5iv0Uw==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "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.Buffers": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, + "System.Collections.Immutable": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "FXkLXiK0sVVewcso0imKQoOxjoPAj42R8HtjjbSjVPAzwDfzoyoznWxgA3c38LDbN9SJux1xXoXYAhz98j7r2g==", + "dependencies": { + "System.Memory": "4.5.4" + } + }, + "System.DoubleNumerics": { + "type": "Transitive", + "resolved": "3.1.3", + "contentHash": "KRKEM/L3KBodjA9VOg3EifFVWUY6EOqaMB05UvPEDm7Zeby/kZW+4kdWUEPzW6xtkwf46p661L9NrbeeQhtLzw==", + "dependencies": { + "NETStandard.Library": "1.6.1" + } + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Numerics.Vectors": "4.4.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + }, + "System.Numerics.Vectors": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" + }, + "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.Reflection.Metadata": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "5NecZgXktdGg34rh1OenY1rFNDCI8xSjFr+Z4OU4cU06AQHUdRnIIEeWENu3Wl4YowbzkymAIMvi3WyK9U53pQ==", + "dependencies": { + "System.Collections.Immutable": "5.0.0" + } + }, + "System.Reflection.TypeExtensions": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "VybpaOQQhqE6siHppMktjfGBw1GCwvCqiufqmP8F1nj7fTUNtW35LOEt3UZTEsECfo+ELAl/9o9nJx3U91i7vA==" + }, + "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" + } + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "ZD9TMpsmYJLrxbbmdvhwt9YEgG5WntEnZ/d1eH8JBX9LBp+Ju8BSBhUGbZMNVHHomWo2KVImJhTDl2hIgw/6MA==" + }, + "System.Runtime.InteropServices.WindowsRuntime": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "J4GUi3xZQLUBasNwZnjrffN8i5wpHrBtZoLG+OhRyGo/+YunMRWWtwoMDlUAIdmX0uRfpHIBDSV6zyr3yf00TA==", + "dependencies": { + "System.Runtime": "4.3.0" + } + }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "5.0.1", + "contentHash": "KmJ+CJXizDofbq6mpqDoRRLcxgOd2z9X3XoFNULSbvbqVRZkFX3istvr+MUjL6Zw1RT+RNdoI4GYidIINtgvqQ==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Memory": "4.5.4" + } + }, + "System.Text.Json": { + "type": "Transitive", + "resolved": "5.0.2", + "contentHash": "I47dVIGiV6SfAyppphxqupertT/5oZkYLDCX6vC3HpOI4ZLjyoKAreUoem2ie6G0RbRuFrlqz/PcTQjfb2DOfQ==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "5.0.0", + "System.Buffers": "4.5.1", + "System.Memory": "4.5.4", + "System.Numerics.Vectors": "4.5.0", + "System.Runtime.CompilerServices.Unsafe": "5.0.0", + "System.Text.Encodings.Web": "5.0.1", + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "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.autofac": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "[7.0.0, )" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "CentralTransitive", + "requested": "[7.0.0, )", + "resolved": "7.0.0", + "contentHash": "kmn78+LPVMOWeITUjIlfxUPDsI0R6G0RkeAMBmQxAJ7vBJn4q2dTva7pWi65ceN5vPGjJ9q/Uae2WKgvfktJAw==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Memory": "4.5.5" + } + }, + "Speckle.Core": { + "type": "CentralTransitive", + "requested": "[3.0.1-alpha.14, )", + "resolved": "3.0.1-alpha.14", + "contentHash": "RzQPVIGFFkKvG56YLr8ACtiwdWJE6IJ9vCQ4qHa0PIsUEpfzAIAi59jnzqtByOFC0FiFrFPow9bkfzylaZorAA==", + "dependencies": { + "GraphQL.Client": "6.0.0", + "Microsoft.CSharp": "4.7.0", + "Microsoft.Data.Sqlite": "7.0.5", + "Polly": "7.2.3", + "Polly.Contrib.WaitAndRetry": "1.1.1", + "Polly.Extensions.Http": "3.0.0", + "Sentry": "3.33.0", + "Sentry.Serilog": "3.33.0", + "Serilog": "2.12.0", + "Serilog.Enrichers.ClientInfo": "1.3.0", + "Serilog.Exceptions": "8.4.0", + "Serilog.Sinks.Console": "4.1.0", + "Serilog.Sinks.Seq": "5.2.2", + "SerilogTimings": "3.0.1", + "Speckle.Newtonsoft.Json": "13.0.2", + "System.DoubleNumerics": "3.1.3" + } + } + } + } +} \ No newline at end of file diff --git a/Directory.Build.props b/Directory.Build.props index e0b95b5d9c..a092bcd468 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -36,9 +36,8 @@ CS1591;CS1573; CA1303;CA1304;CA1305;CA1307;CA1308;CA1309;CA1310;CA1311;CA2101; - NU1701; - CA1815;CA1054;$(NoWarn) + CA1815;CA1852;CA1812;CA1003;CA2109;CA1848;$(NoWarn) @@ -127,7 +126,7 @@ $(MSBuildThisFileDirectory) - + diff --git a/Directory.Build.targets b/Directory.Build.targets index 432400ed48..f55614992c 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -41,14 +41,6 @@ Command="cp '$(TargetDir)$(AssemblyName).dll' $HOME'/.config/Speckle/Kits/$(KitFolder)/'"/> - - - - - CA5394;CA2007;CA1852;CA1819;CA1711;CA1063;CA1816;CA2234;CS8618; + CA5394;CA2007;CA1852;CA1819;CA1711;CA1063;CA1816;CA2234;CS8618;CA1054; false diff --git a/GitVersion.yml b/GitVersion.yml new file mode 100644 index 0000000000..61b6cfeb8e --- /dev/null +++ b/GitVersion.yml @@ -0,0 +1,10 @@ +mode: ContinuousDelivery +assembly-informational-format: "{Major}.{Minor}.{Patch}-{PreReleaseTag}" +branches: + main: + regex: ^main$ + tag: rc + develop: + tag: beta + pull-request: + tag: pr diff --git a/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/Converter.AutocadCivil.Utils.cs b/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/Converter.AutocadCivil.Utils.cs index 56e6b7e48f..e9fc4928e9 100644 --- a/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/Converter.AutocadCivil.Utils.cs +++ b/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/Converter.AutocadCivil.Utils.cs @@ -94,6 +94,11 @@ public Dictionary LineTypeDictionary /// public static string RemoveInvalidChars(string str) { + // TOOD: DUI3! this is temporary check since DUI3 not fully implemented! + if (str == null) + { + return ""; + } // using this to handle rhino nested layer syntax // replace "::" layer delimiter with "$" (acad standard) string cleanDelimiter = str.Replace("::", "$"); diff --git a/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/ConverterAutocadCivil.Other.cs b/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/ConverterAutocadCivil.Other.cs index f97cea6124..338ac88f40 100644 --- a/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/ConverterAutocadCivil.Other.cs +++ b/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/ConverterAutocadCivil.Other.cs @@ -588,8 +588,11 @@ public ObjectId DefinitionToNativeDB(Base definition, out List notes) { notes = new List(); + // TODO: DUI3! Ignore commit info while implementing DUI3! Later find a similar way or use as below! + var commitInfo = "dui3_test"; + // get the definition name - var commitInfo = RemoveInvalidChars(Doc.UserData["commit"] as string); + // var commitInfo = RemoveInvalidAutocadChars(Doc.UserData["commit"] as string); string definitionName = definition is BlockDefinition blockDef ? RemoveInvalidChars(blockDef.name) : definition is RevitSymbolElementType revitDef diff --git a/Objects/Converters/ConverterCSI/ConverterETABS/ConverterETABS.csproj b/Objects/Converters/ConverterCSI/ConverterETABS/ConverterETABS.csproj index 4a0aa91fb2..3c7bd5ee44 100644 --- a/Objects/Converters/ConverterCSI/ConverterETABS/ConverterETABS.csproj +++ b/Objects/Converters/ConverterCSI/ConverterETABS/ConverterETABS.csproj @@ -31,4 +31,4 @@ - \ No newline at end of file + diff --git a/Objects/Converters/ConverterNavisworks/ConverterNavisworks2020/ConverterNavisworks2020.csproj b/Objects/Converters/ConverterNavisworks/ConverterNavisworks2020/ConverterNavisworks2020.csproj index fa1e9ee2c7..1dc2afda3b 100644 --- a/Objects/Converters/ConverterNavisworks/ConverterNavisworks2020/ConverterNavisworks2020.csproj +++ b/Objects/Converters/ConverterNavisworks/ConverterNavisworks2020/ConverterNavisworks2020.csproj @@ -3,7 +3,6 @@ SpeckleConverterNavisworks Objects.Converter.Navisworks2020 Objects.Converter.Navisworks - AnyCPU;x64 bin\x64\$(Configuration) true $(DefineConstants);NAVMAN17 diff --git a/Objects/Converters/ConverterNavisworks/ConverterNavisworks2021/ConverterNavisworks2021.csproj b/Objects/Converters/ConverterNavisworks/ConverterNavisworks2021/ConverterNavisworks2021.csproj index 3494efb2f4..aafe1b03a8 100644 --- a/Objects/Converters/ConverterNavisworks/ConverterNavisworks2021/ConverterNavisworks2021.csproj +++ b/Objects/Converters/ConverterNavisworks/ConverterNavisworks2021/ConverterNavisworks2021.csproj @@ -3,7 +3,6 @@ SpeckleConverterNavisworks Objects.Converter.Navisworks2021 Objects.Converter.Navisworks - AnyCPU;x64 bin\x64\$(Configuration) true $(DefineConstants);NAVMAN18 diff --git a/Objects/Converters/ConverterNavisworks/ConverterNavisworks2022/ConverterNavisworks2022.csproj b/Objects/Converters/ConverterNavisworks/ConverterNavisworks2022/ConverterNavisworks2022.csproj index dc26fb29a6..6f0de2a027 100644 --- a/Objects/Converters/ConverterNavisworks/ConverterNavisworks2022/ConverterNavisworks2022.csproj +++ b/Objects/Converters/ConverterNavisworks/ConverterNavisworks2022/ConverterNavisworks2022.csproj @@ -3,7 +3,6 @@ SpeckleConverterNavisworks Objects.Converter.Navisworks2022 Objects.Converter.Navisworks - AnyCPU;x64 bin\x64\$(Configuration) true $(DefineConstants);NAVMAN19 diff --git a/Objects/Converters/ConverterNavisworks/ConverterNavisworks2023/ConverterNavisworks2023.csproj b/Objects/Converters/ConverterNavisworks/ConverterNavisworks2023/ConverterNavisworks2023.csproj index f752f554f7..ed59912974 100644 --- a/Objects/Converters/ConverterNavisworks/ConverterNavisworks2023/ConverterNavisworks2023.csproj +++ b/Objects/Converters/ConverterNavisworks/ConverterNavisworks2023/ConverterNavisworks2023.csproj @@ -3,7 +3,6 @@ SpeckleConverterNavisworks Objects.Converter.Navisworks2023 Objects.Converter.Navisworks - AnyCPU;x64 bin\x64\$(Configuration) true $(DefineConstants);NAVMAN20 diff --git a/Objects/Converters/ConverterNavisworks/ConverterNavisworks2024/ConverterNavisworks2024.csproj b/Objects/Converters/ConverterNavisworks/ConverterNavisworks2024/ConverterNavisworks2024.csproj index 65fcd43333..2d190db12d 100644 --- a/Objects/Converters/ConverterNavisworks/ConverterNavisworks2024/ConverterNavisworks2024.csproj +++ b/Objects/Converters/ConverterNavisworks/ConverterNavisworks2024/ConverterNavisworks2024.csproj @@ -3,7 +3,6 @@ SpeckleConverterNavisworks Objects.Converter.Navisworks2024 Objects.Converter.Navisworks - AnyCPU;x64 bin\x64\$(Configuration) true $(DefineConstants);NAVMAN21 diff --git a/Objects/Converters/ConverterRevit/ConverterRevitTests/ConverterRevitTestsShared/Generated/REVIT2023/TestGenerator/TestGenerator.Generator/GeneratedTests.g.cs b/Objects/Converters/ConverterRevit/ConverterRevitTests/ConverterRevitTestsShared/Generated/REVIT2023/TestGenerator/TestGenerator.Generator/GeneratedTests.g.cs index f4caac162a..3a0b767ca6 100644 --- a/Objects/Converters/ConverterRevit/ConverterRevitTests/ConverterRevitTestsShared/Generated/REVIT2023/TestGenerator/TestGenerator.Generator/GeneratedTests.g.cs +++ b/Objects/Converters/ConverterRevit/ConverterRevitTests/ConverterRevitTestsShared/Generated/REVIT2023/TestGenerator/TestGenerator.Generator/GeneratedTests.g.cs @@ -6,4 +6,532 @@ namespace ConverterRevitTests { + public class AdaptiveComponentAdaptiveComponentFixture : SpeckleConversionFixture + { + public override string Category => "AdaptiveComponent"; + public override string TestName => "AdaptiveComponent"; + + public AdaptiveComponentAdaptiveComponentFixture() : base() + { + } + } + + public class AdaptiveComponentAdaptiveComponentTests : SpeckleConversionTest, IClassFixture + { + public AdaptiveComponentAdaptiveComponentTests(AdaptiveComponentAdaptiveComponentFixture fixture) : base(fixture) + { + } + + [Fact] + [Trait("AdaptiveComponent", "AdaptiveComponentToSpeckle")] + public async Task AdaptiveComponentAdaptiveComponentToSpeckle() + { + await NativeToSpeckle(); + } + + [Fact] + [Trait("AdaptiveComponent", "AdaptiveComponentToNative")] + public async Task AdaptiveComponentAdaptiveComponentToNative() + { + await SpeckleToNative(AssertUtils.AdaptiveComponentEqual, null); + } + + [Fact] + [Trait("AdaptiveComponent", "AdaptiveComponentSelection")] + public async Task AdaptiveComponentAdaptiveComponentSelectionToNative() + { + await SelectionToNative(AssertUtils.AdaptiveComponentEqual, null); + } + + } + + public class BeamBeamFixture : SpeckleConversionFixture + { + public override string Category => "Beam"; + public override string TestName => "Beam"; + + public BeamBeamFixture() : base() + { + } + } + + public class BeamBeamTests : SpeckleConversionTest, IClassFixture + { + public BeamBeamTests(BeamBeamFixture fixture) : base(fixture) + { + } + + [Fact] + [Trait("Beam", "BeamToSpeckle")] + public async Task BeamBeamToSpeckle() + { + await NativeToSpeckle(); + } + + [Fact] + [Trait("Beam", "BeamToNative")] + public async Task BeamBeamToNative() + { + await SpeckleToNative(AssertUtils.FamilyInstanceEqual, null); + } + + [Fact] + [Trait("Beam", "BeamSelection")] + public async Task BeamBeamSelectionToNative() + { + await SelectionToNative(AssertUtils.FamilyInstanceEqual, null); + } + + [Fact] + [Trait("Beam", "BeamToNativeUpdates")] + public async Task BeamBeamToNativeUpdates() + { + await SpeckleToNativeUpdates(AssertUtils.FamilyInstanceEqual, null); + } + + } + + public class ColumnColumnFixture : SpeckleConversionFixture + { + public override string Category => "Column"; + public override string TestName => "Column"; + + public ColumnColumnFixture() : base() + { + } + } + + public class ColumnColumnTests : SpeckleConversionTest, IClassFixture + { + public ColumnColumnTests(ColumnColumnFixture fixture) : base(fixture) + { + } + + [Fact] + [Trait("Column", "ColumnToSpeckle")] + public async Task ColumnColumnToSpeckle() + { + await NativeToSpeckle(); + } + + [Fact] + [Trait("Column", "ColumnToNative")] + public async Task ColumnColumnToNative() + { + await SpeckleToNative(AssertUtils.FamilyInstanceEqual, null); + } + + [Fact] + [Trait("Column", "ColumnSelection")] + public async Task ColumnColumnSelectionToNative() + { + await SelectionToNative(AssertUtils.FamilyInstanceEqual, null); + } + + [Fact] + [Trait("Column", "ColumnToNativeUpdates")] + public async Task ColumnColumnToNativeUpdates() + { + await SpeckleToNativeUpdates(AssertUtils.FamilyInstanceEqual, null); + } + + } + + public class CurveCurveFixture : SpeckleConversionFixture + { + public override string Category => "Curve"; + public override string TestName => "Curve"; + + public CurveCurveFixture() : base() + { + } + } + + public class CurveCurveTests : SpeckleConversionTest, IClassFixture + { + public CurveCurveTests(CurveCurveFixture fixture) : base(fixture) + { + } + + [Fact] + [Trait("Curve", "CurveToSpeckle")] + public async Task CurveCurveToSpeckle() + { + await NativeToSpeckle(); + } + + [Fact] + [Trait("Curve", "CurveToNative")] + public async Task CurveCurveToNative() + { + await SpeckleToNative(AssertUtils.CurveEqual, null); + } + + [Fact] + [Trait("Curve", "CurveSelection")] + public async Task CurveCurveSelectionToNative() + { + await SelectionToNative(AssertUtils.CurveEqual, null); + } + + } + + public class DirectShapeDirectShapeFixture : SpeckleConversionFixture + { + public override string Category => "DirectShape"; + public override string TestName => "DirectShape"; + + public DirectShapeDirectShapeFixture() : base() + { + } + } + + public class DirectShapeDirectShapeTests : SpeckleConversionTest, IClassFixture + { + public DirectShapeDirectShapeTests(DirectShapeDirectShapeFixture fixture) : base(fixture) + { + } + + [Fact] + [Trait("DirectShape", "DirectShapeToSpeckle")] + public async Task DirectShapeDirectShapeToSpeckle() + { + await NativeToSpeckle(); + } + + [Fact] + [Trait("DirectShape", "DirectShapeToNative")] + public async Task DirectShapeDirectShapeToNative() + { + await SpeckleToNative(AssertUtils.DirectShapeEqual, null); + } + + [Fact] + [Trait("DirectShape", "DirectShapeSelection")] + public async Task DirectShapeDirectShapeSelectionToNative() + { + await SelectionToNative(AssertUtils.DirectShapeEqual, null); + } + + } + + public class FamilyInstanceFamilyInstanceFixture : SpeckleConversionFixture + { + public override string Category => "FamilyInstance"; + public override string TestName => "FamilyInstance"; + + public FamilyInstanceFamilyInstanceFixture() : base() + { + } + } + + public class FamilyInstanceFamilyInstanceTests : SpeckleConversionTest, IClassFixture + { + public FamilyInstanceFamilyInstanceTests(FamilyInstanceFamilyInstanceFixture fixture) : base(fixture) + { + } + + [Fact] + [Trait("FamilyInstance", "FamilyInstanceToSpeckle")] + public async Task FamilyInstanceFamilyInstanceToSpeckle() + { + await NativeToSpeckle(); + } + + [Fact] + [Trait("FamilyInstance", "FamilyInstanceToNative")] + public async Task FamilyInstanceFamilyInstanceToNative() + { + await SpeckleToNative(AssertUtils.NestedEqual, null); + } + + [Fact] + [Trait("FamilyInstance", "FamilyInstanceSelection")] + public async Task FamilyInstanceFamilyInstanceSelectionToNative() + { + await SelectionToNative(AssertUtils.NestedEqual, null); + } + + [Fact] + [Trait("FamilyInstance", "FamilyInstanceToNativeUpdates")] + public async Task FamilyInstanceFamilyInstanceToNativeUpdates() + { + await SpeckleToNativeUpdates(AssertUtils.NestedEqual, null); + } + + } + + public class FloorFloorFixture : SpeckleConversionFixture + { + public override string Category => "Floor"; + public override string TestName => "Floor"; + + public FloorFloorFixture() : base() + { + } + } + + public class FloorFloorTests : SpeckleConversionTest, IClassFixture + { + public FloorFloorTests(FloorFloorFixture fixture) : base(fixture) + { + } + + [Fact] + [Trait("Floor", "FloorToSpeckle")] + public async Task FloorFloorToSpeckle() + { + await NativeToSpeckle(); + } + + [Fact] + [Trait("Floor", "FloorToNative")] + public async Task FloorFloorToNative() + { + await SpeckleToNative(null, AssertUtils.FloorEqual); + } + + [Fact] + [Trait("Floor", "FloorSelection")] + public async Task FloorFloorSelectionToNative() + { + await SelectionToNative(null, AssertUtils.FloorEqual); + } + + } + + public class OpeningOpeningFixture : SpeckleConversionFixture + { + public override string Category => "Opening"; + public override string TestName => "Opening"; + + public OpeningOpeningFixture() : base() + { + } + } + + public class OpeningOpeningTests : SpeckleConversionTest, IClassFixture + { + public OpeningOpeningTests(OpeningOpeningFixture fixture) : base(fixture) + { + } + + [Fact] + [Trait("Opening", "OpeningToSpeckle")] + public async Task OpeningOpeningToSpeckle() + { + await NativeToSpeckle(); + } + + [Fact] + [Trait("Opening", "OpeningToNative")] + public async Task OpeningOpeningToNative() + { + await SpeckleToNative(AssertUtils.OpeningEqual, null); + } + + [Fact] + [Trait("Opening", "OpeningSelection")] + public async Task OpeningOpeningSelectionToNative() + { + await SelectionToNative(AssertUtils.OpeningEqual, null); + } + + } + + public class RoofRoofFixture : SpeckleConversionFixture + { + public override string Category => "Roof"; + public override string TestName => "Roof"; + + public RoofRoofFixture() : base() + { + } + } + + public class RoofRoofTests : SpeckleConversionTest, IClassFixture + { + public RoofRoofTests(RoofRoofFixture fixture) : base(fixture) + { + } + + [Fact] + [Trait("Roof", "RoofToSpeckle")] + public async Task RoofRoofToSpeckle() + { + await NativeToSpeckle(); + } + + [Fact] + [Trait("Roof", "RoofToNative")] + public async Task RoofRoofToNative() + { + await SpeckleToNative(AssertUtils.RoofEqual, null); + } + + [Fact] + [Trait("Roof", "RoofSelection")] + public async Task RoofRoofSelectionToNative() + { + await SelectionToNative(AssertUtils.RoofEqual, null); + } + + } + + public class RoomRoomFixture : SpeckleConversionFixture + { + public override string Category => "Room"; + public override string TestName => "Room"; + + public RoomRoomFixture() : base() + { + } + } + + public class RoomRoomTests : SpeckleConversionTest, IClassFixture + { + public RoomRoomTests(RoomRoomFixture fixture) : base(fixture) + { + } + + [Fact] + [Trait("Room", "RoomToSpeckle")] + public async Task RoomRoomToSpeckle() + { + await NativeToSpeckle(); + } + + } + + public class ScheduleScheduleFixture : SpeckleConversionFixture + { + public override string Category => "Schedule"; + public override string TestName => "Schedule"; + + public ScheduleScheduleFixture() : base() + { + } + } + + public class ScheduleScheduleTests : SpeckleConversionTest, IClassFixture + { + public ScheduleScheduleTests(ScheduleScheduleFixture fixture) : base(fixture) + { + } + + [Fact] + [Trait("Schedule", "ScheduleToSpeckle")] + public async Task ScheduleScheduleToSpeckle() + { + await NativeToSpeckle(); + } + + [Fact] + [Trait("Schedule", "ScheduleToNative")] + public async Task ScheduleScheduleToNative() + { + await SpeckleToNative(null, AssertUtils.ScheduleEqual); + } + + [Fact] + [Trait("Schedule", "ScheduleSelection")] + public async Task ScheduleScheduleSelectionToNative() + { + await SelectionToNative(null, AssertUtils.ScheduleEqual); + } + + [Fact] + [Trait("Schedule", "ScheduleToNativeUpdates")] + public async Task ScheduleScheduleToNativeUpdates() + { + await SpeckleToNativeUpdates(null, AssertUtils.ScheduleEqual); + } + + } + + public class WallWallFixture : SpeckleConversionFixture + { + public override string Category => "Wall"; + public override string TestName => "Wall"; + + public WallWallFixture() : base() + { + } + } + + public class WallWallTests : SpeckleConversionTest, IClassFixture + { + public WallWallTests(WallWallFixture fixture) : base(fixture) + { + } + + [Fact] + [Trait("Wall", "WallToSpeckle")] + public async Task WallWallToSpeckle() + { + await NativeToSpeckle(); + } + + [Fact] + [Trait("Wall", "WallToNative")] + public async Task WallWallToNative() + { + await SpeckleToNative(AssertUtils.WallEqual, null); + } + + [Fact] + [Trait("Wall", "WallSelection")] + public async Task WallWallSelectionToNative() + { + await SelectionToNative(AssertUtils.WallEqual, null); + } + + [Fact] + [Trait("Wall", "WallToNativeUpdates")] + public async Task WallWallToNativeUpdates() + { + await SpeckleToNativeUpdates(AssertUtils.WallEqual, null); + } + + } + + public class WireWireFixture : SpeckleConversionFixture + { + public override string Category => "Wire"; + public override string TestName => "Wire"; + + public WireWireFixture() : base() + { + } + } + + public class WireWireTests : SpeckleConversionTest, IClassFixture + { + public WireWireTests(WireWireFixture fixture) : base(fixture) + { + } + + [Fact] + [Trait("Wire", "WireToSpeckle")] + public async Task WireWireToSpeckle() + { + await NativeToSpeckle(); + } + + [Fact] + [Trait("Wire", "WireToNative")] + public async Task WireWireToNative() + { + await SpeckleToNative(AssertUtils.WireEqual, null); + } + + [Fact] + [Trait("Wire", "WireSelection")] + public async Task WireWireSelectionToNative() + { + await SelectionToNative(AssertUtils.WireEqual, null); + } + + } + } diff --git a/Objects/Converters/ConverterRhinoGh/ConverterRhinoGhShared/ConverterRhinoGh.Utils.cs b/Objects/Converters/ConverterRhinoGh/ConverterRhinoGhShared/ConverterRhinoGh.Utils.cs index 47d0c30d3d..9994577428 100644 --- a/Objects/Converters/ConverterRhinoGh/ConverterRhinoGhShared/ConverterRhinoGh.Utils.cs +++ b/Objects/Converters/ConverterRhinoGh/ConverterRhinoGhShared/ConverterRhinoGh.Utils.cs @@ -82,6 +82,11 @@ public int GetMaterialIndex(string name) private string GetCommitInfo() { + if (Doc.Notes == null) + { + return "Unknown commit"; + } + var segments = Doc.Notes.Split(new[] { "%%%" }, StringSplitOptions.None).ToList(); return segments.Count > 1 ? segments[1] : "Unknown commit"; } diff --git a/Objects/Objects/BuiltElements/Revit/RevitBrace.cs b/Objects/Objects/BuiltElements/Revit/RevitBrace.cs index dc92fbf296..ce5966afc6 100644 --- a/Objects/Objects/BuiltElements/Revit/RevitBrace.cs +++ b/Objects/Objects/BuiltElements/Revit/RevitBrace.cs @@ -42,7 +42,7 @@ public RevitBrace( string family, string type, [SchemaMainParam] ICurve baseLine, - Level level, + Level? level, List? parameters = null ) : this(family, type, baseLine, level, null, null, parameters: parameters) { } diff --git a/Objects/Objects/GIS/CRS.cs b/Objects/Objects/GIS/CRS.cs new file mode 100644 index 0000000000..39f9e0b327 --- /dev/null +++ b/Objects/Objects/GIS/CRS.cs @@ -0,0 +1,14 @@ +using Speckle.Core.Models; + +namespace Objects.GIS; + +public class CRS : Base +{ + public string? name { get; set; } + public string? authority_id { get; set; } + public string? wkt { get; set; } + public string? units_native { get; set; } + public float? offset_x { get; set; } + public float? offset_y { get; set; } + public float? rotation { get; set; } +} diff --git a/Objects/Objects/GIS/GisMultipatchGeometry.cs b/Objects/Objects/GIS/GisMultipatchGeometry.cs new file mode 100644 index 0000000000..3833785e9d --- /dev/null +++ b/Objects/Objects/GIS/GisMultipatchGeometry.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; +using Speckle.Core.Models; + +namespace Objects.GIS; + +public class GisMultipatchGeometry : Base +{ + public string units { get; set; } + public List faces { get; set; } + public List vertices { get; set; } + public List? colors { get; set; } + + public GisMultipatchGeometry() + { + faces = new List(); + vertices = new List(); + } +} diff --git a/Objects/Objects/GIS/GisPolygonGeometry.cs b/Objects/Objects/GIS/GisPolygonGeometry.cs new file mode 100644 index 0000000000..3937f1a64d --- /dev/null +++ b/Objects/Objects/GIS/GisPolygonGeometry.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; +using Objects.Geometry; +using Speckle.Core.Models; + +namespace Objects.GIS; + +public class PolygonGeometry : Base +{ + public string units { get; set; } + public Polyline boundary { get; set; } + public List voids { get; set; } + + public PolygonGeometry() + { + voids = new List(); + } +} diff --git a/Objects/Objects/GIS/GisPolygonGeometry3d.cs b/Objects/Objects/GIS/GisPolygonGeometry3d.cs new file mode 100644 index 0000000000..93613c89f9 --- /dev/null +++ b/Objects/Objects/GIS/GisPolygonGeometry3d.cs @@ -0,0 +1,3 @@ +namespace Objects.GIS; + +public class PolygonGeometry3d : PolygonGeometry { } diff --git a/Objects/Objects/GIS/GisTopography.cs b/Objects/Objects/GIS/GisTopography.cs index 37ac571883..8a34ac706a 100644 --- a/Objects/Objects/GIS/GisTopography.cs +++ b/Objects/Objects/GIS/GisTopography.cs @@ -1,17 +1,3 @@ -using System.Collections.Generic; -using Objects.BuiltElements; - namespace Objects.GIS; -public class GisTopography : Topography -{ - public int band_count { get; set; } - public List band_names { get; set; } - public float x_origin { get; set; } - public float y_origin { get; set; } - public int x_size { get; set; } - public int y_size { get; set; } - public float x_resolution { get; set; } - public float y_resolution { get; set; } - public List noDataValue { get; set; } -} +public class GisTopography : RasterElement { } diff --git a/Objects/Objects/GIS/NonGeometryElement.cs b/Objects/Objects/GIS/NonGeometryElement.cs new file mode 100644 index 0000000000..c5e0dc5714 --- /dev/null +++ b/Objects/Objects/GIS/NonGeometryElement.cs @@ -0,0 +1,10 @@ +using System; +using Speckle.Core.Models; + +namespace Objects.GIS; + +[Obsolete("NonGeometryElement was replaced by a more generic class, \"GisFeature\", which contains more information")] +public class NonGeometryElement : Base +{ + public Base? attributes { get; set; } +} diff --git a/Objects/Objects/GIS/PolygonElement.cs b/Objects/Objects/GIS/PolygonElement.cs index 54b63601b3..8905a7cf5d 100644 --- a/Objects/Objects/GIS/PolygonElement.cs +++ b/Objects/Objects/GIS/PolygonElement.cs @@ -1,8 +1,10 @@ +using System; using System.Collections.Generic; using Speckle.Core.Models; namespace Objects.GIS; +[Obsolete("PolygonElement was replaced by a more generic class, \"GisFeature\", which contains more information")] public class PolygonElement : Base { [DetachProperty] diff --git a/Objects/Objects/GIS/RasterElement.cs b/Objects/Objects/GIS/RasterElement.cs new file mode 100644 index 0000000000..63befed1ac --- /dev/null +++ b/Objects/Objects/GIS/RasterElement.cs @@ -0,0 +1,52 @@ +using System.Collections.Generic; +using Objects.Geometry; +using Speckle.Core.Models; + +namespace Objects.GIS; + +public class RasterElement : Base +{ + public int band_count { get; set; } + public List band_names { get; set; } + public float x_origin { get; set; } + public float y_origin { get; set; } + public int x_size { get; set; } + public int y_size { get; set; } + public float x_resolution { get; set; } + public float y_resolution { get; set; } + public List noDataValue { get; set; } + + [DetachProperty] + public List displayValue { get; set; } + + public RasterElement() + { + displayValue = new List(); + band_names = new List(); + noDataValue = new List(); + } + + public RasterElement( + int bandCount, + List bandNames, + float xOrigin, + float yOrigin, + int xSize, + int ySize, + float xResolution, + float yResolution, + List noDataValue + ) + { + displayValue = new List(); + band_count = bandCount; + band_names = bandNames; + x_origin = xOrigin; + y_origin = yOrigin; + x_size = xSize; + y_size = ySize; + x_resolution = xResolution; + y_resolution = yResolution; + this.noDataValue = noDataValue; + } +} diff --git a/Objects/Objects/GIS/RasterLayer.cs b/Objects/Objects/GIS/RasterLayer.cs new file mode 100644 index 0000000000..b7330d4986 --- /dev/null +++ b/Objects/Objects/GIS/RasterLayer.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; +using Speckle.Core.Models; + +namespace Objects.GIS; + +public class RasterLayer : Collection +{ + public CRS? crs { get; set; } + public string? units { get; set; } + public CRS? rasterCrs { get; set; } + public string? geomType { get; set; } + public Dictionary? renderer { get; set; } + + public RasterLayer() + { + collectionType = "RasterLayer"; + } +} diff --git a/Objects/Objects/GIS/VectorLayer.cs b/Objects/Objects/GIS/VectorLayer.cs new file mode 100644 index 0000000000..618f95d870 --- /dev/null +++ b/Objects/Objects/GIS/VectorLayer.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; +using Speckle.Core.Models; + +namespace Objects.GIS; + +public class VectorLayer : Collection +{ + public CRS? crs { get; set; } + public string? units { get; set; } + public Base attributes { get; set; } + public string? geomType { get; set; } + public string? nativeGeomType { get; set; } + public Dictionary? renderer { get; set; } + + public VectorLayer() + { + collectionType = "VectorLayer"; + attributes = new Base(); + } +} diff --git a/Objects/Objects/Geometry/Autocad/AutocadPolycurve.cs b/Objects/Objects/Geometry/Autocad/AutocadPolycurve.cs new file mode 100644 index 0000000000..a88c5147df --- /dev/null +++ b/Objects/Objects/Geometry/Autocad/AutocadPolycurve.cs @@ -0,0 +1,86 @@ +using System.Collections.Generic; +using Speckle.Core.Models; + +namespace Objects.Geometry.Autocad; + +/// +/// A curve that is comprised of line, arc and/or curve segments, representing the Autocad Polyline, Polyline2d, and Polyline3d classes. +/// +/// +/// and types will have only s and s in . +/// type will have only s in . +/// type will only have s in . +/// , , , and types will have only a single s in . +/// +public class AutocadPolycurve : Polycurve +{ + /// + /// Constructs a new empty instance. + /// + public AutocadPolycurve() { } + + /// + /// Gets or sets the raw coordinates of the vertices. + /// + /// + /// For Polylines, these are xy coordinates in the Object Coordinate System (OCS)/>. + /// For Polyline2d and Polyline3d types, these are xyz coordinates in the Global Coordinate System. fml. + /// + [DetachProperty, Chunkable(31250)] + public List value { get; set; } = new(); + + /// + /// The bulge factor at each vertex. Should be null for Polyline3d. + /// + /// + /// The bulge factor is used to indicate how much of an arc segment is present at this vertex. + /// The bulge factor is the tangent of one fourth the included angle for an arc segment, + /// made negative if the arc goes clockwise from the start point to the endpoint. + /// A bulge of 0 indicates a straight segment, and a bulge of 1 is a semicircle. + /// + public List? bulges { get; set; } + + /// + /// The tangent in radians at each vertex. Should be null for Polyline and Polyline3d. + /// + public List? tangents { get; set; } + + /// + /// The normal of the plane of the Autocad Polyline or Polyline2d. Should be null for Polyline3d. + /// + public Vector? normal { get; set; } + + /// + /// The distance from the plane to the origin of the Autocad Polyline or Polyline2d. Should be null for Polyline3d. + /// + public double? elevation { get; set; } + + public AutocadPolyType polyType { get; set; } +} + +/// +/// Represents the type of a Autocad Polyline. +/// +public enum AutocadPolyType +{ + /// Polyline type is not known + Unknown, + + /// Polyline type is the Autocad Polyline class + Light, + + Simple2d, + + Simple3d, + + /// The Autocad Polyline2d fit curve poly type. Constructed with pairs of arcs with continuous tangents. + FitCurve2d, + + CubicSpline2d, + + CubicSpline3d, + + QuadSpline2d, + + QuadSpline3d, +} diff --git a/Objects/Objects/Objects.csproj b/Objects/Objects/Objects.csproj index 62cb80c226..b4a703d8a8 100644 --- a/Objects/Objects/Objects.csproj +++ b/Objects/Objects/Objects.csproj @@ -15,22 +15,22 @@ true - - $(WarningsNotAsErrors); + + $(NoWarn); CA1008; CA1024; CA1034; CA1065; CA1708; CA1711; CA1716; CA1724; CA1725; - CA1819; + CA1819; CS8618; CA2201; CA2225; CS0659; CS0661; CS0728; CS8618; IDE0041; IDE0060; IDE1006; - + - + - + - + - + diff --git a/build.ps1 b/build.ps1 new file mode 100644 index 0000000000..e7638ce844 --- /dev/null +++ b/build.ps1 @@ -0,0 +1,3 @@ +$ErrorActionPreference = "Stop"; + +dotnet run --project build/build.csproj -- $args \ No newline at end of file diff --git a/build.sh b/build.sh new file mode 100755 index 0000000000..f75ab37619 --- /dev/null +++ b/build.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -euo pipefail + +dotnet run --project Build/Build.csproj -- "$@" diff --git a/global.json b/global.json index 08f229ce09..c09ced8c12 100644 --- a/global.json +++ b/global.json @@ -1,7 +1,7 @@ { "sdk": { - "version": "7.0.0", - "rollForward": "latestMajor", + "version": "8.0.200", + "rollForward": "latestPatch", "allowPrerelease": false } }