diff --git a/.circleci/config.yml b/.circleci/config.yml index f623d0046f..029f1f98bb 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -6,9 +6,8 @@ setup: true # the path-filtering orb is required to continue a pipeline based on # the path of an updated fileset orbs: - path-filtering: circleci/path-filtering@0.1.3 - continuation: circleci/continuation@0.3.1 - python: circleci/python@2.0.3 + continuation: circleci/continuation@1.0.0 + python: circleci/python@2.1.1 jobs: setup: diff --git a/.circleci/scripts/config-template.yml b/.circleci/scripts/config-template.yml index f64173dbb2..cf4a02f4d4 100644 --- a/.circleci/scripts/config-template.yml +++ b/.circleci/scripts/config-template.yml @@ -2,12 +2,8 @@ version: 2.1 orbs: win: circleci/windows@5.0.0 - aws-s3: circleci/aws-s3@2.0.0 - codecov: codecov/codecov@3.2.2 - wait-for: cobli/wait-for@0.0.2 discord: antonioned/discord@0.1.0 - docker: circleci/docker@2.2.0 - macos: circleci/macos@2.4.1 + macos: circleci/macos@2.5.2 # The main workflows for our monorepo pipeline. workflows: @@ -74,14 +70,14 @@ commands: jobs: # Each project will have individual jobs for each specific task it has to execute (build, release...) build-sdk: docker: - - image: cimg/base:2021.01 + - image: cimg/base:2024.07 steps: - cached-checkout - run: name: Install dotnet command: | curl -sSL https://dot.net/v1/dotnet-install.sh | bash /dev/stdin --channel sts - curl -sSL https://dot.net/v1/dotnet-install.sh | bash /dev/stdin --version 8.0.204 + curl -sSL https://dot.net/v1/dotnet-install.sh | bash /dev/stdin --version 8.0.206 $HOME/.dotnet/dotnet --version - run: name: Enforce formatting @@ -121,7 +117,7 @@ jobs: # Each project will have individual jobs for each specific task it has to test-core: machine: - image: ubuntu-2204:2023.02.1 + image: ubuntu-2204:2024.05.1 resource_class: large steps: - cached-checkout @@ -129,7 +125,7 @@ jobs: # Each project will have individual jobs for each specific task it has to name: Install dotnet command: | curl -sSL https://dot.net/v1/dotnet-install.sh | bash /dev/stdin --channel sts - curl -sSL https://dot.net/v1/dotnet-install.sh | bash /dev/stdin --version 8.0.204 + curl -sSL https://dot.net/v1/dotnet-install.sh | bash /dev/stdin --version 8.0.206 $HOME/.dotnet/dotnet --version - run: name: Startup the Speckle Server @@ -203,17 +199,33 @@ jobs: # Each project will have individual jobs for each specific task it has to name: Exit if External PR shell: bash.exe command: if [ "$CIRCLE_PR_REPONAME" ]; then circleci-agent step halt; fi - - unless: # Build installers unsigned on non-tagged builds - condition: << pipeline.git.tag >> + - unless: # Build Unsigned Installers for untagged, or wip/alpha/feature/etc. tags (non-stable, non-rc) + condition: + and: + - << pipeline.git.tag >> + - not: + matches: + pattern: "^.*-(?!rc).*$" + value: << pipeline.git.tag >> steps: - run: - name: Build Installer + name: Build Installer Unsigned command: speckle-sharp-ci-tools\InnoSetup\ISCC.exe speckle-sharp-ci-tools\%SLUG%.iss /Sbyparam=$p shell: cmd.exe #does not work in powershell environment: SLUG: << parameters.slug >> - - when: # Setup certificates and build installers signed for tagged builds - condition: << pipeline.git.tag >> + - persist_to_workspace: + root: ./ + paths: + - speckle-sharp-ci-tools/Installers + - when: # Setup certificates and build installers signed for tagged builds that aren't prerelease + condition: + and: + - << pipeline.git.tag >> + - not: + matches: + pattern: "^.*-(?!rc).*$" + value: << pipeline.git.tag >> steps: - run: name: "Digicert Signing Manager Setup" @@ -232,7 +244,7 @@ jobs: # Each project will have individual jobs for each specific task it has to command: | & $env:SSM\smksp_cert_sync.exe - run: - name: Build Installer + name: Build Installer Signed command: speckle-sharp-ci-tools\InnoSetup\ISCC.exe speckle-sharp-ci-tools\%SLUG%.iss /Sbyparam=$p /DSIGN_INSTALLER /DCODE_SIGNING_CERT_FINGERPRINT=%SM_CODE_SIGNING_CERT_SHA1_HASH% shell: cmd.exe #does not work in powershell environment: @@ -312,7 +324,7 @@ jobs: # Each project will have individual jobs for each specific task it has to name: Install dotnet command: | curl -sSL https://dot.net/v1/dotnet-install.sh | bash /dev/stdin --channel sts - curl -sSL https://dot.net/v1/dotnet-install.sh | bash /dev/stdin --version 8.0.204 + curl -sSL https://dot.net/v1/dotnet-install.sh | bash /dev/stdin --version 8.0.206 $HOME/.dotnet/dotnet --version $HOME/.dotnet/dotnet --list-runtimes @@ -341,6 +353,28 @@ jobs: # Each project will have individual jobs for each specific task it has to msbuild << parameters.slnname >>/<< parameters.slnname >>.sln /r /p:Configuration='<< parameters.build-config >>' /p:IsDesktopBuild=false /p:Version=$SEMVER /p:FileVersion=$VERSION environment: WORKFLOW_NUM: << pipeline.number >> + - unless: + condition: << parameters.build-with-mono >> + steps: + - run: + name: Publish x64 and arm64 + command: | + TAG=$(if [ "${CIRCLE_TAG}" ]; then echo $CIRCLE_TAG; else echo "2.0.999"; fi;) + SEMVER=$(echo "$TAG" | sed -e 's/\/[a-zA-Z-]*//') + VER=$(echo "$SEMVER" | sed -e 's/-.*//') + VERSION=$(echo $VER.$WORKFLOW_NUM) + $HOME/.dotnet/dotnet publish << parameters.slnname >>/<< parameters.projname >>/<< parameters.projname >>.csproj -c Release -r osx-arm64 --self-contained /p:IsDesktopBuild=false /p:Version=$SEMVER /p:FileVersion=$VERSION + $HOME/.dotnet/dotnet publish << parameters.slnname >>/<< parameters.projname >>/<< parameters.projname >>.csproj -c Release -r osx-x64 --self-contained /p:IsDesktopBuild=false /p:Version=$SEMVER /p:FileVersion=$VERSION + environment: + WORKFLOW_NUM: << pipeline.number >> + # Create installer + - run: + name: Exit if External PR + command: if [ "$CIRCLE_PR_REPONAME" ]; then circleci-agent step halt; fi + # Zip files for installer + - when: + condition: << parameters.build-with-mono >> + steps: # Compress build files - run: name: Zip Objects Kit files @@ -362,34 +396,25 @@ jobs: # Each project will have individual jobs for each specific task it has to condition: << parameters.build-with-mono >> steps: - run: - name: Publish x64 and arm64 + name: Create app bundles command: | - TAG=$(if [ "${CIRCLE_TAG}" ]; then echo $CIRCLE_TAG; else echo "2.0.999"; fi;) - SEMVER=$(echo "$TAG" | sed -e 's/\/[a-zA-Z-]*//') - VER=$(echo "$SEMVER" | sed -e 's/-.*//') - VERSION=$(echo $VER.$WORKFLOW_NUM) - $HOME/.dotnet/dotnet publish << parameters.slnname >>/<< parameters.projname >>/<< parameters.projname >>.csproj -c Release -r osx-arm64 --self-contained /p:IsDesktopBuild=false /p:Version=$SEMVER /p:FileVersion=$VERSION - $HOME/.dotnet/dotnet publish << parameters.slnname >>/<< parameters.projname >>/<< parameters.projname >>.csproj -c Release -r osx-x64 --self-contained /p:IsDesktopBuild=false /p:Version=$SEMVER /p:FileVersion=$VERSION - environment: - WORKFLOW_NUM: << pipeline.number >> + sh ./speckle-sharp-ci-tools/Mac/AppBundle/bundle.sh ./<< parameters.slnname >>/<< parameters.slnname >>/bin/Release/net6.0/osx-arm64/publish/ ./<< parameters.slnname >>/<< parameters.slnname >>/Info.plist ./<< parameters.slnname >>/<< parameters.slnname >>/Assets/icon-mac.icns ./<< parameters.slnname >>/<< parameters.slnname >>/bin/Release/net6.0/osx-arm64/<< parameters.slnname >> + sh ./speckle-sharp-ci-tools/Mac/AppBundle/bundle.sh ./<< parameters.slnname >>/<< parameters.slnname >>/bin/Release/net6.0/osx-x64/publish/ ./<< parameters.slnname >>/<< parameters.slnname >>/Info.plist ./<< parameters.slnname >>/<< parameters.slnname >>/Assets/icon-mac.icns ./<< parameters.slnname >>/<< parameters.slnname >>/bin/Release/net6.0/osx-x64/<< parameters.slnname >> - run: name: Zip Connector files command: | - cd "<< parameters.slnname >>/<< parameters.slnname >>/bin/Release/net6.0/osx-arm64/publish" - zip -r << parameters.slug >>-mac-arm64.zip "./" - cd "../../osx-x64/publish" - zip -r << parameters.slug >>-mac-x64.zip "./" + cd "<< parameters.slnname >>/<< parameters.slnname >>/bin/Release/net6.0/osx-arm64/" + zip -r << parameters.slug >>-mac-arm64.zip "./<< parameters.slnname >>.app" + cd "../osx-x64/" + zip -r << parameters.slug >>-mac-x64.zip "./<< parameters.slnname >>.app" # Copy installer files - run: name: Copy files to installer command: | mkdir -p speckle-sharp-ci-tools/Mac/<< parameters.installername >>/.installationFiles/ - cp << parameters.slnname >>/<< parameters.slnname >>/bin/Release/net6.0/osx-arm64/publish/<< parameters.slug >>-mac-arm64.zip speckle-sharp-ci-tools/Mac/<>/.installationFiles - cp << parameters.slnname >>/<< parameters.slnname >>/bin/Release/net6.0/osx-x64/publish/<< parameters.slug >>-mac-x64.zip speckle-sharp-ci-tools/Mac/<>/.installationFiles - # Create installer - - run: - name: Exit if External PR - command: if [ "$CIRCLE_PR_REPONAME" ]; then circleci-agent step halt; fi + cp << parameters.slnname >>/<< parameters.slnname >>/bin/Release/net6.0/osx-arm64/<< parameters.slug >>-mac-arm64.zip speckle-sharp-ci-tools/Mac/<>/.installationFiles + cp << parameters.slnname >>/<< parameters.slnname >>/bin/Release/net6.0/osx-x64/<< parameters.slug >>-mac-x64.zip speckle-sharp-ci-tools/Mac/<>/.installationFiles + - run: name: Build Mac installer command: ~/.dotnet/dotnet publish speckle-sharp-ci-tools/Mac/<>/<>.sln -r osx-x64 -c Release @@ -511,7 +536,7 @@ jobs: # Each project will have individual jobs for each specific task it has to get-ci-tools: # Clones our ci tools and persists them to the workspace docker: - - image: cimg/base:2021.01 + - image: cimg/base:2024.07 steps: - add_ssh_keys: fingerprints: @@ -641,7 +666,7 @@ jobs: # Each project will have individual jobs for each specific task it has to deploy-connectors: # Uploads all installers found to S3 docker: - - image: cimg/base:2021.01 + - image: cimg/base:2024.07 steps: - run: name: Proceed to deploy @@ -649,7 +674,7 @@ jobs: # Each project will have individual jobs for each specific task it has to notify-deploy: docker: - - image: cimg/base:2021.01 + - image: cimg/base:2024.07 steps: - discord/status: mentions: "1067457311980933140" diff --git a/Automate/Speckle.Automate.Sdk/AutomationContext.cs b/Automate/Speckle.Automate.Sdk/AutomationContext.cs index 5844df540a..bacf101565 100644 --- a/Automate/Speckle.Automate.Sdk/AutomationContext.cs +++ b/Automate/Speckle.Automate.Sdk/AutomationContext.cs @@ -3,6 +3,7 @@ using Speckle.Automate.Sdk.Schema; using Speckle.Automate.Sdk.Schema.Triggers; using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL.Models; using Speckle.Core.Credentials; using Speckle.Core.Logging; using Speckle.Core.Models; diff --git a/Automate/Speckle.Automate.Sdk/Speckle.Automate.Sdk.csproj b/Automate/Speckle.Automate.Sdk/Speckle.Automate.Sdk.csproj index 23712d34b6..3b9b37c95e 100644 --- a/Automate/Speckle.Automate.Sdk/Speckle.Automate.Sdk.csproj +++ b/Automate/Speckle.Automate.Sdk/Speckle.Automate.Sdk.csproj @@ -13,7 +13,7 @@ true - $(WarningsNotAsErrors);NU5104 + $(WarningsNotAsErrors);CS8618;CS0618; @@ -22,7 +22,7 @@ - + diff --git a/ConnectorArchicad/AddOn/Sources/AddOn/AddOnMain.cpp b/ConnectorArchicad/AddOn/Sources/AddOn/AddOnMain.cpp index 532f056af6..1fd3ac1daa 100644 --- a/ConnectorArchicad/AddOn/Sources/AddOn/AddOnMain.cpp +++ b/ConnectorArchicad/AddOn/Sources/AddOn/AddOnMain.cpp @@ -18,6 +18,7 @@ #include "Commands/GetElementBaseData.hpp" #include "Commands/GetGridElementData.hpp" #include "Commands/GetObjectData.hpp" +#include "Commands/GetOpeningData.hpp" #include "Commands/GetSlabData.hpp" #include "Commands/GetRoofData.hpp" #include "Commands/GetShellData.hpp" @@ -31,6 +32,7 @@ #include "Commands/CreateColumn.hpp" #include "Commands/CreateGridElement.hpp" #include "Commands/CreateObject.hpp" +#include "Commands/CreateOpening.hpp" #include "Commands/CreateRoof.hpp" #include "Commands/CreateSkylight.hpp" #include "Commands/CreateSlab.hpp" @@ -90,46 +92,41 @@ class AvaloniaProcessManager { private: GS::UniString GetPlatformSpecificExecutablePath () { -#if defined (macintosh) - static const char* FileName = "ConnectorArchicad"; -#else - static const char* FileName = "ConnectorArchicad.exe"; -#endif - static const char* FolderNameCommon = "Common"; - static const char* FolderName = "ConnectorArchicad"; - IO::Location ownFileLoc; auto err = ACAPI_GetOwnLocation (&ownFileLoc); if (err != NoError) { return ""; } +#if defined (macintosh) + static const char* ProductionConnector = "../../../Common/ConnectorArchicad/ConnectorArchicad.app/Contents/MacOS/ConnectorArchicad"; +#else + static const char* ProductionConnector = "../../../Common/ConnectorArchicad/ConnectorArchicad.exe"; +#endif + IO::Location location (ownFileLoc); - location.DeleteLastLocalName (); - location.DeleteLastLocalName (); - location.DeleteLastLocalName (); - location.AppendToLocal (IO::Name (FolderNameCommon)); - location.AppendToLocal (IO::Name (FolderName)); - location.AppendToLocal (IO::Name (FileName)); + location.AppendToLocal (IO::RelativeLocation (ProductionConnector)); bool exist (false); err = IO::fileSystem.Contains (location, &exist); if (err != NoError || !exist) { location = ownFileLoc; - location.DeleteLastLocalName (); - location.DeleteLastLocalName (); - location.DeleteLastLocalName (); - location.DeleteLastLocalName (); - location.AppendToLocal (IO::Name (FolderName)); - location.AppendToLocal (IO::Name ("bin")); +#if defined (macintosh) #ifdef DEBUG - location.AppendToLocal (IO::Name ("Debug")); + static const char* DevelopmentConnector = "../../../../ConnectorArchicad/bin/Debug/net6.0/ConnectorArchicad"; #else - location.AppendToLocal (IO::Name ("Release")); + static const char* DevelopmentConnector = "../../../../ConnectorArchicad/bin/Release/net6.0/ConnectorArchicad"; #endif - location.AppendToLocal (IO::Name ("net6.0")); - location.AppendToLocal (IO::Name (FileName)); +#else +#ifdef DEBUG + static const char* DevelopmentConnector = "../../../../ConnectorArchicad/bin/Debug/net6.0/ConnectorArchicad.exe"; +#else + static const char* DevelopmentConnector = "../../../../ConnectorArchicad/bin/Release/net6.0/ConnectorArchicad.exe"; +#endif +#endif + + location.AppendToLocal (IO::RelativeLocation (DevelopmentConnector)); } GS::UniString executableStr; @@ -200,6 +197,7 @@ static GSErrCode RegisterAddOnCommands () CHECKERROR (ACAPI_AddOnAddOnCommunication_InstallAddOnCommandHandler (NewOwned ())); CHECKERROR (ACAPI_AddOnAddOnCommunication_InstallAddOnCommandHandler (NewOwned ())); CHECKERROR (ACAPI_AddOnAddOnCommunication_InstallAddOnCommandHandler (NewOwned ())); + CHECKERROR (ACAPI_AddOnAddOnCommunication_InstallAddOnCommandHandler (NewOwned ())); CHECKERROR (ACAPI_AddOnAddOnCommunication_InstallAddOnCommandHandler (NewOwned ())); CHECKERROR (ACAPI_AddOnAddOnCommunication_InstallAddOnCommandHandler (NewOwned ())); CHECKERROR (ACAPI_AddOnAddOnCommunication_InstallAddOnCommandHandler (NewOwned ())); @@ -213,6 +211,7 @@ static GSErrCode RegisterAddOnCommands () CHECKERROR (ACAPI_AddOnAddOnCommunication_InstallAddOnCommandHandler (NewOwned ())); CHECKERROR (ACAPI_AddOnAddOnCommunication_InstallAddOnCommandHandler (NewOwned ())); CHECKERROR (ACAPI_AddOnAddOnCommunication_InstallAddOnCommandHandler (NewOwned ())); + CHECKERROR (ACAPI_AddOnAddOnCommunication_InstallAddOnCommandHandler (NewOwned ())); CHECKERROR (ACAPI_AddOnAddOnCommunication_InstallAddOnCommandHandler (NewOwned ())); CHECKERROR (ACAPI_AddOnAddOnCommunication_InstallAddOnCommandHandler (NewOwned ())); CHECKERROR (ACAPI_AddOnAddOnCommunication_InstallAddOnCommandHandler (NewOwned ())); diff --git a/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateCommand.cpp b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateCommand.cpp index b159461dd1..95f74c0232 100644 --- a/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateCommand.cpp +++ b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateCommand.cpp @@ -4,7 +4,6 @@ #include "CreateCommand.hpp" #include "LibpartImportManager.hpp" #include "ClassificationImportManager.hpp" -#include "APIHelper.hpp" #include "FieldNames.hpp" #include "OnExit.hpp" #include "ExchangeManager.hpp" @@ -124,8 +123,6 @@ GS::ObjectState CreateCommand::Execute (const GS::ObjectState& parameters, GS::P parameters.Get (GetFieldName (), objectStates); ACAPI_CallUndoableCommand (GetUndoableCommandName (), [&] () -> GSErrCode { - LibraryHelper helper (false); - GS::Array applicationObjects; AttributeManager* attributeManager = AttributeManager::GetInstance (); diff --git a/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateDoor.cpp b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateDoor.cpp index 381589b14a..baa597270d 100644 --- a/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateDoor.cpp +++ b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateDoor.cpp @@ -6,7 +6,6 @@ #include "RealNumber.h" #include "DGModule.hpp" #include "LibpartImportManager.hpp" -#include "APIHelper.hpp" #include "FieldNames.hpp" #include "OnExit.hpp" #include "ExchangeManager.hpp" diff --git a/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateGridElement.cpp b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateGridElement.cpp index 2b17b910a8..d18939ce1c 100644 --- a/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateGridElement.cpp +++ b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateGridElement.cpp @@ -1,7 +1,6 @@ #include "CreateGridElement.hpp" #include "ResourceIds.hpp" #include "ObjectState.hpp" -#include "APIHelper.hpp" #include "Utility.hpp" #include "Objects/Level.hpp" #include "Objects/Point.hpp" diff --git a/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateObject.cpp b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateObject.cpp index 7381415197..d94909765d 100644 --- a/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateObject.cpp +++ b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateObject.cpp @@ -1,7 +1,6 @@ #include "CreateObject.hpp" #include "APIMigrationHelper.hpp" -#include "APIHelper.hpp" #include "LibpartImportManager.hpp" #include "ResourceIds.hpp" #include "Utility.hpp" @@ -131,8 +130,6 @@ GS::ObjectState CreateObject::Execute (const GS::ObjectState& parameters, GS::Pr parameters.Get (FieldNames::MeshModels, meshModels); ACAPI_CallUndoableCommand (GetUndoableCommandName (), [&] () -> GSErrCode { - LibraryHelper helper (false); - AttributeManager* attributeManager = AttributeManager::GetInstance (); LibpartImportManager* libpartImportManager = LibpartImportManager::GetInstance (); for (ModelInfo meshModel : meshModels) { diff --git a/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateOpening.cpp b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateOpening.cpp new file mode 100644 index 0000000000..dcee4ceea9 --- /dev/null +++ b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateOpening.cpp @@ -0,0 +1,372 @@ +#include "CreateOpening.hpp" +#include "ResourceIds.hpp" +#include "ObjectState.hpp" +#include "Utility.hpp" +#include "Objects/Point.hpp" +#include "Objects/Vector.hpp" +#include "RealNumber.h" +#include "DGModule.hpp" +#include "LibpartImportManager.hpp" +#include "FieldNames.hpp" +#include "OnExit.hpp" +#include "ExchangeManager.hpp" +#include "TypeNameTables.hpp" +#include "Database.hpp" +#include "BM.hpp" + + +using namespace FieldNames; + + +namespace AddOnCommands +{ + + +GS::String CreateOpening::GetFieldName () const +{ + return FieldNames::Openings; +} + + +GS::UniString CreateOpening::GetUndoableCommandName () const +{ + return "CreateSpeckleOpening"; +} + + +GS::ErrCode CreateOpening::GetElementFromObjectState (const GS::ObjectState& os, + API_Element& element, + API_Element& elementMask, + API_ElementMemo& memo, + GS::UInt64& /*memoMask*/, + API_SubElement** /*marker*/, + AttributeManager& /*attributeManager*/, + LibpartImportManager& /*libpartImportManager*/, + GS::Array& log) const +{ + GS::ErrCode err = NoError; + + Utility::SetElementType (element.header, API_OpeningID); + + err = Utility::GetBaseElementData (element, &memo, nullptr, log); + if (err != NoError) + return err; + + err = GetElementBaseFromObjectState (os, element, elementMask); + if (err != NoError) + return err; + + if (!CheckEnvironment (os, element)) + return Error; + + if (os.Contains (Opening::FloorPlanDisplayMode)) { + GS::UniString floorPlanDisplayModeName; + os.Get (Opening::FloorPlanDisplayMode, floorPlanDisplayModeName); + + GS::Optional type = openingFloorPlanDisplayModeNames.FindValue (floorPlanDisplayModeName); + if (type.HasValue ()) { + element.opening.floorPlanParameters.floorPlanDisplayMode = type.Get (); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, floorPlanParameters.floorPlanDisplayMode); + } + } + + if (os.Contains (Opening::ConnectionMode)) { + GS::UniString connectionModeName; + os.Get (Opening::ConnectionMode, connectionModeName); + + GS::Optional type = openingFloorPlanConnectionModeNames.FindValue (connectionModeName); + if (type.HasValue ()) { + element.opening.floorPlanParameters.connectionMode = type.Get (); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, floorPlanParameters.connectionMode); + } + } + + if (os.Contains (Opening::CutSurfacesUseLineOfCutElements)) { + os.Get (Opening::CutSurfacesUseLineOfCutElements, element.opening.floorPlanParameters.cutSurfacesParameters.useLineOfCutElements); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, floorPlanParameters.cutSurfacesParameters.useLineOfCutElements); + } + + if (os.Contains (Opening::CutSurfacesLinePenIndex)) { + os.Get (Opening::CutSurfacesLinePenIndex, element.opening.floorPlanParameters.cutSurfacesParameters.linePenIndex); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, floorPlanParameters.cutSurfacesParameters.linePenIndex); + } + + GS::UniString attributeName; + if (os.Contains (Opening::CutSurfacesLineIndex)) { + + os.Get (Opening::CutSurfacesLineIndex, attributeName); + + if (!attributeName.IsEmpty ()) { + API_Attribute attrib; + BNZeroMemory (&attrib, sizeof (API_Attribute)); + attrib.header.typeID = API_LinetypeID; + CHCopyC (attributeName.ToCStr (), attrib.header.name); + + if (NoError == ACAPI_Attribute_Get (&attrib)) { + element.opening.floorPlanParameters.cutSurfacesParameters.lineIndex = attrib.header.index; + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, floorPlanParameters.cutSurfacesParameters.lineIndex); + } + } + } + + if (os.Contains (Opening::OutlinesStyle)) { + GS::UniString outlinesStyleName; + os.Get (Opening::OutlinesStyle, outlinesStyleName); + + GS::Optional type = openingOutlinesStyleNames.FindValue (outlinesStyleName); + if (type.HasValue ()) { + element.opening.floorPlanParameters.outlinesParameters.outlinesStyle = type.Get (); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, floorPlanParameters.outlinesParameters.outlinesStyle); + } + } + + if (os.Contains (Opening::OutlinesUseLineOfCutElements)) { + os.Get (Opening::OutlinesUseLineOfCutElements, element.opening.floorPlanParameters.outlinesParameters.useLineOfCutElements); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, floorPlanParameters.outlinesParameters.useLineOfCutElements); + } + + if (os.Contains (Opening::OutlinesUncutLineIndex)) { + + os.Get (Opening::OutlinesUncutLineIndex, attributeName); + + if (!attributeName.IsEmpty ()) { + API_Attribute attrib; + BNZeroMemory (&attrib, sizeof (API_Attribute)); + attrib.header.typeID = API_LinetypeID; + CHCopyC (attributeName.ToCStr (), attrib.header.name); + + if (NoError == ACAPI_Attribute_Get (&attrib)) { + element.opening.floorPlanParameters.outlinesParameters.uncutLineIndex = attrib.header.index; + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, floorPlanParameters.outlinesParameters.uncutLineIndex); + } + } + } + + if (os.Contains (Opening::OutlinesOverheadLineIndex)) { + + os.Get (Opening::OutlinesOverheadLineIndex, attributeName); + + if (!attributeName.IsEmpty ()) { + API_Attribute attrib; + BNZeroMemory (&attrib, sizeof (API_Attribute)); + attrib.header.typeID = API_LinetypeID; + CHCopyC (attributeName.ToCStr (), attrib.header.name); + + if (NoError == ACAPI_Attribute_Get (&attrib)) { + element.opening.floorPlanParameters.outlinesParameters.overheadLineIndex = attrib.header.index; + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, floorPlanParameters.outlinesParameters.overheadLineIndex); + } + } + } + + if (os.Contains (Opening::OutlinesOverheadLinePenIndex)) { + os.Get (Opening::OutlinesOverheadLinePenIndex, element.opening.floorPlanParameters.outlinesParameters.overheadLinePenIndex); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, floorPlanParameters.outlinesParameters.overheadLinePenIndex); + } + + if (os.Contains (Opening::UseCoverFills)) { + os.Get (Opening::UseCoverFills, element.opening.floorPlanParameters.coverFillsParameters.useCoverFills); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, floorPlanParameters.coverFillsParameters.useCoverFills); + } + + if (os.Contains (Opening::UseFillsOfCutElements)) { + os.Get (Opening::UseFillsOfCutElements, element.opening.floorPlanParameters.coverFillsParameters.useFillsOfCutElements); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, floorPlanParameters.coverFillsParameters.useFillsOfCutElements); + } + + if (os.Contains (Opening::CoverFillIndex)) { + + os.Get (Opening::CoverFillIndex, attributeName); + + if (!attributeName.IsEmpty ()) { + API_Attribute attrib; + BNZeroMemory (&attrib, sizeof (API_Attribute)); + attrib.header.typeID = API_FilltypeID; + CHCopyC (attributeName.ToCStr (), attrib.header.name); + + if (NoError == ACAPI_Attribute_Get (&attrib)) { + element.opening.floorPlanParameters.coverFillsParameters.coverFillIndex = attrib.header.index; + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, floorPlanParameters.coverFillsParameters.coverFillIndex); + } + } + } + + if (os.Contains (Opening::CoverFillPenIndex)) { + os.Get (Opening::CoverFillPenIndex, element.opening.floorPlanParameters.coverFillsParameters.coverFillPenIndex); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, floorPlanParameters.coverFillsParameters.coverFillPenIndex); + } + + if (os.Contains (Opening::CoverFillBackgroundPenIndex)) { + os.Get (Opening::CoverFillBackgroundPenIndex, element.opening.floorPlanParameters.coverFillsParameters.coverFillBackgroundPenIndex); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, floorPlanParameters.coverFillsParameters.coverFillBackgroundPenIndex); + } + + if (os.Contains (Opening::CoverFillOrientation)) { + os.Get (Opening::CoverFillOrientation, element.opening.floorPlanParameters.coverFillsParameters.coverFillOrientation); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, floorPlanParameters.coverFillsParameters.coverFillOrientation); + } + + if (os.Contains (Opening::CoverFillTransformationOrigoX)) { + os.Get (Opening::CoverFillTransformationOrigoX, element.opening.floorPlanParameters.coverFillTransformation.origo.x); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, floorPlanParameters.coverFillTransformation.origo.x); + } + + if (os.Contains (Opening::CoverFillTransformationOrigoY)) { + os.Get (Opening::CoverFillTransformationOrigoY, element.opening.floorPlanParameters.coverFillTransformation.origo.y); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, floorPlanParameters.coverFillTransformation.origo.y); + } + + if (os.Contains (Opening::CoverFillTransformationXAxisX)) { + os.Get (Opening::CoverFillTransformationXAxisX, element.opening.floorPlanParameters.coverFillTransformation.xAxis.x); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, floorPlanParameters.coverFillTransformation.xAxis.x); + } + + if (os.Contains (Opening::CoverFillTransformationXAxisY)) { + os.Get (Opening::CoverFillTransformationXAxisY, element.opening.floorPlanParameters.coverFillTransformation.xAxis.y); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, floorPlanParameters.coverFillTransformation.xAxis.y); + } + + if (os.Contains (Opening::CoverFillTransformationYAxisX)) { + os.Get (Opening::CoverFillTransformationYAxisX, element.opening.floorPlanParameters.coverFillTransformation.yAxis.x); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, floorPlanParameters.coverFillTransformation.yAxis.x); + } + + if (os.Contains (Opening::CoverFillTransformationYAxisY)) { + os.Get (Opening::CoverFillTransformationYAxisY, element.opening.floorPlanParameters.coverFillTransformation.yAxis.y); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, floorPlanParameters.coverFillTransformation.yAxis.y); + } + + if (os.Contains (Opening::ShowReferenceAxis)) { + os.Get (Opening::ShowReferenceAxis, element.opening.floorPlanParameters.referenceAxisParameters.showReferenceAxis); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, floorPlanParameters.referenceAxisParameters.showReferenceAxis); + } + + if (os.Contains (Opening::ReferenceAxisPenIndex)) { + os.Get (Opening::ReferenceAxisPenIndex, element.opening.floorPlanParameters.referenceAxisParameters.referenceAxisPenIndex); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, floorPlanParameters.referenceAxisParameters.referenceAxisPenIndex); + } + + if (os.Contains (Opening::ReferenceAxisLineTypeIndex)) { + + os.Get (Opening::ReferenceAxisLineTypeIndex, attributeName); + + if (!attributeName.IsEmpty ()) { + API_Attribute attrib; + BNZeroMemory (&attrib, sizeof (API_Attribute)); + attrib.header.typeID = API_LinetypeID; + CHCopyC (attributeName.ToCStr (), attrib.header.name); + + if (NoError == ACAPI_Attribute_Get (&attrib)) { + element.opening.floorPlanParameters.referenceAxisParameters.referenceAxisLineTypeIndex = attrib.header.index; + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, floorPlanParameters.referenceAxisParameters.referenceAxisLineTypeIndex); + } + } + } + + if (os.Contains (Opening::ReferenceAxisOverhang)) { + os.Get (Opening::ReferenceAxisOverhang, element.opening.floorPlanParameters.referenceAxisParameters.referenceAxisOverhang); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, floorPlanParameters.referenceAxisParameters.referenceAxisOverhang); + } + + Objects::Point3D basePoint; + if (os.Contains (Opening::ExtrusionGeometryBasePoint)) { + os.Get (Opening::ExtrusionGeometryBasePoint, basePoint); + element.opening.extrusionGeometryData.frame.basePoint = basePoint.ToAPI_Coord3D (); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, extrusionGeometryData.frame.basePoint); + } + + Objects::Vector3D axisX; + if (os.Contains (Opening::ExtrusionGeometryXAxis)) { + os.Get (Opening::ExtrusionGeometryXAxis, axisX); + element.opening.extrusionGeometryData.frame.axisX = axisX.ToAPI_Vector3D (); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, extrusionGeometryData.frame.axisX); + } + + Objects::Vector3D axisY; + if (os.Contains (Opening::ExtrusionGeometryYAxis)) { + os.Get (Opening::ExtrusionGeometryYAxis, axisY); + element.opening.extrusionGeometryData.frame.axisY = axisY.ToAPI_Vector3D (); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, extrusionGeometryData.frame.axisY); + } + + Objects::Vector3D axisZ; + if (os.Contains (Opening::ExtrusionGeometryZAxis)) { + os.Get (Opening::ExtrusionGeometryZAxis, axisZ); + element.opening.extrusionGeometryData.frame.axisZ = axisZ.ToAPI_Vector3D (); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, extrusionGeometryData.frame.axisZ); + } + + if (os.Contains (Opening::BasePolygonType)) { + GS::UniString basePolygonTypeName; + os.Get (Opening::BasePolygonType, basePolygonTypeName); + + GS::Optional type = openingBasePolygonTypeNames.FindValue (basePolygonTypeName); + if (type.HasValue ()) { + element.opening.extrusionGeometryData.parameters.basePolygonType = type.Get (); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, extrusionGeometryData.parameters.basePolygonType); + } + } + + if (os.Contains (Opening::Width)) { + os.Get (Opening::Width, element.opening.extrusionGeometryData.parameters.width); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, extrusionGeometryData.parameters.width); + + element.opening.extrusionGeometryData.parameters.linkedStatus = API_OpeningNotLinked; + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, extrusionGeometryData.parameters.linkedStatus); + } + + if (os.Contains (Opening::Height)) { + os.Get (Opening::Height, element.opening.extrusionGeometryData.parameters.height); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, extrusionGeometryData.parameters.height); + + element.opening.extrusionGeometryData.parameters.linkedStatus = API_OpeningNotLinked; + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, extrusionGeometryData.parameters.linkedStatus); + } + + if (os.Contains (Opening::Constraint)) { + os.Get (Opening::Constraint, element.opening.extrusionGeometryData.parameters.constraint); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, extrusionGeometryData.parameters.constraint); + } + + if (os.Contains (Opening::AnchorIndex)) { + os.Get (Opening::AnchorIndex, element.opening.extrusionGeometryData.parameters.anchor); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, extrusionGeometryData.parameters.anchor); + } + + if (os.Contains (Opening::AnchorAltitude)) { + os.Get (Opening::AnchorAltitude, element.opening.extrusionGeometryData.parameters.anchorAltitude); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, extrusionGeometryData.parameters.anchorAltitude); + } + + if (os.Contains (Opening::LimitType)) { + GS::UniString limitTypeName; + os.Get (Opening::LimitType, limitTypeName); + + GS::Optional type = openingLimitTypeNames.FindValue (limitTypeName); + if (type.HasValue ()) { + element.opening.extrusionGeometryData.parameters.limitType = type.Get (); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, extrusionGeometryData.parameters.limitType); + } + } + + if (os.Contains (Opening::ExtrusionStartOffSet)) { + os.Get (Opening::ExtrusionStartOffSet, element.opening.extrusionGeometryData.parameters.extrusionStartOffset); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, extrusionGeometryData.parameters.extrusionStartOffset); + } + + if (os.Contains (Opening::FiniteBodyLength)) { + os.Get (Opening::FiniteBodyLength, element.opening.extrusionGeometryData.parameters.finiteBodyLength); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, extrusionGeometryData.parameters.finiteBodyLength); + } + + return err; +} + + +GS::String CreateOpening::GetName () const +{ + return CreateOpeningCommandName; +} + + +} diff --git a/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateOpening.hpp b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateOpening.hpp new file mode 100644 index 0000000000..8c9203b298 --- /dev/null +++ b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateOpening.hpp @@ -0,0 +1,33 @@ +#ifndef CREATE_OPENING_HPP +#define CREATE_OPENING_HPP + +#include "CreateOpeningBase.hpp" +#include "FieldNames.hpp" + + +namespace AddOnCommands { + + +class CreateOpening : public CreateOpeningBase { + GS::String GetFieldName () const override; + GS::UniString GetUndoableCommandName () const override; + + GSErrCode GetElementFromObjectState (const GS::ObjectState& os, + API_Element& element, + API_Element& elementMask, + API_ElementMemo& memo, + GS::UInt64& memoMask, + API_SubElement** marker, + AttributeManager& attributeManager, + LibpartImportManager& libpartImportManager, + GS::Array& log) const override; + +public: + virtual GS::String GetName () const override; +}; + + +} + + +#endif // !CREATE_OPENING_HPP \ No newline at end of file diff --git a/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateOpeningBase.cpp b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateOpeningBase.cpp index d9582c709a..4c91bc8095 100644 --- a/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateOpeningBase.cpp +++ b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateOpeningBase.cpp @@ -29,7 +29,8 @@ bool CreateOpeningBase::CheckEnvironment (const GS::ObjectState& os, API_Element bool isParentWall = Utility::GetElementType (parentArchicadId) == API_WallID; bool isParentRoof = Utility::GetElementType (parentArchicadId) == API_RoofID; bool isParentShell = Utility::GetElementType (parentArchicadId) == API_ShellID; - if (!(parentExists && (isParentWall || isParentRoof || isParentShell))) { + bool isParentSlab = Utility::GetElementType (parentArchicadId) == API_SlabID; + if (!(parentExists && (isParentWall || isParentRoof || isParentShell || isParentSlab))) { return false; } @@ -45,6 +46,8 @@ bool CreateOpeningBase::CheckEnvironment (const GS::ObjectState& os, API_Element Utility::SetElementType (elem.header, API_RoofID); } else if (isParentShell) { Utility::SetElementType (elem.header, API_ShellID); + } else if (isParentSlab) { + Utility::SetElementType (elem.header, API_SlabID); } elem.header.guid = parentArchicadId; @@ -61,6 +64,10 @@ bool CreateOpeningBase::CheckEnvironment (const GS::ObjectState& os, API_Element element.window.owner = parentArchicadId; } else if (elementType == API_SkylightID) { element.skylight.owner = parentArchicadId; + } else if (elementType == API_OpeningID) { + element.opening.owner = parentArchicadId; + } else if (elementType == API_SlabID) { + element.opening.owner = parentArchicadId; } return true; diff --git a/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateSkylight.cpp b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateSkylight.cpp index dab3d245cb..fd56fb1efb 100644 --- a/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateSkylight.cpp +++ b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateSkylight.cpp @@ -7,7 +7,6 @@ #include "RealNumber.h" #include "DGModule.hpp" #include "LibpartImportManager.hpp" -#include "APIHelper.hpp" #include "FieldNames.hpp" #include "OnExit.hpp" #include "ExchangeManager.hpp" diff --git a/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateWindow.cpp b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateWindow.cpp index aac1284db6..1cf6a03e98 100644 --- a/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateWindow.cpp +++ b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateWindow.cpp @@ -7,7 +7,6 @@ #include "RealNumber.h" #include "DGModule.hpp" #include "LibpartImportManager.hpp" -#include "APIHelper.hpp" #include "FieldNames.hpp" #include "OnExit.hpp" #include "ExchangeManager.hpp" diff --git a/ConnectorArchicad/AddOn/Sources/AddOn/Commands/GetDataCommand.cpp b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/GetDataCommand.cpp index 841b77126c..41242b887f 100644 --- a/ConnectorArchicad/AddOn/Sources/AddOn/Commands/GetDataCommand.cpp +++ b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/GetDataCommand.cpp @@ -4,6 +4,382 @@ #include "Utility.hpp" #include "PropertyExportManager.hpp" +#include "BM.hpp" + +#include +#include + +namespace { + + /*! + Structure describing the surface area and volume quantities of a material in an element + */ + struct MaterialQuantity { + //The material index + API_AttributeIndex materialIndex{}; + //The net volume + double volume = 0.0; + //The net surface area + double surfaceArea = 0.0; + }; + + ///Array of elements + using ElementArray = std::vector; + ///Array of material quantities + using MaterialQuantArray = std::vector; + ///Array of quantities from a composite structure + using CompositeQuantityArray = GS::Array; + ///Function to retrieve the material index from an element + using MaterialGet = std::function; + ///Function to set the volume mask in a quantities calculation mask + using MaskSet = std::function; + ///Function to get the volume from a quantity calculation result + using QuantGet = std::function; + + + /*! + Structure facilitating the measurement of material quantities in an element + Each member is a function providing key information for quantity take-offs + */ + struct QuantityManager { + ///Gets the element building material + MaterialGet getMaterial; + ///Sets the volume take-off mask for the element + MaskSet setVolumeMask; + ///Gets the volume for the element (for the above material) + QuantGet getVolume; + ///Sets the surface area take-off mask for the element + MaskSet setAreaMask; + ///Gets the surface area for the element (for the above material) + QuantGet getArea; + }; + + /*! + Set a mask in a specified field (i.e. mark with a non-zero value to flag that this value should be calculated) + @param field A pointer to the target field + */ + void setMask(void* field) { + *(reinterpret_cast(field)) = 0xFF; + } + + /*! + Quantity management operation for handled Archicad element types (combines getter/setter for material, quants mask and quants values + NB: The API for elements is C structs with no abstraction or polymorphic behaviours. Rather than writing object wrappers for these structs, + the following collections of functions facilitate required getters/setters based on a type identifier + */ + std::map quantityManager = { + { API_BeamSegmentID, + { + [](const API_Element& element){ return element.beamSegment.assemblySegmentData.buildingMaterial; }, + [](API_ElementQuantityMask& mask){ setMask(&mask.beamSegment.volume); }, + [](const API_ElementQuantity& quant){ return quant.beamSegment.volume; }, + [](API_ElementQuantityMask& mask){ setMask(&mask.beamSegment.rightSurface); }, + [](const API_ElementQuantity& quant){ return quant.beamSegment.rightSurface; }, + } + }, + { API_ColumnSegmentID, + { + [](const API_Element& element){ return element.columnSegment.assemblySegmentData.buildingMaterial; }, + [](API_ElementQuantityMask& mask){ setMask(&mask.columnSegment.volume); }, + [](const API_ElementQuantity& quant){ return quant.columnSegment.volume; }, + [](API_ElementQuantityMask& mask){ setMask(&mask.columnSegment.coreNetSurface); }, + [](const API_ElementQuantity& quant){ return quant.columnSegment.coreNetSurface; }, + } + }, + { API_MeshID, + { + [](const API_Element& element){ return element.mesh.buildingMaterial; }, + [](API_ElementQuantityMask& mask){ setMask(&mask.mesh.volume); }, + [](const API_ElementQuantity& quant){ return quant.mesh.volume; }, + [](API_ElementQuantityMask& mask){ setMask(&mask.mesh.topSurface); }, + [](const API_ElementQuantity& quant){ return quant.mesh.topSurface; }, + } + }, + { API_MorphID, + { + [](const API_Element& element){ return element.morph.buildingMaterial; }, + [](API_ElementQuantityMask& mask){ setMask(&mask.morph.volume); }, + [](const API_ElementQuantity& quant){ return quant.morph.volume; }, + [](API_ElementQuantityMask& mask){ setMask(&mask.morph.surface); }, + [](const API_ElementQuantity& quant){ return quant.morph.surface; }, + } + }, + { API_RoofID, + { + [](const API_Element& element){ return element.roof.shellBase.buildingMaterial; }, + [](API_ElementQuantityMask& mask){ setMask(&mask.roof.volume); }, + [](const API_ElementQuantity& quant){ return quant.roof.volume; }, + [](API_ElementQuantityMask& mask){ setMask(&mask.roof.topSurface); }, + [](const API_ElementQuantity& quant){ return quant.roof.topSurface; }, + } + }, + { API_ShellID, + { + [](const API_Element& element){ return element.shell.shellBase.buildingMaterial; }, + [](API_ElementQuantityMask& mask){ setMask(&mask.shell.volume); }, + [](const API_ElementQuantity& quant){ return quant.shell.volume; }, + [](API_ElementQuantityMask& mask){ setMask(&mask.shell.referenceSurface); }, + [](const API_ElementQuantity& quant){ return quant.shell.referenceSurface; }, + } + }, + { API_SlabID, + { + [](const API_Element& element){ return element.slab.buildingMaterial; }, + [](API_ElementQuantityMask& mask){ setMask(&mask.slab.volume); }, + [](const API_ElementQuantity& quant){ return quant.slab.volume; }, + [](API_ElementQuantityMask& mask){ setMask(&mask.slab.topSurface); }, + [](const API_ElementQuantity& quant){ return quant.slab.topSurface; }, + } + }, + { API_WallID, + { + [](const API_Element& element){ return element.wall.buildingMaterial; }, + [](API_ElementQuantityMask& mask){ setMask(&mask.wall.volume); }, + [](const API_ElementQuantity& quant){ return quant.wall.volume; }, + [](API_ElementQuantityMask& mask){ setMask(&mask.wall.surface1); }, + [](const API_ElementQuantity& quant){ return quant.wall.surface1; }, + } + }, + }; + + + /*! + Collect the individual segments from a segmented assembly (e.g. beam/column) + @param segments A pointer to the assembly segments + @return An array containing the individual segments + */ + template + auto getSegments(const T* segments, Insert insert) { + ElementArray result; + if (segments == nullptr) + return result; + for (auto segmentIndex = BMGetPtrSize((GSConstPtr) segments) / sizeof(T); segmentIndex--; ) { + API_Element element{}; + insert(segments[segmentIndex], element); + result.push_back(element); + } + return result; + } //getBeamSegments + + + /*! + Get the type identifier for an element + @param element The target element + @return The element type ID, e.g. `API_WallID` + */ + API_ElemTypeID getTypeID(const API_Element& element) { +#ifdef ServerMainVers_2600 + return element.header.type.typeID; +#else + return element.header.typeID; +#endif + } + + + /*! + Determine if the specified element is an assembly, i.e. made of multiple parts (sub-elements) + @return True if the element is an assembly + */ + bool isAssembly(const API_Element& element) { + auto typeID = getTypeID(element); + return (typeID == API_BeamID) || (typeID == API_ColumnID); + } //isAssembly + + + /*! + Get the constituent parts of a specified element, e.g. the segments in a beam/column + @param element The source element + @param memo The source element memo + @return An array of element parts (empty if the element is not made of parts - the original element is never included) + */ + auto getElementParts(const API_Element& element, const API_ElementMemo& memo) { + switch (getTypeID(element)) { + case API_BeamID: + return getSegments(memo.beamSegments, [](const API_BeamSegmentType& segment, API_Element& element){ element.beamSegment = segment; }); + case API_ColumnID: + return getSegments(memo.columnSegments, [](const API_ColumnSegmentType& segment, API_Element& element){ element.columnSegment = segment; }); + default: + break; + } + return ElementArray{}; + } //getElementParts + + + /*! + Get the structure type for a specified element, e.g. how materials are arranged in the element body + @param element The target element + @return The element structural type + */ + auto getStructureType(const API_Element& element) { + switch (getTypeID(element)) { + case API_BeamSegmentID: + return element.beamSegment.assemblySegmentData.modelElemStructureType; + case API_ColumnSegmentID: + return element.columnSegment.assemblySegmentData.modelElemStructureType; + case API_RoofID: + return element.roof.shellBase.modelElemStructureType; + case API_ShellID: + return element.shell.shellBase.modelElemStructureType; + case API_SlabID: + return element.slab.modelElemStructureType; + case API_WallID: + return element.wall.modelElemStructureType; + default: + return API_BasicStructure; + } + } //getStructureType + + + /*! + Collect material quantities from the composite materials of a specified element + @param element The target element to export the properties from + @param elementQuantity Quantities extracted from the target element (out) + @param extendedQuantity Optional extended quantities calculated for some element types + @param quantityMask Mask to determine which quantities are required (minimise calculation time) + @return An error code (NoError = success) + */ + GS::ErrCode measureQuantities(const API_Element& element, API_ElementQuantity& elementQuantity, API_Quantities& extendedQuantity, + const API_QuantitiesMask& quantityMask) { + extendedQuantity.elements = &elementQuantity; + GS::Array elementPartQuantities; + API_QuantityPar quantityParameters{}; + quantityParameters.minOpeningSize = Eps; + MaterialQuantArray result; + return ACAPI_Element_GetQuantities(element.header.guid, &quantityParameters, &extendedQuantity, &quantityMask); + } //measureQuantities + + + /*! + Collect material quantities from the basic (single homogeneous) material of a specified element + @param element The target element to export the properties from + @return An array of material quantities collected from the element + */ + auto collectBasicQuantitites(const API_Element& element) { + MaterialQuantArray result; + //First determine that this element type has a suitable material definition and supports quantity take-offs for volume and area + auto manager = quantityManager.find(getTypeID(element)); + if (manager != quantityManager.end()) { + API_ElementQuantity elementQuantity{}; + API_Quantities extendedQuantity{}; + API_QuantitiesMask quantityMask{}; + //Set the appropriate masks for material volume/area quantity takeoffs + manager->second.setVolumeMask(quantityMask.elements); + manager->second.setAreaMask(quantityMask.elements); + measureQuantities(element, elementQuantity, extendedQuantity, quantityMask); + //Create a material quantity from the quantity takeoff + result.push_back({ + manager->second.getMaterial(element), + manager->second.getVolume(elementQuantity), + manager->second.getArea(elementQuantity) + }); + } + return result; + } //collectBasicQuantitites + + + /*! + Collect material quantities from the composite materials of a specified element + @param element The target element to export the properties from + @return An array of material quantities collected from the element composite structure + */ + auto collectCompositeQuantities(const API_Element& element) { + API_ElementQuantity elementQuantity{}; + API_Quantities extendedQuantity{}; + CompositeQuantityArray compositeQuantity{}; + extendedQuantity.composites = &compositeQuantity; + API_QuantitiesMask quantityMask{}; + //Set the appropriate masks for composite material volume/area quantity takeoffs + setMask(&quantityMask.composites.buildMatIndices); + setMask(&quantityMask.composites.volumes); + setMask(&quantityMask.composites.projectedArea); + measureQuantities(element, elementQuantity, extendedQuantity, quantityMask); + MaterialQuantArray result; + //Create material quantities from the quantity takeoff (one oer skin in the composite structure) + for (auto& skinQuant : compositeQuantity) + result.push_back({skinQuant.buildMatIndices, skinQuant.volumes, skinQuant.projectedArea}); + return result; + } //collectCompositeQuantities + + + /*! + Get the material quantities for a specified element + @param element The source element + @param memo The memo data attached to the element + @return An array of material quantities extracted from the element + */ + MaterialQuantArray getQuantity(const API_Element& element, const API_ElementMemo& memo) { + if (isAssembly(element)) { + MaterialQuantArray result; + //Get the constituent parts of the assembly and process each as an independent element + auto parts = getElementParts (element, memo); + if (!parts.empty()) { + API_ElementMemo partMemo{}; //NB: The memo is not used for the quantity take-offs in part elements, so an empty structure is fine + for (auto& part : parts) { + auto partMaterials = getQuantity(part, partMemo); + result.insert(result.end(), std::make_move_iterator(partMaterials.begin()), std::make_move_iterator(partMaterials.end())); + } + } + return result; + } + switch (getStructureType(element)) { + case API_BasicStructure: + return collectBasicQuantitites(element); + case API_CompositeStructure: + return collectCompositeQuantities(element); + case API_ProfileStructure: + break; + } + return MaterialQuantArray{}; + } //getQuantity + + + /*! + Serialise a specified material attribute for export + @param materialIndex The target material index + @param serialiser A serialiser for the exported data + @return NoError if the export serialisation completed without errors + */ + GS::ErrCode exportMaterial(API_AttributeIndex materialIndex, GS::ObjectState& serialiser) { + //Attempt to load the material attribute using the index + API_Attribute attribute{}; + attribute.header.index = materialIndex; + attribute.header.typeID = API_BuildingMaterialID; + auto error = ACAPI_Attribute_Get(&attribute); + if (error != NoError) + return error; + serialiser.Add(FieldNames::Material::Name, GS::UniString{attribute.header.name}); + return NoError; + } //exportMaterial + + + /*! + Serialise the material quantities of a specified element for export + @param element The target element + @param memo The memo data attached to the element + @param serialiser A serialiser for the exported data + @return NoError if the export serialisation completed without errors + */ + GS::ErrCode exportMaterialQuantities(const API_Element& element, const API_ElementMemo& memo, GS::ObjectState& serialiser) { + auto materialQuants = getQuantity(element, memo); + if (materialQuants.empty()) + return NoError; + const auto& serialMaterialQuants = serialiser.AddList (FieldNames::ElementBase::MaterialQuantities); + for (auto& quantity : materialQuants) { + GS::ObjectState serialMaterialQuant, serialMaterial; + auto error = exportMaterial(quantity.materialIndex, serialMaterial); + if (error != NoError) + return error; + serialMaterialQuant.Add(FieldNames::ElementBase::Quantity::Material, serialMaterial); + serialMaterialQuant.Add(FieldNames::ElementBase::Quantity::Volume, quantity.volume); + serialMaterialQuant.Add(FieldNames::ElementBase::Quantity::Area, quantity.surfaceArea); + serialMaterialQuant.Add(FieldNames::ElementBase::Quantity::Units, GS::UniString{"m"}); + serialMaterialQuants(serialMaterialQuant); + } + return NoError; + } //exportMaterialQuantities + +} + namespace AddOnCommands { @@ -164,19 +540,26 @@ GS::ErrCode SerializePropertyGroups (const GS::Array& de } +/*! + Export attached Archicad properties, listing properties (calculated) and material quantities from a specified element + @param element The target element to export the properties from + @param sendProperties True to export the Archicad properties attached to the element + @param sendListingParameters True to export calculated listing parameters from the element, e.g. top/bottom surface area etc + @param systemItemPairs Array pairing a classification system ID against a classification item ID (attached to the target element) + @param os A collector/serialiser for the exported data + @return NoError if the export was successful + */ GS::ErrCode GetDataCommand::ExportProperties (const API_Element& element, const bool& sendProperties, const bool& sendListingParameters, const GS::Array>& systemItemPairs, GS::ObjectState& os) const { - GS::ErrCode err = NoError; - if (!sendProperties && !sendListingParameters) return NoError; GS::Array elementDefinitions; GS::Array < GS::Pair>> componentsDefinitions; - err = PropertyExportManager::GetInstance ()->GetElementDefinitions (element, sendProperties, sendListingParameters, systemItemPairs, elementDefinitions, componentsDefinitions); + GS::ErrCode err = PropertyExportManager::GetInstance ()->GetElementDefinitions (element, sendProperties, sendListingParameters, systemItemPairs, elementDefinitions, componentsDefinitions); if (err != NoError) - return false; + return err; // element properties { @@ -275,23 +658,32 @@ GS::UInt64 GetDataCommand::GetMemoMask () const } -GS::ErrCode GetDataCommand::SerializeElementType(const API_Element& elem, const API_ElementMemo& /*memo*/, GS::ObjectState& os, const bool& sendProperties, const bool& sendListingParameters) const +/*! + Serialise the attributes, properties and quantities of a specified element + + elem: The target element + memo: Memo data attached to the element + os: A collector/serialiser for the exported data + sendProperties: True to export the Archicad properties attached to the element + sendListingParameters: True to export calculated listing parameters from the element, e.g. top/bottom surface area etc + + return: NoError if the serialisation was successful + */ +GS::ErrCode GetDataCommand::SerializeElementType(const API_Element& elem, const API_ElementMemo& memo, GS::ObjectState& os, const bool& sendProperties, const bool& sendListingParameters) const { - GS::ErrCode err = NoError; - os.Add(FieldNames::ElementBase::ApplicationId, APIGuidToString (elem.header.guid)); API_Attribute attribute; BNZeroMemory (&attribute, sizeof (API_Attribute)); attribute.header.typeID = API_LayerID; attribute.header.index = elem.header.layer; - if (NoError == ACAPI_Attribute_Get (&attribute)) { + if (ACAPI_Attribute_Get (&attribute) == NoError) { os.Add(FieldNames::ElementBase::Layer, GS::UniString{attribute.header.name}); } - - err = ExportClassificationsAndProperties (elem, os, sendProperties, sendListingParameters); - - return err; + auto err = exportMaterialQuantities (elem, memo, os); + if (err != NoError) + return err; + return ExportClassificationsAndProperties (elem, os, sendProperties, sendListingParameters); } @@ -341,7 +733,7 @@ GS::ObjectState GetDataCommand::Execute (const GS::ObjectState& parameters, err = SerializeElementType (element, memo, os); if (err != NoError) continue; - + listAdder (os); } diff --git a/ConnectorArchicad/AddOn/Sources/AddOn/Commands/GetDoorData.hpp b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/GetDoorData.hpp index 317ad154f1..a5a69c739b 100644 --- a/ConnectorArchicad/AddOn/Sources/AddOn/Commands/GetDoorData.hpp +++ b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/GetDoorData.hpp @@ -1,5 +1,5 @@ -#ifndef GET_OPENING_DATA_HPP -#define GET_OPENING_DATA_HPP +#ifndef GET_DOOR_DATA_HPP +#define GET_DOOR_DATA_HPP #include "GetDataCommand.hpp" diff --git a/ConnectorArchicad/AddOn/Sources/AddOn/Commands/GetOpeningData.cpp b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/GetOpeningData.cpp new file mode 100644 index 0000000000..54e4f1691f --- /dev/null +++ b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/GetOpeningData.cpp @@ -0,0 +1,156 @@ +#include "GetOpeningData.hpp" +#include "ResourceIds.hpp" +#include "ObjectState.hpp" +#include "Utility.hpp" +#include "Objects/Point.hpp" +#include "Objects/Vector.hpp" +#include "RealNumber.h" +#include "FieldNames.hpp" +#include "TypeNameTables.hpp" +using namespace FieldNames; + + +namespace AddOnCommands { + + +GS::String GetOpeningData::GetFieldName () const +{ + return Openings; +} + + +API_ElemTypeID GetOpeningData::GetElemTypeID () const +{ + return API_OpeningID; +} + + +GS::ErrCode GetOpeningData::SerializeElementType (const API_Element& element, + const API_ElementMemo& /*memo*/, + GS::ObjectState& os) const +{ + os.Add (ElementBase::ParentElementId, APIGuidToString (element.opening.owner)); + + API_Attribute attrib; + + // Opening Floor Parameters + os.Add (Opening::FloorPlanDisplayMode, openingFloorPlanDisplayModeNames.Get (element.opening.floorPlanParameters.floorPlanDisplayMode)); + os.Add (Opening::ConnectionMode, openingFloorPlanConnectionModeNames.Get (element.opening.floorPlanParameters.connectionMode)); + + // Opening Floor Plan Parameters - Cut Surfaces + os.Add (Opening::CutSurfacesUseLineOfCutElements, element.opening.floorPlanParameters.cutSurfacesParameters.useLineOfCutElements); + os.Add (Opening::CutSurfacesLinePenIndex, element.opening.floorPlanParameters.cutSurfacesParameters.linePenIndex); + BNZeroMemory (&attrib, sizeof (API_Attribute)); + attrib.header.typeID = API_LinetypeID; + attrib.header.index = element.opening.floorPlanParameters.cutSurfacesParameters.lineIndex; + + if (NoError == ACAPI_Attribute_Get (&attrib)) { + os.Add (Opening::CutSurfacesLineIndex, GS::UniString{attrib.header.name}); + } + + // Opening Floor Plan Parameters - Outlines Parameters + os.Add (Opening::OutlinesStyle, openingOutlinesStyleNames.Get (element.opening.floorPlanParameters.outlinesParameters.outlinesStyle)); + os.Add (Opening::OutlinesUseLineOfCutElements, element.opening.floorPlanParameters.outlinesParameters.useLineOfCutElements); + + BNZeroMemory (&attrib, sizeof (API_Attribute)); + attrib.header.typeID = API_LinetypeID; + attrib.header.index = element.opening.floorPlanParameters.outlinesParameters.uncutLineIndex; + + if (NoError == ACAPI_Attribute_Get (&attrib)) { + os.Add (Opening::OutlinesUncutLineIndex, GS::UniString{attrib.header.name}); + } + + BNZeroMemory (&attrib, sizeof (API_Attribute)); + attrib.header.typeID = API_LinetypeID; + attrib.header.index = element.opening.floorPlanParameters.outlinesParameters.overheadLineIndex; + + if (NoError == ACAPI_Attribute_Get (&attrib)) { + os.Add (Opening::OutlinesOverheadLineIndex, GS::UniString{attrib.header.name}); + } + + BNZeroMemory (&attrib, sizeof (API_Attribute)); + attrib.header.typeID = API_LinetypeID; + attrib.header.index = element.opening.floorPlanParameters.outlinesParameters.uncutLineIndex; + + if (NoError == ACAPI_Attribute_Get (&attrib)) { + os.Add (Opening::OutlinesUncutLineIndex, GS::UniString{attrib.header.name}); + } + os.Add (Opening::OutlinesOverheadLinePenIndex, element.opening.floorPlanParameters.outlinesParameters.overheadLinePenIndex); + + // Opening Floor Plan Parameters - Cover Fills + os.Add (Opening::UseCoverFills, element.opening.floorPlanParameters.coverFillsParameters.useCoverFills); + if (element.opening.floorPlanParameters.coverFillsParameters.useCoverFills) { + os.Add (Opening::UseFillsOfCutElements, element.opening.floorPlanParameters.coverFillsParameters.useFillsOfCutElements); + + BNZeroMemory (&attrib, sizeof (API_Attribute)); + attrib.header.typeID = API_FilltypeID; + attrib.header.index = element.opening.floorPlanParameters.coverFillsParameters.coverFillIndex; + + if (NoError == ACAPI_Attribute_Get (&attrib)) { + os.Add (Opening::CoverFillIndex, GS::UniString{attrib.header.name}); + } + + os.Add (Opening::CoverFillPenIndex, element.opening.floorPlanParameters.coverFillsParameters.coverFillPenIndex); + os.Add (Opening::CoverFillBackgroundPenIndex, element.opening.floorPlanParameters.coverFillsParameters.coverFillBackgroundPenIndex); + os.Add (Opening::CoverFillOrientation, element.opening.floorPlanParameters.coverFillsParameters.coverFillOrientation); + } + + // Opening Floor Plan Parameters - Cover Fill Transformation + os.Add (Opening::CoverFillTransformationOrigoX, element.opening.floorPlanParameters.coverFillTransformation.origo.x); + os.Add (Opening::CoverFillTransformationOrigoY, element.opening.floorPlanParameters.coverFillTransformation.origo.y); + os.Add (Opening::CoverFillTransformationXAxisX, element.opening.floorPlanParameters.coverFillTransformation.xAxis.x); + os.Add (Opening::CoverFillTransformationXAxisY, element.opening.floorPlanParameters.coverFillTransformation.xAxis.y); + os.Add (Opening::CoverFillTransformationYAxisX, element.opening.floorPlanParameters.coverFillTransformation.yAxis.x); + os.Add (Opening::CoverFillTransformationYAxisY, element.opening.floorPlanParameters.coverFillTransformation.yAxis.y); + + // Opening Floor Plan Parameters - Reference Axis + os.Add (Opening::ShowReferenceAxis, element.opening.floorPlanParameters.referenceAxisParameters.showReferenceAxis); + if (element.opening.floorPlanParameters.referenceAxisParameters.showReferenceAxis) { + os.Add (Opening::ReferenceAxisPenIndex, element.opening.floorPlanParameters.referenceAxisParameters.referenceAxisPenIndex); + + BNZeroMemory (&attrib, sizeof (API_Attribute)); + attrib.header.typeID = API_LinetypeID; + attrib.header.index = element.opening.floorPlanParameters.referenceAxisParameters.referenceAxisLineTypeIndex; + + if (NoError == ACAPI_Attribute_Get (&attrib)) { + os.Add (Opening::ReferenceAxisLineTypeIndex, GS::UniString{attrib.header.name}); + } + + os.Add (Opening::ReferenceAxisOverhang, element.opening.floorPlanParameters.referenceAxisParameters.referenceAxisOverhang); + } + + // Extrusion Geometry + os.Add (Opening::ExtrusionGeometryBasePoint, Objects::Point3D (element.opening.extrusionGeometryData.frame.basePoint)); + os.Add (Opening::ExtrusionGeometryXAxis, Objects::Vector3D(element.opening.extrusionGeometryData.frame.axisX)); + os.Add (Opening::ExtrusionGeometryYAxis, Objects::Vector3D (element.opening.extrusionGeometryData.frame.axisY)); + os.Add (Opening::ExtrusionGeometryZAxis, Objects::Vector3D (element.opening.extrusionGeometryData.frame.axisZ)); + + // Extrusion Geometry - Opening Extrusion Parameters + os.Add (Opening::BasePolygonType, openingBasePolygonTypeNames.Get (element.opening.extrusionGeometryData.parameters.basePolygonType)); + os.Add (Opening::Width, element.opening.extrusionGeometryData.parameters.width); + os.Add (Opening::Height, element.opening.extrusionGeometryData.parameters.height); + os.Add (Opening::Constraint, element.opening.extrusionGeometryData.parameters.constraint); + os.Add (Opening::Anchor, openingAnchorNames.Get (element.opening.extrusionGeometryData.parameters.anchor)); + os.Add (Opening::AnchorIndex, (element.opening.extrusionGeometryData.parameters.anchor)); + os.Add (Opening::AnchorAltitude, element.opening.extrusionGeometryData.parameters.anchorAltitude); + os.Add (Opening::LimitType, openingLimitTypeNames.Get (element.opening.extrusionGeometryData.parameters.limitType)); + os.Add (Opening::ExtrusionStartOffSet, element.opening.extrusionGeometryData.parameters.extrusionStartOffset); + os.Add (Opening::FiniteBodyLength, element.opening.extrusionGeometryData.parameters.finiteBodyLength); + os.Add (Opening::LinkedStatus, openingLinkedStatusNames.Get (element.opening.extrusionGeometryData.parameters.linkedStatus)); + + // Extrusion Geometry - Custom Base Polygon + if (element.opening.extrusionGeometryData.parameters.basePolygonType == API_OpeningBasePolygonCustom) { + // Reserved for future use + } + + return NoError; +} + + +GS::String GetOpeningData::GetName () const +{ + return GetOpeningDataCommandName; +} + + +} // namespace AddOnCommands diff --git a/ConnectorArchicad/AddOn/Sources/AddOn/Commands/GetOpeningData.hpp b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/GetOpeningData.hpp new file mode 100644 index 0000000000..0a30edd766 --- /dev/null +++ b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/GetOpeningData.hpp @@ -0,0 +1,24 @@ +#ifndef GET_OPENING_DATA_HPP +#define GET_OPENING_DATA_HPP + +#include "GetDataCommand.hpp" + +namespace AddOnCommands { + + +class GetOpeningData : public GetDataCommand { + GS::String GetFieldName() const override; + API_ElemTypeID GetElemTypeID() const override; + GS::ErrCode SerializeElementType(const API_Element& elem, + const API_ElementMemo& memo, + GS::ObjectState& os) const override; + +public: + virtual GS::String GetName() const override; +}; + + +} + + +#endif // GET_OPENING_DATA_HPP \ No newline at end of file diff --git a/ConnectorArchicad/AddOn/Sources/AddOn/FieldNames.hpp b/ConnectorArchicad/AddOn/Sources/AddOn/FieldNames.hpp index 64c80772da..81ac117969 100644 --- a/ConnectorArchicad/AddOn/Sources/AddOn/FieldNames.hpp +++ b/ConnectorArchicad/AddOn/Sources/AddOn/FieldNames.hpp @@ -3,801 +3,881 @@ namespace FieldNames { - - -namespace ApplicationObject { -static const char* ApplicationObjects = "applicationObjects"; -static const char* Status = "Status"; -static const char* StateCreated = "Created"; -static const char* StateSkipped = "Skipped"; -static const char* StateUpdated = "Updated"; -static const char* StateFailed = "Failed"; -static const char* StateRemoved = "Removed"; -static const char* StateUnknown = "Unknown"; -static const char* OriginalId = "OriginalId"; -static const char* CreatedIds = "CreatedIds"; -static const char* Log = "Log"; -} - -namespace ElementBase -{ -static const char* Id = "id"; -static const char* ApplicationId = "applicationId"; -static const char* ApplicationIds = "applicationIds"; -static const char* ParentElementId = "parentApplicationId"; -static const char* ElementFilter = "elementFilter"; -static const char* FilterBy = "filterBy"; -static const char* ElementType = "elementType"; -static const char* ElementTypes = "elementTypes"; -static const char* Elements = "elements"; -static const char* SubElements = "subElements"; -static const char* Level = "level"; -static const char* Layer = "layer"; -static const char* Shape = "shape"; -static const char* Shape1 = "shape1"; -static const char* Shape2 = "shape2"; - -static const char* Classifications = "classifications"; -namespace Classification -{ -static const char* System = "system"; -static const char* Code = "code"; // id is reserved for Speckle id -static const char* Name = "name"; -} - -static const char* ElementProperties = "elementProperties"; -static const char* ComponentProperties = "componentProperties"; -static const char* SendListingParameters = "sendListingParameters"; -static const char* SendProperties = "sendProperties"; -namespace ComponentProperty -{ -static const char* Name = "name"; -static const char* PropertyGroups = "propertyGroups"; -} -namespace PropertyGroup -{ -static const char* Name = "name"; -static const char* PropertList = "propertyList"; -} -namespace Property -{ -static const char* Name = "name"; -static const char* Value = "value"; -static const char* Values = "values"; -static const char* Units = "units"; -} -} - -static const char* Elements = "elements"; -static const char* Beams = "beams"; -static const char* Columns = "columns"; -static const char* DirectShapes = "directShapes"; -static const char* Doors = "doors"; -static const char* GridElements = "gridElements"; -static const char* Objects = "objects"; -static const char* MeshModels = "meshModels"; -static const char* Roofs = "roofs"; -static const char* Shells = "shells"; -static const char* Skylights = "skylights"; -static const char* Slabs = "slabs"; -static const char* Walls = "walls"; -static const char* Windows = "windows"; -static const char* Zones = "zones"; - -static const char* Models = "models"; -static const char* SubelementModels = "subelementModels"; - -static const char* ShowOnStories = "showOnStories"; -static const char* VisibilityContData = "visibilityCont"; -static const char* VisibilityFillData = "visibilityFill"; -static const char* ShowOnHome = "showOnHome"; -static const char* ShowAllAbove = "showAllAbove"; -static const char* ShowAllBelow = "showAllBelow"; -static const char* ShowRelAbove = "showRelAbove"; -static const char* ShowRelBelow = "showRelBelow"; -static const char* HomeStoryOnlyValueName = "homeStoryOnly"; -static const char* HomeAndOneStoryUpValueName = "homeAndOneStoryUp"; -static const char* HomeAndOneStoryDownValueName = "homeAndOneStoryDown"; -static const char* HomeAndOneStoryUpAndDownValueName = "homeAndOneStoryUpAndDown"; -static const char* OneStoryUpValueName = "oneStoryUp"; -static const char* OneStoryDownValueName = "oneStoryDown"; -static const char* AllStoriesValueName = "allStories"; -static const char* AllRelevantStoriesValueName = "allRelevantStories"; -static const char* CustomStoriesValueName = "custom"; - -static const char* CoverFillTransformationType = "coverFillTransformationType"; -static const char* HatchOrientationType = "coverFillTransformationType"; -static const char* LinkToProjectOriginValueName = "linkToProjectOrigin"; -static const char* LinkToFillOriginValueName = "linkToFillOrigin"; -static const char* CustomDistortionValueName = "customDistortion"; -static const char* ThreeDDistortionValueName = "3DDistortion"; - - -namespace AssemblySegment { -static const char* SegmentName = "Segment #%d"; -static const char* SchemeName = "Scheme #%d"; -static const char* CutName = "Cut #%d"; - -static const char* HoleData = "Holes"; -static const char* SegmentData = "Segments"; -static const char* SchemeData = "Schemes"; -static const char* CutData = "Cuts"; -} - - -namespace PivotPolyEdge { -static const char* EdgeName = "Roof Pivot Poly Edge #%d"; -static const char* EdgeData = "roofPivotPolyEdges"; -} - - -namespace PivotPolyEdgeData { -static const char* NumLevelEdgeData = "nLevelEdgeData"; -} - - -namespace LevelEdge { -static const char* LevelEdgeName = "Roof Level #%d"; -static const char* LevelEdgeData = "roofLevels"; -} - - -namespace RoofSegmentData { -static const char* LevelAngle = "edgeLevelAngle"; -static const char* TopMaterial = "topMaterial"; -static const char* BottomMaterial = "bottomMaterial"; -static const char* CoverFillType = "coverFillType"; -static const char* EavesOverhang = "eavesOverhang"; -static const char* AngleType = "angleType"; -} - - -namespace Wall -{ -// Wall geometry -static const char* BaseOffset = "baseOffset"; -static const char* StartPoint = "startPoint"; -static const char* EndPoint = "endPoint"; -static const char* WallComplexity = "wallComplexity"; -static const char* Structure = "structure"; -static const char* GeometryMethod = "geometryMethod"; -static const char* BuildingMaterialName = "buildingMaterialName"; -static const char* CompositeName = "compositeName"; -static const char* ProfileName = "profileName"; -static const char* ArcAngle = "arcAngle"; -static const char* Thickness = "thickness"; -static const char* FirstThickness = "firstThickness"; -static const char* SecondThickness = "secondThickness"; -static const char* OutsideSlantAngle = "outsideSlantAngle"; -static const char* InsideSlantAngle = "insideSlantAngle"; -static const char* Height = "height"; -static const char* PolyCanChange = "polyWalllCornersCanChange"; -// Wall and stories relation -static const char* TopOffset = "topOffset"; -static const char* RelativeTopStoryIndex = "relativeTopStory"; -static const char* ReferenceLineLocation = "referenceLineLocation"; -static const char* ReferenceLineOffset = "referenceLineOffset"; -static const char* OffsetFromOutside = "offsetFromOutside"; -static const char* ReferenceLineStartIndex = "referenceLineStartIndex"; -static const char* ReferenceLineEndIndex = "referenceLineEndIndex"; -static const char* Flipped = "flipped"; -// Floor Plan and Section - Floor Plan Display -static const char* DisplayOptionName = "displayOptionName"; -static const char* ViewDepthLimitationName = "showProjectionName"; -// Floor Plan and Section - Cut Surfaces -static const char* CutLinePenIndex = "cutLinePen"; -static const char* CutLinetypeName = "cutLinetype"; -static const char* OverrideCutFillPenIndex = "overrideCutFillPen"; -static const char* OverrideCutFillBackgroundPenIndex = "overrideCutFillBackgroundPen"; -// Floor Plan and Section - Outlines -static const char* UncutLinePenIndex = "uncutLinePen"; -static const char* UncutLinetypeName = "uncutLinetype"; -static const char* OverheadLinePenIndex = "overheadLinePen"; -static const char* OverheadLinetypeName = "overheadLinetype"; -// Model - Override Surfaces -static const char* ReferenceMaterialName = "referenceMaterialName"; -static const char* ReferenceMaterialStartIndex = "referenceMaterialStartIndex"; -static const char* ReferenceMaterialEndIndex = "referenceMaterialEndIndex"; -static const char* OppositeMaterialName = "oppositeMaterialName"; -static const char* OppositeMaterialStartIndex = "oppositeMaterialStartIndex"; -static const char* OppositeMaterialEndIndex = "oppositeMaterialEndIndex"; -static const char* SideMaterialName = "sideMaterialName"; -static const char* MaterialsChained = "materialsChained"; -static const char* InheritEndSurface = "inheritEndSurface"; -static const char* AlignTexture = "alignTexture"; -static const char* Sequence = "sequence"; -// Model - Log Details (log height, start with half log, surface of horizontal edges, log shape) -static const char* LogHeight = "logHeight"; -static const char* StartWithHalfLog = "startWithHalfLog"; -static const char* SurfaceOfHorizontalEdges = "surfaceOfHorizontalEdges"; -static const char* LogShape = "logShape"; -// Model - Defines the relation of wall to zones (Zone Boundary, Reduce Zone Area Only, No Effect on Zones) -static const char* WallRelationToZoneName = "wallRelationToZoneName"; -// Does it have any embedded object? -static const char* HasDoor = "hasDoor"; -static const char* HasWindow = "hasWindow"; -} - - -namespace OpeningBase { -static const char* Door = "Door #%d"; -// -static const char* guid = "guid"; -// OpeningBase -static const char* width = "width"; -static const char* height = "height"; -static const char* subFloorThickness = "subFloorThickness"; -static const char* reflected = "reflected"; -static const char* oSide = "oSide"; -static const char* refSide = "refSide"; -static const char* VerticalLinkTypeName = "verticalLinkTypeName"; -static const char* VerticalLinkStoryIndex = "verticalLinkStoryIndex"; -static const char* WallCutUsing = "wallCutUsing"; - -static const char* PenIndex = "pen"; -static const char* LineTypeName = "lineTypeName"; -static const char* SectFillName = "sectFill"; -static const char* SectFillPenIndex = "sectFillPen"; -static const char* SectBackgroundPenIndex = "sectBackgroundPen"; -static const char* SectContPenIndex = "sectContPen"; -static const char* CutLineTypeName = "cutLineType"; -static const char* AboveViewLineTypeName = "aboveViewLineType"; -static const char* AboveViewLinePenIndex = "aboveViewLinePen"; -static const char* BelowViewLinePenIndex = "belowViewLinePen"; -static const char* BelowViewLineTypeName = "belowViewLineType"; -static const char* UseObjectPens = "useObjectPens"; -static const char* UseObjLinetypes = "useObjLinetypes"; -static const char* UseObjMaterials = "useObjMaterials"; -static const char* UseObjSectAttrs = "useObjSectAttrs"; -static const char* BuildingMaterialName = "buildingMaterial"; -static const char* LibraryPart = "libraryPart"; -static const char* DisplayOptionName = "displayOptionName"; - -static const char* revealDepthFromSide = "revealDepthFromSide"; -static const char* jambDepthHead = "jambDepthHead"; -static const char* jambDepth = "jambDepth"; -static const char* jambDepth2 = "jambDepth2"; -static const char* objLoc = "objLoc"; -static const char* lower = "lower"; -static const char* directionType = "directionType"; - -static const char* startPoint = "startPoint"; -static const char* dirVector = "dirVector"; -} - - -namespace Beam -{ -// Positioning -static const char* begC = "begC"; -static const char* endC = "endC"; -static const char* level = "level"; -static const char* isSlanted = "isSlanted"; -static const char* slantAngle = "slantAngle"; -static const char* beamShape = "beamShape"; -static const char* sequence = "sequence"; -static const char* curveAngle = "curveAngle"; -static const char* verticalCurveHeight = "verticalCurveHeight"; -static const char* isFlipped = "isFlipped"; -// End Cuts -static const char* nCuts = "nCuts"; -// Rreference Axis -static const char* anchorPoint = "anchorPoint"; -static const char* offset = "offset"; -static const char* profileAngle = "profileAngle"; -// Segment -static const char* segments = "segments"; -static const char* nSegments = "nSegments"; -static const char* nProfiles = "nProfiles"; - - -namespace BeamSegment -{ -// Segment override materials -static const char* LeftMaterial = "leftMaterial"; -static const char* TopMaterial = "topMaterial"; -static const char* RightMaterial = "rightMaterial"; -static const char* BottomMaterial = "bottomMaterial"; -static const char* EndsMaterial = "endsMaterial"; -// Segment - The overridden materials are chained -static const char* MaterialsChained = "materialChained"; -// Segment -static const char* segmentData = "assemblySegmentData"; -} - - -// Scheme -static const char* nSchemes = "nSchemes"; -// Hole -static const char* HoleName = "Hole #%d"; -static const char* holeType = "holeType"; -static const char* holeContourOn = "holeContourOn"; -static const char* holeId = "holeId"; -static const char* centerx = "centerx"; -static const char* centerz = "centerz"; -static const char* width = "width"; -static const char* height = "height"; -// Floor Plan and Section - Floor Plan Display -static const char* DisplayOptionName = "displayOptionName"; -static const char* UncutProjectionModeName = "uncutProjectionMode"; -static const char* OverheadProjectionModeName = "overheadProjectionMode"; -static const char* ViewDepthLimitationName = "showProjectionName"; -// Floor Plan and Section - Cut Surfaces -static const char* cutContourLinePen = "cutContourLinePen"; -static const char* CutContourLinetypeName = "cutContourLineType"; -static const char* OverrideCutFillPenIndex = "overrideCutFillPen"; -static const char* OverrideCutFillBackgroundPenIndex = "overrideCutFillBackgroundPen"; -// Floor Plan and Section - Outlines -static const char* ShowOutlineIndex = "showOutline"; -static const char* UncutLinePenIndex = "uncutLinePen"; -static const char* UncutLinetypeName = "uncutLinetype"; -static const char* OverheadLinePenIndex = "overheadLinePen"; -static const char* OverheadLinetypeName = "overheadLinetype"; -static const char* HiddenLinePenIndex = "hiddenLinePen"; -static const char* HiddenLinetypeName = "hiddenLinetype"; -// Floor Plan and Section - Symbol -static const char* ShowReferenceAxisIndex = "showReferenceAxis"; -static const char* refPen = "referencePen"; -static const char* refLtype = "referenceLinetype"; -// Floor Plan and Section - Cover Fills -static const char* useCoverFill = "useCoverFill"; -static const char* useCoverFillFromSurface = "useCoverFillFromSurface"; -static const char* coverFillForegroundPen = "coverFillForegroundPen"; -static const char* coverFillBackgroundPen = "coverFillBackgroundPen"; -static const char* coverFillType = "coverFillType"; -static const char* CoverFillTransformationOrigoX = "coverFillTransformationOrigoX"; -static const char* CoverFillTransformationOrigoY = "coverFillTransformationOrigoY"; -static const char* CoverFillTransformationXAxisX = "coverFillTransformationXAxisX"; -static const char* CoverFillTransformationXAxisY = "coverFillTransformationXAxisY"; -static const char* CoverFillTransformationYAxisX = "coverFillTransformationYAxisX"; -static const char* CoverFillTransformationYAxisY = "coverFillTransformationYAxisY"; -} - - -namespace Column -{ -// Positioning - geometry -static const char* origoPos = "origoPos"; -static const char* height = "height"; -// Positioning - story relation -static const char* bottomOffset = "bottomOffset"; -static const char* topOffset = "topOffset"; -static const char* relativeTopStory = "relativeTopStory"; -// Positioning - slanted column -static const char* isSlanted = "isSlanted"; -static const char* slantAngle = "slantAngle"; -static const char* slantDirectionAngle = "slantDirectionAngle"; -static const char* isFlipped = "isFlipped"; -// Positioning - wrapping -static const char* Wrapping = "wrapping"; -// Positioning - Defines the relation of column to zones (Zone Boundary, Reduce Zone Area Only, No Effect on Zones) -static const char* ColumnRelationToZoneName = "columnRelationToZoneName"; -// End Cuts -static const char* nCuts = "nCuts"; -// Reference Axis -static const char* coreAnchor = "coreAnchor"; -static const char* axisRotationAngle = "axisRotationAngle"; -// Segment -static const char* segments = "segments"; -static const char* nSegments = "nSegments"; -static const char* nProfiles = "nProfiles"; - - -namespace ColumnSegment -{ -// Segment - Veneer attributes -static const char* VenType = "veneerType"; -static const char* VenBuildingMaterial = "veneerBuildingMaterial"; -static const char* VenThick = "veneerThick"; -// Segment - The extrusion overridden material name -static const char* ExtrusionSurfaceMaterial = "extrusionSurfaceMaterial"; -// Segment - The ends overridden material name -static const char* EndsSurfaceMaterial = "endsSurfaceMaterial"; -// Segment - The overridden materials are chained -static const char* MaterialsChained = "materialChained"; -// Segment -static const char* segmentData = "assemblySegmentData"; -} - - -// Scheme -static const char* nSchemes = "nSchemes"; -// Floor Plan and Section - Floor Plan Display -static const char* DisplayOptionName = "displayOptionName"; -static const char* ViewDepthLimitationName = "showProjectionName"; -// Floor Plan and Section - Cut Surfaces -static const char* corePen = "corePen"; -static const char* CoreLinetypeName = "contLtype"; -static const char* VeneerPenIndex = "venLinePen"; -static const char* VeneerLinetypeName = "venLineType"; -static const char* OverrideCutFillPenIndex = "overrideCutFillPen"; -static const char* OverrideCutFillBackgroundPenIndex = "overrideCutFillBackgroundPen"; -// Floor Plan and Section - Outlines -static const char* UncutLinePenIndex = "uncutLinePen"; -static const char* UncutLinetypeName = "uncutLinetype"; -static const char* OverheadLinePenIndex = "overheadLinePen"; -static const char* OverheadLinetypeName = "overheadLinetype"; -static const char* HiddenLinePenIndex = "hiddenLinePen"; -static const char* HiddenLinetypeName = "hiddenLinetype"; -// Floor Plan and Section - Floor Plan Symbol -static const char* CoreSymbolTypeName = "coreSymbolTypeName"; -static const char* coreSymbolPar1 = "coreSymbolPar1"; -static const char* coreSymbolPar2 = "coreSymbolPar2"; -static const char* CoreSymbolPenIndex = "coreSymbolPen"; -// Floor Plan and Section - Cover Fills -static const char* useCoverFill = "useCoverFill"; -static const char* useCoverFillFromSurface = "useCoverFillFromSurface"; -static const char* coverFillForegroundPen = "coverFillForegroundPen"; -static const char* coverFillBackgroundPen = "coverFillBackgroundPen"; -static const char* coverFillType = "coverFillType"; -static const char* CoverFillTransformationOrigoX = "coverFillTransformationOrigoX"; -static const char* CoverFillTransformationOrigoY = "coverFillTransformationOrigoY"; -static const char* CoverFillTransformationXAxisX = "coverFillTransformationXAxisX"; -static const char* CoverFillTransformationXAxisY = "coverFillTransformationXAxisY"; -static const char* CoverFillTransformationYAxisX = "coverFillTransformationYAxisX"; -static const char* CoverFillTransformationYAxisY = "coverFillTransformationYAxisY"; -} - - -namespace AssemblySegmentData { -static const char* circleBased = "circleBased"; -static const char* modelElemStructureType = "modelElemStructureType"; -static const char* nominalHeight = "nominalHeight"; -static const char* nominalWidth = "nominalWidth"; -static const char* isWidthAndHeightLinked = "isWidthAndHeightLinked"; -static const char* isHomogeneous = "isHomogeneous"; -static const char* endWidth = "endWidth"; -static const char* endHeight = "endHeight"; -static const char* isEndWidthAndHeightLinked = "isEndWidthAndHeightLinked"; -static const char* buildingMaterial = "buildingMaterial"; -static const char* profileAttrName = "profileAttrName"; -} - - -namespace AssemblySegmentSchemeData { -static const char* lengthType = "lengthType"; -static const char* fixedLength = "fixedLength"; -static const char* lengthProportion = "lengthProportion"; -} - - -namespace AssemblySegmentCutData { -static const char* cutType = "cutType"; -static const char* customAngle = "customAngle"; -} - - -namespace Object -{ -// Main -static const char* pos = "pos"; -static const char* transform = "transform"; -} - -namespace GridElement -{ -// Main -static const char* begin = "begin"; -static const char* end = "end"; -static const char* angle = "angle"; -static const char* markerText = "markerText"; -static const char* isArc = "isArc"; -static const char* arcAngle = "arcAngle"; -} - - -namespace Slab -{ -// Geometry and positioning -static const char* Thickness = "thickness"; -static const char* Structure = "structure"; -static const char* CompositeName = "compositeName"; -static const char* BuildingMaterialName = "buildingMaterialName"; -static const char* ReferencePlaneLocation = "referencePlaneLocation"; -// Edge trims -static const char* EdgeAngleType = "edgeAngleType"; -static const char* EdgeAngle = "edgeAngle"; -// Floor Plan and Section - Cut Surfaces -static const char* sectContPen = "sectContPen"; -static const char* sectContLtype = "sectContLtype"; -static const char* cutFillPen = "cutFillPen"; -static const char* cutFillBackgroundPen = "cutFillBackgroundPen"; -// Floor Plan and Section - Outlines -static const char* contourPen = "contourPen"; -static const char* contourLineType = "contourLineType"; -static const char* hiddenContourLinePen = "hiddenContourLinePen"; -static const char* hiddenContourLineType = "hiddenContourLineType"; -// Floor Plan and Section - Cover Fills -static const char* useFloorFill = "useFloorFill"; -static const char* floorFillPen = "floorFillPen"; -static const char* floorFillBGPen = "floorFillBGPen"; -static const char* floorFillName = "floorFillName"; -static const char* use3DHatching = "use3DHatching"; -static const char* hatchOrientation = "hatchOrientation"; -static const char* hatchOrientationOrigoX = "hatchOrientationOrigoX"; -static const char* hatchOrientationOrigoY = "hatchOrientationOrigoY"; -static const char* hatchOrientationXAxisX = "hatchOrientationXAxisX"; -static const char* hatchOrientationXAxisY = "hatchOrientationXAxisY"; -static const char* hatchOrientationYAxisX = "hatchOrientationYAxisX"; -static const char* hatchOrientationYAxisY = "hatchOrientationYAxisY"; -// Model -static const char* topMat = "topMat"; -static const char* sideMat = "sideMat"; -static const char* botMat = "botMat"; -static const char* materialsChained = "materialsChained"; -} - - -namespace Roof -{ -// Geometry and positioning -static const char* RoofClassName = "roofClassName"; -static const char* PlaneRoofAngle = "planeRoofAngle"; -static const char* BaseLine = "baseLine"; -static const char* BegC = "begC"; -static const char* EndC = "endC"; -static const char* PosSign = "posSign"; -static const char* PivotPolygon = "pivotPolygon"; - -// Level -static const char* levels = "levels"; -static const char* LevelNum = "levelNum"; - -namespace RoofLevel { -static const char* LevelName = "Level #%d"; -static const char* levelData = "levels"; -} - -namespace LevelData { -static const char* LevelHeight = "levelHeight"; -static const char* LevelAngle = "levelAngle"; -} - -static const char* Thickness = "thickness"; -static const char* Structure = "structure"; -static const char* CompositeName = "compositeName"; -static const char* BuildingMaterialName = "buildingMaterialName"; -// Edge trims -static const char* EdgeAngleType = "edgeAngleType"; -static const char* EdgeAngle = "edgeAngle"; -// Floor Plan and Section - Floor Plan Display -static const char* DisplayOptionName = "displayOptionName"; -static const char* ViewDepthLimitationName = "showProjectionName"; -// Floor Plan and Section - Cut Surfaces -static const char* SectContPen = "sectContPen"; -static const char* SectContLtype = "sectContLtype"; -static const char* CutFillPen = "cutFillPen"; -static const char* CutFillBackgroundPen = "cutFillBackgroundPen"; -// Floor Plan and Section - Outlines -static const char* ContourPen = "contourPen"; -static const char* ContourLineType = "contourLineType"; -static const char* OverheadLinePen = "overheadLinePen"; -static const char* OverheadLinetype = "overheadLinetype"; -// Floor Plan and Section - Cover Fills -static const char* UseFloorFill = "useFloorFill"; -static const char* FloorFillPen = "floorFillPen"; -static const char* FloorFillBGPen = "floorFillBGPen"; -static const char* FloorFillName = "floorFillName"; -static const char* Use3DHatching = "use3DHatching"; -static const char* UseFillLocBaseLine = "useFillLocBaseLine"; -static const char* UseSlantedFill = "useSlantedFill"; -static const char* HatchOrientation = "hatchOrientation"; -static const char* HatchOrientationOrigoX = "hatchOrientationOrigoX"; -static const char* HatchOrientationOrigoY = "hatchOrientationOrigoY"; -static const char* HatchOrientationXAxisX = "hatchOrientationXAxisX"; -static const char* HatchOrientationXAxisY = "hatchOrientationXAxisY"; -static const char* HatchOrientationYAxisX = "hatchOrientationYAxisX"; -static const char* HatchOrientationYAxisY = "hatchOrientationYAxisY"; -// Model -static const char* TopMat = "topMat"; -static const char* SideMat = "sideMat"; -static const char* BotMat = "botMat"; -static const char* MaterialsChained = "materialsChained"; -static const char* TrimmingBodyName = "trimmingBodyName"; -} - - -namespace Shell -{ -// Geometry and positioning -static const char* ShellClassName = "shellClassName"; - -static const char* BasePlane = "basePlane"; - -static const char* Flipped = "flipped"; -static const char* HasContour = "hasContour"; -static const char* NumHoles = "numHoles"; -static const char* ShellContourName = "shellContour #%d"; -static const char* ShellContourData = "shellContours"; -static const char* ShellContourPlane = "shellContourPlane"; -static const char* ShellContourPoly = "shellContourPoly"; -static const char* ShellContourHeight = "shellContourHeight"; -static const char* ShellContourID = "shellContourID"; -static const char* ShellContourSideTypeName = "sideTypeName"; -static const char* ShellContourSideAngle = "sideAngle"; -static const char* ShellContourEdgeTypeName = "edgeTypeName"; -static const char* ShellContourEdgeSideMaterial = "edgeSideMaterial"; -static const char* ShellContourEdgeName = "shellContourEdge #%d"; -static const char* ShellContourEdgeData = "shellContourEdges"; -static const char* DefaultEdgeType = "defaultEdgeType"; - -static const char* SlantAngle = "slantAngle"; -static const char* RevolutionAngle = "revolutionAngle"; -static const char* DistortionAngle = "distortionAngle"; -static const char* SegmentedSurfaces = "segmentedSurfaces"; -static const char* ShapePlaneTilt = "shapePlaneTilt"; -static const char* BegPlaneTilt = "begPlaneTilt"; -static const char* EndPlaneTilt = "endPlaneTilt"; -static const char* AxisBase = "axisBase"; -static const char* Plane1 = "plane1"; -static const char* Plane2 = "plane2"; -static const char* BegC = "begC"; -static const char* BegAngle = "begAngle"; -static const char* ExtrusionVector = "extrusionVector"; -static const char* ShapeDirection = "shapeDirection"; -static const char* DistortionVector = "distortionVector"; -static const char* MorphingRuleName = "morphingRuleName"; - -static const char* BegShapeEdge = "begShapeEdge"; -static const char* BegShapeEdgeTrimSideType = "begShapeEdgeTrimSideType"; -static const char* BegShapeEdgeTrimSideAngle = "begShapeEdgeTrimSideAngle"; -static const char* BegShapeEdgeSideMaterial = "begShapeEdgeSideMaterial"; -static const char* BegShapeEdgeType = "begShapeEdgeType"; -static const char* EndShapeEdge = "endShapeEdge"; -static const char* EndShapeEdgeTrimSideType = "endShapeEdgeTrimSideType"; -static const char* EndShapeEdgeTrimSideAngle = "endShapeEdgeTrimSideAngle"; -static const char* EndShapeEdgeSideMaterial = "endShapeEdgeSideMaterial"; -static const char* EndShapeEdgeType = "endShapeEdgeType"; - -static const char* ExtrudedEdge1 = "extrudedEdge1"; -static const char* ExtrudedEdgeTrimSideType1 = "extrudedEdgeTrimSideType1"; -static const char* ExtrudedEdgeTrimSideAngle1 = "extrudedEdgeTrimSideAngle1"; -static const char* ExtrudedEdgeSideMaterial1 = "extrudedEdgeSideMaterial1"; -static const char* ExtrudedEdgeType1 = "extrudedEdgeType1"; -static const char* ExtrudedEdge2 = "extrudedEdge2"; -static const char* ExtrudedEdgeTrimSideType2 = "extrudedEdgeTrimSideType2"; -static const char* ExtrudedEdgeTrimSideAngle2 = "extrudedEdgeTrimSideAngle2"; -static const char* ExtrudedEdgeSideMaterial2 = "extrudedEdgeSideMaterial2"; -static const char* ExtrudedEdgeType2 = "extrudedEdgeType2"; - -static const char* RevolvedEdge1 = "revolvedEdge1"; -static const char* RevolvedEdgeTrimSideType1 = "revolvedEdgeTrimSideType1"; -static const char* RevolvedEdgeTrimSideAngle1 = "revolvedEdgeTrimSideAngle1"; -static const char* RevolvedEdgeSideMaterial1 = "revolvedEdgeSideMaterial1"; -static const char* RevolvedEdgeType1 = "revolvedEdgeType1"; -static const char* RevolvedEdge2 = "revolvedEdge2"; -static const char* RevolvedEdgeTrimSideType2 = "revolvedEdgeTrimSideType2"; -static const char* RevolvedEdgeTrimSideAngle2 = "revolvedEdgeTrimSideAngle2"; -static const char* RevolvedEdgeSideMaterial2 = "revolvedEdgeSideMaterial2"; -static const char* RevolvedEdgeType2 = "revolvedEdgeType2"; - -static const char* RuledEdge1 = "ruledEdge1"; -static const char* RuledEdgeTrimSideType1 = "ruledEdgeTrimSideType1"; -static const char* RuledEdgeTrimSideAngle1 = "ruledEdgeTrimSideAngle1"; -static const char* RuledEdgeSideMaterial1 = "ruledEdgeSideMaterial1"; -static const char* RuledEdgeType1 = "ruledEdgeType1"; -static const char* RuledEdge2 = "ruledEdge2"; -static const char* RuledEdgeTrimSideType2 = "ruledEdgeTrimSideType2"; -static const char* RuledEdgeTrimSideAngle2 = "ruledEdgeTrimSideAngle2"; -static const char* RuledEdgeSideMaterial2 = "ruledEdgeSideMaterial2"; -static const char* RuledEdgeType2 = "ruledEdgeType2"; - -static const char* Thickness = "thickness"; -static const char* Structure = "structure"; -static const char* CompositeName = "compositeName"; -static const char* BuildingMaterialName = "buildingMaterialName"; -// Edge trims -static const char* EdgeAngleType = "edgeAngleType"; -static const char* EdgeAngle = "edgeAngle"; -// Floor Plan and Section - Floor Plan Display -static const char* DisplayOptionName = "displayOptionName"; -static const char* ViewDepthLimitationName = "showProjectionName"; -// Floor Plan and Section - Cut Surfaces -static const char* SectContPen = "sectContPen"; -static const char* SectContLtype = "sectContLtype"; -static const char* CutFillPen = "cutFillPen"; -static const char* CutFillBackgroundPen = "cutFillBackgroundPen"; -// Floor Plan and Section - Outlines -static const char* ContourPen = "contourPen"; -static const char* ContourLineType = "contourLineType"; -static const char* OverheadLinePen = "overheadLinePen"; -static const char* OverheadLinetype = "overheadLinetype"; -// Floor Plan and Section - Cover Fills -static const char* UseFloorFill = "useFloorFill"; -static const char* FloorFillPen = "floorFillPen"; -static const char* FloorFillBGPen = "floorFillBGPen"; -static const char* FloorFillName = "floorFillName"; -static const char* Use3DHatching = "use3DHatching"; -static const char* UseFillLocBaseLine = "useFillLocBaseLine"; -static const char* UseSlantedFill = "useSlantedFill"; -static const char* HatchOrientation = "hatchOrientation"; -static const char* HatchOrientationOrigoX = "hatchOrientationOrigoX"; -static const char* HatchOrientationOrigoY = "hatchOrientationOrigoY"; -static const char* HatchOrientationXAxisX = "hatchOrientationXAxisX"; -static const char* HatchOrientationXAxisY = "hatchOrientationXAxisY"; -static const char* HatchOrientationYAxisX = "hatchOrientationYAxisX"; -static const char* HatchOrientationYAxisY = "hatchOrientationYAxisY"; -// Model -static const char* TopMat = "topMat"; -static const char* SideMat = "sideMat"; -static const char* BotMat = "botMat"; -static const char* MaterialsChained = "materialsChained"; -static const char* TrimmingBodyName = "trimmingBodyName"; -} - - -namespace Skylight -{ -// Geometry and positioning -static const char* VertexID = "vertexID"; -static const char* SkylightFixMode = "skylightFixMode"; -static const char* SkylightAnchor = "skylightAnchor"; -static const char* AnchorPosition = "anchorPosition"; -static const char* AnchorLevel = "anchorLevel"; -static const char* AzimuthAngle = "azimuthAngle"; -static const char* ElevationAngle = "elevationAngle"; -} - - -namespace Room -{ -static const char* Name = "name"; -static const char* Number = "number"; -static const char* BasePoint = "basePoint"; -static const char* Height = "height"; -static const char* Area = "area"; -static const char* Volume = "volume"; -} - - -namespace Model -{ -static const char* Vertices = "vertices"; -static const char* VertexX = "x"; -static const char* VertexY = "y"; -static const char* VertexZ = "z"; -static const char* PointId1 = "v1"; -static const char* PointId2 = "v2"; -static const char* PolygonId1 = "p1"; -static const char* PolygonId2 = "p2"; -static const char* EdgeStatus = "s"; -static const char* HiddenEdgeValueName = "HiddenEdge"; -static const char* SmoothEdgeValueName = "SmoothEdge"; -static const char* VisibleEdgeValueName = "VisibleEdge"; -static const char* Polygons = "polygons"; -static const char* Materials = "materials"; -static const char* PointIds = "pointIds"; -static const char* MaterialName = "name"; -static const char* Transparency = "transparency"; -static const char* AmbientColor = "ambientColor"; -static const char* EmissionColor = "emissionColor"; -static const char* Material = "material"; -static const char* Model = "model"; -static const char* ModelIds = "modelIds"; -static const char* Ids = "ids"; -static const char* Edges = "edges"; -} - - -namespace Level -{ -static const char* TypeName = "level"; -static const char* Index = "index"; -static const char* Name = "name"; -static const char* Elevation = "elevation"; -static const char* Units = "units"; -} - - -namespace Point -{ -static const char* X = "x"; -static const char* Y = "y"; -static const char* Z = "z"; -static const char* Units = "units"; -} - - + + + namespace ApplicationObject { + static const char* ApplicationObjects = "applicationObjects"; + static const char* Status = "Status"; + static const char* StateCreated = "Created"; + static const char* StateSkipped = "Skipped"; + static const char* StateUpdated = "Updated"; + static const char* StateFailed = "Failed"; + static const char* StateRemoved = "Removed"; + static const char* StateUnknown = "Unknown"; + static const char* OriginalId = "OriginalId"; + static const char* CreatedIds = "CreatedIds"; + static const char* Log = "Log"; + } + + namespace ElementBase + { + static const char* Id = "id"; + static const char* ApplicationId = "applicationId"; + static const char* ApplicationIds = "applicationIds"; + static const char* ParentElementId = "parentApplicationId"; + static const char* ElementFilter = "elementFilter"; + static const char* FilterBy = "filterBy"; + static const char* ElementType = "elementType"; + static const char* ElementTypes = "elementTypes"; + static const char* Elements = "elements"; + static const char* SubElements = "subElements"; + static const char* Level = "level"; + static const char* Layer = "layer"; + static const char* Shape = "shape"; + static const char* Shape1 = "shape1"; + static const char* Shape2 = "shape2"; + + static const char* Classifications = "classifications"; + namespace Classification + { + static const char* System = "system"; + static const char* Code = "code"; // id is reserved for Speckle id + static const char* Name = "name"; + } + + static const char* MaterialQuantities = "materialQuantities"; + static const char* ElementProperties = "elementProperties"; + static const char* ComponentProperties = "componentProperties"; + static const char* SendListingParameters = "sendListingParameters"; + static const char* SendProperties = "sendProperties"; + namespace Quantity + { + static const char* Material = "material"; + static const char* Volume = "volume"; + static const char* Area = "area"; + static const char* Units = "units"; + } + namespace ComponentProperty + { + static const char* Name = "name"; + static const char* PropertyGroups = "propertyGroups"; + } + namespace PropertyGroup + { + static const char* Name = "name"; + static const char* PropertList = "propertyList"; + } + namespace Property + { + static const char* Name = "name"; + static const char* Value = "value"; + static const char* Values = "values"; + static const char* Units = "units"; + } + } + + static const char* Elements = "elements"; + static const char* Beams = "beams"; + static const char* Columns = "columns"; + static const char* DirectShapes = "directShapes"; + static const char* Doors = "doors"; + static const char* GridElements = "gridElements"; + static const char* Objects = "objects"; + static const char* MeshModels = "meshModels"; + static const char* Roofs = "roofs"; + static const char* Shells = "shells"; + static const char* Skylights = "skylights"; + static const char* Slabs = "slabs"; + static const char* Walls = "walls"; + static const char* Windows = "windows"; + static const char* Zones = "zones"; + static const char* Openings = "openings"; + + static const char* Models = "models"; + static const char* SubelementModels = "subelementModels"; + + static const char* ShowOnStories = "showOnStories"; + static const char* VisibilityContData = "visibilityCont"; + static const char* VisibilityFillData = "visibilityFill"; + static const char* ShowOnHome = "showOnHome"; + static const char* ShowAllAbove = "showAllAbove"; + static const char* ShowAllBelow = "showAllBelow"; + static const char* ShowRelAbove = "showRelAbove"; + static const char* ShowRelBelow = "showRelBelow"; + static const char* HomeStoryOnlyValueName = "homeStoryOnly"; + static const char* HomeAndOneStoryUpValueName = "homeAndOneStoryUp"; + static const char* HomeAndOneStoryDownValueName = "homeAndOneStoryDown"; + static const char* HomeAndOneStoryUpAndDownValueName = "homeAndOneStoryUpAndDown"; + static const char* OneStoryUpValueName = "oneStoryUp"; + static const char* OneStoryDownValueName = "oneStoryDown"; + static const char* AllStoriesValueName = "allStories"; + static const char* AllRelevantStoriesValueName = "allRelevantStories"; + static const char* CustomStoriesValueName = "custom"; + + static const char* CoverFillTransformationType = "coverFillTransformationType"; + static const char* HatchOrientationType = "coverFillTransformationType"; + static const char* LinkToProjectOriginValueName = "linkToProjectOrigin"; + static const char* LinkToFillOriginValueName = "linkToFillOrigin"; + static const char* CustomDistortionValueName = "customDistortion"; + static const char* ThreeDDistortionValueName = "3DDistortion"; + + + namespace AssemblySegment { + static const char* SegmentName = "Segment #%d"; + static const char* SchemeName = "Scheme #%d"; + static const char* CutName = "Cut #%d"; + + static const char* HoleData = "Holes"; + static const char* SegmentData = "Segments"; + static const char* SchemeData = "Schemes"; + static const char* CutData = "Cuts"; + } + + + namespace PivotPolyEdge { + static const char* EdgeName = "Roof Pivot Poly Edge #%d"; + static const char* EdgeData = "roofPivotPolyEdges"; + } + + + namespace PivotPolyEdgeData { + static const char* NumLevelEdgeData = "nLevelEdgeData"; + } + + + namespace LevelEdge { + static const char* LevelEdgeName = "Roof Level #%d"; + static const char* LevelEdgeData = "roofLevels"; + } + + + namespace RoofSegmentData { + static const char* LevelAngle = "edgeLevelAngle"; + static const char* TopMaterial = "topMaterial"; + static const char* BottomMaterial = "bottomMaterial"; + static const char* CoverFillType = "coverFillType"; + static const char* EavesOverhang = "eavesOverhang"; + static const char* AngleType = "angleType"; + } + + + namespace Wall + { + // Wall geometry + static const char* BaseOffset = "baseOffset"; + static const char* StartPoint = "startPoint"; + static const char* EndPoint = "endPoint"; + static const char* WallComplexity = "wallComplexity"; + static const char* Structure = "structure"; + static const char* GeometryMethod = "geometryMethod"; + static const char* BuildingMaterialName = "buildingMaterialName"; + static const char* CompositeName = "compositeName"; + static const char* ProfileName = "profileName"; + static const char* ArcAngle = "arcAngle"; + static const char* Thickness = "thickness"; + static const char* FirstThickness = "firstThickness"; + static const char* SecondThickness = "secondThickness"; + static const char* OutsideSlantAngle = "outsideSlantAngle"; + static const char* InsideSlantAngle = "insideSlantAngle"; + static const char* Height = "height"; + static const char* PolyCanChange = "polyWalllCornersCanChange"; + // Wall and stories relation + static const char* TopOffset = "topOffset"; + static const char* RelativeTopStoryIndex = "relativeTopStory"; + static const char* ReferenceLineLocation = "referenceLineLocation"; + static const char* ReferenceLineOffset = "referenceLineOffset"; + static const char* OffsetFromOutside = "offsetFromOutside"; + static const char* ReferenceLineStartIndex = "referenceLineStartIndex"; + static const char* ReferenceLineEndIndex = "referenceLineEndIndex"; + static const char* Flipped = "flipped"; + // Floor Plan and Section - Floor Plan Display + static const char* DisplayOptionName = "displayOptionName"; + static const char* ViewDepthLimitationName = "showProjectionName"; + // Floor Plan and Section - Cut Surfaces + static const char* CutLinePenIndex = "cutLinePen"; + static const char* CutLinetypeName = "cutLinetype"; + static const char* OverrideCutFillPenIndex = "overrideCutFillPen"; + static const char* OverrideCutFillBackgroundPenIndex = "overrideCutFillBackgroundPen"; + // Floor Plan and Section - Outlines + static const char* UncutLinePenIndex = "uncutLinePen"; + static const char* UncutLinetypeName = "uncutLinetype"; + static const char* OverheadLinePenIndex = "overheadLinePen"; + static const char* OverheadLinetypeName = "overheadLinetype"; + // Model - Override Surfaces + static const char* ReferenceMaterialName = "referenceMaterialName"; + static const char* ReferenceMaterialStartIndex = "referenceMaterialStartIndex"; + static const char* ReferenceMaterialEndIndex = "referenceMaterialEndIndex"; + static const char* OppositeMaterialName = "oppositeMaterialName"; + static const char* OppositeMaterialStartIndex = "oppositeMaterialStartIndex"; + static const char* OppositeMaterialEndIndex = "oppositeMaterialEndIndex"; + static const char* SideMaterialName = "sideMaterialName"; + static const char* MaterialsChained = "materialsChained"; + static const char* InheritEndSurface = "inheritEndSurface"; + static const char* AlignTexture = "alignTexture"; + static const char* Sequence = "sequence"; + // Model - Log Details (log height, start with half log, surface of horizontal edges, log shape) + static const char* LogHeight = "logHeight"; + static const char* StartWithHalfLog = "startWithHalfLog"; + static const char* SurfaceOfHorizontalEdges = "surfaceOfHorizontalEdges"; + static const char* LogShape = "logShape"; + // Model - Defines the relation of wall to zones (Zone Boundary, Reduce Zone Area Only, No Effect on Zones) + static const char* WallRelationToZoneName = "wallRelationToZoneName"; + // Does it have any embedded object? + static const char* HasDoor = "hasDoor"; + static const char* HasWindow = "hasWindow"; + } + + + namespace OpeningBase { + static const char* Door = "Door #%d"; + // + static const char* guid = "guid"; + // OpeningBase + static const char* width = "width"; + static const char* height = "height"; + static const char* subFloorThickness = "subFloorThickness"; + static const char* reflected = "reflected"; + static const char* oSide = "oSide"; + static const char* refSide = "refSide"; + static const char* VerticalLinkTypeName = "verticalLinkTypeName"; + static const char* VerticalLinkStoryIndex = "verticalLinkStoryIndex"; + static const char* WallCutUsing = "wallCutUsing"; + + static const char* PenIndex = "pen"; + static const char* LineTypeName = "lineTypeName"; + static const char* SectFillName = "sectFill"; + static const char* SectFillPenIndex = "sectFillPen"; + static const char* SectBackgroundPenIndex = "sectBackgroundPen"; + static const char* SectContPenIndex = "sectContPen"; + static const char* CutLineTypeName = "cutLineType"; + static const char* AboveViewLineTypeName = "aboveViewLineType"; + static const char* AboveViewLinePenIndex = "aboveViewLinePen"; + static const char* BelowViewLinePenIndex = "belowViewLinePen"; + static const char* BelowViewLineTypeName = "belowViewLineType"; + static const char* UseObjectPens = "useObjectPens"; + static const char* UseObjLinetypes = "useObjLinetypes"; + static const char* UseObjMaterials = "useObjMaterials"; + static const char* UseObjSectAttrs = "useObjSectAttrs"; + static const char* BuildingMaterialName = "buildingMaterial"; + static const char* LibraryPart = "libraryPart"; + static const char* DisplayOptionName = "displayOptionName"; + + static const char* revealDepthFromSide = "revealDepthFromSide"; + static const char* jambDepthHead = "jambDepthHead"; + static const char* jambDepth = "jambDepth"; + static const char* jambDepth2 = "jambDepth2"; + static const char* objLoc = "objLoc"; + static const char* lower = "lower"; + static const char* directionType = "directionType"; + + static const char* startPoint = "startPoint"; + static const char* dirVector = "dirVector"; + } + + + namespace Beam + { + // Positioning + static const char* begC = "begC"; + static const char* endC = "endC"; + static const char* level = "level"; + static const char* isSlanted = "isSlanted"; + static const char* slantAngle = "slantAngle"; + static const char* beamShape = "beamShape"; + static const char* sequence = "sequence"; + static const char* curveAngle = "curveAngle"; + static const char* verticalCurveHeight = "verticalCurveHeight"; + static const char* isFlipped = "isFlipped"; + // End Cuts + static const char* nCuts = "nCuts"; + // Rreference Axis + static const char* anchorPoint = "anchorPoint"; + static const char* offset = "offset"; + static const char* profileAngle = "profileAngle"; + // Segment + static const char* segments = "segments"; + static const char* nSegments = "nSegments"; + static const char* nProfiles = "nProfiles"; + + + namespace BeamSegment + { + // Segment override materials + static const char* LeftMaterial = "leftMaterial"; + static const char* TopMaterial = "topMaterial"; + static const char* RightMaterial = "rightMaterial"; + static const char* BottomMaterial = "bottomMaterial"; + static const char* EndsMaterial = "endsMaterial"; + // Segment - The overridden materials are chained + static const char* MaterialsChained = "materialChained"; + // Segment + static const char* segmentData = "assemblySegmentData"; + } + + + // Scheme + static const char* nSchemes = "nSchemes"; + // Hole + static const char* HoleName = "Hole #%d"; + static const char* holeType = "holeType"; + static const char* holeContourOn = "holeContourOn"; + static const char* holeId = "holeId"; + static const char* centerx = "centerx"; + static const char* centerz = "centerz"; + static const char* width = "width"; + static const char* height = "height"; + // Floor Plan and Section - Floor Plan Display + static const char* DisplayOptionName = "displayOptionName"; + static const char* UncutProjectionModeName = "uncutProjectionMode"; + static const char* OverheadProjectionModeName = "overheadProjectionMode"; + static const char* ViewDepthLimitationName = "showProjectionName"; + // Floor Plan and Section - Cut Surfaces + static const char* cutContourLinePen = "cutContourLinePen"; + static const char* CutContourLinetypeName = "cutContourLineType"; + static const char* OverrideCutFillPenIndex = "overrideCutFillPen"; + static const char* OverrideCutFillBackgroundPenIndex = "overrideCutFillBackgroundPen"; + // Floor Plan and Section - Outlines + static const char* ShowOutlineIndex = "showOutline"; + static const char* UncutLinePenIndex = "uncutLinePen"; + static const char* UncutLinetypeName = "uncutLinetype"; + static const char* OverheadLinePenIndex = "overheadLinePen"; + static const char* OverheadLinetypeName = "overheadLinetype"; + static const char* HiddenLinePenIndex = "hiddenLinePen"; + static const char* HiddenLinetypeName = "hiddenLinetype"; + // Floor Plan and Section - Symbol + static const char* ShowReferenceAxisIndex = "showReferenceAxis"; + static const char* refPen = "referencePen"; + static const char* refLtype = "referenceLinetype"; + // Floor Plan and Section - Cover Fills + static const char* useCoverFill = "useCoverFill"; + static const char* useCoverFillFromSurface = "useCoverFillFromSurface"; + static const char* coverFillForegroundPen = "coverFillForegroundPen"; + static const char* coverFillBackgroundPen = "coverFillBackgroundPen"; + static const char* coverFillType = "coverFillType"; + static const char* CoverFillTransformationOrigoX = "coverFillTransformationOrigoX"; + static const char* CoverFillTransformationOrigoY = "coverFillTransformationOrigoY"; + static const char* CoverFillTransformationXAxisX = "coverFillTransformationXAxisX"; + static const char* CoverFillTransformationXAxisY = "coverFillTransformationXAxisY"; + static const char* CoverFillTransformationYAxisX = "coverFillTransformationYAxisX"; + static const char* CoverFillTransformationYAxisY = "coverFillTransformationYAxisY"; + } + + + namespace Column + { + // Positioning - geometry + static const char* origoPos = "origoPos"; + static const char* height = "height"; + // Positioning - story relation + static const char* bottomOffset = "bottomOffset"; + static const char* topOffset = "topOffset"; + static const char* relativeTopStory = "relativeTopStory"; + // Positioning - slanted column + static const char* isSlanted = "isSlanted"; + static const char* slantAngle = "slantAngle"; + static const char* slantDirectionAngle = "slantDirectionAngle"; + static const char* isFlipped = "isFlipped"; + // Positioning - wrapping + static const char* Wrapping = "wrapping"; + // Positioning - Defines the relation of column to zones (Zone Boundary, Reduce Zone Area Only, No Effect on Zones) + static const char* ColumnRelationToZoneName = "columnRelationToZoneName"; + // End Cuts + static const char* nCuts = "nCuts"; + // Reference Axis + static const char* coreAnchor = "coreAnchor"; + static const char* axisRotationAngle = "axisRotationAngle"; + // Segment + static const char* segments = "segments"; + static const char* nSegments = "nSegments"; + static const char* nProfiles = "nProfiles"; + + + namespace ColumnSegment + { + // Segment - Veneer attributes + static const char* VenType = "veneerType"; + static const char* VenBuildingMaterial = "veneerBuildingMaterial"; + static const char* VenThick = "veneerThick"; + // Segment - The extrusion overridden material name + static const char* ExtrusionSurfaceMaterial = "extrusionSurfaceMaterial"; + // Segment - The ends overridden material name + static const char* EndsSurfaceMaterial = "endsSurfaceMaterial"; + // Segment - The overridden materials are chained + static const char* MaterialsChained = "materialChained"; + // Segment + static const char* segmentData = "assemblySegmentData"; + } + + + // Scheme + static const char* nSchemes = "nSchemes"; + // Floor Plan and Section - Floor Plan Display + static const char* DisplayOptionName = "displayOptionName"; + static const char* ViewDepthLimitationName = "showProjectionName"; + // Floor Plan and Section - Cut Surfaces + static const char* corePen = "corePen"; + static const char* CoreLinetypeName = "contLtype"; + static const char* VeneerPenIndex = "venLinePen"; + static const char* VeneerLinetypeName = "venLineType"; + static const char* OverrideCutFillPenIndex = "overrideCutFillPen"; + static const char* OverrideCutFillBackgroundPenIndex = "overrideCutFillBackgroundPen"; + // Floor Plan and Section - Outlines + static const char* UncutLinePenIndex = "uncutLinePen"; + static const char* UncutLinetypeName = "uncutLinetype"; + static const char* OverheadLinePenIndex = "overheadLinePen"; + static const char* OverheadLinetypeName = "overheadLinetype"; + static const char* HiddenLinePenIndex = "hiddenLinePen"; + static const char* HiddenLinetypeName = "hiddenLinetype"; + // Floor Plan and Section - Floor Plan Symbol + static const char* CoreSymbolTypeName = "coreSymbolTypeName"; + static const char* coreSymbolPar1 = "coreSymbolPar1"; + static const char* coreSymbolPar2 = "coreSymbolPar2"; + static const char* CoreSymbolPenIndex = "coreSymbolPen"; + // Floor Plan and Section - Cover Fills + static const char* useCoverFill = "useCoverFill"; + static const char* useCoverFillFromSurface = "useCoverFillFromSurface"; + static const char* coverFillForegroundPen = "coverFillForegroundPen"; + static const char* coverFillBackgroundPen = "coverFillBackgroundPen"; + static const char* coverFillType = "coverFillType"; + static const char* CoverFillTransformationOrigoX = "coverFillTransformationOrigoX"; + static const char* CoverFillTransformationOrigoY = "coverFillTransformationOrigoY"; + static const char* CoverFillTransformationXAxisX = "coverFillTransformationXAxisX"; + static const char* CoverFillTransformationXAxisY = "coverFillTransformationXAxisY"; + static const char* CoverFillTransformationYAxisX = "coverFillTransformationYAxisX"; + static const char* CoverFillTransformationYAxisY = "coverFillTransformationYAxisY"; + } + + + namespace AssemblySegmentData { + static const char* circleBased = "circleBased"; + static const char* modelElemStructureType = "modelElemStructureType"; + static const char* nominalHeight = "nominalHeight"; + static const char* nominalWidth = "nominalWidth"; + static const char* isWidthAndHeightLinked = "isWidthAndHeightLinked"; + static const char* isHomogeneous = "isHomogeneous"; + static const char* endWidth = "endWidth"; + static const char* endHeight = "endHeight"; + static const char* isEndWidthAndHeightLinked = "isEndWidthAndHeightLinked"; + static const char* buildingMaterial = "buildingMaterial"; + static const char* profileAttrName = "profileAttrName"; + } + + + namespace AssemblySegmentSchemeData { + static const char* lengthType = "lengthType"; + static const char* fixedLength = "fixedLength"; + static const char* lengthProportion = "lengthProportion"; + } + + + namespace AssemblySegmentCutData { + static const char* cutType = "cutType"; + static const char* customAngle = "customAngle"; + } + + + namespace Object + { + // Main + static const char* pos = "pos"; + static const char* transform = "transform"; + } + + + namespace Opening + { + // Floor Plan Parameters + static const char* FloorPlanDisplayMode = "floorPlanDisplayMode"; + static const char* ConnectionMode = "connectionMode"; + + // Cut Surfaces Parameters + static const char* CutSurfacesUseLineOfCutElements = "cutsurfacesUseLineOfCutElements"; + static const char* CutSurfacesLinePenIndex = "cutsurfacesLinePenIndex"; + static const char* CutSurfacesLineIndex = "cutsurfacesLineIndex"; + + // Outlines Parameters + static const char* OutlinesStyle = "outlinesStyle"; + static const char* OutlinesUseLineOfCutElements = "outlinesUseLineOfCutElements"; // => Cut Surfaces Parameters-ben is megtalálható + static const char* OutlinesUncutLineIndex = "outlinesUncutLineIndex"; + static const char* OutlinesOverheadLineIndex = "outlinesOverheadLineIndex"; + static const char* OutlinesUncutLinePenIndex = "outlinesUncutLinePenIndex"; + static const char* OutlinesOverheadLinePenIndex = "outlinesOverheadLinePenIndex"; + + // Opening Cover Fills Parameters + static const char* UseCoverFills = "useCoverFills"; + static const char* UseFillsOfCutElements = "useFillsOfCutElements"; + static const char* CoverFillIndex = "coverFillIndex"; + static const char* CoverFillPenIndex = "coverFillPenIndex"; + static const char* CoverFillBackgroundPenIndex = "coverFillBackgroundPenIndex"; + static const char* CoverFillOrientation = "coverFillOrientation"; + + // Cover Fill Transformation Parameters + static const char* CoverFillTransformationOrigoX = "coverFillTransformationOrigoX"; + static const char* CoverFillTransformationOrigoY = "coverFillTransformationOrigoY"; + static const char* CoverFillTransformationOrigoZ = "coverFillTransformationOrigoZ"; + static const char* CoverFillTransformationXAxisX = "coverFillTransformationXAxisX"; + static const char* CoverFillTransformationXAxisY = "coverFillTransformationXAxisY"; + static const char* CoverFillTransformationXAxisZ = "coverFillTransformationXAxisZ"; + static const char* CoverFillTransformationYAxisX = "coverFillTransformationYAxisX"; + static const char* CoverFillTransformationYAxisY = "coverFillTransformationYAxisY"; + static const char* CoverFillTransformationYAxisZ = "coverFillTransformationYAxisZ"; + + // Reference Axis Parameters + static const char* ShowReferenceAxis = "showReferenceAxis"; + static const char* ReferenceAxisPenIndex = "referenceAxisPenIndex"; + static const char* ReferenceAxisLineTypeIndex = "referenceAxisLineTypeIndex"; + static const char* ReferenceAxisOverhang = "referenceAxisOverhang"; + + // Extrusion Geometry Parameters + static const char* ExtrusionGeometryBasePoint = "extrusionGeometryBasePoint"; + static const char* ExtrusionGeometryXAxis = "extrusionGeometryXAxis"; + static const char* ExtrusionGeometryYAxis = "extrusionGeometryYAxis"; + static const char* ExtrusionGeometryZAxis = "extrusionGeometryZAxis"; + static const char* BasePolygonType = "basePolygonType"; + static const char* Width = "width"; + static const char* Height = "height"; + static const char* Constraint = "constraint"; + static const char* Anchor = "anchor"; + static const char* AnchorIndex = "anchorIndex"; + static const char* AnchorAltitude = "anchorAltitude"; + static const char* LimitType = "limitType"; + static const char* ExtrusionStartOffSet = "extrusionStartOffSet"; + static const char* FiniteBodyLength = "finiteBodyLength"; + static const char* LinkedStatus = "linkedStatus"; + static const char* NCoords = "nCoords"; + static const char* NSubPolys = "nSubPolys"; + static const char* NArcs = "nArcs"; + } + + + namespace GridElement + { + // Main + static const char* begin = "begin"; + static const char* end = "end"; + static const char* angle = "angle"; + static const char* markerText = "markerText"; + static const char* isArc = "isArc"; + static const char* arcAngle = "arcAngle"; + } + + + namespace Slab + { + // Geometry and positioning + static const char* Thickness = "thickness"; + static const char* Structure = "structure"; + static const char* CompositeName = "compositeName"; + static const char* BuildingMaterialName = "buildingMaterialName"; + static const char* ReferencePlaneLocation = "referencePlaneLocation"; + // Edge trims + static const char* EdgeAngleType = "edgeAngleType"; + static const char* EdgeAngle = "edgeAngle"; + // Floor Plan and Section - Cut Surfaces + static const char* sectContPen = "sectContPen"; + static const char* sectContLtype = "sectContLtype"; + static const char* cutFillPen = "cutFillPen"; + static const char* cutFillBackgroundPen = "cutFillBackgroundPen"; + // Floor Plan and Section - Outlines + static const char* contourPen = "contourPen"; + static const char* contourLineType = "contourLineType"; + static const char* hiddenContourLinePen = "hiddenContourLinePen"; + static const char* hiddenContourLineType = "hiddenContourLineType"; + // Floor Plan and Section - Cover Fills + static const char* useFloorFill = "useFloorFill"; + static const char* floorFillPen = "floorFillPen"; + static const char* floorFillBGPen = "floorFillBGPen"; + static const char* floorFillName = "floorFillName"; + static const char* use3DHatching = "use3DHatching"; + static const char* hatchOrientation = "hatchOrientation"; + static const char* hatchOrientationOrigoX = "hatchOrientationOrigoX"; + static const char* hatchOrientationOrigoY = "hatchOrientationOrigoY"; + static const char* hatchOrientationXAxisX = "hatchOrientationXAxisX"; + static const char* hatchOrientationXAxisY = "hatchOrientationXAxisY"; + static const char* hatchOrientationYAxisX = "hatchOrientationYAxisX"; + static const char* hatchOrientationYAxisY = "hatchOrientationYAxisY"; + // Model + static const char* topMat = "topMat"; + static const char* sideMat = "sideMat"; + static const char* botMat = "botMat"; + static const char* materialsChained = "materialsChained"; + } + + + namespace Roof + { + // Geometry and positioning + static const char* RoofClassName = "roofClassName"; + static const char* PlaneRoofAngle = "planeRoofAngle"; + static const char* BaseLine = "baseLine"; + static const char* BegC = "begC"; + static const char* EndC = "endC"; + static const char* PosSign = "posSign"; + static const char* PivotPolygon = "pivotPolygon"; + + // Level + static const char* levels = "levels"; + static const char* LevelNum = "levelNum"; + + namespace RoofLevel { + static const char* LevelName = "Level #%d"; + static const char* levelData = "levels"; + } + + namespace LevelData { + static const char* LevelHeight = "levelHeight"; + static const char* LevelAngle = "levelAngle"; + } + + static const char* Thickness = "thickness"; + static const char* Structure = "structure"; + static const char* CompositeName = "compositeName"; + static const char* BuildingMaterialName = "buildingMaterialName"; + // Edge trims + static const char* EdgeAngleType = "edgeAngleType"; + static const char* EdgeAngle = "edgeAngle"; + // Floor Plan and Section - Floor Plan Display + static const char* DisplayOptionName = "displayOptionName"; + static const char* ViewDepthLimitationName = "showProjectionName"; + // Floor Plan and Section - Cut Surfaces + static const char* SectContPen = "sectContPen"; + static const char* SectContLtype = "sectContLtype"; + static const char* CutFillPen = "cutFillPen"; + static const char* CutFillBackgroundPen = "cutFillBackgroundPen"; + // Floor Plan and Section - Outlines + static const char* ContourPen = "contourPen"; + static const char* ContourLineType = "contourLineType"; + static const char* OverheadLinePen = "overheadLinePen"; + static const char* OverheadLinetype = "overheadLinetype"; + // Floor Plan and Section - Cover Fills + static const char* UseFloorFill = "useFloorFill"; + static const char* FloorFillPen = "floorFillPen"; + static const char* FloorFillBGPen = "floorFillBGPen"; + static const char* FloorFillName = "floorFillName"; + static const char* Use3DHatching = "use3DHatching"; + static const char* UseFillLocBaseLine = "useFillLocBaseLine"; + static const char* UseSlantedFill = "useSlantedFill"; + static const char* HatchOrientation = "hatchOrientation"; + static const char* HatchOrientationOrigoX = "hatchOrientationOrigoX"; + static const char* HatchOrientationOrigoY = "hatchOrientationOrigoY"; + static const char* HatchOrientationXAxisX = "hatchOrientationXAxisX"; + static const char* HatchOrientationXAxisY = "hatchOrientationXAxisY"; + static const char* HatchOrientationYAxisX = "hatchOrientationYAxisX"; + static const char* HatchOrientationYAxisY = "hatchOrientationYAxisY"; + // Model + static const char* TopMat = "topMat"; + static const char* SideMat = "sideMat"; + static const char* BotMat = "botMat"; + static const char* MaterialsChained = "materialsChained"; + static const char* TrimmingBodyName = "trimmingBodyName"; + } + + + namespace Shell + { + // Geometry and positioning + static const char* ShellClassName = "shellClassName"; + + static const char* BasePlane = "basePlane"; + + static const char* Flipped = "flipped"; + static const char* HasContour = "hasContour"; + static const char* NumHoles = "numHoles"; + static const char* ShellContourName = "shellContour #%d"; + static const char* ShellContourData = "shellContours"; + static const char* ShellContourPlane = "shellContourPlane"; + static const char* ShellContourPoly = "shellContourPoly"; + static const char* ShellContourHeight = "shellContourHeight"; + static const char* ShellContourID = "shellContourID"; + static const char* ShellContourSideTypeName = "sideTypeName"; + static const char* ShellContourSideAngle = "sideAngle"; + static const char* ShellContourEdgeTypeName = "edgeTypeName"; + static const char* ShellContourEdgeSideMaterial = "edgeSideMaterial"; + static const char* ShellContourEdgeName = "shellContourEdge #%d"; + static const char* ShellContourEdgeData = "shellContourEdges"; + static const char* DefaultEdgeType = "defaultEdgeType"; + + static const char* SlantAngle = "slantAngle"; + static const char* RevolutionAngle = "revolutionAngle"; + static const char* DistortionAngle = "distortionAngle"; + static const char* SegmentedSurfaces = "segmentedSurfaces"; + static const char* ShapePlaneTilt = "shapePlaneTilt"; + static const char* BegPlaneTilt = "begPlaneTilt"; + static const char* EndPlaneTilt = "endPlaneTilt"; + static const char* AxisBase = "axisBase"; + static const char* Plane1 = "plane1"; + static const char* Plane2 = "plane2"; + static const char* BegC = "begC"; + static const char* BegAngle = "begAngle"; + static const char* ExtrusionVector = "extrusionVector"; + static const char* ShapeDirection = "shapeDirection"; + static const char* DistortionVector = "distortionVector"; + static const char* MorphingRuleName = "morphingRuleName"; + + static const char* BegShapeEdge = "begShapeEdge"; + static const char* BegShapeEdgeTrimSideType = "begShapeEdgeTrimSideType"; + static const char* BegShapeEdgeTrimSideAngle = "begShapeEdgeTrimSideAngle"; + static const char* BegShapeEdgeSideMaterial = "begShapeEdgeSideMaterial"; + static const char* BegShapeEdgeType = "begShapeEdgeType"; + static const char* EndShapeEdge = "endShapeEdge"; + static const char* EndShapeEdgeTrimSideType = "endShapeEdgeTrimSideType"; + static const char* EndShapeEdgeTrimSideAngle = "endShapeEdgeTrimSideAngle"; + static const char* EndShapeEdgeSideMaterial = "endShapeEdgeSideMaterial"; + static const char* EndShapeEdgeType = "endShapeEdgeType"; + + static const char* ExtrudedEdge1 = "extrudedEdge1"; + static const char* ExtrudedEdgeTrimSideType1 = "extrudedEdgeTrimSideType1"; + static const char* ExtrudedEdgeTrimSideAngle1 = "extrudedEdgeTrimSideAngle1"; + static const char* ExtrudedEdgeSideMaterial1 = "extrudedEdgeSideMaterial1"; + static const char* ExtrudedEdgeType1 = "extrudedEdgeType1"; + static const char* ExtrudedEdge2 = "extrudedEdge2"; + static const char* ExtrudedEdgeTrimSideType2 = "extrudedEdgeTrimSideType2"; + static const char* ExtrudedEdgeTrimSideAngle2 = "extrudedEdgeTrimSideAngle2"; + static const char* ExtrudedEdgeSideMaterial2 = "extrudedEdgeSideMaterial2"; + static const char* ExtrudedEdgeType2 = "extrudedEdgeType2"; + + static const char* RevolvedEdge1 = "revolvedEdge1"; + static const char* RevolvedEdgeTrimSideType1 = "revolvedEdgeTrimSideType1"; + static const char* RevolvedEdgeTrimSideAngle1 = "revolvedEdgeTrimSideAngle1"; + static const char* RevolvedEdgeSideMaterial1 = "revolvedEdgeSideMaterial1"; + static const char* RevolvedEdgeType1 = "revolvedEdgeType1"; + static const char* RevolvedEdge2 = "revolvedEdge2"; + static const char* RevolvedEdgeTrimSideType2 = "revolvedEdgeTrimSideType2"; + static const char* RevolvedEdgeTrimSideAngle2 = "revolvedEdgeTrimSideAngle2"; + static const char* RevolvedEdgeSideMaterial2 = "revolvedEdgeSideMaterial2"; + static const char* RevolvedEdgeType2 = "revolvedEdgeType2"; + + static const char* RuledEdge1 = "ruledEdge1"; + static const char* RuledEdgeTrimSideType1 = "ruledEdgeTrimSideType1"; + static const char* RuledEdgeTrimSideAngle1 = "ruledEdgeTrimSideAngle1"; + static const char* RuledEdgeSideMaterial1 = "ruledEdgeSideMaterial1"; + static const char* RuledEdgeType1 = "ruledEdgeType1"; + static const char* RuledEdge2 = "ruledEdge2"; + static const char* RuledEdgeTrimSideType2 = "ruledEdgeTrimSideType2"; + static const char* RuledEdgeTrimSideAngle2 = "ruledEdgeTrimSideAngle2"; + static const char* RuledEdgeSideMaterial2 = "ruledEdgeSideMaterial2"; + static const char* RuledEdgeType2 = "ruledEdgeType2"; + + static const char* Thickness = "thickness"; + static const char* Structure = "structure"; + static const char* CompositeName = "compositeName"; + static const char* BuildingMaterialName = "buildingMaterialName"; + // Edge trims + static const char* EdgeAngleType = "edgeAngleType"; + static const char* EdgeAngle = "edgeAngle"; + // Floor Plan and Section - Floor Plan Display + static const char* DisplayOptionName = "displayOptionName"; + static const char* ViewDepthLimitationName = "showProjectionName"; + // Floor Plan and Section - Cut Surfaces + static const char* SectContPen = "sectContPen"; + static const char* SectContLtype = "sectContLtype"; + static const char* CutFillPen = "cutFillPen"; + static const char* CutFillBackgroundPen = "cutFillBackgroundPen"; + // Floor Plan and Section - Outlines + static const char* ContourPen = "contourPen"; + static const char* ContourLineType = "contourLineType"; + static const char* OverheadLinePen = "overheadLinePen"; + static const char* OverheadLinetype = "overheadLinetype"; + // Floor Plan and Section - Cover Fills + static const char* UseFloorFill = "useFloorFill"; + static const char* FloorFillPen = "floorFillPen"; + static const char* FloorFillBGPen = "floorFillBGPen"; + static const char* FloorFillName = "floorFillName"; + static const char* Use3DHatching = "use3DHatching"; + static const char* UseFillLocBaseLine = "useFillLocBaseLine"; + static const char* UseSlantedFill = "useSlantedFill"; + static const char* HatchOrientation = "hatchOrientation"; + static const char* HatchOrientationOrigoX = "hatchOrientationOrigoX"; + static const char* HatchOrientationOrigoY = "hatchOrientationOrigoY"; + static const char* HatchOrientationXAxisX = "hatchOrientationXAxisX"; + static const char* HatchOrientationXAxisY = "hatchOrientationXAxisY"; + static const char* HatchOrientationYAxisX = "hatchOrientationYAxisX"; + static const char* HatchOrientationYAxisY = "hatchOrientationYAxisY"; + // Model + static const char* TopMat = "topMat"; + static const char* SideMat = "sideMat"; + static const char* BotMat = "botMat"; + static const char* MaterialsChained = "materialsChained"; + static const char* TrimmingBodyName = "trimmingBodyName"; + } + + + namespace Skylight + { + // Geometry and positioning + static const char* VertexID = "vertexID"; + static const char* SkylightFixMode = "skylightFixMode"; + static const char* SkylightAnchor = "skylightAnchor"; + static const char* AnchorPosition = "anchorPosition"; + static const char* AnchorLevel = "anchorLevel"; + static const char* AzimuthAngle = "azimuthAngle"; + static const char* ElevationAngle = "elevationAngle"; + } + + + namespace Room + { + static const char* Name = "name"; + static const char* Number = "number"; + static const char* BasePoint = "basePoint"; + static const char* Height = "height"; + static const char* Area = "area"; + static const char* Volume = "volume"; + } + + + namespace Model + { + static const char* Vertices = "vertices"; + static const char* VertexX = "x"; + static const char* VertexY = "y"; + static const char* VertexZ = "z"; + static const char* PointId1 = "v1"; + static const char* PointId2 = "v2"; + static const char* PolygonId1 = "p1"; + static const char* PolygonId2 = "p2"; + static const char* EdgeStatus = "s"; + static const char* HiddenEdgeValueName = "HiddenEdge"; + static const char* SmoothEdgeValueName = "SmoothEdge"; + static const char* VisibleEdgeValueName = "VisibleEdge"; + static const char* Polygons = "polygons"; + static const char* Materials = "materials"; + static const char* PointIds = "pointIds"; + static const char* MaterialName = "name"; + static const char* Transparency = "transparency"; + static const char* AmbientColor = "ambientColor"; + static const char* EmissionColor = "emissionColor"; + static const char* Material = "material"; + static const char* Model = "model"; + static const char* ModelIds = "modelIds"; + static const char* Ids = "ids"; + static const char* Edges = "edges"; + } + + + namespace Level + { + static const char* TypeName = "level"; + static const char* Index = "index"; + static const char* Name = "name"; + static const char* Elevation = "elevation"; + static const char* Units = "units"; + } + + + namespace Point + { + static const char* X = "x"; + static const char* Y = "y"; + static const char* Z = "z"; + static const char* Units = "units"; + } + + namespace Material + { + static const char* Name = "name"; + } + } diff --git a/ConnectorArchicad/AddOn/Sources/AddOn/PropertyExportManager.cpp b/ConnectorArchicad/AddOn/Sources/AddOn/PropertyExportManager.cpp index 5849c1fc82..68a320db04 100644 --- a/ConnectorArchicad/AddOn/Sources/AddOn/PropertyExportManager.cpp +++ b/ConnectorArchicad/AddOn/Sources/AddOn/PropertyExportManager.cpp @@ -2,7 +2,6 @@ #include "Utility.hpp" #include "MD5Channel.hpp" - PropertyExportManager* PropertyExportManager::instance = nullptr; PropertyExportManager* PropertyExportManager::GetInstance () @@ -286,6 +285,16 @@ GS::UInt64 GenerateFingerPrint (const API_ElemType& elementType, const bool& sen } +/*! + Get property definitions for a specified element + @param element The target element to retrieve the property definitions for + @param sendProperties True to export the Archicad properties attached to the element + @param sendListingParameters True to export calculated listing parameters from the element, e.g. top/bottom surface area etc + @param systemItemPairs Array pairing a classification system ID against a classification item ID (attached to the target element) + @param elementDefinitions The element property definitions (retrieved in this function) + @param componentsDefinitions The component property definitions (paired with the component ID, retrieved in this function) + @return NoError if the definitions were retrieved without errors + */ GSErrCode PropertyExportManager::GetElementDefinitions (const API_Element& element, const bool& sendProperties, const bool& sendListingParameters, const GS::Array>& systemItemPairs, GS::Array& elementDefinitions, GS::Array>>& componentsDefinitions) { GSErrCode err = NoError; @@ -296,13 +305,15 @@ GSErrCode PropertyExportManager::GetElementDefinitions (const API_Element& eleme // element-level properties { + //Create a hash value for target element and prefs GS::UInt64 fingerPrint = GenerateFingerPrint (elementType, sendProperties, sendListingParameters, systemItemPairs); + //If we've already encountered this combo, use the property definitions we already found (will always be the same - saves a lot of time) if (cache.ContainsKey (fingerPrint)) { elementDefinitions = cache.Get (fingerPrint).first; elementUserDefinedDefinitions = cache.Get (fingerPrint).second; - } - else { + } else { if (sendProperties) { + //Collect user-defined property definitions for the target element when the user requests them err = ACAPI_Element_GetPropertyDefinitions (element.header.guid, API_PropertyDefinitionFilter_UserDefined, elementUserDefinedDefinitions); if (err != NoError) return err; @@ -310,10 +321,11 @@ GSErrCode PropertyExportManager::GetElementDefinitions (const API_Element& eleme GS::Array elementUserLevelBuiltInDefinitions; if (sendListingParameters) { + //Collect built-in property definitions for the target element when the user requests them err = ACAPI_Element_GetPropertyDefinitions (element.header.guid, API_PropertyDefinitionFilter_UserLevelBuiltIn, elementUserLevelBuiltInDefinitions); if (err != NoError) return err; - + //The list of definitions can include many things we don't want - filter it to definitions we're really interested in err = FilterDefinitionsByDefinitionIds (elementUserLevelBuiltInDefinitions, propertyGroupFilter.elementPropertiesFilter); if (err != NoError) return err; @@ -321,7 +333,7 @@ GSErrCode PropertyExportManager::GetElementDefinitions (const API_Element& eleme elementDefinitions = elementUserDefinedDefinitions; elementDefinitions.Append (elementUserLevelBuiltInDefinitions); - + //Add the definitions to the cache to save looking them up again for the same target specs cache.Add (fingerPrint, GS::Pair, GS::Array> (elementDefinitions, elementUserDefinedDefinitions)); } } diff --git a/ConnectorArchicad/AddOn/Sources/AddOn/PropertyExportManager.hpp b/ConnectorArchicad/AddOn/Sources/AddOn/PropertyExportManager.hpp index 9710da6832..474df8d9bf 100644 --- a/ConnectorArchicad/AddOn/Sources/AddOn/PropertyExportManager.hpp +++ b/ConnectorArchicad/AddOn/Sources/AddOn/PropertyExportManager.hpp @@ -19,6 +19,7 @@ class PropertyExportManager { PropertyGroupFilter propertyGroupFilter; GS::HashSet complexElementsToSkipFromComponentListing; + ///Cache of property definitions keyed by a hash of the target specifications (e.g. element type) - saves looking these up repeatedly GS::HashTable, GS::Array>> cache; protected: diff --git a/ConnectorArchicad/AddOn/Sources/AddOn/ResourceIds.hpp b/ConnectorArchicad/AddOn/Sources/AddOn/ResourceIds.hpp index 147706b24e..3b71424e11 100644 --- a/ConnectorArchicad/AddOn/Sources/AddOn/ResourceIds.hpp +++ b/ConnectorArchicad/AddOn/Sources/AddOn/ResourceIds.hpp @@ -24,6 +24,7 @@ #define GetElementBaseDataCommandName "GetElementBaseData"; #define GetGridElementCommandName "GetGridElementData"; #define GetObjectDataCommandName "GetObjectData"; +#define GetOpeningDataCommandName "GetOpeningData"; #define GetSlabDataCommandName "GetSlabData"; #define GetRoomDataCommandName "GetRoomData"; #define GetRoofDataCommandName "GetRoofData"; @@ -38,6 +39,7 @@ #define CreateColumnCommandName "CreateColumn"; #define CreateGridElementCommandName "CreateGridElement"; #define CreateObjectCommandName "CreateObject"; +#define CreateOpeningCommandName "CreateOpening"; #define CreateSlabCommandName "CreateSlab"; #define CreateSkylightCommandName "CreateSkylight"; #define CreateRoofCommandName "CreateRoof"; diff --git a/ConnectorArchicad/AddOn/Sources/AddOn/TypeNameTables.cpp b/ConnectorArchicad/AddOn/Sources/AddOn/TypeNameTables.cpp index 52c35275b6..610edbc1cc 100644 --- a/ConnectorArchicad/AddOn/Sources/AddOn/TypeNameTables.cpp +++ b/ConnectorArchicad/AddOn/Sources/AddOn/TypeNameTables.cpp @@ -301,3 +301,63 @@ const GS::HashTable coreSymbolTypeNames { 3, "X"}, { 4, "CrossHair"} }; + + +const GS::HashTable openingFloorPlanDisplayModeNames +{ + { API_OpeningSymbolic, "Symbolic"}, + { API_OpeningSymbolicCut, "Symbolic Cut"}, + { API_OpeningSymbolicOverhead, "Symbolic Overhead"} +}; + + +const GS::HashTable openingFloorPlanConnectionModeNames +{ + { API_OpeningDisconnected, "Disconnected"}, + { API_OpeningConnected, "Connected"} +}; + + +const GS::HashTable openingOutlinesStyleNames +{ + { API_OpeningHideBorder, "Hide Border"}, + { API_OpeningShowUncutBorder, "Show Uncut Border"}, + { API_OpeningShowOverheadBorder, "Show Overhead Border"} +}; + + +const GS::HashTable openingBasePolygonTypeNames +{ + { API_OpeningBasePolygonRectangular, "Rectangular"}, + { API_OpeningBasePolygonCircular, "Circular"}, + { API_OpeningBasePolygonCustom, "Custom"} +}; + + +const GS::HashTable openingAnchorNames +{ + { APIAnc_LT, "Left Top"}, + { APIAnc_MT, "Middle Top"}, + { APIAnc_RT, "Right Top"}, + { APIAnc_LM, "Left Middle"}, + { APIAnc_MM, "Middle Middle"}, + { APIAnc_RM, "Right Middle"}, + { APIAnc_LB, "Left Bottom"}, + { APIAnc_MB, "Middle Bottom"}, + { APIAnc_RB, "Right Bottom"} +}; + + +const GS::HashTable openingLimitTypeNames +{ + { API_OpeningLimitInfinite, "Infinite"}, + { API_OpeningLimitFinite, "Finite"}, + { API_OpeningLimitHalfInfinite, "Half Infinite"} +}; + + +const GS::HashTable openingLinkedStatusNames +{ + { API_OpeningLinked, "Linked"}, + { API_OpeningNotLinked, "Not Linked"} +}; \ No newline at end of file diff --git a/ConnectorArchicad/AddOn/Sources/AddOn/TypeNameTables.hpp b/ConnectorArchicad/AddOn/Sources/AddOn/TypeNameTables.hpp index b478c0ca60..12d8488997 100644 --- a/ConnectorArchicad/AddOn/Sources/AddOn/TypeNameTables.hpp +++ b/ConnectorArchicad/AddOn/Sources/AddOn/TypeNameTables.hpp @@ -45,4 +45,12 @@ extern const GS::HashTable venTypeNames; extern const GS::HashTable coreSymbolTypeNames; +extern const GS::HashTable openingFloorPlanDisplayModeNames; +extern const GS::HashTable openingFloorPlanConnectionModeNames; +extern const GS::HashTable openingOutlinesStyleNames; +extern const GS::HashTable openingBasePolygonTypeNames; +extern const GS::HashTable openingAnchorNames; + +extern const GS::HashTable openingLimitTypeNames; +extern const GS::HashTable openingLinkedStatusNames; #endif diff --git a/ConnectorArchicad/AddOn/Sources/AddOnResources/RFIX/AddOnFix.grc b/ConnectorArchicad/AddOn/Sources/AddOnResources/RFIX/AddOnFix.grc index 02d663f4d1..6ad0e355df 100644 --- a/ConnectorArchicad/AddOn/Sources/AddOnResources/RFIX/AddOnFix.grc +++ b/ConnectorArchicad/AddOn/Sources/AddOnResources/RFIX/AddOnFix.grc @@ -6,9 +6,15 @@ AC_MDID_LOC /* Set AC_MDID_LOC value as your local id. */ } +#ifdef macintosh 'GICN' 10001 "AddOnIcon" { - "AddOnIcon" + "AddOnIconMac" } +#else +'GICN' 10001 "AddOnIcon" { + "AddOnIconWin" +} +#endif 'STR#' ID_FIX_ELEMENT_TYPE_STRINGS "Element Type Strings" { /* [ 1] */ "Wall" diff --git a/ConnectorArchicad/AddOn/Sources/AddOnResources/RFIX/Images/AddOnIconMac_18x18.svg b/ConnectorArchicad/AddOn/Sources/AddOnResources/RFIX/Images/AddOnIconMac_18x18.svg new file mode 100644 index 0000000000..0f1cfa4bb2 --- /dev/null +++ b/ConnectorArchicad/AddOn/Sources/AddOnResources/RFIX/Images/AddOnIconMac_18x18.svg @@ -0,0 +1,18 @@ + + AddOnIcon_18x18-svg + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ConnectorArchicad/AddOn/Sources/AddOnResources/RFIX/Images/AddOnIconWin_18x18.svg b/ConnectorArchicad/AddOn/Sources/AddOnResources/RFIX/Images/AddOnIconWin_18x18.svg new file mode 100644 index 0000000000..fbe59e2a11 --- /dev/null +++ b/ConnectorArchicad/AddOn/Sources/AddOnResources/RFIX/Images/AddOnIconWin_18x18.svg @@ -0,0 +1,17 @@ + + AddOnIconWin_18x18-svg + + + + + + + + + + + \ No newline at end of file diff --git a/ConnectorArchicad/AddOn/Sources/AddOnResources/RFIX/Images/AddOnIcon_18x18.svg b/ConnectorArchicad/AddOn/Sources/AddOnResources/RFIX/Images/AddOnIcon_18x18.svg deleted file mode 100644 index 4031449236..0000000000 --- a/ConnectorArchicad/AddOn/Sources/AddOnResources/RFIX/Images/AddOnIcon_18x18.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/ConnectorArchicad/ConnectorArchicad/Assets/icon-mac.icns b/ConnectorArchicad/ConnectorArchicad/Assets/icon-mac.icns new file mode 100644 index 0000000000..6ef6c0c333 Binary files /dev/null and b/ConnectorArchicad/ConnectorArchicad/Assets/icon-mac.icns differ diff --git a/ConnectorArchicad/ConnectorArchicad/Communication/Commands/Command_CreateOpening.cs b/ConnectorArchicad/ConnectorArchicad/Communication/Commands/Command_CreateOpening.cs new file mode 100644 index 0000000000..ab65f3dde1 --- /dev/null +++ b/ConnectorArchicad/ConnectorArchicad/Communication/Commands/Command_CreateOpening.cs @@ -0,0 +1,42 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Speckle.Core.Models; +using Speckle.Newtonsoft.Json; +using Objects.BuiltElements.Archicad; + +namespace Archicad.Communication.Commands; + +sealed internal class CreateOpening : ICommand> +{ + [JsonObject(MemberSerialization.OptIn)] + public sealed class Parameters + { + [JsonProperty("openings")] + private IEnumerable Datas { get; } + + public Parameters(IEnumerable datas) + { + Datas = datas; + } + } + + [JsonObject(MemberSerialization.OptIn)] + private sealed class Result + { + [JsonProperty("applicationObjects")] + public IEnumerable ApplicationObjects { get; private set; } + } + + private IEnumerable Datas { get; } + + public CreateOpening(IEnumerable datas) + { + Datas = datas; + } + + public async Task> Execute() + { + var result = await HttpCommandExecutor.Execute("CreateOpening", new Parameters(Datas)); + return result == null ? null : result.ApplicationObjects; + } +} diff --git a/ConnectorArchicad/ConnectorArchicad/Communication/Commands/Command_GetElementBaseData.cs b/ConnectorArchicad/ConnectorArchicad/Communication/Commands/Command_GetElementBaseData.cs index 56fe20729e..82d9499427 100644 --- a/ConnectorArchicad/ConnectorArchicad/Communication/Commands/Command_GetElementBaseData.cs +++ b/ConnectorArchicad/ConnectorArchicad/Communication/Commands/Command_GetElementBaseData.cs @@ -1,13 +1,12 @@ using System.Collections.Generic; using System.Threading.Tasks; -using Speckle.Core.Kits; -using Speckle.Newtonsoft.Json; -using Objects.BuiltElements.Archicad; using ConnectorArchicad.Communication.Commands; +using Objects.BuiltElements.Archicad; +using Speckle.Newtonsoft.Json; namespace Archicad.Communication.Commands; -sealed internal class GetElementBaseData : GetDataBase, ICommand> +sealed internal class GetElementBaseData : GetDataBase, ICommand { [JsonObject(MemberSerialization.OptIn)] private sealed class Result @@ -19,17 +18,13 @@ private sealed class Result public GetElementBaseData(IEnumerable applicationIds, bool sendProperties, bool sendListingParameters) : base(applicationIds, sendProperties, sendListingParameters) { } - public async Task> Execute() + public async Task Execute() { - Result result = await HttpCommandExecutor.Execute( + dynamic result = await HttpCommandExecutor.Execute( "GetElementBaseData", new Parameters(ApplicationIds, SendProperties, SendListingParameters) ); - foreach (var directShape in result.Datas) - { - directShape.units = Units.Meters; - } - return result.Datas; + return (Speckle.Newtonsoft.Json.Linq.JArray)result["elements"]; } } diff --git a/ConnectorArchicad/ConnectorArchicad/Communication/Commands/Command_GetOpening.cs b/ConnectorArchicad/ConnectorArchicad/Communication/Commands/Command_GetOpening.cs new file mode 100644 index 0000000000..5e64507ed7 --- /dev/null +++ b/ConnectorArchicad/ConnectorArchicad/Communication/Commands/Command_GetOpening.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using ConnectorArchicad.Communication.Commands; + +namespace Archicad.Communication.Commands; + +sealed internal class GetOpeningData : GetDataBase, ICommand +{ + public GetOpeningData(IEnumerable applicationIds, bool sendProperties, bool sendListingParameters) + : base(applicationIds, sendProperties, sendListingParameters) { } + + public async Task Execute() + { + dynamic result = await HttpCommandExecutor.Execute( + "GetOpeningData", + new Parameters(ApplicationIds, SendProperties, SendListingParameters) + ); + + return (Speckle.Newtonsoft.Json.Linq.JArray)result["openings"]; + } +} diff --git a/ConnectorArchicad/ConnectorArchicad/ConnectorArchicad.csproj b/ConnectorArchicad/ConnectorArchicad/ConnectorArchicad.csproj index e83aa31a00..8341b16c5e 100644 --- a/ConnectorArchicad/ConnectorArchicad/ConnectorArchicad.csproj +++ b/ConnectorArchicad/ConnectorArchicad/ConnectorArchicad.csproj @@ -6,6 +6,7 @@ enable ConnectorArchicad Assets\icon.ico + true diff --git a/ConnectorArchicad/ConnectorArchicad/ConnectorBinding.cs b/ConnectorArchicad/ConnectorArchicad/ConnectorBinding.cs index 8a34225426..650f5c2000 100644 --- a/ConnectorArchicad/ConnectorArchicad/ConnectorBinding.cs +++ b/ConnectorArchicad/ConnectorArchicad/ConnectorBinding.cs @@ -220,7 +220,8 @@ public override async Task SendStream(StreamState state, ProgressViewMod return await Speckle.Core.Api.Helpers.Send( IdentifyStream(state), commitObject, - state.CommitMessage, + state.CommitMessage + ?? $"Sent {progress.Report.ReportObjects.Count} objects from {HostApplications.Archicad.Name + " " + archicadVersion}.", HostApplications.Archicad.Name ); } diff --git a/ConnectorArchicad/ConnectorArchicad/Converters/ConverterArchicad.cs b/ConnectorArchicad/ConnectorArchicad/Converters/ConverterArchicad.cs index 29506c6a58..c00821258c 100644 --- a/ConnectorArchicad/ConnectorArchicad/Converters/ConverterArchicad.cs +++ b/ConnectorArchicad/ConnectorArchicad/Converters/ConverterArchicad.cs @@ -68,6 +68,7 @@ public bool CanConvertToNativeImplemented(Base @object) Objects.BuiltElements.Roof _ => true, Objects.BuiltElements.Room _ => true, Objects.BuiltElements.Wall _ => true, + Objects.BuiltElements.Opening _ => true, // Archicad elements Objects.BuiltElements.Archicad.ArchicadDoor _ => true, diff --git a/ConnectorArchicad/ConnectorArchicad/Converters/Converters/DirectShapeConverter.cs b/ConnectorArchicad/ConnectorArchicad/Converters/Converters/DirectShapeConverter.cs index c47a607798..fdb4711d10 100644 --- a/ConnectorArchicad/ConnectorArchicad/Converters/Converters/DirectShapeConverter.cs +++ b/ConnectorArchicad/ConnectorArchicad/Converters/Converters/DirectShapeConverter.cs @@ -7,6 +7,7 @@ using Archicad.Model; using Archicad.Operations; using Objects.Geometry; +using Speckle.Core.Kits; using Speckle.Core.Models; using Speckle.Core.Models.GraphTraversal; @@ -73,6 +74,7 @@ CancellationToken token IEnumerable result; result = await AsyncCommandProcessor.Execute(new Communication.Commands.CreateDirectShape(directShapes), token); + return result is null ? new List() : result.ToList(); } @@ -83,7 +85,8 @@ ConversionOptions conversionOptions ) { var elementModels = elements as ElementModelData[] ?? elements.ToArray(); - IEnumerable data = await AsyncCommandProcessor.Execute( + + Speckle.Newtonsoft.Json.Linq.JArray jArray = await AsyncCommandProcessor.Execute( new Communication.Commands.GetElementBaseData( elementModels.Select(e => e.applicationId), conversionOptions.SendProperties, @@ -93,7 +96,7 @@ ConversionOptions conversionOptions ); var directShapes = new List(); - if (data is null) + if (jArray is null) { return directShapes; } @@ -103,12 +106,18 @@ ConversionOptions conversionOptions context?.cumulativeTimer?.Begin(ConnectorArchicad.Properties.OperationNameTemplates.ConvertToSpeckle, Type.Name) ) { - foreach (Objects.BuiltElements.Archicad.DirectShape directShape in data) + foreach (Speckle.Newtonsoft.Json.Linq.JToken jToken in jArray) { + // convert between DTOs + Objects.BuiltElements.Archicad.DirectShape directShape = + Archicad.Converters.Utils.ConvertToSpeckleDTOs(jToken); + { + directShape.units = Units.Meters; directShape.displayValue = Operations.ModelConverter.MeshesToSpeckle( elementModels.First(e => e.applicationId == directShape.applicationId).model ); + directShapes.Add(directShape); } } diff --git a/ConnectorArchicad/ConnectorArchicad/Converters/Converters/OpeningConverter.cs b/ConnectorArchicad/ConnectorArchicad/Converters/Converters/OpeningConverter.cs new file mode 100644 index 0000000000..a7bf40d31f --- /dev/null +++ b/ConnectorArchicad/ConnectorArchicad/Converters/Converters/OpeningConverter.cs @@ -0,0 +1,135 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Archicad.Communication; +using Archicad.Model; +using Speckle.Core.Kits; +using Speckle.Core.Logging; +using Speckle.Core.Models; +using Speckle.Core.Models.GraphTraversal; + +namespace Archicad.Converters; + +public sealed class OpeningConverter : IConverter +{ + public Type Type => typeof(Objects.BuiltElements.Opening); + + public Task> ConvertToArchicad( + IEnumerable elements, + CancellationToken token + ) + { + var openings = new List(); + + var context = Archicad.Helpers.Timer.Context.Peek; + using ( + context?.cumulativeTimer?.Begin(ConnectorArchicad.Properties.OperationNameTemplates.ConvertToNative, Type.Name) + ) + { + foreach (var tc in elements) + { + token.ThrowIfCancellationRequested(); + + switch (tc.current) + { + case Objects.BuiltElements.Archicad.ArchicadOpening archicadOpening: + archicadOpening.parentApplicationId = tc.parent.current.id; + Archicad.Converters.Utils.ConvertToArchicadDTOs( + archicadOpening + ); + openings.Add(archicadOpening); + break; + case Objects.BuiltElements.Opening opening: + try + { + Objects.Geometry.Vector extrusionBasePoint, + extrusionXAxis, + extrusionYAxis, + extrusionZAxis; + double width, + height; + Operations.ModelConverter.GetExtrusionParametersFromOutline( + opening.outline, + out extrusionBasePoint, + out extrusionXAxis, + out extrusionYAxis, + out extrusionZAxis, + out width, + out height + ); + openings.Add( + new Objects.BuiltElements.Archicad.ArchicadOpening + { + id = opening.id, + applicationId = opening.applicationId, + parentApplicationId = tc.parent.current.id, + extrusionGeometryBasePoint = new Objects.Geometry.Point(extrusionBasePoint), + extrusionGeometryXAxis = extrusionXAxis, + extrusionGeometryYAxis = extrusionYAxis, + extrusionGeometryZAxis = extrusionZAxis, + width = width, + height = height, + anchorIndex = 4, + } + ); + } + catch (SpeckleException ex) + { + SpeckleLog.Logger.Error(ex.Message); + } + break; + } + } + + IEnumerable result; + result = AsyncCommandProcessor.Execute(new Communication.Commands.CreateOpening(openings), token).Result; + + return Task.FromResult(result is null ? new List() : result.ToList()); + } + } + + public async Task> ConvertToSpeckle( + IEnumerable elements, + CancellationToken token, + ConversionOptions conversionOptions + ) + { + var elementModels = elements as ElementModelData[] ?? elements.ToArray(); + + Speckle.Newtonsoft.Json.Linq.JArray jArray = await AsyncCommandProcessor.Execute( + new Communication.Commands.GetOpeningData( + elementModels.Select(e => e.applicationId), + conversionOptions.SendProperties, + conversionOptions.SendListingParameters + ), + token + ); + + List openings = new(); + if (jArray is null) + { + return openings; + } + + var context = Archicad.Helpers.Timer.Context.Peek; + using ( + context?.cumulativeTimer?.Begin(ConnectorArchicad.Properties.OperationNameTemplates.ConvertToNative, Type.Name) + ) + { + foreach (Speckle.Newtonsoft.Json.Linq.JToken jToken in jArray) + { + Objects.BuiltElements.Archicad.ArchicadOpening opening = + Archicad.Converters.Utils.ConvertToSpeckleDTOs(jToken); + { + opening.outline = Operations.ModelConverter.CreateOpeningOutline(opening); + opening.units = Units.Meters; + } + openings.Add(opening); + } + } + + return openings; + } +} diff --git a/ConnectorArchicad/ConnectorArchicad/Converters/Converters/Utils.cs b/ConnectorArchicad/ConnectorArchicad/Converters/Converters/Utils.cs index 792d1992e7..4bb483f880 100644 --- a/ConnectorArchicad/ConnectorArchicad/Converters/Converters/Utils.cs +++ b/ConnectorArchicad/ConnectorArchicad/Converters/Converters/Utils.cs @@ -5,6 +5,7 @@ using Objects; using Objects.BuiltElements.Archicad; using Objects.Geometry; +using Objects.Other; using Speckle.Core.Kits; using Speckle.Core.Logging; @@ -12,6 +13,8 @@ namespace Archicad.Converters; public static class Utils { + private const string MATERIAL_QUANTITIES_TAG = "materialQuantities"; + public static Point VertexToPoint(MeshModel.Vertex vertex) { return new Point @@ -68,6 +71,14 @@ public static MeshModel.Vertex PointToNative(Point point, string? units = null) }; } + public static Vector ScaleToNative(Vector vector, string? units = null) + { + units ??= vector.units; + var scale = Units.GetConversionFactor(units, Units.Meters); + + return new Vector(vector.x * scale, vector.y * scale, vector.z * scale); + } + public static Polycurve PolycurveToSpeckle(ElementShape.Polyline archiPolyline) { var poly = new Polycurve @@ -151,7 +162,13 @@ public static ElementShape PolycurvesToElementShape(ICurve outline, List return shape; } + /// + /// Convert incoming JSON (from Archicad) to an equivalent (specified) object type + /// + /// The incoming JSON (handled as a dynamic object) + /// An object of the specified type (T) public static T ConvertToSpeckleDTOs(dynamic jObject) + where T : Speckle.Core.Models.Base { Objects.BuiltElements.Archicad.ArchicadLevel level = null; if (jObject.level != null) @@ -176,11 +193,20 @@ public static T ConvertToSpeckleDTOs(dynamic jObject) jObject.Remove("componentProperties"); } + //Seek optional material quantities attached to the element (volume/area associated with a building material) + List materialQuantities = null; + if (jObject.materialQuantities != null) + { + materialQuantities = jObject.materialQuantities.ToObject>(); + jObject.Remove(MATERIAL_QUANTITIES_TAG); + } + T speckleObject = jObject.ToObject(); if (level != null) { - PropertyInfo propLevel = speckleObject.GetType().GetProperty("archicadLevel"); + PropertyInfo propLevel = + speckleObject.GetType().GetProperty("archicadLevel") ?? speckleObject.GetType().GetProperty("level"); propLevel.SetValue(speckleObject, level); } @@ -196,6 +222,11 @@ public static T ConvertToSpeckleDTOs(dynamic jObject) propComponentProperties.SetValue(speckleObject, ComponentProperties.ToBase(componentProperties)); } + if (materialQuantities != null) + { + speckleObject[MATERIAL_QUANTITIES_TAG] = materialQuantities; + } + return speckleObject; } diff --git a/ConnectorArchicad/ConnectorArchicad/Converters/ElementConverterManager.cs b/ConnectorArchicad/ConnectorArchicad/Converters/ElementConverterManager.cs index 330860564c..287ba885c4 100644 --- a/ConnectorArchicad/ConnectorArchicad/Converters/ElementConverterManager.cs +++ b/ConnectorArchicad/ConnectorArchicad/Converters/ElementConverterManager.cs @@ -13,6 +13,7 @@ using Column = Objects.BuiltElements.Column; using Door = Objects.BuiltElements.Archicad.ArchicadDoor; using Fenestration = Objects.BuiltElements.Archicad.ArchicadFenestration; +using Opening = Objects.BuiltElements.Opening; using Floor = Objects.BuiltElements.Floor; using Roof = Objects.BuiltElements.Roof; using Wall = Objects.BuiltElements.Wall; @@ -20,6 +21,7 @@ using Skylight = Objects.BuiltElements.Archicad.ArchicadSkylight; using GridLine = Objects.BuiltElements.GridLine; using DesktopUI2.Models; +using Objects.BuiltElements.Archicad; namespace Archicad; @@ -95,13 +97,24 @@ private ElementConverterManager() allObjects.AddRange(objects); // subelements translated into "elements" property of the parent - if (typeof(Fenestration).IsAssignableFrom(elemenType)) + if (typeof(Fenestration).IsAssignableFrom(elemenType) || typeof(Opening).IsAssignableFrom(elemenType)) { Collection elementCollection = null; foreach (Base item in objects) { - Base parent = allObjects.Find(x => x.applicationId == ((Fenestration)item).parentApplicationId); + string parentApplicationId = null; + + if (item is Fenestration fenestration) + { + parentApplicationId = fenestration.parentApplicationId; + } + else if (item is ArchicadOpening opening) + { + parentApplicationId = opening.parentApplicationId; + } + + Base parent = allObjects.Find(x => x.applicationId == parentApplicationId); if (parent == null) { @@ -247,6 +260,11 @@ bool forReceive return Converters[typeof(Roof)]; } + if (elementType.IsSubclassOf(typeof(Opening))) + { + return Converters[typeof(Opening)]; + } + if (elementType.IsAssignableFrom(typeof(Objects.BuiltElements.Room))) { return Converters[typeof(Archicad.Room)]; diff --git a/ConnectorArchicad/ConnectorArchicad/Converters/ElementTypeProvider.cs b/ConnectorArchicad/ConnectorArchicad/Converters/ElementTypeProvider.cs index da11c33ced..a2cb24e846 100644 --- a/ConnectorArchicad/ConnectorArchicad/Converters/ElementTypeProvider.cs +++ b/ConnectorArchicad/ConnectorArchicad/Converters/ElementTypeProvider.cs @@ -10,6 +10,7 @@ using Wall = Objects.BuiltElements.Archicad.ArchicadWall; using Window = Objects.BuiltElements.Archicad.ArchicadWindow; using Skylight = Objects.BuiltElements.Archicad.ArchicadSkylight; +using Opening = Objects.BuiltElements.Archicad.ArchicadOpening; namespace Archicad; @@ -28,7 +29,8 @@ public static class ElementTypeProvider { "Door", typeof(Door) }, { "Window", typeof(Window) }, { "Skylight", typeof(Skylight) }, - { "GridElement", typeof(GridElement) } + { "GridElement", typeof(GridElement) }, + { "Opening", typeof(Opening) } }; public static Type GetTypeByName(string name) diff --git a/ConnectorArchicad/ConnectorArchicad/Converters/ModelConverter.cs b/ConnectorArchicad/ConnectorArchicad/Converters/ModelConverter.cs index f19785f14a..34f6546e20 100644 --- a/ConnectorArchicad/ConnectorArchicad/Converters/ModelConverter.cs +++ b/ConnectorArchicad/ConnectorArchicad/Converters/ModelConverter.cs @@ -3,11 +3,14 @@ using System.Linq; using Archicad.Converters; using Archicad.Model; +using Objects.BuiltElements.Archicad; +using Objects; using Objects.Geometry; using Objects.Other; using Objects.Utils; using Speckle.Core.Kits; using static Archicad.Model.MeshModel; +using Speckle.Core.Logging; namespace Archicad.Operations; @@ -393,4 +396,208 @@ MeshModel meshModel return angleCos > angleCosLimit; } + + public static ICurve CreateOpeningOutline(ArchicadOpening opening) + { + double halfWidth = opening.width / 2.0 ?? 0.0; + double halfHeight = opening.height / 2.0 ?? 0.0; + Vector basePoint = new(0, 0, 0); + Vector extrusionBasePoint = new(opening.extrusionGeometryBasePoint); + + // Speckle datastructure does not handle the translation component, which we will use manually later, so its left empty. + System.DoubleNumerics.Matrix4x4 rotMatrix = + new( + (float)opening.extrusionGeometryXAxis.x, + (float)opening.extrusionGeometryXAxis.y, + (float)opening.extrusionGeometryXAxis.z, + 0, + (float)opening.extrusionGeometryYAxis.x, + (float)opening.extrusionGeometryYAxis.y, + (float)opening.extrusionGeometryYAxis.z, + 0, + (float)opening.extrusionGeometryZAxis.x, + (float)opening.extrusionGeometryZAxis.y, + (float)opening.extrusionGeometryZAxis.z, + 0, + 0, + 0, + 0, + 1 + ); + + Objects.Other.Transform transform = new(System.DoubleNumerics.Matrix4x4.Transpose(rotMatrix)); + + AdjustBasePoint(ref basePoint, halfWidth, halfHeight, opening.anchorIndex ?? 0); + + return opening.basePolygonType == "Rectangular" + ? CreateRectangle(basePoint, transform, extrusionBasePoint, halfWidth, halfHeight) + : CreateEllipse(basePoint, transform, extrusionBasePoint, halfWidth, halfHeight, opening); + } + + private static readonly Action[] anchorActions = new Action[] + { + (Vector basePoint, double halfWidth, double halfHeight) => + { + basePoint.x = halfWidth; + basePoint.y = -halfHeight; + }, // APIAnc_LT + (Vector basePoint, double halfWidth, double halfHeight) => + { + basePoint.y = -halfHeight; + }, // APIAnc_MT + (Vector basePoint, double halfWidth, double halfHeight) => + { + basePoint.x = -halfWidth; + basePoint.y = -halfHeight; + }, // APIAnc_RT + (Vector basePoint, double halfWidth, double halfHeight) => + { + basePoint.x = halfWidth; + }, // APIAnc_LM + (Vector basePoint, double halfWidth, double halfHeight) => { }, // APIAnc_MM + (Vector basePoint, double halfWidth, double halfHeight) => + { + basePoint.x = -halfWidth; + }, // APIAnc_RM + (Vector basePoint, double halfWidth, double halfHeight) => + { + basePoint.x = halfWidth; + basePoint.y = halfHeight; + }, // APIAnc_LB + (Vector basePoint, double halfWidth, double halfHeight) => + { + basePoint.y = halfHeight; + }, // APIAnc_MB + (Vector basePoint, double halfWidth, double halfHeight) => + { + basePoint.x = -halfWidth; + basePoint.y = halfHeight; + } // APIAnc_RB + }; + + private static void AdjustBasePoint(ref Vector basePoint, double halfWidth, double halfHeight, int anchor) + { + if (anchor >= 0 && anchor < anchorActions.Length) + { + anchorActions[anchor](basePoint, halfWidth, halfHeight); + } + } + + private static Polyline CreateRectangle( + Vector basePoint, + Objects.Other.Transform transform, + Vector extrusionBasePoint, + double halfWidth, + double halfHeight + ) + { + var poly = new Objects.Geometry.Polyline + { + value = new List(), + closed = true, + units = Units.Meters + }; + + // Coordinates of the four corners of the rectangle + Vector[] points = + { + new(-halfWidth, -halfHeight, 0), + new(halfWidth, -halfHeight, 0), + new(halfWidth, halfHeight, 0), + new(-halfWidth, halfHeight, 0) + }; + + // Transform the points to the correct position + foreach (var point in points) + { + Vector transformedPoint = point + basePoint; + transformedPoint.TransformTo(transform, out transformedPoint); + transformedPoint += extrusionBasePoint; + poly.value.AddRange(transformedPoint.ToList()); + } + + // Close the polyline + poly.value.AddRange(poly.value.Take(3)); + + return poly; + } + + private static Ellipse CreateEllipse( + Vector basePoint, + Objects.Other.Transform transform, + Vector extrusionBasePoint, + double halfWidth, + double halfHeight, + ArchicadOpening opening + ) + { + Vector centerPoint = new(basePoint.x, basePoint.y, basePoint.z); + centerPoint.TransformTo(transform, out centerPoint); + centerPoint += extrusionBasePoint; + + Point center = new(centerPoint.x, centerPoint.y, centerPoint.z); + + Objects.Geometry.Plane plane = + new(center, opening.extrusionGeometryZAxis, opening.extrusionGeometryXAxis, opening.extrusionGeometryYAxis); + + return new Ellipse(plane, halfWidth, halfHeight, Units.Meters); + } + + public static void GetExtrusionParametersFromOutline( + ICurve outline, + out Vector extrusionBasePoint, + out Vector extrusionXAxis, + out Vector extrusionYAxis, + out Vector extrusionZAxis, + out double width, + out double height + ) + { + // Assign default values to out parameters + extrusionBasePoint = new Vector(); + extrusionXAxis = new Vector(); + extrusionYAxis = new Vector(); + extrusionZAxis = new Vector(); + width = 0; + height = 0; + + if (outline is not Polyline polyline) + { + throw new SpeckleException( + $"An opening of type {outline.GetType()} has been found - only Polyline openings are currently supported" + ); + } + + // Form the 4 points of the rectangle from the polyline + List points = Enumerable + .Range(0, polyline.value.Count / 3) + .Select( + i => new Vector(polyline.value[i * 3], polyline.value[i * 3 + 1], polyline.value[i * 3 + 2], polyline.units) + ) + .ToList(); + + Vector bottomLeft = Utils.ScaleToNative(points[0]); + Vector topLeft = Utils.ScaleToNative(points[1]); + Vector topRight = Utils.ScaleToNative(points[2]); + Vector bottomRight = Utils.ScaleToNative(points[3]); + + // We set the anchor point to Middle-Middle, so we can calculate the extrusion base point more easily like so. + extrusionBasePoint = (bottomLeft + bottomRight + topRight + topLeft) * 0.25; + + Vector verticalDiff = topRight - bottomRight; + height = verticalDiff.Length; + + extrusionYAxis = verticalDiff / height; + + Vector horizontalDiff = bottomRight - bottomLeft; + width = horizontalDiff.Length; + + // Calculate the extrusion X axis + extrusionXAxis = horizontalDiff / width; + + // The last extrusion axis will be the cross product of the other two + extrusionZAxis = Vector.CrossProduct(extrusionXAxis, extrusionYAxis); + + extrusionZAxis.Normalize(); + } } diff --git a/ConnectorArchicad/ConnectorArchicad/Info.plist b/ConnectorArchicad/ConnectorArchicad/Info.plist new file mode 100644 index 0000000000..e42154e574 --- /dev/null +++ b/ConnectorArchicad/ConnectorArchicad/Info.plist @@ -0,0 +1,39 @@ + + + + + CFBundleName + acconnector + CFBundleDisplayName + Archicad Connector + CFBundleIdentifier + systems.speckle.acconnector + CFBundleVersion + 2.0.0 + CFBundlePackageType + APPL + CFBundleSignature + spkl + CFBundleExecutable + ConnectorArchicad + CFBundleIconFile + icon-mac.icns + CFBundleShortVersionString + 2.0.0 + CFBundleURLTypes + + + CFBundleURLName + Speckle + CFBundleURLSchemes + + speckle + + + + NSPrincipalClass + NSApplication + NSHighResolutionCapable + + + \ No newline at end of file diff --git a/ConnectorAutocadCivil/ConnectorAutocadCivil/UI/ConnectorBindingsAutocadCivil.Receive.cs b/ConnectorAutocadCivil/ConnectorAutocadCivil/UI/ConnectorBindingsAutocadCivil.Receive.cs index dddcd877d2..e17f04d0c8 100644 --- a/ConnectorAutocadCivil/ConnectorAutocadCivil/UI/ConnectorBindingsAutocadCivil.Receive.cs +++ b/ConnectorAutocadCivil/ConnectorAutocadCivil/UI/ConnectorBindingsAutocadCivil.Receive.cs @@ -545,12 +545,15 @@ and not ApplicationObject.State.Updated // add property sets if this is Civil3D #if CIVIL - if (obj["propertySets"] is IReadOnlyList list) + if (obj["propertySets"] is Base propertySetsBase) { List> propertySets = new(); - foreach (var listObj in list) + foreach (object baseObj in propertySetsBase.GetMembers(DynamicBaseMemberType.Dynamic).Values) { - propertySets.Add(listObj as Dictionary); + if (baseObj is Dictionary propertySet) + { + propertySets.Add(propertySet); + } } try diff --git a/ConnectorAutocadCivil/ConnectorAutocadCivil/UI/ConnectorBindingsAutocadCivil.Send.cs b/ConnectorAutocadCivil/ConnectorAutocadCivil/UI/ConnectorBindingsAutocadCivil.Send.cs index 2c96f1eacd..0b329dd4d3 100644 --- a/ConnectorAutocadCivil/ConnectorAutocadCivil/UI/ConnectorBindingsAutocadCivil.Send.cs +++ b/ConnectorAutocadCivil/ConnectorAutocadCivil/UI/ConnectorBindingsAutocadCivil.Send.cs @@ -246,8 +246,7 @@ ref int convertedCount #if CIVIL // add property sets if this is Civil3D - var propertySets = obj.GetPropertySets(tr); - if (propertySets.Count > 0) + if (obj.TryGetPropertySets(tr, out Base propertySets)) { converted["propertySets"] = propertySets; } diff --git a/ConnectorAutocadCivil/ConnectorAutocadCivil/Utils.cs b/ConnectorAutocadCivil/ConnectorAutocadCivil/Utils.cs index eed231d48f..461dec506e 100644 --- a/ConnectorAutocadCivil/ConnectorAutocadCivil/Utils.cs +++ b/ConnectorAutocadCivil/ConnectorAutocadCivil/Utils.cs @@ -460,34 +460,36 @@ public static void SetPropertySets(this Entity entity, Document doc, List /// /// - public static List> GetPropertySets(this DBObject obj, Transaction tr) + public static bool TryGetPropertySets(this DBObject obj, Transaction tr, out Base propertySets) { - var sets = new List>(); - ObjectIdCollection propertySets = null; + propertySets = new(); + + ObjectIdCollection propertySetIds = null; try { - propertySets = PropertyDataServices.GetPropertySets(obj); + propertySetIds = PropertyDataServices.GetPropertySets(obj); } catch (Autodesk.AutoCAD.Runtime.Exception e) { // This may throw if property sets do not exist on the object. // afaik, trycatch is necessary because there is no way to preemptively check if the set already exists. + return false; } - if (propertySets is null) + if (propertySetIds is null || propertySetIds.Count == 0) { - return sets; + return false; } - foreach (ObjectId id in propertySets) + foreach (ObjectId id in propertySetIds) { - var setDictionary = new Dictionary(); + Dictionary setDictionary = new() ; PropertySet propertySet = (PropertySet)tr.GetObject(id, OpenMode.ForRead); PropertySetDefinition setDef = (PropertySetDefinition)tr.GetObject(propertySet.PropertySetDefinition, OpenMode.ForRead); PropertyDefinitionCollection propDef = setDef.Definitions; - var propDefs = new Dictionary(); + Dictionary propDefs = new(); foreach (PropertyDefinition def in propDef) { propDefs.Add(def.Id, def); @@ -507,10 +509,11 @@ public static List> GetPropertySets(this DBObject obj if (setDictionary.Count > 0) { - sets.Add(CleanDictionary(setDictionary)); + propertySets[propertySet.PropertySetDefinitionName] = CleanDictionary(setDictionary); } } - return sets; + + return true; } // Handles object types from property set dictionaries diff --git a/ConnectorAutocadCivil/README.md b/ConnectorAutocadCivil/README.md index 9b74423e0d..02e9f4749b 100644 --- a/ConnectorAutocadCivil/README.md +++ b/ConnectorAutocadCivil/README.md @@ -4,13 +4,13 @@ ## Introduction -This is the âš ALPHAâš  version of the Speckle 2.0 AutoCAD Civil3D Connector. Currently, it only supports the very most basic conversions - please leave any comments, suggestions, and feature requests in our [Making Speckle](https://discourse.speckle.works/t/new-speckle-2-0-autocad-civil3d-suggestions/1155) forum discussion thread! +This is the âš Betaâš  version of the Speckle 2.0 AutoCAD Civil3D Connector. Currently, it supports the basic objects for both Autocad and Civil3D (refer to our docs for a full list) - please leave any comments, suggestions, and feature requests in our [Making Speckle](https://discourse.speckle.works/t/new-speckle-2-0-autocad-civil3d-suggestions/1155) forum discussion thread! ## Documentation Comprehensive developer and user documentation can be found in our: -#### 📚 [Speckle Docs website](https://speckle.guide/dev/) +#### 📚 [Speckle Docs website](https://speckle.guide/user/autocadcivil.html) ## Developing & Debugging @@ -21,8 +21,8 @@ Comprehensive developer and user documentation can be found in our: #### Supported versions -- AutoCAD: 2021, 2022 -- Civil3D: 2021, 2022 +- AutoCAD: 2021, 2022, 2023, 2024, 2025 +- Civil3D: 2021, 2022, 2023, 2024, 2025 ### Getting Started diff --git a/ConnectorCore/DllConflictManagement/DllConflictManager.cs b/ConnectorCore/DllConflictManagement/DllConflictManager.cs index 26f34f13c2..3921b8ee36 100644 --- a/ConnectorCore/DllConflictManagement/DllConflictManager.cs +++ b/ConnectorCore/DllConflictManagement/DllConflictManager.cs @@ -101,11 +101,29 @@ HashSet visitedAssemblies } } - private bool ShouldSkipCheckingConflictBecauseOfAssemblyLocation(Assembly loadedAssembly) + private static bool PathIsValid(string path) { + if (path.Any(x => Path.GetInvalidPathChars().Contains(x))) + { + return false; + } + return true; + } + + private bool ShouldSkipCheckingConflictBecauseOfAssemblyLocation(Assembly? loadedAssembly) + { + if (string.IsNullOrWhiteSpace(loadedAssembly?.Location)) + { + return false; + } + string location = loadedAssembly!.Location; + if (!PathIsValid(location)) + { + return false; + } foreach (var exactPath in _exactAssemblyPathsToIgnore) { - if (Path.GetDirectoryName(loadedAssembly.Location) == exactPath) + if (Path.GetDirectoryName(location) == exactPath) { return true; } @@ -113,7 +131,7 @@ private bool ShouldSkipCheckingConflictBecauseOfAssemblyLocation(Assembly loaded foreach (var pathFragment in _assemblyPathFragmentsToIgnore) { - if (loadedAssembly.Location.Contains(pathFragment)) + if (location.Contains(pathFragment)) { return true; } diff --git a/ConnectorCore/DllConflictManagement/EventEmitter/DllConflictEventEmitter.cs b/ConnectorCore/DllConflictManagement/EventEmitter/DllConflictEventEmitter.cs index 3c7ffdb69b..7feda8a3b8 100644 --- a/ConnectorCore/DllConflictManagement/EventEmitter/DllConflictEventEmitter.cs +++ b/ConnectorCore/DllConflictManagement/EventEmitter/DllConflictEventEmitter.cs @@ -9,9 +9,9 @@ namespace Speckle.DllConflictManagement.EventEmitter; /// public class DllConflictEventEmitter { - public event EventHandler OnError; - public event EventHandler OnInfo; - public event EventHandler OnAction; + public event EventHandler? OnError; + public event EventHandler? OnInfo; + public event EventHandler? OnAction; private bool _shouldEmitEvents; private readonly List _savedErrorEvents = new(); diff --git a/ConnectorDynamo/ConnectorDynamo/CreateStreamNode/CreateStreamViewCustomization.cs b/ConnectorDynamo/ConnectorDynamo/CreateStreamNode/CreateStreamViewCustomization.cs index bc03bd4d7b..004466b5e3 100644 --- a/ConnectorDynamo/ConnectorDynamo/CreateStreamNode/CreateStreamViewCustomization.cs +++ b/ConnectorDynamo/ConnectorDynamo/CreateStreamNode/CreateStreamViewCustomization.cs @@ -7,6 +7,7 @@ using System.Windows; using System.Windows.Controls; using System.Windows.Threading; +using Speckle.Core.Helpers; namespace Speckle.ConnectorDynamo.CreateStreamNode; @@ -73,7 +74,7 @@ private void UpdateContextMenu() }; viewStreamMenuItem.Click += (a, e) => { - System.Diagnostics.Process.Start($"{createNode.Stream.ServerUrl}/streams/{createNode.Stream.StreamId}"); + Open.Url($"{createNode.Stream.ServerUrl}/streams/{createNode.Stream.StreamId}"); }; _nodeView.grid.ContextMenu.Items.Add(viewStreamMenuItem); } diff --git a/ConnectorDynamo/ConnectorDynamo/ReceiveNode/ReceiveViewCustomization.cs b/ConnectorDynamo/ConnectorDynamo/ReceiveNode/ReceiveViewCustomization.cs index 89acd2cc77..88301a5d2e 100644 --- a/ConnectorDynamo/ConnectorDynamo/ReceiveNode/ReceiveViewCustomization.cs +++ b/ConnectorDynamo/ConnectorDynamo/ReceiveNode/ReceiveViewCustomization.cs @@ -6,6 +6,7 @@ using System.Windows; using System.Windows.Controls; using System.Windows.Threading; +using Speckle.Core.Helpers; namespace Speckle.ConnectorDynamo.ReceiveNode; @@ -99,7 +100,7 @@ private void UpdateContextMenu() }; viewStreamMenuItem.Click += (a, e) => { - System.Diagnostics.Process.Start($"{receiveNode.Stream.ServerUrl}/streams/{receiveNode.Stream.StreamId}"); + Open.Url($"{receiveNode.Stream.ServerUrl}/streams/{receiveNode.Stream.StreamId}"); }; _nodeView.grid.ContextMenu.Items.Add(viewStreamMenuItem); } diff --git a/ConnectorDynamo/ConnectorDynamo/SendNode/SendViewCustomization.cs b/ConnectorDynamo/ConnectorDynamo/SendNode/SendViewCustomization.cs index 8c8e888296..fbc6f138e7 100644 --- a/ConnectorDynamo/ConnectorDynamo/SendNode/SendViewCustomization.cs +++ b/ConnectorDynamo/ConnectorDynamo/SendNode/SendViewCustomization.cs @@ -7,6 +7,7 @@ using System.Windows; using System.Windows.Controls; using System.Windows.Threading; +using Speckle.Core.Helpers; namespace Speckle.ConnectorDynamo.SendNode; @@ -83,7 +84,7 @@ private void UpdateContextMenu() var viewStream = new MenuItem { Header = $"View stream {stream.StreamId} @ {stream.ServerUrl} online ↗" }; viewStream.Click += (a, e) => { - System.Diagnostics.Process.Start($"{stream.ServerUrl}/streams/{stream.StreamId}"); + Open.Url($"{stream.ServerUrl}/streams/{stream.StreamId}"); }; customMenuItems.Add(viewStream); _nodeView.grid.ContextMenu.Items.Add(viewStream); diff --git a/ConnectorDynamo/ConnectorDynamo/ViewNode/View.cs b/ConnectorDynamo/ConnectorDynamo/ViewNode/View.cs index 4ad7db39ec..d3a699cfa6 100644 --- a/ConnectorDynamo/ConnectorDynamo/ViewNode/View.cs +++ b/ConnectorDynamo/ConnectorDynamo/ViewNode/View.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.ComponentModel; -using System.Diagnostics; using System.Linq; using Dynamo.Engine; using Dynamo.Graph.Nodes; @@ -9,6 +8,7 @@ using Newtonsoft.Json; using ProtoCore.AST.AssociativeAST; using Speckle.ConnectorDynamo.Functions; +using Speckle.Core.Helpers; namespace Speckle.ConnectorDynamo.ViewNode; @@ -103,7 +103,7 @@ internal void ViewStream() } AnalyticsUtils.TrackNodeRun("Stream View"); - Process.Start(Url); + Open.Url(Url); } //Cache input value each time it comes available diff --git a/ConnectorDynamo/ConnectorDynamoExtension/SpeckleWatchHandler.cs b/ConnectorDynamo/ConnectorDynamoExtension/SpeckleWatchHandler.cs index 310458f7aa..c212bc035f 100644 --- a/ConnectorDynamo/ConnectorDynamoExtension/SpeckleWatchHandler.cs +++ b/ConnectorDynamo/ConnectorDynamoExtension/SpeckleWatchHandler.cs @@ -6,6 +6,7 @@ using ProtoCore.Mirror; using Speckle.Core.Credentials; +using Speckle.Core.Helpers; using Speckle.Core.Logging; namespace Speckle.ConnectorDynamo.Extension; @@ -34,7 +35,7 @@ WatchHandlerCallback callback node.Clicked += () => { - System.Diagnostics.Process.Start(account.serverInfo.url); + Open.Url(account.serverInfo.url); }; node.Link = account.serverInfo.url; diff --git a/ConnectorGrasshopper/ConnectorGrasshopperShared/Accounts/Accounts.ListAccounts.cs b/ConnectorGrasshopper/ConnectorGrasshopperShared/Accounts/Accounts.ListAccounts.cs index c83d8a55d7..3e87ecbfad 100755 --- a/ConnectorGrasshopper/ConnectorGrasshopperShared/Accounts/Accounts.ListAccounts.cs +++ b/ConnectorGrasshopper/ConnectorGrasshopperShared/Accounts/Accounts.ListAccounts.cs @@ -1,7 +1,6 @@ #nullable enable using System; using System.Collections.Generic; -using System.Diagnostics; using System.Drawing; using System.Linq; using System.Windows.Forms; @@ -13,6 +12,7 @@ using Grasshopper.Kernel.Special; using Grasshopper.Kernel.Types; using Speckle.Core.Credentials; +using Speckle.Core.Helpers; namespace ConnectorGrasshopper.Accounts; @@ -47,7 +47,7 @@ public override bool AppendMenuItems(ToolStripDropDown menu) "Launch the Speckle Manager to add new accounts, or remove old ones.", (s, e) => { - Process.Start("speckle://goto=accounts"); + Open.Url("speckle://goto=accounts"); } ); return true; diff --git a/ConnectorGrasshopper/ConnectorGrasshopperShared/Conversion/ToSpeckleTaskCapableComponent.cs b/ConnectorGrasshopper/ConnectorGrasshopperShared/Conversion/ToSpeckleTaskCapableComponent.cs index bb5999362f..9b988a71b3 100644 --- a/ConnectorGrasshopper/ConnectorGrasshopperShared/Conversion/ToSpeckleTaskCapableComponent.cs +++ b/ConnectorGrasshopper/ConnectorGrasshopperShared/Conversion/ToSpeckleTaskCapableComponent.cs @@ -127,10 +127,7 @@ private IGH_Goo DoWork(object item, IGH_DataAccess DA) if (converted == null) { - AddRuntimeMessage( - GH_RuntimeMessageLevel.Warning, - $"Cannot convert item {DA.ParameterTargetPath(0)}[{DA.ParameterTargetIndex(0)}] to Speckle." - ); + AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Cannot convert {item} to Speckle."); return new GH_SpeckleBase(); } diff --git a/ConnectorGrasshopper/ConnectorGrasshopperShared/Extras/Utilities.cs b/ConnectorGrasshopper/ConnectorGrasshopperShared/Extras/Utilities.cs index 022dcfb1c8..e7f71c8af1 100644 --- a/ConnectorGrasshopper/ConnectorGrasshopperShared/Extras/Utilities.cs +++ b/ConnectorGrasshopper/ConnectorGrasshopperShared/Extras/Utilities.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Drawing; using System.Linq; +using System.Reflection; using System.Text.RegularExpressions; using System.Threading; using Grasshopper; @@ -16,6 +17,10 @@ using Speckle.Core.Logging; using Speckle.Core.Models; +#if RHINO8_OR_GREATER +using Grasshopper.Rhinoceros.Model; +#endif + namespace ConnectorGrasshopper.Extras; public static class Utilities @@ -476,14 +481,24 @@ public static object TryConvertItemToSpeckle( { if (value is null) { - return value; + return null; } - string refId = GetRefId(value); + value = UnwrapRhino8Object(value); if (value is IGH_Goo) { - value = value.GetType().GetProperty("Value").GetValue(value); + var valuePropInfo = value + .GetType() + .GetField("m_value", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + if (valuePropInfo != null) + { + var tempValue = valuePropInfo.GetValue(value); + if (tempValue != null) + { + value = tempValue; + } + } } if (value.GetType().IsSimpleType()) @@ -496,6 +511,7 @@ public static object TryConvertItemToSpeckle( var result = converter.ConvertToSpeckle(value); if (result != null) { + string refId = GetRefId(value); result.applicationId = refId; } @@ -522,6 +538,28 @@ public static object TryConvertItemToSpeckle( return null; } + private static object UnwrapRhino8Object(object value) + { +#if RHINO8_OR_GREATER + // INFO: Fill in here as we enable conversion for other non-model object types. + switch (value) + { + case ModelObject modelObject: + { + var propInfo = value + .GetType() + .GetProperty("Geometry", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + + return propInfo?.GetValue(value) ?? value; + } + default: + return value; + } +#else + return value; +#endif + } + public static string GetRefId(object value) { dynamic r = value; diff --git a/ConnectorGrasshopper/ConnectorGrasshopperShared/Loader.cs b/ConnectorGrasshopper/ConnectorGrasshopperShared/Loader.cs index 7a2f00ab71..01663e210f 100644 --- a/ConnectorGrasshopper/ConnectorGrasshopperShared/Loader.cs +++ b/ConnectorGrasshopper/ConnectorGrasshopperShared/Loader.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.Linq; using System.Threading.Tasks; @@ -213,17 +212,17 @@ private void AddSpeckleMenu(MenuStrip mainMenu) speckleMenu.DropDown.Items.Add( "Community Forum", Resources.forum16, - (o, args) => Process.Start("https://speckle.community/tag/grasshopper") + (o, args) => Open.Url("https://speckle.community/tag/grasshopper") ); speckleMenu.DropDown.Items.Add( "Tutorials", Resources.tutorials16, - (o, args) => Process.Start("https://speckle.systems/tag/grasshopper/") + (o, args) => Open.Url("https://speckle.systems/tag/grasshopper/") ); speckleMenu.DropDown.Items.Add( "Docs", Resources.docs16, - (o, args) => Process.Start("https://speckle.guide/user/grasshopper.html") + (o, args) => Open.Url("https://speckle.guide/user/grasshopper.html") ); speckleMenu.DropDown.Items.Add(new ToolStripSeparator()); @@ -257,11 +256,11 @@ private void AddSpeckleMenu(MenuStrip mainMenu) if (File.Exists(path) || Directory.Exists(path)) { - Process.Start(path); + Open.File(path); } else { - Process.Start(new ProcessStartInfo("https://speckle.systems/download") { UseShellExecute = true }); + Open.Url("https://speckle.systems/download"); } } catch (Exception ex) when (!ex.IsFatal()) @@ -314,9 +313,9 @@ private void CreateHeadlessTemplateMenu() Directory.CreateDirectory(path); } #if MAC - Process.Start("file://" + path); + Open.File("file://" + path); #else - Process.Start("explorer.exe", "/select, " + path); + Open.File("explorer.exe", "/select, " + path); #endif } ); diff --git a/ConnectorGrasshopper/ConnectorGrasshopperShared/Ops/Deprecated/Operations.ReceiveComponent.cs b/ConnectorGrasshopper/ConnectorGrasshopperShared/Ops/Deprecated/Operations.ReceiveComponent.cs index 6e535cd975..e4dd583fac 100644 --- a/ConnectorGrasshopper/ConnectorGrasshopperShared/Ops/Deprecated/Operations.ReceiveComponent.cs +++ b/ConnectorGrasshopper/ConnectorGrasshopperShared/Ops/Deprecated/Operations.ReceiveComponent.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; -using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Drawing; using System.Linq; @@ -23,6 +22,7 @@ using Speckle.Core.Api; using Speckle.Core.Api.SubscriptionModels; using Speckle.Core.Credentials; +using Speckle.Core.Helpers; using Speckle.Core.Logging; using Speckle.Core.Models; using Speckle.Core.Models.Extensions; @@ -273,8 +273,7 @@ public override void AppendAdditionalMenuItems(ToolStripDropDown menu) Menu_AppendItem( menu, $"View commit {ReceivedCommitId} @ {StreamWrapper.ServerUrl} online ↗", - (s, e) => - Process.Start($"{StreamWrapper.ServerUrl}/streams/{StreamWrapper.StreamId}/commits/{ReceivedCommitId}") + (s, e) => Open.Url($"{StreamWrapper.ServerUrl}/streams/{StreamWrapper.StreamId}/commits/{ReceivedCommitId}") ); } } diff --git a/ConnectorGrasshopper/ConnectorGrasshopperShared/Ops/Deprecated/Operations.SendComponent.cs b/ConnectorGrasshopper/ConnectorGrasshopperShared/Ops/Deprecated/Operations.SendComponent.cs index c160861f57..e381ec57e9 100644 --- a/ConnectorGrasshopper/ConnectorGrasshopperShared/Ops/Deprecated/Operations.SendComponent.cs +++ b/ConnectorGrasshopper/ConnectorGrasshopperShared/Ops/Deprecated/Operations.SendComponent.cs @@ -21,6 +21,7 @@ using Rhino; using Speckle.Core.Api; using Speckle.Core.Credentials; +using Speckle.Core.Helpers; using Speckle.Core.Logging; using Speckle.Core.Models; using Speckle.Core.Models.Extensions; @@ -179,7 +180,7 @@ public override void AppendAdditionalMenuItems(ToolStripDropDown menu) Menu_AppendItem( menu, $"View commit {ow.CommitId} @ {ow.ServerUrl} online ↗", - (s, e) => Process.Start($"{ow.ServerUrl}/streams/{ow.StreamId}/commits/{ow.CommitId}") + (s, e) => Open.Url($"{ow.ServerUrl}/streams/{ow.StreamId}/commits/{ow.CommitId}") ); } } diff --git a/ConnectorGrasshopper/ConnectorGrasshopperShared/Ops/Deprecated/Operations.SendComponentSync.cs b/ConnectorGrasshopper/ConnectorGrasshopperShared/Ops/Deprecated/Operations.SendComponentSync.cs index 8ab545b007..9296efe98b 100644 --- a/ConnectorGrasshopper/ConnectorGrasshopperShared/Ops/Deprecated/Operations.SendComponentSync.cs +++ b/ConnectorGrasshopper/ConnectorGrasshopperShared/Ops/Deprecated/Operations.SendComponentSync.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Drawing; using System.Linq; @@ -14,6 +13,7 @@ using Grasshopper.Kernel.Types; using Speckle.Core.Api; using Speckle.Core.Credentials; +using Speckle.Core.Helpers; using Speckle.Core.Kits; using Speckle.Core.Models; using Speckle.Core.Models.Extensions; @@ -149,7 +149,7 @@ protected override void AppendAdditionalComponentMenuItems(ToolStripDropDown men Menu_AppendItem( menu, $"View commit {ow.CommitId} @ {ow.ServerUrl} online ↗", - (s, e) => Process.Start($"{ow.ServerUrl}/streams/{ow.StreamId}/commits/{ow.CommitId}") + (s, e) => Open.Url($"{ow.ServerUrl}/streams/{ow.StreamId}/commits/{ow.CommitId}") ); } } diff --git a/ConnectorGrasshopper/ConnectorGrasshopperShared/Ops/Deprecated/Operations.VariableInputSendComponent.cs b/ConnectorGrasshopper/ConnectorGrasshopperShared/Ops/Deprecated/Operations.VariableInputSendComponent.cs index 7dfba45788..9389c4d969 100644 --- a/ConnectorGrasshopper/ConnectorGrasshopperShared/Ops/Deprecated/Operations.VariableInputSendComponent.cs +++ b/ConnectorGrasshopper/ConnectorGrasshopperShared/Ops/Deprecated/Operations.VariableInputSendComponent.cs @@ -22,6 +22,7 @@ using Rhino; using Speckle.Core.Api; using Speckle.Core.Credentials; +using Speckle.Core.Helpers; using Speckle.Core.Logging; using Speckle.Core.Models; using Speckle.Core.Models.Extensions; @@ -217,7 +218,7 @@ public override void AppendAdditionalMenuItems(ToolStripDropDown menu) Menu_AppendItem( menu, $"View commit {ow.CommitId} @ {ow.ServerUrl} online ↗", - (s, e) => Process.Start($"{ow.ServerUrl}/streams/{ow.StreamId}/commits/{ow.CommitId}") + (s, e) => Open.Url($"{ow.ServerUrl}/streams/{ow.StreamId}/commits/{ow.CommitId}") ); } } diff --git a/ConnectorGrasshopper/ConnectorGrasshopperShared/Ops/Operations.SyncSendComponent.cs b/ConnectorGrasshopper/ConnectorGrasshopperShared/Ops/Operations.SyncSendComponent.cs index 7ccbcb324d..1e7031a33c 100644 --- a/ConnectorGrasshopper/ConnectorGrasshopperShared/Ops/Operations.SyncSendComponent.cs +++ b/ConnectorGrasshopper/ConnectorGrasshopperShared/Ops/Operations.SyncSendComponent.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Drawing; using System.Linq; using System.Threading.Tasks; @@ -13,6 +12,7 @@ using Grasshopper.Kernel.Types; using Speckle.Core.Api; using Speckle.Core.Credentials; +using Speckle.Core.Helpers; using Speckle.Core.Logging; using Speckle.Core.Models; using Speckle.Core.Models.Extensions; @@ -86,7 +86,7 @@ public override void AppendAdditionalMenuItems(ToolStripDropDown menu) Menu_AppendItem( menu, $"View commit {ow.CommitId} @ {ow.ServerUrl} online ↗", - (s, e) => Process.Start($"{ow.ServerUrl}/streams/{ow.StreamId}/commits/{ow.CommitId}") + (s, e) => Open.Url($"{ow.ServerUrl}/streams/{ow.StreamId}/commits/{ow.CommitId}") ); } } diff --git a/ConnectorGrasshopper/ConnectorGrasshopperShared/Ops/Operations.VariableInputReceiveComponent.cs b/ConnectorGrasshopper/ConnectorGrasshopperShared/Ops/Operations.VariableInputReceiveComponent.cs index ce1a78b883..608e7f4616 100644 --- a/ConnectorGrasshopper/ConnectorGrasshopperShared/Ops/Operations.VariableInputReceiveComponent.cs +++ b/ConnectorGrasshopper/ConnectorGrasshopperShared/Ops/Operations.VariableInputReceiveComponent.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; -using System.Diagnostics; using System.Drawing; using System.Linq; using System.Net.Http; @@ -342,8 +341,7 @@ public override void AppendAdditionalMenuItems(ToolStripDropDown menu) Menu_AppendItem( menu, $"View commit {ReceivedCommitId} @ {StreamWrapper.ServerUrl} online ↗", - (s, e) => - Process.Start($"{StreamWrapper.ServerUrl}/streams/{StreamWrapper.StreamId}/commits/{ReceivedCommitId}") + (s, e) => Open.Url($"{StreamWrapper.ServerUrl}/streams/{StreamWrapper.StreamId}/commits/{ReceivedCommitId}") ); } } @@ -547,6 +545,23 @@ public async Task ResetApiClient(StreamWrapper wrapper) try { account = wrapper?.GetAccount().Result; + + // hack for FE2 + // should we also fetch this for FE1-accounts that are trying to access FE2 stream? Probably not + if (account.serverInfo.frontend2 is true) + { + Client client = new(account); + var streamObj = client.StreamGet(StreamWrapper.StreamId, 100).Result; + foreach (Branch branch in streamObj.branches.items) + { + if (branch.id == StreamWrapper.BranchName) + { + StreamWrapper.BranchName = branch.name; + break; + } + } + client.Dispose(); + } } catch (SpeckleException e) { diff --git a/ConnectorGrasshopper/ConnectorGrasshopperShared/Ops/Operations.VariableInputSendComponent.cs b/ConnectorGrasshopper/ConnectorGrasshopperShared/Ops/Operations.VariableInputSendComponent.cs index cc4d4339e6..b80b5e5980 100644 --- a/ConnectorGrasshopper/ConnectorGrasshopperShared/Ops/Operations.VariableInputSendComponent.cs +++ b/ConnectorGrasshopper/ConnectorGrasshopperShared/Ops/Operations.VariableInputSendComponent.cs @@ -228,7 +228,7 @@ public override void AppendAdditionalMenuItems(ToolStripDropDown menu) Menu_AppendItem( menu, $"View commit {ow.CommitId} @ {ow.ServerUrl} online ↗", - (s, e) => Process.Start($"{ow.ServerUrl}/streams/{ow.StreamId}/commits/{ow.CommitId}") + (s, e) => Open.Url($"{ow.ServerUrl}/streams/{ow.StreamId}/commits/{ow.CommitId}") ); } } diff --git a/ConnectorGrasshopper/ConnectorGrasshopperShared/SchemaBuilder/CreateSchemaObject.cs b/ConnectorGrasshopper/ConnectorGrasshopperShared/SchemaBuilder/CreateSchemaObject.cs index 3749d94648..67f00ce746 100644 --- a/ConnectorGrasshopper/ConnectorGrasshopperShared/SchemaBuilder/CreateSchemaObject.cs +++ b/ConnectorGrasshopper/ConnectorGrasshopperShared/SchemaBuilder/CreateSchemaObject.cs @@ -252,13 +252,14 @@ public void SwitchConstructor(ConstructorInfo constructor) RegisterPropertyAsInputParameter(p, k++); } + SelectedConstructor = constructor; + UserInterfaceUtils.CreateCanvasDropdownForAllEnumInputs(this, props); Name = constructor.GetCustomAttribute().Name; Description = constructor.GetCustomAttribute().Description; Message = constructor.DeclaringType.FullName.Split('.')[0]; - SelectedConstructor = constructor; Params.Output[0].NickName = constructor.DeclaringType.Name; Params.OnParametersChanged(); diff --git a/ConnectorNavisworks/ConnectorNavisworks/Bindings/ConnectorNavisworksBindings.Conversion.cs b/ConnectorNavisworks/ConnectorNavisworks/Bindings/ConnectorNavisworksBindings.Conversion.cs new file mode 100644 index 0000000000..2823be9e47 --- /dev/null +++ b/ConnectorNavisworks/ConnectorNavisworks/Bindings/ConnectorNavisworksBindings.Conversion.cs @@ -0,0 +1,390 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Autodesk.Navisworks.Api; +using DesktopUI2.Models; +using DesktopUI2.Models.Settings; +using Speckle.ConnectorNavisworks.Other; +using Speckle.Core.Kits; +using Speckle.Core.Logging; +using Speckle.Core.Models; + +namespace Speckle.ConnectorNavisworks.Bindings; + +public partial class ConnectorBindingsNavisworks +{ + private ConversionInvoker _conversionInvoker; + + /// + /// Configures the converter settings. + /// + private void ConfigureConverter() + { + _navisworksConverter.SetConverterSettings(new Dictionary { { "_Mode", "objects" } }); + _conversionInvoker = new ConversionInvoker(_navisworksConverter); + } + + /// + /// Initializes the conversion of model items to elements. + /// + /// The list of model items to convert. + /// The dictionary to store the converted elements. + private void InitializeConversion( + IReadOnlyList modelItemsToConvert, + Dictionary> conversions + ) + { + var totalObjects = modelItemsToConvert.Count; + var objectIncrement = 1 / Math.Max((double)totalObjects, 1); + var objectInterval = Math.Max(totalObjects / 100, 1); + + for (int index = 0; index < modelItemsToConvert.Count; index++) + { + HandleConversionCancellation(); + + ModelItem modelItem = modelItemsToConvert[index]; + var element = new Element(modelItem); + + conversions.Add(element, new Tuple(Constants.ConversionState.ToConvert, null)); + + if (index % objectInterval == 0 || index == modelItemsToConvert.Count - 1) + { + _progressBar.Update((index + 1) * objectIncrement); + } + } + } + + /// + /// Sets up the converter with the appropriate settings and context document. + /// + /// The stream state containing the settings. + private void SetupConverter(StreamState state) + { + _defaultKit = KitManager.GetDefaultKit(); + _navisworksConverter = _defaultKit.LoadConverter(SpeckleNavisworksUtilities.VersionedAppName); + + CurrentSettings = state.Settings; + var settings = state.Settings.ToDictionary(setting => setting.Slug, setting => setting.Selection); + + _navisworksConverter.SetContextDocument(s_activeDoc); + _navisworksConverter.SetConverterSettings(settings); + _navisworksConverter.Report.ReportObjects.Clear(); + } + + /// + /// Prepares the elements from the provided model items for conversion. + /// + /// The list of model items to convert. + /// A dictionary of elements that are ready for conversion. + private Dictionary> PrepareElementsForConversion( + IReadOnlyList modelItemsToConvert + ) + { + _progressBar.BeginSubOperation(0.1, "Who's who? Let's check the ID cards..."); + var conversions = new Dictionary>(); + + InitializeConversion(modelItemsToConvert, conversions); + _progressBar.EndSubOperation(); + + return conversions; + } + + /// + /// Handles the cancellation of the conversion process. + /// + private void HandleConversionCancellation() + { + if (_progressBar.IsCanceled) + { + _progressViewModel.CancellationTokenSource.Cancel(); + } + _progressViewModel.CancellationToken.ThrowIfCancellationRequested(); + } + + /// + /// Handles conversion of elements and views. + /// + /// The current stream state. + /// Dictionary of elements to be converted. + /// The objects to commit. + /// Count of successfully converted objects. + private int ElementAndViewsConversion( + StreamState state, + IDictionary> conversions, + Collection commitObject + ) + { + _progressBar.BeginSubOperation(0.3, $"Spinning the alchemy wheel, transmuting {conversions.Count} objects..."); + ConfigureConverter(); + + var converted = ConvertObjects(conversions); + var convertedCount = ValidateConversion(converted); + + _progressBar.EndSubOperation(); + + BuildHierarchy(converted, state, commitObject); + ValidateHierarchy(commitObject); + + // Update report with conversion results + _progressViewModel.Report.Merge(_navisworksConverter.Report); + + ConvertViewsProcess(state, commitObject); + + // _progressBar.EndSubOperation(); + + return convertedCount; + } + + /// + /// Validates the converted objects and handles any conversion errors. + /// + /// The dictionary of converted objects. + /// The count of successfully converted objects. + /// Thrown when zero objects are converted successfully. + private int ValidateConversion(IDictionary> converted) + { + var convertedCount = converted.Count(x => x.Value.Item1 == Constants.ConversionState.Converted); + + if (convertedCount != 0) + { + return convertedCount; + } + + _settingsHandler.RestoreAutoSave(); + throw new SpeckleException("Zero objects converted successfully. Send stopped."); + } + + /// + /// Builds the nested object hierarchy for the converted elements. + /// + /// The dictionary of converted objects. + /// The current stream state. + /// The objects to commit. + private void BuildHierarchy( + Dictionary> converted, + StreamState state, + Collection commitObject + ) => + // _progressBar.StartNewSubOperation(0.2, "Building a family tree, data-style..."); + // commitObject.elements = Element.BuildNestedObjectHierarchy(converted, state).ToList(); + commitObject.elements = Element.BuildNestedObjectHierarchyInParallel(converted, state, _progressBar).ToList(); + + /// + /// Validates the hierarchy and ensures it contains elements. + /// + /// The objects to commit. + /// Thrown when the hierarchy contains no elements. + private void ValidateHierarchy(Collection commitObject) + { + if (commitObject.elements.Count != 0) + { + return; + } + + _settingsHandler.RestoreAutoSave(); + throw new SpeckleException( + "All Geometry objects in the selection are hidden or cannot be converted. Send stopped." + ); + } + + /// + /// Converts the views and updates the progress bar. + /// + /// The current stream state. + /// The objects to commit. + private void ConvertViewsProcess(StreamState state, Collection commitObject) + { + _progressBar.StartNewSubOperation(0.1, "Sending Views."); + ConvertViews(state, commitObject); + } + + /// + /// Converts a set of Navisworks elements into Speckle objects and logs their conversion status. + /// + /// A dictionary mapping elements to their conversion states and corresponding Base objects. + /// The number of successfully converted elements. + private Dictionary> ConvertObjects( + IDictionary> allConversions + ) + { + Dictionary> conversions = allConversions + .Where(c => c.Key != null && c.Value.Item1 == Constants.ConversionState.ToConvert) + .ToDictionary(c => c.Key, c => c.Value); + + int convertedCount = 0; + var conversionIncrement = conversions.Count != 0 ? 1.0 / conversions.Count : 0.0; + var conversionInterval = Math.Max(conversions.Count / 100, 1); + + for (var i = 0; i < conversions.Count; i++) + { + if (_progressBar.IsCanceled) + { + _progressViewModel.CancellationTokenSource.Cancel(); + } + + _progressViewModel.CancellationToken.ThrowIfCancellationRequested(); + + var nextToConvert = conversions.ElementAt(i); + var element = nextToConvert.Key; + + // Get the descriptor of the element + var descriptor = element.Descriptor(); + + if (_navisworksConverter.Report.ReportObjects.TryGetValue(element.IndexPath, out var applicationObject)) + { + _progressViewModel.Report.Log(applicationObject); + conversions[element] = new Tuple( + Constants.ConversionState.Converted, + nextToConvert.Value.Item2 + ); + continue; + } + + var reportObject = new ApplicationObject(element.IndexPath, descriptor) { applicationId = element.IndexPath }; + + if (!_navisworksConverter.CanConvertToSpeckle(element.ModelItem)) + { + reportObject.Update( + status: ApplicationObject.State.Skipped, + logItem: "Sending this object type is not supported in Navisworks" + ); + _progressViewModel.Report.Log(reportObject); + conversions[element] = new Tuple( + Constants.ConversionState.Skipped, + nextToConvert.Value.Item2 + ); + continue; + } + + _navisworksConverter.Report.Log(reportObject); + + // Convert the model item to Speckle + var converted = _conversionInvoker.Convert(element.ModelItem); + + if (converted == null) + { + reportObject.Update(status: ApplicationObject.State.Failed, logItem: "Conversion returned null"); + _progressViewModel.Report.Log(reportObject); + conversions[element] = new Tuple( + Constants.ConversionState.Failed, + nextToConvert.Value.Item2 + ); + continue; + } + + converted.applicationId = element.IndexPath; + conversions[element] = new Tuple(Constants.ConversionState.Converted, converted); + convertedCount++; + _conversionProgressDict["Conversion"] = convertedCount; + _progressViewModel.Update(_conversionProgressDict); + reportObject.Update(status: ApplicationObject.State.Created, logItem: $"Sent as {converted.speckle_type}"); + _progressViewModel.Report.Log(reportObject); + + if (i % conversionInterval != 0 && i != conversions.Count) + { + continue; + } + + double progress = (i + 1) * conversionIncrement; + _progressBar.Update(progress); + } + + return conversions; + } + + /// + /// Retrieves a list of ModelItems from the provided stream state. This method checks the selection filter, + /// populates hierarchy, omits hidden items, and verifies the visibility of the objects for conversion. + /// + /// The current stream state. + /// A list of model items to convert. + /// Thrown when no objects are selected or visible for conversion. + private List GetModelItemsForConversion(StreamState state) + { + var modelItemsToConvert = new List(); + + var selectionBuilder = new SelectionHandler(state, _progressViewModel) { ProgressBar = _progressBar }; + + selectionBuilder.GetFromFilter(); + + selectionBuilder.ValidateStartNodes(); + + // Check if any items have been selected + if (selectionBuilder.Count == 0) + { + throw new InvalidOperationException( + "Zero objects selected; send stopped. Please select some objects, or check that your filter can actually select something." + ); + } + + try + { + selectionBuilder.PopulateHierarchyAndOmitHidden(); + } + catch + { + throw new InvalidOperationException("An error occurred retrieving objects from your saved selection source."); + } + + modelItemsToConvert.AddRange(selectionBuilder.ModelItems); + + if (modelItemsToConvert.Count == 0) + { + throw new InvalidOperationException( + "Zero objects visible for conversion; send stopped. Please select some objects, or check that your filter can actually select something." + ); + } + + return modelItemsToConvert; + } + + /// + /// Converts Navisworks views into Base objects and adds them to a given commit object. + /// This includes selected views from a provided filter and, optionally, the active and home views. + /// + /// The current stream state, containing filter data. + /// The object to which converted views should be added. + private void ConvertViews(StreamState state, DynamicBase commitObject) + { + var views = new List(); + + _navisworksConverter.SetConverterSettings(new Dictionary { { "_Mode", "views" } }); + + if (state.Filter?.Slug == "views") + { + var selectionBuilder = new SelectionHandler(state, _progressViewModel) { ProgressBar = _progressBar }; + + var selectedViews = state.Filter.Selection + .Distinct() + .Select(selectionBuilder.ResolveSavedViewpoint) + .Select(_conversionInvoker.Convert) + .Where(c => c != null) + .ToList(); + + views.AddRange(selectedViews); + } + // Only send current view if we aren't sending other views. + else if (CurrentSettings.Find(x => x.Slug == "current-view") is CheckBoxSetting { IsChecked: true }) + { + var currentView = _conversionInvoker.Convert(s_activeDoc.CurrentViewpoint.ToViewpoint()); + var homeView = _conversionInvoker.Convert(s_activeDoc.HomeView); + + if (currentView != null) + { + currentView["name"] = "Active View"; + views.Add(currentView); + } + + if (homeView != null) + { + homeView["name"] = "Home View"; + views.Add(homeView); + } + } + + if (views.Count != 0) + { + commitObject["views"] = views; + } + } +} diff --git a/ConnectorNavisworks/ConnectorNavisworks/Bindings/ConnectorNavisworksBindings.Events.cs b/ConnectorNavisworks/ConnectorNavisworks/Bindings/ConnectorNavisworksBindings.Events.cs index 5e08d4d426..95fcdea20a 100644 --- a/ConnectorNavisworks/ConnectorNavisworks/Bindings/ConnectorNavisworksBindings.Events.cs +++ b/ConnectorNavisworks/ConnectorNavisworks/Bindings/ConnectorNavisworksBindings.Events.cs @@ -22,9 +22,9 @@ public partial class ConnectorBindingsNavisworks public void RegisterAppEvents() { // Register event handlers for document, selection sets, and model collection changes - s_doc.FileNameChanged += DocumentChangedEvent; - s_doc.SelectionSets.Changed += SetsChangedEvent; - s_doc.Models.CollectionChanged += ModelsChangedEvent; + s_activeDoc.FileNameChanged += DocumentChangedEvent; + s_activeDoc.SelectionSets.Changed += SetsChangedEvent; + s_activeDoc.Models.CollectionChanged += ModelsChangedEvent; } /// diff --git a/ConnectorNavisworks/ConnectorNavisworks/Bindings/ConnectorNavisworksBindings.File.cs b/ConnectorNavisworks/ConnectorNavisworks/Bindings/ConnectorNavisworksBindings.File.cs index 53edb2ffad..4393c0d21c 100644 --- a/ConnectorNavisworks/ConnectorNavisworks/Bindings/ConnectorNavisworksBindings.File.cs +++ b/ConnectorNavisworks/ConnectorNavisworks/Bindings/ConnectorNavisworksBindings.File.cs @@ -12,7 +12,7 @@ public partial class ConnectorBindingsNavisworks /// /// The list of stream states to write. public override void WriteStreamsToFile(List streams) => - SpeckleStreamManager.WriteStreamStateList(s_doc, streams); + SpeckleStreamManager.WriteStreamStateList(s_activeDoc, streams); /// /// Retrieves the list of stream states from the file. @@ -21,9 +21,9 @@ public override void WriteStreamsToFile(List streams) => public override List GetStreamsInFile() { var streams = new List(); - if (s_doc != null) + if (s_activeDoc != null) { - streams = SpeckleStreamManager.ReadState(s_doc); + streams = SpeckleStreamManager.ReadState(s_activeDoc); } return streams; @@ -35,19 +35,19 @@ public override List GetStreamsInFile() /// The name of the current file. public override string GetFileName() { - IsFileAndModelsPresent(); + IsFileAndAreModelsPresent(); - return s_doc?.CurrentFileName ?? string.Empty; + return s_activeDoc?.CurrentFileName ?? string.Empty; } - private static void IsFileAndModelsPresent() + private static void IsFileAndAreModelsPresent() { - if (s_doc == null) + if (s_activeDoc == null) { throw new FileNotFoundException("No active document found. Cannot Send."); } - if (s_doc.Models.Count == 0) + if (s_activeDoc.Models.Count == 0) { throw new FileNotFoundException("No models are appended. Nothing to Send."); } diff --git a/ConnectorNavisworks/ConnectorNavisworks/Bindings/ConnectorNavisworksBindings.Filters.cs b/ConnectorNavisworks/ConnectorNavisworks/Bindings/ConnectorNavisworksBindings.Filters.cs index 24ad7f49c3..a11f862a72 100644 --- a/ConnectorNavisworks/ConnectorNavisworks/Bindings/ConnectorNavisworksBindings.Filters.cs +++ b/ConnectorNavisworks/ConnectorNavisworks/Bindings/ConnectorNavisworksBindings.Filters.cs @@ -23,14 +23,14 @@ public override List GetSelectionFilters() var manualFilter = new ManualSelectionFilter(); - if (s_doc == null) + if (s_activeDoc == null) { return filters; } filters.Add(manualFilter); - var selectSetsRootItem = s_doc.SelectionSets.RootItem; + var selectSetsRootItem = s_activeDoc.SelectionSets.RootItem; var savedSelectionSets = selectSetsRootItem?.Children.Select(GetSets).ToList() ?? new List(); @@ -47,7 +47,7 @@ public override List GetSelectionFilters() filters.Add(selectionSetsFilter); } - var savedViewsRootItem = s_doc.SavedViewpoints.RootItem; + var savedViewsRootItem = s_activeDoc.SavedViewpoints.RootItem; var savedViews = savedViewsRootItem?.Children.Select(GetViews).Select(RemoveNullNodes).Where(x => x != null).ToList() @@ -61,14 +61,14 @@ public override List GetSelectionFilters() Name = "Saved Viewpoints", Icon = "FileTree", Description = - "Select a saved viewpoint and send its visible items in the commit. Only views with Hide/Require attribute checked are listed.", + "Select saved viewpoints and send their visible items in the commit. Only views with Hide/Require attribute checked are listed.", Values = savedViews, - SelectionMode = "Toggle" + SelectionMode = "Multiple" }; filters.Add(savedViewsFilter); } - DocumentClash clashPlugin = s_doc.GetClash(); + DocumentClash clashPlugin = s_activeDoc.GetClash(); var clashTests = clashPlugin?.TestsData; @@ -102,7 +102,7 @@ private static TreeNode GetSets(SavedItem savedItem) DisplayName = savedItem.DisplayName, Guid = savedItem.Guid, IndexWith = nameof(TreeNode.Guid), - Indices = s_doc.SelectionSets.CreateIndexPath(savedItem).ToArray() + Indices = s_activeDoc.SelectionSets.CreateIndexPath(savedItem).ToArray() }; if (!savedItem.IsGroup) @@ -121,7 +121,7 @@ private static TreeNode GetSets(SavedItem savedItem) private static TreeNode GetViews(SavedItem savedItem) { - var reference = s_doc.SavedViewpoints.CreateReference(savedItem); + var reference = s_activeDoc.SavedViewpoints.CreateReference(savedItem); var treeNode = new TreeNode { diff --git a/ConnectorNavisworks/ConnectorNavisworks/Bindings/ConnectorNavisworksBindings.Selections.cs b/ConnectorNavisworks/ConnectorNavisworks/Bindings/ConnectorNavisworksBindings.Selections.cs index 0a4b5b58b0..67a4c4de62 100644 --- a/ConnectorNavisworks/ConnectorNavisworks/Bindings/ConnectorNavisworksBindings.Selections.cs +++ b/ConnectorNavisworks/ConnectorNavisworks/Bindings/ConnectorNavisworksBindings.Selections.cs @@ -1,53 +1,35 @@ using System.Collections.Generic; using System.Linq; using System.Windows.Forms; -using Autodesk.Navisworks.Api; using Speckle.ConnectorNavisworks.Other; -using Application = Autodesk.Navisworks.Api.Application; using Cursor = System.Windows.Forms.Cursor; namespace Speckle.ConnectorNavisworks.Bindings; public partial class ConnectorBindingsNavisworks { + /// /// /// Parses list all selected Elements and their descendants that match criteria: /// 1. Is Selected /// 2. Is Visible in the current view /// - /// List of unique pseudoIds + /// List of unique IndexPaths as strings public override List GetSelectedObjects() { Cursor.Current = Cursors.WaitCursor; - // Current document, models and selected elements. - s_doc = Application.ActiveDocument; - - IsFileAndModelsPresent(); - - var appSelectedItems = s_doc.CurrentSelection.SelectedItems; + IsFileAndAreModelsPresent(); // Storing as a Set for consistency with the converter's handling of fragments and paths. - var selectedObjects = new HashSet(); - - var visible = appSelectedItems.Where(IsElementVisible); - var visibleItems = visible.ToList(); - var ids = visibleItems.Select(i => new Element().GetElement(i).PseudoId).Where(x => x != null); - - selectedObjects.UnionWith(ids); + var selectedObjects = new HashSet( + s_activeDoc.CurrentSelection.SelectedItems + .Where(Element.IsElementVisible) + .Select(Element.ResolveModelItemToIndexPath) + ); Cursor.Current = Cursors.Default; return selectedObjects.ToList(); } - - /// - /// Checks is the Element is hidden or if any of its ancestors is hidden - /// - /// - /// - private static bool IsElementVisible(ModelItem element) => - // Hidden status is stored at the earliest node in the hierarchy - // All of the the tree path nodes need to not be Hidden - element.AncestorsAndSelf.All(x => x.IsHidden != true); } diff --git a/ConnectorNavisworks/ConnectorNavisworks/Bindings/ConnectorNavisworksBindings.Send.cs b/ConnectorNavisworks/ConnectorNavisworks/Bindings/ConnectorNavisworksBindings.Send.cs index 11f23fd75d..4ca3c86a6e 100644 --- a/ConnectorNavisworks/ConnectorNavisworks/Bindings/ConnectorNavisworksBindings.Send.cs +++ b/ConnectorNavisworks/ConnectorNavisworks/Bindings/ConnectorNavisworksBindings.Send.cs @@ -15,7 +15,6 @@ using Speckle.Core.Logging; using Speckle.Core.Models; using Speckle.Core.Transports; -using static Speckle.ConnectorNavisworks.Other.SpeckleNavisworksUtilities; using Application = Autodesk.Navisworks.Api.Application; using Cursor = System.Windows.Forms.Cursor; @@ -23,7 +22,6 @@ namespace Speckle.ConnectorNavisworks.Bindings; public partial class ConnectorBindingsNavisworks { - private ConversionInvoker _conversionInvoker; private ConcurrentDictionary _conversionProgressDict; private int _convertedCount; private ProgressInvoker _progressBar; @@ -31,23 +29,11 @@ public partial class ConnectorBindingsNavisworks public override bool CanPreviewSend => false; - /// - /// Gets a new instance of a commit object with initial properties. - /// - private static Collection CommitObject => - new() - { - ["units"] = GetUnits(s_doc), - collectionType = "Navisworks Model", - name = s_doc.Title, - applicationId = "Root" - }; - // Stub - Preview send is not supported public override async void PreviewSend(StreamState state, ProgressViewModel progress) => await Task.Delay(TimeSpan.FromMilliseconds(500)).ConfigureAwait(false); - // TODO! + /// /// /// Sends the stream to Speckle. /// @@ -68,7 +54,7 @@ public override async Task SendStream(StreamState state, ProgressViewMod SetupProgressViewModel(); SetupConverter(state); - InitializeManagerOptionsForSend(state); + _settingsHandler.InitializeManagerOptionsForSend(state); try { @@ -115,11 +101,6 @@ public override async Task SendStream(StreamState state, ProgressViewMod var objectId = await SendConvertedObjectsToSpeckle(state, commitObject).ConfigureAwait(false); - if (_progressViewModel.Report.OperationErrors.Count != 0) - { - ConnectorHelpers.DefaultSendErrorHandler("", _progressViewModel.Report.OperationErrors.Last()); - } - _progressViewModel.CancellationToken.ThrowIfCancellationRequested(); commitId = await CreateCommit(state, objectId).ConfigureAwait(false); @@ -150,141 +131,10 @@ public override async Task SendStream(StreamState state, ProgressViewMod return commitId; } - /// - /// The SettingsManager can be seeded with the options for the conversion. - /// - /// - /// - private void InitializeManagerOptionsForSend(StreamState state) - { - var internalPropertySettings = state.Settings.Find(x => x.Slug == "internal-properties"); - var internalPropertyNames = state.Settings.Find(x => x.Slug == "internal-property-names"); - - if (internalPropertySettings != null && ((CheckBoxSetting)internalPropertySettings).IsChecked) - { - _settingsHandler.ShowInternalProperties(); - } - - if (internalPropertyNames != null && ((CheckBoxSetting)internalPropertyNames).IsChecked) - { - _settingsHandler.UseInternalPropertyNames(); - } - } - - /// - /// Validates the required properties before sending a stream. - /// - /// The stream state. - private void ValidateBeforeSending(StreamState state) - { - if (_progressViewModel == null) - { - throw new ArgumentException("No ProgressViewModel provided."); - } - - if (s_doc.ActiveSheet == null) - { - throw new InvalidOperationException("Your Document is empty. Nothing to Send."); - } - - if (state.Filter == null) - { - throw new InvalidOperationException("No filter provided. Nothing to Send."); - } - - if (state.Filter.Slug == "all" || state.CommitMessage == "Sent everything") - { - throw new InvalidOperationException("Everything Mode is not yet implemented. Send stopped."); - } - } - - /// - /// Configures the progress view model and sets up cancellation behavior. - /// - private void SetupProgressViewModel() - { - _progressViewModel.Report = new ProgressReport(); - - _progressViewModel.CancellationToken.Register(() => - { - try - { - _progressBar.Cancel(); - Application.EndProgress(); - } - catch (InvalidOperationException) - { - // ignored - } - - _settingsHandler.RestoreAutoSave(); - Cursor.Current = Cursors.Default; - }); - } - - /// - /// Prepares the elements from the provided model items for conversion. - /// - /// The list of model items to convert. - /// A dictionary of elements ready for conversion. - private Dictionary> PrepareElementsForConversion( - IReadOnlyList modelItemsToConvert - ) - { - _progressBar.BeginSubOperation(0.1, "Who's who? Let's check the ID cards..."); - var conversions = new Dictionary>(); - - var totalObjects = modelItemsToConvert.Count; - var objectIncrement = 1 / Math.Max((double)totalObjects, 1); - var objectInterval = Math.Max(totalObjects / 100, 1); - - for (int index = 0; index < modelItemsToConvert.Count; index++) - { - if (_progressBar.IsCanceled) - { - _progressViewModel.CancellationTokenSource.Cancel(); - } - - _progressViewModel.CancellationToken.ThrowIfCancellationRequested(); - - ModelItem modelItem = modelItemsToConvert[index]; - var element = new Element(); - element.GetElement(modelItem); - conversions.Add(element, new Tuple(Constants.ConversionState.ToConvert, null)); - - if (index % objectInterval == 0 || index == modelItemsToConvert.Count - 1) - { - _progressBar.Update((index + 1) * objectIncrement); - } - } - - _progressBar.EndSubOperation(); - - return conversions; - } - - /// - /// Sets up the converter with the appropriate settings and context document. - /// - /// The stream state containing the settings. - private void SetupConverter(StreamState state) - { - _defaultKit = KitManager.GetDefaultKit(); - _navisworksConverter = _defaultKit.LoadConverter(VersionedAppName); - - CurrentSettings = state.Settings; - var settings = state.Settings.ToDictionary(setting => setting.Slug, setting => setting.Selection); - - _navisworksConverter.SetContextDocument(s_doc); - _navisworksConverter.SetConverterSettings(settings); - _navisworksConverter.Report.ReportObjects.Clear(); - } - /// /// Prepares model items to be converted for a given stream state. /// /// The stream state. - /// Out parameter to return the total number of objects to convert. /// A list of ModelItem objects that are ready to be converted. private List PrepareModelItemsToConvert(StreamState state) { @@ -297,57 +147,10 @@ private List PrepareModelItemsToConvert(StreamState state) return modelItemsToConvert.Where(e => e != null).ToList(); } - /// - /// Handles conversion of elements and views. - /// - /// The current stream state. - /// Dictionary of elements to be converted. - /// The objects to commit. - /// Count of successfully converted objects. - private int ElementAndViewsConversion( - StreamState state, - IDictionary> conversions, - Collection commitObject - ) - { - _progressBar.BeginSubOperation(0.35, "Spinning the alchemy wheel, transmuting data..."); - _navisworksConverter.SetConverterSettings(new Dictionary { { "_Mode", "objects" } }); - _conversionInvoker = new ConversionInvoker(_navisworksConverter); - var converted = ConvertObjects(conversions); - var convertedCount = converted.Count(x => x.Value.Item1 == Constants.ConversionState.Converted); - - if (convertedCount == 0) - { - _settingsHandler.RestoreAutoSave(); - throw new SpeckleException("Zero objects converted successfully. Send stopped."); - } - - _progressBar.StartNewSubOperation(0.2, "Building a family tree, data-style..."); - - commitObject.elements = Element.BuildNestedObjectHierarchy(converted, state).ToList(); - - if (commitObject.elements.Count == 0) - { - _settingsHandler.RestoreAutoSave(); - throw new SpeckleException( - "All Geometry objects in the selection are hidden or cannot be converted. Send stopped." - ); - } - - _progressViewModel.Report.Merge(_navisworksConverter.Report); - - _progressBar.StartNewSubOperation(0.1, "Sending Views."); - ConvertViews(state, commitObject); - _progressBar.EndSubOperation(); - - return convertedCount; - } - /// /// Handles updates to the progress of the operation. /// /// A dictionary containing progress details. - /// The total number of converted items. private void HandleProgress(ConcurrentDictionary progressDict) { // If the "RemoteTransport" key exists in the dictionary and has a positive value @@ -375,12 +178,12 @@ private void HandleError(string _, Exception ex) => /// /// The current state of the stream. /// The collection of objects to send. - /// The total number of converted items. - /// The ID of the sent object. + /// The ID of the object being sent. private async Task SendConvertedObjectsToSpeckle(StreamState state, Base commitObject) { + _progressBar.EndSubOperation(); _progressBar.BeginSubOperation( - 0.3, + 0.95, $"Pack your bags, data! That's {_convertedCount} objects going on a trip to the Speckle universe..." ); @@ -413,11 +216,10 @@ private async Task SendConvertedObjectsToSpeckle(StreamState state, Base /// /// The StreamState object, contains stream details and client. /// The id of the object to commit. - /// The count of converted elements. /// The id of the created commit. private async Task CreateCommit(StreamState state, string objectId) { - _progressBar.BeginSubOperation(0.1, "Sealing the deal... Your data's new life begins in Speckle!"); + _progressBar.BeginSubOperation(1, "Sealing the deal... Your data's new life begins in Speckle!"); // Define a new commit input with stream details, object ID, and commit message var commit = new CommitCreateInput @@ -429,207 +231,126 @@ private async Task CreateCommit(StreamState state, string objectId) sourceApplication = HostAppNameVersion }; - string commitId = - // This block enables forcing a failed send to test the caching feature - // #if DEBUG - // if (!isRetrying) - // throw new SpeckleException("Debug mode: commit not created."); - // #endif - // Use the helper function to create the commit and retrieve the commit ID - await ConnectorHelpers + string commitId = await ConnectorHelpers .CreateCommit(state.Client, commit, _progressViewModel.CancellationToken) .ConfigureAwait(false); return commitId; } - /// - /// Retrieves a list of ModelItems from the provided stream state. This method checks the selection filter, - /// populates hierarchy, omits hidden items, and verifies the visibility of the objects for conversion. - /// - /// The current stream state. - /// A list of model items to convert. - /// Thrown when no objects are selected or visible for conversion. - private List GetModelItemsForConversion(StreamState state) + public async Task RetryLastConversionSend() { - var modelItemsToConvert = new List(); - - var selectionBuilder = new SelectionHandler(state, _progressViewModel) { ProgressBar = _progressBar }; - - selectionBuilder.GetFromFilter(); - - selectionBuilder.ValidateStartNodes(); - - // Check if any items have been selected - if (selectionBuilder.Count == 0) + if (s_activeDoc == null) { - throw new InvalidOperationException( - "Zero objects selected; send stopped. Please select some objects, or check that your filter can actually select something." - ); + return; } - try + if (CachedConvertedElements == null || s_cachedCommit == null) { - selectionBuilder.PopulateHierarchyAndOmitHidden(); + throw new SpeckleException("Cant retry last conversion: no cached conversion or commit found."); } - catch + + if (s_cachedCommit is Collection commitObject) { - throw new InvalidOperationException("An error occurred retrieving objects from your saved selection source."); - } + // _isRetrying = true; - modelItemsToConvert.AddRange(selectionBuilder.ModelItems); + var applicationProgress = Application.BeginProgress("Retrying that send to Speckle."); + _progressBar = new ProgressInvoker(applicationProgress); + _progressViewModel = new ProgressViewModel(); - if (modelItemsToConvert.Count == 0) - { - throw new InvalidOperationException( - "Zero objects visible for conversion; send stopped. Please select some objects, or check that your filter can actually select something." - ); - } + commitObject.elements = CachedConvertedElements; - return modelItemsToConvert; - } + var state = s_cachedState; - /// - /// Converts Navisworks views into Base objects and adds them to a given commit object. - /// This includes selected views from a provided filter and, optionally, the active and home views. - /// - /// The current stream state, containing filter data. - /// The object to which converted views should be added. - private void ConvertViews(StreamState state, DynamicBase commitObject) - { - var views = new List(); + _progressBar.BeginSubOperation(0.7, "Retrying cached conversion."); + _progressBar.EndSubOperation(); - _navisworksConverter.SetConverterSettings(new Dictionary { { "_Mode", "views" } }); + var objectId = await SendConvertedObjectsToSpeckle(state, commitObject).ConfigureAwait(false); - if (state.Filter?.Slug == "views") - { - var selectionBuilder = new SelectionHandler(state, _progressViewModel) { ProgressBar = _progressBar }; + if (_progressViewModel.Report.OperationErrors.Count != 0) + { + ConnectorHelpers.DefaultSendErrorHandler("", _progressViewModel.Report.OperationErrors.Last()); + } - var selectedViews = state.Filter.Selection - .Distinct() - .Select(selectionBuilder.ResolveSavedViewpoint) - .Select(_conversionInvoker.Convert) - .Where(c => c != null) - .ToList(); + _progressViewModel.CancellationToken.ThrowIfCancellationRequested(); - views.AddRange(selectedViews); - } - // Only send current view if we aren't sending other views. - else if (CurrentSettings.Find(x => x.Slug == "current-view") is CheckBoxSetting { IsChecked: true }) - { - var currentView = _conversionInvoker.Convert(s_doc.CurrentViewpoint.ToViewpoint()); - var homeView = _conversionInvoker.Convert(s_doc.HomeView); + state.Settings.Add(new CheckBoxSetting { Slug = "retrying", IsChecked = true }); - if (currentView != null) + string commitId; + try + { + commitId = await CreateCommit(state, objectId).ConfigureAwait(false); + } + finally { - currentView["name"] = "Active View"; - views.Add(currentView); + _progressBar.EndSubOperation(); + Application.EndProgress(); + Cursor.Current = Cursors.Default; } - if (homeView != null) + state.Settings.RemoveAll(x => x.Slug == "retrying"); + + if (string.IsNullOrEmpty(commitId)) { - homeView["name"] = "Home View"; - views.Add(homeView); + return; } } - if (views.Count != 0) - { - commitObject["views"] = views; - } + // nullify the cached conversion and commit on success. + s_cachedCommit = null; + + CachedConvertedElements = null; + // _isRetrying = false; } /// - /// Converts a set of Navisworks elements into Speckle objects and logs their conversion status. + /// Validates the required properties before sending a stream. /// - /// A dictionary mapping elements to their conversion states and corresponding Base objects. - /// The number of successfully converted elements. - private Dictionary> ConvertObjects( - IDictionary> allConversions - ) + /// The stream state. + void ValidateBeforeSending(StreamState state) { - Dictionary> conversions = allConversions - .Where(c => c.Key != null && c.Value.Item1 == Constants.ConversionState.ToConvert) - .ToDictionary(c => c.Key, c => c.Value); - - int convertedCount = 0; - var conversionIncrement = conversions.Count != 0 ? 1.0 / conversions.Count : 0.0; - var conversionInterval = Math.Max(conversions.Count / 100, 1); - - for (var i = 0; i < conversions.Count; i++) + if (_progressViewModel == null) { - if (_progressBar.IsCanceled) - { - _progressViewModel.CancellationTokenSource.Cancel(); - } - - _progressViewModel.CancellationToken.ThrowIfCancellationRequested(); - - var nextToConvert = conversions.ElementAt(i); - var element = nextToConvert.Key; - - // Get the descriptor of the element - var descriptor = element.Descriptor(); - - if (_navisworksConverter.Report.ReportObjects.TryGetValue(element.PseudoId, out var applicationObject)) - { - _progressViewModel.Report.Log(applicationObject); - conversions[element] = new Tuple( - Constants.ConversionState.Converted, - nextToConvert.Value.Item2 - ); - continue; - } + throw new ArgumentException("No ProgressViewModel provided."); + } - var reportObject = new ApplicationObject(element.PseudoId, descriptor) { applicationId = element.PseudoId }; + if (s_activeDoc.ActiveSheet == null) + { + throw new InvalidOperationException("Your Document is empty. Nothing to Send."); + } - if (!_navisworksConverter.CanConvertToSpeckle(element.ModelItem)) - { - reportObject.Update( - status: ApplicationObject.State.Skipped, - logItem: "Sending this object type is not supported in Navisworks" - ); - _progressViewModel.Report.Log(reportObject); - conversions[element] = new Tuple( - Constants.ConversionState.Skipped, - nextToConvert.Value.Item2 - ); - continue; - } + if (state.Filter == null) + { + throw new InvalidOperationException("No filter provided. Nothing to Send."); + } - _navisworksConverter.Report.Log(reportObject); + if (state.Filter.Slug == "all" || state.CommitMessage == "Sent everything") + { + throw new InvalidOperationException("Everything Mode is not yet implemented. Send stopped."); + } + } - // Convert the model item to Speckle - var converted = _conversionInvoker.Convert(element.ModelItem); + /// + /// Configures the progress view model and sets up cancellation behavior. + /// + private void SetupProgressViewModel() + { + _progressViewModel.Report = new ProgressReport(); - if (converted == null) + _progressViewModel.CancellationToken.Register(() => + { + try { - reportObject.Update(status: ApplicationObject.State.Failed, logItem: "Conversion returned null"); - _progressViewModel.Report.Log(reportObject); - conversions[element] = new Tuple( - Constants.ConversionState.Failed, - nextToConvert.Value.Item2 - ); - continue; + _progressBar.Cancel(); + Application.EndProgress(); } - - converted.applicationId = element.PseudoId; - conversions[element] = new Tuple(Constants.ConversionState.Converted, converted); - convertedCount++; - _conversionProgressDict["Conversion"] = convertedCount; - _progressViewModel.Update(_conversionProgressDict); - reportObject.Update(status: ApplicationObject.State.Created, logItem: $"Sent as {converted.speckle_type}"); - _progressViewModel.Report.Log(reportObject); - - if (i % conversionInterval != 0 && i != conversions.Count) + catch (InvalidOperationException) { - continue; + // ignored } - double progress = (i + 1) * conversionIncrement; - _progressBar.Update(progress); - } - - return conversions; + _settingsHandler.RestoreAutoSave(); + Cursor.Current = Cursors.Default; + }); } } diff --git a/ConnectorNavisworks/ConnectorNavisworks/Bindings/ConnectorNavisworksBindings.Settings.cs b/ConnectorNavisworks/ConnectorNavisworks/Bindings/ConnectorNavisworksBindings.Settings.cs index 009066868f..177a17ea8d 100644 --- a/ConnectorNavisworks/ConnectorNavisworks/Bindings/ConnectorNavisworksBindings.Settings.cs +++ b/ConnectorNavisworks/ConnectorNavisworks/Bindings/ConnectorNavisworksBindings.Settings.cs @@ -80,6 +80,13 @@ public override List GetSettings() "Include the internal properties that reflect option types. Can be useful for downstream data analysis." }, new CheckBoxSetting + { + Slug = "exclude-properties", + Name = "Exclude Properties", + IsChecked = false, + Description = "Excludes Properties from the commit. Can reduce payloads for large models." + }, + new CheckBoxSetting { Slug = "internal-property-names", Name = "Internal Property Names", @@ -91,7 +98,7 @@ public override List GetSettings() { Slug = "coalesce-data", Name = "Coalesce Data from First Object to Geometry", - IsChecked = false, + IsChecked = true, Description = "All properties from the Geometry up the tree to the next First Object will be coalesced into the Geometry Node." } diff --git a/ConnectorNavisworks/ConnectorNavisworks/Bindings/ConnectorNavisworksBindings.cs b/ConnectorNavisworks/ConnectorNavisworks/Bindings/ConnectorNavisworksBindings.cs index 93dbe93c60..c73f1219a8 100644 --- a/ConnectorNavisworks/ConnectorNavisworks/Bindings/ConnectorNavisworksBindings.cs +++ b/ConnectorNavisworks/ConnectorNavisworks/Bindings/ConnectorNavisworksBindings.cs @@ -1,21 +1,13 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; using System.Windows.Forms; using Autodesk.Navisworks.Api; using DesktopUI2; using DesktopUI2.Models; -using DesktopUI2.Models.Settings; -using DesktopUI2.ViewModels; using Speckle.ConnectorNavisworks.NavisworksOptions; -using Speckle.ConnectorNavisworks.Other; using Speckle.Core.Kits; -using Speckle.Core.Logging; using Speckle.Core.Models; using static Speckle.ConnectorNavisworks.Other.SpeckleNavisworksUtilities; -using Application = Autodesk.Navisworks.Api.Application; -using Cursor = System.Windows.Forms.Cursor; using MenuItem = DesktopUI2.Models.MenuItem; namespace Speckle.ConnectorNavisworks.Bindings; @@ -23,7 +15,7 @@ namespace Speckle.ConnectorNavisworks.Bindings; public partial class ConnectorBindingsNavisworks : ConnectorBindings { // Much of the interaction in Navisworks is through the ActiveDocument API - private static Document s_doc; + private static Document s_activeDoc; internal static Control Control; private static object s_cachedCommit; @@ -37,10 +29,21 @@ public partial class ConnectorBindingsNavisworks : ConnectorBindings private readonly NavisworksOptionsManager _settingsHandler; + /// + /// Gets a new instance of a commit object with initial properties. + /// + private static Collection CommitObject => + new() + { + ["units"] = GetUnits(s_activeDoc), + collectionType = "Navisworks Model", + name = s_activeDoc.Title + }; + public ConnectorBindingsNavisworks(Document navisworksActiveDocument) { - s_doc = navisworksActiveDocument; - s_doc.SelectionSets.ToSavedItemCollection(); + s_activeDoc = navisworksActiveDocument; + s_activeDoc.SelectionSets.ToSavedItemCollection(); // Sets the Main Thread Control to Invoke commands on. Control = new Control(); @@ -84,7 +87,7 @@ public override string GetDocumentId() { // TODO! // An unsaved document has no path or filename - var fileName = s_doc.CurrentFileName; + var fileName = s_activeDoc.CurrentFileName; var hash = Core.Models.Utilities.HashString(fileName, Core.Models.Utilities.HashingFunctions.MD5); return hash; } @@ -93,69 +96,4 @@ public override List GetObjectsInView() // this returns all visible doc // TODO! => throw new NotImplementedException(); - - public async Task RetryLastConversionSend() - { - if (s_doc == null) - { - return; - } - - if (CachedConvertedElements == null || s_cachedCommit == null) - { - throw new SpeckleException("Cant retry last conversion: no cached conversion or commit found."); - } - - if (s_cachedCommit is Collection commitObject) - { - // _isRetrying = true; - - var applicationProgress = Application.BeginProgress("Retrying that send to Speckle."); - _progressBar = new ProgressInvoker(applicationProgress); - _progressViewModel = new ProgressViewModel(); - - commitObject.elements = CachedConvertedElements; - - var state = s_cachedState; - - _progressBar.BeginSubOperation(0.7, "Retrying cached conversion."); - _progressBar.EndSubOperation(); - - var objectId = await SendConvertedObjectsToSpeckle(state, commitObject).ConfigureAwait(false); - - if (_progressViewModel.Report.OperationErrors.Count != 0) - { - ConnectorHelpers.DefaultSendErrorHandler("", _progressViewModel.Report.OperationErrors.Last()); - } - - _progressViewModel.CancellationToken.ThrowIfCancellationRequested(); - - state.Settings.Add(new CheckBoxSetting { Slug = "retrying", IsChecked = true }); - - string commitId; - try - { - commitId = await CreateCommit(state, objectId).ConfigureAwait(false); - } - finally - { - _progressBar.EndSubOperation(); - Application.EndProgress(); - Cursor.Current = Cursors.Default; - } - - state.Settings.RemoveAll(x => x.Slug == "retrying"); - - if (string.IsNullOrEmpty(commitId)) - { - return; - } - } - - // nullify the cached conversion and commit on success. - s_cachedCommit = null; - - CachedConvertedElements = null; - // _isRetrying = false; - } } diff --git a/ConnectorNavisworks/ConnectorNavisworks/ConnectorNavisworks.Shared.projitems b/ConnectorNavisworks/ConnectorNavisworks/ConnectorNavisworks.Shared.projitems index 63fcb74627..fbabbd2c39 100644 --- a/ConnectorNavisworks/ConnectorNavisworks/ConnectorNavisworks.Shared.projitems +++ b/ConnectorNavisworks/ConnectorNavisworks/ConnectorNavisworks.Shared.projitems @@ -9,6 +9,7 @@ Speckle.ConnectorNavisworks + @@ -23,6 +24,7 @@ + diff --git a/ConnectorNavisworks/ConnectorNavisworks/Entry/Ribbon.xaml.cs b/ConnectorNavisworks/ConnectorNavisworks/Entry/Ribbon.xaml.cs index 081412e1ba..d602f5e96e 100644 --- a/ConnectorNavisworks/ConnectorNavisworks/Entry/Ribbon.xaml.cs +++ b/ConnectorNavisworks/ConnectorNavisworks/Entry/Ribbon.xaml.cs @@ -1,10 +1,10 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Windows.Forms; using Autodesk.Navisworks.Api.Plugins; using Speckle.ConnectorNavisworks.Bindings; +using Speckle.Core.Helpers; using Speckle.Core.Logging; using NavisworksApp = Autodesk.Navisworks.Api.Application; @@ -262,7 +262,7 @@ public override int ExecuteCommand(string commandId, params string[] parameters) case Community.COMMAND: { - Process.Start("https://speckle.community/tag/navisworks"); + Open.Url("https://speckle.community/tag/navisworks"); break; } diff --git a/ConnectorNavisworks/ConnectorNavisworks/NavisworksOptions/Manager.cs b/ConnectorNavisworks/ConnectorNavisworks/NavisworksOptions/Manager.cs index e58f088a69..84b34c6ef8 100644 --- a/ConnectorNavisworks/ConnectorNavisworks/NavisworksOptions/Manager.cs +++ b/ConnectorNavisworks/ConnectorNavisworks/NavisworksOptions/Manager.cs @@ -1,6 +1,34 @@ +using DesktopUI2.Models; +using DesktopUI2.Models.Settings; + namespace Speckle.ConnectorNavisworks.NavisworksOptions; /// /// Manages the Setting and Getting of Internal Navisworks options. /// -public partial class NavisworksOptionsManager { } +public partial class NavisworksOptionsManager +{ + internal void InitializeManagerOptionsForSend(StreamState state) + { + var internalPropertySettings = state.Settings.Find(x => x.Slug == "internal-properties"); + var internalPropertyNames = state.Settings.Find(x => x.Slug == "internal-property-names"); + + if (internalPropertySettings != null && ((CheckBoxSetting)internalPropertySettings).IsChecked) + { + ShowInternalProperties(); + } + else + { + HideInternalProperties(); + } + + if (internalPropertyNames != null && ((CheckBoxSetting)internalPropertyNames).IsChecked) + { + UseInternalPropertyNames(); + } + else + { + MaskInternalPropertyNames(); + } + } +} diff --git a/ConnectorNavisworks/ConnectorNavisworks/NavisworksOptions/Properties.cs b/ConnectorNavisworks/ConnectorNavisworks/NavisworksOptions/Properties.cs index 81992fa473..d5abf842c0 100644 --- a/ConnectorNavisworks/ConnectorNavisworks/NavisworksOptions/Properties.cs +++ b/ConnectorNavisworks/ConnectorNavisworks/NavisworksOptions/Properties.cs @@ -78,11 +78,21 @@ private void UpdateInternalPropertyNameSetting(bool enable) => /// public void ShowInternalProperties() => UpdateInternalPropertySetting(true); + /// + /// Hides internal properties. + /// + public void HideInternalProperties() => UpdateInternalPropertySetting(false); + /// /// Uses internal property names. /// public void UseInternalPropertyNames() => UpdateInternalPropertyNameSetting(true); + /// + /// Masks internal property names. + /// + public void MaskInternalPropertyNames() => UpdateInternalPropertyNameSetting(false); + /// /// Restores the internal properties display to its original state after the send process. /// diff --git a/ConnectorNavisworks/ConnectorNavisworks/Other/Developer.cs b/ConnectorNavisworks/ConnectorNavisworks/Other/Developer.cs new file mode 100644 index 0000000000..5c4b1230d0 --- /dev/null +++ b/ConnectorNavisworks/ConnectorNavisworks/Other/Developer.cs @@ -0,0 +1,3 @@ +namespace Speckle.ConnectorNavisworks.Other; + +public class Developer { } diff --git a/ConnectorNavisworks/ConnectorNavisworks/Other/Elements.cs b/ConnectorNavisworks/ConnectorNavisworks/Other/Elements.cs index 52068a8612..8559813ed5 100644 --- a/ConnectorNavisworks/ConnectorNavisworks/Other/Elements.cs +++ b/ConnectorNavisworks/ConnectorNavisworks/Other/Elements.cs @@ -1,10 +1,10 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Dynamic; using System.Linq; using Autodesk.Navisworks.Api; -using Autodesk.Navisworks.Api.ComApi; -using Autodesk.Navisworks.Api.Interop.ComApi; +using Autodesk.Navisworks.Api.DocumentParts; using DesktopUI2.Models; using DesktopUI2.Models.Settings; using Speckle.Core.Models; @@ -15,148 +15,48 @@ public class Element { private ModelItem _modelItem; - /// - /// Initializes a new instance of the Element class with a specified pseudoId and optionally a ModelItem. - /// - /// The pseudoId used to create the Element instance. - /// Optional ModelItem used to create the Element instance. Default is null. - private Element(string pseudoId, ModelItem modelItem = null) - { - PseudoId = pseudoId; - _modelItem = modelItem; - } - - private Element(string pseudoId) - { - PseudoId = pseudoId; - } - - public Element() { } + private string _indexPath; - public ModelItem ModelItem => Resolve(); - - public string PseudoId { get; private set; } - - private static readonly int[] s_lowerBounds = new[] { 1 }; - private static readonly string[] s_separator = new[] { "." }; - - /// - /// Creates a new Element instance using a given pseudoId. - /// - /// The pseudoId used to create the Element instance. - /// A new Element instance with its pseudoId set. - public static Element GetElement(string pseudoId) => new(pseudoId); - - /// - /// Creates a new Element instance using a given ModelItem. - /// - /// The ModelItem used to create the Element instance. - /// A new Element instance with its PseudoId and _modelItem field set. - public Element GetElement(ModelItem modelItem) => new(GetPseudoId(modelItem), modelItem); + const char SEPARATOR = '/'; - /// - /// Gets the PseudoId for the given ModelItem. - /// - /// The ModelItem for which to get the PseudoId. - /// The PseudoId of the given ModelItem. If the PseudoId is not set, it is calculated and returned. - private string GetPseudoId(ModelItem modelItem) + public string IndexPath { - if (PseudoId != null) + get { - return PseudoId; - } + if (_indexPath == null && _modelItem != null) + { + _indexPath = ResolveModelItemToIndexPath(_modelItem); + } - var arrayData = ((Array)ComApiBridge.ToInwOaPath(modelItem).ArrayData).ToArray(); - PseudoId = - arrayData.Length == 0 - ? Constants.ROOT_NODE_PSEUDO_ID - : string.Join("-", arrayData.Select(x => x.ToString().PadLeft(4, '0'))); - return PseudoId; + return _indexPath; + } } - /// - /// Resolves a ModelItem from a PseudoId. - /// - /// A ModelItem that corresponds to the PseudoId, or null if the PseudoId could not be resolved. - private ModelItem Resolve() + public ModelItem ModelItem { - if (_modelItem != null) + get { - return _modelItem; - } - - if (PseudoId == Constants.ROOT_NODE_PSEUDO_ID) - { - return Application.ActiveDocument.Models.RootItems.First; - } - - if (PseudoId != null) - { - int[] pathArray; - - try + if (_modelItem == null && _indexPath != null) { - pathArray = ParsePseudoIdToPathArray(PseudoId); - } - catch (ArgumentException) - { - return null; + _modelItem = ResolveIndexPath(_indexPath); } - var oneBasedArray = ConvertTo1BasedArray(pathArray); - var protoPath = CreateProtoPath(oneBasedArray); - - _modelItem = ComApiBridge.ToModelItem(protoPath); + return _modelItem; } - - return _modelItem; } - /// - /// Parses a PseudoId into a path array. - /// - /// The PseudoId to parse. - /// An array of integers representing the path. - /// Thrown when the PseudoId is malformed. - private int[] ParsePseudoIdToPathArray(string pseudoId) => - pseudoId - .Split('-') - .Select(x => - { - if (int.TryParse(x, out var value)) - { - return value; - } - - throw new ArgumentException("malformed path pseudoId"); - }) - .ToArray(); - - /// - /// Converts a zero-based integer array into a one-based array. - /// - /// The zero-based integer array to convert. - /// A one-based array with the same elements as the input array. - private Array ConvertTo1BasedArray(int[] pathArray) + public Element(string indexPath) { - var oneBasedArray = Array.CreateInstance(typeof(int), new[] { pathArray.Length }, s_lowerBounds); - Array.Copy(pathArray, 0, oneBasedArray, 1, pathArray.Length); - return oneBasedArray; + _indexPath = indexPath; } - /// - /// Creates a protoPath from a one-based array. - /// - /// The one-based array to use for creating the protoPath. - /// A protoPath that corresponds to the input array. - private InwOaPath CreateProtoPath(Array oneBasedArray) + public Element(ModelItem modelItem) { - var oState = ComApiBridge.State; - var protoPath = (InwOaPath)oState.ObjectFactory(nwEObjectType.eObjectType_nwOaPath); - protoPath.ArrayData = oneBasedArray; - return protoPath; + _modelItem = modelItem; } + private static readonly string[] s_separator = { SEPARATOR.ToString() }; + /// /// Generates a descriptor for the given model item. /// @@ -185,92 +85,148 @@ private static string ElementDescriptor(ModelItem modelItem) /// /// /// - /// The input dictionary to be converted into a hierarchical structure. /// An IEnumerable of root nodes representing the hierarchical structure. - public static IEnumerable BuildNestedObjectHierarchy( + public static IEnumerable BuildNestedObjectHierarchyInParallel( Dictionary> converted, - StreamState streamState + StreamState streamState, + ProgressInvoker progressBar ) { - var convertedDictionary = converted.ToDictionary(x => x.Key.PseudoId, x => (x.Value.Item2, x.Key)); + var convertedDictionary = converted.ToDictionary(x => x.Key.IndexPath, x => (x.Value.Item2, x.Key)); - // This dictionary is for looking up parents quickly - Dictionary lookupDictionary = new(); + ConcurrentDictionary lookupDictionary = new(); + ConcurrentDictionary potentialRootNodes = new(); - // This dictionary will hold potential root nodes until we confirm they are roots - Dictionary potentialRootNodes = new(); + int totalCount = convertedDictionary.Count; + const int DEFAULT_UPDATE_INTERVAL = 1000; - // First pass: Create lookup dictionary and identify potential root nodes - foreach (var pair in convertedDictionary) + List rootNodes = new(); // Initialize rootNodes here + + try { - var element = pair.Value.Key; - var pseudoId = element.PseudoId; - var baseNode = pair.Value.Item1; - var modelItem = element.ModelItem; - var type = baseNode?.GetType().Name; + // First pass: Populate lookup dictionary and identify potential root nodes + ExecuteWithProgress( + totalCount, + progressBar, + "Identifying roots", + i => + { + var pair = convertedDictionary.ElementAt(i); + var element = pair.Value.Key; + var indexPath = element.IndexPath; + var baseNode = pair.Value.Item1; + var modelItem = element.ModelItem; + var type = baseNode?.GetType().Name; + + if (baseNode == null) + { + return; + } - if (baseNode == null) - { - continue; - } + if ( + streamState.Settings.Find(x => x.Slug == "coalesce-data") is CheckBoxSetting { IsChecked: true } + && type == "GeometryNode" + ) + { + AddPropertyStackToGeometryNode(converted, modelItem, baseNode); + } - // Geometry Nodes can add all the properties to the FirstObject classification - this will help with the selection logic - if ( - streamState.Settings.Find(x => x.Slug == "coalesce-data") is CheckBoxSetting { IsChecked: true } - && type == "GeometryNode" - ) - { - AddPropertyStackToGeometryNode(converted, modelItem, baseNode); - } + string[] parts = indexPath.Split(SEPARATOR); + string parentKey = string.Join(SEPARATOR.ToString(), parts.Take(parts.Length - 1)); - string[] parts = pseudoId.Split('-'); - string parentKey = string.Join("-", parts.Take(parts.Length - 1)); + lookupDictionary.TryAdd(indexPath, baseNode); - lookupDictionary.Add(pseudoId, baseNode); + if (!lookupDictionary.ContainsKey(parentKey)) + { + potentialRootNodes.TryAdd(indexPath, baseNode); + } + } + ); + + // Second pass: Attach child nodes to parents and confirm root nodes + ExecuteWithProgress( + lookupDictionary.Count, + progressBar, + "Reuniting children with parents", + i => + { + var pair = lookupDictionary.ElementAt(i); + string key = pair.Key; + Base value = pair.Value; - if (!lookupDictionary.ContainsKey(parentKey)) - { - potentialRootNodes.Add(pseudoId, baseNode); - } - } + string[] parts = key.Split(SEPARATOR); + string parentKey = string.Join(SEPARATOR.ToString(), parts.Take(parts.Length - 1)); - // Second pass: Attach child nodes to their parents, and confirm root nodes - foreach (var pair in lookupDictionary) - { - string key = pair.Key; - Base value = pair.Value; + if (!lookupDictionary.TryGetValue(parentKey, out Base parentValue) || parentValue is not Collection parent) + { + return; + } - string[] parts = key.Split('-'); - string parentKey = string.Join("-", parts.Take(parts.Length - 1)); + parent.elements.Add(value); - if (!lookupDictionary.TryGetValue(parentKey, out Base value1)) - { - continue; - } + potentialRootNodes.TryRemove(key, out _); + } + ); - if (value1 is Collection parent) - { - parent.elements ??= new List(); - if (value != null) + rootNodes = potentialRootNodes.Values.ToList(); + + // Prune empty collections + ExecuteWithProgress( + rootNodes.Count, + progressBar, + "Recycling empties", + i => { - parent.elements.Add(value); + var rootNode = rootNodes[i]; + if (rootNode != null) + { + PruneEmptyCollections(rootNode); + } } - } + ); - // This node has a parent, so it's not a root node - potentialRootNodes.Remove(key); + rootNodes.RemoveAll(node => node is Collection { elements: null }); + } + catch (OperationCanceledException) + { + // Handle cancellation if needed + } + catch (Exception ex) + { + throw new InvalidOperationException("An error occurred during the operation.", ex); } - List rootNodes = potentialRootNodes.Values.ToList(); + return rootNodes; + } + + private static void ExecuteWithProgress( + int totalCount, + ProgressInvoker progressBar, + string operationName, + Action action + ) + { + int progressCounter = 0; + const int DEFAULT_UPDATE_INTERVAL = 1000; + + progressBar.BeginSubOperation(0.2, operationName); + progressBar.Update(0); - foreach (var rootNode in rootNodes.Where(rootNode => rootNode != null)) + for (int i = 0; i < totalCount; i++) { - PruneEmptyCollections(rootNode); - } + action(i); + + progressCounter++; + if (progressCounter % DEFAULT_UPDATE_INTERVAL != 0 && progressCounter != totalCount) + { + continue; + } - rootNodes.RemoveAll(node => node is Collection { elements: null }); + double progressValue = Math.Min((double)progressCounter / totalCount, 1.0); + progressBar.Update(progressValue); + } - return rootNodes; + progressBar.EndSubOperation(); } /// @@ -285,27 +241,41 @@ private static void AddPropertyStackToGeometryNode( DynamicBase baseNode ) { - var firstObjectAncestor = modelItem.FindFirstObjectAncestor(); - var ancestors = modelItem.Ancestors; + if (modelItem == null || baseNode == null || converted == null) + { + throw new ArgumentNullException("modelItem, baseNode, and converted cannot be null."); + } + + var firstObjectAncestor = + modelItem.FindFirstObjectAncestor() ?? throw new InvalidOperationException("firstObjectAncestor is null."); + var ancestors = modelItem.Ancestors ?? throw new InvalidOperationException("ancestors is null."); var trimmedAncestors = ancestors.TakeWhile(ancestor => ancestor != firstObjectAncestor).Append(firstObjectAncestor); - var propertyStack = trimmedAncestors + var filtered = trimmedAncestors .Select(item => converted.FirstOrDefault(keyValuePair => Equals(keyValuePair.Key.ModelItem, item))) + .Where(kVp => kVp.Key != null) // Filter out null keys .Select(kVp => kVp.Value.Item2["properties"] as Base) + .Where(propertySet => propertySet != null); // Filter out null property sets + + var categoryProperties = filtered.SelectMany( + propertySet => propertySet.GetMembers().Where(member => member.Value is Base), + (_, propertyCategory) => + new { Category = propertyCategory.Key, Properties = ((Base)propertyCategory.Value).GetMembers() } + ); + + var properties = categoryProperties .SelectMany( - propertySet => propertySet?.GetMembers().Where(member => member.Value is Base), - (_, propertyCategory) => - new { Category = propertyCategory.Key, Properties = ((Base)propertyCategory.Value).GetMembers() } - ) - .SelectMany( - categoryProperties => categoryProperties.Properties, - (categoryProperties, property) => - new { ConcatenatedKey = $"{categoryProperties.Category}--{property.Key}", property.Value } + cp => cp.Properties, + (cp, property) => new { ConcatenatedKey = $"{cp.Category}--{property.Key}", property.Value } ) - .Where(property => property.Value != null && !string.IsNullOrEmpty(property.Value.ToString())) + .Where(property => property.Value != null && !string.IsNullOrEmpty(property.Value.ToString())); + + var groupedProperties = properties .GroupBy(property => property.ConcatenatedKey) .Where(group => group.Select(item => item.Value).Distinct().Count() == 1) - .ToDictionary(group => group.Key, group => group.First().Value) + .ToDictionary(group => group.Key, group => group.First().Value); + + var formattedProperties = groupedProperties .Select( kVp => new @@ -315,11 +285,17 @@ DynamicBase baseNode kVp.Value } ) - .Where(item => item.Category != "Internal") + .Where(item => item.Category != "Internal"); + + var propertyStack = formattedProperties .GroupBy(item => item.Category) .ToDictionary(group => group.Key, group => group.ToDictionary(item => item.Property, item => item.Value)); - var propertiesBase = (Base)baseNode["properties"]; + if (baseNode["properties"] is not Base propertiesBase) + { + propertiesBase = new Base(); + baseNode["properties"] = propertiesBase; + } var baseProperties = propertiesBase.GetMembers().Where(item => item.Value is Base).ToList(); @@ -371,8 +347,6 @@ DynamicBase baseNode propertiesBase[stackProperty.Key] = newPropertyCategory; } } - - // baseNode["property-stack"] = propertyStack; } /// @@ -386,7 +360,7 @@ private static void PruneEmptyCollections(IDynamicMetaObjectProvider node) return; } - if (collection.elements == null) + if (collection.elements.Count == 0) { return; } @@ -395,10 +369,7 @@ private static void PruneEmptyCollections(IDynamicMetaObjectProvider node) { PruneEmptyCollections(collection.elements[i]); - if ( - collection.elements[i] is Collection childCollection - && (childCollection.elements == null || childCollection.elements.Count == 0) - ) + if (collection.elements[i] is Collection { elements.Count: 0 }) { collection.elements.RemoveAt(i); } @@ -406,7 +377,40 @@ collection.elements[i] is Collection childCollection if (collection.elements.Count == 0) { - collection.elements = null; + collection.elements = null!; } } + + public static ModelItem ResolveIndexPath(string indexPath) + { + var indexPathParts = indexPath.Split(SEPARATOR); + + var modelIndex = int.Parse(indexPathParts[0]); + var pathId = string.Join(SEPARATOR.ToString(), indexPathParts.Skip(1)); + + // assign the first part of indexPathParts to modelIndex and parse it to int, the second part to pathId string + ModelItemPathId modelItemPathId = new() { ModelIndex = modelIndex, PathId = pathId }; + + var modelItem = Application.ActiveDocument.Models.ResolvePathId(modelItemPathId); + return modelItem; + } + + public static string ResolveModelItemToIndexPath(ModelItem modelItem) + { + var modelItemPathId = Application.ActiveDocument.Models.CreatePathId(modelItem); + + return modelItemPathId.PathId == "a" + ? $"{modelItemPathId.ModelIndex}" + : $"{modelItemPathId.ModelIndex}{SEPARATOR}{modelItemPathId.PathId}"; + } + + /// + /// Checks is the Element is hidden or if any of its ancestors is hidden + /// + /// + /// + internal static bool IsElementVisible(ModelItem element) => + // Hidden status is stored at the earliest node in the hierarchy + // All the tree path nodes need to not be Hidden + element.AncestorsAndSelf.All(x => x.IsHidden != true); } diff --git a/ConnectorNavisworks/ConnectorNavisworks/Other/Invokers.cs b/ConnectorNavisworks/ConnectorNavisworks/Other/Invokers.cs index 88f72466ea..aeff0ca483 100644 --- a/ConnectorNavisworks/ConnectorNavisworks/Other/Invokers.cs +++ b/ConnectorNavisworks/ConnectorNavisworks/Other/Invokers.cs @@ -66,7 +66,6 @@ public sealed class ProgressInvoker : Invoker /// Provides an instance of the Progress class which starts reporting of progress /// of an operation, typically displaying a progress bar or dialog to the end user. /// - /// The title of the progress operation. Defaults to an empty string. public ProgressInvoker(Progress applicationProgress) { _progressBar = applicationProgress; diff --git a/ConnectorNavisworks/ConnectorNavisworks/Other/SelectionBuilder.cs b/ConnectorNavisworks/ConnectorNavisworks/Other/SelectionBuilder.cs index b1dcf4dfb6..80fcc3b514 100644 --- a/ConnectorNavisworks/ConnectorNavisworks/Other/SelectionBuilder.cs +++ b/ConnectorNavisworks/ConnectorNavisworks/Other/SelectionBuilder.cs @@ -1,6 +1,9 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; +using System.Threading; +using System.Threading.Tasks; using Autodesk.Navisworks.Api; using Autodesk.Navisworks.Gui; using DesktopUI2.Models; @@ -73,25 +76,24 @@ private HashSet GetObjectsFromSelection() // Selections are modelItem pseudo-ids. var selection = _filter.Selection; - var count = selection.Count; - var progressIncrement = 1.0 / count != 0 ? count : 1.0; - // Begin the progress sub-operation for getting objects from selection - ProgressBar.BeginSubOperation(0.05, "Rolling up the sleeves... Time to handpick your favorite data items!"); - - // Iterate over the selection and retrieve the corresponding model items - for (var i = 0; i < count; i++) - { - _progressViewModel.CancellationToken.ThrowIfCancellationRequested(); - ProgressBar.Update(i * progressIncrement); + ProgressLooper( + "Rolling up the sleeves... Time to handpick your favourite data items!", + (index) => + { + if (index >= selection.Count) + { + return false; + } - var pseudoId = selection[i]; - var element = Element.GetElement(pseudoId); - _uniqueModelItems.Add(element.ModelItem); - } + _progressViewModel.CancellationToken.ThrowIfCancellationRequested(); + _uniqueModelItems.Add(Element.ResolveIndexPath(selection[index])); - // End the progress sub-operation - ProgressBar.EndSubOperation(); + return true; + }, + 0.05, // Fraction of remaining time + totalCount: selection.Count // Pass the total count if known, else pass null + ); return _uniqueModelItems; } @@ -99,23 +101,18 @@ private HashSet GetObjectsFromSelection() /// /// Retrieves the model items from the saved viewpoint. /// - private IEnumerable GetObjectsFromSavedViewpoint() + private HashSet GetObjectsFromSavedViewpoint() { _uniqueModelItems.Clear(); - // Begin the progress sub-operation for getting objects from selection - ProgressBar.BeginSubOperation(0.05, "Checking the Canvas... Looking Closely!"); - // Get the selection from the filter var selection = _filter.Selection.FirstOrDefault(); if (string.IsNullOrEmpty(selection)) { - return Enumerable.Empty(); + return new HashSet(); } // Resolve the saved viewpoint based on the selection - // Makes the view active on the main thread. - var success = false; new Invoker().Invoke(() => @@ -132,26 +129,34 @@ private IEnumerable GetObjectsFromSavedViewpoint() if (!success) { - return Enumerable.Empty(); + return new HashSet(); } var models = Application.ActiveDocument.Models; Application.ActiveDocument.CurrentSelection.Clear(); - for (var i = 0; i < models.Count; i++) - { - var model = models.ElementAt(i); - var rootItem = model.RootItem; - if (!rootItem.IsHidden) + // Use ProgressLooper to handle the looping and progress updates + ProgressLooper( + "Checking the Canvas... Looking Closely!", + (index) => { - _uniqueModelItems.Add(rootItem); - } + if (index >= models.Count) + { + return false; + } - ProgressBar.Update(i + 1 / (double)models.Count); - } + var model = models[index]; + var rootItem = model.RootItem; + if (!rootItem.IsHidden) + { + _uniqueModelItems.Add(rootItem); + } - // End the progress sub-operation - ProgressBar.EndSubOperation(); + return true; + }, + 0.05, // Fraction of remaining time + models.Count // Pass the total count if known, else pass null + ); return _uniqueModelItems; } @@ -204,7 +209,6 @@ public SavedViewpoint ResolveSavedViewpoint(string savedViewReference) /// /// Resolves the SavedViewpoint based on the provided viewpoint match and saved view reference. /// - /// The dynamic object representing the viewpoint match. /// The saved view reference to resolve. /// The resolved SavedViewpoint. private SavedViewpoint ResolveSavedViewpointMatch(string savedViewReference) @@ -290,11 +294,14 @@ private HashSet GetObjectsFromSavedSets() { if (item.HasExplicitModelItems) { + // This is for saved selections. If the models are as were when selection was made, then the static results are all valid. _uniqueModelItems.AddRange(item.ExplicitModelItems); } - else if (item.HasSearch) + else if (item.HasSearch) // This is for saved searches. The results are dynamic and need to be resolved. { - _uniqueModelItems.AddRange(item.Search.FindAll(Application.ActiveDocument, false)); + // This is for saved searches. The results are dynamic and need to be resolved. + var foundModelItems = item.Search.FindAll(Application.ActiveDocument, false); + _uniqueModelItems.AddRange(foundModelItems); } } @@ -331,7 +338,7 @@ public void PopulateHierarchyAndOmitHidden() || _uniqueModelItems.Contains(firstObjectAncestor) ) { - return Enumerable.Empty(); + return new HashSet(); } var trimmedAncestors = targetFirstObjectChild.Ancestors @@ -350,29 +357,56 @@ public void PopulateHierarchyAndOmitHidden() var allAncestors = startNodes.SelectMany(e => e.Ancestors).Distinct().ToList(); ProgressLooper( - allAncestors.Count, "Brb, time traveling to find your data's great-grandparents...", i => { - _uniqueModelItems.Add(allAncestors.ElementAt(i)); + _uniqueModelItems.Add(allAncestors[i]); return true; }, - 0.05 + 0.05, + allAncestors.Count ); } _visited = new HashSet(); _descendantProgress = 0; - var allDescendants = startNodes.SelectMany(e => e.Descendants).Distinct().Count(); - ProgressBar.BeginSubOperation(0.1, "Validating descendants..."); + HashSet distinctDescendants = DistinctDescendants(startNodes); + var allDescendants = distinctDescendants.Count; + + ProgressLooper( + "Validating descendants...", + i => + { + _progressViewModel.CancellationToken.ThrowIfCancellationRequested(); + + TraverseDescendants( + startNodes[i], + allDescendants, + new Progress(value => + { + ProgressBar.Update(value); + }) + ); + + return true; + }, + 0.1, + startNodes.Count + ); + } + + private static HashSet DistinctDescendants(List startNodes) + { + var distinctDescendants = new HashSet(); foreach (var node in startNodes) { - TraverseDescendants(node, allDescendants); + var nodeDescendants = node.Descendants.ToList(); + distinctDescendants.UnionWith(nodeDescendants); } - ProgressBar.EndSubOperation(); + return distinctDescendants; } /// @@ -380,11 +414,13 @@ public void PopulateHierarchyAndOmitHidden() /// /// The starting node for traversal. /// The total number of descendants. - private void TraverseDescendants(ModelItem startNode, int totalDescendants) + private void TraverseDescendants(ModelItem startNode, int totalDescendants, IProgress progress) { - var descendantInterval = Math.Max(totalDescendants / 100.0, 1); + var descendantInterval = Math.Max(totalDescendants / 100.0, 1); // Update progress every 1% var validDescendants = new HashSet(); - int lastPercentile = 0; + + int updateCounter = 0; // Counter to track when to update the UI + int lastUpdate = 0; // Track the last update to avoid frequent updates Stack stack = new(); stack.Push(startNode); @@ -400,6 +436,7 @@ private void TraverseDescendants(ModelItem startNode, int totalDescendants) ModelItem currentNode = stack.Pop(); + // ReSharper disable once CanSimplifySetAddingWithSingleCall if (_visited.Contains(currentNode)) { continue; @@ -407,84 +444,196 @@ private void TraverseDescendants(ModelItem startNode, int totalDescendants) _visited.Add(currentNode); - if (currentNode.IsHidden) + bool isVisible = IsVisibleCached(currentNode); + if (!isVisible) { + // If node is hidden, skip processing it and all its descendants var descendantsCount = currentNode.Descendants.Count(); - _descendantProgress += descendantsCount + 1; - } - else - { - validDescendants.Add(currentNode); - _descendantProgress++; + Interlocked.Add(ref _descendantProgress, descendantsCount + 1); + continue; } + validDescendants.Add(currentNode); // currentNode is visible, process it + Interlocked.Increment(ref _descendantProgress); + if (currentNode.Children.Any()) { - foreach (var child in currentNode.Children.Where(e => !e.IsHidden)) + // Add visible children to the stack + + var childrenToProcess = new ConcurrentBag(); + + Parallel.ForEach( + currentNode.Children, + child => + { + if (_visited.Contains(child)) + { + return; + } + + if (IsVisibleCached(child)) + { + childrenToProcess.Add(child); + } + else + { + // If child is hidden, skip processing it and all its descendants + int descendantsCount = child.Descendants.Count(); + Interlocked.Add(ref _descendantProgress, descendantsCount + 1); + } + } + ); + + foreach (ModelItem child in childrenToProcess) { stack.Push(child); } } - _uniqueModelItems.AddRange(validDescendants); + lock (_uniqueModelItems) + { + _uniqueModelItems.UnionWith(validDescendants); + } + validDescendants.Clear(); + + updateCounter++; - int currentPercentile = (int)(_descendantProgress / descendantInterval); - if (currentPercentile <= lastPercentile) + if (!(updateCounter >= descendantInterval) || lastUpdate >= _descendantProgress) { continue; } - double progress = _descendantProgress / (double)totalDescendants; - ProgressBar.Update(progress); - lastPercentile = currentPercentile; + double progressValue = _descendantProgress / (double)totalDescendants; + progress.Report(progressValue); + lastUpdate = _descendantProgress; + updateCounter = 0; } } + // Cache to store visibility status of ModelItems + private readonly Dictionary _visibilityCache = new(); + + /// + /// Checks if a ModelItem is visible, with caching to avoid redundant calculations. + /// + /// The ModelItem to check visibility for. + /// True if the item is visible, false otherwise. + private bool IsVisibleCached(ModelItem item) + { + // Check if the result is already in the cache + if (_visibilityCache.TryGetValue(item, out bool isVisible)) + { + return isVisible; + } + // Calculate visibility if not in cache + isVisible = CalculateVisibility(item); + _visibilityCache[item] = isVisible; + return isVisible; + } + + /// + /// Placeholder for if the default visibility determination logic need augmenting. + /// + /// The ModelItem to check. + /// True if visible, false otherwise. + private static bool CalculateVisibility(ModelItem item) => !item.IsHidden; + /// /// Executes a given function while updating a progress bar. /// - /// The total number of iterations. /// The name of the operation. /// The function to execute on each iteration. /// The fraction of remaining time for the operation (optional). - private void ProgressLooper( - int totalCount, + /// The total number of iterations, if known. + public void ProgressLooper( string operationName, Func fn, - double fractionOfRemainingTime = 0 + double fractionOfRemainingTime = 0, + int? totalCount = null ) { - var increment = 1.0 / totalCount != 0 ? 1.0 / totalCount : 1.0; - var updateInterval = Math.Max(totalCount / 100, 1); + const int DEFAULT_UPDATE_INTERVAL = 1000; + const double DEFAULT_PROGRESS_INCREMENT = 0.01; + ProgressBar.BeginSubOperation(fractionOfRemainingTime, operationName); ProgressBar.Update(0); - for (int i = 0; i < totalCount; i++) + try { - if (ProgressBar.IsCanceled) + int i = 0; + double progress = 0; + double increment; + int updateInterval; + + if (totalCount.HasValue) { - _progressViewModel.CancellationTokenSource.Cancel(); + increment = 1.0 / totalCount.Value; + updateInterval = Math.Max(totalCount.Value / 100, 1); } - - _progressViewModel.CancellationToken.ThrowIfCancellationRequested(); - - bool shouldContinue = fn(i); - - if (!shouldContinue) + else { - break; + increment = DEFAULT_PROGRESS_INCREMENT; + updateInterval = DEFAULT_UPDATE_INTERVAL; } - if (i % updateInterval != 0 && i != totalCount) + while (true) { - continue; + if (ProgressBar.IsCanceled) + { + _progressViewModel.CancellationTokenSource.Cancel(); + break; + } + + _progressViewModel.CancellationToken.ThrowIfCancellationRequested(); + + if (!fn(i)) + { + break; + } + + var test = fn(i); + + i++; + + if (totalCount.HasValue) + { + progress = Math.Min((double)i / totalCount.Value, 1.0); + if (i % updateInterval == 0 || i == totalCount.Value) + { + ProgressBar.Update(progress); + } + + if (i >= totalCount.Value) + { + break; + } + } + else + { + if (i % updateInterval != 0) + { + continue; + } + + progress = Math.Min(progress + increment, 1.0); + ProgressBar.Update(progress); + } } - double progress = (i + 1) * increment; - ProgressBar.Update(progress); + ProgressBar.Update(1.0); + } + catch (OperationCanceledException) + { + // Handle cancellation if needed + } + catch (Exception ex) + { + throw new InvalidOperationException("An error occurred during the operation.", ex); + } + finally + { + ProgressBar.EndSubOperation(); } - - ProgressBar.EndSubOperation(); } /// diff --git a/ConnectorRevit/ConnectorRevit/Entry/App.cs b/ConnectorRevit/ConnectorRevit/Entry/App.cs index a02e17c337..f5f37980c9 100644 --- a/ConnectorRevit/ConnectorRevit/Entry/App.cs +++ b/ConnectorRevit/ConnectorRevit/Entry/App.cs @@ -1,5 +1,4 @@ using System; -using System.Diagnostics; using System.IO; using System.Reflection; using System.Windows.Media; @@ -18,6 +17,7 @@ using Speckle.DllConflictManagement.EventEmitter; using Speckle.DllConflictManagement.ConflictManagementOptions; using Autodesk.Revit.DB.Events; +using Speckle.Core.Helpers; namespace Speckle.ConnectorRevit.Entry; @@ -378,7 +378,7 @@ ex is KitException if (TaskDialogResult.CommandLink1 == tResult) { - Process.Start("https://speckle.community/"); + Open.Url("https://speckle.community/"); } } diff --git a/ConnectorRevit/ConnectorRevit/Entry/HelpCommand.cs b/ConnectorRevit/ConnectorRevit/Entry/HelpCommand.cs index 6c1cdcf8c6..01c558422b 100644 --- a/ConnectorRevit/ConnectorRevit/Entry/HelpCommand.cs +++ b/ConnectorRevit/ConnectorRevit/Entry/HelpCommand.cs @@ -1,9 +1,9 @@ using System; -using System.Diagnostics; using System.IO; using Autodesk.Revit.Attributes; using Autodesk.Revit.DB; using Autodesk.Revit.UI; +using Speckle.Core.Helpers; namespace Speckle.ConnectorRevit.Entry; @@ -12,7 +12,7 @@ public class ForumCommand : IExternalCommand { public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements) { - Process.Start("https://speckle.community/"); + Open.Url("https://speckle.community/"); return Result.Succeeded; } } @@ -22,7 +22,7 @@ public class DocsCommand : IExternalCommand { public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements) { - Process.Start("https://speckle.guide/user/revit.html"); + Open.Url("https://speckle.guide/user/revit.html"); return Result.Succeeded; } } @@ -32,7 +32,7 @@ public class TutorialsCommand : IExternalCommand { public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements) { - Process.Start("https://speckle.systems/tutorials/"); + Open.Url("https://speckle.systems/tutorials/"); return Result.Succeeded; } } @@ -50,7 +50,7 @@ public Result Execute(ExternalCommandData commandData, ref string message, Eleme ); if (File.Exists(path)) { - Process.Start(path); + Open.File(path); } else { diff --git a/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/ConnectorBindingsRhino.Previews.cs b/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/ConnectorBindingsRhino.Previews.cs index 00e1bb4df1..cd551584f8 100644 --- a/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/ConnectorBindingsRhino.Previews.cs +++ b/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/ConnectorBindingsRhino.Previews.cs @@ -100,7 +100,9 @@ private static bool IsPreviewIgnore(Base @object) || @object.speckle_type.Contains("View") || @object.speckle_type.Contains("Level") || @object.speckle_type.Contains("GridLine") - || @object.speckle_type.Contains("Collection"); + || @object.speckle_type.Contains("Collection") + || @object.speckle_type.Contains("PolygonElement") + || @object.speckle_type.Contains("GisFeature"); } public override async Task PreviewReceive(StreamState state, ProgressViewModel progress) diff --git a/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/ConnectorBindingsRhino.Receive.cs b/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/ConnectorBindingsRhino.Receive.cs index e85475969d..944d35a57c 100644 --- a/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/ConnectorBindingsRhino.Receive.cs +++ b/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/ConnectorBindingsRhino.Receive.cs @@ -415,8 +415,8 @@ ApplicationObject NewAppObj() return null; } - // get parameters - var parameters = current["parameters"] as Base; + // get parameters and attributes from revit/gis + var parameters = current["parameters"] as Base ?? current["attributes"] as Base; //Handle convertable objects if (converter.CanConvertToNative(current)) @@ -569,29 +569,15 @@ private void BakeObject( continue; } - var attributes = new ObjectAttributes(); - // handle display style - Base display = obj["displayStyle"] as Base ?? obj["@displayStyle"] as Base; - Base render = obj["renderMaterial"] as Base ?? obj["@renderMaterial"] as Base; - if (display != null) - { - var convertedDisplay = converter.ConvertToNative(display) as ObjectAttributes; - if (convertedDisplay is not null) - { - attributes = convertedDisplay; - } - } - else if (render != null) - { - attributes.ColorSource = ObjectColorSource.ColorFromMaterial; - } - - // assign layer - attributes.LayerIndex = layer.Index; - - // handle user info, application id, revit parameters - SetUserInfo(obj, attributes, converter, parent); + // create attributes with layer, display and render, user info, application id, revit/gis parameters + ObjectAttributes attributes = CreateAttributesFromObject( + obj, + layer.Index, + converter, + out RenderMaterial renderMaterial, + parent + ); Guid id = Doc.Objects.Add(o, attributes); if (id == Guid.Empty) @@ -621,22 +607,26 @@ private void BakeObject( bakedCount++; // handle render material - if (render != null) + if (renderMaterial is not null) { - var convertedMaterial = converter.ConvertToNative(render) as RenderMaterial; - if (convertedMaterial is not null) - { - RhinoObject rhinoObject = Doc.Objects.FindId(id); - rhinoObject.RenderMaterial = convertedMaterial; - rhinoObject.CommitChanges(); - } + RhinoObject rhinoObject = Doc.Objects.FindId(id); + rhinoObject.RenderMaterial = renderMaterial; + rhinoObject.CommitChanges(); } break; case RhinoObject o: // this was prbly a block instance, baked during conversion - o.Attributes.LayerIndex = layer.Index; // assign layer - SetUserInfo(obj, o.Attributes, converter, parent); // handle user info, including application id + // create attributes with layer, display and render, user info, application id, revit/gis parameters + ObjectAttributes objectAttributes = CreateAttributesFromObject( + obj, + layer.Index, + converter, + out RenderMaterial objectRenderMaterial, + parent + ); + + o.Attributes = objectAttributes; o.CommitChanges(); if (parent != null) { @@ -650,6 +640,42 @@ private void BakeObject( bakedCount++; break; + case Group o: // this is a GIS object + // create attributes with layer, display and render, user info, application id, revit/gis parameters + ObjectAttributes groupAttributes = CreateAttributesFromObject( + obj, + layer.Index, + converter, + out RenderMaterial groupRenderMaterial, + parent + ); + + groupAttributes.AddToGroup(o.Index); + + foreach (RhinoObject groupObject in Doc.Objects.FindByGroup(o.Index)) + { + groupObject.Attributes = groupAttributes; + + // handle render material + if (groupRenderMaterial is not null) + { + groupObject.RenderMaterial = groupRenderMaterial; + } + + groupObject.CommitChanges(); + } + + if (parent != null) + { + parent.Update(o.Id.ToString()); + } + else + { + appObj.Update(o.Id.ToString()); + } + bakedCount++; + break; + case ViewInfo o: // this is a view, baked during conversion appObj.Update(o.Name); bakedCount++; @@ -675,13 +701,36 @@ private void BakeObject( } } - private void SetUserInfo( + private ObjectAttributes CreateAttributesFromObject( Base obj, - ObjectAttributes attributes, + int layerIndex, ISpeckleConverter converter, + out RenderMaterial renderMaterial, ApplicationObject parent = null ) { + ObjectAttributes attributes = new(); + renderMaterial = null; + + // handle display style + Base display = obj["displayStyle"] as Base ?? obj["@displayStyle"] as Base; + Base render = obj["renderMaterial"] as Base ?? obj["@renderMaterial"] as Base; + if (display != null) + { + var convertedDisplay = converter.ConvertToNative(display) as ObjectAttributes; + if (convertedDisplay is not null) + { + attributes = convertedDisplay; + } + } + else if (render != null) + { + attributes.ColorSource = ObjectColorSource.ColorFromMaterial; + } + + // assign layer + attributes.LayerIndex = layerIndex; + // set user strings if (obj[UserStrings] is Base userStrings) { @@ -708,12 +757,13 @@ private void SetUserInfo( attributes.Name = name; } - // set revit parameters as user strings + // set revit/gis parameters as user strings var paramId = parent != null ? parent.OriginalId : obj.id; if (StoredObjectParams.TryGetValue(paramId, out Base parameters)) { foreach (var member in parameters.GetMembers(DynamicBaseMemberType.Dynamic)) { + // parameters coming from revit, value Base is Objects.BuiltElements.Revit.Parameter if (member.Value is Base parameter) { var convertedParameter = converter.ConvertToNative(parameter) as Tuple; @@ -723,8 +773,22 @@ private void SetUserInfo( attributes.SetUserString(paramName, convertedParameter.Item2); } } + // attributes coming from GIS + else + { + string userStringValue = member.Value is object value ? value.ToString() : string.Empty; + attributes.SetUserString(member.Key, userStringValue); + } } } + + // render material + if (render is not null) + { + renderMaterial = converter.ConvertToNative(render) as RenderMaterial; + } + + return attributes; } // Clears the stored objects, params, and preview objects diff --git a/Core/Core/Api/Exceptions.cs b/Core/Core/Api/Exceptions.cs index 7d10bd9974..f100fd22c3 100644 --- a/Core/Core/Api/Exceptions.cs +++ b/Core/Core/Api/Exceptions.cs @@ -9,50 +9,55 @@ namespace Speckle.Core.Api; /// /// Base class for GraphQL API exceptions /// -public class SpeckleGraphQLException : SpeckleException +public class SpeckleGraphQLException : SpeckleGraphQLException { - private readonly GraphQLRequest _request; - public GraphQLResponse? Response { get; } - - public SpeckleGraphQLException(string message, GraphQLRequest request, GraphQLResponse? response) - : base(message) - { - _request = request; - Response = response; - } + public new GraphQLResponse? Response => (GraphQLResponse?)base.Response; - public SpeckleGraphQLException(string message, Exception inner, GraphQLRequest request, GraphQLResponse? response) - : this(message, inner) - { - _request = request; - Response = response; - } + public SpeckleGraphQLException( + string message, + GraphQLRequest request, + GraphQLResponse? response, + Exception? innerException = null + ) + : base(message, request, response, innerException) { } public SpeckleGraphQLException() { } - public SpeckleGraphQLException(string message) + public SpeckleGraphQLException(string? message) : base(message) { } - public SpeckleGraphQLException(string message, Exception innerException) + public SpeckleGraphQLException(string? message, Exception? innerException) : base(message, innerException) { } +} + +public class SpeckleGraphQLException : SpeckleException +{ + private readonly GraphQLRequest _request; + public IGraphQLResponse? Response { get; } public IEnumerable ErrorMessages => Response?.Errors != null ? Response.Errors.Select(e => e.Message) : Enumerable.Empty(); public IDictionary? Extensions => Response?.Extensions; -} -public class SpeckleGraphQLException : SpeckleGraphQLException -{ - public SpeckleGraphQLException(string message, GraphQLRequest request, GraphQLResponse? response) - : base(message, request, response) { } + public SpeckleGraphQLException( + string? message, + GraphQLRequest request, + IGraphQLResponse? response, + Exception? innerException = null + ) + : base(message, innerException) + { + _request = request; + Response = response; + } public SpeckleGraphQLException() { } - public SpeckleGraphQLException(string message) + public SpeckleGraphQLException(string? message) : base(message) { } - public SpeckleGraphQLException(string message, Exception innerException) + public SpeckleGraphQLException(string? message, Exception? innerException) : base(message, innerException) { } } @@ -61,44 +66,56 @@ public SpeckleGraphQLException(string message, Exception innerException) /// https://www.apollographql.com/docs/apollo-server/v2/data/errors/#unauthenticated /// https://www.apollographql.com/docs/apollo-server/v2/data/errors/#forbidden /// -public class SpeckleGraphQLForbiddenException : SpeckleGraphQLException +public class SpeckleGraphQLForbiddenException : SpeckleGraphQLException { - public SpeckleGraphQLForbiddenException(GraphQLRequest request, GraphQLResponse response) - : base("Your request was forbidden", request, response) { } + public SpeckleGraphQLForbiddenException( + GraphQLRequest request, + IGraphQLResponse response, + Exception? innerException = null + ) + : base("Your request was forbidden", request, response, innerException) { } public SpeckleGraphQLForbiddenException() { } - public SpeckleGraphQLForbiddenException(string message) + public SpeckleGraphQLForbiddenException(string? message) : base(message) { } - public SpeckleGraphQLForbiddenException(string message, Exception innerException) + public SpeckleGraphQLForbiddenException(string? message, Exception? innerException) : base(message, innerException) { } } -public class SpeckleGraphQLInternalErrorException : SpeckleGraphQLException +public class SpeckleGraphQLInternalErrorException : SpeckleGraphQLException { - public SpeckleGraphQLInternalErrorException(GraphQLRequest request, GraphQLResponse response) - : base("Your request failed on the server side", request, response) { } + public SpeckleGraphQLInternalErrorException( + GraphQLRequest request, + IGraphQLResponse response, + Exception? innerException = null + ) + : base("Your request failed on the server side", request, response, innerException) { } public SpeckleGraphQLInternalErrorException() { } - public SpeckleGraphQLInternalErrorException(string message) + public SpeckleGraphQLInternalErrorException(string? message) : base(message) { } - public SpeckleGraphQLInternalErrorException(string message, Exception innerException) + public SpeckleGraphQLInternalErrorException(string? message, Exception? innerException) : base(message, innerException) { } } -public class SpeckleGraphQLStreamNotFoundException : SpeckleGraphQLException +public class SpeckleGraphQLStreamNotFoundException : SpeckleGraphQLException { - public SpeckleGraphQLStreamNotFoundException(GraphQLRequest request, GraphQLResponse response) - : base("Stream not found", request, response) { } + public SpeckleGraphQLStreamNotFoundException( + GraphQLRequest request, + IGraphQLResponse response, + Exception? innerException = null + ) + : base("Stream not found", request, response, innerException) { } public SpeckleGraphQLStreamNotFoundException() { } - public SpeckleGraphQLStreamNotFoundException(string message) + public SpeckleGraphQLStreamNotFoundException(string? message) : base(message) { } - public SpeckleGraphQLStreamNotFoundException(string message, Exception innerException) + public SpeckleGraphQLStreamNotFoundException(string? message, Exception? innerException) : base(message, innerException) { } } diff --git a/Core/Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.ObsoleteOperations.cs b/Core/Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.ObsoleteOperations.cs deleted file mode 100644 index 15d0054f0e..0000000000 --- a/Core/Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.ObsoleteOperations.cs +++ /dev/null @@ -1,250 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Threading; -using System.Threading.Tasks; -using GraphQL; - -namespace Speckle.Core.Api; - -[SuppressMessage("Design", "CA1068:CancellationToken parameters must come last")] -public partial class Client -{ - #region Stream Grant Permission - - /// - /// Grants permissions to a user on a given stream. - /// - /// - /// - [Obsolete("Please use the `StreamUpdatePermission` method", true)] - public Task StreamGrantPermission(StreamPermissionInput permissionInput) - { - return StreamGrantPermission(CancellationToken.None, permissionInput); - } - - /// - /// Grants permissions to a user on a given stream. - /// - /// - /// - /// - [Obsolete("Please use the `StreamUpdatePermission` method", true)] - public async Task StreamGrantPermission( - CancellationToken cancellationToken, - StreamPermissionInput permissionInput - ) - { - var request = new GraphQLRequest - { - Query = - @" - mutation streamGrantPermission($permissionParams: StreamGrantPermissionInput!) { - streamGrantPermission(permissionParams:$permissionParams) - }", - Variables = new { permissionParams = permissionInput } - }; - - var res = await ExecuteGraphQLRequest>(request, cancellationToken).ConfigureAwait(false); - return (bool)res["streamGrantPermission"]; - } - - #endregion - - #region Cancellation token as last param - - [Obsolete("Use overload with cancellation token parameter last")] - public Task> StreamGetActivity( - CancellationToken cancellationToken, - string id, - DateTime? after = null, - DateTime? before = null, - DateTime? cursor = null, - string actionType = "", - int limit = 25 - ) - { - return StreamGetActivity(id, after, before, cursor, actionType, limit, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task> StreamGetBranches( - CancellationToken cancellationToken, - string streamId, - int branchesLimit = 10, - int commitsLimit = 10 - ) - { - return StreamGetBranches(streamId, branchesLimit, commitsLimit, CancellationToken.None); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task BranchCreate(CancellationToken cancellationToken, BranchCreateInput branchInput) - { - return BranchCreate(branchInput, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task BranchGet( - CancellationToken cancellationToken, - string streamId, - string branchName, - int commitsLimit = 10 - ) - { - return BranchGet(streamId, branchName, commitsLimit, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task BranchUpdate(CancellationToken cancellationToken, BranchUpdateInput branchInput) - { - return BranchUpdate(branchInput, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task BranchDelete(CancellationToken cancellationToken, BranchDeleteInput branchInput) - { - return BranchDelete(branchInput, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task StreamGetComments( - CancellationToken cancellationToken, - string streamId, - int limit = 25, - string? cursor = null - ) - { - return StreamGetComments(streamId, limit, cursor, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task StreamGetCommentScreenshot(CancellationToken cancellationToken, string id, string streamId) - { - return StreamGetCommentScreenshot(id, streamId, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task CommitGet(CancellationToken cancellationToken, string streamId, string commitId) - { - return CommitGet(streamId, commitId, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task> StreamGetCommits(CancellationToken cancellationToken, string streamId, int limit = 10) - { - return StreamGetCommits(streamId, limit, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task CommitCreate(CancellationToken cancellationToken, CommitCreateInput commitInput) - { - return CommitCreate(commitInput, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task CommitUpdate(CancellationToken cancellationToken, CommitUpdateInput commitInput) - { - return CommitUpdate(commitInput, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task CommitDelete(CancellationToken cancellationToken, CommitDeleteInput commitInput) - { - return CommitDelete(commitInput, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task CommitReceived(CancellationToken cancellationToken, CommitReceivedInput commitReceivedInput) - { - return CommitReceived(commitReceivedInput, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task ObjectGet(CancellationToken cancellationToken, string streamId, string objectId) - { - return ObjectGet(streamId, objectId, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task ObjectCountGet(CancellationToken cancellationToken, string streamId, string objectId) - { - return ObjectCountGet(streamId, objectId, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task StreamGet(CancellationToken cancellationToken, string id, int branchesLimit = 10) - { - return StreamGet(id, branchesLimit, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task> StreamsGet(CancellationToken cancellationToken, int limit = 10) - { - return StreamsGet(limit, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task> FavoriteStreamsGet(CancellationToken cancellationToken, int limit = 10) - { - return FavoriteStreamsGet(limit, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task> StreamSearch(CancellationToken cancellationToken, string query, int limit = 10) - { - return StreamSearch(query, limit, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task StreamCreate(CancellationToken cancellationToken, StreamCreateInput streamInput) - { - return StreamCreate(streamInput, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task StreamUpdate(CancellationToken cancellationToken, StreamUpdateInput streamInput) - { - return StreamUpdate(streamInput, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task StreamDelete(CancellationToken cancellationToken, string id) - { - return StreamDelete(id, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task StreamRevokePermission( - CancellationToken cancellationToken, - StreamRevokePermissionInput permissionInput - ) - { - return StreamRevokePermission(permissionInput, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task StreamGetPendingCollaborators(CancellationToken cancellationToken, string id) - { - return StreamGetPendingCollaborators(id, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task StreamInviteCreate(CancellationToken cancellationToken, StreamInviteCreateInput inviteCreateInput) - { - return StreamInviteCreate(inviteCreateInput, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task OtherUserGet(CancellationToken cancellationToken, string id) - { - return OtherUserGet(id, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task> UserSearch(CancellationToken cancellationToken, string query, int limit = 10) - { - return UserSearch(query, limit, cancellationToken); - } - #endregion -} diff --git a/Core/Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.UserOperations.cs b/Core/Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.UserOperations.cs deleted file mode 100644 index a79764e551..0000000000 --- a/Core/Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.UserOperations.cs +++ /dev/null @@ -1,99 +0,0 @@ -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using GraphQL; - -namespace Speckle.Core.Api; - -public partial class Client -{ - /// - /// Gets the currently active user profile. - /// - /// - /// - public async Task ActiveUserGet(CancellationToken cancellationToken = default) - { - var request = new GraphQLRequest - { - Query = - @"query User { - activeUser { - id, - email, - name, - bio, - company, - avatar, - verified, - profiles, - role, - } - }" - }; - return (await ExecuteGraphQLRequest(request, cancellationToken).ConfigureAwait(false)).activeUser; - } - - /// - /// Get another user's profile by its user id. - /// - /// Id of the user you are looking for - /// - /// - public async Task OtherUserGet(string id, CancellationToken cancellationToken = default) - { - var request = new GraphQLRequest - { - Query = - @"query LimitedUser($id: String!) { - otherUser(id: $id){ - id, - name, - bio, - company, - avatar, - verified, - role, - } - }", - Variables = new { id } - }; - return (await ExecuteGraphQLRequest(request, cancellationToken).ConfigureAwait(false)).otherUser; - } - - /// - /// Searches for a user on the server. - /// - /// String to search for. Must be at least 3 characters - /// Max number of users to return - /// - public async Task> UserSearch( - string query, - int limit = 10, - CancellationToken cancellationToken = default - ) - { - var request = new GraphQLRequest - { - Query = - @"query UserSearch($query: String!, $limit: Int!) { - userSearch(query: $query, limit: $limit) { - cursor, - items { - id - name - bio - company - avatar - verified - role - } - } - }", - Variables = new { query, limit } - }; - return (await ExecuteGraphQLRequest(request, cancellationToken).ConfigureAwait(false)) - .userSearch - .items; - } -} diff --git a/Core/Core/Api/GraphQL/Client.cs b/Core/Core/Api/GraphQL/Client.cs index bacfa63bc0..70384a6d12 100644 --- a/Core/Core/Api/GraphQL/Client.cs +++ b/Core/Core/Api/GraphQL/Client.cs @@ -15,6 +15,9 @@ using Serilog.Context; using Serilog.Core; using Serilog.Core.Enrichers; +using Serilog.Events; +using Speckle.Core.Api.GraphQL; +using Speckle.Core.Api.GraphQL.Resources; using Speckle.Core.Api.GraphQL.Serializer; using Speckle.Core.Credentials; using Speckle.Core.Helpers; @@ -23,71 +26,55 @@ namespace Speckle.Core.Api; -public sealed partial class Client : IDisposable +public sealed partial class Client : ISpeckleGraphQLClient, IDisposable { - [Obsolete] - internal Client() { } + public ProjectResource Project { get; } + public ModelResource Model { get; } + public VersionResource Version { get; } + public ActiveUserResource ActiveUser { get; } + public OtherUserResource OtherUser { get; } + public ProjectInviteResource ProjectInvite { get; } + public CommentResource Comment { get; } + public SubscriptionResource Subscription { get; } - public Client(Account account) - { - Account = account ?? throw new SpeckleException("Provided account is null."); - - HttpClient = Http.GetHttpProxyClient(null, TimeSpan.FromSeconds(30)); - Http.AddAuthHeader(HttpClient, account.token); + public string ServerUrl => Account.serverInfo.url; - HttpClient.DefaultRequestHeaders.Add("apollographql-client-name", Setup.HostApplication); - HttpClient.DefaultRequestHeaders.Add( - "apollographql-client-version", - Assembly.GetExecutingAssembly().GetName().Version.ToString() - ); + public string ApiToken => Account.token; - GQLClient = new GraphQLHttpClient( - new GraphQLHttpClientOptions - { - EndPoint = new Uri(new Uri(account.serverInfo.url), "/graphql"), - UseWebSocketForQueriesAndMutations = false, - WebSocketProtocol = "graphql-ws", - ConfigureWebSocketConnectionInitPayload = _ => - { - return Http.CanAddAuth(account.token, out string? authValue) ? new { Authorization = authValue } : null; - }, - }, - new NewtonsoftJsonSerializer(), - HttpClient - ); + public System.Version? ServerVersion { get; private set; } - GQLClient.WebSocketReceiveErrors.Subscribe(e => - { - if (e is WebSocketException we) - { - Console.WriteLine( - $"WebSocketException: {we.Message} (WebSocketError {we.WebSocketErrorCode}, ErrorCode {we.ErrorCode}, NativeErrorCode {we.NativeErrorCode}" - ); - } - else - { - Console.WriteLine($"Exception in websocket receive stream: {e}"); - } - }); - } + [JsonIgnore] + public Account Account { get; } - public string ServerUrl => Account.serverInfo.url; + private HttpClient HttpClient { get; } - public string ApiToken => Account.token; + public GraphQLHttpClient GQLClient { get; } - public System.Version? ServerVersion { get; set; } + /// + /// was null + public Client(Account account) + { + Account = account ?? throw new ArgumentException("Provided account is null."); - [JsonIgnore] - public Account Account { get; set; } + Project = new(this); + Model = new(this); + Version = new(this); + ActiveUser = new(this); + OtherUser = new(this); + ProjectInvite = new(this); + Comment = new(this); + Subscription = new(this); - private HttpClient HttpClient { get; set; } + HttpClient = CreateHttpClient(account); - public GraphQLHttpClient GQLClient { get; set; } + GQLClient = CreateGraphQLClient(account, HttpClient); + } public void Dispose() { try { + Subscription.Dispose(); UserStreamAddedSubscription?.Dispose(); UserStreamRemovedSubscription?.Dispose(); StreamUpdatedSubscription?.Dispose(); @@ -105,53 +92,36 @@ public void Dispose() internal async Task ExecuteWithResiliencePolicies(Func> func) { - // TODO: handle these in the HttpClient factory with a custom RequestHandler class - // 408 Request Timeout - // 425 Too Early - // 429 Too Many Requests - // 500 Internal Server Error - // 502 Bad Gateway - // 503 Service Unavailable - // 504 Gateway Timeout - var delay = Backoff.DecorrelatedJitterBackoffV2(TimeSpan.FromSeconds(1), 5); var graphqlRetry = Policy - .Handle>() + .Handle() .WaitAndRetryAsync( delay, - (ex, timeout, context) => + (ex, timeout, _) => { - var graphqlEx = (SpeckleGraphQLException)ex; - SpeckleLog.Logger - .ForContext("graphqlExtensions", graphqlEx.Extensions) - .ForContext("graphqlErrorMessages", graphqlEx.ErrorMessages) - .Warning( - ex, - "The previous attempt at executing function to get {resultType} failed with {exceptionMessage}. Retrying after {timeout}.", - typeof(T).Name, - ex.Message, - timeout - ); + SpeckleLog.Logger.Debug( + ex, + "The previous attempt at executing function to get {resultType} failed with {exceptionMessage}. Retrying after {timeout}", + typeof(T).Name, + ex.Message, + timeout + ); } ); return await graphqlRetry.ExecuteAsync(func).ConfigureAwait(false); } - /// "FORBIDDEN" on "UNAUTHORIZED" response from server - /// All other request errors - /// The requested a cancel + /// public async Task ExecuteGraphQLRequest(GraphQLRequest request, CancellationToken cancellationToken = default) { using IDisposable context0 = LogContext.Push(CreateEnrichers(request)); + var timer = Stopwatch.StartNew(); - SpeckleLog.Logger.Debug("Starting execution of graphql request to get {resultType}", typeof(T).Name); - var timer = new Stopwatch(); - var success = false; - timer.Start(); + Exception? exception = null; try { - var result = await ExecuteWithResiliencePolicies(async () => + return await ExecuteWithResiliencePolicies(async () => { GraphQLResponse result = await GQLClient .SendMutationAsync(request, cancellationToken) @@ -160,58 +130,28 @@ public async Task ExecuteGraphQLRequest(GraphQLRequest request, Cancellati return result.Data; }) .ConfigureAwait(false); - success = true; - return result; - } - // cancellations are bubbling up with no logging - catch (OperationCanceledException) - { - throw; } - // we catch forbidden to rethrow, making sure its not logged. - catch (SpeckleGraphQLForbiddenException) + catch (Exception ex) { + exception = ex; throw; } - // anything else related to graphql gets logged - catch (SpeckleGraphQLException gqlException) - { - SpeckleLog.Logger - .ForContext("graphqlResponse", gqlException.Response) - .ForContext("graphqlExtensions", gqlException.Extensions) - .ForContext("graphqlErrorMessages", gqlException.ErrorMessages.ToList()) - .Warning( - gqlException, - "Execution of the graphql request to get {resultType} failed with {graphqlExceptionType} {exceptionMessage}.", - typeof(T).Name, - gqlException.GetType().Name, - gqlException.Message - ); - throw; - } - // we log and wrap anything that is not a graphql exception. - // this makes sure, that any graphql operation only throws SpeckleGraphQLExceptions - catch (Exception ex) when (!ex.IsFatal()) - { - SpeckleLog.Logger.Warning( - ex, - "Execution of the graphql request to get {resultType} failed without a graphql response. Cause {exceptionMessage}", - typeof(T).Name, - ex.Message - ); - throw new SpeckleGraphQLException("The graphql request failed without a graphql response", ex, request, null); - } finally { - // this is a performance metric log operation - // this makes sure that both success and failed operations report - // the same performance log - timer.Stop(); - var status = success ? "succeeded" : "failed"; - SpeckleLog.Logger.Information( - "Execution of graphql request to get {resultType} {resultStatus} after {elapsed} seconds", + LogEventLevel logLevel = exception switch + { + null => LogEventLevel.Information, + OperationCanceledException + => cancellationToken.IsCancellationRequested ? LogEventLevel.Debug : LogEventLevel.Error, + SpeckleException => LogEventLevel.Warning, + _ => LogEventLevel.Error, + }; + SpeckleLog.Logger.Write( + logLevel, + exception, + "Execution of the graphql request to get {resultType} completed with success:{status} after {elapsed} seconds", typeof(T).Name, - status, + exception is null, timer.Elapsed.TotalSeconds ); } @@ -236,7 +176,7 @@ internal void MaybeThrowFromGraphQLErrors(GraphQLRequest request, GraphQLResp ) ) { - throw new SpeckleGraphQLForbiddenException(request, response); + throw new SpeckleGraphQLForbiddenException(request, response); } if ( @@ -246,7 +186,7 @@ internal void MaybeThrowFromGraphQLErrors(GraphQLRequest request, GraphQLResp ) ) { - throw new SpeckleGraphQLStreamNotFoundException(request, response); + throw new SpeckleGraphQLStreamNotFoundException(request, response); } if ( @@ -257,7 +197,7 @@ internal void MaybeThrowFromGraphQLErrors(GraphQLRequest request, GraphQLResp ) ) { - throw new SpeckleGraphQLInternalErrorException(request, response); + throw new SpeckleGraphQLInternalErrorException(request, response); } throw new SpeckleGraphQLException("Request failed with errors", request, response); @@ -299,6 +239,10 @@ private ILogEventEnricher[] CreateEnrichers(GraphQLRequest request) }; } + IDisposable ISpeckleGraphQLClient.SubscribeTo(GraphQLRequest request, Action callback) => + SubscribeTo(request, callback); + + /// internal IDisposable SubscribeTo(GraphQLRequest request, Action callback) { using (LogContext.Push(CreateEnrichers(request))) @@ -325,7 +269,7 @@ internal IDisposable SubscribeTo(GraphQLRequest request, Action ca } } // we catch forbidden to rethrow, making sure its not logged. - catch (SpeckleGraphQLForbiddenException) + catch (SpeckleGraphQLForbiddenException) { throw; } @@ -356,7 +300,7 @@ internal IDisposable SubscribeTo(GraphQLRequest request, Action ca // so far we've swallowed these errors SpeckleLog.Logger.Error( ex, - "Subscription request for {resultType} failed with {exceptionMessage}", + "Subscription for {resultType} terminated unexpectedly with {exceptionMessage}", typeof(T).Name, ex.Message ); @@ -375,11 +319,57 @@ internal IDisposable SubscribeTo(GraphQLRequest request, Action ca ); throw new SpeckleGraphQLException( "The graphql request failed without a graphql response", - ex, request, - null + null, + ex ); } } } + + private static GraphQLHttpClient CreateGraphQLClient(Account account, HttpClient httpClient) + { + var gQLClient = new GraphQLHttpClient( + new GraphQLHttpClientOptions + { + EndPoint = new Uri(new Uri(account.serverInfo.url), "/graphql"), + UseWebSocketForQueriesAndMutations = false, + WebSocketProtocol = "graphql-ws", + ConfigureWebSocketConnectionInitPayload = _ => + { + return Http.CanAddAuth(account.token, out string? authValue) ? new { Authorization = authValue } : null; + }, + }, + new NewtonsoftJsonSerializer(), + httpClient + ); + + gQLClient.WebSocketReceiveErrors.Subscribe(e => + { + if (e is WebSocketException we) + { + Console.WriteLine( + $"WebSocketException: {we.Message} (WebSocketError {we.WebSocketErrorCode}, ErrorCode {we.ErrorCode}, NativeErrorCode {we.NativeErrorCode}" + ); + } + else + { + Console.WriteLine($"Exception in websocket receive stream: {e}"); + } + }); + return gQLClient; + } + + private static HttpClient CreateHttpClient(Account account) + { + var httpClient = Http.GetHttpProxyClient(null, TimeSpan.FromSeconds(30)); + Http.AddAuthHeader(httpClient, account.token); + + httpClient.DefaultRequestHeaders.Add("apollographql-client-name", Setup.HostApplication); + httpClient.DefaultRequestHeaders.Add( + "apollographql-client-version", + Assembly.GetExecutingAssembly().GetName().Version.ToString() + ); + return httpClient; + } } diff --git a/Core/Core/Api/GraphQL/Enums/FileUploadConversionStatus.cs b/Core/Core/Api/GraphQL/Enums/FileUploadConversionStatus.cs new file mode 100644 index 0000000000..4ab9e751bc --- /dev/null +++ b/Core/Core/Api/GraphQL/Enums/FileUploadConversionStatus.cs @@ -0,0 +1,10 @@ +namespace Speckle.Core.Api.GraphQL.Enums; + +//This enum isn't explicitly defined in the schema, instead its usages are int typed (But represent an enum) +public enum FileUploadConversionStatus +{ + Queued = 0, + Processing = 1, + Success = 2, + Error = 3, +} diff --git a/Core/Core/Api/GraphQL/Enums/ProjectCommentsUpdatedMessageType.cs b/Core/Core/Api/GraphQL/Enums/ProjectCommentsUpdatedMessageType.cs new file mode 100644 index 0000000000..2f11301dec --- /dev/null +++ b/Core/Core/Api/GraphQL/Enums/ProjectCommentsUpdatedMessageType.cs @@ -0,0 +1,8 @@ +namespace Speckle.Core.Api.GraphQL.Enums; + +public enum ProjectCommentsUpdatedMessageType +{ + ARCHIVED, + CREATED, + UPDATED, +} diff --git a/Core/Core/Api/GraphQL/Enums/ProjectFileImportUpdatedMessageType.cs b/Core/Core/Api/GraphQL/Enums/ProjectFileImportUpdatedMessageType.cs new file mode 100644 index 0000000000..7cb7d933b9 --- /dev/null +++ b/Core/Core/Api/GraphQL/Enums/ProjectFileImportUpdatedMessageType.cs @@ -0,0 +1,7 @@ +namespace Speckle.Core.Api.GraphQL.Enums; + +public enum ProjectFileImportUpdatedMessageType +{ + CREATED, + UPDATED, +} diff --git a/Core/Core/Api/GraphQL/Enums/ProjectModelsUpdatedMessageType.cs b/Core/Core/Api/GraphQL/Enums/ProjectModelsUpdatedMessageType.cs new file mode 100644 index 0000000000..1416691fa0 --- /dev/null +++ b/Core/Core/Api/GraphQL/Enums/ProjectModelsUpdatedMessageType.cs @@ -0,0 +1,8 @@ +namespace Speckle.Core.Api.GraphQL.Enums; + +public enum ProjectModelsUpdatedMessageType +{ + CREATED, + DELETED, + UPDATED, +} diff --git a/Core/Core/Api/GraphQL/Enums/ProjectPendingModelsUpdatedMessageType.cs b/Core/Core/Api/GraphQL/Enums/ProjectPendingModelsUpdatedMessageType.cs new file mode 100644 index 0000000000..42ac2bebf1 --- /dev/null +++ b/Core/Core/Api/GraphQL/Enums/ProjectPendingModelsUpdatedMessageType.cs @@ -0,0 +1,7 @@ +namespace Speckle.Core.Api.GraphQL.Enums; + +public enum ProjectPendingModelsUpdatedMessageType +{ + CREATED, + UPDATED, +} diff --git a/Core/Core/Api/GraphQL/Enums/ProjectUpdatedMessageType.cs b/Core/Core/Api/GraphQL/Enums/ProjectUpdatedMessageType.cs new file mode 100644 index 0000000000..3eccdd5a04 --- /dev/null +++ b/Core/Core/Api/GraphQL/Enums/ProjectUpdatedMessageType.cs @@ -0,0 +1,7 @@ +namespace Speckle.Core.Api.GraphQL.Enums; + +public enum ProjectUpdatedMessageType +{ + DELETED, + UPDATED, +} diff --git a/Core/Core/Api/GraphQL/Enums/ProjectVersionsUpdatedMessageType.cs b/Core/Core/Api/GraphQL/Enums/ProjectVersionsUpdatedMessageType.cs new file mode 100644 index 0000000000..14e1f7008e --- /dev/null +++ b/Core/Core/Api/GraphQL/Enums/ProjectVersionsUpdatedMessageType.cs @@ -0,0 +1,8 @@ +namespace Speckle.Core.Api.GraphQL.Enums; + +public enum ProjectVersionsUpdatedMessageType +{ + CREATED, + DELETED, + UPDATED, +} diff --git a/Core/Core/Api/GraphQL/Enums/ProjectVisibility.cs b/Core/Core/Api/GraphQL/Enums/ProjectVisibility.cs new file mode 100644 index 0000000000..9a62fff999 --- /dev/null +++ b/Core/Core/Api/GraphQL/Enums/ProjectVisibility.cs @@ -0,0 +1,8 @@ +namespace Speckle.Core.Api.GraphQL.Enums; + +public enum ProjectVisibility +{ + Private, + Public, + Unlisted +} diff --git a/Core/Core/Api/GraphQL/Enums/ResourceType.cs b/Core/Core/Api/GraphQL/Enums/ResourceType.cs new file mode 100644 index 0000000000..2fa31c46f1 --- /dev/null +++ b/Core/Core/Api/GraphQL/Enums/ResourceType.cs @@ -0,0 +1,9 @@ +namespace Speckle.Core.Api.GraphQL.Enums; + +public enum ResourceType +{ + commit, + stream, + @object, + comment +} diff --git a/Core/Core/Api/GraphQL/Enums/UserProjectsUpdatedMessageType.cs b/Core/Core/Api/GraphQL/Enums/UserProjectsUpdatedMessageType.cs new file mode 100644 index 0000000000..5225929afe --- /dev/null +++ b/Core/Core/Api/GraphQL/Enums/UserProjectsUpdatedMessageType.cs @@ -0,0 +1,7 @@ +namespace Speckle.Core.Api.GraphQL.Enums; + +public enum UserProjectsUpdatedMessageType +{ + ADDED, + REMOVED, +} diff --git a/Core/Core/Api/GraphQL/GraphQLHttpClientExtensions.cs b/Core/Core/Api/GraphQL/GraphQLHttpClientExtensions.cs index 64b7a76645..0e7244e98b 100644 --- a/Core/Core/Api/GraphQL/GraphQLHttpClientExtensions.cs +++ b/Core/Core/Api/GraphQL/GraphQLHttpClientExtensions.cs @@ -3,6 +3,8 @@ using System.Threading; using GraphQL.Client.Http; using System.Linq; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Models.Responses; namespace Speckle.Core.Api.GraphQL; @@ -13,7 +15,7 @@ public static class GraphQLHttpClientExtensions /// /// [Optional] defaults to an empty cancellation token /// object excluding any strings (eg "2.7.2-alpha.6995" becomes "2.7.2.6995") - /// + /// public static async Task GetServerVersion( this GraphQLHttpClient client, CancellationToken cancellationToken = default diff --git a/Core/Core/Api/GraphQL/ISpeckleGraphQLClient.cs b/Core/Core/Api/GraphQL/ISpeckleGraphQLClient.cs new file mode 100644 index 0000000000..5b6a372095 --- /dev/null +++ b/Core/Core/Api/GraphQL/ISpeckleGraphQLClient.cs @@ -0,0 +1,20 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using GraphQL; + +namespace Speckle.Core.Api.GraphQL; + +internal interface ISpeckleGraphQLClient +{ + /// "FORBIDDEN" on "UNAUTHORIZED" response from server + /// All other request errors + /// The requested a cancel + /// This already been disposed + internal Task ExecuteGraphQLRequest(GraphQLRequest request, CancellationToken cancellationToken); + + /// "FORBIDDEN" on "UNAUTHORIZED" response from server + /// All other request errors + /// This already been disposed + internal IDisposable SubscribeTo(GraphQLRequest request, Action callback); +} diff --git a/Core/Core/Api/GraphQL/Inputs/CommentInputs.cs b/Core/Core/Api/GraphQL/Inputs/CommentInputs.cs new file mode 100644 index 0000000000..df810adeb6 --- /dev/null +++ b/Core/Core/Api/GraphQL/Inputs/CommentInputs.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; + +namespace Speckle.Core.Api.GraphQL.Inputs; + +public sealed record CreateCommentInput( + CommentContentInput content, + string projectId, + string resourceIdString, + string? screenshot, + object? viewerState +); + +public sealed record EditCommentInput(CommentContentInput content, string commentId); + +public sealed record CreateCommentReplyInput(CommentContentInput content, string threadId); + +public sealed record CommentContentInput(IReadOnlyCollection? blobIds, object? doc); diff --git a/Core/Core/Api/GraphQL/Inputs/ModelInputs.cs b/Core/Core/Api/GraphQL/Inputs/ModelInputs.cs new file mode 100644 index 0000000000..817df64cab --- /dev/null +++ b/Core/Core/Api/GraphQL/Inputs/ModelInputs.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace Speckle.Core.Api.GraphQL.Inputs; + +public sealed record CreateModelInput(string name, string? description, string projectId); + +public sealed record DeleteModelInput(string id, string projectId); + +public sealed record UpdateModelInput(string id, string? name, string? description, string projectId); + +public sealed record ModelVersionsFilter(IReadOnlyList priorityIds, bool? priorityIdsOnly); diff --git a/Core/Core/Api/GraphQL/Inputs/ProjectInputs.cs b/Core/Core/Api/GraphQL/Inputs/ProjectInputs.cs new file mode 100644 index 0000000000..568d0093aa --- /dev/null +++ b/Core/Core/Api/GraphQL/Inputs/ProjectInputs.cs @@ -0,0 +1,39 @@ +using System.Collections.Generic; +using Speckle.Core.Api.GraphQL.Enums; + +namespace Speckle.Core.Api.GraphQL.Inputs; + +public sealed record ProjectCommentsFilter(bool? includeArchived, bool? loadedVersionsOnly, string? resourceIdString); + +public sealed record ProjectCreateInput(string? name, string? description, ProjectVisibility? visibility); + +public sealed record ProjectInviteCreateInput(string? email, string? role, string? serverRole, string? userId); + +public sealed record ProjectInviteUseInput(bool accept, string projectId, string token); + +public sealed record ProjectModelsFilter( + IReadOnlyList? contributors, + IReadOnlyList? excludeIds, + IReadOnlyList? ids, + bool? onlyWithVersions, + string? search, + IReadOnlyList sourceApps +); + +public sealed record ProjectModelsTreeFilter( + IReadOnlyList? contributors, + string? search, + IReadOnlyList? sourceApps +); + +public sealed record ProjectUpdateInput( + string id, + string? name = null, + string? description = null, + bool? allowPublicComments = null, + ProjectVisibility? visibility = null +); + +public sealed record ProjectUpdateRoleInput(string userId, string projectId, string? role); + +public sealed record UserProjectsFilter(string search, IReadOnlyList? onlyWithRoles = null); diff --git a/Core/Core/Api/GraphQL/Inputs/SubscriptionInputs.cs b/Core/Core/Api/GraphQL/Inputs/SubscriptionInputs.cs new file mode 100644 index 0000000000..86688a2864 --- /dev/null +++ b/Core/Core/Api/GraphQL/Inputs/SubscriptionInputs.cs @@ -0,0 +1,7 @@ +namespace Speckle.Core.Api.GraphQL.Inputs; + +public sealed record ViewerUpdateTrackingTarget( + string projectId, + string resourceIdString, + bool? loadedVersionsOnly = null +); diff --git a/Core/Core/Api/GraphQL/Inputs/VersionInputs.cs b/Core/Core/Api/GraphQL/Inputs/VersionInputs.cs new file mode 100644 index 0000000000..5bbee6e791 --- /dev/null +++ b/Core/Core/Api/GraphQL/Inputs/VersionInputs.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace Speckle.Core.Api.GraphQL.Inputs; + +public sealed record UpdateVersionInput(string versionId, string? message); + +public sealed record MoveVersionsInput(string targetModelName, IReadOnlyList versionIds); + +public sealed record DeleteVersionsInput(IReadOnlyList versionIds); diff --git a/Core/Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.ActivityOperations.cs b/Core/Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.ActivityOperations.cs similarity index 96% rename from Core/Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.ActivityOperations.cs rename to Core/Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.ActivityOperations.cs index 34250ecb8a..df43029474 100644 --- a/Core/Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.ActivityOperations.cs +++ b/Core/Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.ActivityOperations.cs @@ -8,6 +8,7 @@ namespace Speckle.Core.Api; public partial class Client { + //TODO: API gap /// /// Gets the activity of a stream /// @@ -25,7 +26,7 @@ public async Task> StreamGetActivity( DateTime? before = null, DateTime? cursor = null, string actionType = "", - int limit = 25, + int limit = ServerLimits.DEFAULT_PAGINATION_REQUEST, CancellationToken cancellationToken = default ) { diff --git a/Core/Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.BranchOperations.cs b/Core/Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.BranchOperations.cs similarity index 86% rename from Core/Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.BranchOperations.cs rename to Core/Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.BranchOperations.cs index 1c3a67b181..456098e093 100644 --- a/Core/Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.BranchOperations.cs +++ b/Core/Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.BranchOperations.cs @@ -1,7 +1,9 @@ +using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using GraphQL; +using Speckle.Core.Api.GraphQL.Resources; namespace Speckle.Core.Api; @@ -14,6 +16,9 @@ public partial class Client /// Id of the stream to get the branches from /// Max number of commits to retrieve /// + /// + /// + [Obsolete($"Use client.{nameof(Model)}.{nameof(ModelResource.GetModels)}")] public async Task> StreamGetBranchesWithLimitRetry(string streamId, int commitsLimit = 10) { List branches; @@ -38,6 +43,9 @@ public async Task> StreamGetBranchesWithLimitRetry(string streamId, /// Max number of commits to retrieve /// /// + /// + /// + [Obsolete($"Use client.{nameof(Model)}.{nameof(ModelResource.GetModels)}")] public async Task> StreamGetBranches( string streamId, int branchesLimit = 10, @@ -86,6 +94,8 @@ public async Task> StreamGetBranches( /// /// /// The branch id. + /// + [Obsolete($"Use client.{nameof(Model)}.{nameof(ModelResource.Create)}")] public async Task BranchCreate(BranchCreateInput branchInput, CancellationToken cancellationToken = default) { var request = new GraphQLRequest @@ -105,6 +115,10 @@ public async Task BranchCreate(BranchCreateInput branchInput, Cancellati /// Name of the branch to get /// /// The requested branch + /// Updated to Model.GetWithVersions + /// + /// + [Obsolete($"Use client.{nameof(Model)}.{nameof(ModelResource.Get)}")] public async Task BranchGet( string streamId, string branchName, @@ -154,6 +168,8 @@ public async Task BranchGet( /// Id of the project to get the model from /// Id of the model /// + /// + [Obsolete($"Use client.{nameof(Model)}.{nameof(ModelResource.Get)}")] public async Task ModelGet(string projectId, string modelId, CancellationToken cancellationToken = default) { var request = new GraphQLRequest @@ -190,6 +206,8 @@ public async Task ModelGet(string projectId, string modelId, Cancellatio /// /// /// The stream's id. + /// + [Obsolete($"Use client.{nameof(Model)}.{nameof(ModelResource.Update)}")] public async Task BranchUpdate(BranchUpdateInput branchInput, CancellationToken cancellationToken = default) { var request = new GraphQLRequest @@ -208,6 +226,8 @@ public async Task BranchUpdate(BranchUpdateInput branchInput, Cancellation /// /// /// + /// + [Obsolete($"Use client.{nameof(Model)}.{nameof(ModelResource.Delete)}")] public async Task BranchDelete(BranchDeleteInput branchInput, CancellationToken cancellationToken = default) { var request = new GraphQLRequest diff --git a/Core/Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.CommentOperations.cs b/Core/Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.CommentOperations.cs similarity index 89% rename from Core/Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.CommentOperations.cs rename to Core/Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.CommentOperations.cs index 20feaaf3be..aebce10af3 100644 --- a/Core/Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.CommentOperations.cs +++ b/Core/Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.CommentOperations.cs @@ -1,6 +1,8 @@ +using System; using System.Threading; using System.Threading.Tasks; using GraphQL; +using Speckle.Core.Api.GraphQL.Resources; namespace Speckle.Core.Api; @@ -14,6 +16,8 @@ public partial class Client /// Time to filter the comments with /// /// + /// + [Obsolete($"Use client.{nameof(CommentResource)}.{nameof(CommentResource.GetProjectComments)}")] public async Task StreamGetComments( string streamId, int limit = 25, @@ -78,6 +82,8 @@ public async Task StreamGetComments( /// Id of the stream to get the comment from /// /// + /// + [Obsolete($"Use client.{nameof(CommentResource)}.{nameof(CommentResource.GetProjectComments)}")] public async Task StreamGetCommentScreenshot( string id, string streamId, diff --git a/Core/Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.CommitOperations.cs b/Core/Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.CommitOperations.cs similarity index 85% rename from Core/Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.CommitOperations.cs rename to Core/Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.CommitOperations.cs index d76bc43037..d267b7623d 100644 --- a/Core/Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.CommitOperations.cs +++ b/Core/Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.CommitOperations.cs @@ -1,7 +1,9 @@ +using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using GraphQL; +using Speckle.Core.Api.GraphQL.Resources; namespace Speckle.Core.Api; @@ -14,6 +16,8 @@ public partial class Client /// Id of the commit to get /// /// + /// + [Obsolete($"Use client.{nameof(Version)}.{nameof(VersionResource.Get)}")] public async Task CommitGet(string streamId, string commitId, CancellationToken cancellationToken = default) { var request = new GraphQLRequest @@ -48,6 +52,8 @@ public async Task CommitGet(string streamId, string commitId, Cancellati /// Max number of commits to get /// /// The requested commits + /// + [Obsolete($"Use client.{nameof(Version)}.{nameof(VersionResource.GetVersions)}")] public async Task> StreamGetCommits( string streamId, int limit = 10, @@ -88,6 +94,8 @@ public async Task> StreamGetCommits( /// /// /// The commit id. + /// + [Obsolete($"Use client.{nameof(VersionResource)}.{nameof(VersionResource.Create)}")] public async Task CommitCreate(CommitCreateInput commitInput, CancellationToken cancellationToken = default) { var request = new GraphQLRequest @@ -106,6 +114,8 @@ public async Task CommitCreate(CommitCreateInput commitInput, Cancellati /// /// /// The stream's id. + /// + [Obsolete($"Use client.{nameof(VersionResource)}.{nameof(VersionResource.Update)}")] public async Task CommitUpdate(CommitUpdateInput commitInput, CancellationToken cancellationToken = default) { var request = new GraphQLRequest @@ -124,6 +134,8 @@ public async Task CommitUpdate(CommitUpdateInput commitInput, Cancellation /// /// /// + /// + [Obsolete($"Use client.{nameof(VersionResource)}.{nameof(VersionResource.Delete)}")] public async Task CommitDelete(CommitDeleteInput commitInput, CancellationToken cancellationToken = default) { var request = new GraphQLRequest @@ -143,6 +155,8 @@ public async Task CommitDelete(CommitDeleteInput commitInput, Cancellation /// /// /// + /// + [Obsolete($"Use client.{nameof(VersionResource)}.{nameof(VersionResource.Received)}")] public async Task CommitReceived( CommitReceivedInput commitReceivedInput, CancellationToken cancellationToken = default diff --git a/Core/Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.ObjectOperations.cs b/Core/Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.ObjectOperations.cs similarity index 99% rename from Core/Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.ObjectOperations.cs rename to Core/Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.ObjectOperations.cs index 79e9f8b90f..0f842bc5e6 100644 --- a/Core/Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.ObjectOperations.cs +++ b/Core/Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.ObjectOperations.cs @@ -6,6 +6,7 @@ namespace Speckle.Core.Api; public partial class Client { + //TODO: API Gap /// /// Gets data about the requested Speckle object from a stream. /// diff --git a/Core/Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.ServerOperations.cs b/Core/Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.ServerOperations.cs similarity index 96% rename from Core/Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.ServerOperations.cs rename to Core/Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.ServerOperations.cs index 2244822d73..1ceb07fab1 100644 --- a/Core/Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.ServerOperations.cs +++ b/Core/Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.ServerOperations.cs @@ -3,6 +3,7 @@ using System.Threading; using System.Threading.Tasks; using GraphQL; +using Speckle.Core.Api.GraphQL.Models.Responses; using Speckle.Core.Logging; namespace Speckle.Core.Api; diff --git a/Core/Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.StreamOperations.cs b/Core/Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.StreamOperations.cs similarity index 87% rename from Core/Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.StreamOperations.cs rename to Core/Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.StreamOperations.cs index f90a6849f3..dd7be354a3 100644 --- a/Core/Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.StreamOperations.cs +++ b/Core/Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.StreamOperations.cs @@ -4,6 +4,9 @@ using System.Threading; using System.Threading.Tasks; using GraphQL; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Models.Responses; +using Speckle.Core.Api.GraphQL.Resources; using Speckle.Core.Logging; namespace Speckle.Core.Api; @@ -34,11 +37,11 @@ public async Task IsStreamAccessible(string id, CancellationToken cancella return stream.id == id; } - catch (SpeckleGraphQLForbiddenException) + catch (SpeckleGraphQLForbiddenException) { return false; } - catch (SpeckleGraphQLStreamNotFoundException) + catch (SpeckleGraphQLStreamNotFoundException) { return false; } @@ -52,6 +55,9 @@ public async Task IsStreamAccessible(string id, CancellationToken cancella /// Max number of branches to retrieve /// /// + /// + /// + [Obsolete($"Use client.{nameof(Project)}.{nameof(ProjectResource.GetWithModels)}")] public async Task StreamGet(string id, int branchesLimit = 10, CancellationToken cancellationToken = default) { var request = new GraphQLRequest @@ -100,6 +106,8 @@ public async Task StreamGet(string id, int branchesLimit = 10, Cancellat /// Max number of streams to return /// /// + /// + [Obsolete($"Use client.{nameof(ActiveUser)}.{nameof(ActiveUserResource.GetProjects)}")] public async Task> StreamsGet(int limit = 10, CancellationToken cancellationToken = default) { var request = new GraphQLRequest @@ -142,7 +150,7 @@ public async Task> StreamsGet(int limit = 10, CancellationToken can }}" }; - var res = await ExecuteGraphQLRequest(request, cancellationToken).ConfigureAwait(false); + var res = await ExecuteGraphQLRequest(request, cancellationToken).ConfigureAwait(false); if (res?.activeUser == null) { @@ -154,6 +162,7 @@ public async Task> StreamsGet(int limit = 10, CancellationToken can return res.activeUser.streams.items; } + //TODO: API GAP /// /// Gets all favorite streams for the current user /// @@ -201,7 +210,7 @@ public async Task> FavoriteStreamsGet(int limit = 10, CancellationT }} }}" }; - return (await ExecuteGraphQLRequest(request, cancellationToken).ConfigureAwait(false)) + return (await ExecuteGraphQLRequest(request, cancellationToken).ConfigureAwait(false)) .activeUser .favoriteStreams .items; @@ -214,6 +223,8 @@ public async Task> FavoriteStreamsGet(int limit = 10, CancellationT /// Max number of streams to return /// /// + /// + [Obsolete($"Use client.{nameof(ActiveUser)}.{nameof(ActiveUserResource.GetProjects)}")] public async Task> StreamSearch( string query, int limit = 10, @@ -258,6 +269,8 @@ public async Task> StreamSearch( /// /// /// The stream's id. + /// + [Obsolete($"Use client.{nameof(Project)}.{nameof(ProjectResource.Create)}")] public async Task StreamCreate(StreamCreateInput streamInput, CancellationToken cancellationToken = default) { var request = new GraphQLRequest @@ -275,6 +288,8 @@ public async Task StreamCreate(StreamCreateInput streamInput, Cancellati /// Note: the id field needs to be a valid stream id. /// /// The stream's id. + /// + [Obsolete($"Use client.{nameof(Project)}.{nameof(ProjectResource.Update)}")] public async Task StreamUpdate(StreamUpdateInput streamInput, CancellationToken cancellationToken = default) { var request = new GraphQLRequest @@ -294,6 +309,8 @@ public async Task StreamUpdate(StreamUpdateInput streamInput, Cancellation /// Id of the stream to be deleted /// /// + /// + [Obsolete($"Use client.{nameof(Project)}.{nameof(ProjectResource.Delete)}")] public async Task StreamDelete(string id, CancellationToken cancellationToken = default) { var request = new GraphQLRequest @@ -336,6 +353,8 @@ public async Task StreamRevokePermission( /// /// /// + /// + [Obsolete($"Use client.{nameof(Project)}.{nameof(ProjectResource.UpdateRole)}")] public async Task StreamUpdatePermission( StreamPermissionInput updatePermissionInput, CancellationToken cancellationToken = default @@ -362,6 +381,8 @@ mutation streamUpdatePermission($permissionParams: StreamUpdatePermissionInput!) /// /// /// + /// + [Obsolete($"Use client.{nameof(Project)}.{nameof(ProjectResource.GetWithTeam)}")] public async Task StreamGetPendingCollaborators( string streamId, CancellationToken cancellationToken = default @@ -396,6 +417,8 @@ public async Task StreamGetPendingCollaborators( /// /// /// + /// + [Obsolete($"Use client.{nameof(ProjectInvite)}.{nameof(ProjectInviteResource.Create)}")] public async Task StreamInviteCreate( StreamInviteCreateInput inviteCreateInput, CancellationToken cancellationToken = default @@ -427,6 +450,8 @@ mutation streamInviteCreate($input: StreamInviteCreateInput!) { /// Id of the invite to cancel /// /// + /// + [Obsolete($"Use client.{nameof(ProjectInvite)}.{nameof(ProjectInviteResource.Cancel)}")] public async Task StreamInviteCancel( string streamId, string inviteId, @@ -456,6 +481,8 @@ mutation streamInviteCancel( $streamId: String!, $inviteId: String! ) { /// /// /// + /// + [Obsolete($"Use client.{nameof(ProjectInvite)}.{nameof(ProjectInviteResource.Use)}")] public async Task StreamInviteUse( string streamId, string token, @@ -482,6 +509,13 @@ mutation streamInviteUse( $accept: Boolean!, $streamId: String!, $token: String! return (bool)res["streamInviteUse"]; } + /// + /// + /// + /// + /// + /// + [Obsolete($"Use client.{nameof(ActiveUser)}.{nameof(ActiveUserResource.ProjectInvites)}")] public async Task> GetAllPendingInvites(CancellationToken cancellationToken = default) { var request = new GraphQLRequest diff --git a/Core/Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.UserOperations.cs b/Core/Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.UserOperations.cs new file mode 100644 index 0000000000..b1cbd1e762 --- /dev/null +++ b/Core/Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.UserOperations.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Resources; + +namespace Speckle.Core.Api; + +public partial class Client +{ + /// + /// Gets the currently active user profile. + /// + /// + /// + /// + [Obsolete($"Use client.{nameof(ActiveUser)}.{nameof(ActiveUserResource.Get)}")] + public async Task ActiveUserGet(CancellationToken cancellationToken = default) + { + return await ActiveUser.Get(cancellationToken).ConfigureAwait(false); + } + + /// + /// Get another user's profile by its user id. + /// + /// Id of the user you are looking for + /// + /// + /// + [Obsolete($"Use client.{nameof(OtherUser)}.{nameof(OtherUserResource.Get)}")] + public async Task OtherUserGet(string id, CancellationToken cancellationToken = default) + { + return await OtherUser.Get(id, cancellationToken).ConfigureAwait(false); + } + + /// + /// Searches for a user on the server. + /// + /// String to search for. Must be at least 3 characters + /// Max number of users to return + /// + /// + [Obsolete($"Use client.{nameof(OtherUser)}.{nameof(OtherUserResource.UserSearch)}")] + public async Task> UserSearch( + string query, + int limit = 10, + CancellationToken cancellationToken = default + ) + { + var res = await OtherUser.UserSearch(query, limit, cancellationToken: cancellationToken).ConfigureAwait(false); + return res.items; + } +} diff --git a/Core/Core/Api/GraphQL/Client.Subscriptions/Client.Subscriptions.Branch.cs b/Core/Core/Api/GraphQL/Legacy/Client.Subscriptions/Client.Subscriptions.Branch.cs similarity index 100% rename from Core/Core/Api/GraphQL/Client.Subscriptions/Client.Subscriptions.Branch.cs rename to Core/Core/Api/GraphQL/Legacy/Client.Subscriptions/Client.Subscriptions.Branch.cs diff --git a/Core/Core/Api/GraphQL/Client.Subscriptions/Client.Subscriptions.Commit.cs b/Core/Core/Api/GraphQL/Legacy/Client.Subscriptions/Client.Subscriptions.Commit.cs similarity index 100% rename from Core/Core/Api/GraphQL/Client.Subscriptions/Client.Subscriptions.Commit.cs rename to Core/Core/Api/GraphQL/Legacy/Client.Subscriptions/Client.Subscriptions.Commit.cs diff --git a/Core/Core/Api/GraphQL/Client.Subscriptions/Client.Subscriptions.Stream.cs b/Core/Core/Api/GraphQL/Legacy/Client.Subscriptions/Client.Subscriptions.Stream.cs similarity index 100% rename from Core/Core/Api/GraphQL/Client.Subscriptions/Client.Subscriptions.Stream.cs rename to Core/Core/Api/GraphQL/Legacy/Client.Subscriptions/Client.Subscriptions.Stream.cs diff --git a/Core/Core/Api/GraphQL/Models.cs b/Core/Core/Api/GraphQL/Legacy/LegacyGraphQLModels.cs similarity index 59% rename from Core/Core/Api/GraphQL/Models.cs rename to Core/Core/Api/GraphQL/Legacy/LegacyGraphQLModels.cs index e1edf79595..5dcff259f6 100644 --- a/Core/Core/Api/GraphQL/Models.cs +++ b/Core/Core/Api/GraphQL/Legacy/LegacyGraphQLModels.cs @@ -1,12 +1,20 @@ #nullable disable using System; using System.Collections.Generic; -using System.Text.Json.Serialization; +using Speckle.Core.Api.GraphQL.Enums; +using Speckle.Core.Api.GraphQL.Models; namespace Speckle.Core.Api; #region inputs +internal static class DeprecationMessages +{ + public const string FE2_DEPRECATION_MESSAGE = + $"Stream/Branch/Commit API is now deprecated, Use the new Project/Model/Version API functions in {nameof(Client)}"; +} + +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class StreamCreateInput { public string name { get; set; } @@ -14,6 +22,7 @@ public class StreamCreateInput public bool isPublic { get; set; } = true; } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class StreamUpdateInput { public string id { get; set; } @@ -22,6 +31,7 @@ public class StreamUpdateInput public bool isPublic { get; set; } = true; } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class StreamPermissionInput { public string streamId { get; set; } @@ -29,12 +39,14 @@ public class StreamPermissionInput public string role { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class StreamRevokePermissionInput { public string streamId { get; set; } public string userId { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class StreamInviteCreateInput { public string streamId { get; set; } @@ -44,6 +56,7 @@ public class StreamInviteCreateInput public string role { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class BranchCreateInput { public string streamId { get; set; } @@ -51,6 +64,7 @@ public class BranchCreateInput public string description { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class BranchUpdateInput { public string streamId { get; set; } @@ -59,6 +73,7 @@ public class BranchUpdateInput public string description { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class BranchDeleteInput { public string streamId { get; set; } @@ -79,6 +94,7 @@ public class CommitCreateInput public List previousCommitIds { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class CommitUpdateInput { public string streamId { get; set; } @@ -86,12 +102,14 @@ public class CommitUpdateInput public string message { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class CommitDeleteInput { public string streamId { get; set; } public string id { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class CommitReceivedInput { public string streamId { get; set; } @@ -102,6 +120,7 @@ public class CommitReceivedInput #endregion +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class Stream { public string id { get; set; } @@ -146,6 +165,7 @@ public override string ToString() } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class Collaborator { public string id { get; set; } @@ -159,24 +179,13 @@ public override string ToString() } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class StreamInvitesResponse { public List streamInvites { get; set; } } -public class PendingStreamCollaborator -{ - public string id { get; set; } - public string inviteId { get; set; } - public string streamId { get; set; } - public string streamName { get; set; } - public string title { get; set; } - public string role { get; set; } - public User invitedBy { get; set; } - public User user { get; set; } - public string token { get; set; } -} - +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class Branches { public int totalCount { get; set; } @@ -184,6 +193,7 @@ public class Branches public List items { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class Commits { public int totalCount { get; set; } @@ -191,6 +201,7 @@ public class Commits public List items { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class Commit { public string id { get; set; } @@ -246,6 +257,7 @@ public class InfoCommit public string branchName { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class SpeckleObject { public string id { get; set; } @@ -255,6 +267,7 @@ public class SpeckleObject public DateTime createdAt { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class Branch { public string id { get; set; } @@ -268,6 +281,7 @@ public override string ToString() } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class Streams { public int totalCount { get; set; } @@ -275,51 +289,14 @@ public class Streams public List items { get; set; } } -public class UserBase -{ - public string id { get; set; } - public string name { get; set; } - public string bio { get; set; } - public string company { get; set; } - public string avatar { get; set; } - public bool verified { get; set; } - public string role { get; set; } - public Streams streams { get; set; } -} - -public class LimitedUser : UserBase -{ - public override string ToString() - { - return $"Other user profile: ({name} | {id})"; - } -} - -public class User : UserBase -{ - public string email { get; set; } - public Streams favoriteStreams { get; set; } - - public override string ToString() - { - return $"User ({email} | {name} | {id})"; - } -} - +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class Resource { public string resourceId { get; set; } public ResourceType resourceType { get; set; } } -public enum ResourceType -{ - commit, - stream, - @object, - comment -} - +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class Location { public double x { get; set; } @@ -327,103 +304,33 @@ public class Location public double z { get; set; } } -public class UserData -{ - public User user { get; set; } -} - -/// -/// GraphQL DTO model for active user data -/// -public class ActiveUserData -{ - /// - /// User profile of the active user. - /// - public User activeUser { get; set; } -} - -/// -/// GraphQL DTO model for limited user data. Mostly referring to other user's profile. -/// -public class LimitedUserData -{ - /// - /// The limited user profile of another (non active user) - /// - public LimitedUser otherUser { get; set; } -} - +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class UserSearchData { public UserSearch userSearch { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class UserSearch { public string cursor { get; set; } public List items { get; set; } } -public class ServerInfoResponse -{ - // TODO: server and user models are duplicated here and in Core.Credentials.Responses - // a bit weird and unnecessary - shouldn't both Credentials and Api share the same models since they're - // all server models that should be consistent? am creating a new obj here as to not reference Credentials in - // this file but it should prob be refactored in the futrue - public ServerInfo serverInfo { get; set; } -} - -// TODO: prob remove and bring one level up and shared w Core.Credentials -public class ServerInfo -{ - public string name { get; set; } - public string company { get; set; } - public string version { get; set; } - public string adminContact { get; set; } - public string description { get; set; } - - /// - /// This field is not returned from the GQL API, - /// it should populated on construction from the response headers. - /// see - /// - public bool frontend2 { get; set; } - - /// - /// This field is not returned from the GQL API, - /// it should populated on construction. - /// see - /// - public string url { get; set; } - - public ServerMigration migration { get; set; } -} - -public class ServerMigration -{ - /// - /// New URI where this server is now deployed - /// - public Uri movedTo { get; set; } - - /// - /// Previous URI where this server used to be deployed - /// - public Uri movedFrom { get; set; } -} - +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class StreamData { public Stream stream { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class StreamsData { public Streams streams { get; set; } } #region comments +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class Comments { public int totalCount { get; set; } @@ -431,16 +338,18 @@ public class Comments public List items { get; set; } } -public class CommentData +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] +public sealed class CommentData { - public Comments comments { get; set; } - public List camPos { get; set; } - public object filters { get; set; } - public Location location { get; set; } - public object selection { get; set; } - public object sectionBox { get; set; } + public Comments comments { get; init; } + public List camPos { get; init; } + public object filters { get; init; } + public Location location { get; init; } + public object selection { get; init; } + public object sectionBox { get; init; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class CommentItem { public string id { get; set; } @@ -457,6 +366,7 @@ public class CommentItem public List resources { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class ContentContent { public string Type { get; set; } @@ -465,116 +375,28 @@ public class ContentContent public string Text { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class CommentsData { public Comments comments { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class CommentItemData { public CommentItem comment { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class CommentActivityMessage { public string type { get; set; } public CommentItem comment { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class CommentActivityResponse { public CommentActivityMessage commentActivity { get; set; } } #endregion - -#region manager api - -public class Connector -{ - public List Versions { get; set; } = new(); -} - -public class Version -{ - public Version(string number, string url, Os os = Os.Win, Architecture architecture = Architecture.Any) - { - Number = number; - Url = url; - Date = DateTime.Now; - Prerelease = Number.Contains("-"); - Os = os; - Architecture = architecture; - } - - public string Number { get; set; } - public string Url { get; set; } - public Os Os { get; set; } - public Architecture Architecture { get; set; } = Architecture.Any; - public DateTime Date { get; set; } - - [JsonIgnore] - public string DateTimeAgo => Helpers.TimeAgo(Date); - - public bool Prerelease { get; set; } -} - -/// -/// OS -/// NOTE: do not edit order and only append new items as they are serialized to ints -/// -public enum Os -{ - Win, //0 - OSX, //1 - Linux, //2 - Any //3 -} - -/// -/// Architecture -/// NOTE: do not edit order and only append new items as they are serialized to ints -/// -public enum Architecture -{ - Any, //0 - Arm, //1 - Intel //2 -} - -//GHOST API -public class Meta -{ - public Pagination pagination { get; set; } -} - -public class Pagination -{ - public int page { get; set; } - public string limit { get; set; } - public int pages { get; set; } - public int total { get; set; } - public object next { get; set; } - public object prev { get; set; } -} - -public class Tags -{ - public List tags { get; set; } - public Meta meta { get; set; } -} - -public class Tag -{ - public string id { get; set; } - public string name { get; set; } - public string slug { get; set; } - public string description { get; set; } - public string feature_image { get; set; } - public string visibility { get; set; } - public string codeinjection_head { get; set; } - public object codeinjection_foot { get; set; } - public object canonical_url { get; set; } - public string accent_color { get; set; } - public string url { get; set; } -} -#endregion diff --git a/Core/Core/Api/GraphQL/Legacy/Manager.cs b/Core/Core/Api/GraphQL/Legacy/Manager.cs new file mode 100644 index 0000000000..94c1585efb --- /dev/null +++ b/Core/Core/Api/GraphQL/Legacy/Manager.cs @@ -0,0 +1,98 @@ +#nullable disable +using System; +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace Speckle.Core.Api.GraphQL; + +#region manager api + +public class Connector +{ + public List Versions { get; set; } = new(); +} + +public class ConnectorVersion +{ + public ConnectorVersion(string number, string url, Os os = Os.Win, Architecture architecture = Architecture.Any) + { + Number = number; + Url = url; + Date = DateTime.Now; + Prerelease = Number.Contains("-"); + Os = os; + Architecture = architecture; + } + + public string Number { get; set; } + public string Url { get; set; } + public Os Os { get; set; } + public Architecture Architecture { get; set; } = Architecture.Any; + public DateTime Date { get; set; } + + [JsonIgnore] + public string DateTimeAgo => Helpers.TimeAgo(Date); + + public bool Prerelease { get; set; } +} + +/// +/// OS +/// NOTE: do not edit order and only append new items as they are serialized to ints +/// +public enum Os +{ + Win, //0 + OSX, //1 + Linux, //2 + Any //3 +} + +/// +/// Architecture +/// NOTE: do not edit order and only append new items as they are serialized to ints +/// +public enum Architecture +{ + Any, //0 + Arm, //1 + Intel //2 +} + +//GHOST API +public class Meta +{ + public Pagination pagination { get; set; } +} + +public class Pagination +{ + public int page { get; set; } + public string limit { get; set; } + public int pages { get; set; } + public int total { get; set; } + public object next { get; set; } + public object prev { get; set; } +} + +public class Tags +{ + public List tags { get; set; } + public Meta meta { get; set; } +} + +public class Tag +{ + public string id { get; set; } + public string name { get; set; } + public string slug { get; set; } + public string description { get; set; } + public string feature_image { get; set; } + public string visibility { get; set; } + public string codeinjection_head { get; set; } + public object codeinjection_foot { get; set; } + public object canonical_url { get; set; } + public string accent_color { get; set; } + public string url { get; set; } +} +#endregion diff --git a/Core/Core/Api/GraphQL/SubscriptionModels.cs b/Core/Core/Api/GraphQL/Legacy/SubscriptionModels.cs similarity index 74% rename from Core/Core/Api/GraphQL/SubscriptionModels.cs rename to Core/Core/Api/GraphQL/Legacy/SubscriptionModels.cs index f1b253b610..f330899f23 100644 --- a/Core/Core/Api/GraphQL/SubscriptionModels.cs +++ b/Core/Core/Api/GraphQL/Legacy/SubscriptionModels.cs @@ -5,6 +5,7 @@ namespace Speckle.Core.Api.SubscriptionModels; #region streams +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class StreamInfo { public string id { get; set; } @@ -13,16 +14,19 @@ public class StreamInfo public string sharedBy { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class UserStreamAddedResult { public StreamInfo userStreamAdded { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class StreamUpdatedResult { public StreamInfo streamUpdated { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class UserStreamRemovedResult { public StreamInfo userStreamRemoved { get; set; } @@ -31,6 +35,7 @@ public class UserStreamRemovedResult #region branches +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class BranchInfo { public string id { get; set; } @@ -40,16 +45,19 @@ public class BranchInfo public string authorId { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class BranchCreatedResult { public BranchInfo branchCreated { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class BranchUpdatedResult { public BranchInfo branchUpdated { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class BranchDeletedResult { public BranchInfo branchDeleted { get; set; } @@ -58,6 +66,7 @@ public class BranchDeletedResult #region commits +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class CommitInfo { public string id { get; set; } @@ -74,16 +83,19 @@ public class CommitInfo public IList previousCommitIds { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class CommitCreatedResult { public CommitInfo commitCreated { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class CommitUpdatedResult { public CommitInfo commitUpdated { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class CommitDeletedResult { public CommitInfo commitDeleted { get; set; } diff --git a/Core/Core/Api/GraphQL/Models/Collections.cs b/Core/Core/Api/GraphQL/Models/Collections.cs new file mode 100644 index 0000000000..3e4738aad0 --- /dev/null +++ b/Core/Core/Api/GraphQL/Models/Collections.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; + +namespace Speckle.Core.Api.GraphQL.Models; + +public class ResourceCollection +{ + public int totalCount { get; init; } + + public List items { get; init; } + + public string? cursor { get; init; } +} + +public sealed class CommentReplyAuthorCollection : ResourceCollection { } + +public sealed class ProjectCommentCollection : ResourceCollection +{ + public int totalArchivedCount { get; init; } +} diff --git a/Core/Core/Api/GraphQL/Models/Comment.cs b/Core/Core/Api/GraphQL/Models/Comment.cs new file mode 100644 index 0000000000..75da443dd3 --- /dev/null +++ b/Core/Core/Api/GraphQL/Models/Comment.cs @@ -0,0 +1,25 @@ +#nullable disable + +using System; +using System.Collections.Generic; + +namespace Speckle.Core.Api.GraphQL.Models; + +public sealed class Comment +{ + public bool archived { get; init; } + public LimitedUser author { get; init; } + public string authorId { get; init; } + public DateTime createdAt { get; init; } + public bool hasParent { get; init; } + public string id { get; init; } + public Comment parent { get; init; } + public string rawText { get; init; } + public ResourceCollection replies { get; init; } + public CommentReplyAuthorCollection replyAuthors { get; init; } + public List resources { get; init; } + public string screenshot { get; init; } + public DateTime updatedAt { get; init; } + public DateTime? viewedAt { get; init; } + public List viewerResources { get; init; } +} diff --git a/Core/Core/Api/GraphQL/Models/FileUpload.cs b/Core/Core/Api/GraphQL/Models/FileUpload.cs new file mode 100644 index 0000000000..8327e24817 --- /dev/null +++ b/Core/Core/Api/GraphQL/Models/FileUpload.cs @@ -0,0 +1,30 @@ +#nullable disable + +using System; +using Speckle.Core.Api.GraphQL.Enums; + +namespace Speckle.Core.Api.GraphQL.Models; + +public sealed class FileUpload +{ + public string convertedCommitId { get; init; } + public DateTime convertedLastUpdate { get; init; } + public FileUploadConversionStatus convertedStatus { get; init; } + public string convertedVersionId { get; init; } + public string fileName { get; init; } + public int fileSize { get; init; } + public string fileType { get; init; } + public string id { get; init; } + public Model model { get; init; } + public string modelName { get; init; } + public string projectId { get; init; } + public bool uploadComplete { get; init; } + public DateTime uploadDate { get; init; } + public string userId { get; init; } + + [Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] + public string branchName { get; init; } + + [Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] + public string streamId { get; init; } +} diff --git a/Core/Core/Api/GraphQL/Models/Model.cs b/Core/Core/Api/GraphQL/Models/Model.cs new file mode 100644 index 0000000000..3c779960fa --- /dev/null +++ b/Core/Core/Api/GraphQL/Models/Model.cs @@ -0,0 +1,23 @@ +#nullable disable +using System; + +using System.Collections.Generic; + +namespace Speckle.Core.Api.GraphQL.Models; + +public sealed class Model +{ + public LimitedUser author { get; init; } + public List childrenTree { get; init; } + public ResourceCollection commentThreads { get; init; } + public DateTime createdAt { get; init; } + public string description { get; init; } + public string displayName { get; init; } + public string id { get; init; } + public string name { get; init; } + public List pendingImportedVersions { get; init; } + public Uri previewUrl { get; init; } + public DateTime updatedAt { get; init; } + public ResourceCollection versions { get; init; } + public Version version { get; init; } +} diff --git a/Core/Core/Api/GraphQL/Models/ModelsTreeItem.cs b/Core/Core/Api/GraphQL/Models/ModelsTreeItem.cs new file mode 100644 index 0000000000..f0d6e49966 --- /dev/null +++ b/Core/Core/Api/GraphQL/Models/ModelsTreeItem.cs @@ -0,0 +1,17 @@ +#nullable disable + +using System; +using System.Collections.Generic; + +namespace Speckle.Core.Api.GraphQL.Models; + +public sealed class ModelsTreeItem +{ + public List children { get; init; } + public string fullName { get; init; } + public bool hasChildren { get; init; } + public string id { get; init; } + public Model model { get; init; } + public string name { get; init; } + public DateTime updatedAt { get; init; } +} diff --git a/Core/Core/Api/GraphQL/Models/PendingStreamCollaborator.cs b/Core/Core/Api/GraphQL/Models/PendingStreamCollaborator.cs new file mode 100644 index 0000000000..805c0231ba --- /dev/null +++ b/Core/Core/Api/GraphQL/Models/PendingStreamCollaborator.cs @@ -0,0 +1,25 @@ +#nullable disable +using System; + +namespace Speckle.Core.Api.GraphQL.Models; + +public sealed class PendingStreamCollaborator +{ + public string id { get; init; } + public string inviteId { get; init; } + + public string projectId { get; init; } + + public string projectName { get; init; } + public string title { get; init; } + public string role { get; init; } + public LimitedUser invitedBy { get; init; } + public LimitedUser user { get; init; } + public string token { get; init; } + + [Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] + public string streamId { get; init; } + + [Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] + public string streamName { get; init; } +} diff --git a/Core/Core/Api/GraphQL/Models/Project.cs b/Core/Core/Api/GraphQL/Models/Project.cs new file mode 100644 index 0000000000..537ceb4d75 --- /dev/null +++ b/Core/Core/Api/GraphQL/Models/Project.cs @@ -0,0 +1,30 @@ +#nullable disable +using System; +using System.Collections.Generic; +using Speckle.Core.Api.GraphQL.Enums; + +namespace Speckle.Core.Api.GraphQL.Models; + +public sealed class Project +{ + public bool AllowPublicComments { get; init; } + public ProjectCommentCollection commentThreads { get; init; } + public DateTime createdAt { get; init; } + public string description { get; init; } + public string id { get; init; } + public List invitedTeam { get; init; } + public ResourceCollection models { get; init; } + public string name { get; init; } + public List pendingImportedModels { get; init; } + public string role { get; init; } + public List sourceApps { get; init; } + public List team { get; init; } + public DateTime updatedAt { get; init; } + public ProjectVisibility visibility { get; init; } + + public List viewerResources { get; init; } + public ResourceCollection versions { get; init; } + public Model model { get; init; } + public List modelChildrenTree { get; init; } + public ResourceCollection modelsTree { get; init; } +} diff --git a/Core/Core/Api/GraphQL/Models/ProjectCollaborator.cs b/Core/Core/Api/GraphQL/Models/ProjectCollaborator.cs new file mode 100644 index 0000000000..ea0628316a --- /dev/null +++ b/Core/Core/Api/GraphQL/Models/ProjectCollaborator.cs @@ -0,0 +1,9 @@ +#nullable disable + +namespace Speckle.Core.Api.GraphQL.Models; + +public sealed class ProjectCollaborator +{ + public string role { get; init; } + public LimitedUser user { get; init; } +} diff --git a/Core/Core/Api/GraphQL/Models/ResourceIdentifier.cs b/Core/Core/Api/GraphQL/Models/ResourceIdentifier.cs new file mode 100644 index 0000000000..670111caf9 --- /dev/null +++ b/Core/Core/Api/GraphQL/Models/ResourceIdentifier.cs @@ -0,0 +1,10 @@ +#nullable disable +using Speckle.Core.Api.GraphQL.Enums; + +namespace Speckle.Core.Api.GraphQL.Models; + +public sealed class ResourceIdentifier +{ + public string resourceId { get; init; } + public ResourceType resourceType { get; init; } +} diff --git a/Core/Core/Api/GraphQL/Models/Responses/MutationResponses.cs b/Core/Core/Api/GraphQL/Models/Responses/MutationResponses.cs new file mode 100644 index 0000000000..b456d05ea0 --- /dev/null +++ b/Core/Core/Api/GraphQL/Models/Responses/MutationResponses.cs @@ -0,0 +1,42 @@ +namespace Speckle.Core.Api.GraphQL.Models.Responses; + +#nullable disable +internal sealed class ProjectMutation +{ + public Project create { get; init; } + public Project update { get; init; } + public bool delete { get; init; } + public ProjectInviteMutation invites { get; init; } + + public Project updateRole { get; init; } +} + +internal sealed class ProjectInviteMutation +{ + public Project create { get; init; } + public bool use { get; init; } + public Project cancel { get; init; } +} + +internal sealed class ModelMutation +{ + public Model create { get; init; } + public Model update { get; init; } + public bool delete { get; init; } +} + +internal sealed class VersionMutation +{ + public bool delete { get; init; } + public Model moveToModel { get; init; } + public Version update { get; init; } +} + +internal sealed class CommentMutation +{ + public bool archive { get; init; } + public Comment create { get; init; } + public Comment edit { get; init; } + public bool markViewed { get; init; } + public Comment reply { get; init; } +} diff --git a/Core/Core/Api/GraphQL/Models/Responses/Responses.cs b/Core/Core/Api/GraphQL/Models/Responses/Responses.cs new file mode 100644 index 0000000000..e731f3118d --- /dev/null +++ b/Core/Core/Api/GraphQL/Models/Responses/Responses.cs @@ -0,0 +1,30 @@ +using Speckle.Newtonsoft.Json; + +namespace Speckle.Core.Api.GraphQL.Models.Responses; + +// This file holds simple records that represent the root GraphQL response data +// For this reason, we're keeping them internal, allowing us to be flexible without the concern for breaking. +// It also exposes fewer similarly named types to dependent assemblies + +internal record ProjectResponse([property: JsonRequired] Project project); + +internal record ActiveUserResponse(User? activeUser); + +internal record LimitedUserResponse(LimitedUser? otherUser); + +internal record ServerInfoResponse([property: JsonRequired] ServerInfo serverInfo); + +internal record ProjectMutationResponse([property: JsonRequired] ProjectMutation projectMutations); + +internal record ModelMutationResponse([property: JsonRequired] ModelMutation modelMutations); + +internal record VersionMutationResponse([property: JsonRequired] VersionMutation versionMutations); + +internal record ProjectInviteResponse(PendingStreamCollaborator? projectInvite); + +internal record UserSearchResponse([property: JsonRequired] ResourceCollection userSearch); + +//All of the above records could be replaced by either RequiredResponse or OptionalResponse, if we use an alias (see https://www.baeldung.com/graphql-field-name) +internal record RequiredResponse([property: JsonRequired] T data); + +internal record OptionalResponse(T? data); diff --git a/Core/Core/Api/GraphQL/Models/ServerInfo.cs b/Core/Core/Api/GraphQL/Models/ServerInfo.cs new file mode 100644 index 0000000000..11f053ebe8 --- /dev/null +++ b/Core/Core/Api/GraphQL/Models/ServerInfo.cs @@ -0,0 +1,42 @@ +#nullable disable +using System; + +namespace Speckle.Core.Api.GraphQL.Models; + +public sealed class ServerInfo +{ + public string name { get; init; } + public string company { get; init; } + public string version { get; init; } + public string adminContact { get; init; } + public string description { get; init; } + + /// + /// This field is not returned from the GQL API, + /// it should be populated after construction from the response headers. + /// see + /// + public bool frontend2 { get; set; } + + /// + /// This field is not returned from the GQL API, + /// it should be populated after construction. + /// see + /// + public string url { get; set; } + + public ServerMigration migration { get; init; } +} + +public sealed class ServerMigration +{ + /// + /// New URI where this server is now deployed + /// + public Uri movedTo { get; set; } + + /// + /// Previous URI where this server used to be deployed + /// + public Uri movedFrom { get; set; } +} diff --git a/Core/Core/Api/GraphQL/Models/SubscriptionMessages.cs b/Core/Core/Api/GraphQL/Models/SubscriptionMessages.cs new file mode 100644 index 0000000000..0ce6dc5607 --- /dev/null +++ b/Core/Core/Api/GraphQL/Models/SubscriptionMessages.cs @@ -0,0 +1,84 @@ +using System; +using Speckle.Core.Api.GraphQL.Enums; +using Speckle.Newtonsoft.Json; + +namespace Speckle.Core.Api.GraphQL.Models; + +public sealed class UserProjectsUpdatedMessage : EventArgs +{ + [JsonRequired] + public string id { get; init; } + + [JsonRequired] + public UserProjectsUpdatedMessageType type { get; init; } + + public Project? project { get; init; } +} + +public sealed class ProjectCommentsUpdatedMessage : EventArgs +{ + [JsonRequired] + public string id { get; init; } + + [JsonRequired] + public ProjectCommentsUpdatedMessageType type { get; init; } + + public Comment? comment { get; init; } +} + +public sealed class ProjectFileImportUpdatedMessage : EventArgs +{ + [JsonRequired] + public string id { get; init; } + + [JsonRequired] + public ProjectFileImportUpdatedMessageType type { get; init; } + + public FileUpload? upload { get; init; } +} + +public sealed class ProjectModelsUpdatedMessage : EventArgs +{ + [JsonRequired] + public string id { get; init; } + + [JsonRequired] + public ProjectModelsUpdatedMessageType type { get; init; } + + public Model? model { get; init; } +} + +public sealed class ProjectPendingModelsUpdatedMessage : EventArgs +{ + [JsonRequired] + public string id { get; init; } + + [JsonRequired] + public ProjectPendingModelsUpdatedMessageType type { get; init; } + + public FileUpload? model { get; init; } +} + +public sealed class ProjectUpdatedMessage : EventArgs +{ + [JsonRequired] + public string id { get; init; } + + [JsonRequired] + public ProjectUpdatedMessageType type { get; init; } + + public Project? project { get; init; } +} + +public sealed class ProjectVersionsUpdatedMessage : EventArgs +{ + [JsonRequired] + public string id { get; init; } + + [JsonRequired] + public ProjectVersionsUpdatedMessageType type { get; init; } + + public string? modelId { get; init; } + + public Version? version { get; init; } +} diff --git a/Core/Core/Api/GraphQL/Models/User.cs b/Core/Core/Api/GraphQL/Models/User.cs new file mode 100644 index 0000000000..f3d6b26863 --- /dev/null +++ b/Core/Core/Api/GraphQL/Models/User.cs @@ -0,0 +1,52 @@ +#nullable disable +using System; +using System.Collections.Generic; + +namespace Speckle.Core.Api.GraphQL.Models; + +public abstract class UserBase +{ + public ResourceCollection activity { get; init; } + public string avatar { get; init; } + public string bio { get; init; } + public string company { get; set; } + public string id { get; init; } + public string name { get; init; } + public string role { get; init; } + + public ResourceCollection timeline { get; init; } + public int totalOwnedStreamsFavorites { get; init; } + public bool? verified { get; init; } + + [Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] + public ResourceCollection commits { get; init; } + + [Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] + public ResourceCollection streams { get; init; } +} + +public sealed class LimitedUser : UserBase +{ + public override string ToString() + { + return $"Other user profile: ({name} | {id})"; + } +} + +public sealed class User : UserBase +{ + public DateTime? createdAt { get; init; } + public string email { get; init; } + public bool? hasPendingVerification { get; init; } + public bool? isOnboardingFinished { get; init; } + public List projectInvites { get; init; } + public ResourceCollection projects { get; init; } + + [Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] + public ResourceCollection favoriteStreams { get; init; } + + public override string ToString() + { + return $"User ({email} | {name} | {id})"; + } +} diff --git a/Core/Core/Api/GraphQL/Models/Version.cs b/Core/Core/Api/GraphQL/Models/Version.cs new file mode 100644 index 0000000000..1aa46b0dae --- /dev/null +++ b/Core/Core/Api/GraphQL/Models/Version.cs @@ -0,0 +1,18 @@ +#nullable disable + +using System; + +namespace Speckle.Core.Api.GraphQL.Models; + +public sealed class Version +{ + public LimitedUser authorUser { get; init; } + public ResourceCollection commentThreads { get; init; } + public DateTime createdAt { get; init; } + public string id { get; init; } + public string message { get; init; } + public Model model { get; init; } + public Uri previewUrl { get; init; } + public string referencedObject { get; init; } + public string sourceApplication { get; init; } +} diff --git a/Core/Core/Api/GraphQL/Models/ViewerResourceGroup.cs b/Core/Core/Api/GraphQL/Models/ViewerResourceGroup.cs new file mode 100644 index 0000000000..3ef11ed67f --- /dev/null +++ b/Core/Core/Api/GraphQL/Models/ViewerResourceGroup.cs @@ -0,0 +1,11 @@ +#nullable disable + +using System.Collections.Generic; + +namespace Speckle.Core.Api.GraphQL.Models; + +public class ViewerResourceGroup +{ + public string identifier { get; init; } + public List items { get; init; } +} diff --git a/Core/Core/Api/GraphQL/Models/ViewerResourceItem.cs b/Core/Core/Api/GraphQL/Models/ViewerResourceItem.cs new file mode 100644 index 0000000000..12fc34e29f --- /dev/null +++ b/Core/Core/Api/GraphQL/Models/ViewerResourceItem.cs @@ -0,0 +1,10 @@ +#nullable disable + +namespace Speckle.Core.Api.GraphQL.Models; + +public class ViewerResourceItem +{ + public string modelId { get; init; } + public string objectId { get; init; } + public string versionId { get; init; } +} diff --git a/Core/Core/Api/GraphQL/Resources/ActiveUserResource.cs b/Core/Core/Api/GraphQL/Resources/ActiveUserResource.cs new file mode 100644 index 0000000000..400385cc93 --- /dev/null +++ b/Core/Core/Api/GraphQL/Resources/ActiveUserResource.cs @@ -0,0 +1,165 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using GraphQL; +using Speckle.Core.Api.GraphQL.Inputs; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Models.Responses; + +namespace Speckle.Core.Api.GraphQL.Resources; + +public sealed class ActiveUserResource +{ + private readonly ISpeckleGraphQLClient _client; + + internal ActiveUserResource(ISpeckleGraphQLClient client) + { + _client = client; + } + + /// + /// Gets the currently active user profile. + /// + /// + /// + /// the requested user, or null if the user does not exist (i.e. was initialised with an unauthenticated account) + /// + public async Task Get(CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + query User { + activeUser { + id, + email, + name, + bio, + company, + avatar, + verified, + profiles, + role, + } + } + """; + var request = new GraphQLRequest { Query = QUERY }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + + return response.activeUser; + } + + /// Max number of projects to fetch + /// Optional cursor for pagination + /// Optional filter + /// + /// + /// + public async Task> GetProjects( + int limit = ServerLimits.DEFAULT_PAGINATION_REQUEST, + string? cursor = null, + UserProjectsFilter? filter = null, + CancellationToken cancellationToken = default + ) + { + //language=graphql + const string QUERY = """ + query User($limit : Int!, $cursor: String, $filter: UserProjectsFilter) { + activeUser { + projects(limit: $limit, cursor: $cursor, filter: $filter) { + totalCount + items { + id + name + description + visibility + allowPublicComments + role + createdAt + updatedAt + sourceApps + } + } + } + } + """; + var request = new GraphQLRequest + { + Query = QUERY, + Variables = new + { + limit, + cursor, + filter + } + }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + + if (response.activeUser is null) + { + throw new SpeckleGraphQLException("GraphQL response indicated that the ActiveUser could not be found"); + } + + return response.activeUser.projects; + } + + /// + /// + /// + public async Task> ProjectInvites(CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + query ProjectInvites { + activeUser { + projectInvites { + id + inviteId + invitedBy { + avatar + bio + company + id + name + role + verified + } + projectId + projectName + role + streamId + streamName + title + token + user { + id, + name, + bio, + company, + verified, + role, + } + } + } + } + """; + + var request = new GraphQLRequest { Query = QUERY }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + + if (response.activeUser is null) + { + throw new SpeckleGraphQLException("GraphQL response indicated that the ActiveUser could not be found"); + } + + return response.activeUser.projectInvites; + } +} diff --git a/Core/Core/Api/GraphQL/Resources/CommentResource.cs b/Core/Core/Api/GraphQL/Resources/CommentResource.cs new file mode 100644 index 0000000000..bd23ae8e5c --- /dev/null +++ b/Core/Core/Api/GraphQL/Resources/CommentResource.cs @@ -0,0 +1,279 @@ +using System.Threading; +using System.Threading.Tasks; +using GraphQL; +using Speckle.Core.Api.GraphQL.Inputs; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Models.Responses; + +namespace Speckle.Core.Api.GraphQL.Resources; + +public sealed class CommentResource +{ + private readonly ISpeckleGraphQLClient _client; + + internal CommentResource(ISpeckleGraphQLClient client) + { + _client = client; + } + + /// + /// Max number of comments to fetch + /// Optional cursor for pagination + /// Optional filter + /// Max number of comment replies to fetch + /// Optional cursor for pagination + /// + /// + /// + public async Task> GetProjectComments( + string projectId, + int limit = ServerLimits.DEFAULT_PAGINATION_REQUEST, + string? cursor = null, + ProjectCommentsFilter? filter = null, + int repliesLimit = ServerLimits.DEFAULT_PAGINATION_REQUEST, + string? repliesCursor = null, + CancellationToken cancellationToken = default + ) + { + //language=graphql + const string QUERY = """ + query CommentThreads($projectId: String!, $cursor: String, $limit: Int!, $filter: ProjectCommentsFilter, $repliesLimit: Int, $repliesCursor: String) { + project(id: $projectId) { + commentThreads(cursor: $cursor, limit: $limit, filter: $filter) { + cursor + totalArchivedCount + totalCount + items { + archived + authorId + createdAt + hasParent + id + rawText + replies(limit: $repliesLimit, cursor: $repliesCursor) { + cursor + items { + archived + authorId + createdAt + hasParent + id + rawText + updatedAt + viewedAt + } + totalCount + } + resources { + resourceId + resourceType + } + screenshot + updatedAt + viewedAt + viewerResources { + modelId + objectId + versionId + } + data + } + } + } + } + """; + + GraphQLRequest request = + new() + { + Query = QUERY, + Variables = new + { + projectId, + cursor, + limit, + filter, + repliesLimit, + repliesCursor, + } + }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + + return response.project.commentThreads; + } + + /// + /// This function only exists here to be able to integration tests the queries. + /// The process of creating a comment is more complex and javascript specific than we can expose to our SDKs at this time. + /// + /// + /// + /// + /// + internal async Task Create(CreateCommentInput input, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + mutation Mutation($input: CreateCommentInput!) { + data:commentMutations { + create(input: $input) { + archived + authorId + createdAt + hasParent + id + rawText + resources { + resourceId + resourceType + } + screenshot + updatedAt + viewedAt + viewerResources { + modelId + objectId + versionId + } + data + } + } + } + """; + GraphQLRequest request = new(QUERY, variables: new { input }); + var res = await _client + .ExecuteGraphQLRequest>(request, cancellationToken) + .ConfigureAwait(false); + return res.data.create; + } + + /// + /// + /// + /// + /// + internal async Task Edit(EditCommentInput input, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + mutation Mutation($input: EditCommentInput!) { + data:commentMutations { + edit(input: $input) { + archived + authorId + createdAt + hasParent + id + rawText + resources { + resourceId + resourceType + } + screenshot + updatedAt + viewedAt + viewerResources { + modelId + objectId + versionId + } + data + } + } + } + """; + GraphQLRequest request = new(QUERY, variables: new { input }); + var res = await _client + .ExecuteGraphQLRequest>(request, cancellationToken) + .ConfigureAwait(false); + return res.data.edit; + } + + /// + /// + /// + /// + /// + public async Task Archive(string commentId, bool archive = true, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + mutation Mutation($commentId: String!, $archive: Boolean!) { + data:commentMutations { + archive(commentId: $commentId, archived: $archive) + } + } + """; + GraphQLRequest request = new(QUERY, variables: new { commentId, archive }); + var res = await _client + .ExecuteGraphQLRequest>(request, cancellationToken) + .ConfigureAwait(false); + return res.data.archive; + } + + /// + /// + /// + /// + public async Task MarkViewed(string commentId, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + mutation Mutation($commentId: String!) { + data:commentMutations { + markViewed(commentId: $commentId) + } + } + """; + GraphQLRequest request = new(QUERY, variables: new { commentId }); + var res = await _client + .ExecuteGraphQLRequest>(request, cancellationToken) + .ConfigureAwait(false); + return res.data.markViewed; + } + + /// + /// + /// + /// + /// + internal async Task Reply(CreateCommentReplyInput input, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + mutation Mutation($input: CreateCommentReplyInput!) { + data:commentMutations { + reply(input: $input) { + archived + authorId + createdAt + hasParent + id + rawText + resources { + resourceId + resourceType + } + screenshot + updatedAt + viewedAt + viewerResources { + modelId + objectId + versionId + } + data + } + } + } + """; + GraphQLRequest request = new(QUERY, variables: new { input }); + var res = await _client + .ExecuteGraphQLRequest>(request, cancellationToken) + .ConfigureAwait(false); + return res.data.reply; + } +} diff --git a/Core/Core/Api/GraphQL/Resources/ModelResource.cs b/Core/Core/Api/GraphQL/Resources/ModelResource.cs new file mode 100644 index 0000000000..95b3514a2b --- /dev/null +++ b/Core/Core/Api/GraphQL/Resources/ModelResource.cs @@ -0,0 +1,309 @@ +using System.Threading; +using System.Threading.Tasks; +using GraphQL; +using Speckle.Core.Api.GraphQL.Inputs; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Models.Responses; + +namespace Speckle.Core.Api.GraphQL.Resources; + +public sealed class ModelResource +{ + private readonly ISpeckleGraphQLClient _client; + + internal ModelResource(ISpeckleGraphQLClient client) + { + _client = client; + } + + /// + /// + /// + /// + /// + /// + public async Task Get(string modelId, string projectId, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + query ModelGet($modelId: String!, $projectId: String!) { + project(id: $projectId) { + model(id: $modelId) { + id + name + previewUrl + updatedAt + description + displayName + createdAt + author { + avatar + bio + company + id + name + role + totalOwnedStreamsFavorites + verified + } + } + } + } + """; + var request = new GraphQLRequest { Query = QUERY, Variables = new { modelId, projectId } }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + + return response.project.model; + } + + /// + /// + /// Max number of versions to fetch + /// Optional cursor for pagination + /// Optional versions filter + /// + /// + /// + /// + public async Task GetWithVersions( + string modelId, + string projectId, + int versionsLimit = ServerLimits.DEFAULT_PAGINATION_REQUEST, + string? versionsCursor = null, + ModelVersionsFilter? versionsFilter = null, + CancellationToken cancellationToken = default + ) + { + //language=graphql + const string QUERY = """ + query ModelGetWithVersions($modelId: String!, $projectId: String!, $versionsLimit: Int!, $versionsCursor: String, $versionsFilter: ModelVersionsFilter) { + project(id: $projectId) { + model(id: $modelId) { + id + name + previewUrl + updatedAt + versions(limit: $versionsLimit, cursor: $versionsCursor, filter: $versionsFilter) { + items { + id + referencedObject + message + sourceApplication + createdAt + previewUrl + authorUser { + totalOwnedStreamsFavorites + id + name + bio + company + verified + role + } + } + totalCount + cursor + } + description + displayName + createdAt + author { + avatar + bio + company + id + name + role + totalOwnedStreamsFavorites + verified + } + } + } + } + """; + + var request = new GraphQLRequest + { + Query = QUERY, + Variables = new + { + projectId, + modelId, + versionsLimit, + versionsCursor, + versionsFilter, + } + }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + + return response.project.model; + } + + /// + /// Max number of models to fetch + /// Optional cursor for pagination + /// Optional models filter + /// + /// + /// + public async Task> GetModels( + string projectId, + int modelsLimit = ServerLimits.DEFAULT_PAGINATION_REQUEST, + string? modelsCursor = null, + ProjectModelsFilter? modelsFilter = null, + CancellationToken cancellationToken = default + ) + { + //language=graphql + const string QUERY = """ + query ProjectGetWithModels($projectId: String!, $modelsLimit: Int!, $modelsCursor: String, $modelsFilter: ProjectModelsFilter) { + project(id: $projectId) { + models(limit: $modelsLimit, cursor: $modelsCursor, filter: $modelsFilter) { + items { + id + name + previewUrl + updatedAt + displayName + description + createdAt + } + totalCount + cursor + } + } + } + """; + GraphQLRequest request = + new() + { + Query = QUERY, + Variables = new + { + projectId, + modelsLimit, + modelsCursor, + modelsFilter + } + }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + return response.project.models; + } + + /// + /// + /// + /// + public async Task Create(CreateModelInput input, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + mutation ModelCreate($input: CreateModelInput!) { + modelMutations { + create(input: $input) { + id + displayName + name + description + createdAt + updatedAt + previewUrl + author { + avatar + bio + company + id + name + role + totalOwnedStreamsFavorites + verified + } + } + } + } + """; + + GraphQLRequest request = new() { Query = QUERY, Variables = new { input } }; + + var res = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + + return res.modelMutations.create; + } + + /// + /// + /// + /// + public async Task Delete(DeleteModelInput input, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + mutation ModelDelete($input: DeleteModelInput!) { + modelMutations { + delete(input: $input) + } + } + """; + + GraphQLRequest request = new() { Query = QUERY, Variables = new { input } }; + + var res = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + + return res.modelMutations.delete; + } + + /// + /// + /// + /// + public async Task Update(UpdateModelInput input, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + mutation ModelUpdate($input: UpdateModelInput!) { + modelMutations { + update(input: $input) { + id + name + displayName + description + createdAt + updatedAt + previewUrl + author { + avatar + bio + company + id + name + role + totalOwnedStreamsFavorites + verified + } + } + } + } + """; + + GraphQLRequest request = new() { Query = QUERY, Variables = new { input } }; + + var res = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + + return res.modelMutations.update; + } +} diff --git a/Core/Core/Api/GraphQL/Resources/OtherUserResource.cs b/Core/Core/Api/GraphQL/Resources/OtherUserResource.cs new file mode 100644 index 0000000000..5f51ced885 --- /dev/null +++ b/Core/Core/Api/GraphQL/Resources/OtherUserResource.cs @@ -0,0 +1,108 @@ +using System.Threading; +using System.Threading.Tasks; +using GraphQL; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Models.Responses; + +namespace Speckle.Core.Api.GraphQL.Resources; + +public sealed class OtherUserResource +{ + private readonly ISpeckleGraphQLClient _client; + + internal OtherUserResource(ISpeckleGraphQLClient client) + { + _client = client; + } + + /// + /// + /// + /// + /// + /// the requested user, or null if the user does not exist + /// + public async Task Get(string id, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + query LimitedUser($id: String!) { + otherUser(id: $id){ + id, + name, + bio, + company, + avatar, + verified, + role, + } + } + """; + + var request = new GraphQLRequest { Query = QUERY, Variables = new { id } }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + + return response.otherUser; + } + + /// + /// Searches for a user on the server. + /// + /// String to search for. Must be at least 3 characters + /// Max number of users to fetch + /// Optional cursor for pagination + /// + /// + /// + /// + /// + public async Task> UserSearch( + string query, + int limit = ServerLimits.DEFAULT_PAGINATION_REQUEST, + string? cursor = null, + bool archived = false, + bool emailOnly = false, + CancellationToken cancellationToken = default + ) + { + //language=graphql + const string QUERY = """ + query UserSearch($query: String!, $limit: Int!, $cursor: String, $archived: Boolean, $emailOnly: Boolean) { + userSearch(query: $query, limit: $limit, cursor: $cursor, archived: $archived, emailOnly: $emailOnly) { + cursor, + items { + id + name + bio + company + avatar + verified + role + } + } + } + """; + + var request = new GraphQLRequest + { + Query = QUERY, + Variables = new + { + query, + limit, + cursor, + archived, + emailOnly + } + }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + + return response.userSearch; + } +} diff --git a/Core/Core/Api/GraphQL/Resources/ProjectInviteResource.cs b/Core/Core/Api/GraphQL/Resources/ProjectInviteResource.cs new file mode 100644 index 0000000000..65f1cb1e30 --- /dev/null +++ b/Core/Core/Api/GraphQL/Resources/ProjectInviteResource.cs @@ -0,0 +1,260 @@ +using System.Threading; +using System.Threading.Tasks; +using GraphQL; +using Speckle.Core.Api.GraphQL.Inputs; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Models.Responses; + +namespace Speckle.Core.Api.GraphQL.Resources; + +public sealed class ProjectInviteResource +{ + private readonly ISpeckleGraphQLClient _client; + + internal ProjectInviteResource(ISpeckleGraphQLClient client) + { + _client = client; + } + + /// + /// + /// + /// + /// + public async Task Create( + string projectId, + ProjectInviteCreateInput input, + CancellationToken cancellationToken = default + ) + { + //language=graphql + const string QUERY = """ + mutation ProjectInviteCreate($projectId: ID!, $input: ProjectInviteCreateInput!) { + projectMutations { + invites { + create(projectId: $projectId, input: $input) { + id + name + description + visibility + allowPublicComments + role + createdAt + updatedAt + team { + role + user { + totalOwnedStreamsFavorites + id + name + bio + company + avatar + verified + role + } + } + invitedTeam { + id + inviteId + projectId + projectName + streamName + title + role + streamId + token + user { + totalOwnedStreamsFavorites + id + name + bio + company + avatar + verified + role + } + invitedBy { + totalOwnedStreamsFavorites + id + name + bio + company + avatar + verified + role + } + } + } + } + } + } + """; + GraphQLRequest request = new() { Query = QUERY, Variables = new { projectId, input } }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + + return response.projectMutations.invites.create; + } + + /// + /// + /// + /// + public async Task Use(ProjectInviteUseInput input, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + mutation ProjectInviteUse($input: ProjectInviteUseInput!) { + projectMutations { + invites { + use(input: $input) + } + } + } + """; + GraphQLRequest request = new() { Query = QUERY, Variables = new { input } }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + return response.projectMutations.invites.use; + } + + /// + /// + /// + /// The invite, or null if no invite exists + /// + public async Task Get( + string projectId, + string? token, + CancellationToken cancellationToken = default + ) + { + //language=graphql + const string QUERY = """ + query ProjectInvite($projectId: String!, $token: String) { + projectInvite(projectId: $projectId, token: $token) { + id + inviteId + invitedBy { + avatar + bio + company + id + name + role + totalOwnedStreamsFavorites + verified + } + projectId + projectName + role + streamId + streamName + title + token + user { + avatar + bio + company + id + name + role + totalOwnedStreamsFavorites + verified + } + } + } + """; + GraphQLRequest request = new() { Query = QUERY, Variables = new { projectId, token } }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + + return response.projectInvite; + } + + /// + /// + /// + /// + /// + public async Task Cancel(string projectId, string inviteId, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + mutation ProjectInviteCancel($projectId: ID!, $inviteId: String!) { + projectMutations { + invites { + cancel(projectId: $projectId, inviteId: $inviteId) { + id + name + description + visibility + allowPublicComments + role + createdAt + updatedAt + team { + role + user { + totalOwnedStreamsFavorites + id + name + bio + company + avatar + verified + role + } + } + invitedTeam { + id + inviteId + projectId + projectName + streamName + title + role + streamId + token + user { + totalOwnedStreamsFavorites + id + name + bio + company + avatar + verified + role + } + invitedBy { + totalOwnedStreamsFavorites + id + name + bio + company + avatar + verified + role + } + } + } + } + } + } + """; + GraphQLRequest request = new() { Query = QUERY, Variables = new { projectId, inviteId } }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + + return response.projectMutations.invites.cancel; + } +} diff --git a/Core/Core/Api/GraphQL/Resources/ProjectResource.cs b/Core/Core/Api/GraphQL/Resources/ProjectResource.cs new file mode 100644 index 0000000000..f21fb723ae --- /dev/null +++ b/Core/Core/Api/GraphQL/Resources/ProjectResource.cs @@ -0,0 +1,349 @@ +using System.Threading; +using System.Threading.Tasks; +using GraphQL; +using Speckle.Core.Api.GraphQL.Inputs; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Models.Responses; + +namespace Speckle.Core.Api.GraphQL.Resources; + +public sealed class ProjectResource +{ + private readonly ISpeckleGraphQLClient _client; + + internal ProjectResource(ISpeckleGraphQLClient client) + { + _client = client; + } + + /// + /// + /// + /// + /// + /// + public async Task Get(string projectId, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + query Project($projectId: String!) { + project(id: $projectId) { + id + name + description + visibility + allowPublicComments + role + createdAt + updatedAt + sourceApps + } + } + """; + GraphQLRequest request = new() { Query = QUERY, Variables = new { projectId } }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + return response.project; + } + + /// + /// Max number of models to fetch + /// Optional cursor for pagination + /// Optional models filter + /// + /// + /// + /// + /// + public async Task GetWithModels( + string projectId, + int modelsLimit = ServerLimits.DEFAULT_PAGINATION_REQUEST, + string? modelsCursor = null, + ProjectModelsFilter? modelsFilter = null, + CancellationToken cancellationToken = default + ) + { + //language=graphql + const string QUERY = """ + query ProjectGetWithModels($projectId: String!, $modelsLimit: Int!, $modelsCursor: String, $modelsFilter: ProjectModelsFilter) { + project(id: $projectId) { + id + name + description + visibility + allowPublicComments + role + createdAt + updatedAt + sourceApps + models(limit: $modelsLimit, cursor: $modelsCursor, filter: $modelsFilter) { + items { + id + name + previewUrl + updatedAt + displayName + description + createdAt + } + cursor + totalCount + } + } + } + """; + GraphQLRequest request = + new() + { + Query = QUERY, + Variables = new + { + projectId, + modelsLimit, + modelsCursor, + modelsFilter + } + }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + return response.project; + } + + /// + /// + /// + /// + /// + /// + public async Task GetWithTeam(string projectId, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + query ProjectGetWithTeam($projectId: String!) { + project(id: $projectId) { + id + name + description + visibility + allowPublicComments + role + createdAt + updatedAt + team { + role + user { + totalOwnedStreamsFavorites + id + name + bio + company + avatar + verified + role + } + } + invitedTeam { + id + inviteId + projectId + projectName + streamId + streamName + title + role + token + user { + totalOwnedStreamsFavorites + id + name + bio + company + avatar + verified + role + } + invitedBy { + totalOwnedStreamsFavorites + id + name + bio + company + avatar + verified + role + } + } + } + } + """; + GraphQLRequest request = new() { Query = QUERY, Variables = new { projectId } }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + return response.project; + } + + /// + /// + /// + /// + public async Task Create(ProjectCreateInput input, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + mutation ProjectCreate($input: ProjectCreateInput) { + projectMutations { + create(input: $input) { + id + name + description + visibility + allowPublicComments + role + createdAt + updatedAt + sourceApps + } + } + } + """; + GraphQLRequest request = new() { Query = QUERY, Variables = new { input } }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + return response.projectMutations.create; + } + + /// + /// + /// + /// + public async Task Update(ProjectUpdateInput input, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + mutation ProjectUpdate($input: ProjectUpdateInput!) { + projectMutations{ + update(update: $input) { + id + name + description + visibility + allowPublicComments + role + createdAt + updatedAt + } + } + } + """; + GraphQLRequest request = new() { Query = QUERY, Variables = new { input } }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + return response.projectMutations.update; + } + + /// The id of the Project to delete + /// + /// + /// + public async Task Delete(string projectId, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + mutation ProjectDelete($projectId: String!) { + projectMutations { + delete(id: $projectId) + } + } + """; + GraphQLRequest request = new() { Query = QUERY, Variables = new { projectId } }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + return response.projectMutations.delete; + } + + /// + /// + /// + public async Task UpdateRole(ProjectUpdateRoleInput input, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + mutation ProjectUpdateRole($input: ProjectUpdateRoleInput!) { + projectMutations { + updateRole(input: $input) { + id + name + description + visibility + allowPublicComments + role + createdAt + updatedAt + team { + role + user { + totalOwnedStreamsFavorites + id + name + bio + company + avatar + verified + role + } + } + invitedTeam { + id + inviteId + projectId + projectName + streamId + streamName + title + role + token + user { + totalOwnedStreamsFavorites + id + name + bio + company + avatar + verified + role + } + invitedBy { + totalOwnedStreamsFavorites + id + name + bio + company + avatar + verified + role + } + } + } + } + } + """; + GraphQLRequest request = new() { Query = QUERY, Variables = new { input } }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + return response.projectMutations.updateRole; + } +} diff --git a/Core/Core/Api/GraphQL/Resources/SubscriptionResource.cs b/Core/Core/Api/GraphQL/Resources/SubscriptionResource.cs new file mode 100644 index 0000000000..d476ae71e0 --- /dev/null +++ b/Core/Core/Api/GraphQL/Resources/SubscriptionResource.cs @@ -0,0 +1,216 @@ +using System; +using System.Collections.Generic; +using GraphQL; +using Speckle.Core.Api.GraphQL.Inputs; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Models.Responses; + +namespace Speckle.Core.Api.GraphQL.Resources; + +public sealed class Subscription : IDisposable + where TEventArgs : EventArgs +{ + internal Subscription(ISpeckleGraphQLClient client, GraphQLRequest request) + { + _subscription = client.SubscribeTo>(request, (o, t) => Listeners?.Invoke(o, t.data)); + } + + public event EventHandler? Listeners; + + private readonly IDisposable _subscription; + + public void Dispose() + { + _subscription.Dispose(); + } +} + +public sealed class SubscriptionResource : IDisposable +{ + private readonly ISpeckleGraphQLClient _client; + private readonly List _subscriptions; + + internal SubscriptionResource(ISpeckleGraphQLClient client) + { + _client = client; + _subscriptions = new(); + } + + /// Track newly added or deleted projects owned by the active user + /// + /// You should add event listeners to the returned object.
+ /// You can add multiple listeners to a , and this should be preferred over creating many subscriptions.
+ /// You should ensure proper disposal of the when you're done (see )
+ /// Disposing of the or will also dispose any s it created. + ///
+ /// + public Subscription CreateUserProjectsUpdatedSubscription() + { + //language=graphql + const string QUERY = """ + subscription UserProjectsUpdated { + data:userProjectsUpdated { + id + project { + id + } + type + } + } + """; + GraphQLRequest request = new() { Query = QUERY }; + + Subscription subscription = new(_client, request); + _subscriptions.Add(subscription); + return subscription; + } + + /// Subscribe to updates to resource comments/threads. Optionally specify resource ID string to only receive updates regarding comments for those resources + /// + /// + public Subscription CreateProjectCommentsUpdatedSubscription( + ViewerUpdateTrackingTarget target + ) + { + //language=graphql + const string QUERY = """ + subscription Subscription($target: ViewerUpdateTrackingTarget!) { + data:projectCommentsUpdated(target: $target) { + comment { + id + } + id + type + } + } + """; + GraphQLRequest request = new() { Query = QUERY, Variables = new { target } }; + + Subscription subscription = new(_client, request); + _subscriptions.Add(subscription); + return subscription; + } + + /// Subscribe to changes to a project's models. Optionally specify to track + /// + /// + public Subscription CreateProjectModelsUpdatedSubscription( + string id, + IReadOnlyList? modelIds = null + ) + { + //language=graphql + const string QUERY = """ + subscription ProjectModelsUpdated($id: String!, $modelIds: [String!]) { + data:projectModelsUpdated(id: $id, modelIds: $modelIds) { + id + model { + id + name + previewUrl + updatedAt + description + displayName + createdAt + author { + avatar + bio + company + id + name + role + totalOwnedStreamsFavorites + verified + } + } + type + } + } + """; + GraphQLRequest request = new() { Query = QUERY, Variables = new { id, modelIds } }; + + Subscription subscription = new(_client, request); + _subscriptions.Add(subscription); + return subscription; + } + + /// Track updates to a specific project + /// + /// + public Subscription CreateProjectUpdatedSubscription(string id) + { + //language=graphql + const string QUERY = """ + subscription ProjectUpdated($id: String!) { + data:projectUpdated(id: $id) { + id + project { + id + name + description + visibility + allowPublicComments + role + createdAt + updatedAt + sourceApps + } + type + } + } + """; + GraphQLRequest request = new() { Query = QUERY, Variables = new { id } }; + + Subscription subscription = new(_client, request); + _subscriptions.Add(subscription); + return subscription; + } + + /// Subscribe to changes to a project's versions. + /// + /// + public Subscription CreateProjectVersionsUpdatedSubscription(string id) + { + //language=graphql + const string QUERY = """ + subscription ProjectVersionsUpdated($id: String!) { + data:projectVersionsUpdated(id: $id) { + id + modelId + type + version { + id + referencedObject + message + sourceApplication + createdAt + previewUrl + authorUser { + totalOwnedStreamsFavorites + id + name + bio + company + verified + role + avatar + } + } + } + } + """; + GraphQLRequest request = new() { Query = QUERY, Variables = new { id } }; + + Subscription subscription = new(_client, request); + _subscriptions.Add(subscription); + return subscription; + } + + public void Dispose() + { + foreach (var subscription in _subscriptions) + { + subscription.Dispose(); + } + } +} diff --git a/Core/Core/Api/GraphQL/Resources/VersionResource.cs b/Core/Core/Api/GraphQL/Resources/VersionResource.cs new file mode 100644 index 0000000000..e21ac09e0c --- /dev/null +++ b/Core/Core/Api/GraphQL/Resources/VersionResource.cs @@ -0,0 +1,252 @@ +using System.Threading; +using System.Threading.Tasks; +using GraphQL; +using Speckle.Core.Api.GraphQL.Inputs; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Models.Responses; +using Version = Speckle.Core.Api.GraphQL.Models.Version; + +namespace Speckle.Core.Api.GraphQL.Resources; + +public sealed class VersionResource +{ + private readonly ISpeckleGraphQLClient _client; + + internal VersionResource(ISpeckleGraphQLClient client) + { + _client = client; + } + + /// + /// + /// + /// + /// + /// + public async Task Get( + string versionId, + string modelId, + string projectId, + CancellationToken cancellationToken = default + ) + { + //language=graphql + const string QUERY = """ + query VersionGet($projectId: String!, $modelId: String!, $versionId: String!) { + project(id: $projectId) { + model(id: $modelId) { + version(id: $versionId) { + id + referencedObject + message + sourceApplication + createdAt + previewUrl + authorUser { + totalOwnedStreamsFavorites + id + name + bio + company + verified + role + avatar + } + } + } + } + } + """; + GraphQLRequest request = + new() + { + Query = QUERY, + Variables = new + { + projectId, + modelId, + versionId + } + }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + return response.project.model.version; + } + + /// + /// + /// Max number of versions to fetch + /// Optional cursor for pagination + /// Optional filter + /// + /// + public async Task> GetVersions( + string modelId, + string projectId, + int limit = ServerLimits.DEFAULT_PAGINATION_REQUEST, + string? cursor = null, + ModelVersionsFilter? filter = null, + CancellationToken cancellationToken = default + ) + { + //language=graphql + const string QUERY = """ + query VersionGetVersions($projectId: String!, $modelId: String!, $limit: Int!, $cursor: String, $filter: ModelVersionsFilter) { + project(id: $projectId) { + model(id: $modelId) { + versions(limit: $limit, cursor: $cursor, filter: $filter) { + items { + id + referencedObject + message + sourceApplication + createdAt + previewUrl + authorUser { + totalOwnedStreamsFavorites + id + name + bio + company + verified + role + avatar + } + } + cursor + totalCount + } + } + } + } + """; + + GraphQLRequest request = + new() + { + Query = QUERY, + Variables = new + { + projectId, + modelId, + limit, + cursor, + filter, + } + }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + return response.project.model.versions; + } + + /// + /// + /// + public async Task Create(CommitCreateInput input, CancellationToken cancellationToken = default) + { + //TODO: Implement on server + return await ((Client)_client).CommitCreate(input, cancellationToken).ConfigureAwait(false); + } + + /// + /// + /// + public async Task Update(UpdateVersionInput input, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + mutation VersionUpdate($input: UpdateVersionInput!) { + versionMutations { + update(input: $input) { + id + referencedObject + message + sourceApplication + createdAt + previewUrl + authorUser { + totalOwnedStreamsFavorites + id + name + bio + company + verified + role + avatar + } + } + } + } + """; + GraphQLRequest request = new() { Query = QUERY, Variables = new { input, } }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + return response.versionMutations.update; + } + + //TODO: Would we rather return the full model here? with or with out versions? + /// + /// + /// + /// + public async Task MoveToModel(MoveVersionsInput input, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + mutation VersionMoveToModel($input: MoveVersionsInput!) { + versionMutations { + moveToModel(input: $input) { + id + } + } + } + """; + GraphQLRequest request = new() { Query = QUERY, Variables = new { input, } }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + return response.versionMutations.moveToModel.id; + } + + /// + /// + /// + public async Task Delete(DeleteVersionsInput input, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + mutation VersionDelete($input: DeleteVersionsInput!) { + versionMutations { + delete(input: $input) + } + } + """; + GraphQLRequest request = new() { Query = QUERY, Variables = new { input } }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + + return response.versionMutations.delete; + } + + /// + /// + /// + /// + public async Task Received( + CommitReceivedInput commitReceivedInput, + CancellationToken cancellationToken = default + ) + { + //TODO: Implement on server + return await ((Client)_client).CommitReceived(commitReceivedInput, cancellationToken).ConfigureAwait(false); + } +} diff --git a/Core/Core/Api/GraphQL/Resources/graphql.config.yml b/Core/Core/Api/GraphQL/Resources/graphql.config.yml new file mode 100644 index 0000000000..64c50ab285 --- /dev/null +++ b/Core/Core/Api/GraphQL/Resources/graphql.config.yml @@ -0,0 +1,2 @@ +schema: https://app.speckle.systems/graphql +documents: '**/*.graphql' diff --git a/Core/Core/Api/GraphQL/StreamRoles.cs b/Core/Core/Api/GraphQL/StreamRoles.cs new file mode 100644 index 0000000000..963fd2dd11 --- /dev/null +++ b/Core/Core/Api/GraphQL/StreamRoles.cs @@ -0,0 +1,12 @@ +namespace Speckle.Core.Api.GraphQL; + +/// +/// These are the default roles used by the server +/// +public static class StreamRoles +{ + public const string STREAM_OWNER = "stream:owner"; + public const string STREAM_CONTRIBUTOR = "stream:contributor"; + public const string STREAM_REVIEWER = "stream:reviewer"; + public const string? REVOKE = null; +} diff --git a/Core/Core/Api/Helpers.cs b/Core/Core/Api/Helpers.cs index dc3e2ef1c4..1450074229 100644 --- a/Core/Core/Api/Helpers.cs +++ b/Core/Core/Api/Helpers.cs @@ -10,6 +10,8 @@ using System.Runtime.InteropServices; using System.Text.Json; using System.Threading.Tasks; +using Speckle.Core.Api.GraphQL; +using Speckle.Core.Api.GraphQL.Models; using Speckle.Core.Credentials; using Speckle.Core.Helpers; using Speckle.Core.Kits; diff --git a/Core/Core/Api/Operations/Operations.Send.Obsolete.cs b/Core/Core/Api/Operations/Operations.Send.Obsolete.cs index 2d07a601eb..15f11ebd10 100644 --- a/Core/Core/Api/Operations/Operations.Send.Obsolete.cs +++ b/Core/Core/Api/Operations/Operations.Send.Obsolete.cs @@ -125,7 +125,7 @@ public static async Task Send( transports ??= new List(); using var sqLiteTransport = new SQLiteTransport { TransportName = "LC" }; - if (transports.Count == 0 && useDefaultCache == false) + if (transports.Count == 0 && !useDefaultCache) { throw new ArgumentException( "You need to provide at least one transport: cannot send with an empty transport list and no default cache.", diff --git a/Core/Core/Api/Operations/Operations.Send.cs b/Core/Core/Api/Operations/Operations.Send.cs index 882a831c24..da01128a02 100644 --- a/Core/Core/Api/Operations/Operations.Send.cs +++ b/Core/Core/Api/Operations/Operations.Send.cs @@ -83,9 +83,11 @@ public static async Task Send( var transportContext = transports.ToDictionary(t => t.TransportName, t => t.TransportContext); + var correlationId = Guid.NewGuid().ToString(); + // make sure all logs in the operation have the proper context using (LogContext.PushProperty("transportContext", transportContext)) - using (LogContext.PushProperty("correlationId", Guid.NewGuid().ToString())) + using (LogContext.PushProperty("correlationId", correlationId)) { var sendTimer = Stopwatch.StartNew(); SpeckleLog.Logger.Information("Starting send operation"); @@ -110,8 +112,9 @@ public static async Task Send( { SpeckleLog.Logger.Information( ex, - "Send operation failed after {elapsed} seconds", - sendTimer.Elapsed.TotalSeconds + "Send operation failed after {elapsed} seconds. Correlation ID: {correlationId}", + sendTimer.Elapsed.TotalSeconds, + correlationId ); if (ex is OperationCanceledException or SpeckleException) { @@ -134,10 +137,11 @@ public static async Task Send( .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}", + "Finished sending {objectCount} objects after {elapsed}, result: {objectId}. Correlation ID: {correlationId}", transports.Max(t => t.SavedObjectCount), sendTimer.Elapsed.TotalSeconds, - hash + hash, + correlationId ); return hash; } diff --git a/Core/Core/Api/ServerLimits.cs b/Core/Core/Api/ServerLimits.cs index 7f78def96a..262d1a63fc 100644 --- a/Core/Core/Api/ServerLimits.cs +++ b/Core/Core/Api/ServerLimits.cs @@ -11,4 +11,7 @@ public static class ServerLimits { public const int BRANCH_GET_LIMIT = 500; public const int OLD_BRANCH_GET_LIMIT = 100; + + /// the default `limit` argument value for paginated requests + public const int DEFAULT_PAGINATION_REQUEST = 25; } diff --git a/Core/Core/Core.csproj b/Core/Core/Core.csproj index 2b74ff5528..fac2359928 100644 --- a/Core/Core/Core.csproj +++ b/Core/Core/Core.csproj @@ -23,7 +23,7 @@ CA1502; CA1506; CA1708; CA1710; CA1711; CA1716; CA1720; CA1721; CA1724; CA1816; CA1851; CA1861; - CA2201; + CA2201; CS8618; CS0419; CS0618; CS0659; CS0809; CS8600; CS8602; CS8603; CS8604; IDE0032; IDE0059; IDE0130; IDE1006; @@ -44,11 +44,10 @@ + - - @@ -57,8 +56,8 @@ - + diff --git a/Core/Core/Credentials/Account.cs b/Core/Core/Credentials/Account.cs index 04d33e7693..184797d1ab 100644 --- a/Core/Core/Credentials/Account.cs +++ b/Core/Core/Credentials/Account.cs @@ -1,7 +1,7 @@ #nullable disable using System; using System.Threading.Tasks; -using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL.Models; using Speckle.Core.Helpers; namespace Speckle.Core.Credentials; diff --git a/Core/Core/Credentials/AccountManager.cs b/Core/Core/Credentials/AccountManager.cs index 0f9138825f..6a5bf478b2 100644 --- a/Core/Core/Credentials/AccountManager.cs +++ b/Core/Core/Credentials/AccountManager.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.Linq; using System.Net; @@ -14,7 +13,9 @@ using GraphQL; using GraphQL.Client.Http; using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL.Models; using Speckle.Core.Api.GraphQL; +using Speckle.Core.Api.GraphQL.Models.Responses; using Speckle.Core.Api.GraphQL.Serializer; using Speckle.Core.Helpers; using Speckle.Core.Logging; @@ -114,22 +115,29 @@ public static async Task GetUserInfo( new NewtonsoftJsonSerializer(), httpClient ); - //language=graphql - var request = new GraphQLRequest { Query = " query { activeUser { name email id company } }" }; - - var response = await gqlClient.SendQueryAsync(request, cancellationToken).ConfigureAwait(false); + const string QUERY = """ + query { + data:activeUser { + name + email + id + company + } + } + """; + var request = new GraphQLRequest { Query = QUERY }; + + var response = await gqlClient + .SendQueryAsync>(request, cancellationToken) + .ConfigureAwait(false); if (response.Errors != null) { - throw new SpeckleGraphQLException( - $"GraphQL request {nameof(GetUserInfo)} failed", - request, - response - ); + throw new SpeckleGraphQLException($"GraphQL request {nameof(GetUserInfo)} failed", request, response); } - return response.Data.activeUser; + return response.Data.data; } /// @@ -552,7 +560,7 @@ private static async Task GetAccessCode(string server, string challenge, var accessCode = ""; - Process.Start(new ProcessStartInfo($"{server}/authn/verify/sca/{challenge}") { UseShellExecute = true }); + Open.Url($"{server}/authn/verify/sca/{challenge}"); var task = Task.Run(() => { diff --git a/Core/Core/Credentials/Responses.cs b/Core/Core/Credentials/Responses.cs index 810d3cae6a..212173f6bd 100644 --- a/Core/Core/Credentials/Responses.cs +++ b/Core/Core/Credentials/Responses.cs @@ -1,56 +1,43 @@ -#nullable disable using System; using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL.Models; namespace Speckle.Core.Credentials; -[Obsolete("Use activeUser query and ActiveUserServerInfoResponse instead", true)] -public class UserServerInfoResponse +internal sealed class ActiveUserServerInfoResponse { - public UserInfo user { get; set; } - public ServerInfo serverInfo { get; set; } + public UserInfo activeUser { get; init; } + public ServerInfo serverInfo { get; init; } } -public class ActiveUserServerInfoResponse +internal sealed class TokenExchangeResponse { - public UserInfo activeUser { get; set; } - public ServerInfo serverInfo { get; set; } + public string token { get; init; } + public string refreshToken { get; init; } } -[Obsolete("Use activeUser query and ActiveUserResponse instead", true)] -public class UserInfoResponse +public sealed class UserInfo { - public UserInfo user { get; set; } -} + public string id { get; init; } + public string name { get; init; } + public string email { get; init; } + public string? company { get; init; } + public string? avatar { get; init; } -public class ActiveUserResponse -{ - public UserInfo activeUser { get; set; } -} + [Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] + public Streams streams { get; init; } -public class UserInfo -{ - public string id { get; set; } - public string name { get; set; } - public string email { get; set; } - public string company { get; set; } - public string avatar { get; set; } - - public Streams streams { get; set; } - public Commits commits { get; set; } -} - -public class TokenExchangeResponse -{ - public string token { get; set; } - public string refreshToken { get; set; } + [Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] + public Commits commits { get; init; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class Streams { public int totalCount { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class Commits { public int totalCount { get; set; } diff --git a/Core/Core/Credentials/StreamWrapper.cs b/Core/Core/Credentials/StreamWrapper.cs index 7c9d49caa2..5c578c46f3 100644 --- a/Core/Core/Credentials/StreamWrapper.cs +++ b/Core/Core/Credentials/StreamWrapper.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using System.Web; using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL.Models; using Speckle.Core.Helpers; using Speckle.Core.Logging; diff --git a/Core/Core/Helpers/Http.cs b/Core/Core/Helpers/Http.cs index 8cb9076820..5fe4ef0edb 100644 --- a/Core/Core/Helpers/Http.cs +++ b/Core/Core/Helpers/Http.cs @@ -30,16 +30,10 @@ public static AsyncRetryPolicy HttpAsyncPolicy(IEnumerable< .HandleTransientHttpError() .WaitAndRetryAsync( delay ?? DefaultDelay(), - (ex, timeSpan, retryAttempt, context) => { - //context.Remove("retryCount"); - //context.Add("retryCount", retryAttempt); - //Log.Information( - // ex.Exception, - // "The http request failed with {exceptionType} exception retrying after {cooldown} milliseconds. This is retry attempt {retryAttempt}", - // ex.GetType().Name, - // timeSpan.TotalSeconds * 1000, - // retryAttempt - //); + (ex, timeSpan, retryAttempt, context) => + { + context.Remove("retryCount"); + context.Add("retryCount", retryAttempt); } ); } @@ -209,11 +203,13 @@ CancellationToken cancellationToken var timer = new Stopwatch(); timer.Start(); context.Add("retryCount", 0); + + request.Headers.Add("x-request-id", context.CorrelationId.ToString()); + var policyResult = await Http.HttpAsyncPolicy(_delay) .ExecuteAndCaptureAsync( ctx => { - request.Headers.Add("x-request-id", ctx.CorrelationId.ToString()); return base.SendAsync(request, cancellationToken); }, context @@ -225,14 +221,15 @@ CancellationToken cancellationToken SpeckleLog.Logger .ForContext("ExceptionType", policyResult.FinalException?.GetType()) .Information( - "Execution of http request to {httpScheme}://{hostUrl}/{relativeUrl} {resultStatus} with {httpStatusCode} after {elapsed} seconds and {retryCount} retries", + "Execution of http request to {httpScheme}://{hostUrl}{relativeUrl} {resultStatus} with {httpStatusCode} after {elapsed} seconds and {retryCount} retries. Request correlation ID: {correlationId}", request.RequestUri.Scheme, request.RequestUri.Host, request.RequestUri.PathAndQuery, status, policyResult.Result?.StatusCode, timer.Elapsed.TotalSeconds, - retryCount ?? 0 + retryCount ?? 0, + context.CorrelationId.ToString() ); if (policyResult.Outcome == OutcomeType.Successful) { diff --git a/Core/Core/Helpers/Open.cs b/Core/Core/Helpers/Open.cs new file mode 100644 index 0000000000..06ac1265b4 --- /dev/null +++ b/Core/Core/Helpers/Open.cs @@ -0,0 +1,31 @@ +using System.Diagnostics; +using System.IO; + +namespace Speckle.Core.Helpers; + +public static class Open +{ + public static void Url(string url) + { + var psi = new ProcessStartInfo { FileName = url, UseShellExecute = true }; + Process.Start(psi); + } + + public static void File(string path, string? arguments = null) + { + var psi = new ProcessStartInfo { FileName = path, UseShellExecute = true }; + FileAttributes attr = System.IO.File.GetAttributes(path); + if (attr.HasFlag(FileAttributes.Directory)) + { + Process.Start(psi); + } + else + { + if (!string.IsNullOrWhiteSpace(arguments)) + { + psi.Arguments = arguments; + } + Process.Start(psi); + } + } +} diff --git a/Core/Core/Logging/SpeckleException.cs b/Core/Core/Logging/SpeckleException.cs index 397b8b16ac..ac59ed9cd2 100644 --- a/Core/Core/Logging/SpeckleException.cs +++ b/Core/Core/Logging/SpeckleException.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Linq; using GraphQL; -using Sentry; +using Speckle.Core.Api; namespace Speckle.Core.Logging; @@ -18,21 +18,21 @@ public SpeckleException(string? message, Exception? inner = null) #region obsolete [Obsolete("Use any other constructor", true)] - public SpeckleException(string? message, Exception? inner, bool log = true, SentryLevel level = SentryLevel.Info) + public SpeckleException(string? message, Exception? inner, bool log = true) : base(message, inner) { } - [Obsolete("Use any other constructor")] - public SpeckleException(string? message, GraphQLError[] errors, bool log = true, SentryLevel level = SentryLevel.Info) + [Obsolete($"Use {nameof(SpeckleGraphQLException)} instead", true)] + public SpeckleException(string? message, GraphQLError[] errors, bool log = true) : base(message) { GraphQLErrors = errors.Select(error => new KeyValuePair("error", error.Message)).ToList(); } [Obsolete("Use any other constructor", true)] - public SpeckleException(string message, bool log, SentryLevel level = SentryLevel.Info) + public SpeckleException(string message, bool log) : base(message) { } - [Obsolete("Use any other constructor", true)] + [Obsolete($"Use {nameof(SpeckleGraphQLException)} instead", true)] public List> GraphQLErrors { get; set; } #endregion } diff --git a/Core/Core/Logging/SpeckleLog.cs b/Core/Core/Logging/SpeckleLog.cs index fba2ac094f..6285549e46 100644 --- a/Core/Core/Logging/SpeckleLog.cs +++ b/Core/Core/Logging/SpeckleLog.cs @@ -3,7 +3,6 @@ using System.IO; using System.Reflection; using System.Runtime.InteropServices; -using Sentry; using Serilog; using Serilog.Core; using Serilog.Events; @@ -138,17 +137,6 @@ public static void Initialize( var id = GetUserIdFromDefaultAccount(); s_logger = s_logger.ForContext("id", id).ForContext("isMachineId", s_isMachineIdUsed); - // Configure scope after logger created. - SentrySdk.ConfigureScope(scope => - { - scope.User = new User { Id = id }; - }); - - SentrySdk.ConfigureScope(scope => - { - scope.SetTag("hostApplication", hostApplicationName); - }); - Logger .ForContext("userApplicationDataPath", SpecklePathProvider.UserApplicationDataPath()) .ForContext("installApplicationDataPath", SpecklePathProvider.InstallApplicationDataPath) @@ -222,35 +210,6 @@ SpeckleLogConfiguration logConfiguration ); } - if (logConfiguration.LogToSentry) - { - const string ENV = -#if DEBUG - "dev"; -#else - "production"; -#endif - - serilogLogConfiguration = serilogLogConfiguration.WriteTo.Sentry(o => - { - o.Dsn = logConfiguration.SentryDns; - o.Debug = false; - o.Environment = ENV; - o.Release = "SpeckleCore@" + Assembly.GetExecutingAssembly().GetName().Version; - o.AttachStacktrace = true; - o.StackTraceMode = StackTraceMode.Enhanced; - // Set traces_sample_rate to 1.0 to capture 100% of transactions for performance monitoring. - // We recommend adjusting this value in production. - o.TracesSampleRate = 1.0; - // Enable Global Mode if running in a client app - o.IsGlobalModeEnabled = true; - // Debug and higher are stored as breadcrumbs (default is Information) - o.MinimumBreadcrumbLevel = LogEventLevel.Debug; - // Warning and higher is sent as event (default is Error) - o.MinimumEventLevel = LogEventLevel.Error; - }); - } - var logger = serilogLogConfiguration.CreateLogger(); if (logConfiguration.LogToFile && !canLogToFile) @@ -270,7 +229,7 @@ public static void OpenCurrentLogFolder() { try { - Process.Start(s_logFolderPath); + Open.File(s_logFolderPath); } catch (FileNotFoundException ex) { diff --git a/Core/Core/Models/DynamicBase.cs b/Core/Core/Models/DynamicBase.cs index 9c024df836..8ef2314c20 100644 --- a/Core/Core/Models/DynamicBase.cs +++ b/Core/Core/Models/DynamicBase.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Dynamic; @@ -22,7 +23,7 @@ public class DynamicBase : DynamicObject, IDynamicMetaObjectProvider public const DynamicBaseMemberType DEFAULT_INCLUDE_MEMBERS = DynamicBaseMemberType.Instance | DynamicBaseMemberType.Dynamic; - private static readonly Dictionary> s_propInfoCache = new(); + private static readonly ConcurrentDictionary> s_propInfoCache = new(); /// /// The actual property bag, where dynamically added props are stored. @@ -47,8 +48,7 @@ public object? this[string key] return value; } - PopulatePropInfoCache(GetType()); - var prop = s_propInfoCache[GetType()].FirstOrDefault(p => p.Name == key); + var prop = GetPopulatePropInfoFromCache(GetType()).FirstOrDefault(p => p.Name == key); if (prop == null) { @@ -70,8 +70,7 @@ public object? this[string key] return; } - PopulatePropInfoCache(GetType()); - var prop = s_propInfoCache[GetType()].FirstOrDefault(p => p.Name == key); + var prop = GetPopulatePropInfoFromCache(GetType()).FirstOrDefault(p => p.Name == key); if (prop == null) { @@ -157,15 +156,14 @@ public bool IsPropNameValid(string name, out string reason) return true; } - private static void PopulatePropInfoCache(Type type) - { - if (!s_propInfoCache.ContainsKey(type)) - { - s_propInfoCache[type] = type.GetProperties(BindingFlags.Instance | BindingFlags.Public) - .Where(p => !p.IsDefined(typeof(IgnoreTheItemAttribute), true)) - .ToList(); - } - } + private static List GetPopulatePropInfoFromCache(Type type) => + s_propInfoCache.GetOrAdd( + type, + t => + t.GetProperties(BindingFlags.Instance | BindingFlags.Public) + .Where(p => !p.IsDefined(typeof(IgnoreTheItemAttribute), true)) + .ToList() + ); /// /// Gets all of the property names on this class, dynamic or not. @@ -173,8 +171,7 @@ private static void PopulatePropInfoCache(Type type) [Obsolete("Use `GetMembers(DynamicBaseMemberType.All).Keys` instead")] public override IEnumerable GetDynamicMemberNames() { - PopulatePropInfoCache(GetType()); - var pinfos = s_propInfoCache[GetType()]; + var pinfos = GetPopulatePropInfoFromCache(GetType()); var names = new List(_properties.Count + pinfos.Count); foreach (var pinfo in pinfos) @@ -202,8 +199,7 @@ public IEnumerable GetInstanceMembersNames() public static IEnumerable GetInstanceMembersNames(Type t) { - PopulatePropInfoCache(t); - var pinfos = s_propInfoCache[t]; + var pinfos = GetPopulatePropInfoFromCache(t); var names = new List(pinfos.Count); foreach (var pinfo in pinfos) @@ -225,8 +221,7 @@ public IEnumerable GetInstanceMembers() public static IEnumerable GetInstanceMembers(Type t) { - PopulatePropInfoCache(t); - var pinfos = s_propInfoCache[t]; + var pinfos = GetPopulatePropInfoFromCache(t); var names = new List(pinfos.Count); @@ -269,20 +264,20 @@ public IEnumerable GetMemberNames() if (includeMembers.HasFlag(DynamicBaseMemberType.Instance)) { - PopulatePropInfoCache(GetType()); - var pinfos = s_propInfoCache[GetType()].Where(x => - { - var hasIgnored = x.IsDefined(typeof(SchemaIgnore), true); - var hasObsolete = x.IsDefined(typeof(ObsoleteAttribute), true); - - // If obsolete is false and prop has obsolete attr - // OR - // If schemaIgnored is true and prop has schemaIgnore attr - return !( - !includeMembers.HasFlag(DynamicBaseMemberType.SchemaIgnored) && hasIgnored - || !includeMembers.HasFlag(DynamicBaseMemberType.Obsolete) && hasObsolete - ); - }); + var pinfos = GetPopulatePropInfoFromCache(GetType()) + .Where(x => + { + var hasIgnored = x.IsDefined(typeof(SchemaIgnore), true); + var hasObsolete = x.IsDefined(typeof(ObsoleteAttribute), true); + + // If obsolete is false and prop has obsolete attr + // OR + // If schemaIgnored is true and prop has schemaIgnore attr + return !( + !includeMembers.HasFlag(DynamicBaseMemberType.SchemaIgnored) && hasIgnored + || !includeMembers.HasFlag(DynamicBaseMemberType.Obsolete) && hasObsolete + ); + }); foreach (var pi in pinfos) { if (!dic.ContainsKey(pi.Name)) //todo This is a TEMP FIX FOR #1969, and should be reverted after a proper fix is made! diff --git a/Core/Core/Serialisation/SerializationUtilities/ValueConverter.cs b/Core/Core/Serialisation/SerializationUtilities/ValueConverter.cs index f648f3274f..70246a1363 100644 --- a/Core/Core/Serialisation/SerializationUtilities/ValueConverter.cs +++ b/Core/Core/Serialisation/SerializationUtilities/ValueConverter.cs @@ -1,6 +1,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Diagnostics.Contracts; using System.DoubleNumerics; using System.Drawing; using System.Globalization; @@ -157,16 +158,17 @@ public static bool ConvertValue(Type type, object? value, out object? convertedV #endregion } - // Handle List - if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>)) + // Handle List<>, IList<>, and IReadOnlyList<> + if (type.IsGenericType && IsGenericList(type)) { if (value is not List valueList) { return false; } + var targetType = typeof(List<>).MakeGenericType(type.GenericTypeArguments); Type listElementType = type.GenericTypeArguments[0]; - IList ret = Activator.CreateInstance(type, valueList.Count) as IList; + IList ret = Activator.CreateInstance(targetType, valueList.Count) as IList; foreach (object inputListElement in valueList) { if (!ConvertValue(listElementType, inputListElement, out object? convertedListElement)) @@ -311,4 +313,21 @@ public static bool ConvertValue(Type type, object? value, out object? convertedV return false; } + + /// + /// Tests that the given is assignable from a generic type def + /// + /// + /// + [Pure] + private static bool IsGenericList(Type type) + { + if (!type.IsGenericType) + { + return false; + } + + Type typeDef = type.GetGenericTypeDefinition(); + return typeDef == typeof(List<>) || typeDef == typeof(IList<>) || typeDef == typeof(IReadOnlyList<>); + } } diff --git a/Core/Core/SharpResources.cs b/Core/Core/SharpResources.cs new file mode 100644 index 0000000000..3a7998cfa2 --- /dev/null +++ b/Core/Core/SharpResources.cs @@ -0,0 +1,6 @@ +#if !NET5_0_OR_GREATER +namespace System.Runtime.CompilerServices; + +internal static class IsExternalInit { } + +#endif diff --git a/Core/Core/Transports/Server.cs b/Core/Core/Transports/Server.cs index e92d238343..2c2eabb991 100644 --- a/Core/Core/Transports/Server.cs +++ b/Core/Core/Transports/Server.cs @@ -198,7 +198,7 @@ private void WriteTimerElapsed(object sender, ElapsedEventArgs e) return; } - if (_totalElapsed > 300 && _isWriting == false && _queue.Count != 0) + if (_totalElapsed > 300 && !_isWriting && _queue.Count != 0) { _totalElapsed = 0; _writeTimer.Enabled = false; diff --git a/Core/Core/Transports/ServerUtils/ServerAPI.cs b/Core/Core/Transports/ServerUtils/ServerAPI.cs index d6a32a5412..09912368cf 100644 --- a/Core/Core/Transports/ServerUtils/ServerAPI.cs +++ b/Core/Core/Transports/ServerUtils/ServerAPI.cs @@ -27,8 +27,6 @@ public sealed class ServerApi : IDisposable, IServerApi private const int MAX_REQUEST_SIZE = 100_000_000; - private const int RETRY_COUNT = 3; - private static readonly HashSet s_retryCodes = new() { 408, 502, 503, 504 }; private static readonly char[] s_separator = { '\t' }; private static readonly string[] s_filenameSeparator = { "filename=" }; @@ -77,13 +75,9 @@ public async Task DownloadSingleObject(string streamId, string objectId) Method = HttpMethod.Get }; - HttpResponseMessage rootHttpResponse; - do - { - rootHttpResponse = await _client - .SendAsync(rootHttpMessage, HttpCompletionOption.ResponseContentRead, CancellationToken) - .ConfigureAwait(false); - } while (ShouldRetry(rootHttpResponse)); + HttpResponseMessage rootHttpResponse = await _client + .SendAsync(rootHttpMessage, HttpCompletionOption.ResponseContentRead, CancellationToken) + .ConfigureAwait(false); rootHttpResponse.EnsureSuccessStatusCode(); @@ -256,11 +250,7 @@ public async Task UploadBlobs(string streamId, IReadOnlyList<(string, string)> o try { - HttpResponseMessage response; - do - { - response = await _client.SendAsync(message, CancellationToken).ConfigureAwait(false); - } while (ShouldRetry(response)); //TODO: can we get rid of this now we have polly? + HttpResponseMessage response = await _client.SendAsync(message, CancellationToken).ConfigureAwait(false); response.EnsureSuccessStatusCode(); @@ -335,13 +325,9 @@ CbObjectDownloaded onObjectCallback childrenHttpMessage.Content = new StringContent(serializedPayload, Encoding.UTF8, "application/json"); childrenHttpMessage.Headers.Add("Accept", "text/plain"); - HttpResponseMessage childrenHttpResponse; - do - { - childrenHttpResponse = await _client - .SendAsync(childrenHttpMessage, HttpCompletionOption.ResponseHeadersRead, CancellationToken) - .ConfigureAwait(false); - } while (ShouldRetry(childrenHttpResponse)); + HttpResponseMessage childrenHttpResponse = await _client + .SendAsync(childrenHttpMessage, HttpCompletionOption.ResponseHeadersRead, CancellationToken) + .ConfigureAwait(false); childrenHttpResponse.EnsureSuccessStatusCode(); @@ -370,12 +356,8 @@ private async Task> HasObjectsImpl(string streamId, IRe string serializedPayload = JsonConvert.SerializeObject(payload); var uri = new Uri($"/api/diff/{streamId}", UriKind.Relative); - HttpResponseMessage response; using StringContent stringContent = new(serializedPayload, Encoding.UTF8, "application/json"); - do - { - response = await _client.PostAsync(uri, stringContent, CancellationToken).ConfigureAwait(false); - } while (ShouldRetry(response)); + HttpResponseMessage response = await _client.PostAsync(uri, stringContent, CancellationToken).ConfigureAwait(false); response.EnsureSuccessStatusCode(); @@ -434,11 +416,7 @@ private async Task UploadObjectsImpl(string streamId, List> HasBlobs(string streamId, IReadOnlyList using StringContent stringContent = new(payload, Encoding.UTF8, "application/json"); - //TODO: can we get rid of this now we have polly? - HttpResponseMessage response; - do - { - response = await _client.PostAsync(uri, stringContent, CancellationToken).ConfigureAwait(false); - } while (ShouldRetry(response)); + HttpResponseMessage response = await _client.PostAsync(uri, stringContent, CancellationToken).ConfigureAwait(false); response.EnsureSuccessStatusCode(); @@ -473,28 +446,6 @@ public async Task> HasBlobs(string streamId, IReadOnlyList return parsed; } - //TODO: can we get rid of this now we have polly? - private bool ShouldRetry(HttpResponseMessage? serverResponse) - { - if (serverResponse == null) - { - return true; - } - - if (!s_retryCodes.Contains((int)serverResponse.StatusCode)) - { - return false; - } - - if (RetriedCount >= RETRY_COUNT) - { - return false; - } - - RetriedCount += 1; - return true; - } - private sealed class BlobUploadResult { public List uploadResults { get; set; } diff --git a/Core/Tests/Speckle.Core.Tests.Integration/Api.cs b/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Legacy/LegacyAPITests.cs similarity index 98% rename from Core/Tests/Speckle.Core.Tests.Integration/Api.cs rename to Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Legacy/LegacyAPITests.cs index bc3b574fcd..46b87d799d 100644 --- a/Core/Tests/Speckle.Core.Tests.Integration/Api.cs +++ b/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Legacy/LegacyAPITests.cs @@ -1,12 +1,13 @@ using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL; using Speckle.Core.Credentials; using Speckle.Core.Models; using Speckle.Core.Tests.Unit.Kits; using Speckle.Core.Transports; -namespace Speckle.Core.Tests.Integration; +namespace Speckle.Core.Tests.Integration.Api.GraphQL.Legacy; -public class Api : IDisposable +public class LegacyAPITests : IDisposable { private string _branchId = ""; private string _branchName = ""; @@ -177,7 +178,7 @@ public async Task StreamUpdatePermission() var res = await _myClient.StreamUpdatePermission( new StreamPermissionInput { - role = "stream:reviewer", + role = StreamRoles.STREAM_REVIEWER, streamId = _streamId, userId = _secondUserAccount.userInfo.id } diff --git a/Core/Tests/Speckle.Core.Tests.Integration/Subscriptions/Branches.cs b/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Legacy/Subscriptions/Branches.cs similarity index 97% rename from Core/Tests/Speckle.Core.Tests.Integration/Subscriptions/Branches.cs rename to Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Legacy/Subscriptions/Branches.cs index dce806731c..2833a279c1 100644 --- a/Core/Tests/Speckle.Core.Tests.Integration/Subscriptions/Branches.cs +++ b/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Legacy/Subscriptions/Branches.cs @@ -2,7 +2,7 @@ using Speckle.Core.Api.SubscriptionModels; using Speckle.Core.Credentials; -namespace Speckle.Core.Tests.Integration.Subscriptions; +namespace Speckle.Core.Tests.Integration.Api.GraphQL.Legacy.Subscriptions; public class Branches : IDisposable { diff --git a/Core/Tests/Speckle.Core.Tests.Integration/Subscriptions/Commits.cs b/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Legacy/Subscriptions/Commits.cs similarity index 98% rename from Core/Tests/Speckle.Core.Tests.Integration/Subscriptions/Commits.cs rename to Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Legacy/Subscriptions/Commits.cs index ed10de7b4c..7e25fad641 100644 --- a/Core/Tests/Speckle.Core.Tests.Integration/Subscriptions/Commits.cs +++ b/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Legacy/Subscriptions/Commits.cs @@ -5,7 +5,7 @@ using Speckle.Core.Tests.Unit.Kits; using Speckle.Core.Transports; -namespace Speckle.Core.Tests.Integration.Subscriptions; +namespace Speckle.Core.Tests.Integration.Api.GraphQL.Legacy.Subscriptions; public class Commits : IDisposable { diff --git a/Core/Tests/Speckle.Core.Tests.Integration/Subscriptions/Streams.cs b/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Legacy/Subscriptions/Streams.cs similarity index 97% rename from Core/Tests/Speckle.Core.Tests.Integration/Subscriptions/Streams.cs rename to Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Legacy/Subscriptions/Streams.cs index 5acd4d71c9..457c2d6b41 100644 --- a/Core/Tests/Speckle.Core.Tests.Integration/Subscriptions/Streams.cs +++ b/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Legacy/Subscriptions/Streams.cs @@ -2,7 +2,7 @@ using Speckle.Core.Api.SubscriptionModels; using Speckle.Core.Credentials; -namespace Speckle.Core.Tests.Integration.Subscriptions; +namespace Speckle.Core.Tests.Integration.Api.GraphQL.Legacy.Subscriptions; public class Streams : IDisposable { diff --git a/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ActiveUserResourceTests.cs b/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ActiveUserResourceTests.cs new file mode 100644 index 0000000000..1b2e810fa2 --- /dev/null +++ b/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ActiveUserResourceTests.cs @@ -0,0 +1,51 @@ +using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Resources; + +namespace Speckle.Core.Tests.Integration.API.GraphQL.Resources; + +[TestOf(typeof(ActiveUserResource))] +public class ActiveUserResourceTests +{ + private Client _testUser; + private ActiveUserResource Sut => _testUser.ActiveUser; + + [OneTimeSetUp] + public async Task Setup() + { + _testUser = await Fixtures.SeedUserWithClient(); + } + + [Test] + public async Task ActiveUserGet() + { + var res = await Sut.Get(); + Assert.That(res.id, Is.EqualTo(_testUser.Account.userInfo.id)); + } + + [Test] + public async Task ActiveUserGet_NonAuthed() + { + var result = await Fixtures.Unauthed.ActiveUser.Get(); + Assert.That(result, Is.EqualTo(null)); + } + + [Test] + public async Task ActiveUserGetProjects() + { + var p1 = await _testUser.Project.Create(new("Project 1", null, null)); + var p2 = await _testUser.Project.Create(new("Project 2", null, null)); + + var res = await Sut.GetProjects(); + + Assert.That(res.items, Has.Exactly(1).Items.With.Property(nameof(Project.id)).EqualTo(p1.id)); + Assert.That(res.items, Has.Exactly(1).Items.With.Property(nameof(Project.id)).EqualTo(p2.id)); + Assert.That(res.items, Has.Count.EqualTo(2)); + } + + [Test] + public void ActiveUserGetProjects_NoAuth() + { + Assert.ThrowsAsync(async () => await Fixtures.Unauthed.ActiveUser.GetProjects()); + } +} diff --git a/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/CommentResourceTests.cs b/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/CommentResourceTests.cs new file mode 100644 index 0000000000..cdf25948ea --- /dev/null +++ b/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/CommentResourceTests.cs @@ -0,0 +1,97 @@ +using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL.Inputs; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Resources; + +namespace Speckle.Core.Tests.Integration.API.GraphQL.Resources; + +[TestOf(typeof(CommentResource))] +public class CommentResourceTests +{ + private Client _testUser; + private CommentResource Sut => _testUser.Comment; + private Project _project; + private Model _model; + private string _versionId; + private Comment _comment; + + [SetUp] + public async Task Setup() + { + _testUser = await Fixtures.SeedUserWithClient(); + _project = await _testUser.Project.Create(new("Test project", "", null)); + _model = await _testUser.Model.Create(new("Test Model 1", "", _project.id)); + _versionId = await Fixtures.CreateVersion(_testUser, _project.id, _model.name); + _comment = await CreateComment(); + } + + [Test] + public async Task GetProjectComments() + { + var comments = await Sut.GetProjectComments(_project.id); + Assert.That(comments.items.Count, Is.EqualTo(1)); + Assert.That(comments.totalCount, Is.EqualTo(1)); + + Comment comment = comments.items[0]; + Assert.That(comment, Is.Not.Null); + Assert.That(comment, Has.Property(nameof(Comment.authorId)).EqualTo(_testUser.Account.userInfo.id)); + + Assert.That(comment, Has.Property(nameof(Comment.id)).EqualTo(_comment.id)); + Assert.That(comment, Has.Property(nameof(Comment.authorId)).EqualTo(_comment.authorId)); + Assert.That(comment, Has.Property(nameof(Comment.archived)).EqualTo(_comment.archived)); + Assert.That(comment, Has.Property(nameof(Comment.archived)).EqualTo(false)); + Assert.That(comment, Has.Property(nameof(Comment.createdAt)).EqualTo(_comment.createdAt)); + } + + [Test] + public async Task MarkViewed() + { + var viewed = await Sut.MarkViewed(_comment.id); + Assert.That(viewed, Is.True); + viewed = await Sut.MarkViewed(_comment.id); + Assert.That(viewed, Is.True); + } + + [Test] + public async Task Archive() + { + var archived = await Sut.Archive(_comment.id); + Assert.That(archived, Is.True); + + archived = await Sut.Archive(_comment.id); + Assert.That(archived, Is.True); + } + + [Test] + public async Task Edit() + { + var blobs = await Fixtures.SendBlobData(_testUser.Account, _project.id); + var blobIds = blobs.Select(b => b.id).ToList(); + EditCommentInput input = new(new(blobIds, null), _comment.id); + + var editedComment = await Sut.Edit(input); + + Assert.That(editedComment, Is.Not.Null); + Assert.That(editedComment, Has.Property(nameof(Comment.id)).EqualTo(_comment.id)); + Assert.That(editedComment, Has.Property(nameof(Comment.authorId)).EqualTo(_comment.authorId)); + Assert.That(editedComment, Has.Property(nameof(Comment.createdAt)).EqualTo(_comment.createdAt)); + Assert.That(editedComment, Has.Property(nameof(Comment.updatedAt)).GreaterThanOrEqualTo(_comment.updatedAt)); + } + + [Test] + public async Task Reply() + { + var blobs = await Fixtures.SendBlobData(_testUser.Account, _project.id); + var blobIds = blobs.Select(b => b.id).ToList(); + CreateCommentReplyInput input = new(new(blobIds, null), _comment.id); + + var editedComment = await Sut.Reply(input); + + Assert.That(editedComment, Is.Not.Null); + } + + private async Task CreateComment() + { + return await Fixtures.CreateComment(_testUser, _project.id, _model.id, _versionId); + } +} diff --git a/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ModelResourceExceptionalTests.cs b/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ModelResourceExceptionalTests.cs new file mode 100644 index 0000000000..8fe43ba10c --- /dev/null +++ b/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ModelResourceExceptionalTests.cs @@ -0,0 +1,88 @@ +using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL.Enums; +using Speckle.Core.Api.GraphQL.Inputs; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Resources; + +namespace Speckle.Core.Tests.Integration.API.GraphQL.Resources; + +[TestOf(typeof(ModelResource))] +public class ModelResourceExceptionalTests +{ + private Client _testUser; + private ModelResource Sut => _testUser.Model; + private Project _project; + private Model _model; + + [OneTimeSetUp] + public async Task Setup() + { + _testUser = await Fixtures.SeedUserWithClient(); + _project = await _testUser.Project.Create(new("Test project", "", ProjectVisibility.Private)); + _model = await _testUser.Model.Create(new("Test Model", "", _project.id)); + } + + [TestCase(null)] + [TestCase("")] + [TestCase(" ")] + public void ModelCreate_Throws_InvalidInput(string name) + { + CreateModelInput input = new(name, null, _project.id); + Assert.CatchAsync(async () => await Sut.Create(input)); + } + + [Test] + public void ModelGet_Throws_NoAuth() + { + Assert.CatchAsync(async () => await Fixtures.Unauthed.Model.Get(_model.id, _project.id)); + } + + [Test] + public void ModelGet_Throws_NonExistentModel() + { + Assert.CatchAsync(async () => await Sut.Get("non existent model", _project.id)); + } + + [Test] + public void ModelGet_Throws_NonExistentProject() + { + Assert.ThrowsAsync( + async () => await Sut.Get(_model.id, "non existent project") + ); + } + + [Test] + public void ModelUpdate_Throws_NonExistentModel() + { + UpdateModelInput input = new("non-existent model", "MY new name", "MY new desc", _project.id); + + Assert.CatchAsync(async () => await Sut.Update(input)); + } + + [Test] + public void ModelUpdate_Throws_NonExistentProject() + { + UpdateModelInput input = new(_model.id, "MY new name", "MY new desc", "non-existent project"); + + Assert.ThrowsAsync(async () => await Sut.Update(input)); + } + + [Test] + public void ModelUpdate_Throws_NonAuthProject() + { + UpdateModelInput input = new(_model.id, "MY new name", "MY new desc", _project.id); + + Assert.CatchAsync(async () => await Fixtures.Unauthed.Model.Update(input)); + } + + [Test] + public async Task ModelDelete_Throws_NoAuth() + { + Model toDelete = await Sut.Create(new("Delete me", null, _project.id)); + DeleteModelInput input = new(toDelete.id, _project.id); + bool response = await Sut.Delete(input); + Assert.That(response, Is.True); + + Assert.CatchAsync(async () => _ = await Sut.Delete(input)); + } +} diff --git a/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ModelResourceTests.cs b/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ModelResourceTests.cs new file mode 100644 index 0000000000..e2995acf29 --- /dev/null +++ b/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ModelResourceTests.cs @@ -0,0 +1,96 @@ +using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL.Inputs; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Resources; + +namespace Speckle.Core.Tests.Integration.API.GraphQL.Resources; + +[TestOf(typeof(ModelResource))] +public class ModelResourceTests +{ + private Client _testUser; + private ModelResource Sut => _testUser.Model; + private Project _project; + private Model _model; + + [SetUp] + public async Task Setup() + { + _testUser = await Fixtures.SeedUserWithClient(); + _project = await _testUser.Project.Create(new("Test project", "", null)); + _model = await _testUser.Model.Create(new("Test Model", "", _project.id)); + } + + [TestCase("My Model", "My model description")] + [TestCase("my/nested/model", null)] + public async Task ModelCreate(string name, string description) + { + CreateModelInput input = new(name, description, _project.id); + Model result = await Sut.Create(input); + + Assert.That(result, Is.Not.Null); + Assert.That(result, Has.Property(nameof(result.id)).Not.Null); + Assert.That(result, Has.Property(nameof(result.name)).EqualTo(input.name).IgnoreCase); + Assert.That(result, Has.Property(nameof(result.description)).EqualTo(input.description)); + } + + [Test] + public async Task ModelGet() + { + Model result = await Sut.Get(_model.id, _project.id); + + Assert.That(result.id, Is.EqualTo(_model.id)); + Assert.That(result.name, Is.EqualTo(_model.name)); + Assert.That(result.description, Is.EqualTo(_model.description)); + Assert.That(result.createdAt, Is.EqualTo(_model.createdAt)); + Assert.That(result.updatedAt, Is.EqualTo(_model.updatedAt)); + } + + [Test] + public async Task GetModels() + { + var result = await Sut.GetModels(_project.id); + + Assert.That(result.items, Has.Count.EqualTo(1)); + Assert.That(result.totalCount, Is.EqualTo(1)); + Assert.That(result.items[0], Has.Property(nameof(Model.id)).EqualTo(_model.id)); + } + + [Test] + public async Task Project_GetModels() + { + var result = await _testUser.Project.GetWithModels(_project.id); + + Assert.That(result, Has.Property(nameof(Project.id)).EqualTo(_project.id)); + Assert.That(result.models.items, Has.Count.EqualTo(1)); + Assert.That(result.models.totalCount, Is.EqualTo(1)); + Assert.That(result.models.items[0], Has.Property(nameof(Model.id)).EqualTo(_model.id)); + } + + [Test] + public async Task ModelUpdate() + { + const string NEW_NAME = "MY new name"; + const string NEW_DESCRIPTION = "MY new desc"; + + UpdateModelInput input = new(_model.id, NEW_NAME, NEW_DESCRIPTION, _project.id); + Model updatedModel = await Sut.Update(input); + + Assert.That(updatedModel.id, Is.EqualTo(_model.id)); + Assert.That(updatedModel.name, Is.EqualTo(NEW_NAME).IgnoreCase); + Assert.That(updatedModel.description, Is.EqualTo(NEW_DESCRIPTION)); + Assert.That(updatedModel.updatedAt, Is.GreaterThanOrEqualTo(_model.updatedAt)); + } + + [Test] + public async Task ModelDelete() + { + DeleteModelInput input = new(_model.id, _project.id); + + bool response = await Sut.Delete(input); + Assert.That(response, Is.True); + + Assert.CatchAsync(async () => _ = await Sut.Get(_model.id, _project.id)); + Assert.CatchAsync(async () => _ = await Sut.Delete(input)); + } +} diff --git a/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/OtherUserResourceTests.cs b/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/OtherUserResourceTests.cs new file mode 100644 index 0000000000..9959131e88 --- /dev/null +++ b/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/OtherUserResourceTests.cs @@ -0,0 +1,49 @@ +using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL.Resources; +using Speckle.Core.Credentials; + +namespace Speckle.Core.Tests.Integration.API.GraphQL.Resources; + +[TestOf(typeof(OtherUserResource))] +public class OtherUserResourceTests +{ + private Client _testUser; + private Account _testData; + private OtherUserResource Sut => _testUser.OtherUser; + + [OneTimeSetUp] + public async Task Setup() + { + _testUser = await Fixtures.SeedUserWithClient(); + _testData = await Fixtures.SeedUser(); + } + + [Test] + public async Task OtherUserGet() + { + var res = await Sut.Get(_testData.userInfo.id); + Assert.That(res.name, Is.EqualTo(_testData.userInfo.name)); + } + + [Test] + public async Task OtherUserGet_NonExistentUser() + { + var result = await Sut.Get("AnIdThatDoesntExist"); + Assert.That(result, Is.Null); + } + + [Test] + public async Task UserSearch() + { + var res = await Sut.UserSearch(_testData.userInfo.email, 25); + Assert.That(res.items, Has.Count.EqualTo(1)); + Assert.That(res.items[0].id, Is.EqualTo(_testData.userInfo.id)); + } + + [Test] + public async Task UserSearch_NonExistentUser() + { + var res = await Sut.UserSearch("idontexist@example.com", 25); + Assert.That(res.items, Has.Count.EqualTo(0)); + } +} diff --git a/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ProjectInviteResourceExceptionalTests.cs b/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ProjectInviteResourceExceptionalTests.cs new file mode 100644 index 0000000000..b2cee38c2a --- /dev/null +++ b/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ProjectInviteResourceExceptionalTests.cs @@ -0,0 +1,32 @@ +using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL.Inputs; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Resources; + +namespace Speckle.Core.Tests.Integration.API.GraphQL.Resources; + +[TestOf(typeof(ProjectInviteResource))] +public class ProjectInviteResourceExceptionalTests +{ + private Client _testUser; + private Project _project; + private ProjectInviteResource Sut => _testUser.ProjectInvite; + + [OneTimeSetUp] + public async Task Setup() + { + _testUser = await Fixtures.SeedUserWithClient(); + _project = await _testUser.Project.Create(new("test", null, null)); + } + + [TestCase(null, null, null, null)] + [TestCase(null, "something", "something", null)] + public void ProjectInviteCreate_InvalidInput(string email, string role, string serverRole, string userId) + { + Assert.CatchAsync(async () => + { + var input = new ProjectInviteCreateInput(email, role, serverRole, userId); + await Sut.Create(_project.id, input); + }); + } +} diff --git a/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ProjectInviteResourceTests.cs b/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ProjectInviteResourceTests.cs new file mode 100644 index 0000000000..f6268db573 --- /dev/null +++ b/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ProjectInviteResourceTests.cs @@ -0,0 +1,107 @@ +using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL; +using Speckle.Core.Api.GraphQL.Inputs; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Resources; + +namespace Speckle.Core.Tests.Integration.API.GraphQL.Resources; + +[TestOf(typeof(ProjectInviteResource))] +public class ProjectInviteResourceTests +{ + private Client _inviter, + _invitee; + private Project _project; + private PendingStreamCollaborator _createdInvite; + + [SetUp] + public async Task Setup() + { + _inviter = await Fixtures.SeedUserWithClient(); + _invitee = await Fixtures.SeedUserWithClient(); + _project = await _inviter.Project.Create(new("test", null, null)); + _createdInvite = await SeedInvite(); + } + + private async Task SeedInvite() + { + ProjectInviteCreateInput input = new(_invitee.Account.userInfo.email, null, null, null); + var res = await _inviter.ProjectInvite.Create(_project.id, input); + var invites = await _invitee.ActiveUser.ProjectInvites(); + return invites.First(i => i.projectId == res.id); + } + + [Test] + public async Task ProjectInviteCreate_ByEmail() + { + ProjectInviteCreateInput input = new(_invitee.Account.userInfo.email, null, null, null); + var res = await _inviter.ProjectInvite.Create(_project.id, input); + + var invites = await _invitee.ActiveUser.ProjectInvites(); + var invite = invites.First(i => i.projectId == res.id); + + Assert.That(res, Has.Property(nameof(_project.id)).EqualTo(_project.id)); + Assert.That(res.invitedTeam, Has.Count.EqualTo(1)); + Assert.That(invite.user.id, Is.EqualTo(_invitee.Account.userInfo.id)); + Assert.That(invite.token, Is.Not.Null); + } + + [Test] + public async Task ProjectInviteCreate_ByUserId() + { + ProjectInviteCreateInput input = new(null, null, null, _invitee.Account.userInfo.id); + var res = await _inviter.ProjectInvite.Create(_project.id, input); + + Assert.That(res, Has.Property(nameof(_project.id)).EqualTo(_project.id)); + Assert.That(res.invitedTeam, Has.Count.EqualTo(1)); + Assert.That(res.invitedTeam[0].user.id, Is.EqualTo(_invitee.Account.userInfo.id)); + } + + [Test] + public async Task ProjectInviteGet() + { + var collaborator = await _invitee.ProjectInvite.Get(_project.id, _createdInvite.token); + + Assert.That( + collaborator, + Has.Property(nameof(PendingStreamCollaborator.inviteId)).EqualTo(_createdInvite.inviteId) + ); + Assert.That(collaborator.user.id, Is.EqualTo(_createdInvite.user.id)); + } + + [Test] + public async Task ProjectInviteUse_MemberAdded() + { + ProjectInviteUseInput input = new(true, _createdInvite.projectId, _createdInvite.token); + var res = await _invitee.ProjectInvite.Use(input); + Assert.That(res, Is.True); + + var project = await _inviter.Project.GetWithTeam(_project.id); + var teamMembers = project.team.Select(c => c.user.id); + var expectedTeamMembers = new[] { _inviter.Account.userInfo.id, _invitee.Account.userInfo.id }; + Assert.That(teamMembers, Is.EquivalentTo(expectedTeamMembers)); + } + + [Test] + public async Task ProjectInviteCancel_MemberNotAdded() + { + var res = await _inviter.ProjectInvite.Cancel(_createdInvite.projectId, _createdInvite.inviteId); + + Assert.That(res.invitedTeam, Is.Empty); + } + + [Test] + [TestCase(StreamRoles.STREAM_OWNER)] + [TestCase(StreamRoles.STREAM_REVIEWER)] + [TestCase(StreamRoles.STREAM_CONTRIBUTOR)] + [TestCase(StreamRoles.REVOKE)] + public async Task ProjectUpdateRole(string newRole) + { + await ProjectInviteUse_MemberAdded(); + ProjectUpdateRoleInput input = new(_invitee.Account.userInfo.id, _project.id, newRole); + _ = await _inviter.Project.UpdateRole(input); + + Project finalProject = await _invitee.Project.Get(_project.id); + Assert.That(finalProject.role, Is.EqualTo(newRole)); + } +} diff --git a/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ProjectResourceExceptionalTests.cs b/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ProjectResourceExceptionalTests.cs new file mode 100644 index 0000000000..47222b761f --- /dev/null +++ b/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ProjectResourceExceptionalTests.cs @@ -0,0 +1,113 @@ +using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL; +using Speckle.Core.Api.GraphQL.Enums; +using Speckle.Core.Api.GraphQL.Inputs; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Resources; + +namespace Speckle.Core.Tests.Integration.API.GraphQL.Resources; + +[TestOf(typeof(ProjectResource))] +public class ProjectResourceExceptionalTests +{ + private Client _testUser, + _secondUser, + _unauthedUser; + private Project _testProject; + private ProjectResource Sut => _testUser.Project; + + [OneTimeSetUp] + public async Task Setup() + { + _testUser = await Fixtures.SeedUserWithClient(); + _secondUser = await Fixtures.SeedUserWithClient(); + _unauthedUser = Fixtures.Unauthed; + _testProject = await _testUser.Project.Create(new("test project123", "desc", null)); + } + + //We want to check the following cases + // 1. User lacks permissions (without auth) + // 2. Target (Project or user) doesn't exist) + // 3. Cancellation + // 4. Server doesn't exist (is down) + //There's got to be a smarter way to parametrise these... + + [Test] + public void ProjectCreate_WithoutAuth() + { + ProjectCreateInput input = + new("The best project", "The best description for the best project", ProjectVisibility.Private); + + Assert.ThrowsAsync(async () => await _unauthedUser.Project.Create(input)); + } + + [Test] + public async Task ProjectGet_WithoutAuth() + { + ProjectCreateInput input = new("Private Stream", "A very private stream", ProjectVisibility.Private); + + Project privateStream = await Sut.Create(input); + + Assert.ThrowsAsync(async () => await _unauthedUser.Project.Get(privateStream.id)); + } + + [Test] + public void ProjectGet_NonExistentProject() + { + Assert.ThrowsAsync(async () => await Sut.Get("NonExistentProject")); + } + + [Test] + public void ProjectUpdate_NonExistentProject() + { + Assert.ThrowsAsync( + async () => _ = await Sut.Update(new("NonExistentProject", "My new name")) + ); + } + + [Test] + public void ProjectUpdate_NoAuth() + { + Assert.ThrowsAsync( + async () => _ = await _unauthedUser.Project.Update(new(_testProject.id, "My new name")) + ); + } + + [Test] + [TestCase(StreamRoles.STREAM_OWNER)] + [TestCase(StreamRoles.STREAM_CONTRIBUTOR)] + [TestCase(StreamRoles.STREAM_REVIEWER)] + [TestCase(StreamRoles.REVOKE)] + public void ProjectUpdateRole_NonExistentProject(string newRole) + { + ProjectUpdateRoleInput input = new(_secondUser.Account.id, "NonExistentProject", newRole); + + Assert.ThrowsAsync(async () => await Sut.UpdateRole(input)); + } + + [Test] + [TestCase(StreamRoles.STREAM_OWNER)] + [TestCase(StreamRoles.STREAM_CONTRIBUTOR)] + [TestCase(StreamRoles.STREAM_REVIEWER)] + [TestCase(StreamRoles.REVOKE)] + public void ProjectUpdateRole_NonAuth(string newRole) + { + ProjectUpdateRoleInput input = new(_secondUser.Account.id, "NonExistentProject", newRole); + Assert.ThrowsAsync(async () => await _unauthedUser.Project.UpdateRole(input)); + } + + [Test] + public async Task ProjectDelete_NonExistentProject() + { + bool response = await Sut.Delete(_testProject.id); + Assert.That(response, Is.True); + + Assert.ThrowsAsync(async () => _ = await Sut.Get(_testProject.id)); //TODO: Exception types + } + + [Test] + public void ProjectInvites_NoAuth() + { + Assert.ThrowsAsync(async () => await Fixtures.Unauthed.ActiveUser.ProjectInvites()); + } +} diff --git a/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ProjectResourceTests.cs b/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ProjectResourceTests.cs new file mode 100644 index 0000000000..66dcba0a1e --- /dev/null +++ b/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ProjectResourceTests.cs @@ -0,0 +1,72 @@ +using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL.Enums; +using Speckle.Core.Api.GraphQL.Inputs; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Resources; + +namespace Speckle.Core.Tests.Integration.API.GraphQL.Resources; + +[TestOf(typeof(ProjectResource))] +public class ProjectResourceTests +{ + private Client _testUser; + private Project _testProject; + private ProjectResource Sut => _testUser.Project; + + [OneTimeSetUp] + public async Task Setup() + { + _testUser = await Fixtures.SeedUserWithClient(); + _testProject = await _testUser.Project.Create(new("test project123", "desc", null)); + } + + [TestCase("Very private project", "My secret project", ProjectVisibility.Private)] + [TestCase("Very public project", null, ProjectVisibility.Public)] + public async Task ProjectCreate(string name, string desc, ProjectVisibility visibility) + { + ProjectCreateInput input = new(name, desc, visibility); + Project result = await Sut.Create(input); + Assert.That(result, Is.Not.Null); + Assert.That(result, Has.Property(nameof(Project.id)).Not.Null); + Assert.That(result, Has.Property(nameof(Project.name)).EqualTo(input.name)); + Assert.That(result, Has.Property(nameof(Project.description)).EqualTo(input.description ?? string.Empty)); + Assert.That(result, Has.Property(nameof(Project.visibility)).EqualTo(input.visibility)); + } + + [Test] + public async Task ProjectGet() + { + Project result = await Sut.Get(_testProject.id); + + Assert.That(result.id, Is.EqualTo(_testProject.id)); + Assert.That(result.name, Is.EqualTo(_testProject.name)); + Assert.That(result.description, Is.EqualTo(_testProject.description)); + Assert.That(result.visibility, Is.EqualTo(_testProject.visibility)); + Assert.That(result.createdAt, Is.EqualTo(_testProject.createdAt)); + } + + [Test] + public async Task ProjectUpdate() + { + const string NEW_NAME = "MY new name"; + const string NEW_DESCRIPTION = "MY new desc"; + const ProjectVisibility NEW_VISIBILITY = ProjectVisibility.Public; + + Project newProject = await Sut.Update(new(_testProject.id, NEW_NAME, NEW_DESCRIPTION, null, NEW_VISIBILITY)); + + Assert.That(newProject.id, Is.EqualTo(_testProject.id)); + Assert.That(newProject.name, Is.EqualTo(NEW_NAME)); + Assert.That(newProject.description, Is.EqualTo(NEW_DESCRIPTION)); + Assert.That(newProject.visibility, Is.EqualTo(NEW_VISIBILITY)); + } + + [Test] + public async Task ProjectDelete() + { + Project toDelete = await Sut.Create(new("Delete me", null, null)); + bool response = await Sut.Delete(toDelete.id); + Assert.That(response, Is.True); + + Assert.ThrowsAsync(async () => _ = await Sut.Get(toDelete.id)); + } +} diff --git a/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/SubscriptionResourceTests.cs b/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/SubscriptionResourceTests.cs new file mode 100644 index 0000000000..6e1e845c84 --- /dev/null +++ b/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/SubscriptionResourceTests.cs @@ -0,0 +1,120 @@ +using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL.Inputs; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Resources; + +namespace Speckle.Core.Tests.Integration.API.GraphQL.Resources; + +[TestOf(typeof(SubscriptionResource))] +public class SubscriptionResourceTests +{ + private const int WAIT_PERIOD = 300; + private Client _testUser; + private Project _testProject; + private Model _testModel; + private string _testVersion; + + private SubscriptionResource Sut => _testUser.Subscription; + + [OneTimeSetUp] + public async Task Setup() + { + _testUser = await Fixtures.SeedUserWithClient(); + _testProject = await _testUser.Project.Create(new("test project123", "desc", null)); + _testModel = await _testUser.Model.Create(new("test model", "desc", _testProject.id)); + _testVersion = await Fixtures.CreateVersion(_testUser, _testProject.id, _testModel.name); + } + + [Test] + public async Task UserProjectsUpdated_SubscriptionIsCalled() + { + UserProjectsUpdatedMessage subscriptionMessage = null; + + using var sub = Sut.CreateUserProjectsUpdatedSubscription(); + sub.Listeners += (_, message) => subscriptionMessage = message; + + await Task.Delay(WAIT_PERIOD); // Give time to subscription to be setup + + var created = await _testUser.Project.Create(new(null, null, null)); + + await Task.Delay(WAIT_PERIOD); // Give time for subscription to be triggered + + Assert.That(subscriptionMessage, Is.Not.Null); + Assert.That(subscriptionMessage.id, Is.EqualTo(created.id)); + } + + [Test] + public async Task ProjectModelsUpdated_SubscriptionIsCalled() + { + ProjectModelsUpdatedMessage subscriptionMessage = null; + + using var sub = Sut.CreateProjectModelsUpdatedSubscription(_testProject.id); + sub.Listeners += (_, message) => subscriptionMessage = message; + + await Task.Delay(WAIT_PERIOD); // Give time to subscription to be setup + + CreateModelInput input = new("my model", "myDescription", _testProject.id); + var created = await _testUser.Model.Create(input); + + await Task.Delay(WAIT_PERIOD); // Give time for subscription to be triggered + + Assert.That(subscriptionMessage, Is.Not.Null); + Assert.That(subscriptionMessage.id, Is.EqualTo(created.id)); + } + + [Test] + public async Task ProjectUpdated_SubscriptionIsCalled() + { + ProjectUpdatedMessage subscriptionMessage = null; + + using var sub = Sut.CreateProjectUpdatedSubscription(_testProject.id); + sub.Listeners += (_, message) => subscriptionMessage = message; + + await Task.Delay(WAIT_PERIOD); // Give time to subscription to be setup + + var input = new ProjectUpdateInput(_testProject.id, "This is my new name"); + var created = await _testUser.Project.Update(input); + + await Task.Delay(WAIT_PERIOD); // Give time for subscription to be triggered + + Assert.That(subscriptionMessage, Is.Not.Null); + Assert.That(subscriptionMessage.id, Is.EqualTo(created.id)); + } + + [Test] + public async Task ProjectVersionsUpdated_SubscriptionIsCalled() + { + ProjectVersionsUpdatedMessage subscriptionMessage = null; + + using var sub = Sut.CreateProjectVersionsUpdatedSubscription(_testProject.id); + sub.Listeners += (_, message) => subscriptionMessage = message; + + await Task.Delay(WAIT_PERIOD); // Give time to subscription to be setup + + var created = await Fixtures.CreateVersion(_testUser, _testProject.id, _testModel.name); + + await Task.Delay(WAIT_PERIOD); // Give time for subscription to be triggered + + Assert.That(subscriptionMessage, Is.Not.Null); + Assert.That(subscriptionMessage.id, Is.EqualTo(created)); + } + + [Test] + public async Task ProjectCommentsUpdated_SubscriptionIsCalled() + { + string resourceIdString = $"{_testProject.id},{_testModel.id},{_testVersion}"; + ProjectCommentsUpdatedMessage subscriptionMessage = null; + + using var sub = Sut.CreateProjectCommentsUpdatedSubscription(new(_testProject.id, resourceIdString)); + sub.Listeners += (_, message) => subscriptionMessage = message; + + await Task.Delay(WAIT_PERIOD); // Give time to subscription to be setup + + var created = await Fixtures.CreateComment(_testUser, _testProject.id, _testModel.id, _testVersion); + + await Task.Delay(WAIT_PERIOD); // Give time for subscription to be triggered + + Assert.That(subscriptionMessage, Is.Not.Null); + Assert.That(subscriptionMessage.id, Is.EqualTo(created.id)); + } +} diff --git a/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/VersionResourceTests.cs b/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/VersionResourceTests.cs new file mode 100644 index 0000000000..d54c966e0b --- /dev/null +++ b/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/VersionResourceTests.cs @@ -0,0 +1,117 @@ +using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL.Inputs; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Resources; +using Version = Speckle.Core.Api.GraphQL.Models.Version; + +namespace Speckle.Core.Tests.Integration.API.GraphQL.Resources; + +[TestOf(typeof(VersionResource))] +public class VersionResourceTests +{ + private Client _testUser; + private VersionResource Sut => _testUser.Version; + private Project _project; + private Model _model1; + private Model _model2; + private Version _version; + + [SetUp] + public async Task Setup() + { + _testUser = await Fixtures.SeedUserWithClient(); + _project = await _testUser.Project.Create(new("Test project", "", null)); + _model1 = await _testUser.Model.Create(new("Test Model 1", "", _project.id)); + _model2 = await _testUser.Model.Create(new("Test Model 2", "", _project.id)); + + string versionId = await Fixtures.CreateVersion(_testUser, _project.id, "Test Model 1"); + + _version = await Sut.Get(versionId, _model1.id, _project.id); + } + + [Test] + public async Task VersionGet() + { + Version result = await Sut.Get(_version.id, _model1.id, _project.id); + + Assert.That(result, Has.Property(nameof(Version.id)).EqualTo(_version.id)); + Assert.That(result, Has.Property(nameof(Version.message)).EqualTo(_version.message)); + } + + [Test] + public async Task VersionsGet() + { + ResourceCollection result = await Sut.GetVersions(_model1.id, _project.id); + + Assert.That(result.items, Has.Count.EqualTo(1)); + Assert.That(result.totalCount, Is.EqualTo(1)); + Assert.That(result.items[0], Has.Property(nameof(Version.id)).EqualTo(_version.id)); + } + + [Test] + public async Task VersionReceived() + { + CommitReceivedInput input = + new() + { + commitId = _version.id, + message = "we receieved it", + sourceApplication = "Integration test", + streamId = _project.id + }; + var result = await Sut.Received(input); + + Assert.That(result, Is.True); + } + + [Test] + public async Task ModelGetWithVersions() + { + Model result = await _testUser.Model.GetWithVersions(_model1.id, _project.id); + + Assert.That(result, Has.Property(nameof(Model.id)).EqualTo(_model1.id)); + Assert.That(result.versions.items, Has.Count.EqualTo(1)); + Assert.That(result.versions.totalCount, Is.EqualTo(1)); + Assert.That(result.versions.items[0], Has.Property(nameof(Version.id)).EqualTo(_version.id)); + } + + [Test] + public async Task VersionUpdate() + { + const string NEW_MESSAGE = "MY new version message"; + + UpdateVersionInput input = new(_version.id, NEW_MESSAGE); + Version updatedVersion = await Sut.Update(input); + + Assert.That(updatedVersion, Has.Property(nameof(Version.id)).EqualTo(_version.id)); + Assert.That(updatedVersion, Has.Property(nameof(Version.message)).EqualTo(NEW_MESSAGE)); + Assert.That(updatedVersion, Has.Property(nameof(Version.previewUrl)).EqualTo(_version.previewUrl)); + } + + [Test] + public async Task VersionMoveToModel() + { + MoveVersionsInput input = new(_model2.name, new[] { _version.id }); + string id = await Sut.MoveToModel(input); + Assert.That(id, Is.EqualTo(_model2.id)); + Version movedVersion = await Sut.Get(_version.id, _model2.id, _project.id); + + Assert.That(movedVersion, Has.Property(nameof(Version.id)).EqualTo(_version.id)); + Assert.That(movedVersion, Has.Property(nameof(Version.message)).EqualTo(_version.message)); + Assert.That(movedVersion, Has.Property(nameof(Version.previewUrl)).EqualTo(_version.previewUrl)); + + Assert.CatchAsync(async () => await Sut.Get(id, _model1.id, _project.id)); + } + + [Test] + public async Task VersionDelete() + { + DeleteVersionsInput input = new(new[] { _version.id }); + + bool response = await Sut.Delete(input); + Assert.That(response, Is.True); + + Assert.CatchAsync(async () => _ = await Sut.Get(_version.id, _model1.id, _project.id)); + Assert.CatchAsync(async () => _ = await Sut.Delete(input)); + } +} diff --git a/Core/Tests/Speckle.Core.Tests.Integration/Credentials/UserServerInfoTests.cs b/Core/Tests/Speckle.Core.Tests.Integration/Credentials/UserServerInfoTests.cs index 0929c90926..2df5d5b3f5 100644 --- a/Core/Tests/Speckle.Core.Tests.Integration/Credentials/UserServerInfoTests.cs +++ b/Core/Tests/Speckle.Core.Tests.Integration/Credentials/UserServerInfoTests.cs @@ -1,5 +1,5 @@ using GraphQL.Client.Http; -using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL.Models; using Speckle.Core.Credentials; namespace Speckle.Core.Tests.Integration.Credentials; diff --git a/Core/Tests/Speckle.Core.Tests.Integration/Fixtures.cs b/Core/Tests/Speckle.Core.Tests.Integration/Fixtures.cs index 0e046f9bbf..7150dc38b8 100644 --- a/Core/Tests/Speckle.Core.Tests.Integration/Fixtures.cs +++ b/Core/Tests/Speckle.Core.Tests.Integration/Fixtures.cs @@ -4,9 +4,12 @@ using System.Web; using Newtonsoft.Json; using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL.Inputs; +using Speckle.Core.Api.GraphQL.Models; using Speckle.Core.Credentials; using Speckle.Core.Logging; using Speckle.Core.Models; +using Speckle.Core.Transports; namespace Speckle.Core.Tests.Integration; @@ -23,7 +26,29 @@ public void BeforeAll() public static class Fixtures { - private static readonly ServerInfo s_server = new() { url = "http://localhost:3000", name = "Docker Server" }; + public static readonly ServerInfo Server = new() { url = "http://localhost:3000", name = "Docker Server" }; + + public static Client Unauthed => new Client(new Account { serverInfo = Server, userInfo = new UserInfo() }); + + public static async Task SeedUserWithClient() + { + return new Client(await SeedUser()); + } + + public static async Task CreateVersion(Client client, string projectId, string branchName) + { + using ServerTransport remote = new(client.Account, projectId); + var objectId = await Operations.Send(new() { applicationId = "ASDF" }, remote, false); + CommitCreateInput input = + new() + { + branchName = branchName, + message = "test version", + objectId = objectId, + streamId = projectId + }; + return await client.Version.Create(input); + } public static async Task SeedUser() { @@ -31,7 +56,7 @@ public static async Task SeedUser() Dictionary user = new() { - ["email"] = $"{seed.Substring(0, 7)}@acme.com", + ["email"] = $"{seed.Substring(0, 7)}@example.com", ["password"] = "12ABC3456789DEF0GHO", ["name"] = $"{seed.Substring(0, 5)} Name" }; @@ -40,7 +65,7 @@ public static async Task SeedUser() new HttpClientHandler { AllowAutoRedirect = false, CheckCertificateRevocationList = true } ); - httpClient.BaseAddress = new Uri(s_server.url); + httpClient.BaseAddress = new Uri(Server.url); string redirectUrl; try @@ -54,7 +79,7 @@ public static async Task SeedUser() } catch (Exception e) { - throw new Exception($"Cannot seed user on the server {s_server.url}", e); + throw new Exception($"Cannot seed user on the server {Server.url}", e); } Uri uri = new(redirectUrl); @@ -87,12 +112,11 @@ await tokenResponse.Content.ReadAsStringAsync() email = user["email"], name = user["name"] }, - serverInfo = s_server + serverInfo = Server }; - using var client = new Client(acc); - var user1 = await client.ActiveUserGet(); - acc.userInfo.id = user1.id; + var user1 = await AccountManager.GetUserInfo(acc.token, acc.serverInfo.url); + acc.userInfo = user1; return acc; } @@ -132,6 +156,23 @@ private static Blob GenerateBlob(string content) File.WriteAllText(filePath, content); return new Blob(filePath); } + + internal static async Task CreateComment(Client client, string projectId, string modelId, string versionId) + { + var blobs = await SendBlobData(client.Account, projectId); + var blobIds = blobs.Select(b => b.id).ToList(); + CreateCommentInput input = new(new(blobIds, null), projectId, $"{projectId},{modelId},{versionId}", null, null); + return await client.Comment.Create(input); + } + + internal static async Task SendBlobData(Account account, string projectId) + { + using ServerTransport remote = new(account, projectId); + var blobs = Fixtures.GenerateThreeBlobs(); + Base myObject = new() { ["blobs"] = blobs }; + await Operations.Send(myObject, remote, false); + return blobs; + } } public class UserIdResponse diff --git a/Core/Tests/Speckle.Core.Tests.Integration/GraphQLCLient.cs b/Core/Tests/Speckle.Core.Tests.Integration/GraphQLCLient.cs index afdb06caed..985aae175b 100644 --- a/Core/Tests/Speckle.Core.Tests.Integration/GraphQLCLient.cs +++ b/Core/Tests/Speckle.Core.Tests.Integration/GraphQLCLient.cs @@ -19,7 +19,7 @@ public async Task Setup() [Test] public void ThrowsForbiddenException() { - Assert.ThrowsAsync>>( + Assert.ThrowsAsync( async () => await _client.ExecuteGraphQLRequest>( new GraphQLRequest diff --git a/Core/Tests/Speckle.Core.Tests.Unit/Api/GraphQLClient.cs b/Core/Tests/Speckle.Core.Tests.Unit/Api/GraphQLClient.cs index 0733e39a85..9baadcac8b 100644 --- a/Core/Tests/Speckle.Core.Tests.Unit/Api/GraphQLClient.cs +++ b/Core/Tests/Speckle.Core.Tests.Unit/Api/GraphQLClient.cs @@ -2,6 +2,7 @@ using GraphQL; using NUnit.Framework; using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL.Models; using Speckle.Core.Credentials; namespace Speckle.Core.Tests.Unit.Api; @@ -30,16 +31,10 @@ public void Dispose() private static IEnumerable ErrorCases() { + yield return new TestCaseData(typeof(SpeckleGraphQLForbiddenException), new Map { { "code", "FORBIDDEN" } }); + yield return new TestCaseData(typeof(SpeckleGraphQLForbiddenException), new Map { { "code", "UNAUTHENTICATED" } }); yield return new TestCaseData( - typeof(SpeckleGraphQLForbiddenException), - new Map { { "code", "FORBIDDEN" } } - ); - yield return new TestCaseData( - typeof(SpeckleGraphQLForbiddenException), - new Map { { "code", "UNAUTHENTICATED" } } - ); - yield return new TestCaseData( - typeof(SpeckleGraphQLInternalErrorException), + typeof(SpeckleGraphQLInternalErrorException), new Map { { "code", "INTERNAL_SERVER_ERROR" } } ); yield return new TestCaseData(typeof(SpeckleGraphQLException), new Map { { "foo", "bar" } }); @@ -109,7 +104,7 @@ public async Task TestExecuteWithResiliencePoliciesRetry() counter++; if (counter < maxRetryCount) { - throw new SpeckleGraphQLInternalErrorException(new GraphQLRequest(), new GraphQLResponse()); + throw new SpeckleGraphQLInternalErrorException(new GraphQLRequest(), new GraphQLResponse()); } return Task.FromResult(expectedResult); diff --git a/Core/Tests/Speckle.Core.Tests.Unit/Credentials/AccountServerMigrationTests.cs b/Core/Tests/Speckle.Core.Tests.Unit/Credentials/AccountServerMigrationTests.cs index 35c21adf7c..78c4f09330 100644 --- a/Core/Tests/Speckle.Core.Tests.Unit/Credentials/AccountServerMigrationTests.cs +++ b/Core/Tests/Speckle.Core.Tests.Unit/Credentials/AccountServerMigrationTests.cs @@ -1,5 +1,5 @@ using NUnit.Framework; -using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL.Models; using Speckle.Core.Credentials; namespace Speckle.Core.Tests.Unit.Credentials; @@ -14,12 +14,10 @@ public static IEnumerable MigrationTestCase() const string NEW_URL = "https://new.example.com"; const string OTHER_URL = "https://other.example.com"; Account oldAccount = CreateTestAccount(OLD_URL, null, new(NEW_URL)); - Account newAccount = CreateTestAccount(NEW_URL, new(OLD_URL), null); + string accountId = oldAccount.userInfo.id; // new account user must match old account user id + Account newAccount = CreateTestAccount(NEW_URL, new(OLD_URL), null, accountId); Account otherAccount = CreateTestAccount(OTHER_URL, null, null); - // new account user must match old account user id - newAccount.userInfo.id = oldAccount.userInfo.id; - List givenAccounts = new() { oldAccount, newAccount, otherAccount }; yield return new TestCaseData(givenAccounts, NEW_URL, new[] { newAccount }) @@ -59,8 +57,9 @@ public void TearDown() _accountsToCleanUp.Clear(); } - private static Account CreateTestAccount(string url, Uri movedFrom, Uri movedTo) + private static Account CreateTestAccount(string url, Uri movedFrom, Uri movedTo, string id = null) { + id ??= Guid.NewGuid().ToString(); return new Account { token = "myToken", @@ -72,7 +71,7 @@ private static Account CreateTestAccount(string url, Uri movedFrom, Uri movedTo) }, userInfo = new UserInfo { - id = Guid.NewGuid().ToString(), + id = id, email = "user@example.com", name = "user" } diff --git a/Core/Tests/Speckle.Core.Tests.Unit/Credentials/Accounts.cs b/Core/Tests/Speckle.Core.Tests.Unit/Credentials/Accounts.cs index ba0b1aec09..a9221ac4d7 100644 --- a/Core/Tests/Speckle.Core.Tests.Unit/Credentials/Accounts.cs +++ b/Core/Tests/Speckle.Core.Tests.Unit/Credentials/Accounts.cs @@ -1,5 +1,5 @@ using NUnit.Framework; -using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL.Models; using Speckle.Core.Credentials; namespace Speckle.Core.Tests.Unit.Credentials; diff --git a/Core/Tests/Speckle.Core.Tests.Unit/Serialisation/SerializerBreakingChanges.cs b/Core/Tests/Speckle.Core.Tests.Unit/Serialisation/SerializerBreakingChanges.cs index 163d7abd21..0fc9549fd2 100644 --- a/Core/Tests/Speckle.Core.Tests.Unit/Serialisation/SerializerBreakingChanges.cs +++ b/Core/Tests/Speckle.Core.Tests.Unit/Serialisation/SerializerBreakingChanges.cs @@ -1,5 +1,4 @@ using NUnit.Framework; -using Speckle.Core.Logging; using Speckle.Core.Serialisation; namespace Speckle.Core.Tests.Unit.Serialisation; diff --git a/Core/Tests/Speckle.Core.Tests.Unit/Serialisation/SerializerNonBreakingChanges.cs b/Core/Tests/Speckle.Core.Tests.Unit/Serialisation/SerializerNonBreakingChanges.cs index ef2c5e501e..30dbea5d06 100644 --- a/Core/Tests/Speckle.Core.Tests.Unit/Serialisation/SerializerNonBreakingChanges.cs +++ b/Core/Tests/Speckle.Core.Tests.Unit/Serialisation/SerializerNonBreakingChanges.cs @@ -87,6 +87,42 @@ public void ListToArray(double[] testCase) Assert.That(res.value, Is.EquivalentTo(testCase)); } + [Test, TestCaseSource(nameof(s_arrayTestCases))] + public void ListToIList(double[] testCase) + { + var from = new ListDoubleValueMock { value = testCase.ToList() }; + + var res = from.SerializeAsTAndDeserialize(); + Assert.That(res.value, Is.EquivalentTo(testCase)); + } + + [Test, TestCaseSource(nameof(s_arrayTestCases))] + public void ListToIReadOnlyList(double[] testCase) + { + var from = new ListDoubleValueMock { value = testCase.ToList() }; + + var res = from.SerializeAsTAndDeserialize(); + Assert.That(res.value, Is.EquivalentTo(testCase)); + } + + [Test, TestCaseSource(nameof(s_arrayTestCases))] + public void IListToList(double[] testCase) + { + var from = new IListDoubleValueMock { value = testCase.ToList() }; + + var res = from.SerializeAsTAndDeserialize(); + Assert.That(res.value, Is.EquivalentTo(testCase)); + } + + [Test, TestCaseSource(nameof(s_arrayTestCases))] + public void IReadOnlyListToList(double[] testCase) + { + var from = new IReadOnlyListDoubleValueMock { value = testCase.ToList() }; + + var res = from.SerializeAsTAndDeserialize(); + Assert.That(res.value, Is.EquivalentTo(testCase)); + } + [Test, TestCaseSource(nameof(MyEnums))] public void EnumToInt(MyEnum testCase) { @@ -171,6 +207,16 @@ public class ListDoubleValueMock : SerializerMock public List value { get; set; } } +public class IListDoubleValueMock : SerializerMock +{ + public IList value { get; set; } +} + +public class IReadOnlyListDoubleValueMock : SerializerMock +{ + public IReadOnlyList value { get; set; } +} + public class ArrayDoubleValueMock : SerializerMock { public double[] value { get; set; } diff --git a/Core/Tests/Speckle.Core.Tests.Unit/Transports/TransportTests.cs b/Core/Tests/Speckle.Core.Tests.Unit/Transports/TransportTests.cs index b264a6fe9d..067c5298c3 100644 --- a/Core/Tests/Speckle.Core.Tests.Unit/Transports/TransportTests.cs +++ b/Core/Tests/Speckle.Core.Tests.Unit/Transports/TransportTests.cs @@ -1,6 +1,5 @@ #nullable enable using NUnit.Framework; -using Speckle.Core.Api; using Speckle.Core.Transports; using Speckle.Newtonsoft.Json; diff --git a/DesktopUI2/DesktopUI2/ConnectorBindings.cs b/DesktopUI2/DesktopUI2/ConnectorBindings.cs index a86e22d24c..b0c7f3335b 100644 --- a/DesktopUI2/DesktopUI2/ConnectorBindings.cs +++ b/DesktopUI2/DesktopUI2/ConnectorBindings.cs @@ -5,7 +5,6 @@ using DesktopUI2.Models.Filters; using DesktopUI2.Models.Settings; using DesktopUI2.ViewModels; -using Sentry.Reflection; using Speckle.Core.Kits; using Speckle.Core.Models; @@ -16,9 +15,9 @@ namespace DesktopUI2; public abstract class ConnectorBindings { - public string ConnectorVersion => Assembly.GetAssembly(GetType()).GetNameAndVersion().Version; + public string ConnectorVersion => Assembly.GetAssembly(GetType()).GetName().Version.ToString(); - public string ConnectorName => Assembly.GetAssembly(GetType()).GetNameAndVersion().Name; + public string ConnectorName => Assembly.GetAssembly(GetType()).GetName().Name; //public List SavedStreamStates = new List(); diff --git a/DesktopUI2/DesktopUI2/DummyBindings.cs b/DesktopUI2/DesktopUI2/DummyBindings.cs index d818645212..6a813d5330 100644 --- a/DesktopUI2/DesktopUI2/DummyBindings.cs +++ b/DesktopUI2/DesktopUI2/DummyBindings.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; -using System.Diagnostics; using System.Globalization; using System.Threading.Tasks; using DesktopUI2.Models; @@ -9,7 +8,9 @@ using DesktopUI2.Models.Settings; using DesktopUI2.ViewModels; using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL; using Speckle.Core.Credentials; +using Speckle.Core.Helpers; using Speckle.Core.Kits; using Speckle.Core.Models; using Commits = Speckle.Core.Api.Commits; @@ -55,8 +56,7 @@ public override List GetCustomStreamMenuItems() public void OpenLink(StreamState state) { - //to open urls in .net core must set UseShellExecute = true - Process.Start(new ProcessStartInfo(state.ServerUrl) { UseShellExecute = true }); + Open.Url(state.ServerUrl); } public override string GetDocumentId() @@ -224,21 +224,21 @@ public override List GetStreamsInFile() { id = "123", name = "Matteo Cominetti", - role = "stream:contributor", + role = StreamRoles.STREAM_CONTRIBUTOR, avatar = "https://avatars0.githubusercontent.com/u/2679513?s=88&v=4" }, new() { id = "321", name = "Izzy Lyseggen", - role = "stream:owner", + role = StreamRoles.STREAM_OWNER, avatar = "https://avatars2.githubusercontent.com/u/7717434?s=88&u=08db51f5799f6b21580485d915054b3582d519e6&v=4" }, new() { id = "456", name = "Dimitrie Stefanescu", - role = "stream:contributor", + role = StreamRoles.STREAM_CONTRIBUTOR, avatar = "https://avatars3.githubusercontent.com/u/7696515?s=88&u=fa253b5228d512e1ce79357c63925b7258e69f4c&v=4" } }; diff --git a/DesktopUI2/DesktopUI2/Utils.cs b/DesktopUI2/DesktopUI2/Utils.cs index 10057b4e38..90a0cf8b1b 100644 --- a/DesktopUI2/DesktopUI2/Utils.cs +++ b/DesktopUI2/DesktopUI2/Utils.cs @@ -19,7 +19,9 @@ using Material.Dialog.Interfaces; using SkiaSharp; using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL.Models; using Speckle.Core.Credentials; +using Speckle.Core.Helpers; using Speckle.Core.Logging; namespace DesktopUI2; @@ -226,11 +228,11 @@ public static void LaunchManager() if (File.Exists(path) || Directory.Exists(path)) { - Process.Start(path); + Open.File(path); } else { - Process.Start(new ProcessStartInfo("https://speckle.systems/download") { UseShellExecute = true }); + Open.Url("https://speckle.systems/download"); } } catch (Exception ex) diff --git a/DesktopUI2/DesktopUI2/ViewModels/AccountViewModel.cs b/DesktopUI2/DesktopUI2/ViewModels/AccountViewModel.cs index d1e5154642..f9faee7727 100644 --- a/DesktopUI2/DesktopUI2/ViewModels/AccountViewModel.cs +++ b/DesktopUI2/DesktopUI2/ViewModels/AccountViewModel.cs @@ -6,6 +6,7 @@ using Avalonia.Media.Imaging; using ReactiveUI; using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL.Models; using Speckle.Core.Credentials; using Speckle.Core.Helpers; using Speckle.Core.Logging; diff --git a/DesktopUI2/DesktopUI2/ViewModels/CommentViewModel.cs b/DesktopUI2/DesktopUI2/ViewModels/CommentViewModel.cs index 4b43e32a3e..359116a9d7 100644 --- a/DesktopUI2/DesktopUI2/ViewModels/CommentViewModel.cs +++ b/DesktopUI2/DesktopUI2/ViewModels/CommentViewModel.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.Linq; using System.Threading.Tasks; @@ -10,6 +9,7 @@ using DesktopUI2.Views; using ReactiveUI; using Speckle.Core.Api; +using Speckle.Core.Helpers; using Speckle.Core.Logging; using Splat; using Stream = System.IO.Stream; @@ -135,7 +135,7 @@ public void OpenComment() url = $"{_client.Account.serverInfo.url}/projects/{StreamId}/"; } - Process.Start(new ProcessStartInfo(url) { UseShellExecute = true }); + Open.Url(url); Analytics.TrackEvent(Analytics.Events.DUIAction, new Dictionary { { "name", "Comment View" } }); } } diff --git a/DesktopUI2/DesktopUI2/ViewModels/DialogViewModel.cs b/DesktopUI2/DesktopUI2/ViewModels/DialogViewModel.cs index 3621f691ee..711d412c38 100644 --- a/DesktopUI2/DesktopUI2/ViewModels/DialogViewModel.cs +++ b/DesktopUI2/DesktopUI2/ViewModels/DialogViewModel.cs @@ -1,4 +1,3 @@ -using DesktopUI2.Models; using ReactiveUI; namespace DesktopUI2.ViewModels; diff --git a/DesktopUI2/DesktopUI2/ViewModels/HomeViewModel.cs b/DesktopUI2/DesktopUI2/ViewModels/HomeViewModel.cs index 4e6169fb8f..d1385cae54 100644 --- a/DesktopUI2/DesktopUI2/ViewModels/HomeViewModel.cs +++ b/DesktopUI2/DesktopUI2/ViewModels/HomeViewModel.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Diagnostics; using System.Linq; using System.Reactive; using System.Threading; @@ -605,7 +604,7 @@ public async void RemoveAccountCommand(Account account) public void OpenProfileCommand(Account account) { - Process.Start(new ProcessStartInfo($"{account.serverInfo.url}/profile") { UseShellExecute = true }); + Open.Url($"{account.serverInfo.url}/profile"); Analytics.TrackEvent( account, Analytics.Events.DUIAction, @@ -633,7 +632,7 @@ public void ViewOnlineCommand(object parameter) url = $"{streamAcc.Account.serverInfo.url.TrimEnd('/')}/projects/{streamAcc.Stream.id}"; } - Process.Start(new ProcessStartInfo(url) { UseShellExecute = true }); + Open.Url(url); Analytics.TrackEvent( streamAcc.Account, Analytics.Events.DUIAction, diff --git a/DesktopUI2/DesktopUI2/ViewModels/MappingTool/MappingsViewModel.cs b/DesktopUI2/DesktopUI2/ViewModels/MappingTool/MappingsViewModel.cs index f0e309d50b..69a9c7e29c 100644 --- a/DesktopUI2/DesktopUI2/ViewModels/MappingTool/MappingsViewModel.cs +++ b/DesktopUI2/DesktopUI2/ViewModels/MappingTool/MappingsViewModel.cs @@ -1,7 +1,6 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; using System.Reactive; using System.Threading.Tasks; @@ -15,6 +14,7 @@ using Objects.BuiltElements.Revit; using ReactiveUI; using Speckle.Core.Api; +using Speckle.Core.Helpers; using Speckle.Core.Logging; using Speckle.Core.Models; using Speckle.Core.Transports; @@ -672,7 +672,7 @@ public void SelectAllMappingsCommand() public void OpenGuideCommand() { - Process.Start(new ProcessStartInfo("https://speckle.guide/user/mapping-tool.html") { UseShellExecute = true }); + Open.Url("https://speckle.guide/user/mapping-tool.html"); } public void OpenStreamSelectorCommand() @@ -682,11 +682,6 @@ public void OpenStreamSelectorCommand() public void FeedbackCommand() { - Process.Start( - new ProcessStartInfo("https://speckle.community/t/mapping-tool-for-cad-bim-workflows/4086") - { - UseShellExecute = true - } - ); + Open.Url("https://speckle.community/t/mapping-tool-for-cad-bim-workflows/4086"); } } diff --git a/DesktopUI2/DesktopUI2/ViewModels/NotificationViewModel.cs b/DesktopUI2/DesktopUI2/ViewModels/NotificationViewModel.cs index 816e36c755..a3fba0479c 100644 --- a/DesktopUI2/DesktopUI2/ViewModels/NotificationViewModel.cs +++ b/DesktopUI2/DesktopUI2/ViewModels/NotificationViewModel.cs @@ -1,9 +1,9 @@ using System; -using System.Diagnostics; using Avalonia.Media; using Material.Icons; using ReactiveUI; -using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Helpers; namespace DesktopUI2.ViewModels; @@ -19,7 +19,7 @@ public NotificationViewModel(PendingStreamCollaborator invite, string serverUrl) Message = $"{invite.invitedBy.name} is inviting you to collaborate on '{invite.streamName}'!"; Launch = () => { - Process.Start(new ProcessStartInfo($"{serverUrl}/streams/{invite.streamId}") { UseShellExecute = true }); + Open.Url($"{serverUrl}/streams/{invite.streamId}"); }; } diff --git a/DesktopUI2/DesktopUI2/ViewModels/OneClickViewModel.cs b/DesktopUI2/DesktopUI2/ViewModels/OneClickViewModel.cs index b1f2865823..5204e04330 100644 --- a/DesktopUI2/DesktopUI2/ViewModels/OneClickViewModel.cs +++ b/DesktopUI2/DesktopUI2/ViewModels/OneClickViewModel.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; using System.Threading.Tasks; using Avalonia; @@ -8,6 +7,7 @@ using ReactiveUI; using Speckle.Core.Api; using Speckle.Core.Credentials; +using Speckle.Core.Helpers; using Speckle.Core.Logging; using Speckle.Core.Models; using Splat; @@ -235,7 +235,7 @@ private void CloseSendModal() private void ViewOnlineCommand() { - Process.Start(new ProcessStartInfo(Url) { UseShellExecute = true }); + Open.Url(Url); } private void CopyCommand() diff --git a/DesktopUI2/DesktopUI2/ViewModels/ProgressViewModel.cs b/DesktopUI2/DesktopUI2/ViewModels/ProgressViewModel.cs index 61bd5386ed..45cf8e54e3 100644 --- a/DesktopUI2/DesktopUI2/ViewModels/ProgressViewModel.cs +++ b/DesktopUI2/DesktopUI2/ViewModels/ProgressViewModel.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; using System.Threading; using System.Web; @@ -130,14 +129,7 @@ public void GetHelpCommand() } var safeReport = HttpUtility.UrlEncode(report); - Process.Start( - new ProcessStartInfo( - $"https://speckle.community/new-topic?title=I%20need%20help%20with...&body={safeReport}&category=help" - ) - { - UseShellExecute = true - } - ); + Open.Url($"https://speckle.community/new-topic?title=I%20need%20help%20with...&body={safeReport}&category=help"); } public void CancelCommand() diff --git a/DesktopUI2/DesktopUI2/ViewModels/StreamViewModel.cs b/DesktopUI2/DesktopUI2/ViewModels/StreamViewModel.cs index 87fd03799d..e5b92759d1 100644 --- a/DesktopUI2/DesktopUI2/ViewModels/StreamViewModel.cs +++ b/DesktopUI2/DesktopUI2/ViewModels/StreamViewModel.cs @@ -1363,16 +1363,10 @@ public async void ViewOnlineSavedStreamCommand() //ensure click transition has finished await Task.Delay(100).ConfigureAwait(true); - OpenUrl(Url); + Open.Url(Url); Analytics.TrackEvent(Analytics.Events.DUIAction, new Dictionary { { "name", "Stream View" } }); } - private void OpenUrl(string url) - { - //to open urls in .net core must set UseShellExecute = true - Process.Start(new ProcessStartInfo(url) { UseShellExecute = true }); - } - public async void CopyStreamURLCommand() { //ensure click transition has finished @@ -1443,7 +1437,7 @@ public async void SendCommand() url = $"{StreamState.ServerUrl}/projects/{StreamState.StreamId}/models/{SelectedBranch.Branch.id}"; } - OpenUrl(url); + Open.Url(url); }, Type = NotificationType.Success, Expiration = TimeSpan.FromSeconds(10) diff --git a/Directory.Build.props b/Directory.Build.props index dbaa3aea2d..e0b95b5d9c 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -34,8 +34,6 @@ CA5399;CA1862; CS1591;CS1573; - - CS8618; CA1303;CA1304;CA1305;CA1307;CA1308;CA1309;CA1310;CA1311;CA2101; NU1701; diff --git a/Directory.Build.targets b/Directory.Build.targets index bfe533e029..432400ed48 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -74,7 +74,7 @@ CS0618;CA1034;CA2201;CA1051;CA1040;CA1724; IDE0044;IDE0130;CA1508; - CA5394;CA2007;CA1852;CA1819;CA1711;CA1063;CA1816;CA2234; + CA5394;CA2007;CA1852;CA1819;CA1711;CA1063;CA1816;CA2234;CS8618; false diff --git a/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/AdvanceSteel/ConverterAutocadCivil.Beams.cs b/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/AdvanceSteel/ConverterAutocadCivil.Beams.cs index 1646396b3d..2bd293fa24 100644 --- a/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/AdvanceSteel/ConverterAutocadCivil.Beams.cs +++ b/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/AdvanceSteel/ConverterAutocadCivil.Beams.cs @@ -1,5 +1,4 @@ #if ADVANCESTEEL -using System; using System.Collections.Generic; using System.Linq; diff --git a/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/AdvanceSteel/Properties/ASBaseProperties.cs b/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/AdvanceSteel/Properties/ASBaseProperties.cs index c4d51c0744..c1c00b44f8 100644 --- a/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/AdvanceSteel/Properties/ASBaseProperties.cs +++ b/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/AdvanceSteel/Properties/ASBaseProperties.cs @@ -1,16 +1,6 @@ #if ADVANCESTEEL using System; using System.Collections.Generic; -using System.Reflection; -using System.Text; -using Autodesk.AdvanceSteel.DocumentManagement; -using Autodesk.AdvanceSteel.DotNetRoots.Units; -using Autodesk.AutoCAD.DatabaseServices; -using Autodesk.AutoCAD.Internal.DatabaseServices; -using Objects.Structural.Analysis; -using Sentry; -using Speckle.Core.Kits; -using Speckle.Newtonsoft.Json.Linq; using static Autodesk.AdvanceSteel.DotNetRoots.Units.Unit; namespace Objects.Converter.AutocadCivil; diff --git a/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/ConverterAutocadCivil.Civil.cs b/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/ConverterAutocadCivil.Civil.cs index 20ede66c9c..60110843bd 100644 --- a/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/ConverterAutocadCivil.Civil.cs +++ b/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/ConverterAutocadCivil.Civil.cs @@ -12,11 +12,11 @@ using Civil = Autodesk.Civil; using Autodesk.AutoCAD.Geometry; using Acad = Autodesk.AutoCAD.Geometry; -using AcadDB = Autodesk.AutoCAD.DatabaseServices; using Objects.BuiltElements.Civil; using Alignment = Objects.BuiltElements.Alignment; using Arc = Objects.Geometry.Arc; +using CivilDataField = Objects.Other.Civil.CivilDataField; using Polycurve = Objects.Geometry.Polycurve; using Featureline = Objects.BuiltElements.Featureline; using Line = Objects.Geometry.Line; @@ -29,6 +29,7 @@ using SpiralType = Objects.Geometry.SpiralType; using Station = Objects.BuiltElements.Station; using Structure = Objects.BuiltElements.Structure; +using Vector = Objects.Geometry.Vector; using Speckle.Core.Logging; namespace Objects.Converter.AutocadCivil; @@ -147,7 +148,8 @@ public CivilAlignment AlignmentToSpeckle(CivilDB.Alignment alignment) stationEquationDirections = directions, offset = alignment.IsOffsetAlignment ? alignment.OffsetAlignmentInfo.NominalOffset : 0, site = alignment.SiteName ?? "", - style = alignment.StyleName ?? "" + style = alignment.StyleName ?? "", + units = ModelUnits }; AddNameAndDescriptionProperty(alignment.Name, alignment.Description, speckleAlignment); @@ -541,7 +543,8 @@ public CivilProfile ProfileToSpeckle(CivilDB.Profile profile) offset = profile.Offset, style = profile.StyleName ?? "", startStation = profile.StartingStation, - endStation = profile.EndingStation + endStation = profile.EndingStation, + units = ModelUnits }; AddNameAndDescriptionProperty(profile.Name, profile.Description, speckleProfile); @@ -614,8 +617,6 @@ public CivilProfile ProfileToSpeckle(CivilDB.Profile profile) speckleProfile.displayValue = PolylineToSpeckle(pvis, profile.Closed); } - speckleProfile.units = ModelUnits; - return speckleProfile; } private Line ProfileLineToSpeckle(ProfileTangent tangent) @@ -639,7 +640,7 @@ private Line ProfileGenericToSpeckle(double startStation, double startElevation, } // featurelines - public Featureline FeatureLineToSpeckle(CivilDB.FeatureLine featureline) + public Featureline FeaturelineToSpeckle(CivilDB.FeatureLine featureline) { // get all points var points = new List(); @@ -665,25 +666,18 @@ public Featureline FeatureLineToSpeckle(CivilDB.FeatureLine featureline) piPoints.Add(allPoints.IndexOf(piPoint)); } - /* - // get bulges at pi point indices - int count = (featureline.Closed) ? featureline.PointsCount : featureline.PointsCount - 1; - List bulges = new List(); - for (int i = 0; i < count; i++) bulges.Add(featureline.GetBulge(i)); - var piBulges = new List(); - foreach (var index in indices) piBulges.Add(bulges[index]); - */ - // get displayvalue var polyline = PolylineToSpeckle(new Polyline3d(Poly3dType.SimplePoly, intersectionPoints, false)); // featureline Featureline speckleFeatureline = new() { + points = points, curve = CurveToSpeckle(featureline.BaseCurve, ModelUnits), units = ModelUnits, displayValue = new List() { polyline } }; + AddNameAndDescriptionProperty(featureline.Name, featureline.Description, speckleFeatureline); speckleFeatureline["@piPoints"] = piPoints; speckleFeatureline["@elevationPoints"] = ePoints; @@ -708,7 +702,7 @@ private Featureline FeaturelineToSpeckle(CivilDB.CorridorFeatureLine featureline var point = featureline.FeatureLinePoints[i]; baseCurvePoints.Add(point.XYZ); if (!point.IsBreak) { polylinePoints.Add(point.XYZ); } - if (polylinePoints.Count > 0 && (i == featureline.FeatureLinePoints.Count - 1 || point.IsBreak )) + if (polylinePoints.Count > 1 && (i == featureline.FeatureLinePoints.Count - 1 || point.IsBreak )) { var polyline = PolylineToSpeckle(new Polyline3d(Poly3dType.SimplePoly, polylinePoints, false)); polylines.Add(polyline); @@ -1016,26 +1010,64 @@ public Structure StructureToSpeckle(CivilDB.Structure structure) // assign additional structure props AddNameAndDescriptionProperty(structure.Name, structure.Description, speckleStructure); + speckleStructure["partData"] = PartDataRecordToSpeckle(structure.PartData); + + try { speckleStructure["station"] = structure.Station; } catch (Exception ex) when (!ex.IsFatal()) { } + try { speckleStructure["network"] = structure.NetworkName; } catch (Exception ex) when (!ex.IsFatal()) { } + try { speckleStructure["rotation"] = structure.Rotation; } catch (Exception e) when (!e.IsFatal()) { } + try { speckleStructure["sumpDepth"] = structure.SumpDepth; } catch (Exception e) when (!e.IsFatal()) { } + try { speckleStructure["rimElevation"] = structure.RimElevation; } catch (Exception e) when (!e.IsFatal()) { } + try { speckleStructure["sumpElevation"] = structure.SumpElevation; } catch (Exception e) when (!e.IsFatal()) { } + try { speckleStructure["lengthOuter"] = structure.Length; } catch (Exception e) when (!e.IsFatal()) { } + try { speckleStructure["lengthInner"] = structure.InnerLength; } catch (Exception e) when (!e.IsFatal()) { } + try { speckleStructure["structureId"] = structure.Id.ToString(); } catch (Exception ex) when (!ex.IsFatal()) { } - try + return speckleStructure; + } + + // part data + /// + /// Converts PartData into a list of DataField + /// + private Base PartDataRecordToSpeckle(PartDataRecord partData) + { + Base partDataBase = new(); + List fields = new(); + + foreach (PartDataField partField in partData.GetAllDataFields()) { - speckleStructure["grate"] = structure.Grate; - speckleStructure["station"] = structure.Station; - speckleStructure["network"] = structure.NetworkName; + CivilDataField field = new(partField.Name, partField.DataType.ToString(), partField.Value, partField.Units.ToString(),partField.Context.ToString(), null); + partDataBase[partField.Name] = field; } - catch (Exception e) when (!e.IsFatal()) + + return partDataBase; + } + +#if CIVIL2022_OR_GREATER + /// + /// Converts PressureNetworkPartData into a list of DataField + /// + private List PartDataRecordToSpeckle(PressureNetworkPartData partData) + { + List fields = new(); + + foreach (PressurePartProperty partField in partData) { - // Couldn't set non-essential structure properties + CivilDataField field = new(partField.Name, partField.GetType().ToString(), partField.Value, null, null, partField.DisplayName); + fields.Add(field); } - return speckleStructure; + return fields; } +#endif // pipes // TODO: add pressure fittings public Pipe PipeToSpeckle(CivilDB.Pipe pipe) { // get the pipe curve + // rant: if this is a straight or curved pipe, the BaseCurve prop is fake news && will return a DB.line with start and endpoints set to [0,0,0] & [0,0,1] + // do not use CurveToSpeckle(basecurve) 😡 ICurve curve; switch (pipe.SubEntityType) { @@ -1043,8 +1075,11 @@ public Pipe PipeToSpeckle(CivilDB.Pipe pipe) var line = new Acad.LineSegment3d(pipe.StartPoint, pipe.EndPoint); curve = LineToSpeckle(line); break; + case PipeSubEntityType.Curved: + curve = ArcToSpeckle(pipe.Curve2d); + break; default: - curve = CurveToSpeckle(pipe.BaseCurve); + curve = CurveToSpeckle(pipe.BaseCurve); // basecurve is fake news, but we're still sending the other types with props for now break; } @@ -1059,6 +1094,7 @@ public Pipe PipeToSpeckle(CivilDB.Pipe pipe) // assign additional pipe props AddNameAndDescriptionProperty(pipe.Name, pipe.Description, specklePipe); + specklePipe["partData"] = PartDataRecordToSpeckle(pipe.PartData); try { specklePipe["shape"] = pipe.CrossSectionalShape.ToString(); } catch(Exception ex) when(!ex.IsFatal()) { } try { specklePipe["slope"] = pipe.Slope; } catch(Exception ex) when(!ex.IsFatal()) { } @@ -1069,8 +1105,9 @@ public Pipe PipeToSpeckle(CivilDB.Pipe pipe) try { specklePipe["endOffset"] = pipe.EndOffset; } catch(Exception ex) when(!ex.IsFatal()) { } try { specklePipe["startStation"] = pipe.StartStation; } catch(Exception ex) when(!ex.IsFatal()) { } try { specklePipe["endStation"] = pipe.EndStation; } catch(Exception ex) when(!ex.IsFatal()) { } - try { specklePipe["startStructure"] = pipe.StartStructureId.ToString(); } catch(Exception ex) when(!ex.IsFatal()) { } - try { specklePipe["endStructure"] = pipe.EndStructureId.ToString(); } catch(Exception ex) when(!ex.IsFatal()) { } + try { specklePipe["startStructureId"] = pipe.StartStructureId.ToString(); } catch(Exception ex) when(!ex.IsFatal()) { } + try { specklePipe["endStructureId"] = pipe.EndStructureId.ToString(); } catch(Exception ex) when(!ex.IsFatal()) { } + try { specklePipe["pipeId"] = pipe.Id.ToString(); } catch (Exception ex) when (!ex.IsFatal()) { } return specklePipe; } @@ -1078,13 +1115,21 @@ public Pipe PipeToSpeckle(CivilDB.Pipe pipe) public Pipe PipeToSpeckle(PressurePipe pipe) { // get the pipe curve + // rant: if this is a straight or curved pipe, the BaseCurve prop is fake news && will return a DB.line with start and endpoints set to [0,0,0] & [0,0,1] + // do not use CurveToSpeckle(basecurve) 😡 ICurve curve; switch (pipe.BaseCurve) { - case AcadDB.Line: + case Autodesk.AutoCAD.DatabaseServices.Line: var line = new LineSegment3d(pipe.StartPoint, pipe.EndPoint); curve = LineToSpeckle(line); break; +#if CIVIL2024_OR_GREATER + case Autodesk.AutoCAD.DatabaseServices.Arc: + var arc = pipe.CurveGeometry.GetArc2d(); + curve = ArcToSpeckle(arc); + break; +#endif default: curve = CurveToSpeckle(pipe.BaseCurve); break; @@ -1101,8 +1146,11 @@ public Pipe PipeToSpeckle(PressurePipe pipe) // assign additional pipe props AddNameAndDescriptionProperty(pipe.Name, pipe.Description, specklePipe); - specklePipe["isPressurePipe"] = true; +#if CIVIL2022_OR_GREATER + specklePipe["partData"] = PartDataRecordToSpeckle(pipe.PartData); +#endif + specklePipe["isPressurePipe"] = true; try { specklePipe["partType"] = pipe.PartType.ToString(); } catch (Exception e) when (!e.IsFatal()) { } try { specklePipe["slope"] = pipe.Slope; } catch (Exception e) when (!e.IsFatal()) { } try { specklePipe["network"] = pipe.NetworkName; } catch (Exception e) when (!e.IsFatal()) { } @@ -1110,19 +1158,195 @@ public Pipe PipeToSpeckle(PressurePipe pipe) try { specklePipe["endOffset"] = pipe.EndOffset; } catch (Exception e) when (!e.IsFatal()) { } try { specklePipe["startStation"] = pipe.StartStation; } catch (Exception e) when (!e.IsFatal()) { } try { specklePipe["endStation"] = pipe.EndStation; } catch (Exception e) when (!e.IsFatal()) { } + try { specklePipe["pipeId"] = pipe.Id.ToString(); } catch (Exception ex) when (!ex.IsFatal()) { } return specklePipe; } // corridors // this is composed of assemblies, alignments, and profiles, use point codes to generate featurelines (which will have the 3d curve) + + private CivilDataField AppliedSubassemblyParamToSpeckle(IAppliedSubassemblyParam param) + { + CivilDataField baseParam = new(param.KeyName, param.ValueType.Name, param.ValueAsObject, null, null, param.DisplayName); + + return baseParam; + } + + private CivilAppliedSubassembly AppliedSubassemblyToSpeckle(AppliedSubassembly appliedSubassembly) + { + // retrieve subassembly name + Subassembly subassembly = Trans.GetObject(appliedSubassembly.SubassemblyId, OpenMode.ForRead) as Subassembly; + + // get the calculated shapes + List speckleShapes = new(); + foreach (CalculatedShape shape in appliedSubassembly.Shapes) + { + CivilCalculatedShape speckleShape = CalculatedShapeToSpeckle(shape); + speckleShapes.Add(speckleShape); + } + + Point soePoint = PointToSpeckle(appliedSubassembly.OriginStationOffsetElevationToBaseline); + List speckleParameters = appliedSubassembly.Parameters.Select(p => AppliedSubassemblyParamToSpeckle(p)).ToList(); + + CivilAppliedSubassembly speckleAppliedSubassembly = new(appliedSubassembly.SubassemblyId.ToString(), subassembly.Name, speckleShapes, soePoint, speckleParameters) + { + units = ModelUnits + }; + + return speckleAppliedSubassembly; + } + + private CivilAppliedAssembly AppliedAssemblyToSpeckle(AppliedAssembly appliedAssembly) + { + // get the applied subassemblies + List speckleSubassemblies = new(); + foreach (AppliedSubassembly appliedSubassembly in appliedAssembly.GetAppliedSubassemblies()) + { + CivilAppliedSubassembly speckleSubassembly = AppliedSubassemblyToSpeckle(appliedSubassembly); + speckleSubassemblies.Add(speckleSubassembly); + } + + double? adjustedElevation = null; + try + { + adjustedElevation = appliedAssembly.AdjustedElevation; + } + catch (ArgumentException e) when (!e.IsFatal()) + { + // Do nothing. Leave the value as null. + } + + CivilAppliedAssembly speckleAppliedAssembly = new(speckleSubassemblies, adjustedElevation, ModelUnits); + + return speckleAppliedAssembly; + } + + private CivilBaselineRegion BaselineRegionToSpeckle(BaselineRegion region) + { + // get the region assembly + Assembly assembly = Trans.GetObject(region.AssemblyId, OpenMode.ForRead) as Assembly; + + // get the applied assemblies by station + List speckleAppliedAssemblies = new(); + double[] sortedStations = region.SortedStations(); + for (int i = 0; i < sortedStations.Length; i++) + { + double station = sortedStations[i]; + CivilAppliedAssembly speckleAssembly = AppliedAssemblyToSpeckle(region.AppliedAssemblies[i]); + speckleAssembly["station"] = station; + speckleAppliedAssemblies.Add(speckleAssembly); + } + + // create the speckle region + CivilBaselineRegion speckleRegion = new(region.Name, region.StartStation, region.EndStation, assembly.Id.ToString(), assembly.Name, speckleAppliedAssemblies) + { + units = ModelUnits + }; + + return speckleRegion; + } + + private CivilCalculatedShape CalculatedShapeToSpeckle(CalculatedShape shape) + { + List codes = shape.CorridorCodes.ToList(); + List speckleLinks = new(); + foreach (CalculatedLink link in shape.CalculatedLinks) + { + CivilCalculatedLink speckleLink = CalculatedLinkToSpeckle(link); + speckleLinks.Add(speckleLink); + } + + CivilCalculatedShape speckleCalculatedShape = new(codes, speckleLinks, shape.Area, ModelUnits); + return speckleCalculatedShape; + } + + private CivilCalculatedLink CalculatedLinkToSpeckle(CalculatedLink link) + { + List codes = link.CorridorCodes.ToList(); + List specklePoints = new(); + foreach (CalculatedPoint point in link.CalculatedPoints) + { + CivilCalculatedPoint specklePoint = CalculatedPointToSpeckle(point); + specklePoints.Add(specklePoint); + } + + CivilCalculatedLink speckleLink = new(codes, specklePoints) + { + units = ModelUnits + }; + + return speckleLink; + } + + private CivilCalculatedPoint CalculatedPointToSpeckle(CalculatedPoint point) + { + Point specklePoint = PointToSpeckle(point.XYZ); + List codes = point.CorridorCodes.ToList(); + Vector normalBaseline = VectorToSpeckle(point.NormalToBaseline); + Vector normalSubAssembly = VectorToSpeckle(point.NormalToSubassembly); + Point soePoint = PointToSpeckle(point.StationOffsetElevationToBaseline); + CivilCalculatedPoint speckleCalculatedPoint = new(specklePoint, codes, normalBaseline, normalSubAssembly, soePoint) + { + units = ModelUnits + }; + + return speckleCalculatedPoint; + } + + private CivilBaseline BaselineToSpeckle(CivilDB.Baseline baseline) + { + CivilBaseline speckleBaseline = null; + + // get the speckle regions + List speckleRegions = new(); + foreach (BaselineRegion region in baseline.BaselineRegions) + { + CivilBaselineRegion speckleRegion = BaselineRegionToSpeckle(region); + speckleRegions.Add(speckleRegion); + } + + // get profile and alignment if nonfeaturelinebased + // for featureline based corridors, accessing AlignmentId and ProfileId will return NULL + // and throw an exception ""This operation on feature line based baseline is invalid". + if (!baseline.IsFeatureLineBased()) + { + // get the speckle alignment + var alignment = Trans.GetObject(baseline.AlignmentId, OpenMode.ForRead) as CivilDB.Alignment; + CivilAlignment speckleAlignment = AlignmentToSpeckle(alignment); + + // get the speckle profile + var profile = Trans.GetObject(baseline.ProfileId, OpenMode.ForRead) as CivilDB.Profile; + CivilProfile speckleProfile = ProfileToSpeckle(profile); + + speckleBaseline = new(baseline.Name, speckleRegions, baseline.SortedStations().ToList(), baseline.StartStation, baseline.EndStation, speckleAlignment, speckleProfile) + { + units = ModelUnits + }; + } + else + { + // get the baseline featureline + var featureline = Trans.GetObject(baseline.FeatureLineId, OpenMode.ForRead) as CivilDB.FeatureLine; + Featureline speckleFeatureline = FeaturelineToSpeckle(featureline); + + speckleBaseline = new(baseline.Name, speckleRegions, baseline.SortedStations().ToList(), baseline.StartStation, baseline.EndStation, speckleFeatureline) + { + units = ModelUnits + }; + } + + return speckleBaseline; + } + public Base CorridorToSpeckle(Corridor corridor) { - List alignments = new(); - List profiles = new(); List featurelines = new(); - foreach (Baseline baseline in corridor.Baselines) + List baselines = new(); + foreach (CivilDB.Baseline baseline in corridor.Baselines) { + CivilBaseline speckleBaseline = BaselineToSpeckle(baseline); + baselines.Add(speckleBaseline); // get the collection of featurelines for this baseline foreach (FeatureLineCollection mainFeaturelineCollection in baseline.MainBaselineFeatureLines.FeatureLineCollectionMap) // main featurelines @@ -1143,32 +1367,6 @@ public Base CorridorToSpeckle(Corridor corridor) } } } - - // get alignment and profile if relevant - // for featureline based corridors, accessing AlignmentId and ProfileId will return NULL - // and throw an exception ""This operation on feature line based baseline is invalid". - if (!baseline.IsFeatureLineBased()) - { - if (baseline.AlignmentId is ObjectId alignmentId) - { - var alignment = Trans.GetObject(alignmentId, OpenMode.ForRead) as CivilDB.Alignment; - var convertedAlignment = AlignmentToSpeckle(alignment); - if (convertedAlignment != null) - { - alignments.Add(convertedAlignment); - } - } - - if (baseline.ProfileId is ObjectId profileId) - { - var profile = Trans.GetObject(profileId, OpenMode.ForRead) as CivilDB.Profile; - var convertedProfile = ProfileToSpeckle(profile); - if (convertedProfile != null) - { - profiles.Add(convertedProfile); - } - } - } } // get corridor surfaces @@ -1190,9 +1388,8 @@ public Base CorridorToSpeckle(Corridor corridor) } var corridorBase = new Base(); - corridorBase["@alignments"] = alignments; - corridorBase["@profiles"] = profiles; corridorBase["@featurelines"] = featurelines; + corridorBase["@baselines"] = baselines; AddNameAndDescriptionProperty(corridor.Name, corridor.Description, corridorBase); corridorBase["units"] = ModelUnits; if (surfaces.Count > 0) diff --git a/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/ConverterAutocadCivil.cs b/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/ConverterAutocadCivil.cs index 957fff734b..96bd06d9f1 100644 --- a/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/ConverterAutocadCivil.cs +++ b/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/ConverterAutocadCivil.cs @@ -206,7 +206,7 @@ public Base ConvertToSpeckle(object @object) @base = CorridorToSpeckle(o); break; case CivilDB.FeatureLine o: - @base = FeatureLineToSpeckle(o); + @base = FeaturelineToSpeckle(o); break; case CivilDB.Structure o: @base = StructureToSpeckle(o); diff --git a/Objects/Converters/ConverterCSI/ConverterCSIShared/ConverterCSIUtils.cs b/Objects/Converters/ConverterCSI/ConverterCSIShared/ConverterCSIUtils.cs index 61dd48d6f6..49ef57d0ea 100644 --- a/Objects/Converters/ConverterCSI/ConverterCSIShared/ConverterCSIUtils.cs +++ b/Objects/Converters/ConverterCSI/ConverterCSIShared/ConverterCSIUtils.cs @@ -27,9 +27,25 @@ public string ModelUnits() return _modelUnits; } +#if ETABS || SAFE + + _modelUnits = GetModelUnitsFromETABS(); + +#else + + _modelUnits = GetModelUnitsFromSAP(); + +#endif + + return _modelUnits; + } + + public string GetModelUnitsFromETABS() + { eForce forceUnits = eForce.NotApplicable; eLength lengthUnits = eLength.NotApplicable; eTemperature temperatureUnits = eTemperature.NotApplicable; + // GetPresentUnits_2() works for ETABS and SAFE _ = Model.GetPresentUnits_2(ref forceUnits, ref lengthUnits, ref temperatureUnits); if (lengthUnits == eLength.NotApplicable) @@ -37,8 +53,19 @@ public string ModelUnits() throw new SpeckleException("Unable to retreive valid length units from the ETABS document"); } - _modelUnits = lengthUnits.ToString(); - return _modelUnits; + return lengthUnits.ToString(); + } + + public string GetModelUnitsFromSAP() + { + // GetPresentUnits() works for SAP 2000 and CSIBridge + var units = Model.GetPresentUnits(); + if (units != 0) + { + string[] unitsCat = units.ToString().Split('_'); + return unitsCat[1]; + } + throw new SpeckleException("Unable to retreive valid length units from the SAP2000 document"); } public double ScaleToNative(double value, string units) diff --git a/Objects/Converters/ConverterCSI/ConverterCSIShared/PartialClasses/Geometry/ConvertFrame.cs b/Objects/Converters/ConverterCSI/ConverterCSIShared/PartialClasses/Geometry/ConvertFrame.cs index 437e01d621..1d46e480aa 100644 --- a/Objects/Converters/ConverterCSI/ConverterCSIShared/PartialClasses/Geometry/ConvertFrame.cs +++ b/Objects/Converters/ConverterCSI/ConverterCSIShared/PartialClasses/Geometry/ConvertFrame.cs @@ -8,6 +8,7 @@ using System.Linq; using CSiAPIv1; using Speckle.Core.Kits; +using Speckle.Core.Logging; namespace Objects.Converter.CSI; @@ -167,18 +168,13 @@ public CSIElement1D FrameToSpeckle(string name) { string units = ModelUnits(); - var speckleStructFrame = new CSIElement1D(); - - speckleStructFrame.name = name; string pointI, pointJ; pointI = pointJ = null; _ = Model.FrameObj.GetPoints(name, ref pointI, ref pointJ); var pointINode = PointToSpeckle(pointI); var pointJNode = PointToSpeckle(pointJ); - speckleStructFrame.end1Node = pointINode; - speckleStructFrame.end2Node = pointJNode; - var speckleLine = new Line(); + Line speckleLine; if (units != null) { speckleLine = new Line(pointINode.basePoint, pointJNode.basePoint, units); @@ -187,38 +183,22 @@ public CSIElement1D FrameToSpeckle(string name) { speckleLine = new Line(pointINode.basePoint, pointJNode.basePoint); } - speckleStructFrame.baseLine = speckleLine; + + string property, + SAuto; + property = SAuto = null; + Model.FrameObj.GetSection(name, ref property, ref SAuto); + var speckleProperty = Property1DToSpeckle(property); + eFrameDesignOrientation frameDesignOrientation = eFrameDesignOrientation.Null; Model.FrameObj.GetDesignOrientation(name, ref frameDesignOrientation); - switch (frameDesignOrientation) + var elementType = FrameDesignOrientationToElement1dType(frameDesignOrientation); + var speckleStructFrame = new CSIElement1D(speckleLine, speckleProperty, elementType) { - case eFrameDesignOrientation.Column: - { - speckleStructFrame.type = ElementType1D.Column; - break; - } - case eFrameDesignOrientation.Beam: - { - speckleStructFrame.type = ElementType1D.Beam; - break; - } - case eFrameDesignOrientation.Brace: - { - speckleStructFrame.type = ElementType1D.Brace; - break; - } - case eFrameDesignOrientation.Null: - { - //speckleStructFrame.memberType = MemberType.Generic1D; - speckleStructFrame.type = ElementType1D.Null; - break; - } - case eFrameDesignOrientation.Other: - { - speckleStructFrame.type = ElementType1D.Other; - break; - } - } + name = name, + end1Node = pointINode, + end2Node = pointJNode + }; bool[] iRelease, jRelease; @@ -238,12 +218,6 @@ public CSIElement1D FrameToSpeckle(string name) Model.FrameObj.GetLocalAxes(name, ref localAxis, ref advanced); speckleStructFrame.orientationAngle = localAxis; - string property, - SAuto; - property = SAuto = null; - Model.FrameObj.GetSection(name, ref property, ref SAuto); - speckleStructFrame.property = Property1DToSpeckle(property); - double offSetEnd1 = 0; double offSetEnd2 = 0; double RZ = 0; @@ -331,6 +305,17 @@ public CSIElement1D FrameToSpeckle(string name) return speckleStructFrame; } + private static ElementType1D FrameDesignOrientationToElement1dType(eFrameDesignOrientation frameDesignOrientation) => + frameDesignOrientation switch + { + eFrameDesignOrientation.Column => ElementType1D.Column, + eFrameDesignOrientation.Beam => ElementType1D.Beam, + eFrameDesignOrientation.Brace => ElementType1D.Brace, + eFrameDesignOrientation.Null => ElementType1D.Null, + eFrameDesignOrientation.Other => ElementType1D.Other, + _ => throw new SpeckleException($"Unrecognized eFrameDesignOrientation value, {frameDesignOrientation}"), + }; + public void SetFrameElementProperties(Element1D element1D, string newFrame, IList? log) { bool[] end1Release = null; diff --git a/Objects/Converters/ConverterCSI/ConverterCSIShared/PartialClasses/Geometry/ConvertLinks.cs b/Objects/Converters/ConverterCSI/ConverterCSIShared/PartialClasses/Geometry/ConvertLinks.cs index 205111dfa6..e1f6626b2e 100644 --- a/Objects/Converters/ConverterCSI/ConverterCSIShared/PartialClasses/Geometry/ConvertLinks.cs +++ b/Objects/Converters/ConverterCSI/ConverterCSIShared/PartialClasses/Geometry/ConvertLinks.cs @@ -6,6 +6,7 @@ using Objects.Structural.CSI.Properties; using System.Linq; using Speckle.Core.Kits; +using Objects.Structural.Properties; namespace Objects.Converter.CSI; @@ -46,18 +47,12 @@ public CSIElement1D LinkToSpeckle(string name) { string units = ModelUnits(); - var speckleStructLink = new CSIElement1D(); - - speckleStructLink.type = ElementType1D.Link; - speckleStructLink.name = name; string pointI, pointJ; pointI = pointJ = null; _ = Model.LinkObj.GetPoints(name, ref pointI, ref pointJ); var pointINode = PointToSpeckle(pointI); var pointJNode = PointToSpeckle(pointJ); - speckleStructLink.end1Node = pointINode; - speckleStructLink.end2Node = pointJNode; var speckleLine = new Line(); if (units != null) { @@ -67,17 +62,21 @@ public CSIElement1D LinkToSpeckle(string name) { speckleLine = new Line(pointINode.basePoint, pointJNode.basePoint); } - speckleStructLink.baseLine = speckleLine; + + string linkProp = null; + Model.LinkObj.GetProperty(name, ref linkProp); + Property1D property = LinkPropertyToSpeckle(linkProp); + + var speckleStructLink = new CSIElement1D(speckleLine, property, ElementType1D.Link); double localAxis = 0; bool advanced = false; Model.LinkObj.GetLocalAxes(name, ref localAxis, ref advanced); speckleStructLink.orientationAngle = localAxis; - speckleStructLink.type = ElementType1D.Link; - string linkProp = null; - Model.LinkObj.GetProperty(name, ref linkProp); - speckleStructLink.property = LinkPropertyToSpeckle(linkProp); + speckleStructLink.name = name; + speckleStructLink.end1Node = pointINode; + speckleStructLink.end2Node = pointJNode; var GUID = ""; Model.LinkObj.GetGUID(name, ref GUID); diff --git a/Objects/Converters/ConverterNavisworks/ConverterNavisworks/ConverterNavisworks.Properties.cs b/Objects/Converters/ConverterNavisworks/ConverterNavisworks/ConverterNavisworks.Properties.cs index 34164afc93..09e1cf4b1d 100644 --- a/Objects/Converters/ConverterNavisworks/ConverterNavisworks/ConverterNavisworks.Properties.cs +++ b/Objects/Converters/ConverterNavisworks/ConverterNavisworks/ConverterNavisworks.Properties.cs @@ -78,8 +78,6 @@ Base propertyCategoryBase var useInternalNames = UseInternalPropertyNames; UpdatePropertyCategoryBase(propertyCategoryBase, useInternalNames ? internalName : propertyName, propertyValue); - - propertyCategoryBase.applicationId = propertyCategory.CombinedName.ToString(); } private static string GetSanitizedPropertyName(string displayName) @@ -195,40 +193,57 @@ private static void UpdatePropertyCategoryBase(Base propertyCategoryBase, string } } + /// + /// Adds properties of a given ModelItem to a Base object. + /// + /// The ModelItem whose properties are to be added. + /// The Base object to which the properties are to be added. private static void AddItemProperties(ModelItem element, Base @base) { @base["class"] = element.ClassName; - bool properties = ShouldIncludeProperties(); - + if (ExcludeProperties) + { + return; + } // Cascade through the Property Sets - @base["properties"] = properties ? GetPropertiesBase(element) : new Base(); + @base["properties"] = GetPropertiesBase(element); // If the node is a Model if (element.HasModel) { - ((Base)@base["properties"])["Model"] = GetModelProperties(element.Model); + (((Base)@base["properties"])!)["Model"] = GetModelProperties(element.Model); } // Internal Properties - AddInternalProperties(element, (Base)@base["properties"]); + if (IncludeInternalProperties) + { + AddInternalProperties(element, (Base)@base["properties"]); + } } - private static bool ShouldIncludeProperties() => - !bool.TryParse(Settings.FirstOrDefault(x => x.Key == "include-properties").Value, out bool result) || result; - + /// + /// Adds internal properties of a given ModelItem to a Base object. + /// + /// The ModelItem whose properties are to be added. + /// The Base object to which the properties are to be added. private static void AddInternalProperties(ModelItem element, Base propertiesBase) { Base internals = (Base)propertiesBase["Internal"] ?? new Base(); - internals["ClassDisplayName"] = element.ClassDisplayName ?? internals["ClassDisplayName"]; - internals["ClassName"] = element.ClassName ?? internals["ClassName"]; - internals["DisplayName"] = element.DisplayName ?? internals["DisplayName"]; - internals["InstanceGuid"] = - element.InstanceGuid.ToByteArray().Select(x => (int)x).Sum() > 0 ? element.InstanceGuid : null; - internals["Source"] = element.Model?.SourceFileName ?? internals["Source"]; - internals["Source Guid"] = element.Model?.SourceGuid ?? internals["Source Guid"]; - internals["NodeType"] = element.IsCollection + AddPropertyIfNotNullOrEmpty(internals, "ClassDisplayName", element.ClassDisplayName); + AddPropertyIfNotNullOrEmpty(internals, "ClassName", element.ClassName); + AddPropertyIfNotNullOrEmpty(internals, "DisplayName", element.DisplayName); + + if (element.InstanceGuid.ToByteArray().Select(x => (int)x).Sum() > 0) + { + internals["InstanceGuid"] = element.InstanceGuid; + } + + AddPropertyIfNotNullOrEmpty(internals, "Source", element.Model?.SourceFileName); + AddPropertyIfNotNullOrEmpty(internals, "Source Guid", element.Model?.SourceGuid); + + string nodeType = element.IsCollection ? "Collection" : element.IsComposite ? "Composite Object" @@ -238,9 +253,32 @@ private static void AddInternalProperties(ModelItem element, Base propertiesBase ? "Layer" : null; + AddPropertyIfNotNullOrEmpty(internals, "NodeType", nodeType); + propertiesBase["Internal"] = internals; } + /// + /// Adds a property to a Base object if the value is not null or empty. + /// + /// The Base object to which the property is to be added. + /// The name of the property to add. + /// The value of the property. + private static void AddPropertyIfNotNullOrEmpty(Base baseObject, string propertyName, object value) + { + if (value is string stringValue) + { + if (!string.IsNullOrEmpty(stringValue)) + { + baseObject[propertyName] = value; + } + } + else if (value != null) + { + baseObject[propertyName] = value; + } + } + private static Base GetModelProperties(Model elementModel) { Base model = diff --git a/Objects/Converters/ConverterNavisworks/ConverterNavisworks/ConverterNavisworks.Settings.cs b/Objects/Converters/ConverterNavisworks/ConverterNavisworks/ConverterNavisworks.Settings.cs index 780381fe2f..931a4f1f3b 100644 --- a/Objects/Converters/ConverterNavisworks/ConverterNavisworks/ConverterNavisworks.Settings.cs +++ b/Objects/Converters/ConverterNavisworks/ConverterNavisworks/ConverterNavisworks.Settings.cs @@ -70,6 +70,32 @@ private static Units CoordinateUnits } } + private static bool ExcludeProperties + { + get + { + if (!Settings.TryGetValue("exclude-properties", out string shouldExcludeProperties)) + { + return false; + } + + return shouldExcludeProperties == "True"; + } + } + + private static bool IncludeInternalProperties + { + get + { + if (!Settings.TryGetValue("internal-properties", out string shouldIncludeInternalProperties)) + { + return false; + } + + return shouldIncludeInternalProperties == "True"; + } + } + private static bool UseInternalPropertyNames { get diff --git a/Objects/Converters/ConverterRevit/ConverterRevitShared/AllRevitCategories.cs b/Objects/Converters/ConverterRevit/ConverterRevitShared/AllRevitCategories.cs index cb51ab0a0f..14edc8539f 100644 --- a/Objects/Converters/ConverterRevit/ConverterRevitShared/AllRevitCategories.cs +++ b/Objects/Converters/ConverterRevit/ConverterRevitShared/AllRevitCategories.cs @@ -36,27 +36,45 @@ public IRevitCategoryInfo GetRevitCategoryInfo(Base @base) return elementType; } - var matchingType = revitDocumentAggregateCache - .GetOrInitializeWithDefaultFactory() - .GetAllObjects() - .Where(catInfo => catInfo.ElementTypeType == typeof(T) && catInfo.BuiltInCategories.Count == 0) - .FirstOrDefault(); + IRevitCategoryInfo matchingType; + + if (revitDocumentAggregateCache is null) + { + matchingType = elementType; + } + else + { + matchingType = revitDocumentAggregateCache + .GetOrInitializeWithDefaultFactory() + .GetAllObjects() + .Where(catInfo => catInfo.ElementTypeType == typeof(T) && catInfo.BuiltInCategories.Count == 0) + .FirstOrDefault(); + } if (matchingType != null) { return matchingType; } - var categoryInfo = revitDocumentAggregateCache - .GetOrInitializeWithDefaultFactory() - .GetOrAdd( - typeof(T).Name, - () => - { - return new RevitCategoryInfo(typeof(T).Name, null, typeof(T), new List()); - }, - out _ - ); + IRevitCategoryInfo categoryInfo; + + if (revitDocumentAggregateCache is null) + { + categoryInfo = new RevitCategoryInfo(typeof(T).Name, null, typeof(T), new List()); + } + else + { + categoryInfo = revitDocumentAggregateCache + .GetOrInitializeWithDefaultFactory() + .GetOrAdd( + typeof(T).Name, + () => + { + return new RevitCategoryInfo(typeof(T).Name, null, typeof(T), new List()); + }, + out _ + ); + } return categoryInfo; } @@ -128,30 +146,26 @@ public IRevitCategoryInfo GetRevitCategoryInfo(string categoryName) } categoryName = CategoryNameFormatted(categoryName); - var revitCategoryInfoCache = revitDocumentAggregateCache.GetOrInitializeWithDefaultFactory(); - categoryInfo = revitCategoryInfoCache.TryGet(categoryName); - if (categoryInfo != null) + IRevitObjectCache revitCategoryInfoCache; + if (revitDocumentAggregateCache is not null) { - return categoryInfo; - } + revitCategoryInfoCache = revitDocumentAggregateCache.GetOrInitializeWithDefaultFactory(); - foreach (var info in revitCategoryInfoCache.GetAllObjects()) - { - if (categoryName.IndexOf(info.CategoryName, StringComparison.OrdinalIgnoreCase) >= 0) + categoryInfo = revitCategoryInfoCache.TryGet(categoryName); + if (categoryInfo != null) { - return info; + return categoryInfo; } - foreach (var alias in info.CategoryAliases) + else { - if (categoryName.IndexOf(alias, StringComparison.OrdinalIgnoreCase) >= 0) - { - return info; - } + return SHC.Undefined; } } - - return SHC.Undefined; + else + { + return SHC.Undefined; + } } #endregion @@ -173,29 +187,43 @@ public IRevitCategoryInfo GetRevitCategoryInfo(string categoryName) // pre 2.16 we're passing the "category" string else { - var revitCat = revitDocumentAggregateCache - .GetOrInitializeWithDefaultFactory() - .TryGet(unformattedCatName); - - if (revitCat == null) + if (revitDocumentAggregateCache is null) { return null; } + else + { + var revitCat = revitDocumentAggregateCache + .GetOrInitializeWithDefaultFactory() + .TryGet(unformattedCatName); - bic = Categories.GetBuiltInCategory(revitCat); - formattedName = CategoryNameFormatted(unformattedCatName); + if (revitCat == null) + { + return null; + } + + bic = Categories.GetBuiltInCategory(revitCat); + formattedName = CategoryNameFormatted(unformattedCatName); + } } - return revitDocumentAggregateCache - .GetOrInitializeWithDefaultFactory() - .GetOrAdd( - formattedName, - () => - { - return new RevitCategoryInfo(formattedName, null, null, new List { bic }); - }, - out _ - ); + if (revitDocumentAggregateCache is null) + { + return new RevitCategoryInfo(formattedName, null, null, new List { bic }); + } + else + { + return revitDocumentAggregateCache + .GetOrInitializeWithDefaultFactory() + .GetOrAdd( + formattedName, + () => + { + return new RevitCategoryInfo(formattedName, null, null, new List { bic }); + }, + out _ + ); + } } private static string CategoryNameFormatted(string name) diff --git a/Objects/Converters/ConverterRevit/ConverterRevitShared/ConversionUtils.cs b/Objects/Converters/ConverterRevit/ConverterRevitShared/ConversionUtils.cs index 5aae514f7a..307cca0e29 100644 --- a/Objects/Converters/ConverterRevit/ConverterRevitShared/ConversionUtils.cs +++ b/Objects/Converters/ConverterRevit/ConverterRevitShared/ConversionUtils.cs @@ -390,39 +390,46 @@ private Parameter ParameterToSpeckle( #else ForgeTypeId unitTypeId = null; #endif + ParameterToSpeckleData paramData; + + // Local function to create ParameterToSpeckleData + ParameterToSpeckleData CreateParamData() + { + var definition = rp.Definition; + var newParamData = new ParameterToSpeckleData() + { + Definition = definition, + InternalName = paramInternalName, + IsReadOnly = rp.IsReadOnly, + IsShared = rp.IsShared, + IsTypeParameter = isTypeParameter, + Name = definition.Name, + UnitType = definition.GetUnityTypeString(), + }; + if (rp.StorageType == StorageType.Double) + { + unitTypeId = rp.GetUnitTypeId(); + newParamData.UnitsSymbol = GetSymbolUnit(rp, definition, unitTypeId); + newParamData.ApplicationUnits = + unitsOverride != null ? UnitsToNative(unitsOverride).ToUniqueString() : unitTypeId.ToUniqueString(); + } + return newParamData; + } // The parameter definitions are cached using the ParameterToSpeckleData struct // This is done because in the case of type and instance parameter there is lots of redundant data that needs to be extracted from the Revit DB // Caching noticeably speeds up the send process // TODO : could add some generic getOrAdd overloads to avoid creating closures - var paramData = revitDocumentAggregateCache - .GetOrInitializeEmptyCacheOfType(out _) - .GetOrAdd( - paramInternalName, - () => - { - var definition = rp.Definition; - var newParamData = new ParameterToSpeckleData() - { - Definition = definition, - InternalName = paramInternalName, - IsReadOnly = rp.IsReadOnly, - IsShared = rp.IsShared, - IsTypeParameter = isTypeParameter, - Name = definition.Name, - UnitType = definition.GetUnityTypeString(), - }; - if (rp.StorageType == StorageType.Double) - { - unitTypeId = rp.GetUnitTypeId(); - newParamData.UnitsSymbol = GetSymbolUnit(rp, definition, unitTypeId); - newParamData.ApplicationUnits = - unitsOverride != null ? UnitsToNative(unitsOverride).ToUniqueString() : unitTypeId.ToUniqueString(); - } - return newParamData; - }, - out _ - ); + if (revitDocumentAggregateCache is null) + { + paramData = CreateParamData(); + } + else + { + paramData = revitDocumentAggregateCache + .GetOrInitializeEmptyCacheOfType(out _) + .GetOrAdd(paramInternalName, CreateParamData, out _); + } return paramData.GetParameterObjectWithValue(rp.GetValue(paramData.Definition, unitTypeId)); } @@ -450,9 +457,16 @@ ForgeTypeId unitTypeId return null; } - return revitDocumentAggregateCache - .GetOrInitializeEmptyCacheOfType(out _) - .GetOrAdd(unitTypeId.ToUniqueString(), () => unitTypeId.GetSymbol(), out _); + if (revitDocumentAggregateCache is null) + { + return unitTypeId.GetSymbol(); + } + else + { + return revitDocumentAggregateCache + .GetOrInitializeEmptyCacheOfType(out _) + .GetOrAdd(unitTypeId.ToUniqueString(), () => unitTypeId.GetSymbol(), out _); + } } /// diff --git a/Objects/Converters/ConverterRevit/ConverterRevitShared/ConverterRevit.cs b/Objects/Converters/ConverterRevit/ConverterRevitShared/ConverterRevit.cs index 337f32a2f4..36fc3ba036 100644 --- a/Objects/Converters/ConverterRevit/ConverterRevitShared/ConverterRevit.cs +++ b/Objects/Converters/ConverterRevit/ConverterRevitShared/ConverterRevit.cs @@ -1,3 +1,4 @@ +#nullable enable using System; using System.Collections.Generic; using System.Linq; @@ -109,7 +110,7 @@ public ConverterRevit() Report.Log($"Using converter: {Name} v{ver}"); } - private IRevitDocumentAggregateCache revitDocumentAggregateCache; + private IRevitDocumentAggregateCache? revitDocumentAggregateCache; private IConvertedObjectsCache receivedObjectsCache; private TransactionManager transactionManager; @@ -733,6 +734,9 @@ public object ConvertToNativeObject(Base @object) case PolygonElement o: return PolygonElementToNative(o); + case GisFeature o: + return GisFeatureToNative(o); + #if (REVIT2024) case RevitToposolid o: return ToposolidToNative(o); @@ -909,6 +913,13 @@ public bool CanConvertToNative(Base @object) Organization.DataTable _ => true, // GIS PolygonElement _ => true, + GisFeature feat + when ( + feat.GetMembers(DynamicBaseMemberType.All).TryGetValue("displayValue", out var value) + && value is List valueList + && valueList.Count > 0 + ) + => true, _ => false, }; if (objRes) diff --git a/Objects/Converters/ConverterRevit/ConverterRevitShared/Extensions/ParameterExtensions.cs b/Objects/Converters/ConverterRevit/ConverterRevitShared/Extensions/ParameterExtensions.cs index 0103f15db5..319a46ac65 100644 --- a/Objects/Converters/ConverterRevit/ConverterRevitShared/Extensions/ParameterExtensions.cs +++ b/Objects/Converters/ConverterRevit/ConverterRevitShared/Extensions/ParameterExtensions.cs @@ -49,7 +49,9 @@ internal static class ParameterExtensions case StorageType.String: return parameter.AsString(); case StorageType.ElementId: - return parameter.AsElementId().ToString(); + ElementId id = parameter.AsElementId(); + Element el = parameter.Element.Document.GetElement(id); + return el?.Name ?? id.ToString(); default: return null; } diff --git a/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertArea.cs b/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertArea.cs index 7d3e199be0..8c43b38e20 100644 --- a/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertArea.cs +++ b/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertArea.cs @@ -1,6 +1,13 @@ +using System; +using System.Collections.Generic; using System.Linq; using Autodesk.Revit.DB; +using Objects.Geometry; +using Objects.Utils; +using Speckle.Core.Models; using DB = Autodesk.Revit.DB; +using Line = Objects.Geometry.Line; +using Mesh = Objects.Geometry.Mesh; using Point = Objects.Geometry.Point; namespace Objects.Converter.Revit; @@ -44,13 +51,14 @@ public BuiltElements.Area AreaToSpeckle(DB.Area revitArea) { var profiles = GetProfiles(revitArea); - var speckleArea = new BuiltElements.Area(); - - speckleArea.name = revitArea.get_Parameter(BuiltInParameter.ROOM_NAME).AsString(); - speckleArea.number = revitArea.Number; - speckleArea.center = (Point)LocationToSpeckle(revitArea); - speckleArea.level = ConvertAndCacheLevel(revitArea, BuiltInParameter.ROOM_LEVEL_ID); - if (profiles.Any()) + var speckleArea = new BuiltElements.Area + { + name = revitArea.get_Parameter(BuiltInParameter.ROOM_NAME).AsString(), + number = revitArea.Number, + center = (Point)LocationToSpeckle(revitArea), + level = ConvertAndCacheLevel(revitArea, BuiltInParameter.ROOM_LEVEL_ID) + }; + if (profiles.Count != 0) { speckleArea.outline = profiles[0]; } @@ -63,8 +71,57 @@ public BuiltElements.Area AreaToSpeckle(DB.Area revitArea) GetAllRevitParamsAndIds(speckleArea, revitArea); - //no mesh seems to be retriavable, not even using the SpatialElementGeometryCalculator + //no mesh seems to be retrievable, not even using the SpatialElementGeometryCalculator //speckleArea.displayValue = GetElementDisplayValue(revitArea); + + speckleArea.displayValue ??= new List(); + + if (profiles.Count != 0 && profiles[0] is Polycurve polyCurve) + { + speckleArea.displayValue.Add(polyCurve); + } + + // If life were simple this triangulation world be sufficient - we know areas are 2d planar - but could have curves. + // speckleArea.displayValue.Add(PolycurveToMesh(speckleArea.outline as Polycurve)); + return speckleArea; } + + private static Mesh PolycurveToMesh(Polycurve polycurve) + { + var mesh = new Mesh { units = polycurve.units }; + + // Convert all segments to Lines (assuming they are all Lines) + var segments = polycurve.segments.OfType().ToList(); + + var points = new List(); + foreach (var segment in segments.Where(segment => !PointExists(points, segment.start))) + { + points.Add(segment.start); + } + if (!polycurve.closed && !PointExists(points, segments.Last().end)) + { + points.Add(segments.Last().end); + } + + mesh.vertices = points.SelectMany(p => new List { p.x, p.y, p.z }).ToList(); + + mesh.faces = new List { points.Count }; // First element is the number of vertices in the face + mesh.faces.AddRange(Enumerable.Range(0, points.Count)); + + mesh.TriangulateMesh(); + + return mesh; + } + + private static bool PointExists(List points, Point newPoint) + { + const double TOLERANCE = 1e-6; // Adjust this tolerance as needed + return points.Any( + p => + Math.Abs(p.x - newPoint.x) < TOLERANCE + && Math.Abs(p.y - newPoint.y) < TOLERANCE + && Math.Abs(p.z - newPoint.z) < TOLERANCE + ); + } } diff --git a/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertBrace.cs b/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertBrace.cs index 0613b4d8ab..8de864d03a 100644 --- a/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertBrace.cs +++ b/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertBrace.cs @@ -1,8 +1,10 @@ +using System; using Autodesk.Revit.DB.Structure; using Objects.BuiltElements; using Objects.BuiltElements.Revit; using Speckle.Core.Models; using System.Collections.Generic; +using Objects.Geometry; using DB = Autodesk.Revit.DB; namespace Objects.Converter.Revit; @@ -37,18 +39,21 @@ public ApplicationObject BraceToNative(Brace speckleBrace) private Base BraceToSpeckle(DB.FamilyInstance myFamily, out List notes) { - notes = new List(); - var myBeam = BeamToSpeckle(myFamily, out notes) as RevitBeam; + var myBeam = (RevitBeam)BeamToSpeckle(myFamily, out notes); - var myBrace = new RevitBrace() + var myBrace = new RevitBrace( + myBeam.family, + myBeam.type, + myBeam.baseLine, + myBeam.level, + myBeam.units, + myBeam.elementId, + Array.Empty() + ) { + displayValue = myBeam.displayValue, applicationId = myBeam.applicationId, - type = myBeam.type, - baseLine = myBeam.baseLine, - level = myBeam.level, - family = myBeam.family, parameters = myBeam.parameters, - displayValue = myBeam.displayValue, }; var dynamicProps = myBeam.GetMembers(DynamicBaseMemberType.Dynamic); diff --git a/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertColumn.cs b/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertColumn.cs index 0f813d6165..94efd55a14 100644 --- a/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertColumn.cs +++ b/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertColumn.cs @@ -249,39 +249,29 @@ double topOffset public Base ColumnToSpeckle(DB.FamilyInstance revitColumn, out List notes) { notes = new List(); - var symbol = revitColumn.Document.GetElement(revitColumn.GetTypeId()) as FamilySymbol; - - var speckleColumn = new RevitColumn(); - speckleColumn.family = symbol.FamilyName; - speckleColumn.type = revitColumn.Document.GetElement(revitColumn.GetTypeId()).Name; - speckleColumn.level = ConvertAndCacheLevel(revitColumn, BuiltInParameter.FAMILY_BASE_LEVEL_PARAM); - speckleColumn.topLevel = ConvertAndCacheLevel(revitColumn, BuiltInParameter.FAMILY_TOP_LEVEL_PARAM); - speckleColumn.baseOffset = GetParamValue(revitColumn, BuiltInParameter.FAMILY_BASE_LEVEL_OFFSET_PARAM); - speckleColumn.topOffset = GetParamValue(revitColumn, BuiltInParameter.FAMILY_TOP_LEVEL_OFFSET_PARAM); - speckleColumn.facingFlipped = revitColumn.FacingFlipped; - speckleColumn.handFlipped = revitColumn.HandFlipped; - speckleColumn.isSlanted = revitColumn.IsSlantedColumn; - //speckleColumn.structural = revitColumn.StructuralType == StructuralType.Column; + var symbol = (FamilySymbol)revitColumn.Document.GetElement(revitColumn.GetTypeId()); + + RevitLevel level = ConvertAndCacheLevel(revitColumn, BuiltInParameter.FAMILY_BASE_LEVEL_PARAM); + RevitLevel topLevel = ConvertAndCacheLevel(revitColumn, BuiltInParameter.FAMILY_TOP_LEVEL_PARAM); + double baseOffset = GetParamValue(revitColumn, BuiltInParameter.FAMILY_BASE_LEVEL_OFFSET_PARAM); + double topOffset = GetParamValue(revitColumn, BuiltInParameter.FAMILY_TOP_LEVEL_OFFSET_PARAM); //geometry var baseGeometry = LocationToSpeckle(revitColumn); var baseLine = baseGeometry as ICurve; - //make line from point and height if (baseLine == null && baseGeometry is Point basePoint) + //make line from point and height { - if ( - symbol.Family.FamilyPlacementType == FamilyPlacementType.OneLevelBased - || symbol.Family.FamilyPlacementType == FamilyPlacementType.WorkPlaneBased - ) + if (symbol.Family.FamilyPlacementType is FamilyPlacementType.OneLevelBased or FamilyPlacementType.WorkPlaneBased) { return RevitInstanceToSpeckle(revitColumn, out notes, null); } - var elevation = speckleColumn.topLevel.elevation; + var elevation = topLevel.elevation; baseLine = new Line( basePoint, - new Point(basePoint.x, basePoint.y, elevation + speckleColumn.topOffset, ModelUnits), + new Point(basePoint.x, basePoint.y, elevation + topOffset, ModelUnits), ModelUnits ); } @@ -291,7 +281,25 @@ public Base ColumnToSpeckle(DB.FamilyInstance revitColumn, out List note return RevitElementToSpeckle(revitColumn, out notes); } - speckleColumn.baseLine = baseLine; //all speckle columns should be line based + double rotation = revitColumn.Location is LocationPoint location ? location.Rotation : 0; + + var speckleColumn = new RevitColumn( + symbol.FamilyName, + revitColumn.Document.GetElement(revitColumn.GetTypeId()).Name, + baseLine, //all speckle columns should be line based + level, + topLevel, + ModelUnits, + revitColumn.Id.ToString(), + baseOffset, + topOffset, + revitColumn.FacingFlipped, + revitColumn.HandFlipped, + revitColumn.IsSlantedColumn, + rotation, + GetElementDisplayValue(revitColumn) + //structural: revitColumn.StructuralType == StructuralType.Column; + ); GetAllRevitParamsAndIds( speckleColumn, @@ -307,13 +315,6 @@ public Base ColumnToSpeckle(DB.FamilyInstance revitColumn, out List note } ); - if (revitColumn.Location is LocationPoint) - { - speckleColumn.rotation = ((LocationPoint)revitColumn.Location).Rotation; - } - - speckleColumn.displayValue = GetElementDisplayValue(revitColumn); - return speckleColumn; } } diff --git a/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertPipe.cs b/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertPipe.cs index ac2d2ce760..a1d24fcb61 100644 --- a/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertPipe.cs +++ b/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertPipe.cs @@ -185,7 +185,7 @@ public BuiltElements.Pipe PipeToSpeckle(DB.Plumbing.Pipe revitPipe) baseCurve = baseLine, family = revitPipe.PipeType.FamilyName, type = revitPipe.PipeType.Name, - systemName = revitPipe.MEPSystem.Name, + systemName = revitPipe.MEPSystem?.Name ?? "", systemType = GetParamValue(revitPipe, BuiltInParameter.RBS_SYSTEM_CLASSIFICATION_PARAM), diameter = GetParamValue(revitPipe, BuiltInParameter.RBS_PIPE_DIAMETER_PARAM), length = GetParamValue(revitPipe, BuiltInParameter.CURVE_ELEM_LENGTH), @@ -242,7 +242,7 @@ public BuiltElements.Pipe PipeToSpeckle(DB.Plumbing.FlexPipe revitPipe) baseCurve = polyline, family = revitPipe.FlexPipeType.FamilyName, type = revitPipe.FlexPipeType.Name, - systemName = revitPipe.MEPSystem.Name, + systemName = revitPipe.MEPSystem?.Name ?? "", systemType = GetParamValue(revitPipe, BuiltInParameter.RBS_SYSTEM_CLASSIFICATION_PARAM), diameter = GetParamValue(revitPipe, BuiltInParameter.RBS_PIPE_DIAMETER_PARAM), length = GetParamValue(revitPipe, BuiltInParameter.CURVE_ELEM_LENGTH), diff --git a/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertPolygonElement.cs b/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertPolygonElement.cs index 890662766e..25f0dbcf0a 100644 --- a/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertPolygonElement.cs +++ b/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertPolygonElement.cs @@ -11,6 +11,34 @@ namespace Objects.Converter.Revit; public partial class ConverterRevit { + public ApplicationObject GisFeatureToNative(GisFeature gisFeature) + { + // targeting Polygon features with List of Meshes as dispayValue + var speckleDirectShape = new Objects.BuiltElements.Revit.DirectShape() + { + applicationId = gisFeature.applicationId ??= Guid.NewGuid().ToString(), + baseGeometries = new List(), + parameters = new Base(), + name = "", + category = RevitCategory.GenericModel + }; + + var traversal = new GraphTraversal(DefaultTraversal.DefaultRule); + var meshes = traversal.Traverse(gisFeature).Select(tc => tc.current).Where(b => b is Mesh); + + speckleDirectShape.baseGeometries.AddRange(meshes); + + foreach (var kvp in gisFeature.attributes.GetMembers()) + { + speckleDirectShape.parameters[kvp.Key] = new Objects.BuiltElements.Revit.Parameter() + { + name = kvp.Key, + value = kvp.Value + }; + } + return DirectShapeToNative(speckleDirectShape, ToNativeMeshSettingEnum.Default); + } + public ApplicationObject PolygonElementToNative(PolygonElement polygonElement) { var speckleDirectShape = new Objects.BuiltElements.Revit.DirectShape() diff --git a/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertView.Schedule.cs b/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertView.Schedule.cs index 2dc43fefce..bf6de53479 100644 --- a/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertView.Schedule.cs +++ b/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertView.Schedule.cs @@ -244,7 +244,7 @@ Element firstElement var columnMetadata = new Base(); columnMetadata["BuiltInParameterInteger"] = info.field.ParameterId.IntegerValue; - columnMetadata["FieldType"] = info.field.FieldType.ToString(); + string fieldType = info.field.FieldType.ToString(); Parameter param; if (info.field.FieldType == ScheduleFieldType.ElementType) @@ -257,10 +257,26 @@ Element firstElement } else if (info.field.FieldType == ScheduleFieldType.Instance) { + // All shared parameters also use this type, regardless of whether they are instance or type parameters. + // ref: https://www.revitapidocs.com/2024/9888db7d-00d0-4fd7-a1a9-cdd1fb5fce16.htm if (firstElement != null) { param = firstElement.get_Parameter(builtInParameter); - columnMetadata["IsReadOnly"] = param?.IsReadOnly; + + // if the parameter is shared, we need to check the type parameterer too + if (firstType != null) + { + Parameter typeParam = firstType.get_Parameter(builtInParameter); + + // If the parameter is readonly in the element but not in the type, is a type parameter + if (typeParam != null && !typeParam.IsReadOnly && param != null && param.IsReadOnly) + { + columnMetadata["IsReadOnly"] = false; + fieldType = ScheduleFieldType.ElementType.ToString(); + } + } + + columnMetadata["IsReadOnly"] ??= param?.IsReadOnly ?? true; } } else @@ -273,6 +289,8 @@ Element firstElement info.field.FieldType.ToString() ); } + + columnMetadata["FieldType"] = fieldType; speckleTable.DefineColumn(columnMetadata); } diff --git a/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertWall.cs b/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertWall.cs index 11bbd0ceaf..73033b0442 100644 --- a/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertWall.cs +++ b/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertWall.cs @@ -187,18 +187,21 @@ public Base WallToSpeckle(DB.Wall revitWall, out List notes) return RevitElementToSpeckle(revitWall, out notes); } - RevitWall speckleWall = new(); - speckleWall.family = revitWall.WallType.FamilyName.ToString(); - speckleWall.type = revitWall.WallType.Name; - speckleWall.baseLine = (ICurve)baseGeometry; - speckleWall.level = ConvertAndCacheLevel(revitWall, BuiltInParameter.WALL_BASE_CONSTRAINT); - speckleWall.topLevel = ConvertAndCacheLevel(revitWall, BuiltInParameter.WALL_HEIGHT_TYPE); - speckleWall.height = GetParamValue(revitWall, BuiltInParameter.WALL_USER_HEIGHT_PARAM); - speckleWall.baseOffset = GetParamValue(revitWall, BuiltInParameter.WALL_BASE_OFFSET); - speckleWall.topOffset = GetParamValue(revitWall, BuiltInParameter.WALL_TOP_OFFSET); - speckleWall.structural = GetParamValue(revitWall, BuiltInParameter.WALL_STRUCTURAL_SIGNIFICANT); - speckleWall.flipped = revitWall.Flipped; - + RevitWall speckleWall = + new( + revitWall.WallType.FamilyName, + revitWall.WallType.Name, + (ICurve)baseGeometry, + ConvertAndCacheLevel(revitWall, BuiltInParameter.WALL_BASE_CONSTRAINT), + ConvertAndCacheLevel(revitWall, BuiltInParameter.WALL_HEIGHT_TYPE), + GetParamValue(revitWall, BuiltInParameter.WALL_USER_HEIGHT_PARAM), + ModelUnits, + revitWall.Id.ToString(), + GetParamValue(revitWall, BuiltInParameter.WALL_BASE_OFFSET), + GetParamValue(revitWall, BuiltInParameter.WALL_TOP_OFFSET), + revitWall.Flipped, + GetParamValue(revitWall, BuiltInParameter.WALL_STRUCTURAL_SIGNIFICANT) + ); //CreateVoids(revitWall, speckleWall); if (revitWall.CurtainGrid is not CurtainGrid grid) @@ -261,19 +264,33 @@ private IEnumerable GetSubsetOfElementsInView(BuiltInCategory categor return children; } - var allSubelementsInView = revitDocumentAggregateCache - .GetOrInitializeEmptyCacheOfType>(out _) - .GetOrAdd( - category.ToString(), - () => - { - using var filter = new ElementCategoryFilter(category); - using var collector = new FilteredElementCollector(Doc, ViewSpecificOptions.View.Id); + var allSubelementsInView = new HashSet(); + + if (revitDocumentAggregateCache is null) + { + var filter = new ElementCategoryFilter(category); + var collector = new FilteredElementCollector(Doc, ViewSpecificOptions.View.Id); - return new HashSet(collector.WhereElementIsNotElementType().WherePasses(filter).ToElementIds()); - }, - out _ + allSubelementsInView = new HashSet( + collector.WhereElementIsNotElementType().WherePasses(filter).ToElementIds() ); + } + else + { + allSubelementsInView = revitDocumentAggregateCache + .GetOrInitializeEmptyCacheOfType>(out _) + .GetOrAdd( + category.ToString(), + () => + { + using var filter = new ElementCategoryFilter(category); + using var collector = new FilteredElementCollector(Doc, ViewSpecificOptions.View.Id); + + return new HashSet(collector.WhereElementIsNotElementType().WherePasses(filter).ToElementIds()); + }, + out _ + ); + } return children.Where(allSubelementsInView.Contains); } diff --git a/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertZone.cs b/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertZone.cs index a0a1e699ca..a12b1f32d3 100644 --- a/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertZone.cs +++ b/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertZone.cs @@ -146,7 +146,7 @@ private DB.Zone CreateRevitZoneIfNeeded(Space speckleSpace, DB.Zone revitZone, P { revitZone = ConvertZoneToRevit(speckleSpace.zone, out _); } - else if (revitZone != null && revitZone.Phase.Name != targetPhase.Name) + else if (revitZone != null && revitZone.Phase.Name != targetPhase.Name && speckleSpace.zone != null) { Doc.Delete(revitZone.Id); diff --git a/Objects/Converters/ConverterRevit/ConverterRevitShared/RevitElementTypeUtils.cs b/Objects/Converters/ConverterRevit/ConverterRevitShared/RevitElementTypeUtils.cs index f6ce277023..43678de1c8 100644 --- a/Objects/Converters/ConverterRevit/ConverterRevitShared/RevitElementTypeUtils.cs +++ b/Objects/Converters/ConverterRevit/ConverterRevitShared/RevitElementTypeUtils.cs @@ -140,6 +140,12 @@ public void SetElementFamily(Base @base, string family) appObj.Update(logItem: $"Could not find valid incoming type for element of type \"{element.speckle_type}\""); } var typeInfo = AllCategories.GetRevitCategoryInfo(element); + + if (revitDocumentAggregateCache is null) + { + return default; + } + var types = revitDocumentAggregateCache .GetOrInitializeWithDefaultFactory>() .GetOrAddGroupOfTypes(typeInfo); diff --git a/Objects/Converters/ConverterRhinoGh/ConverterRhinoGhShared/ConverterRhinoGh.GIS.cs b/Objects/Converters/ConverterRhinoGh/ConverterRhinoGhShared/ConverterRhinoGh.GIS.cs new file mode 100644 index 0000000000..cc766f2784 --- /dev/null +++ b/Objects/Converters/ConverterRhinoGh/ConverterRhinoGhShared/ConverterRhinoGh.GIS.cs @@ -0,0 +1,151 @@ +using System; +using System.Collections.Generic; +using Objects.GIS; +using Speckle.Core.Models; +using RH = Rhino.DocObjects; +using System.Linq; +using Rhino.Geometry; + +namespace Objects.Converter.RhinoGh; + +public partial class ConverterRhinoGh +{ + // polygon element + // NOTE: class no longer in use? from 2.19 + public ApplicationObject PolygonElementToNative(PolygonElement poly) + { + var appObj = new ApplicationObject(poly.id, poly.speckle_type) { applicationId = poly.applicationId }; + + // get the group name + var commitInfo = GetCommitInfo(); + string groupName = $"{commitInfo} - " + poly.id; + if (Doc.Groups.FindName(groupName) is RH.Group existingGroup) + { + Doc.Groups.Delete(existingGroup); + } + + List addedGeometry = new(); + foreach (object geo in poly.geometry) + { + if (geo is Base geoBase) + { + var display = geoBase["displayValue"] as List ?? geoBase["@displayValue"] as List; + if (display is null) + { + continue; + } + + foreach (object displayObject in display) + { + if (displayObject is Base baseObj) + { + if (ConvertToNative(baseObj) is GeometryBase convertedObject) + { + Guid id = Doc.Objects.Add(convertedObject); + if (id != Guid.Empty) + { + addedGeometry.Add(id); + } + } + } + } + } + } + + if (addedGeometry.Count == 0) + { + appObj.Update(status: ApplicationObject.State.Failed, logItem: "No objects were created for group"); + return appObj; + } + + int groupIndex = Doc.Groups.Add(groupName, addedGeometry); + if (groupIndex == -1) + { + appObj.Update(status: ApplicationObject.State.Failed, logItem: "Could not add group to doc"); + return appObj; + } + + RH.Group convertedGroup = Doc.Groups.FindIndex(groupIndex); + + // update appobj + appObj.Update(convertedItem: convertedGroup, createdIds: addedGeometry.Select(o => o.ToString()).ToList()); + + return appObj; + } + + // gis feature + public ApplicationObject GisFeatureToNative(GisFeature feature) + { + var appObj = new ApplicationObject(feature.id, feature.speckle_type) { applicationId = feature.applicationId }; + + // get the group name + var commitInfo = GetCommitInfo(); + string groupName = $"{commitInfo} - " + feature.id; + if (Doc.Groups.FindName(groupName) is RH.Group existingGroup) + { + Doc.Groups.Delete(existingGroup); + } + + // for gis features, we are assuming that the `displayValue prop` should be converted first + // if there are no objects in `displayValue`, then we will fall back to check for convertible objects in `geometries` + List convertedObjects = new(); + if (feature.displayValue is List displayValue && displayValue.Count > 0) + { + foreach (Base displayObj in displayValue) + { + if (ConvertToNative(displayObj) is GeometryBase convertedObject) + { + convertedObjects.Add(convertedObject); + } + } + } + else if (feature.geometry is List geometries && geometries.Count > 0) + { + foreach (Base displayObj in geometries) + { + if (ConvertToNative(displayObj) is GeometryBase convertedObject) + { + convertedObjects.Add(convertedObject); + } + } + } + else + { + appObj.Update( + status: ApplicationObject.State.Failed, + logItem: "No objects in displayValue or geometries was found" + ); + return appObj; + } + + List addedGeometry = new(); + foreach (GeometryBase convertedObject in convertedObjects) + { + Guid id = Doc.Objects.Add(convertedObject); + if (id != Guid.Empty) + { + addedGeometry.Add(id); + } + } + + if (addedGeometry.Count == 0) + { + appObj.Update(status: ApplicationObject.State.Failed, logItem: "No objects were created for group"); + return appObj; + } + + int groupIndex = Doc.Groups.Add(groupName, addedGeometry); + if (groupIndex == -1) + { + appObj.Update(status: ApplicationObject.State.Failed, logItem: "Could not add group to doc"); + return appObj; + } + + RH.Group convertedGroup = Doc.Groups.FindIndex(groupIndex); + + // update appobj + appObj.Update(convertedItem: convertedGroup, createdIds: addedGeometry.Select(o => o.ToString()).ToList()); + + return appObj; + } +} diff --git a/Objects/Converters/ConverterRhinoGh/ConverterRhinoGhShared/ConverterRhinoGh.Other.cs b/Objects/Converters/ConverterRhinoGh/ConverterRhinoGhShared/ConverterRhinoGh.Other.cs index 934809c384..bac2179410 100644 --- a/Objects/Converters/ConverterRhinoGh/ConverterRhinoGhShared/ConverterRhinoGh.Other.cs +++ b/Objects/Converters/ConverterRhinoGh/ConverterRhinoGhShared/ConverterRhinoGh.Other.cs @@ -218,7 +218,7 @@ public Other.RenderMaterial RenderMaterialToSpeckle(RH.Material material) } #else RH.Material matToUse = material; - if (!material.IsPhysicallyBased) + if (!material.IsPhysicallyBased && !Doc.IsHeadless) { matToUse = new RH.Material(); matToUse.CopyFrom(material); diff --git a/Objects/Converters/ConverterRhinoGh/ConverterRhinoGhShared/ConverterRhinoGh.cs b/Objects/Converters/ConverterRhinoGh/ConverterRhinoGhShared/ConverterRhinoGh.cs index b467aad06a..7530e0e981 100644 --- a/Objects/Converters/ConverterRhinoGh/ConverterRhinoGhShared/ConverterRhinoGh.cs +++ b/Objects/Converters/ConverterRhinoGh/ConverterRhinoGhShared/ConverterRhinoGh.cs @@ -7,6 +7,7 @@ using Objects.BuiltElements.Revit; using Objects.BuiltElements.Revit.Curve; using Objects.Geometry; +using Objects.GIS; using Objects.Other; using Objects.Primitive; using Objects.Structural.Geometry; @@ -513,6 +514,14 @@ public object ConvertToNative(Base @object) rhinoObj = AlignmentToNative(o); break; + case PolygonElement o: + rhinoObj = PolygonElementToNative(o); + break; + + case GisFeature o: + rhinoObj = GisFeatureToNative(o); + break; + case Level o: rhinoObj = LevelToNative(o); break; @@ -735,6 +744,8 @@ public bool CanConvertToNative(Base @object) case Instance _: case GridLine _: case Alignment _: + case PolygonElement _: + case GisFeature _: case Level _: case Dimension _: case Collection c when !c.collectionType.ToLower().Contains("model"): diff --git a/Objects/Converters/ConverterRhinoGh/ConverterRhinoGhShared/ConverterRhinoGhShared.projitems b/Objects/Converters/ConverterRhinoGh/ConverterRhinoGhShared/ConverterRhinoGhShared.projitems index c0bb3fc5d2..67a382db59 100644 --- a/Objects/Converters/ConverterRhinoGh/ConverterRhinoGhShared/ConverterRhinoGhShared.projitems +++ b/Objects/Converters/ConverterRhinoGh/ConverterRhinoGhShared/ConverterRhinoGhShared.projitems @@ -9,6 +9,7 @@ ConverterRhinoGhShared + diff --git a/Objects/Converters/ConverterTeklaStructures/ConverterTeklaStructuresShared/PartialClasses/ConvertBeam.cs b/Objects/Converters/ConverterTeklaStructures/ConverterTeklaStructuresShared/PartialClasses/ConvertBeam.cs index 8ad1e75f55..1c9f0980ff 100644 --- a/Objects/Converters/ConverterTeklaStructures/ConverterTeklaStructuresShared/PartialClasses/ConvertBeam.cs +++ b/Objects/Converters/ConverterTeklaStructures/ConverterTeklaStructuresShared/PartialClasses/ConvertBeam.cs @@ -192,12 +192,14 @@ public TeklaBeam BeamToSpeckle(Tekla.Structures.Model.Beam beam) Point speckleStartPoint = new(startPoint.X, startPoint.Y, startPoint.Z, units); Point speckleEndPoint = new(endPoint.X, endPoint.Y, endPoint.Z, units); - speckleBeam.baseLine = new Line(speckleStartPoint, speckleEndPoint, units); - speckleBeam.baseLine.length = Math.Sqrt( - Math.Pow((startPoint.X - endPoint.X), 2) - + Math.Pow((startPoint.Y - endPoint.Y), 2) - + Math.Pow((startPoint.Z - endPoint.Z), 2) - ); + speckleBeam.baseLine = new Line(speckleStartPoint, speckleEndPoint, units) + { + length = Math.Sqrt( + Math.Pow((startPoint.X - endPoint.X), 2) + + Math.Pow((startPoint.Y - endPoint.Y), 2) + + Math.Pow((startPoint.Z - endPoint.Z), 2) + ), + }; speckleBeam.profile = GetBeamProfile(beam.Profile.ProfileString); speckleBeam.material = GetMaterial(beam.Material.MaterialString); var beamCS = beam.GetCoordinateSystem(); diff --git a/Objects/Converters/ConverterTeklaStructures/ConverterTeklaStructuresShared/PartialClasses/ConvertContourPlate.cs b/Objects/Converters/ConverterTeklaStructures/ConverterTeklaStructuresShared/PartialClasses/ConvertContourPlate.cs index deaed09493..3ead851cd0 100644 --- a/Objects/Converters/ConverterTeklaStructures/ConverterTeklaStructuresShared/PartialClasses/ConvertContourPlate.cs +++ b/Objects/Converters/ConverterTeklaStructures/ConverterTeklaStructuresShared/PartialClasses/ConvertContourPlate.cs @@ -3,6 +3,7 @@ using BE = Objects.BuiltElements; using Objects.BuiltElements.TeklaStructures; using System.Linq; +using Speckle.Core.Models; using Tekla.Structures.Model; namespace Objects.Converter.TeklaStructures; @@ -12,6 +13,7 @@ public partial class ConverterTeklaStructures public void ContourPlateToNative(BE.Area area) { if (!(area.outline is Polyline)) { } + var ContourPlate = new ContourPlate(); if (area is TeklaContourPlate) { @@ -25,8 +27,10 @@ public void ContourPlateToNative(BE.Area area) { ContourPlate.AddContourPoint(ToTeklaContourPoint(cp)); } + SetPartProperties(ContourPlate, contour); } + ContourPlate.Insert(); //Model.CommitChanges(); } @@ -63,7 +67,15 @@ public TeklaContourPlate ContourPlateToSpeckle(Tekla.Structures.Model.ContourPla GetAllUserProperties(specklePlate, plate); var solid = plate.GetSolid(); - specklePlate.displayValue = new List { GetMeshFromSolid(solid) }; + + var displayMesh = GetMeshFromSolid(solid); + if (displayMesh != null) + { + var displayValue = new List(); + displayValue.Add(displayMesh); + specklePlate.displayValue = displayValue; + } + var rebars = plate.GetReinforcements(); if (rebars != null) { @@ -75,6 +87,7 @@ public TeklaContourPlate ContourPlateToSpeckle(Tekla.Structures.Model.ContourPla } } } + return specklePlate; } diff --git a/Objects/Objects/BuiltElements/AdvanceSteel/AsteelBeam.cs b/Objects/Objects/BuiltElements/AdvanceSteel/AsteelBeam.cs index 83db3e1c81..af9fde1bba 100644 --- a/Objects/Objects/BuiltElements/AdvanceSteel/AsteelBeam.cs +++ b/Objects/Objects/BuiltElements/AdvanceSteel/AsteelBeam.cs @@ -1,5 +1,3 @@ -using System.Collections.Generic; -using Objects.Geometry; using Objects.Structural.Materials; using Objects.Structural.Properties.Profiles; using Speckle.Core.Kits; @@ -7,7 +5,7 @@ namespace Objects.BuiltElements.AdvanceSteel; -public class AsteelBeam : Beam, IDisplayValue>, IHasVolume, IHasArea, IAsteelObject +public class AsteelBeam : Beam, IHasVolume, IHasArea, IAsteelObject { [DetachProperty] public SectionProfile profile { get; set; } diff --git a/Objects/Objects/BuiltElements/AdvanceSteel/AsteelPlate.cs b/Objects/Objects/BuiltElements/AdvanceSteel/AsteelPlate.cs index a8335933ce..3fadf25571 100644 --- a/Objects/Objects/BuiltElements/AdvanceSteel/AsteelPlate.cs +++ b/Objects/Objects/BuiltElements/AdvanceSteel/AsteelPlate.cs @@ -6,7 +6,8 @@ namespace Objects.BuiltElements.AdvanceSteel; -public class AsteelPlate : Area, IDisplayValue>, IHasArea, IHasVolume, IAsteelObject +// TODO: This class really shouldn't inherit from Area, but we need to fix the inheritance chain in the future. +public class AsteelPlate : Area, IDisplayValue>, IHasArea, IHasVolume, IAsteelObject { [DetachProperty] public StructuralMaterial? material { get; set; } diff --git a/Objects/Objects/BuiltElements/AdvanceSteel/AsteelSlab.cs b/Objects/Objects/BuiltElements/AdvanceSteel/AsteelSlab.cs index bed8a7c882..85c0010215 100644 --- a/Objects/Objects/BuiltElements/AdvanceSteel/AsteelSlab.cs +++ b/Objects/Objects/BuiltElements/AdvanceSteel/AsteelSlab.cs @@ -6,7 +6,8 @@ namespace Objects.BuiltElements.AdvanceSteel; -public class AsteelSlab : Area, IDisplayValue>, IHasArea, IHasVolume, IAsteelObject +// TODO: This class really shouldn't inherit from Area, but we need to fix the inheritance chain in the future. +public class AsteelSlab : Area, IDisplayValue>, IHasArea, IHasVolume, IAsteelObject { [DetachProperty] public StructuralMaterial? material { get; set; } diff --git a/Objects/Objects/BuiltElements/Archicad/ArchicadOpening.cs b/Objects/Objects/BuiltElements/Archicad/ArchicadOpening.cs new file mode 100644 index 0000000000..3c501c95bf --- /dev/null +++ b/Objects/Objects/BuiltElements/Archicad/ArchicadOpening.cs @@ -0,0 +1,83 @@ +using System.Collections.Generic; +using Speckle.Core.Kits; +using Speckle.Core.Models; +using Objects.Geometry; + +namespace Objects.BuiltElements.Archicad; + +public class ArchicadOpening : Opening +{ + [SchemaInfo("ArchicadOpening", "Creates an Archicad opening.", "Archicad", "Structure")] + public ArchicadOpening() { } + + public string parentApplicationId { get; set; } + + // Element base + public string? elementType { get; set; } /*APINullabe*/ + + public List? classifications { get; set; } /*APINullabe*/ + public Base? elementProperties { get; set; } + public Base? componentProperties { get; set; } + + // Floor Plan Parameters + public string? floorPlanDisplayMode { get; set; } /*APINullabe*/ + public string? connectionMode { get; set; } /*APINullabe*/ + + // Cut Surfaces Parameters + public bool? cutsurfacesUseLineOfCutElements { get; set; } /*APINullabe*/ + public short? cutsurfacesLinePenIndex { get; set; } /*APINullabe*/ + public string? cutsurfacesLineIndex { get; set; } /*APINullabe*/ + + // Outlines Parameters + public string? outlinesStyle { get; set; } /*APINullabe*/ + public bool? outlinesUseLineOfCutElements { get; set; } /*APINullabe*/ + public string? outlinesUncutLineIndex { get; set; } /*APINullabe*/ + public string? outlinesOverheadLineIndex { get; set; } /*APINullabe*/ + public short? outlinesUncutLinePenIndex { get; set; } /*APINullabe*/ + public short? outlinesOverheadLinePenIndex { get; set; } /*APINullabe*/ + + // Opening Cover Fills Parameters + public bool? useCoverFills { get; set; } /*APINullabe*/ + public bool? useFillsOfCutElements { get; set; } /*APINullabe*/ + public string? coverFillIndex { get; set; } /*APINullabe*/ + public short? coverFillPenIndex { get; set; } /*APINullabe*/ + public short? coverFillBackgroundPenIndex { get; set; } /*APINullabe*/ + public string? coverFillOrientation { get; set; } /*APINullabe*/ // Kérdéses.. + + // Cover Fill Transformation Parameters + public double? coverFillTransformationOrigoX { get; set; } + public double? coverFillTransformationOrigoY { get; set; } + public double? coverFillTransformationOrigoZ { get; set; } + public double? coverFillTransformationXAxisX { get; set; } + public double? coverFillTransformationXAxisY { get; set; } + public double? coverFillTransformationXAxisZ { get; set; } + public double? coverFillTransformationYAxisX { get; set; } + public double? coverFillTransformationYAxisY { get; set; } + public double? coverFillTransformationYAxisZ { get; set; } + + // Reference Axis Parameters + public bool? showReferenceAxis { get; set; } /*APINullabe*/ + public short? referenceAxisPenIndex { get; set; } /*APINullabe*/ + public string? referenceAxisLineTypeIndex { get; set; } /*APINullabe*/ + public double? referenceAxisOverhang { get; set; } /*APINullabe*/ + + // Extrusion Geometry Parameters + // Plane Frame + public Point extrusionGeometryBasePoint { get; set; } + public Vector extrusionGeometryXAxis { get; set; } + public Vector extrusionGeometryYAxis { get; set; } + public Vector extrusionGeometryZAxis { get; set; } + + // Opening Extrustion Parameters + public string? basePolygonType { get; set; } /*APINullabe*/ + public double? width { get; set; } /*APINullabe*/ + public double? height { get; set; } /*APINullabe*/ + public string? constraint { get; set; } /*APINullabe*/ + public string? anchor { get; set; } /*APINullabe */ + public int? anchorIndex { get; set; } /*APINullabe*/ + public double? anchorAltitude { get; set; } /*APINullabe*/ + public string? limitType { get; set; } /*APINullabe*/ + public double? extrusionStartOffSet { get; set; } /*APINullabe*/ + public double? finiteBodyLength { get; set; } /*APINullabe*/ + public string? linkedStatus { get; set; } /*APINullabe*/ +} diff --git a/Objects/Objects/BuiltElements/Archicad/DirectShape.cs b/Objects/Objects/BuiltElements/Archicad/DirectShape.cs index 8b2f11caf0..8ae08ece91 100644 --- a/Objects/Objects/BuiltElements/Archicad/DirectShape.cs +++ b/Objects/Objects/BuiltElements/Archicad/DirectShape.cs @@ -16,7 +16,10 @@ public DirectShape(string applicationId, List displayValue) // Element base public string elementType { get; set; } + public List classifications { get; set; } + public Base? elementProperties { get; set; } + public Base? componentProperties { get; set; } public ArchicadLevel level { get; set; } diff --git a/Objects/Objects/BuiltElements/Archicad/ElementShape.cs b/Objects/Objects/BuiltElements/Archicad/ElementShape.cs index 4da45cb3ae..cd78ab4717 100644 --- a/Objects/Objects/BuiltElements/Archicad/ElementShape.cs +++ b/Objects/Objects/BuiltElements/Archicad/ElementShape.cs @@ -1,7 +1,9 @@ using System.Collections.Generic; using Objects.Geometry; using Objects.Primitive; +using Speckle.Core.Kits; using Speckle.Core.Models; +using Speckle.Newtonsoft.Json; namespace Objects.BuiltElements.Archicad; @@ -19,6 +21,9 @@ public ElementShape(Polyline contourPolyline, List? holePolylines = nu public List? holePolylines { get; set; } + /// + /// This class is only used for Archicad interop + /// public sealed class PolylineSegment : Base, ICurve { public PolylineSegment() { } @@ -33,21 +38,30 @@ public PolylineSegment(Point startPoint, Point endPoint, double? arcAngle = null public Point startPoint { get; set; } public Point endPoint { get; set; } + + [JsonIgnore] + public string units => Units.Meters; public double arcAngle { get; set; } public bool? bodyFlag { get; set; } public double length { get; set; } public Interval domain { get; set; } = new(0, 1); } + /// + /// This class is only used for Archicad interop + /// public sealed class Polyline : Base, ICurve { public Polyline() { } public Polyline(List segments) { - polylineSegments = segments; + this.polylineSegments = segments; } + [JsonIgnore] + public string units => Units.Meters; + public List polylineSegments { get; set; } = new(); public double length { get; set; } public Interval domain { get; set; } = new(0, 1); diff --git a/Objects/Objects/BuiltElements/Area.cs b/Objects/Objects/BuiltElements/Area.cs index 9305f8733f..119d8987c1 100644 --- a/Objects/Objects/BuiltElements/Area.cs +++ b/Objects/Objects/BuiltElements/Area.cs @@ -5,7 +5,7 @@ namespace Objects.BuiltElements; -public class Area : Base, IHasArea, IHasVolume, IDisplayValue> +public class Area : Base, IHasArea, IHasVolume, IDisplayValue> { public Area() { } @@ -31,7 +31,7 @@ public Area(string name, string number, Level level, [SchemaMainParam] Point cen public string units { get; set; } [DetachProperty] - public List displayValue { get; set; } + public List displayValue { get; set; } public double area { get; set; } public double volume { get; set; } diff --git a/Objects/Objects/BuiltElements/Baseline.cs b/Objects/Objects/BuiltElements/Baseline.cs new file mode 100644 index 0000000000..11b78ab09f --- /dev/null +++ b/Objects/Objects/BuiltElements/Baseline.cs @@ -0,0 +1,90 @@ +using Speckle.Core.Models; +using Speckle.Newtonsoft.Json; + +namespace Objects.BuiltElements; + +public abstract class Baseline : Base +{ + protected Baseline() { } + + protected Baseline(string name, bool isFeaturelineBased) + { + this.name = name; + this.isFeaturelineBased = isFeaturelineBased; + } + + /// + /// The name of this baseline + /// + public string name { get; set; } + + /// + /// The horizontal component of this baseline + /// + public abstract Alignment? alignment { get; internal set; } + + /// + /// The vertical component of this baseline + /// + public abstract Profile? profile { get; internal set; } + + [DetachProperty] + public Featureline? featureline { get; internal set; } + + public bool isFeaturelineBased { get; set; } + + public string units { get; set; } +} + +/// +/// Generic instance class +/// +public abstract class Baseline : Baseline + where TA : Alignment + where TP : Profile +{ + protected Baseline(string name, TA alignment, TP profile, Featureline? featureline, bool isFeaturelineBased) + : base(name, isFeaturelineBased) + { + this.name = name; + typedAlignment = alignment; + typedProfile = profile; + this.featureline = featureline; + this.isFeaturelineBased = isFeaturelineBased; + } + + protected Baseline() + : base(string.Empty, false) { } + + [JsonIgnore] + public TA typedAlignment { get; set; } + + [JsonIgnore] + public TP typedProfile { get; set; } + + [DetachProperty] + public override Alignment? alignment + { + get => typedAlignment; + internal set + { + if (value is TA typeA) + { + typedAlignment = typeA; + } + } + } + + [DetachProperty] + public override Profile? profile + { + get => typedProfile; + internal set + { + if (value is TP typeP) + { + typedProfile = typeP; + } + } + } +} diff --git a/Objects/Objects/BuiltElements/Beam.cs b/Objects/Objects/BuiltElements/Beam.cs index 7d917c6c93..d868921632 100644 --- a/Objects/Objects/BuiltElements/Beam.cs +++ b/Objects/Objects/BuiltElements/Beam.cs @@ -5,22 +5,31 @@ namespace Objects.BuiltElements; -public class Beam : Base, IDisplayValue> +public class Beam : Base, IDisplayValue> { public Beam() { } - [SchemaInfo("Beam", "Creates a Speckle beam", "BIM", "Structure")] - public Beam([SchemaMainParam] ICurve baseLine) + public Beam(ICurve baseLine, Level? level, string? units, IReadOnlyList? displayValue = null) { this.baseLine = baseLine; + this.level = level; + this.units = units; + this.displayValue = ((IReadOnlyList?)displayValue) ?? new[] { (Base)baseLine }; } public ICurve baseLine { get; set; } public virtual Level? level { get; internal set; } - public string units { get; set; } + public string? units { get; set; } [DetachProperty] - public List displayValue { get; set; } + public IReadOnlyList displayValue { get; set; } + + #region Schema Info Constructors + [SchemaInfo("Beam", "Creates a Speckle beam", "BIM", "Structure")] + public Beam([SchemaMainParam] ICurve baseLine) + : this(baseLine, null, null) { } + + #endregion } diff --git a/Objects/Objects/BuiltElements/Brace.cs b/Objects/Objects/BuiltElements/Brace.cs index c51cd56828..c8a90447ee 100644 --- a/Objects/Objects/BuiltElements/Brace.cs +++ b/Objects/Objects/BuiltElements/Brace.cs @@ -5,20 +5,25 @@ namespace Objects.BuiltElements; -public class Brace : Base, IDisplayValue> +public class Brace : Base, IDisplayValue> { public Brace() { } - [SchemaInfo("Brace", "Creates a Speckle brace", "BIM", "Structure")] - public Brace([SchemaMainParam] ICurve baseLine) + public Brace(ICurve baseLine, string? units, IReadOnlyList? displayValue = null) { this.baseLine = baseLine; + this.units = units; + this.displayValue = ((IReadOnlyList?)displayValue) ?? new[] { (Base)baseLine }; } public ICurve baseLine { get; set; } - public string units { get; set; } + public string? units { get; set; } [DetachProperty] - public List displayValue { get; set; } + public IReadOnlyList displayValue { get; set; } + + [SchemaInfo("Brace", "Creates a Speckle brace", "BIM", "Structure")] + public Brace([SchemaMainParam] ICurve baseLine) + : this(baseLine, null) { } } diff --git a/Objects/Objects/BuiltElements/Civil/CivilAppliedAssembly.cs b/Objects/Objects/BuiltElements/Civil/CivilAppliedAssembly.cs new file mode 100644 index 0000000000..be99041904 --- /dev/null +++ b/Objects/Objects/BuiltElements/Civil/CivilAppliedAssembly.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using Speckle.Core.Models; + +namespace Objects.BuiltElements.Civil; + +public class CivilAppliedAssembly : Base +{ + public CivilAppliedAssembly() { } + + public CivilAppliedAssembly( + List appliedSubassemblies, + double? adjustedElevation, + string units + ) + { + this.appliedSubassemblies = appliedSubassemblies; + this.adjustedElevation = adjustedElevation; + this.units = units; + } + + public List appliedSubassemblies { get; set; } + + public double? adjustedElevation { get; set; } + + public string units { get; set; } +} diff --git a/Objects/Objects/BuiltElements/Civil/CivilAppliedSubassembly.cs b/Objects/Objects/BuiltElements/Civil/CivilAppliedSubassembly.cs new file mode 100644 index 0000000000..4619484ff9 --- /dev/null +++ b/Objects/Objects/BuiltElements/Civil/CivilAppliedSubassembly.cs @@ -0,0 +1,39 @@ +using System.Collections.Generic; +using Objects.Geometry; +using Objects.Other.Civil; +using Speckle.Core.Models; + +namespace Objects.BuiltElements.Civil; + +public class CivilAppliedSubassembly : Base +{ + public CivilAppliedSubassembly() { } + + public CivilAppliedSubassembly( + string subassemblyId, + string subassemblyName, + List shapes, + Point stationOffsetElevationToBaseline, + List parameters + ) + { + this.subassemblyId = subassemblyId; + this.subassemblyName = subassemblyName; + this.shapes = shapes; + this.stationOffsetElevationToBaseline = stationOffsetElevationToBaseline; + this.parameters = parameters; + } + + public string subassemblyId { get; set; } + + public string subassemblyName { get; set; } + + public List shapes { get; set; } + + public Point stationOffsetElevationToBaseline { get; set; } + + [DetachProperty] + public List parameters { get; set; } + + public string units { get; set; } +} diff --git a/Objects/Objects/BuiltElements/Civil/CivilBaseline.cs b/Objects/Objects/BuiltElements/Civil/CivilBaseline.cs new file mode 100644 index 0000000000..826ef8df2b --- /dev/null +++ b/Objects/Objects/BuiltElements/Civil/CivilBaseline.cs @@ -0,0 +1,54 @@ +using System.Collections.Generic; + +namespace Objects.BuiltElements.Civil; + +public class CivilBaseline : Baseline +{ + public CivilBaseline() { } + + public CivilBaseline( + string name, + List regions, + List stations, + double startStation, + double endStation, + CivilAlignment alignment, + CivilProfile profile + ) + { + this.name = name; + this.regions = regions; + this.stations = stations; + this.startStation = startStation; + this.endStation = endStation; + this.alignment = alignment; + this.profile = profile; + isFeaturelineBased = false; + } + + public CivilBaseline( + string name, + List regions, + List stations, + double startStation, + double endStation, + Featureline featureline + ) + { + this.name = name; + this.regions = regions; + this.stations = stations; + this.startStation = startStation; + this.endStation = endStation; + this.featureline = featureline; + isFeaturelineBased = true; + } + + public List regions { get; set; } + + public List stations { get; set; } + + public double startStation { get; set; } + + public double endStation { get; set; } +} diff --git a/Objects/Objects/BuiltElements/Civil/CivilBaselineRegion.cs b/Objects/Objects/BuiltElements/Civil/CivilBaselineRegion.cs new file mode 100644 index 0000000000..b4d10b7c20 --- /dev/null +++ b/Objects/Objects/BuiltElements/Civil/CivilBaselineRegion.cs @@ -0,0 +1,47 @@ +using System.Collections.Generic; +using Speckle.Core.Models; + +namespace Objects.BuiltElements.Civil; + +public class CivilBaselineRegion : Base +{ + public CivilBaselineRegion() { } + + public CivilBaselineRegion( + string name, + double startStation, + double endStation, + string assemblyId, + string? assemblyName, + List appliedAssemblies + ) + { + this.name = name; + this.startStation = startStation; + this.endStation = endStation; + this.assemblyId = assemblyId; + this.assemblyName = assemblyName; + this.appliedAssemblies = appliedAssemblies; + } + + /// + /// The name of the region + /// + public string name { get; set; } + + /// + /// The id of the assembly of the region + /// + public string assemblyId { get; set; } + + public string? assemblyName { get; set; } + + public double startStation { get; set; } + + public double endStation { get; set; } + + [DetachProperty] + public List appliedAssemblies { get; set; } + + public string units { get; set; } +} diff --git a/Objects/Objects/BuiltElements/Civil/CivilCalculatedLink.cs b/Objects/Objects/BuiltElements/Civil/CivilCalculatedLink.cs new file mode 100644 index 0000000000..165f7ee268 --- /dev/null +++ b/Objects/Objects/BuiltElements/Civil/CivilCalculatedLink.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; +using Speckle.Core.Models; + +namespace Objects.BuiltElements.Civil; + +public class CivilCalculatedLink : Base, ICivilCalculatedObject +{ + public CivilCalculatedLink() { } + + public CivilCalculatedLink(List codes, List points) + { + this.codes = codes; + this.points = points; + } + + public List codes { get; set; } + + [DetachProperty] + public List points { get; set; } + + public string units { get; set; } +} diff --git a/Objects/Objects/BuiltElements/Civil/CivilCalculatedPoint.cs b/Objects/Objects/BuiltElements/Civil/CivilCalculatedPoint.cs new file mode 100644 index 0000000000..4393a9a594 --- /dev/null +++ b/Objects/Objects/BuiltElements/Civil/CivilCalculatedPoint.cs @@ -0,0 +1,37 @@ +using System.Collections.Generic; +using Objects.Geometry; +using Speckle.Core.Models; + +namespace Objects.BuiltElements.Civil; + +public class CivilCalculatedPoint : Base, ICivilCalculatedObject +{ + public CivilCalculatedPoint() { } + + public CivilCalculatedPoint( + Point point, + List codes, + Vector normalToBaseline, + Vector normalToSubassembly, + Point stationOffsetElevationToBaseline + ) + { + this.point = point; + this.codes = codes; + this.normalToBaseline = normalToBaseline; + this.normalToSubassembly = normalToSubassembly; + this.stationOffsetElevationToBaseline = stationOffsetElevationToBaseline; + } + + public Point point { get; set; } + + public List codes { get; set; } + + public Vector normalToBaseline { get; set; } + + public Vector normalToSubassembly { get; set; } + + public Point stationOffsetElevationToBaseline { get; set; } + + public string units { get; set; } +} diff --git a/Objects/Objects/BuiltElements/Civil/CivilCalculatedShape.cs b/Objects/Objects/BuiltElements/Civil/CivilCalculatedShape.cs new file mode 100644 index 0000000000..1e6a3fb7b6 --- /dev/null +++ b/Objects/Objects/BuiltElements/Civil/CivilCalculatedShape.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using Speckle.Core.Models; + +namespace Objects.BuiltElements.Civil; + +public class CivilCalculatedShape : Base, ICivilCalculatedObject +{ + public CivilCalculatedShape() { } + + public CivilCalculatedShape(List codes, List links, double area, string units) + { + this.codes = codes; + this.links = links; + this.area = area; + this.units = units; + } + + public List codes { get; set; } + + [DetachProperty] + public List links { get; set; } + + public double area { get; set; } + + public string units { get; set; } +} diff --git a/Objects/Objects/BuiltElements/Column.cs b/Objects/Objects/BuiltElements/Column.cs index e13e054eaa..3b4d405575 100644 --- a/Objects/Objects/BuiltElements/Column.cs +++ b/Objects/Objects/BuiltElements/Column.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using Objects.Geometry; using Speckle.Core.Kits; @@ -5,22 +6,32 @@ namespace Objects.BuiltElements; -public class Column : Base, IDisplayValue> +public class Column : Base, IDisplayValue> { public Column() { } - [SchemaInfo("Column", "Creates a Speckle column", "BIM", "Structure")] - public Column([SchemaMainParam] ICurve baseLine) + public Column(ICurve baseLine, string? units, Level? level = null, IReadOnlyList? displayValue = null) { this.baseLine = baseLine; + this.units = units; + this.level = level; + this.displayValue = ((IReadOnlyList?)displayValue) ?? new[] { (Base)baseLine }; } public ICurve baseLine { get; set; } public virtual Level? level { get; internal set; } - public string units { get; set; } + public string? units { get; set; } [DetachProperty] - public List displayValue { get; set; } + public IReadOnlyList displayValue { get; set; } + + #region Schema Info Constructors + + [SchemaInfo("Column", "Creates a Speckle column", "BIM", "Structure")] + [SchemaDeprecated, Obsolete("Use other constructor")] + public Column([SchemaMainParam] ICurve baseLine) + : this(baseLine, null) { } + #endregion } diff --git a/Objects/Objects/BuiltElements/Duct.cs b/Objects/Objects/BuiltElements/Duct.cs index f530be6faf..6ef56bbc53 100644 --- a/Objects/Objects/BuiltElements/Duct.cs +++ b/Objects/Objects/BuiltElements/Duct.cs @@ -7,10 +7,47 @@ namespace Objects.BuiltElements; -public class Duct : Base, IDisplayValue> +public class Duct : Base, IDisplayValue> { public Duct() { } + public Duct( + ICurve baseCurve, + double width, + double height, + double diameter, + double length, + string? units, + double velocity = 0, + IReadOnlyList? displayValue = null + ) + { + this.baseCurve = baseCurve; + this.width = width; + this.height = height; + this.diameter = diameter; + this.length = length; + this.units = units; + this.velocity = velocity; + this.displayValue = ((IReadOnlyList?)displayValue) ?? new[] { (Base)baseCurve }; + } + + [JsonIgnore, Obsolete("Replaced with baseCurve property")] + public Line? baseLine { get; set; } + + public ICurve baseCurve { get; set; } + public double width { get; set; } + public double height { get; set; } + public double diameter { get; set; } + public double length { get; set; } + public double velocity { get; set; } + + public string? units { get; set; } + + [DetachProperty] + public IReadOnlyList displayValue { get; set; } + + #region Schema Info Constructors /// /// SchemaBuilder constructor for a Speckle duct /// @@ -22,13 +59,8 @@ public Duct() { } /// Assign units when using this constructor due to , , and params [SchemaInfo("Duct", "Creates a Speckle duct", "BIM", "MEP"), SchemaDeprecated] public Duct([SchemaMainParam] Line baseLine, double width, double height, double diameter, double velocity = 0) - { - baseCurve = baseLine; - this.width = width; - this.height = height; - this.diameter = diameter; - this.velocity = velocity; - } + : this(baseLine, width, height, diameter, default, null, velocity) //TODO: what to do with length??? + { } /// /// SchemaBuilder constructor for a Speckle duct @@ -41,26 +73,6 @@ public Duct([SchemaMainParam] Line baseLine, double width, double height, double /// Assign units when using this constructor due to , , and params [SchemaInfo("Duct", "Creates a Speckle duct", "BIM", "MEP")] public Duct([SchemaMainParam] ICurve baseCurve, double width, double height, double diameter, double velocity = 0) - { - this.baseCurve = baseCurve; - this.width = width; - this.height = height; - this.diameter = diameter; - this.velocity = velocity; - } - - [JsonIgnore, Obsolete("Replaced with baseCurve property")] - public Line baseLine { get; set; } - - public ICurve baseCurve { get; set; } - public double width { get; set; } - public double height { get; set; } - public double diameter { get; set; } - public double length { get; set; } - public double velocity { get; set; } - - public string units { get; set; } - - [DetachProperty] - public List displayValue { get; set; } + : this(baseCurve, width, height, diameter, default, null, velocity) { } //TODO: what to do with length??? + #endregion } diff --git a/Objects/Objects/BuiltElements/Revit/RevitBeam.cs b/Objects/Objects/BuiltElements/Revit/RevitBeam.cs index b483b12a11..6d7859e3da 100644 --- a/Objects/Objects/BuiltElements/Revit/RevitBeam.cs +++ b/Objects/Objects/BuiltElements/Revit/RevitBeam.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Objects.Geometry; using Objects.Utils; using Speckle.Core.Kits; using Speckle.Core.Models; @@ -9,20 +10,20 @@ public class RevitBeam : Beam { public RevitBeam() { } - [SchemaInfo("RevitBeam", "Creates a Revit beam by curve and base level.", "Revit", "Structure")] public RevitBeam( string family, string type, - [SchemaMainParam] ICurve baseLine, - Level level, + ICurve baseLine, + Level? level, + string? units, + List? displayValue = null, List? parameters = null ) + : base(baseLine, level, units, displayValue) { this.family = family; this.type = type; - this.baseLine = baseLine; this.parameters = parameters?.ToBase(); - this.level = level; } public string family { get; set; } @@ -35,4 +36,18 @@ public RevitBeam( get => base.level; set => base.level = value; } + + #region Schema Info Constructors + + [SchemaInfo("RevitBeam", "Creates a Revit beam by curve and base level.", "Revit", "Structure")] + public RevitBeam( + string family, + string type, + [SchemaMainParam] ICurve baseLine, + Level level, + List? parameters = null + ) + : this(family, type, baseLine, level, null, parameters: parameters) { } + + #endregion } diff --git a/Objects/Objects/BuiltElements/Revit/RevitBrace.cs b/Objects/Objects/BuiltElements/Revit/RevitBrace.cs index 99499f8826..dc92fbf296 100644 --- a/Objects/Objects/BuiltElements/Revit/RevitBrace.cs +++ b/Objects/Objects/BuiltElements/Revit/RevitBrace.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Objects.Geometry; using Objects.Utils; using Speckle.Core.Kits; using Speckle.Core.Models; @@ -9,25 +10,42 @@ public class RevitBrace : Brace { public RevitBrace() { } - [SchemaInfo("RevitBrace", "Creates a Revit brace by curve and base level.", "Revit", "Structure")] public RevitBrace( string family, string type, - [SchemaMainParam] ICurve baseLine, - Level level, + ICurve baseLine, + Level? level, + string? units, + string? elementId, + IReadOnlyList? displayValue = null, List? parameters = null ) + : base(baseLine, units, displayValue) { this.family = family; this.type = type; - this.baseLine = baseLine; - this.parameters = parameters?.ToBase(); this.level = level; + this.elementId = elementId; + this.parameters = parameters?.ToBase(); } public string family { get; set; } public string type { get; set; } public Base? parameters { get; set; } - public string elementId { get; set; } - public Level level { get; set; } + public string? elementId { get; set; } + public Level? level { get; set; } + + #region Schema Info Constructor + + [SchemaInfo("RevitBrace", "Creates a Revit brace by curve and base level.", "Revit", "Structure")] + public RevitBrace( + string family, + string type, + [SchemaMainParam] ICurve baseLine, + Level level, + List? parameters = null + ) + : this(family, type, baseLine, level, null, null, parameters: parameters) { } + + #endregion } diff --git a/Objects/Objects/BuiltElements/Revit/RevitColumn.cs b/Objects/Objects/BuiltElements/Revit/RevitColumn.cs index 5a770db9ba..64e4db93b0 100644 --- a/Objects/Objects/BuiltElements/Revit/RevitColumn.cs +++ b/Objects/Objects/BuiltElements/Revit/RevitColumn.cs @@ -1,4 +1,6 @@ +using System; using System.Collections.Generic; +using Objects.Geometry; using Objects.Utils; using Speckle.Core.Kits; using Speckle.Core.Models; @@ -9,49 +11,82 @@ public class RevitColumn : Column { public RevitColumn() { } - /// - /// SchemaBuilder constructor for a Revit column - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Assign units when using this constructor due to and params - [SchemaInfo("RevitColumn Vertical", "Creates a vertical Revit Column by point and levels.", "Revit", "Architecture")] public RevitColumn( string family, string type, - [SchemaParamInfo("Only the lower point of this line will be used as base point."), SchemaMainParam] ICurve baseLine, - Level level, - Level topLevel, + ICurve baseLine, + Level? level, + Level? topLevel, + string? units, + string? elementId, double baseOffset = 0, double topOffset = 0, - bool structural = false, - [SchemaParamInfo("Rotation angle in radians")] double rotation = 0, + bool facingFlipped = false, + bool handFlipped = false, + bool isSlanted = false, + double rotation = 0, + IReadOnlyList? displayValue = null, List? parameters = null ) + : base(baseLine, units, level, displayValue) { this.family = family; this.type = type; - this.baseLine = baseLine; this.topLevel = topLevel; + this.elementId = elementId; this.baseOffset = baseOffset; this.topOffset = topOffset; + this.facingFlipped = facingFlipped; + this.handFlipped = handFlipped; + this.isSlanted = isSlanted; this.rotation = rotation; this.parameters = parameters?.ToBase(); - this.level = level; } - [ - SchemaDeprecated, - SchemaInfo("RevitColumn Slanted (old)", "Creates a slanted Revit Column by curve.", "Revit", "Structure") - ] + public Level? topLevel { get; set; } + public double baseOffset { get; set; } + public double topOffset { get; set; } + public bool facingFlipped { get; set; } + public bool handFlipped { get; set; } + public double rotation { get; set; } + public bool isSlanted { get; set; } + public string family { get; set; } + public string type { get; set; } + public Base? parameters { get; set; } + public string? elementId { get; set; } + + #region Schema Info Constructors + + [SchemaInfo("RevitColumn Vertical", "Creates a vertical Revit Column by point and levels.", "Revit", "Architecture")] + public RevitColumn( + string family, + string type, + [SchemaParamInfo("Only the lower point of this line will be used as base point."), SchemaMainParam] ICurve baseLine, + Level level, + Level topLevel, + double baseOffset = 0, + double topOffset = 0, + bool structural = false, + [SchemaParamInfo("Rotation angle in radians")] double rotation = 0, + List? parameters = null + ) + : this( + family, + type, + baseLine, + level, + topLevel, + null, + null, + baseOffset, + topOffset, + rotation: rotation, + parameters: parameters + ) { } + + [Obsolete("Use other constructors")] + [SchemaDeprecated] + [SchemaInfo("RevitColumn Slanted (old)", "Creates a slanted Revit Column by curve.", "Revit", "Structure")] [System.Diagnostics.CodeAnalysis.SuppressMessage( "Style", "IDE0060:Remove unused parameter", @@ -84,31 +119,7 @@ public RevitColumn( bool structural = false, List? parameters = null ) - { - this.family = family; - this.type = type; - this.baseLine = baseLine; - this.level = level; - this.topLevel = topLevel; - isSlanted = true; - this.parameters = parameters?.ToBase(); - } - - public new Level? level - { - get => base.level; - set => base.level = value; - } + : this(family, type, baseLine, level, topLevel, null, null, displayValue: null, parameters: parameters) { } - public Level? topLevel { get; set; } - public double baseOffset { get; set; } - public double topOffset { get; set; } - public bool facingFlipped { get; set; } - public bool handFlipped { get; set; } - public double rotation { get; set; } - public bool isSlanted { get; set; } - public string family { get; set; } - public string type { get; set; } - public Base? parameters { get; set; } - public string elementId { get; set; } + #endregion } diff --git a/Objects/Objects/BuiltElements/Revit/RevitDuct.cs b/Objects/Objects/BuiltElements/Revit/RevitDuct.cs index 8c75f4ba04..1501466456 100644 --- a/Objects/Objects/BuiltElements/Revit/RevitDuct.cs +++ b/Objects/Objects/BuiltElements/Revit/RevitDuct.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using Objects.BuiltElements.Revit.Interfaces; using Objects.Geometry; @@ -11,69 +12,52 @@ public class RevitDuct : Duct, IHasMEPConnectors { public RevitDuct() { } - /// - /// SchemaBuilder constructor for a Revit duct (deprecated) - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Assign units when using this constructor due to , , and params - [SchemaInfo("RevitDuct", "Creates a Revit duct", "Revit", "MEP"), SchemaDeprecated] public RevitDuct( string family, string type, - [SchemaMainParam] Line baseLine, + ICurve baseCurve, string systemName, string systemType, Level level, double width, double height, double diameter, + double length, + string? units, + string? elementId, double velocity = 0, + IReadOnlyList? displayValue = null, List? parameters = null ) + : base(baseCurve, width, height, diameter, length, units, velocity, displayValue) { - baseCurve = baseLine; this.family = family; this.type = type; - this.width = width; - this.height = height; - this.diameter = diameter; - this.velocity = velocity; this.systemName = systemName; this.systemType = systemType; - this.parameters = parameters?.ToBase(); this.level = level; + this.parameters = parameters?.ToBase(); + this.elementId = elementId; } - /// - /// SchemaBuilder constructor for a Revit duct - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Assign units when using this constructor due to , , and params - [SchemaInfo("RevitDuct", "Creates a Revit duct", "Revit", "MEP")] + public string family { get; set; } + public string type { get; set; } + public string systemName { get; set; } + public string systemType { get; set; } + public Level level { get; set; } + public Base? parameters { get; set; } + public string? elementId { get; set; } + public List Connectors { get; set; } = new(); + + #region Schema Info Constructors + + [SchemaInfo("RevitDuct (DEPRECATED)", "Creates a Revit duct", "Revit", "MEP")] + [SchemaDeprecated] + [Obsolete("Use other Constructor")] public RevitDuct( string family, string type, - [SchemaMainParam] ICurve baseCurve, + [SchemaMainParam] Line baseLine, string systemName, string systemType, Level level, @@ -84,7 +68,7 @@ public RevitDuct( List? parameters = null ) { - this.baseCurve = baseCurve; + baseCurve = baseLine; this.family = family; this.type = type; this.width = width; @@ -97,36 +81,44 @@ public RevitDuct( this.level = level; } - public string family { get; set; } - public string type { get; set; } - public string systemName { get; set; } - public string systemType { get; set; } - public Level level { get; set; } - public Base? parameters { get; set; } - public string elementId { get; set; } - public List Connectors { get; set; } = new(); + [SchemaInfo("RevitDuct", "Creates a Revit duct", "Revit", "MEP")] + public RevitDuct( + string family, + string type, + [SchemaMainParam] ICurve baseCurve, + string systemName, + string systemType, + Level level, + double width, + double height, + double diameter, + double velocity = 0, + List? parameters = null + ) + : this( + family, + type, + baseCurve, + systemName, + systemType, + level, + width, + height, + diameter, + default, //TODO: what to do with length? + null, + null, + velocity, + parameters: parameters + ) { } + + #endregion } public class RevitFlexDuct : RevitDuct { public RevitFlexDuct() { } - /// - /// SchemaBuilder constructor for a Revit flex duct - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Assign units when using this constructor due to , , and params - [SchemaInfo("RevitFlexDuct", "Creates a Revit flex duct", "Revit", "MEP")] public RevitFlexDuct( string family, string type, @@ -137,27 +129,76 @@ public RevitFlexDuct( double width, double height, double diameter, + double length, Vector startTangent, Vector endTangent, + string? units, + string? elementId, double velocity = 0, + IReadOnlyList? displayValue = null, List? parameters = null ) + : base( + family, + type, + baseCurve, + systemName, + systemType, + level, + width, + height, + diameter, + length, + units, + elementId, + velocity, + displayValue, + parameters + ) { - this.baseCurve = baseCurve; - this.family = family; - this.type = type; - this.width = width; - this.height = height; - this.diameter = diameter; this.startTangent = startTangent; this.endTangent = endTangent; - this.velocity = velocity; - this.systemName = systemName; - this.systemType = systemType; - this.parameters = parameters?.ToBase(); - this.level = level; } public Vector startTangent { get; set; } public Vector endTangent { get; set; } + + #region Schema Info Constructor + + [SchemaInfo("RevitFlexDuct", "Creates a Revit flex duct", "Revit", "MEP")] + public RevitFlexDuct( + string family, + string type, + [SchemaMainParam] ICurve baseCurve, + string systemName, + string systemType, + Level level, + double width, + double height, + double diameter, + Vector startTangent, + Vector endTangent, + double velocity = 0, + List? parameters = null + ) + : this( + family, + type, + baseCurve, + systemName, + systemType, + level, + width, + height, + diameter, + 0, + startTangent, + endTangent, + null, + null, + velocity, + parameters: parameters + ) { } + + #endregion } diff --git a/Objects/Objects/BuiltElements/Revit/RevitWall.cs b/Objects/Objects/BuiltElements/Revit/RevitWall.cs index ac9d4bfe29..a9d59dc5ad 100644 --- a/Objects/Objects/BuiltElements/Revit/RevitWall.cs +++ b/Objects/Objects/BuiltElements/Revit/RevitWall.cs @@ -11,69 +11,84 @@ public class RevitWall : Wall { public RevitWall() { } - /// - /// SchemaBuilder constructor for a Revit wall - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Assign units when using this constructor due to and params - [SchemaInfo( - "RevitWall by curve and levels", - "Creates a Revit wall with a top and base level.", - "Revit", - "Architecture" - )] public RevitWall( string family, string type, - [SchemaMainParam] ICurve baseLine, + ICurve baseLine, Level level, - Level topLevel, + Level? topLevel, + double height, + string? units, + string? elementId, double baseOffset = 0, double topOffset = 0, bool flipped = false, bool structural = false, - [SchemaParamInfo("Set in here any nested elements that this level might have.")] List? elements = null, + IReadOnlyList? displayValue = null, + List? elements = null, List? parameters = null ) + : base(height, units, baseLine, level, displayValue, elements) { this.family = family; this.type = type; - this.baseLine = baseLine; this.baseOffset = baseOffset; this.topOffset = topOffset; this.flipped = flipped; this.structural = structural; - this.level = level; + this.elementId = elementId; this.topLevel = topLevel; - this.elements = elements; this.parameters = parameters?.ToBase(); } - /// - /// SchemaBuilder constructor for a Revit wall - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Assign units when using this constructor due to , , and params + public string family { get; set; } + public string type { get; set; } + public double baseOffset { get; set; } + public double topOffset { get; set; } + public bool flipped { get; set; } + public bool structural { get; set; } + public Level? topLevel { get; set; } + public Base? parameters { get; set; } + public string? elementId { get; set; } + + #region Schema Info Constructors + + [SchemaInfo( + "RevitWall by curve and levels", + "Creates a Revit wall with a top and base level.", + "Revit", + "Architecture" + )] + public RevitWall( + string family, + string type, + [SchemaMainParam] ICurve baseLine, + Level level, + Level topLevel, + double baseOffset = 0, + double topOffset = 0, + bool flipped = false, + bool structural = false, + [SchemaParamInfo("Set in here any nested elements that this level might have.")] List? elements = null, + List? parameters = null + ) + : this( + family, + type, + baseLine, + level, + topLevel, + 0, + null, + null, + baseOffset, + topOffset, + flipped, + structural, + elements: elements, + parameters: parameters + ) { } + [SchemaInfo("RevitWall by curve and height", "Creates an unconnected Revit wall.", "Revit", "Architecture")] public RevitWall( string family, @@ -88,36 +103,23 @@ public RevitWall( [SchemaParamInfo("Set in here any nested elements that this wall might have.")] List? elements = null, List? parameters = null ) - { - this.family = family; - this.type = type; - this.baseLine = baseLine; - this.height = height; - this.baseOffset = baseOffset; - this.topOffset = topOffset; - this.flipped = flipped; - this.structural = structural; - this.level = level; - this.elements = elements; - this.parameters = parameters?.ToBase(); - } - - public string family { get; set; } - public string type { get; set; } - public double baseOffset { get; set; } - public double topOffset { get; set; } - public bool flipped { get; set; } - public bool structural { get; set; } - - public new Level? level - { - get => base.level; - set => base.level = value; - } - - public Level topLevel { get; set; } - public Base? parameters { get; set; } - public string elementId { get; set; } + : this( + family, + type, + baseLine, + level, + null, + height, + null, + null, + baseOffset, + topOffset, + flipped, + structural, + elements: elements, + parameters: parameters + ) { } + #endregion } public class RevitFaceWall : Wall diff --git a/Objects/Objects/BuiltElements/TeklaStructures/TeklaContourPlate.cs b/Objects/Objects/BuiltElements/TeklaStructures/TeklaContourPlate.cs index 7fcdcd22dc..c5944452a9 100644 --- a/Objects/Objects/BuiltElements/TeklaStructures/TeklaContourPlate.cs +++ b/Objects/Objects/BuiltElements/TeklaStructures/TeklaContourPlate.cs @@ -7,6 +7,7 @@ namespace Objects.BuiltElements.TeklaStructures; +// TODO: This class really shouldn't inherit from Area, but we need to fix the inheritance chain in the future. public class TeklaContourPlate : Area { [SchemaInfo("ContourPlate", "Creates a TeklaStructures contour plate.", "Tekla", "Structure")] diff --git a/Objects/Objects/BuiltElements/Wall.cs b/Objects/Objects/BuiltElements/Wall.cs index 9f6cb754d7..46b8684356 100644 --- a/Objects/Objects/BuiltElements/Wall.cs +++ b/Objects/Objects/BuiltElements/Wall.cs @@ -5,39 +5,48 @@ namespace Objects.BuiltElements; -public class Wall : Base, IDisplayValue> +public class Wall : Base, IDisplayValue> { public Wall() { } - /// - /// SchemaBuilder constructor for a Speckle wall - /// - /// - /// - /// - /// Assign units when using this constructor due to param - [SchemaInfo("Wall", "Creates a Speckle wall", "BIM", "Architecture")] public Wall( double height, - [SchemaMainParam] ICurve baseLine, - [SchemaParamInfo("Any nested elements that this wall might have")] List? elements = null + string? units, + ICurve baseLine, + Level? level = null, + IReadOnlyList? displayValue = null, + List? elements = null ) { this.height = height; + this.units = units; this.baseLine = baseLine; + this.level = level; + this.displayValue = ((IReadOnlyList?)displayValue) ?? new[] { (Base)baseLine }; this.elements = elements; } public double height { get; set; } - [DetachProperty] - public List? elements { get; set; } - + public string? units { get; set; } public ICurve baseLine { get; set; } public virtual Level? level { get; internal set; } - public string units { get; set; } + [DetachProperty] + public List? elements { get; set; } [DetachProperty] - public List displayValue { get; set; } + public IReadOnlyList displayValue { get; set; } + + #region SchemaInfo Ctors + + [SchemaInfo("Wall", "Creates a Speckle wall", "BIM", "Architecture")] + public Wall( + double height, + [SchemaMainParam] ICurve baseLine, + [SchemaParamInfo("Any nested elements that this wall might have")] List? elements = null + ) + : this(height, null, baseLine, null, null, elements) { } + + #endregion } diff --git a/Objects/Objects/GIS/GisFeature.cs b/Objects/Objects/GIS/GisFeature.cs new file mode 100644 index 0000000000..1a5f4dc5ac --- /dev/null +++ b/Objects/Objects/GIS/GisFeature.cs @@ -0,0 +1,43 @@ +using System.Collections.Generic; +using Speckle.Core.Models; + +namespace Objects.GIS; + +public class GisFeature : Base +{ + public GisFeature() + { + attributes = new Base(); + } + + public GisFeature(Base attributes) + { + this.attributes = attributes; + } + + public GisFeature(List geometry, Base attributes) + { + this.geometry = geometry; + this.attributes = attributes; + } + + public GisFeature(Base attributes, List displayValue) + { + this.attributes = attributes; + this.displayValue = displayValue; + } + + public GisFeature(List geometry, Base attributes, List displayValue) + { + this.geometry = geometry; + this.attributes = attributes; + this.displayValue = displayValue; + } + + [DetachProperty] + public List? geometry { get; set; } + + [DetachProperty] + public List? displayValue { get; set; } + public Base attributes { get; set; } +} diff --git a/Objects/Objects/Interfaces.cs b/Objects/Objects/Interfaces.cs index f732d0d64f..373e129d2e 100644 --- a/Objects/Objects/Interfaces.cs +++ b/Objects/Objects/Interfaces.cs @@ -28,7 +28,7 @@ public interface IHasArea /// /// The area of the object /// - double area { get; set; } + double area { get; } } /// @@ -39,7 +39,7 @@ public interface IHasVolume /// /// The volume of the object /// - double volume { get; set; } + double volume { get; } } /// @@ -50,12 +50,14 @@ public interface ICurve /// /// The length of the curve. /// - double length { get; set; } + double length { get; } /// /// The numerical domain driving the curve's internal parametrization. /// - Interval domain { get; set; } + Interval domain { get; } + + string units { get; } } /// @@ -101,13 +103,24 @@ public interface ITransformable /// Expected to be either a type or a of s, /// most likely or . /// -public interface IDisplayValue +public interface IDisplayValue { /// /// (s) will be used to display this /// if a native displayable object cannot be converted. /// - T displayValue { get; set; } + T displayValue { get; } +} + +/// +/// Represents a calculated object for civil disciplines +/// +public interface ICivilCalculatedObject +{ + /// + /// for this calculated object. + /// + List codes { get; set; } } diff --git a/Objects/Objects/Objects.csproj b/Objects/Objects/Objects.csproj index 77a9ad6bbd..62cb80c226 100644 --- a/Objects/Objects/Objects.csproj +++ b/Objects/Objects/Objects.csproj @@ -19,9 +19,9 @@ $(WarningsNotAsErrors); CA1008; CA1024; CA1034; CA1065; CA1708; CA1711; CA1716; CA1724; CA1725; - CA1819; + CA1819; CA2201; CA2225; - CS0659; CS0661; CS0728; + CS0659; CS0661; CS0728; CS8618; IDE0041; IDE0060; IDE1006; diff --git a/Objects/Objects/Other/Civil/CivilDataField.cs b/Objects/Objects/Other/Civil/CivilDataField.cs new file mode 100644 index 0000000000..6919669d85 --- /dev/null +++ b/Objects/Objects/Other/Civil/CivilDataField.cs @@ -0,0 +1,30 @@ +namespace Objects.Other.Civil; + +public class CivilDataField : DataField +{ + public CivilDataField() { } + + public CivilDataField( + string name, + string type, + object? value, + string? units = null, + string? context = null, + string? displayName = null + ) + { + this.name = name; + this.type = type; + this.value = value; + this.units = units; + this.context = context; + this.displayName = displayName; + } + + /// + /// The context type of the Civil3D part + /// + public string? context { get; set; } + + public string? displayName { get; set; } +} diff --git a/Objects/Objects/Other/DataField.cs b/Objects/Objects/Other/DataField.cs new file mode 100644 index 0000000000..4d3fd5a1ea --- /dev/null +++ b/Objects/Objects/Other/DataField.cs @@ -0,0 +1,27 @@ +using Speckle.Core.Models; + +namespace Objects.Other; + +/// +/// Generic class for a data field +/// +public class DataField : Base +{ + public DataField() { } + + public DataField(string name, string type, object? value, string? units = null) + { + this.name = name; + this.type = type; + this.value = value; + this.units = units; + } + + public string name { get; set; } + + public string type { get; set; } + + public object? value { get; set; } + + public string? units { get; set; } +} diff --git a/Objects/Objects/Other/Revit/RevitInstance.cs b/Objects/Objects/Other/Revit/RevitInstance.cs index 98614d7511..093cfec906 100644 --- a/Objects/Objects/Other/Revit/RevitInstance.cs +++ b/Objects/Objects/Other/Revit/RevitInstance.cs @@ -19,7 +19,11 @@ public class RevitInstance : Instance protected override IEnumerable GetTransformableGeometry() { - var allChildren = typedDefinition.elements ?? new List(); + var allChildren = new List(); + if (typedDefinition.elements != null) + { + allChildren.AddRange(typedDefinition.elements); + } if (typedDefinition.displayValue.Count != 0) { allChildren.AddRange(typedDefinition.displayValue); diff --git a/Objects/Objects/Structural/CSI/Geometry/CSIElement1D.cs b/Objects/Objects/Structural/CSI/Geometry/CSIElement1D.cs index 44d682b451..c3d4e3ad7d 100644 --- a/Objects/Objects/Structural/CSI/Geometry/CSIElement1D.cs +++ b/Objects/Objects/Structural/CSI/Geometry/CSIElement1D.cs @@ -10,6 +10,9 @@ namespace Objects.Structural.CSI.Geometry; public class CSIElement1D : Element1D { + public CSIElement1D(Line baseLine, Property1D property, ElementType1D type) + : this(baseLine, property, type, null, null, null, null, null, null, null) { } + /// /// SchemaBuilder constructor for structural 1D element (based on local axis) /// @@ -40,16 +43,8 @@ public CSIElement1D( double[]? Modifier = null, DesignProcedure DesignProcedure = DesignProcedure.NoDesign ) + : base(baseLine, property, type, name, end1Releases, end2Releases, end1Offset, end2Offset, localAxis) { - this.baseLine = baseLine; - this.property = property; - this.type = type; - this.name = name; - this.end1Releases = end1Releases ?? new Restraint("FFFFFF"); - this.end2Releases = end2Releases ?? new Restraint("FFFFFF"); - this.end1Offset = end1Offset ?? new Vector(0, 0, 0); - this.end2Offset = end2Offset ?? new Vector(0, 0, 0); - this.localAxis = localAxis; this.CSILinearSpring = CSILinearSpring; this.DesignProcedure = DesignProcedure; Modifiers = Modifier; @@ -92,17 +87,19 @@ public CSIElement1D( double[]? Modifier = null, DesignProcedure DesignProcedure = DesignProcedure.NoDesign ) + : base( + baseLine, + property, + type, + name, + end1Releases, + end2Releases, + end1Offset, + end2Offset, + orientationNode, + orientationAngle + ) { - this.baseLine = baseLine; - this.property = property; - this.type = type; - this.name = name; - this.end1Releases = end1Releases ?? new Restraint("FFFFFF"); - this.end2Releases = end2Releases ?? new Restraint("FFFFFF"); - this.end1Offset = end1Offset ?? new Vector(0, 0, 0); - this.end2Offset = end2Offset ?? new Vector(0, 0, 0); - this.orientationNode = orientationNode; - this.orientationAngle = orientationAngle; this.CSILinearSpring = CSILinearSpring; this.DesignProcedure = DesignProcedure; Modifiers = Modifier; diff --git a/Objects/Objects/Structural/Geometry/Element1D.cs b/Objects/Objects/Structural/Geometry/Element1D.cs index d1d74eab82..abce21d796 100644 --- a/Objects/Objects/Structural/Geometry/Element1D.cs +++ b/Objects/Objects/Structural/Geometry/Element1D.cs @@ -6,27 +6,74 @@ namespace Objects.Structural.Geometry; -public class Element1D : Base, IDisplayValue> +public class Element1D : Base, IDisplayValue> { public Element1D() { } - public Element1D(Line baseLine) + public Element1D( + Line baseLine, + Property1D property, + ElementType1D type, + string? name, + string? units, + Restraint? end1Releases = null, + Restraint? end2Releases = null, + Vector? end1Offset = null, + Vector? end2Offset = null, + Plane? localAxis = null, + Node? orientationNode = null, + double orientationAngle = 0, + IReadOnlyList? displayValue = null + ) { this.baseLine = baseLine; + this.property = property; + this.type = type; + this.name = name; + this.units = units; + this.end1Releases = end1Releases ?? new Restraint("FFFFFF"); + this.end2Releases = end2Releases ?? new Restraint("FFFFFF"); + this.end1Offset = end1Offset ?? new Vector(0, 0, 0); + this.end2Offset = end2Offset ?? new Vector(0, 0, 0); + this.localAxis = localAxis; + this.orientationNode = orientationNode; + this.orientationAngle = orientationAngle; + this.displayValue = ((IReadOnlyList?)displayValue) ?? new[] { (Base)baseLine }; } - /// - /// SchemaBuilder constructor for structural 1D element (based on local axis) - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// + public string? name { get; set; } //add unique id as base identifier, name can change too easily + public Line baseLine { get; set; } + + [DetachProperty] + public Property1D property { get; set; } + + public ElementType1D type { get; set; } + public Restraint end1Releases { get; set; } + public Restraint end2Releases { get; set; } + public Vector end1Offset { get; set; } + public Vector end2Offset { get; set; } + public Node? orientationNode { get; set; } + public double orientationAngle { get; set; } + public Plane? localAxis { get; set; } + + [DetachProperty] + public Base? parent { get; set; } //parent element + + [DetachProperty] + public Node? end1Node { get; set; } //startNode + + [DetachProperty] + public Node? end2Node { get; set; } //endNode + + [DetachProperty] + public List? topology { get; set; } + + public string? units { get; set; } + + [DetachProperty] + public IReadOnlyList displayValue { get; set; } + + #region Schema Info Constructors [SchemaInfo( "Element1D (from local axis)", "Creates a Speckle structural 1D element (from local axis)", @@ -46,31 +93,8 @@ public Element1D( [SchemaParamInfo("If null, defaults to no offsets")] Vector? end2Offset = null, Plane? localAxis = null ) - { - this.baseLine = baseLine; - this.property = property; - this.type = type; - this.name = name; - this.end1Releases = end1Releases ?? new Restraint("FFFFFF"); - this.end2Releases = end2Releases ?? new Restraint("FFFFFF"); - this.end1Offset = end1Offset ?? new Vector(0, 0, 0); - this.end2Offset = end2Offset ?? new Vector(0, 0, 0); - this.localAxis = localAxis; - } + : this(baseLine, property, type, name, null, end1Releases, end2Releases, end1Offset, end2Offset, localAxis) { } - /// - /// SchemaBuilder constructor for structural 1D element (based on orientation node and angle) - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// [SchemaInfo( "Element1D (from orientation node and angle)", "Creates a Speckle structural 1D element (from orientation node and angle)", @@ -91,48 +115,19 @@ public Element1D( Node? orientationNode = null, double orientationAngle = 0 ) - { - this.baseLine = baseLine; - this.property = property; - this.type = type; - this.name = name; - this.end1Releases = end1Releases ?? new Restraint("FFFFFF"); - this.end2Releases = end2Releases ?? new Restraint("FFFFFF"); - this.end1Offset = end1Offset ?? new Vector(0, 0, 0); - this.end2Offset = end2Offset ?? new Vector(0, 0, 0); - this.orientationNode = orientationNode; - this.orientationAngle = orientationAngle; - } - - public string? name { get; set; } //add unique id as base identifier, name can change too easily - public Line baseLine { get; set; } + : this( + baseLine, + property, + type, + name, + null, + end1Releases, + end2Releases, + end1Offset, + end2Offset, + orientationNode: orientationNode, + orientationAngle: orientationAngle + ) { } - [DetachProperty] - public Property1D property { get; set; } - - public ElementType1D type { get; set; } - public Restraint end1Releases { get; set; } - public Restraint end2Releases { get; set; } - public Vector end1Offset { get; set; } - public Vector end2Offset { get; set; } - public Node? orientationNode { get; set; } - public double orientationAngle { get; set; } - public Plane? localAxis { get; set; } - - [DetachProperty] - public Base parent { get; set; } //parent element - - [DetachProperty] - public Node end1Node { get; set; } //startNode - - [DetachProperty] - public Node end2Node { get; set; } //endNode - - [DetachProperty] - public List topology { get; set; } - - public string units { get; set; } - - [DetachProperty] - public List displayValue { get; set; } + #endregion } diff --git a/Objects/Tests/Objects.Tests.Unit/ModelPropertySupportedTypes.cs b/Objects/Tests/Objects.Tests.Unit/ModelPropertySupportedTypes.cs new file mode 100644 index 0000000000..342e0cad23 --- /dev/null +++ b/Objects/Tests/Objects.Tests.Unit/ModelPropertySupportedTypes.cs @@ -0,0 +1,81 @@ +using System; +using System.Collections.Generic; +using System.DoubleNumerics; +using System.Drawing; +using System.Linq; +using NUnit.Framework; +using Speckle.Core.Models; +using Speckle.Core.Serialisation; +using Speckle.Newtonsoft.Json; + +namespace Objects.Tests.Unit; + +/// +/// Tests that all Base object models in the kit have properties that are an allowed type +/// This test is not exhaustive, there are plenty of generic arg combinations that will pass this test, +/// but still not work / are not defined behaviour. This test will just catch many types that definitely won't work +/// +public class ModelPropertySupportedTypes +{ + /// + /// Set of types that we support in Base objects + /// If it's not in the list, or is commented out, it's not supported by our serializer! + /// + /// + /// If you're tempted to add to this list, please ensure both our serializer and deserializer support properties of this type + /// Check the + /// Check the + /// (or is an interface where all concrete types are supported) + /// You should also consider adding a test in SerializerNonBreakingChanges + /// + private readonly HashSet _allowedTypes = + new() + { + typeof(Boolean), + typeof(Byte), + typeof(UInt32), + typeof(UInt64), + typeof(Int16), + typeof(Int32), + typeof(Int64), + //typeof(Half), + typeof(Single), + typeof(Double), + typeof(Char), + typeof(string), + typeof(DateTime), + typeof(Guid), + typeof(Color), + typeof(List<>), + typeof(Nullable<>), + typeof(IList<>), + typeof(IReadOnlyList<>), + typeof(Dictionary<,>), + //typeof(IDictionary<,>), + //typeof(IReadOnlyDictionary<,>), + typeof(ICurve), + typeof(Object), + typeof(Matrix4x4), + }; + + [Test] + [TestCaseSource(typeof(GenericTests), nameof(GenericTests.AvailableTypesInKit))] + public void TestObjects(Type t) + { + var members = DynamicBase.GetInstanceMembers(t).Where(p => !p.IsDefined(typeof(JsonIgnoreAttribute), true)); + + foreach (var prop in members) + { + if (prop.PropertyType.IsAssignableTo(typeof(Base))) + continue; + if (prop.PropertyType.IsEnum) + continue; + if (prop.PropertyType.IsSZArray) + continue; + + Type propType = prop.PropertyType; + Type typeDef = propType.IsGenericType ? propType.GetGenericTypeDefinition() : propType; + Assert.That(_allowedTypes, Does.Contain(typeDef), $"{typeDef} was not in allowedTypes"); + } + } +}