diff --git a/.circleci/scripts/config-template.yml b/.circleci/scripts/config-template.yml index 7a29460839..1acc8a2280 100644 --- a/.circleci/scripts/config-template.yml +++ b/.circleci/scripts/config-template.yml @@ -504,15 +504,13 @@ jobs: # Each project will have individual jobs for each specific task it has to build-archicad-add-on: # build Archicad C++ add-on parameters: - e: - type: string - default: win/default archicadversion: type: string default: "" executor: - name: << parameters.e >> + name: win/server-2019 shell: bash.exe + version: 2023.04.1 # Version 2023.08.01 broke this step due to missing MSVC v142 C++ build tools. Fixed to the prior working version till a fix is issued. steps: - cached-checkout - attach_workspace: diff --git a/.circleci/scripts/connector-jobs.yml b/.circleci/scripts/connector-jobs.yml index 69b6628843..3c711e5a1e 100644 --- a/.circleci/scripts/connector-jobs.yml +++ b/.circleci/scripts/connector-jobs.yml @@ -124,13 +124,11 @@ csi: archicad: - build-archicad-add-on: - e: win/server-2019 archicadversion: "25" requires: - get-ci-tools name: build-archicad-add-on-25 - build-archicad-add-on: - e: win/server-2019 archicadversion: "26" requires: - get-ci-tools diff --git a/.vscode/settings.json b/.vscode/settings.json index 444722ff71..f5bc003aa2 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -8,10 +8,10 @@ }, "omnisharp.enableEditorConfigSupport": true, "omnisharp.enableRoslynAnalyzers": true, - "omnisharp.defaultLaunchSolution": "SDK.slnf", "[dotnet][xml]": { "editor.defaultFormatter": "ms-dotnettools.csharp", "editor.tabSize": 2 }, - "xml.format.spaceBeforeEmptyCloseTag": false + "xml.format.spaceBeforeEmptyCloseTag": false, + "dotnet.defaultSolution": "SDK.slnf" } diff --git a/All.sln b/All.sln index 6d06a8307c..5e1fe75b15 100644 --- a/All.sln +++ b/All.sln @@ -364,6 +364,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConnectorTeklaStructures202 EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConverterTeklaStructures2023", "Objects\Converters\ConverterTeklaStructures\ConverterTeklaStructures2023\ConverterTeklaStructures2023.csproj", "{EB52E451-9ED8-460E-9EE4-6717BFB12EAB}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestsPerformance", "Core\TestsPerformance\TestsPerformance.csproj", "{4D1C70D7-FFD5-4518-A374-2A23E020D416}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConnectorAdvanceSteel2024", "ConnectorAutocadCivil\ConnectorAdvanceSteel2024\ConnectorAdvanceSteel2024.csproj", "{3B9189B9-E485-448A-8793-9B9587A36791}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConverterAdvanceSteel2024", "Objects\Converters\ConverterAutocadCivil\ConverterAdvanceSteel2024\ConverterAdvanceSteel2024.csproj", "{737D5567-7B1F-410D-9B7B-BAE8065ED15B}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug Mac|Any CPU = Debug Mac|Any CPU @@ -1985,6 +1991,54 @@ Global {EB52E451-9ED8-460E-9EE4-6717BFB12EAB}.Release|Any CPU.Build.0 = Release|Any CPU {EB52E451-9ED8-460E-9EE4-6717BFB12EAB}.Release|x64.ActiveCfg = Release|Any CPU {EB52E451-9ED8-460E-9EE4-6717BFB12EAB}.Release|x64.Build.0 = Release|Any CPU + {4D1C70D7-FFD5-4518-A374-2A23E020D416}.Debug Mac|Any CPU.ActiveCfg = Debug|Any CPU + {4D1C70D7-FFD5-4518-A374-2A23E020D416}.Debug Mac|Any CPU.Build.0 = Debug|Any CPU + {4D1C70D7-FFD5-4518-A374-2A23E020D416}.Debug Mac|x64.ActiveCfg = Debug|Any CPU + {4D1C70D7-FFD5-4518-A374-2A23E020D416}.Debug Mac|x64.Build.0 = Debug|Any CPU + {4D1C70D7-FFD5-4518-A374-2A23E020D416}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4D1C70D7-FFD5-4518-A374-2A23E020D416}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4D1C70D7-FFD5-4518-A374-2A23E020D416}.Debug|x64.ActiveCfg = Debug|Any CPU + {4D1C70D7-FFD5-4518-A374-2A23E020D416}.Debug|x64.Build.0 = Debug|Any CPU + {4D1C70D7-FFD5-4518-A374-2A23E020D416}.Release Mac|Any CPU.ActiveCfg = Debug|Any CPU + {4D1C70D7-FFD5-4518-A374-2A23E020D416}.Release Mac|Any CPU.Build.0 = Debug|Any CPU + {4D1C70D7-FFD5-4518-A374-2A23E020D416}.Release Mac|x64.ActiveCfg = Debug|Any CPU + {4D1C70D7-FFD5-4518-A374-2A23E020D416}.Release Mac|x64.Build.0 = Debug|Any CPU + {4D1C70D7-FFD5-4518-A374-2A23E020D416}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4D1C70D7-FFD5-4518-A374-2A23E020D416}.Release|Any CPU.Build.0 = Release|Any CPU + {4D1C70D7-FFD5-4518-A374-2A23E020D416}.Release|x64.ActiveCfg = Release|Any CPU + {4D1C70D7-FFD5-4518-A374-2A23E020D416}.Release|x64.Build.0 = Release|Any CPU + {3B9189B9-E485-448A-8793-9B9587A36791}.Debug Mac|Any CPU.ActiveCfg = Debug|Any CPU + {3B9189B9-E485-448A-8793-9B9587A36791}.Debug Mac|Any CPU.Build.0 = Debug|Any CPU + {3B9189B9-E485-448A-8793-9B9587A36791}.Debug Mac|x64.ActiveCfg = Debug|Any CPU + {3B9189B9-E485-448A-8793-9B9587A36791}.Debug Mac|x64.Build.0 = Debug|Any CPU + {3B9189B9-E485-448A-8793-9B9587A36791}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3B9189B9-E485-448A-8793-9B9587A36791}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3B9189B9-E485-448A-8793-9B9587A36791}.Debug|x64.ActiveCfg = Debug|Any CPU + {3B9189B9-E485-448A-8793-9B9587A36791}.Debug|x64.Build.0 = Debug|Any CPU + {3B9189B9-E485-448A-8793-9B9587A36791}.Release Mac|Any CPU.ActiveCfg = Release|Any CPU + {3B9189B9-E485-448A-8793-9B9587A36791}.Release Mac|Any CPU.Build.0 = Release|Any CPU + {3B9189B9-E485-448A-8793-9B9587A36791}.Release Mac|x64.ActiveCfg = Release|Any CPU + {3B9189B9-E485-448A-8793-9B9587A36791}.Release Mac|x64.Build.0 = Release|Any CPU + {3B9189B9-E485-448A-8793-9B9587A36791}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3B9189B9-E485-448A-8793-9B9587A36791}.Release|Any CPU.Build.0 = Release|Any CPU + {3B9189B9-E485-448A-8793-9B9587A36791}.Release|x64.ActiveCfg = Release|Any CPU + {3B9189B9-E485-448A-8793-9B9587A36791}.Release|x64.Build.0 = Release|Any CPU + {737D5567-7B1F-410D-9B7B-BAE8065ED15B}.Debug Mac|Any CPU.ActiveCfg = Debug|Any CPU + {737D5567-7B1F-410D-9B7B-BAE8065ED15B}.Debug Mac|Any CPU.Build.0 = Debug|Any CPU + {737D5567-7B1F-410D-9B7B-BAE8065ED15B}.Debug Mac|x64.ActiveCfg = Debug|Any CPU + {737D5567-7B1F-410D-9B7B-BAE8065ED15B}.Debug Mac|x64.Build.0 = Debug|Any CPU + {737D5567-7B1F-410D-9B7B-BAE8065ED15B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {737D5567-7B1F-410D-9B7B-BAE8065ED15B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {737D5567-7B1F-410D-9B7B-BAE8065ED15B}.Debug|x64.ActiveCfg = Debug|Any CPU + {737D5567-7B1F-410D-9B7B-BAE8065ED15B}.Debug|x64.Build.0 = Debug|Any CPU + {737D5567-7B1F-410D-9B7B-BAE8065ED15B}.Release Mac|Any CPU.ActiveCfg = Release|Any CPU + {737D5567-7B1F-410D-9B7B-BAE8065ED15B}.Release Mac|Any CPU.Build.0 = Release|Any CPU + {737D5567-7B1F-410D-9B7B-BAE8065ED15B}.Release Mac|x64.ActiveCfg = Release|Any CPU + {737D5567-7B1F-410D-9B7B-BAE8065ED15B}.Release Mac|x64.Build.0 = Release|Any CPU + {737D5567-7B1F-410D-9B7B-BAE8065ED15B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {737D5567-7B1F-410D-9B7B-BAE8065ED15B}.Release|Any CPU.Build.0 = Release|Any CPU + {737D5567-7B1F-410D-9B7B-BAE8065ED15B}.Release|x64.ActiveCfg = Release|Any CPU + {737D5567-7B1F-410D-9B7B-BAE8065ED15B}.Release|x64.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -2127,6 +2181,7 @@ Global {8AD2EA4F-14FB-4BB6-94CD-932630DFED9C} = {C73C19B5-72A3-4C63-8D56-0A7E7DB46CA5} {EA34AC83-5825-4473-A572-D5127FD33B1B} = {C73C19B5-72A3-4C63-8D56-0A7E7DB46CA5} {521A7D9C-637F-4965-A6E6-BA96DF99807D} = {C73C19B5-72A3-4C63-8D56-0A7E7DB46CA5} + {61C1304B-ED48-456B-AB90-A89066187952} = {C73C19B5-72A3-4C63-8D56-0A7E7DB46CA5} {071F914C-F473-4FB2-9FAF-98632AFB164B} = {C73C19B5-72A3-4C63-8D56-0A7E7DB46CA5} {54E90327-5F48-468D-9349-17AACEAA0A77} = {25F45C77-279F-4608-86D1-87345EC42CB4} {6499CA05-6864-47AE-9204-B11B10C23417} = {25F45C77-279F-4608-86D1-87345EC42CB4} @@ -2137,6 +2192,9 @@ Global {B4D6F6DC-0712-4F9F-A24F-6B76DAE84B6F} = {BE521908-7944-46F3-98BF-B47D34509934} {511C2FB0-9C73-4AC9-BA59-C8A84C089C59} = {18C8730C-0173-4987-9416-46F86EC20541} {EB52E451-9ED8-460E-9EE4-6717BFB12EAB} = {5D988C50-8E85-402A-9020-A4AB0565F0C9} + {4D1C70D7-FFD5-4518-A374-2A23E020D416} = {8AA78EE8-C33B-4BC5-992A-E5DE7AB0BEC7} + {3B9189B9-E485-448A-8793-9B9587A36791} = {7B7C4CB1-3D60-4A5B-9902-C812521A24B3} + {737D5567-7B1F-410D-9B7B-BAE8065ED15B} = {BE521908-7944-46F3-98BF-B47D34509934} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {1D43D91B-4F01-4A78-8250-CC6F9BD93A14} @@ -2160,6 +2218,7 @@ Global Objects\Converters\ConverterRevit\ConverterRevitShared\ConverterRevitShared.projitems*{2dcd648d-dca5-4d2a-8b14-ad2cb85d24b0}*SharedItemsImports = 13 ConnectorBentley\ConnectorBentleyShared\ConnectorBentleyShared.projitems*{372d9f0f-ede9-4050-bf8c-758911c5c2e0}*SharedItemsImports = 13 ConnectorTeklaStructures\ConnectorTeklaStructuresShared\ConnectorTeklaStructuresShared.projitems*{3af1ef30-0906-4926-a02c-4e3ad666352a}*SharedItemsImports = 5 + ConnectorAutocadCivil\ConnectorAutocadCivil\ConnectorAutocadCivilShared.projitems*{3b9189b9-e485-448a-8793-9b9587a36791}*SharedItemsImports = 5 Objects\Converters\ConverterRhinoGh\ConverterRhinoGhShared\ConverterRhinoGhShared.projitems*{3cdef4cc-2cfa-4939-8427-3ed00fa9db55}*SharedItemsImports = 5 Objects\Converters\ConverterDynamo\ConverterDynamoShared\ConverterDynamoShared.projitems*{3df12639-78b6-41b3-a046-a675035369be}*SharedItemsImports = 5 ConnectorAutocadCivil\ConnectorAutocadCivil\ConnectorAutocadCivilShared.projitems*{3e30d170-3cb4-4728-97d5-887c5019da9b}*SharedItemsImports = 5 @@ -2187,6 +2246,7 @@ Global ConnectorTeklaStructures\ConnectorTeklaStructuresShared\ConnectorTeklaStructuresShared.projitems*{67157264-aaa5-46a8-a38b-16254b49b892}*SharedItemsImports = 5 Objects\Converters\ConverterDynamo\ConverterDynamoShared\ConverterDynamoShared.projitems*{67a463d3-e98b-4b16-b069-d7bbb05386a1}*SharedItemsImports = 5 Objects\Converters\ConverterRevit\ConverterRevitShared\ConverterRevitShared.projitems*{67a463d3-e98b-4b16-b069-d7bbb05386a1}*SharedItemsImports = 5 + Objects\Converters\ConverterAutocadCivil\ConverterAutocadCivilShared\ConverterAutocadCivilShared.projitems*{737d5567-7b1f-410d-9b7b-bae8065ed15b}*SharedItemsImports = 5 ConnectorNavisworks\ConnectorNavisworks\ConnectorNavisworks.Shared.projitems*{74e39841-b2fa-494d-ac40-a6e505de6b33}*SharedItemsImports = 5 Objects\Converters\ConverterRevit\ConverterRevitShared\ConverterRevitShared.projitems*{76937388-bc9e-4083-9d6e-59cc627e3804}*SharedItemsImports = 5 ConnectorNavisworks\ConnectorNavisworks\ConnectorNavisworks.Shared.projitems*{77d4f346-aca5-42c8-8522-5ef176f3adf1}*SharedItemsImports = 5 diff --git a/ConnectorArchicad/AddOn/Sources/AddOn/AddOnMain.cpp b/ConnectorArchicad/AddOn/Sources/AddOn/AddOnMain.cpp index 76dc800b11..b7f1f7c4ee 100644 --- a/ConnectorArchicad/AddOn/Sources/AddOn/AddOnMain.cpp +++ b/ConnectorArchicad/AddOn/Sources/AddOn/AddOnMain.cpp @@ -17,12 +17,12 @@ #include "Commands/GetElementBaseData.hpp" #include "Commands/GetObjectData.hpp" #include "Commands/GetSlabData.hpp" -#include "Commands/GetRoomData.hpp" #include "Commands/GetRoofData.hpp" #include "Commands/GetShellData.hpp" #include "Commands/GetSkylightData.hpp" #include "Commands/GetProjectInfo.hpp" #include "Commands/GetSubElementInfo.hpp" +#include "Commands/GetZoneData.hpp" #include "Commands/CreateWall.hpp" #include "Commands/CreateDoor.hpp" #include "Commands/CreateWindow.hpp" @@ -198,12 +198,12 @@ static GSErrCode RegisterAddOnCommands () CHECKERROR (ACAPI_Install_AddOnCommandHandler (NewOwned ())); CHECKERROR (ACAPI_Install_AddOnCommandHandler (NewOwned ())); CHECKERROR (ACAPI_Install_AddOnCommandHandler (NewOwned ())); - CHECKERROR (ACAPI_Install_AddOnCommandHandler (NewOwned ())); CHECKERROR (ACAPI_Install_AddOnCommandHandler (NewOwned ())); CHECKERROR (ACAPI_Install_AddOnCommandHandler (NewOwned ())); CHECKERROR (ACAPI_Install_AddOnCommandHandler (NewOwned ())); CHECKERROR (ACAPI_Install_AddOnCommandHandler (NewOwned ())); CHECKERROR (ACAPI_Install_AddOnCommandHandler (NewOwned ())); + CHECKERROR (ACAPI_Install_AddOnCommandHandler (NewOwned ())); CHECKERROR (ACAPI_Install_AddOnCommandHandler (NewOwned ())); CHECKERROR (ACAPI_Install_AddOnCommandHandler (NewOwned ())); CHECKERROR (ACAPI_Install_AddOnCommandHandler (NewOwned ())); diff --git a/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateZone.cpp b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateZone.cpp index 778c15ead2..edd5cfc022 100644 --- a/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateZone.cpp +++ b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateZone.cpp @@ -59,6 +59,9 @@ GSErrCode CreateZone::GetElementFromObjectState (const GS::ObjectState& os, ACAPI_ELEMENT_MASK_SET (mask, API_ZoneType, poly.nArcs); zoneShape.SetToMemo (memo, Objects::ElementShape::MemoMainPolygon); + + element.zone.manual = true; + ACAPI_ELEMENT_MASK_SET (mask, API_ZoneType, manual); } if (os.Contains (Room::Height)) { @@ -68,11 +71,15 @@ GSErrCode CreateZone::GetElementFromObjectState (const GS::ObjectState& os, // The name and number of the zone if (os.Contains (Room::Name)) { - os.Get (Room::Name, element.zone.roomName); + GS::UniString str; + os.Get (Room::Name, str); + GS::ucscpy (element.zone.roomName, str.ToUStr ()); ACAPI_ELEMENT_MASK_SET (mask, API_ZoneType, roomName); } if (os.Contains (Room::Number)) { - os.Get (Room::Number, element.zone.roomNoStr); + GS::UniString str; + os.Get (Room::Number, str); + GS::ucscpy (element.zone.roomNoStr, str.ToUStr ()); ACAPI_ELEMENT_MASK_SET (mask, API_ZoneType, roomNoStr); } diff --git a/ConnectorArchicad/AddOn/Sources/AddOn/Commands/GetDataCommand.cpp b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/GetDataCommand.cpp index 6a1163cc99..8ff0987b34 100644 --- a/ConnectorArchicad/AddOn/Sources/AddOn/Commands/GetDataCommand.cpp +++ b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/GetDataCommand.cpp @@ -14,7 +14,7 @@ GS::ErrCode GetDataCommand::ExportClassificationsAndProperties (const API_Elemen { GS::UniString typeName; - err = Utility::GetTypeNameFromElementType(elem.header, typeName); + err = Utility::GetLocalizedElementTypeName (elem.header, typeName); if (err != NoError) return err; diff --git a/ConnectorArchicad/AddOn/Sources/AddOn/Commands/GetElementIds.cpp b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/GetElementIds.cpp index 22b01cd958..edc86bb282 100644 --- a/ConnectorArchicad/AddOn/Sources/AddOn/Commands/GetElementIds.cpp +++ b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/GetElementIds.cpp @@ -49,6 +49,31 @@ static GS::Array GetAllElementGuids () } +static GS::Array GetElementsFilteredByElementTypes (GS::Array& elementTypes) +{ + GS::Array filteredGuids; + GS::Array elementGuids; + GSErrCode err = ACAPI_Element_GetElemList (API_ZombieElemID, &elementGuids, APIFilt_OnVisLayer | APIFilt_In3D); + if (err == NoError) { + for (const API_Guid& guid : elementGuids) { + if (Utility::IsElement3D (guid)) { + API_Elem_Head elementHead = {}; + elementHead.guid = guid; + err = ACAPI_Element_GetHeader (&elementHead); + if (err != NoError) + continue; + + GS::UniString elementTypeName; + Utility::GetNonLocalizedElementTypeName (elementHead, elementTypeName); + if (elementTypes.Contains (elementTypeName)) + filteredGuids.Push (guid); + } + } + } + return filteredGuids; +} + + GS::String GetElementIds::GetName () const { return GetElementIdsCommandName; @@ -67,6 +92,12 @@ GS::ObjectState GetElementIds::Execute (const GS::ObjectState& parameters, GS::P elementGuids = GetSelectedElementGuids (); else if (elementFilter == "All") elementGuids = GetAllElementGuids (); + else if (elementFilter == "ElementType") { + GS::Array elementTypes; + parameters.Get (ElementBase::FilterBy, elementTypes); + if(elementTypes.GetSize() > 0) + elementGuids = GetElementsFilteredByElementTypes (elementTypes); + } const auto& listAdder = retVal.AddList (ElementBase::ApplicationIds); for (const API_Guid& guid : elementGuids) { diff --git a/ConnectorArchicad/AddOn/Sources/AddOn/Commands/GetRoomData.cpp b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/GetZoneData.cpp similarity index 62% rename from ConnectorArchicad/AddOn/Sources/AddOn/Commands/GetRoomData.cpp rename to ConnectorArchicad/AddOn/Sources/AddOn/Commands/GetZoneData.cpp index 82c9ef3a19..99a6d4ea18 100644 --- a/ConnectorArchicad/AddOn/Sources/AddOn/Commands/GetRoomData.cpp +++ b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/GetZoneData.cpp @@ -1,4 +1,4 @@ -#include "GetRoomData.hpp" +#include "GetZoneData.hpp" #include #include "ResourceIds.hpp" #include "ObjectState.hpp" @@ -14,19 +14,19 @@ namespace AddOnCommands { -GS::String GetRoomData::GetFieldName () const +GS::String GetZoneData::GetFieldName () const { return Zones; } -API_ElemTypeID GetRoomData::GetElemTypeID () const +API_ElemTypeID GetZoneData::GetElemTypeID () const { return API_ZoneID; } -GS::ErrCode GetRoomData::SerializeElementType (const API_Element& element, +GS::ErrCode GetZoneData::SerializeElementType (const API_Element& element, const API_ElementMemo& memo, GS::ObjectState& os) const { @@ -60,30 +60,39 @@ GS::ErrCode GetRoomData::SerializeElementType (const API_Element& element, // The base point of the room double level = Utility::GetStoryLevel (element.zone.head.floorInd) + element.zone.roomBaseLev; - os.Add (Room::BasePoint, Objects::Point3D (0, 0, level)); + { + Geometry::Polygon2DData polygon; + Utility::ConstructPoly2DDataFromElementMemo (memo, polygon); + + const Box2DData boundingBox = polygon.boundBox; + GS::Array sectors; + bool res = Geometry::IntersectLineWithPolygon (polygon, + boundingBox.GetMidPoint(), + boundingBox.GetWidth() > boundingBox.GetHeight() ? Vector2D (1.0, 0.0) : Vector2D (0.0, 1.0), + §ors); + + Geometry::FreePolygon2DData (&polygon); + + Objects::Point3D basePoint (0, 0, level); + if (res && sectors.GetSize() > 0) { + Sector sector = sectors[sectors.GetSize() / 2]; + basePoint = Objects::Point3D (sector.GetMidPoint ().GetX (), sector.GetMidPoint ().GetY (), level); + } + + os.Add (Room::BasePoint, Objects::Point3D (basePoint.x, basePoint.y, basePoint.z)); + } os.Add (ElementBase::Shape, Objects::ElementShape (element.zone.poly, memo, Objects::ElementShape::MemoMainPolygon, level)); - // double polyCoords [zone.poly.nCoords*3]; - // - // for (Int32 point_index = 0, coord_index = 0; point_index < zone.poly.nCoords; ++point_index, coord_index+=3) - // { - // const API_Coord coord = (*memo.coords)[point_index]; - // polyCoords[coord_index] = coord.x; - // polyCoords[coord_index+1] = coord.y; - // polyCoords[coord_index+2] = level; - // } - // Room Props os.Add (Room::Height, element.zone.roomHeight); os.Add (Room::Area, quantity.zone.area); os.Add (Room::Volume, quantity.zone.volume); - return NoError; } -GS::String GetRoomData::GetName () const +GS::String GetZoneData::GetName () const { return GetRoomDataCommandName; } diff --git a/ConnectorArchicad/AddOn/Sources/AddOn/Commands/GetRoomData.hpp b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/GetZoneData.hpp similarity index 83% rename from ConnectorArchicad/AddOn/Sources/AddOn/Commands/GetRoomData.hpp rename to ConnectorArchicad/AddOn/Sources/AddOn/Commands/GetZoneData.hpp index 37b0f6b688..f630ff3cbb 100644 --- a/ConnectorArchicad/AddOn/Sources/AddOn/Commands/GetRoomData.hpp +++ b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/GetZoneData.hpp @@ -1,4 +1,4 @@ -#ifndef GET_ROOM_DATA_HPP +#ifndef GET_ROOM_DATA_HPP #define GET_ROOM_DATA_HPP #include "GetDataCommand.hpp" @@ -7,7 +7,7 @@ namespace AddOnCommands { -class GetRoomData : public GetDataCommand { +class GetZoneData : public GetDataCommand { GS::String GetFieldName () const override; API_ElemTypeID GetElemTypeID () const override; GS::ErrCode SerializeElementType (const API_Element& elem, @@ -22,4 +22,4 @@ class GetRoomData : public GetDataCommand { } -#endif \ No newline at end of file +#endif diff --git a/ConnectorArchicad/AddOn/Sources/AddOn/FieldNames.hpp b/ConnectorArchicad/AddOn/Sources/AddOn/FieldNames.hpp index 5dce0c6bf6..1cb4425c36 100644 --- a/ConnectorArchicad/AddOn/Sources/AddOn/FieldNames.hpp +++ b/ConnectorArchicad/AddOn/Sources/AddOn/FieldNames.hpp @@ -26,6 +26,7 @@ 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"; diff --git a/ConnectorArchicad/AddOn/Sources/AddOn/ResourceIds.hpp b/ConnectorArchicad/AddOn/Sources/AddOn/ResourceIds.hpp index 8c6f700ede..e01654193d 100644 --- a/ConnectorArchicad/AddOn/Sources/AddOn/ResourceIds.hpp +++ b/ConnectorArchicad/AddOn/Sources/AddOn/ResourceIds.hpp @@ -1,11 +1,12 @@ #ifndef RESOURCEIDS_HPP #define RESOURCEIDS_HPP -#define ID_ADDON_INFO 32000 -#define ID_ADDON_MENU 32500 -#define ID_DEFAULT_STORY_FORMAT 32800 -#define ID_LOG_MESSAGES 33000 -#define ID_ELEMENT_TYPE_STRINGS 34000 +#define ID_ADDON_INFO 32000 +#define ID_ADDON_MENU 32010 +#define ID_DEFAULT_STORY_FORMAT 32020 +#define ID_LOG_MESSAGES 33030 +#define ID_ELEMENT_TYPE_STRINGS 32040 +#define ID_FIX_ELEMENT_TYPE_STRINGS 32050 #define ID_LOG_MESSAGE_LIBPART_SEARCH_ERROR 1 #define ID_LOG_MESSAGE_ATTRIBUTE_SEARCH_ERROR 2 diff --git a/ConnectorArchicad/AddOn/Sources/AddOn/ResourceStrings.cpp b/ConnectorArchicad/AddOn/Sources/AddOn/ResourceStrings.cpp index 4c7af5be08..18b2865c50 100644 --- a/ConnectorArchicad/AddOn/Sources/AddOn/ResourceStrings.cpp +++ b/ConnectorArchicad/AddOn/Sources/AddOn/ResourceStrings.cpp @@ -25,3 +25,9 @@ const GS::UniString& ResourceStrings::GetElementTypeStringFromResource (const El { return TGetStringFromResource (ID_ELEMENT_TYPE_STRINGS, resourceItemId); } + + +const GS::UniString& ResourceStrings::GetFixElementTypeStringFromResource (const ElementTypeStringItems& resourceItemId) +{ + return TGetStringFromResource (ID_FIX_ELEMENT_TYPE_STRINGS, resourceItemId); +} diff --git a/ConnectorArchicad/AddOn/Sources/AddOn/ResourceStrings.hpp b/ConnectorArchicad/AddOn/Sources/AddOn/ResourceStrings.hpp index 232cbde786..1a5abe9b8d 100644 --- a/ConnectorArchicad/AddOn/Sources/AddOn/ResourceStrings.hpp +++ b/ConnectorArchicad/AddOn/Sources/AddOn/ResourceStrings.hpp @@ -84,6 +84,7 @@ enum class ElementTypeStringItems { }; const GS::UniString& GetElementTypeStringFromResource (const ElementTypeStringItems& resourceItemId); +const GS::UniString& GetFixElementTypeStringFromResource (const ElementTypeStringItems& resourceItemId); } diff --git a/ConnectorArchicad/AddOn/Sources/AddOn/Utility.cpp b/ConnectorArchicad/AddOn/Sources/AddOn/Utility.cpp index 61c907c4e3..bac3f786b4 100644 --- a/ConnectorArchicad/AddOn/Sources/AddOn/Utility.cpp +++ b/ConnectorArchicad/AddOn/Sources/AddOn/Utility.cpp @@ -5,6 +5,7 @@ #include "FieldNames.hpp" #include "TypeNameTables.hpp" #include "ResourceStrings.hpp" +#include "Polygon2DData.h" using namespace FieldNames; namespace Utility { @@ -33,93 +34,115 @@ API_ElemTypeID GetElementType (const API_Guid& guid) } -GS::ErrCode GetTypeNameFromElementType (const API_Elem_Head& header, GS::UniString& typeName) +GSErrCode GetElementTypeStringItem (const API_Elem_Head& header, ResourceStrings::ElementTypeStringItems& elementTypeStringItem) { #ifdef ServerMainVers_2600 - return ACAPI_Goodies_GetElemTypeName (header.type, typeName); + switch (header.type.typeID) { #else - ResourceStrings::ElementTypeStringItems elementTypeStringItems; switch (header.typeID) { - case API_WallID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::WallString; break; - case API_ColumnID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::ColumnString; break; - case API_BeamID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::BeamString; break; - case API_WindowID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::WindowString; break; - case API_DoorID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::DoorString; break; - case API_ObjectID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::ObjectString; break; - case API_LampID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::LampString; break; - case API_SlabID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::SlabString; break; - case API_RoofID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::RoofString; break; - case API_MeshID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::MeshString; break; - case API_DimensionID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::DimensionString; break; - case API_RadialDimensionID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::RadialDimensionString; break; - case API_LevelDimensionID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::LevelDimensionString; break; - case API_AngleDimensionID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::AngleDimensionString; break; - case API_TextID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::TextString; break; - case API_LabelID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::LabelString; break; - case API_ZoneID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::ZoneString; break; - case API_HatchID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::HatchString; break; - case API_LineID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::LineString; break; - case API_PolyLineID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::PolyLineString; break; - case API_ArcID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::ArcString; break; - case API_CircleID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::CircleString; break; - case API_SplineID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::SplineString; break; - case API_HotspotID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::HotspotString; break; - case API_CutPlaneID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::CutPlaneString; break; - case API_CameraID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::CameraString; break; - case API_CamSetID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::CamSetString; break; - case API_GroupID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::GroupString; break; - case API_SectElemID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::SectElemString; break; - case API_DrawingID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::DrawingString; break; - case API_PictureID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::PictureString; break; - case API_DetailID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::DetailString; break; - case API_ElevationID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::ElevationString; break; - case API_InteriorElevationID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::InteriorElevationString; break; - case API_WorksheetID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::WorksheetString; break; - case API_HotlinkID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::HotlinkString; break; - case API_CurtainWallID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::CurtainWallString; break; - case API_CurtainWallSegmentID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::CurtainWallSegmentString; break; - case API_CurtainWallFrameID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::CurtainWallFrameString; break; - case API_CurtainWallPanelID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::CurtainWallPanelString; break; - case API_CurtainWallJunctionID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::CurtainWallJunctionString; break; - case API_CurtainWallAccessoryID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::CurtainWallAccessoryString; break; - case API_ShellID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::ShellString; break; - case API_SkylightID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::SkylightString; break; - case API_MorphID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::MorphString; break; - case API_ChangeMarkerID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::ChangeMarkerString; break; - case API_StairID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::StairString; break; - case API_RiserID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::RiserString; break; - case API_TreadID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::TreadString; break; - case API_StairStructureID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::StairStructureString; break; - case API_RailingID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::RailingString; break; - case API_RailingToprailID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::RailingToprailString; break; - case API_RailingHandrailID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::RailingHandrailString; break; - case API_RailingRailID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::RailingRailString; break; - case API_RailingPostID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::RailingPostString; break; - case API_RailingInnerPostID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::RailingInnerPostString; break; - case API_RailingBalusterID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::RailingBalusterString; break; - case API_RailingPanelID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::RailingPanelString; break; - case API_RailingSegmentID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::RailingSegmentString; break; - case API_RailingNodeID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::RailingNodeString; break; - case API_RailingBalusterSetID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::RailingBalusterSetString; break; - case API_RailingPatternID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::RailingPatternString; break; - case API_RailingToprailEndID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::RailingToprailEndString; break; - case API_RailingHandrailEndID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::RailingHandrailEndString; break; - case API_RailingRailEndID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::RailingRailEndString; break; - case API_RailingToprailConnectionID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::RailingToprailConnectionString; break; - case API_RailingHandrailConnectionID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::RailingHandrailConnectionString; break; - case API_RailingRailConnectionID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::RailingRailConnectionString; break; - case API_RailingEndFinishID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::RailingEndFinishString; break; - case API_AnalyticalSupportID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::AnalyticalSupportString; break; - case API_AnalyticalLinkID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::AnalyticalLinkString; break; - case API_BeamSegmentID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::BeamSegmentString; break; - case API_ColumnSegmentID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::ColumnSegmentString; break; - case API_OpeningID: elementTypeStringItems = ResourceStrings::ElementTypeStringItems::OpeningString; break; +#endif + case API_WallID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::WallString; break; + case API_ColumnID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::ColumnString; break; + case API_BeamID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::BeamString; break; + case API_WindowID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::WindowString; break; + case API_DoorID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::DoorString; break; + case API_ObjectID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::ObjectString; break; + case API_LampID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::LampString; break; + case API_SlabID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::SlabString; break; + case API_RoofID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::RoofString; break; + case API_MeshID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::MeshString; break; + case API_DimensionID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::DimensionString; break; + case API_RadialDimensionID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::RadialDimensionString; break; + case API_LevelDimensionID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::LevelDimensionString; break; + case API_AngleDimensionID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::AngleDimensionString; break; + case API_TextID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::TextString; break; + case API_LabelID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::LabelString; break; + case API_ZoneID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::ZoneString; break; + case API_HatchID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::HatchString; break; + case API_LineID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::LineString; break; + case API_PolyLineID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::PolyLineString; break; + case API_ArcID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::ArcString; break; + case API_CircleID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::CircleString; break; + case API_SplineID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::SplineString; break; + case API_HotspotID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::HotspotString; break; + case API_CutPlaneID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::CutPlaneString; break; + case API_CameraID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::CameraString; break; + case API_CamSetID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::CamSetString; break; + case API_GroupID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::GroupString; break; + case API_SectElemID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::SectElemString; break; + case API_DrawingID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::DrawingString; break; + case API_PictureID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::PictureString; break; + case API_DetailID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::DetailString; break; + case API_ElevationID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::ElevationString; break; + case API_InteriorElevationID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::InteriorElevationString; break; + case API_WorksheetID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::WorksheetString; break; + case API_HotlinkID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::HotlinkString; break; + case API_CurtainWallID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::CurtainWallString; break; + case API_CurtainWallSegmentID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::CurtainWallSegmentString; break; + case API_CurtainWallFrameID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::CurtainWallFrameString; break; + case API_CurtainWallPanelID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::CurtainWallPanelString; break; + case API_CurtainWallJunctionID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::CurtainWallJunctionString; break; + case API_CurtainWallAccessoryID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::CurtainWallAccessoryString; break; + case API_ShellID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::ShellString; break; + case API_SkylightID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::SkylightString; break; + case API_MorphID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::MorphString; break; + case API_ChangeMarkerID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::ChangeMarkerString; break; + case API_StairID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::StairString; break; + case API_RiserID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::RiserString; break; + case API_TreadID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::TreadString; break; + case API_StairStructureID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::StairStructureString; break; + case API_RailingID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::RailingString; break; + case API_RailingToprailID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::RailingToprailString; break; + case API_RailingHandrailID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::RailingHandrailString; break; + case API_RailingRailID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::RailingRailString; break; + case API_RailingPostID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::RailingPostString; break; + case API_RailingInnerPostID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::RailingInnerPostString; break; + case API_RailingBalusterID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::RailingBalusterString; break; + case API_RailingPanelID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::RailingPanelString; break; + case API_RailingSegmentID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::RailingSegmentString; break; + case API_RailingNodeID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::RailingNodeString; break; + case API_RailingBalusterSetID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::RailingBalusterSetString; break; + case API_RailingPatternID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::RailingPatternString; break; + case API_RailingToprailEndID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::RailingToprailEndString; break; + case API_RailingHandrailEndID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::RailingHandrailEndString; break; + case API_RailingRailEndID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::RailingRailEndString; break; + case API_RailingToprailConnectionID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::RailingToprailConnectionString; break; + case API_RailingHandrailConnectionID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::RailingHandrailConnectionString; break; + case API_RailingRailConnectionID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::RailingRailConnectionString; break; + case API_RailingEndFinishID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::RailingEndFinishString; break; +#ifndef ServerMainVers_2600 + case API_AnalyticalSupportID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::AnalyticalSupportString; break; + case API_AnalyticalLinkID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::AnalyticalLinkString; break; +#endif + case API_BeamSegmentID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::BeamSegmentString; break; + case API_ColumnSegmentID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::ColumnSegmentString; break; + case API_OpeningID: elementTypeStringItem = ResourceStrings::ElementTypeStringItems::OpeningString; break; default: - { return Error; } - } - typeName = GetElementTypeStringFromResource (elementTypeStringItems); + + return Error; +} + + +GS::ErrCode GetNonLocalizedElementTypeName (const API_Elem_Head& header, GS::UniString& typeName) +{ + ResourceStrings::ElementTypeStringItems elementTypeStringItem; + GetElementTypeStringItem (header, elementTypeStringItem); + typeName = GetFixElementTypeStringFromResource (elementTypeStringItem); + + return typeName.IsEmpty () ? Error : NoError; +} + + +GS::ErrCode GetLocalizedElementTypeName (const API_Elem_Head& header, GS::UniString& typeName) +{ +#ifdef ServerMainVers_2600 + return ACAPI_Goodies_GetElemTypeName (header.type, typeName); +#else + ResourceStrings::ElementTypeStringItems elementTypeStringItem; + GetElementTypeStringItem (header, elementTypeStringItem); + typeName = GetElementTypeStringFromResource (elementTypeStringItem); return typeName.IsEmpty () ? Error : NoError; #endif @@ -1135,4 +1158,52 @@ GSErrCode CreateTransform (const GS::ObjectState& os, API_Tranmat& transform) return NoError; } + +GSErrCode ConstructPoly2DDataFromElementMemo (const API_ElementMemo& memo, Geometry::Polygon2DData& polygon2DData) +{ + GSErrCode err = NoError; + + Geometry::InitPolygon2DData (&polygon2DData); + + static_assert (sizeof (API_Coord) == sizeof (Coord), "sizeof (API_Coord) != sizeof (Coord)"); + static_assert (sizeof (API_PolyArc) == sizeof (PolyArcRec), "sizeof (API_PolyArc) != sizeof (PolyArcRec)"); + + polygon2DData.nVertices = BMGetHandleSize (reinterpret_cast (memo.coords)) / sizeof (Coord) - 1; + polygon2DData.vertices = reinterpret_cast (BMAllocateHandle ((polygon2DData.nVertices + 1) * sizeof (Coord), ALLOCATE_CLEAR, 0)); + if (polygon2DData.vertices != nullptr) + BNCopyMemory (*polygon2DData.vertices, *memo.coords, (polygon2DData.nVertices + 1) * sizeof (Coord)); + else + err = APIERR_MEMFULL; + + if (err == NoError && memo.parcs != nullptr) { + polygon2DData.nArcs = BMGetHandleSize (reinterpret_cast (memo.parcs)) / sizeof (PolyArcRec); + if (polygon2DData.nArcs > 0) { + polygon2DData.arcs = reinterpret_cast (BMAllocateHandle ((polygon2DData.nArcs + 1) * sizeof (PolyArcRec), ALLOCATE_CLEAR, 0)); + if (polygon2DData.arcs != nullptr) + BNCopyMemory (*polygon2DData.arcs + 1, *memo.parcs, polygon2DData.nArcs * sizeof (PolyArcRec)); + else + err = APIERR_MEMFULL; + } + } + + if (err == NoError) { + polygon2DData.nContours = BMGetHandleSize (reinterpret_cast (memo.pends)) / sizeof (Int32) - 1; + polygon2DData.contourEnds = reinterpret_cast (BMAllocateHandle ((polygon2DData.nContours + 1) * sizeof (UIndex), ALLOCATE_CLEAR, 0)); + if (polygon2DData.contourEnds != nullptr) + BNCopyMemory (*polygon2DData.contourEnds, *memo.pends, (polygon2DData.nContours + 1) * sizeof (UIndex)); + else + err = APIERR_MEMFULL; + } + + if (err == NoError) { + Geometry::GetPolygon2DDataBoundBox (polygon2DData, &polygon2DData.boundBox); + polygon2DData.status.isBoundBoxValid = true; + } else { + Geometry::FreePolygon2DData (&polygon2DData); + } + + return err; +} + + } diff --git a/ConnectorArchicad/AddOn/Sources/AddOn/Utility.hpp b/ConnectorArchicad/AddOn/Sources/AddOn/Utility.hpp index d0effed48f..e37328bc04 100644 --- a/ConnectorArchicad/AddOn/Sources/AddOn/Utility.hpp +++ b/ConnectorArchicad/AddOn/Sources/AddOn/Utility.hpp @@ -4,6 +4,7 @@ #include "APIEnvir.h" #include "ACAPinc.h" #include "ResourceIds.hpp" +#include "Polygon2DData.h" #define UNUSED(x) (void)(x) @@ -13,7 +14,8 @@ namespace Utility { // Element Type API_ElemTypeID GetElementType (const API_Elem_Head& header); API_ElemTypeID GetElementType (const API_Guid& guid); -GS::ErrCode GetTypeNameFromElementType (const API_Elem_Head& header, GS::UniString& typeName); +GS::ErrCode GetNonLocalizedElementTypeName (const API_Elem_Head& header, GS::UniString& typeName); +GS::ErrCode GetLocalizedElementTypeName (const API_Elem_Head& header, GS::UniString& typeName); void SetElementType (API_Elem_Head& header, const API_ElemTypeID& elementType); bool ElementExists (const API_Guid& guid); @@ -85,6 +87,9 @@ GS::UniString ComposeLogMessage (const Int32 resourceIndex, Args... args) return GS::UniString::Printf (errMsgFromatString, args...); } +// Geometry helpers +GSErrCode ConstructPoly2DDataFromElementMemo (const API_ElementMemo& memo, Geometry::Polygon2DData& polygon2DData); + } diff --git a/ConnectorArchicad/AddOn/Sources/AddOnResources/RFIX/AddOnFix.grc b/ConnectorArchicad/AddOn/Sources/AddOnResources/RFIX/AddOnFix.grc index fc6db6e691..02d663f4d1 100644 --- a/ConnectorArchicad/AddOn/Sources/AddOnResources/RFIX/AddOnFix.grc +++ b/ConnectorArchicad/AddOn/Sources/AddOnResources/RFIX/AddOnFix.grc @@ -1,4 +1,5 @@ #include "ResourceMDIDIds.hpp" +#include "ResourceIds.hpp" 'MDID' 32500 "Add-On Identifier" { AC_MDID_DEV /* Set AC_MDID_DEV value as your developer id. */ @@ -8,3 +9,80 @@ 'GICN' 10001 "AddOnIcon" { "AddOnIcon" } + +'STR#' ID_FIX_ELEMENT_TYPE_STRINGS "Element Type Strings" { +/* [ 1] */ "Wall" +/* [ 2] */ "Column" +/* [ 3] */ "Beam" +/* [ 4] */ "Window" +/* [ 5] */ "Door" +/* [ 6] */ "Object" +/* [ 7] */ "Lamp" +/* [ 8] */ "Slab" +/* [ 9] */ "Roof" +/* [ 10] */ "Mesh" +/* [ 11] */ "Dimension" +/* [ 12] */ "Radial Dimension" +/* [ 13] */ "Level Dimension" +/* [ 14] */ "Angle Dimension" +/* [ 15] */ "Text" +/* [ 16] */ "Label" +/* [ 17] */ "Zone" +/* [ 18] */ "Hatch" +/* [ 19] */ "Line" +/* [ 10] */ "PolyLine" +/* [ 21] */ "Arc" +/* [ 22] */ "Circle" +/* [ 23] */ "Spline" +/* [ 24] */ "Hotspot" +/* [ 25] */ "CutPlane" +/* [ 26] */ "Camera" +/* [ 27] */ "CamSet" +/* [ 28] */ "Group" +/* [ 29] */ "SectElem" +/* [ 20] */ "Drawing" +/* [ 31] */ "Picture" +/* [ 32] */ "Detail" +/* [ 33] */ "Elevation" +/* [ 34] */ "Interior Elevation" +/* [ 35] */ "Worksheet" +/* [ 36] */ "Hotlink" +/* [ 37] */ "Curtain Wall" +/* [ 38] */ "Curtain Wall Segment" +/* [ 39] */ "Curtain Wall Frame" +/* [ 30] */ "Curtain Wall Panel" +/* [ 41] */ "Curtain Wall Junction" +/* [ 42] */ "Curtain Wall Accessory" +/* [ 43] */ "Shell" +/* [ 44] */ "Skylight" +/* [ 45] */ "Morph" +/* [ 46] */ "Change Marker" +/* [ 47] */ "Stair" +/* [ 48] */ "Riser" +/* [ 49] */ "Tread" +/* [ 40] */ "Stair Structure" +/* [ 51] */ "Railing" +/* [ 52] */ "Railing Toprail" +/* [ 53] */ "Railing Handrail" +/* [ 54] */ "Railing Rail" +/* [ 55] */ "Railing Post" +/* [ 56] */ "Railing Inner Post" +/* [ 57] */ "Railing Baluster" +/* [ 58] */ "Railing Panel" +/* [ 59] */ "Railing Segment" +/* [ 50] */ "Railing Node" +/* [ 61] */ "Railing Baluste rSet" +/* [ 62] */ "Railing Pattern" +/* [ 63] */ "Railing Toprail End" +/* [ 64] */ "Railing Handrail End" +/* [ 65] */ "Railing Rail End" +/* [ 66] */ "Railing Toprail Connection" +/* [ 67] */ "Railing Handrail Connection" +/* [ 68] */ "Railing Rail Connection" +/* [ 69] */ "Railing End Finish" +/* [ 70] */ "Analytical Support" +/* [ 71] */ "Analytical Link" +/* [ 72] */ "Beam Segment" +/* [ 73] */ "Column Segment" +/* [ 74] */ "Opening" +} diff --git a/ConnectorArchicad/ConnectorArchicad/Communication/Commands/Command_CreateRoom.cs b/ConnectorArchicad/ConnectorArchicad/Communication/Commands/Command_CreateRoom.cs index 4a4c53afec..fa8b82dde6 100644 --- a/ConnectorArchicad/ConnectorArchicad/Communication/Commands/Command_CreateRoom.cs +++ b/ConnectorArchicad/ConnectorArchicad/Communication/Commands/Command_CreateRoom.cs @@ -2,7 +2,6 @@ using System.Threading.Tasks; using Speckle.Core.Models; using Speckle.Newtonsoft.Json; -using Objects.BuiltElements.Archicad; namespace Archicad.Communication.Commands { @@ -12,9 +11,9 @@ sealed internal class CreateRoom : ICommand> public sealed class Parameters { [JsonProperty("zones")] - private IEnumerable Datas { get; } + private IEnumerable Datas { get; } - public Parameters(IEnumerable datas) + public Parameters(IEnumerable datas) { Datas = datas; } @@ -27,9 +26,9 @@ private sealed class Result public IEnumerable ApplicationObjects { get; private set; } } - private IEnumerable Datas { get; } + private IEnumerable Datas { get; } - public CreateRoom(IEnumerable datas) + public CreateRoom(IEnumerable datas) { Datas = datas; } diff --git a/ConnectorArchicad/ConnectorArchicad/Communication/Commands/Command_GetElementIds.cs b/ConnectorArchicad/ConnectorArchicad/Communication/Commands/Command_GetElementIds.cs index 0cc39587e7..8e38022ad4 100644 --- a/ConnectorArchicad/ConnectorArchicad/Communication/Commands/Command_GetElementIds.cs +++ b/ConnectorArchicad/ConnectorArchicad/Communication/Commands/Command_GetElementIds.cs @@ -12,7 +12,8 @@ internal sealed class GetElementIds : ICommand> public enum ElementFilter { All, - Selection + Selection, + ElementType } #region --- Classes --- @@ -26,13 +27,16 @@ public sealed class Parameters [JsonConverter(typeof(StringEnumConverter))] private ElementFilter Filter { get; } + [JsonProperty("filterBy")] + private List? FilterBy { get; } #endregion #region --- Ctor \ Dtor --- - public Parameters(ElementFilter filter) + public Parameters(ElementFilter filter, List? filterBy = null) { Filter = filter; + FilterBy = filterBy; } #endregion @@ -54,14 +58,16 @@ private sealed class Result #region --- Fields --- private ElementFilter Filter { get; } + private List? FilterBy { get; } #endregion #region --- Ctor \ Dtor --- - public GetElementIds(ElementFilter filter) + public GetElementIds(ElementFilter filter, List? filterBy = null) { Filter = filter; + FilterBy = filterBy; } #endregion @@ -70,7 +76,10 @@ public GetElementIds(ElementFilter filter) public async Task> Execute() { - Result result = await HttpCommandExecutor.Execute("GetElementIds", new Parameters(Filter)); + Result result = await HttpCommandExecutor.Execute( + "GetElementIds", + new Parameters(Filter, FilterBy) + ); return result.ApplicationIds; } diff --git a/ConnectorArchicad/ConnectorArchicad/Communication/Commands/Command_GetRoomData.cs b/ConnectorArchicad/ConnectorArchicad/Communication/Commands/Command_GetRoomData.cs index 2dada6fc28..86ed84d4a7 100644 --- a/ConnectorArchicad/ConnectorArchicad/Communication/Commands/Command_GetRoomData.cs +++ b/ConnectorArchicad/ConnectorArchicad/Communication/Commands/Command_GetRoomData.cs @@ -6,7 +6,7 @@ namespace Archicad.Communication.Commands { - sealed internal class GetRoomData : ICommand> + sealed internal class GetRoomData : ICommand> { [JsonObject(MemberSerialization.OptIn)] public sealed class Parameters @@ -24,7 +24,7 @@ public Parameters(IEnumerable applicationIds) private sealed class Result { [JsonProperty("zones")] - public IEnumerable Rooms { get; private set; } + public IEnumerable Rooms { get; private set; } } private IEnumerable ApplicationIds { get; } @@ -34,11 +34,9 @@ public GetRoomData(IEnumerable applicationIds) ApplicationIds = applicationIds; } - public async Task> Execute() + public async Task> Execute() { var result = await HttpCommandExecutor.Execute("GetRoomData", new Parameters(ApplicationIds)); - foreach (var room in result.Rooms) - room.units = Units.Meters; return result.Rooms; } diff --git a/ConnectorArchicad/ConnectorArchicad/ConnectorBinding.cs b/ConnectorArchicad/ConnectorArchicad/ConnectorBinding.cs index 2f331c6632..95182d9c0d 100644 --- a/ConnectorArchicad/ConnectorArchicad/ConnectorBinding.cs +++ b/ConnectorArchicad/ConnectorArchicad/ConnectorBinding.cs @@ -92,6 +92,34 @@ public override List GetSelectionFilters() Name = "Everything", Icon = "CubeScan", Description = "Sends all supported elements and project information." + }, + new ListSelectionFilter + { + Slug = "elementType", + Name = "Element Type", + Icon = "Category", + Values = new List + { + "Wall", + "Column", + "Beam", + "Slab", + "Roof", + "Shell", + "Stair", + "Railing", + "Curtain Wall", + "Door", + "Window", + "Skylight", + "Opening", + "Zone", + "Mesh", + "Morph", + "Object", + "Lamp" + }, + Description = "Adds all elements with the selected Element Types" } }; } diff --git a/ConnectorArchicad/ConnectorArchicad/Converters/ConverterArchicad.cs b/ConnectorArchicad/ConnectorArchicad/Converters/ConverterArchicad.cs index 3ad57e4b05..e2805c7de6 100644 --- a/ConnectorArchicad/ConnectorArchicad/Converters/ConverterArchicad.cs +++ b/ConnectorArchicad/ConnectorArchicad/Converters/ConverterArchicad.cs @@ -1,165 +1,169 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Archicad; -using Objects.Geometry; -using Speckle.Core.Kits; -using Speckle.Core.Models; -using Speckle.Core.Models.GraphTraversal; -using static Speckle.Core.Models.ApplicationObject; - -namespace Objects.Converter.Archicad -{ - public partial class ConverterArchicad : ISpeckleConverter - { - public string Description => "Default Speckle Kit for Archicad"; - public string Name => nameof(ConverterArchicad); - public string Author => "Speckle"; - public string WebsiteOrEmail => "https://speckle.systems"; - - public IEnumerable GetServicedApplications() => new string[] { "Archicad" }; - - public ConversionOptions ConversionOptions { get; set; } - - /// - /// Keeps track of the conversion process - /// - public ProgressReport Report { get; private set; } = new ProgressReport(); - - /// - /// Decides what to do when an element being received already exists - /// - public ReceiveMode ReceiveMode { get; set; } - - // send - public Base ConvertToSpeckle(object @object) - { - return null; - } - - public List ConvertToSpeckle(List objects) => objects.Select(ConvertToSpeckle).ToList(); - - public bool CanConvertToSpeckle(object @object) - { - return false; - } - - // receive - public object ConvertToNative(Base @object) - { - return null; - } - - public List ConvertToNative(List objects) => objects.Select(ConvertToNative).ToList(); - - public bool CanConvertToNativeImplemented(Base @object) - { - return @object - switch - { - // Speckle BIM elements - Objects.BuiltElements.Beam _ => true, - Objects.BuiltElements.Column _ => true, - Objects.BuiltElements.Floor _ => true, - Objects.BuiltElements.Ceiling _ => true, - Objects.BuiltElements.Roof _ => true, - Objects.BuiltElements.Room _ => true, - Objects.BuiltElements.Wall _ => true, - - // Archicad elements - Objects.BuiltElements.Archicad.ArchicadDoor _ => true, - Objects.BuiltElements.Archicad.ArchicadWindow _ => true, - Objects.BuiltElements.Archicad.ArchicadSkylight _ => true, - Objects.BuiltElements.Archicad.DirectShape _ => true, - - // Revit elements - Objects.BuiltElements.Revit.FamilyInstance _ => true, - Objects.Other.Revit.RevitInstance _ => true, - - // Other - Objects.Other.BlockInstance _ => true, - - // Speckle geomtries - Objects.Geometry.Mesh _ => true, - Objects.Geometry.Brep _ => true, - - _ => false - }; - } - - public bool CanConvertToNativeNotImplemented(Base @object) - { - return @object - switch - { - // Project info - Objects.Organization.ModelInfo _ => true, - - _ => false - }; - } - - public bool CanConvertToNative(Base @object) - { - return CanConvertToNativeImplemented(@object) || CanConvertToNativeNotImplemented(@object); - } - - /// - /// To know which other objects are being converted, in order to sort relationships between them. - /// For example, elements that have children use this to determine whether they should send their children out or not. - /// - public List ContextObjects { get; set; } = new List(); - - /// - /// To keep track of previously received objects from a given stream in here. If possible, conversions routines - /// will edit an existing object, otherwise they will delete the old one and create the new one. - /// - public List PreviousContextObjects { get; set; } = new List(); - - public void SetContextDocument(object doc) - { - } - - public void SetContextObjects(List objects) => ContextObjects = objects; - - /// - /// Removes all inherited classes from speckle type string - /// - /// - /// - public static string SimplifySpeckleType(string type) - { - return type.Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries).FirstOrDefault(); - } - - public void SetContextObjects(List flattenObjects) - { - List objects; - - foreach (var tc in flattenObjects) - { - var applicationObject = new ApplicationObject(tc.current.id, SimplifySpeckleType(tc.current.speckle_type)) - { - applicationId = tc.current.applicationId, - Convertible = true - }; - - ContextObjects.Add(applicationObject); - } - } - - public void SetPreviousContextObjects(List objects) => PreviousContextObjects = objects; - - public void SetConverterSettings(object settings) - { - } - - public ConverterArchicad(ConversionOptions conversionOptions) - { - this.ConversionOptions = conversionOptions; - - var ver = System.Reflection.Assembly.GetAssembly(typeof(ConverterArchicad)).GetName().Version; - Report.Log($"Using converter: {Name} v{ver}"); - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using Archicad; +using Objects.Geometry; +using Speckle.Core.Kits; +using Speckle.Core.Models; +using Speckle.Core.Models.GraphTraversal; +using static Speckle.Core.Models.ApplicationObject; + +namespace Objects.Converter.Archicad +{ + public partial class ConverterArchicad : ISpeckleConverter + { + public string Description => "Default Speckle Kit for Archicad"; + public string Name => nameof(ConverterArchicad); + public string Author => "Speckle"; + public string WebsiteOrEmail => "https://speckle.systems"; + + public IEnumerable GetServicedApplications() => new string[] { "Archicad" }; + + public ConversionOptions ConversionOptions { get; set; } + + /// + /// Keeps track of the conversion process + /// + public ProgressReport Report { get; private set; } = new ProgressReport(); + + /// + /// Decides what to do when an element being received already exists + /// + public ReceiveMode ReceiveMode { get; set; } + + // send + public Base ConvertToSpeckle(object @object) + { + return null; + } + + public List ConvertToSpeckle(List objects) => objects.Select(ConvertToSpeckle).ToList(); + + public bool CanConvertToSpeckle(object @object) + { + return false; + } + + // receive + public object ConvertToNative(Base @object) + { + return null; + } + + public object ConvertToNativeDisplayable(Base @object) + { + throw new NotImplementedException(); + } + + public List ConvertToNative(List objects) => objects.Select(ConvertToNative).ToList(); + + public bool CanConvertToNativeImplemented(Base @object) + { + return @object switch + { + // Speckle BIM elements + Objects.BuiltElements.Beam _ => true, + Objects.BuiltElements.Column _ => true, + Objects.BuiltElements.Floor _ => true, + Objects.BuiltElements.Ceiling _ => true, + Objects.BuiltElements.Roof _ => true, + Objects.BuiltElements.Room _ => true, + Objects.BuiltElements.Wall _ => true, + + // Archicad elements + Objects.BuiltElements.Archicad.ArchicadDoor _ => true, + Objects.BuiltElements.Archicad.ArchicadWindow _ => true, + Objects.BuiltElements.Archicad.ArchicadSkylight _ => true, + Objects.BuiltElements.Archicad.DirectShape _ => true, + + // Revit elements + Objects.BuiltElements.Revit.FamilyInstance _ => true, + Objects.Other.Revit.RevitInstance _ => true, + + // Other + Objects.Other.BlockInstance _ => true, + + // Speckle geomtries + Objects.Geometry.Mesh _ => true, + Objects.Geometry.Brep _ => true, + + _ => false + }; + } + + public bool CanConvertToNativeNotImplemented(Base @object) + { + return @object switch + { + // Project info + Objects.Organization.ModelInfo _ => true, + + _ => false + }; + } + + public bool CanConvertToNative(Base @object) + { + return CanConvertToNativeImplemented(@object) || CanConvertToNativeNotImplemented(@object); + } + + public bool CanConvertToNativeDisplayable(Base @object) + { + return false; + } + + /// + /// To know which other objects are being converted, in order to sort relationships between them. + /// For example, elements that have children use this to determine whether they should send their children out or not. + /// + public List ContextObjects { get; set; } = new List(); + + /// + /// To keep track of previously received objects from a given stream in here. If possible, conversions routines + /// will edit an existing object, otherwise they will delete the old one and create the new one. + /// + public List PreviousContextObjects { get; set; } = new List(); + + public void SetContextDocument(object doc) { } + + public void SetContextObjects(List objects) => ContextObjects = objects; + + /// + /// Removes all inherited classes from speckle type string + /// + /// + /// + public static string SimplifySpeckleType(string type) + { + return type.Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries).FirstOrDefault(); + } + + public void SetContextObjects(List flattenObjects) + { + List objects; + + foreach (var tc in flattenObjects) + { + var applicationObject = new ApplicationObject(tc.current.id, SimplifySpeckleType(tc.current.speckle_type)) + { + applicationId = tc.current.applicationId, + Convertible = true + }; + + ContextObjects.Add(applicationObject); + } + } + + public void SetPreviousContextObjects(List objects) => PreviousContextObjects = objects; + + public void SetConverterSettings(object settings) { } + + public ConverterArchicad(ConversionOptions conversionOptions) + { + this.ConversionOptions = conversionOptions; + + var ver = System.Reflection.Assembly.GetAssembly(typeof(ConverterArchicad)).GetName().Version; + Report.Log($"Using converter: {Name} v{ver}"); + } + } +} diff --git a/ConnectorArchicad/ConnectorArchicad/Converters/Converters/ColumnConverter.cs b/ConnectorArchicad/ConnectorArchicad/Converters/Converters/ColumnConverter.cs index c55810827f..a278f51ab9 100644 --- a/ConnectorArchicad/ConnectorArchicad/Converters/Converters/ColumnConverter.cs +++ b/ConnectorArchicad/ConnectorArchicad/Converters/Converters/ColumnConverter.cs @@ -72,7 +72,7 @@ await AsyncCommandProcessor.Execute( column.displayValue = Operations.ModelConverter.MeshesToSpeckle(elementModels.First(e => e.applicationId == column.applicationId) .model); - //column.baseLine = ... + column.baseLine = new Line(column.origoPos, column.origoPos + new Point (0, 0, column.height)); columns.Add(column); } diff --git a/ConnectorArchicad/ConnectorArchicad/Converters/Converters/RoomConverter.cs b/ConnectorArchicad/ConnectorArchicad/Converters/Converters/RoomConverter.cs index baa96b96a6..d460808e05 100644 --- a/ConnectorArchicad/ConnectorArchicad/Converters/Converters/RoomConverter.cs +++ b/ConnectorArchicad/ConnectorArchicad/Converters/Converters/RoomConverter.cs @@ -5,9 +5,12 @@ using System.Threading.Tasks; using Archicad.Communication; using Archicad.Model; +using DynamicData; using Objects; +using Objects.BuiltElements; using Objects.BuiltElements.Archicad; using Objects.Geometry; +using Speckle.Core.Kits; using Speckle.Core.Models; using Speckle.Core.Models.GraphTraversal; @@ -15,14 +18,19 @@ namespace Archicad.Converters { public sealed class Room : IConverter { - public Type Type => typeof(Objects.BuiltElements.Room); + public Type Type => typeof(Archicad.Room); - public async Task> ConvertToArchicad(IEnumerable elements, CancellationToken token) + public async Task> ConvertToArchicad( + IEnumerable elements, + CancellationToken token + ) { - var rooms = new List(); + var rooms = new List(); var context = Archicad.Helpers.Timer.Context.Peek; - using (context?.cumulativeTimer?.Begin(ConnectorArchicad.Properties.OperationNameTemplates.ConvertToNative, Type.Name)) + using ( + context?.cumulativeTimer?.Begin(ConnectorArchicad.Properties.OperationNameTemplates.ConvertToNative, Type.Name) + ) { foreach (var tc in elements) { @@ -30,22 +38,53 @@ public async Task> ConvertToArchicad(IEnumerable> ConvertToArchicad(IEnumerable() : result.ToList(); } - public async Task> ConvertToSpeckle(IEnumerable elements, - CancellationToken token) + public async Task> ConvertToSpeckle( + IEnumerable elements, + CancellationToken token + ) { var elementModels = elements as ElementModelData[] ?? elements.ToArray(); - IEnumerable data = - await AsyncCommandProcessor.Execute( - new Communication.Commands.GetRoomData(elementModels.Select(e => e.applicationId)), - token); + IEnumerable data = await AsyncCommandProcessor.Execute( + new Communication.Commands.GetRoomData(elementModels.Select(e => e.applicationId)), + token + ); if (data is null) { return new List(); } List rooms = new List(); - foreach (Objects.BuiltElements.Archicad.ArchicadRoom room in data) + foreach (Archicad.Room archicadRoom in data) { - room.displayValue = - Operations.ModelConverter.MeshesToSpeckle(elementModels.First(e => e.applicationId == room.applicationId) - .model); - room.outline = Utils.PolycurveToSpeckle(room.shape.contourPolyline); - if (room.shape.holePolylines?.Count > 0) - room.voids = new List(room.shape.holePolylines.Select(Utils.PolycurveToSpeckle)); - rooms.Add(room); + Objects.BuiltElements.Archicad.ArchicadRoom speckleRoom = new Objects.BuiltElements.Archicad.ArchicadRoom(); + + // convert from Archicad to Speckle data structure + // Speckle base properties + speckleRoom.id = archicadRoom.id; + speckleRoom.applicationId = archicadRoom.applicationId; + speckleRoom.displayValue = Operations.ModelConverter.MeshesToSpeckle( + elementModels.First(e => e.applicationId == archicadRoom.applicationId).model + ); + speckleRoom.units = Units.Meters; + + // Archicad properties + speckleRoom.elementType = archicadRoom.elementType; + speckleRoom.classifications = archicadRoom.classifications; + speckleRoom.level = archicadRoom.level; + speckleRoom.height = archicadRoom.height ?? .0; + speckleRoom.shape = archicadRoom.shape; + + // downdgrade + speckleRoom.name = archicadRoom.name; + speckleRoom.number = archicadRoom.number; + speckleRoom.area = archicadRoom.area ?? .0; + speckleRoom.volume = archicadRoom.volume ?? .0; + + ElementShape.Polyline polyLine = archicadRoom.shape.contourPolyline; + Polycurve polycurve = Utils.PolycurveToSpeckle(polyLine); + speckleRoom.outline = polycurve; + if (archicadRoom.shape.holePolylines?.Count > 0) + speckleRoom.voids = new List(archicadRoom.shape.holePolylines.Select(Utils.PolycurveToSpeckle)); + + // calculate base point + speckleRoom.basePoint = archicadRoom.basePoint; + + rooms.Add(speckleRoom); } return rooms; diff --git a/ConnectorArchicad/ConnectorArchicad/Converters/ElementConverterManager.cs b/ConnectorArchicad/ConnectorArchicad/Converters/ElementConverterManager.cs index bf3f65411a..555756feac 100644 --- a/ConnectorArchicad/ConnectorArchicad/Converters/ElementConverterManager.cs +++ b/ConnectorArchicad/ConnectorArchicad/Converters/ElementConverterManager.cs @@ -60,6 +60,18 @@ private ElementConverterManager() elementIds = AsyncCommandProcessor .Execute(new Communication.Commands.GetElementIds(Communication.Commands.GetElementIds.ElementFilter.All)) ?.Result; + else if (filter.Slug == "elementType") + { + var elementTypes = filter.Summary.Split(",").Select(elementType => elementType.Trim()).ToList(); + elementIds = AsyncCommandProcessor + .Execute( + new Communication.Commands.GetElementIds( + Communication.Commands.GetElementIds.ElementFilter.ElementType, + elementTypes + ) + ) + ?.Result; + } SelectedObjects = await GetElementsType(elementIds, progress.CancellationToken); // Gets all selected objects SelectedObjects = SortSelectedObjects(); @@ -154,8 +166,8 @@ bool forReceive return Converters[typeof(Floor)]; if (elementType.IsSubclassOf(typeof(Roof))) return Converters[typeof(Roof)]; - if (elementType.IsSubclassOf(typeof(Objects.BuiltElements.Room))) - return Converters[typeof(Objects.BuiltElements.Room)]; + if (elementType.IsAssignableFrom(typeof(Objects.BuiltElements.Room))) + return Converters[typeof(Archicad.Room)]; return forReceive ? DefaultConverterForReceive : DefaultConverterForSend; } @@ -212,7 +224,14 @@ CancellationToken token { var subElementsAsBases = new List(); - if (convertedObject is not (Objects.BuiltElements.Archicad.ArchicadWall or Objects.BuiltElements.Archicad.ArchicadRoof or Objects.BuiltElements.Archicad.ArchicadShell)) + if ( + convertedObject + is not ( + Objects.BuiltElements.Archicad.ArchicadWall + or Objects.BuiltElements.Archicad.ArchicadRoof + or Objects.BuiltElements.Archicad.ArchicadShell + ) + ) return subElementsAsBases; var subElements = await GetAllSubElements(convertedObject.applicationId); diff --git a/ConnectorArchicad/ConnectorArchicad/Converters/ElementTypeProvider.cs b/ConnectorArchicad/ConnectorArchicad/Converters/ElementTypeProvider.cs index 22da5abf45..91597861b5 100644 --- a/ConnectorArchicad/ConnectorArchicad/Converters/ElementTypeProvider.cs +++ b/ConnectorArchicad/ConnectorArchicad/Converters/ElementTypeProvider.cs @@ -6,7 +6,7 @@ using Door = Objects.BuiltElements.Archicad.ArchicadDoor; using Floor = Objects.BuiltElements.Archicad.ArchicadFloor; using Roof = Objects.BuiltElements.Archicad.ArchicadRoof; -using Room = Objects.BuiltElements.Archicad.ArchicadRoom; +using Room = Archicad.Room; using Shell = Objects.BuiltElements.Archicad.ArchicadShell; using Wall = Objects.BuiltElements.Archicad.ArchicadWall; using Window = Objects.BuiltElements.Archicad.ArchicadWindow; @@ -16,19 +16,20 @@ namespace Archicad { public static class ElementTypeProvider { - private static Dictionary _nameToType = new() { - { "Wall", typeof(Wall) }, - { "Slab", typeof(Floor) }, - { "Roof", typeof(Roof) }, - { "Shell", typeof(Shell) }, - { "Zone", typeof(Room) }, - { "Beam", typeof(Beam) }, - { "Column", typeof(Column) }, - { "Door", typeof(Door) }, - { "Window", typeof(Window) }, - { "Skylight", typeof(Skylight) } - - }; + private static Dictionary _nameToType = + new() + { + { "Wall", typeof(Wall) }, + { "Slab", typeof(Floor) }, + { "Roof", typeof(Roof) }, + { "Shell", typeof(Shell) }, + { "Zone", typeof(Room) }, + { "Beam", typeof(Beam) }, + { "Column", typeof(Column) }, + { "Door", typeof(Door) }, + { "Window", typeof(Window) }, + { "Skylight", typeof(Skylight) } + }; public static Type GetTypeByName(string name) { diff --git a/ConnectorArchicad/ConnectorArchicad/Elements/Room.cs b/ConnectorArchicad/ConnectorArchicad/Elements/Room.cs new file mode 100644 index 0000000000..ad2ccbaebf --- /dev/null +++ b/ConnectorArchicad/ConnectorArchicad/Elements/Room.cs @@ -0,0 +1,43 @@ +using Objects.Geometry; +using System.Collections.Generic; +using Objects.BuiltElements.Archicad; + +namespace Archicad +{ + public class Room + { + // Speckle-specific properties + // Base + public string? id { get; set; } + public string? applicationId { get; set; } + + // General + public string? name { get; set; } + public string? number { get; set; } + + public double? area { get; set; } + public double? volume { get; set; } + + // Helper + public Point? basePoint { get; set; } // Archicad geometry kernel needed for calculation + + // Archicad API properties + // Element base + public string? elementType { get; set; } + public List? classifications { get; set; } + public ArchicadLevel? level { get; set; } + + // Room + public double? height { get; set; } + + public ElementShape? shape { get; set; } + + public Room() { } + + public Room(string id, string applicationId) + { + this.id = id; + this.applicationId = applicationId; + } + } +} diff --git a/ConnectorAutocadCivil/ConnectorAdvanceSteel.slnf b/ConnectorAutocadCivil/ConnectorAdvanceSteel.slnf index 1f800a7fa8..365a290f91 100644 --- a/ConnectorAutocadCivil/ConnectorAdvanceSteel.slnf +++ b/ConnectorAutocadCivil/ConnectorAdvanceSteel.slnf @@ -4,7 +4,9 @@ "projects": [ "ConnectorAutocadCivil\\AdvanceSteelAddinRegistrator\\AdvanceSteelAddinRegistrator.csproj", "ConnectorAutocadCivil\\ConnectorAdvanceSteel2023\\ConnectorAdvanceSteel2023.csproj", + "ConnectorAutocadCivil\\ConnectorAdvanceSteel2024\\ConnectorAdvanceSteel2024.csproj", "ConnectorAutocadCivil\\ConnectorAutocad2023\\ConnectorAutocad2023.csproj", + "ConnectorAutocadCivil\\ConnectorAutocad2024\\ConnectorAutocad2024.csproj", "ConnectorAutocadCivil\\ConnectorAutocadCivil\\ConnectorAutocadCivilShared.shproj", "Core\\Core\\Core.csproj", "Core\\Tests\\TestsUnit.csproj", @@ -12,7 +14,9 @@ "DesktopUI2\\AvaloniaHwndHost\\AvaloniaHwndHost.csproj", "DesktopUI2\\DesktopUI2\\DesktopUI2.csproj", "Objects\\Converters\\ConverterAutocadCivil\\ConverterAdvanceSteel2023\\ConverterAdvanceSteel2023.csproj", + "Objects\\Converters\\ConverterAutocadCivil\\ConverterAdvanceSteel2024\\ConverterAdvanceSteel2024.csproj", "Objects\\Converters\\ConverterAutocadCivil\\ConverterAutocad2023\\ConverterAutocad2023.csproj", + "Objects\\Converters\\ConverterAutocadCivil\\ConverterAutocad2024\\ConverterAutocad2024.csproj", "Objects\\Converters\\ConverterAutocadCivil\\ConverterAutocadCivilShared\\ConverterAutocadCivilShared.shproj", "Objects\\Objects\\Objects.csproj", "Objects\\Tests\\Tests.csproj" diff --git a/ConnectorAutocadCivil/ConnectorAdvanceSteel2023/ConnectorAdvanceSteel2023.csproj b/ConnectorAutocadCivil/ConnectorAdvanceSteel2023/ConnectorAdvanceSteel2023.csproj index a36819fcaf..f7b63a6de1 100644 --- a/ConnectorAutocadCivil/ConnectorAdvanceSteel2023/ConnectorAdvanceSteel2023.csproj +++ b/ConnectorAutocadCivil/ConnectorAdvanceSteel2023/ConnectorAdvanceSteel2023.csproj @@ -6,7 +6,7 @@ ConnectorAdvanceSteel2023 ConnectorAdvanceSteel2023 x64 - $(DefineConstants);ADVANCESTEEL2023 + $(DefineConstants);ADVANCESTEEL;ADVANCESTEEL2023 false Pedro Henrique Liberato diff --git a/ConnectorAutocadCivil/ConnectorAdvanceSteel2024/ConnectorAdvanceSteel2024.csproj b/ConnectorAutocadCivil/ConnectorAdvanceSteel2024/ConnectorAdvanceSteel2024.csproj new file mode 100644 index 0000000000..898f68ee64 --- /dev/null +++ b/ConnectorAutocadCivil/ConnectorAdvanceSteel2024/ConnectorAdvanceSteel2024.csproj @@ -0,0 +1,64 @@ + + + Speckle.ConnectorAdvanceSteel + SpeckleConnectorAdvanceSteel + net48 + ConnectorAdvanceSteel2024 + ConnectorAdvanceSteel2024 + x64 + $(DefineConstants);ADVANCESTEEL;ADVANCESTEEL2024 + false + Pedro Henrique Liberato + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ConnectorAutocadCivil/ConnectorAdvanceSteel2024/MyAddons.xml b/ConnectorAutocadCivil/ConnectorAdvanceSteel2024/MyAddons.xml new file mode 100644 index 0000000000..d4af375593 --- /dev/null +++ b/ConnectorAutocadCivil/ConnectorAdvanceSteel2024/MyAddons.xml @@ -0,0 +1,9 @@ + + + + + Speckle + $PATH$ + + + \ No newline at end of file diff --git a/ConnectorAutocadCivil/ConnectorAdvanceSteel2024/Properties/launchSettings.json b/ConnectorAutocadCivil/ConnectorAdvanceSteel2024/Properties/launchSettings.json new file mode 100644 index 0000000000..74a79ccc0e --- /dev/null +++ b/ConnectorAutocadCivil/ConnectorAdvanceSteel2024/Properties/launchSettings.json @@ -0,0 +1,9 @@ +{ + "profiles": { + "Debug AdvanceSteel": { + "commandName": "Executable", + "executablePath": "C:\\Program Files\\Autodesk\\AutoCAD 2024\\acad.exe", + "commandLineArgs": "/language \"en-US\" /product \"ADVS\" /p \"<>\"" + } + } +} \ No newline at end of file diff --git a/ConnectorAutocadCivil/ConnectorAutocadCivil.sln b/ConnectorAutocadCivil/ConnectorAutocadCivil.sln index d19e92cfdd..e122c2b1bd 100644 --- a/ConnectorAutocadCivil/ConnectorAutocadCivil.sln +++ b/ConnectorAutocadCivil/ConnectorAutocadCivil.sln @@ -53,13 +53,17 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConnectorAdvanceSteel2023", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AdvanceSteelAddinRegistrator", "AdvanceSteelAddinRegistrator\AdvanceSteelAddinRegistrator.csproj", "{4A9E92FD-CFF2-494F-AFA8-6AA2FE8BA761}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConverterCivil2024", "..\Objects\Converters\ConverterAutocadCivil\ConverterCivil2024\ConverterCivil2024.csproj", "{5484584C-9B0B-4313-B25E-E52D8D3741CA}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConverterCivil2024", "..\Objects\Converters\ConverterAutocadCivil\ConverterCivil2024\ConverterCivil2024.csproj", "{5484584C-9B0B-4313-B25E-E52D8D3741CA}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConverterAutocad2024", "..\Objects\Converters\ConverterAutocadCivil\ConverterAutocad2024\ConverterAutocad2024.csproj", "{79E41261-3078-4F38-A71E-4B4020CF8F57}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConverterAutocad2024", "..\Objects\Converters\ConverterAutocadCivil\ConverterAutocad2024\ConverterAutocad2024.csproj", "{79E41261-3078-4F38-A71E-4B4020CF8F57}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConnectorAutocad2024", "ConnectorAutocad2024\ConnectorAutocad2024.csproj", "{54779824-3887-458D-A241-684518D77464}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConnectorAutocad2024", "ConnectorAutocad2024\ConnectorAutocad2024.csproj", "{54779824-3887-458D-A241-684518D77464}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConnectorCivil2024", "ConnectorCivil2024\ConnectorCivil2024.csproj", "{23478901-5C29-4D54-A66A-AE9FEA6118C7}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConnectorCivil2024", "ConnectorCivil2024\ConnectorCivil2024.csproj", "{23478901-5C29-4D54-A66A-AE9FEA6118C7}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConverterAdvanceSteel2024", "..\Objects\Converters\ConverterAutocadCivil\ConverterAdvanceSteel2024\ConverterAdvanceSteel2024.csproj", "{C7F4DFA0-18FE-4C0B-A9DC-DAD74D5E6E13}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConnectorAdvanceSteel2024", "ConnectorAdvanceSteel2024\ConnectorAdvanceSteel2024.csproj", "{572B1D77-83BA-4E96-A70A-7D000D2AC220}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -155,6 +159,14 @@ Global {23478901-5C29-4D54-A66A-AE9FEA6118C7}.Debug|Any CPU.Build.0 = Debug|Any CPU {23478901-5C29-4D54-A66A-AE9FEA6118C7}.Release|Any CPU.ActiveCfg = Release|Any CPU {23478901-5C29-4D54-A66A-AE9FEA6118C7}.Release|Any CPU.Build.0 = Release|Any CPU + {C7F4DFA0-18FE-4C0B-A9DC-DAD74D5E6E13}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C7F4DFA0-18FE-4C0B-A9DC-DAD74D5E6E13}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C7F4DFA0-18FE-4C0B-A9DC-DAD74D5E6E13}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C7F4DFA0-18FE-4C0B-A9DC-DAD74D5E6E13}.Release|Any CPU.Build.0 = Release|Any CPU + {572B1D77-83BA-4E96-A70A-7D000D2AC220}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {572B1D77-83BA-4E96-A70A-7D000D2AC220}.Debug|Any CPU.Build.0 = Debug|Any CPU + {572B1D77-83BA-4E96-A70A-7D000D2AC220}.Release|Any CPU.ActiveCfg = Release|Any CPU + {572B1D77-83BA-4E96-A70A-7D000D2AC220}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -182,23 +194,31 @@ Global {79E41261-3078-4F38-A71E-4B4020CF8F57} = {8AFDB1E6-D3E7-45EA-BCDC-F3554BDDEF1E} {54779824-3887-458D-A241-684518D77464} = {A07071D5-E197-487D-B543-28639AC3C719} {23478901-5C29-4D54-A66A-AE9FEA6118C7} = {A07071D5-E197-487D-B543-28639AC3C719} + {C7F4DFA0-18FE-4C0B-A9DC-DAD74D5E6E13} = {8AFDB1E6-D3E7-45EA-BCDC-F3554BDDEF1E} + {572B1D77-83BA-4E96-A70A-7D000D2AC220} = {A07071D5-E197-487D-B543-28639AC3C719} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {EAFBA51C-7222-435F-8BDF-8C15B27A34C8} EndGlobalSection GlobalSection(SharedMSBuildProjectFiles) = preSolution ..\Objects\Converters\ConverterAutocadCivil\ConverterAutocadCivilShared\ConverterAutocadCivilShared.projitems*{0a736319-40f8-4a30-868a-69e856a849e3}*SharedItemsImports = 5 + ConnectorAutocadCivil\ConnectorAutocadCivilShared.projitems*{23478901-5c29-4d54-a66a-ae9fea6118c7}*SharedItemsImports = 5 ConnectorAutocadCivil\ConnectorAutocadCivilShared.projitems*{2639e37d-80d3-415a-b4d1-20d7f321f27f}*SharedItemsImports = 5 ..\Objects\Converters\ConverterAutocadCivil\ConverterAutocadCivilShared\ConverterAutocadCivilShared.projitems*{2d0f9f8a-2e89-4780-978a-cd92d6d7b843}*SharedItemsImports = 13 ..\Objects\Converters\ConverterAutocadCivil\ConverterAutocadCivilShared\ConverterAutocadCivilShared.projitems*{361b45eb-dc3f-42bb-93ee-21f31a05fa68}*SharedItemsImports = 5 ConnectorAutocadCivil\ConnectorAutocadCivilShared.projitems*{464f2220-d7d9-4d8c-bb3d-b93a1c603469}*SharedItemsImports = 5 + ConnectorAutocadCivil\ConnectorAutocadCivilShared.projitems*{54779824-3887-458d-a241-684518d77464}*SharedItemsImports = 5 + ..\Objects\Converters\ConverterAutocadCivil\ConverterAutocadCivilShared\ConverterAutocadCivilShared.projitems*{5484584c-9b0b-4313-b25e-e52d8d3741ca}*SharedItemsImports = 5 + ConnectorAutocadCivil\ConnectorAutocadCivilShared.projitems*{572b1d77-83ba-4e96-a70a-7d000d2ac220}*SharedItemsImports = 5 ..\Objects\Converters\ConverterAutocadCivil\ConverterAutocadCivilShared\ConverterAutocadCivilShared.projitems*{575293fb-158c-4f91-abc1-18b60f310b32}*SharedItemsImports = 5 ..\Objects\Converters\ConverterAutocadCivil\ConverterAutocadCivilShared\ConverterAutocadCivilShared.projitems*{57a28a09-c64e-47f6-b602-b6d58b19f1d5}*SharedItemsImports = 5 ConnectorAutocadCivil\ConnectorAutocadCivilShared.projitems*{58a88f1a-7489-46d2-949d-2fc3f68c8d84}*SharedItemsImports = 5 + ..\Objects\Converters\ConverterAutocadCivil\ConverterAutocadCivilShared\ConverterAutocadCivilShared.projitems*{79e41261-3078-4f38-a71e-4b4020cf8f57}*SharedItemsImports = 5 ..\Objects\Converters\ConverterAutocadCivil\ConverterAutocadCivilShared\ConverterAutocadCivilShared.projitems*{8cfc7609-f640-4683-bf13-fe144d3dc50b}*SharedItemsImports = 5 ..\Objects\Converters\ConverterAutocadCivil\ConverterAutocadCivilShared\ConverterAutocadCivilShared.projitems*{9a11c16b-790f-437a-9e04-7e0d3ecdc06b}*SharedItemsImports = 5 ConnectorAutocadCivil\ConnectorAutocadCivilShared.projitems*{ad10c167-937f-4706-9eb5-c99f86c35e8f}*SharedItemsImports = 5 ConnectorAutocadCivil\ConnectorAutocadCivilShared.projitems*{b47492d9-2eda-4016-a930-7fa708c85c3d}*SharedItemsImports = 13 + ..\Objects\Converters\ConverterAutocadCivil\ConverterAutocadCivilShared\ConverterAutocadCivilShared.projitems*{c7f4dfa0-18fe-4c0b-a9dc-dad74d5e6e13}*SharedItemsImports = 5 ..\Objects\Converters\ConverterAutocadCivil\ConverterAutocadCivilShared\ConverterAutocadCivilShared.projitems*{d6731414-a695-4f89-aea1-ae2141a1dac1}*SharedItemsImports = 5 ConnectorAutocadCivil\ConnectorAutocadCivilShared.projitems*{f4aa033f-4f85-4990-afe9-86be00abe973}*SharedItemsImports = 5 ConnectorAutocadCivil\ConnectorAutocadCivilShared.projitems*{f4bfb155-7ba9-4e46-8240-9c825060c904}*SharedItemsImports = 5 diff --git a/ConnectorAutocadCivil/ConnectorAutocadCivil.slnf b/ConnectorAutocadCivil/ConnectorAutocadCivil.slnf index 38e498ad3c..72900fe54e 100644 --- a/ConnectorAutocadCivil/ConnectorAutocadCivil.slnf +++ b/ConnectorAutocadCivil/ConnectorAutocadCivil.slnf @@ -5,10 +5,12 @@ "ConnectorAutocadCivil\\ConnectorAutocad2021\\ConnectorAutocad2021.csproj", "ConnectorAutocadCivil\\ConnectorAutocad2022\\ConnectorAutocad2022.csproj", "ConnectorAutocadCivil\\ConnectorAutocad2023\\ConnectorAutocad2023.csproj", + "ConnectorAutocadCivil\\ConnectorAutocad2024\\ConnectorAutocad2024.csproj", "ConnectorAutocadCivil\\ConnectorAutocadCivil\\ConnectorAutocadCivilShared.shproj", "ConnectorAutocadCivil\\ConnectorCivil2021\\ConnectorCivil2021.csproj", "ConnectorAutocadCivil\\ConnectorCivil2022\\ConnectorCivil2022.csproj", "ConnectorAutocadCivil\\ConnectorCivil2023\\ConnectorCivil2023.csproj", + "ConnectorAutocadCivil\\ConnectorCivil2024\\ConnectorCivil2024.csproj", "Core\\Core\\Core.csproj", "Core\\Tests\\TestsUnit.csproj", "Core\\Transports\\DiskTransport\\DiskTransport.csproj", @@ -17,10 +19,12 @@ "Objects\\Converters\\ConverterAutocadCivil\\ConverterAutocad2021\\ConverterAutocad2021.csproj", "Objects\\Converters\\ConverterAutocadCivil\\ConverterAutocad2022\\ConverterAutocad2022.csproj", "Objects\\Converters\\ConverterAutocadCivil\\ConverterAutocad2023\\ConverterAutocad2023.csproj", + "Objects\\Converters\\ConverterAutocadCivil\\ConverterAutocad2024\\ConverterAutocad2024.csproj", "Objects\\Converters\\ConverterAutocadCivil\\ConverterAutocadCivilShared\\ConverterAutocadCivilShared.shproj", "Objects\\Converters\\ConverterAutocadCivil\\ConverterCivil2021\\ConverterCivil2021.csproj", "Objects\\Converters\\ConverterAutocadCivil\\ConverterCivil2022\\ConverterCivil2022.csproj", "Objects\\Converters\\ConverterAutocadCivil\\ConverterCivil2023\\ConverterCivil2023.csproj", + "Objects\\Converters\\ConverterAutocadCivil\\ConverterCivil2024\\ConverterCivil2024.csproj", "Objects\\Objects\\Objects.csproj", "Objects\\Tests\\Tests.csproj" ] diff --git a/ConnectorAutocadCivil/ConnectorAutocadCivil/DocumentUtils/TransactionContext.cs b/ConnectorAutocadCivil/ConnectorAutocadCivil/DocumentUtils/TransactionContext.cs index 773b30bb17..3677f82e52 100644 --- a/ConnectorAutocadCivil/ConnectorAutocadCivil/DocumentUtils/TransactionContext.cs +++ b/ConnectorAutocadCivil/ConnectorAutocadCivil/DocumentUtils/TransactionContext.cs @@ -1,10 +1,10 @@ -using System; +using System; using System.Collections.Generic; using System.Text; using Autodesk.AutoCAD.ApplicationServices; using Document = Autodesk.AutoCAD.ApplicationServices.Document; -#if ADVANCESTEEL2023 +#if ADVANCESTEEL using Autodesk.AdvanceSteel.DocumentManagement; #else using Autodesk.AutoCAD.DatabaseServices; @@ -12,7 +12,7 @@ namespace Speckle.ConnectorAutocadCivil.DocumentUtils { -#if ADVANCESTEEL2023 +#if ADVANCESTEEL public class TransactionContext : IDisposable { private bool DocumentLocked = false; diff --git a/ConnectorAutocadCivil/ConnectorAutocadCivil/Entry/App.cs b/ConnectorAutocadCivil/ConnectorAutocadCivil/Entry/App.cs index 2b40cb8bd0..432fe3ce29 100644 --- a/ConnectorAutocadCivil/ConnectorAutocadCivil/Entry/App.cs +++ b/ConnectorAutocadCivil/ConnectorAutocadCivil/Entry/App.cs @@ -11,13 +11,13 @@ using Speckle.Core.Logging; using Forms = System.Windows.Forms; -#if ADVANCESTEEL2023 +#if ADVANCESTEEL using Autodesk.AdvanceSteel.Runtime; #else using Autodesk.AutoCAD.Runtime; #endif -#if ADVANCESTEEL2023 +#if ADVANCESTEEL [assembly: ExtensionApplication(typeof(Speckle.ConnectorAutocadCivil.Entry.App))] #endif @@ -34,7 +34,7 @@ public void Initialize() { //Advance Steel addon is initialized after ribbon creation bool advanceSteel = false; -#if ADVANCESTEEL2023 +#if ADVANCESTEEL advanceSteel = true; #endif @@ -150,7 +150,7 @@ private RibbonTab FindOrMakeTab(string name) ribbon.Tabs.Add(tab); } -#if !ADVANCESTEEL2023 +#if !ADVANCESTEEL tab.IsActive = true; // optional debug: set ribbon tab active #endif return tab; diff --git a/ConnectorAutocadCivil/ConnectorAutocadCivil/Entry/SpeckleAutocadCommand.cs b/ConnectorAutocadCivil/ConnectorAutocadCivil/Entry/SpeckleAutocadCommand.cs index 86f3493777..9bb9b077d9 100644 --- a/ConnectorAutocadCivil/ConnectorAutocadCivil/Entry/SpeckleAutocadCommand.cs +++ b/ConnectorAutocadCivil/ConnectorAutocadCivil/Entry/SpeckleAutocadCommand.cs @@ -1,11 +1,12 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; using System.Runtime.InteropServices; using System.Threading; using Autodesk.AutoCAD.ApplicationServices; -#if ADVANCESTEEL2023 +#if ADVANCESTEEL using Autodesk.AdvanceSteel.Runtime; #else using Autodesk.AutoCAD.Runtime; @@ -22,7 +23,7 @@ using DesktopUI2.Views; using Speckle.ConnectorAutocadCivil.UI; -#if ADVANCESTEEL2023 +#if ADVANCESTEEL [assembly: CommandClass(typeof(Speckle.ConnectorAutocadCivil.Entry.SpeckleAutocadCommand))] #endif @@ -32,6 +33,7 @@ public class SpeckleAutocadCommand { #region Avalonia parent window [DllImport("user32.dll", SetLastError = true)] + [SuppressMessage("Security", "CA5392:Use DefaultDllImportSearchPaths attribute for P/Invokes")] static extern IntPtr SetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr value); const int GWL_HWNDPARENT = -8; #endregion diff --git a/ConnectorAutocadCivil/ConnectorAutocadCivil/UI/ConnectorBindingsAutocadCivil.Events.cs b/ConnectorAutocadCivil/ConnectorAutocadCivil/UI/ConnectorBindingsAutocadCivil.Events.cs index dc26a65dd1..2c149e17d8 100644 --- a/ConnectorAutocadCivil/ConnectorAutocadCivil/UI/ConnectorBindingsAutocadCivil.Events.cs +++ b/ConnectorAutocadCivil/ConnectorAutocadCivil/UI/ConnectorBindingsAutocadCivil.Events.cs @@ -4,7 +4,7 @@ using DesktopUI2.ViewModels; using Speckle.ConnectorAutocadCivil.Entry; -#if ADVANCESTEEL2023 +#if ADVANCESTEEL using ASFilerObject = Autodesk.AdvanceSteel.CADAccess.FilerObject; #endif diff --git a/ConnectorAutocadCivil/ConnectorAutocadCivil/UI/ConnectorBindingsAutocadCivil.Previews.cs b/ConnectorAutocadCivil/ConnectorAutocadCivil/UI/ConnectorBindingsAutocadCivil.Previews.cs index a742e0027e..c7222de601 100644 --- a/ConnectorAutocadCivil/ConnectorAutocadCivil/UI/ConnectorBindingsAutocadCivil.Previews.cs +++ b/ConnectorAutocadCivil/ConnectorAutocadCivil/UI/ConnectorBindingsAutocadCivil.Previews.cs @@ -8,7 +8,7 @@ using Speckle.Core.Kits; using Speckle.Core.Models; -#if ADVANCESTEEL2023 +#if ADVANCESTEEL using ASFilerObject = Autodesk.AdvanceSteel.CADAccess.FilerObject; #endif @@ -62,7 +62,7 @@ public override async void PreviewSend(StreamState state, ProgressViewModel prog } else { -#if ADVANCESTEEL2023 +#if ADVANCESTEEL UpdateASObject(appObj, obj); #endif appObj.Update( diff --git a/ConnectorAutocadCivil/ConnectorAutocadCivil/UI/ConnectorBindingsAutocadCivil.Receive.cs b/ConnectorAutocadCivil/ConnectorAutocadCivil/UI/ConnectorBindingsAutocadCivil.Receive.cs index 048d97c5a3..0c16fb83a8 100644 --- a/ConnectorAutocadCivil/ConnectorAutocadCivil/UI/ConnectorBindingsAutocadCivil.Receive.cs +++ b/ConnectorAutocadCivil/ConnectorAutocadCivil/UI/ConnectorBindingsAutocadCivil.Receive.cs @@ -17,7 +17,7 @@ using Speckle.Core.Models.GraphTraversal; using static Speckle.ConnectorAutocadCivil.Utils; -#if ADVANCESTEEL2023 +#if ADVANCESTEEL using ASFilerObject = Autodesk.AdvanceSteel.CADAccess.FilerObject; #endif diff --git a/ConnectorAutocadCivil/ConnectorAutocadCivil/UI/ConnectorBindingsAutocadCivil.Selection.cs b/ConnectorAutocadCivil/ConnectorAutocadCivil/UI/ConnectorBindingsAutocadCivil.Selection.cs index fde1384bce..d28057ef05 100644 --- a/ConnectorAutocadCivil/ConnectorAutocadCivil/UI/ConnectorBindingsAutocadCivil.Selection.cs +++ b/ConnectorAutocadCivil/ConnectorAutocadCivil/UI/ConnectorBindingsAutocadCivil.Selection.cs @@ -7,7 +7,7 @@ using DesktopUI2.Models.Filters; using Speckle.Core.Kits; -#if ADVANCESTEEL2023 +#if ADVANCESTEEL using ASFilerObject = Autodesk.AdvanceSteel.CADAccess.FilerObject; #endif diff --git a/ConnectorAutocadCivil/ConnectorAutocadCivil/UI/ConnectorBindingsAutocadCivil.Send.cs b/ConnectorAutocadCivil/ConnectorAutocadCivil/UI/ConnectorBindingsAutocadCivil.Send.cs index 3037fd5fea..2527d5e2e3 100644 --- a/ConnectorAutocadCivil/ConnectorAutocadCivil/UI/ConnectorBindingsAutocadCivil.Send.cs +++ b/ConnectorAutocadCivil/ConnectorAutocadCivil/UI/ConnectorBindingsAutocadCivil.Send.cs @@ -15,7 +15,7 @@ using Speckle.Core.Transports; using static Speckle.ConnectorAutocadCivil.Utils; -#if ADVANCESTEEL2023 +#if ADVANCESTEEL using ASFilerObject = Autodesk.AdvanceSteel.CADAccess.FilerObject; #endif @@ -193,7 +193,7 @@ ref int convertedCount if (!converter.CanConvertToSpeckle(obj)) { -#if ADVANCESTEEL2023 +#if ADVANCESTEEL UpdateASObject(reportObj, obj); #endif reportObj.Update( @@ -322,7 +322,7 @@ bool isOldApplicationId(string appId) } } -#if ADVANCESTEEL2023 +#if ADVANCESTEEL private void UpdateASObject(ApplicationObject applicationObject, DBObject obj) { if (!CheckAdvanceSteelObject(obj)) diff --git a/ConnectorAutocadCivil/ConnectorAutocadCivil/UI/ConnectorBindingsAutocadCivil.cs b/ConnectorAutocadCivil/ConnectorAutocadCivil/UI/ConnectorBindingsAutocadCivil.cs index 8d14fbbefa..bdce3c2653 100644 --- a/ConnectorAutocadCivil/ConnectorAutocadCivil/UI/ConnectorBindingsAutocadCivil.cs +++ b/ConnectorAutocadCivil/ConnectorAutocadCivil/UI/ConnectorBindingsAutocadCivil.cs @@ -9,7 +9,7 @@ using Speckle.Core.Kits; using Speckle.Core.Models; -#if ADVANCESTEEL2023 +#if ADVANCESTEEL using ASFilerObject = Autodesk.AdvanceSteel.CADAccess.FilerObject; #endif diff --git a/ConnectorAutocadCivil/ConnectorAutocadCivil/Utils.cs b/ConnectorAutocadCivil/ConnectorAutocadCivil/Utils.cs index 146362e0cb..00cad83e85 100644 --- a/ConnectorAutocadCivil/ConnectorAutocadCivil/Utils.cs +++ b/ConnectorAutocadCivil/ConnectorAutocadCivil/Utils.cs @@ -18,7 +18,7 @@ using Autodesk.Aec.PropertyData.DatabaseServices; #endif -#if ADVANCESTEEL2023 +#if ADVANCESTEEL using ASObjectId = Autodesk.AdvanceSteel.CADLink.Database.ObjectId; using ASFilerObject = Autodesk.AdvanceSteel.CADAccess.FilerObject; using Autodesk.AdvanceSteel.Connection; @@ -66,6 +66,10 @@ public static class Utils public static string VersionedAppName = HostApplications.AdvanceSteel.GetVersion(HostAppVersion.v2023); public static string AppName = HostApplications.AdvanceSteel.Name; public static string Slug = HostApplications.AdvanceSteel.Slug; +#elif ADVANCESTEEL2024 + public static string VersionedAppName = HostApplications.AdvanceSteel.GetVersion(HostAppVersion.v2024); + public static string AppName = HostApplications.AdvanceSteel.Name; + public static string Slug = HostApplications.AdvanceSteel.Slug; #endif public static string invalidChars = @"<>/\:;""?*|=,‘"; @@ -109,7 +113,7 @@ public static List GetHandles(this SelectionSet selection) DBObject obj = tr.GetObject(selObj.ObjectId, OpenMode.ForRead); if (obj != null && obj.Visible()) { -#if ADVANCESTEEL2023 +#if ADVANCESTEEL if (CheckAdvanceSteelObject(obj)) { @@ -701,7 +705,7 @@ public static string RemoveInvalidChars(string str) return Regex.Replace(cleanDelimiter, $"[{invalidChars}]", string.Empty); } -#if ADVANCESTEEL2023 +#if ADVANCESTEEL public static T GetFilerObjectByEntity(DBObject @object) where T : ASFilerObject { diff --git a/ConnectorCSI/ConnectorCSIShared/cPlugin.cs b/ConnectorCSI/ConnectorCSIShared/cPlugin.cs index a3be3146fe..0f9c021889 100644 --- a/ConnectorCSI/ConnectorCSIShared/cPlugin.cs +++ b/ConnectorCSI/ConnectorCSIShared/cPlugin.cs @@ -117,7 +117,7 @@ public void Main(ref cSapModel SapModel, ref cPluginCallback ISapPlugin) } catch (Exception e) { - throw e; + throw; ISapPlugin.Finish(0); //return; } @@ -127,4 +127,4 @@ public void Main(ref cSapModel SapModel, ref cPluginCallback ISapPlugin) } -} \ No newline at end of file +} diff --git a/ConnectorDynamo/ConnectorDynamo/ReceiveNode/Receive.cs b/ConnectorDynamo/ConnectorDynamo/ReceiveNode/Receive.cs index 99c5e5d0e0..316f24c522 100644 --- a/ConnectorDynamo/ConnectorDynamo/ReceiveNode/Receive.cs +++ b/ConnectorDynamo/ConnectorDynamo/ReceiveNode/Receive.cs @@ -455,7 +455,7 @@ internal void InitializeReceiver() Warning(exceptionMessage); Message = exceptionMessage.Contains("don't have access") ? "Not authorised" : "Error"; ReceiveEnabled = false; - throw e; + throw; } } diff --git a/ConnectorGrasshopper/ConnectorGrasshopper7/ConnectorGrasshopper7.csproj b/ConnectorGrasshopper/ConnectorGrasshopper7/ConnectorGrasshopper7.csproj index 1d1b796deb..dd8b147826 100644 --- a/ConnectorGrasshopper/ConnectorGrasshopper7/ConnectorGrasshopper7.csproj +++ b/ConnectorGrasshopper/ConnectorGrasshopper7/ConnectorGrasshopper7.csproj @@ -18,22 +18,22 @@ $(DefineConstants);MAC - + - - - + + + - - - + + + - - + + diff --git a/ConnectorGrasshopper/ConnectorGrasshopperShared/Extras/Utilities.cs b/ConnectorGrasshopper/ConnectorGrasshopperShared/Extras/Utilities.cs index 2a429647c1..965152c338 100644 --- a/ConnectorGrasshopper/ConnectorGrasshopperShared/Extras/Utilities.cs +++ b/ConnectorGrasshopper/ConnectorGrasshopperShared/Extras/Utilities.cs @@ -203,25 +203,18 @@ public static List DataTreeToNestedLists( private static void RecurseTreeToList(List parent, List path, int pathIndex, List objects) { - try - { - var listIndex = path[pathIndex]; //there should be a list at this index inside this parent list - - parent = EnsureHasSublistAtIndex(parent, listIndex); - var sublist = parent[listIndex] as List; - //it's the last index of the path => the last sublist => add objects - if (pathIndex == path.Count - 1) - { - sublist.AddRange(objects); - return; - } + var listIndex = path[pathIndex]; //there should be a list at this index inside this parent list - RecurseTreeToList(sublist, path, pathIndex + 1, objects); - } - catch (Exception ex) + parent = EnsureHasSublistAtIndex(parent, listIndex); + var sublist = parent[listIndex] as List; + //it's the last index of the path => the last sublist => add objects + if (pathIndex == path.Count - 1) { - throw ex; + sublist.AddRange(objects); + return; } + + RecurseTreeToList(sublist, path, pathIndex + 1, objects); } /// diff --git a/ConnectorGrasshopper/ConnectorGrasshopperShared/Objects/DeconstructSpeckleObjectTaskComponent.cs b/ConnectorGrasshopper/ConnectorGrasshopperShared/Objects/DeconstructSpeckleObjectTaskComponent.cs index fbf74bbd20..372fabea3f 100644 --- a/ConnectorGrasshopper/ConnectorGrasshopperShared/Objects/DeconstructSpeckleObjectTaskComponent.cs +++ b/ConnectorGrasshopper/ConnectorGrasshopperShared/Objects/DeconstructSpeckleObjectTaskComponent.cs @@ -199,7 +199,7 @@ private bool OutputMismatch() { var isDetached = t.StartsWith("@"); var name = isDetached ? t.Substring(1) : t; - var nickChange = Params.Output[i].NickName != t; + var nickChange = Params.Output[i].NickName != name; var detachChange = (Params.Output[i] as GenericAccessParam).Detachable != isDetached; return nickChange || detachChange; } @@ -244,46 +244,50 @@ private void AutoCreateOutputs() renameParam.Name = cleanName; renameParam.Description = $"Data from property: {cleanName}"; (renameParam as GenericAccessParam).Detachable = isDetached; - return; } - // Check what params must be deleted, and do so when safe. - var remove = Params.Output - .Select( - (p, i) => - { - var res = outputList.Find(o => o == p.Name); - return res == null ? i : -1; - } - ) - .ToList(); - remove.Reverse(); - remove.ForEach(b => + else { - if (b != -1 && Params.Output[b].Recipients.Count == 0) - Params.UnregisterOutputParameter(Params.Output[b]); - }); + // Check what params must be deleted, and do so when safe. + var remove = Params.Output + .Select( + (p, i) => + { + var res = outputList.Find(o => o == p.Name); + return res == null ? i : -1; + } + ) + .ToList(); + remove.Reverse(); + remove.ForEach(b => + { + if (b != -1 && Params.Output[b].Recipients.Count == 0) + Params.UnregisterOutputParameter(Params.Output[b]); + }); - outputList.Sort(); - outputList.ForEach(s => - { - var isDetached = s.StartsWith("@"); - var name = isDetached ? s.Substring(1) : s; - var param = Params.Output.Find(p => p.Name == name); - if (param == null) + outputList.Sort(); + outputList.ForEach(s => { - var newParam = CreateParameter(GH_ParameterSide.Output, Params.Output.Count) as GenericAccessParam; - newParam.Name = name; - newParam.NickName = name; - newParam.Description = $"Data from property: {name}"; - newParam.MutableNickName = false; - newParam.Access = GH_ParamAccess.list; - newParam.Detachable = isDetached; - newParam.Optional = false; - Params.RegisterOutputParam(newParam); - } - if (param is GenericAccessParam srParam) - srParam.Detachable = isDetached; - }); + var isDetached = s.StartsWith("@"); + var name = isDetached ? s.Substring(1) : s; + var param = Params.Output.Find(p => p.Name == name); + if (param == null) + { + var newParam = CreateParameter(GH_ParameterSide.Output, Params.Output.Count) as GenericAccessParam; + newParam.Name = name; + newParam.NickName = name; + newParam.Description = $"Data from property: {name}"; + newParam.MutableNickName = false; + newParam.Access = GH_ParamAccess.list; + newParam.Detachable = isDetached; + newParam.Optional = false; + Params.RegisterOutputParam(newParam); + } + if (param is GenericAccessParam srParam) + srParam.Detachable = isDetached; + }); + } + + // Regardless of the type of change, we should always sort the params again var paramNames = Params.Output.Select(p => p.Name).ToList(); paramNames.Sort(); var sortOrder = Params.Output.Select(p => paramNames.IndexOf(p.Name)).ToArray(); diff --git a/ConnectorRevit/ConnectorRevit/ConnectorRevit.projitems b/ConnectorRevit/ConnectorRevit/ConnectorRevit.projitems index a54160f280..601a93eddd 100644 --- a/ConnectorRevit/ConnectorRevit/ConnectorRevit.projitems +++ b/ConnectorRevit/ConnectorRevit/ConnectorRevit.projitems @@ -42,7 +42,6 @@ - @@ -52,7 +51,7 @@ - + diff --git a/ConnectorRevit/ConnectorRevit/Entry/App.cs b/ConnectorRevit/ConnectorRevit/Entry/App.cs index b5b8cd62ed..20ceb40060 100644 --- a/ConnectorRevit/ConnectorRevit/Entry/App.cs +++ b/ConnectorRevit/ConnectorRevit/Entry/App.cs @@ -15,7 +15,6 @@ namespace Speckle.ConnectorRevit.Entry { public class App : IExternalApplication { - public static UIApplication AppInstance { get; set; } public static UIControlledApplication UICtrlApp { get; set; } @@ -40,7 +39,15 @@ public Result OnStartup(UIControlledApplication application) string path = typeof(App).Assembly.Location; //desktopui 2 - var speckleButton2 = specklePanel.AddItem(new PushButtonData("Speckle 2", "Revit Connector", typeof(App).Assembly.Location, typeof(SpeckleRevitCommand).FullName)) as PushButton; + var speckleButton2 = + specklePanel.AddItem( + new PushButtonData( + "Speckle 2", + "Revit Connector", + typeof(App).Assembly.Location, + typeof(SpeckleRevitCommand).FullName + ) + ) as PushButton; if (speckleButton2 != null) { @@ -52,7 +59,10 @@ public Result OnStartup(UIControlledApplication application) speckleButton2.SetContextualHelp(new ContextualHelp(ContextualHelpType.Url, "https://speckle.systems")); } - var schedulerButton = specklePanel.AddItem(new PushButtonData("Scheduler", "Scheduler", typeof(App).Assembly.Location, typeof(SchedulerCommand).FullName)) as PushButton; + var schedulerButton = + specklePanel.AddItem( + new PushButtonData("Scheduler", "Scheduler", typeof(App).Assembly.Location, typeof(SchedulerCommand).FullName) + ) as PushButton; if (schedulerButton != null) { @@ -64,43 +74,59 @@ public Result OnStartup(UIControlledApplication application) schedulerButton.SetContextualHelp(new ContextualHelp(ContextualHelpType.Url, "https://speckle.systems")); } - PulldownButton helpPulldown = specklePanel.AddItem(new PulldownButtonData("Help&Resources", "Help & Resources")) as PulldownButton; + PulldownButton helpPulldown = + specklePanel.AddItem(new PulldownButtonData("Help&Resources", "Help & Resources")) as PulldownButton; helpPulldown.Image = LoadPngImgSource("Speckle.ConnectorRevit.Assets.help16.png", path); helpPulldown.LargeImage = LoadPngImgSource("Speckle.ConnectorRevit.Assets.help32.png", path); - PushButton forum = helpPulldown.AddPushButton(new PushButtonData("forum", "Community Forum", typeof(App).Assembly.Location, typeof(ForumCommand).FullName)) as PushButton; + PushButton forum = + helpPulldown.AddPushButton( + new PushButtonData("forum", "Community Forum", typeof(App).Assembly.Location, typeof(ForumCommand).FullName) + ) as PushButton; forum.ToolTip = "Check out our Community Forum! Opens a page in your web browser."; forum.Image = LoadPngImgSource("Speckle.ConnectorRevit.Assets.forum16.png", path); forum.LargeImage = LoadPngImgSource("Speckle.ConnectorRevit.Assets.forum32.png", path); - PushButton tutorials = helpPulldown.AddPushButton(new PushButtonData("tutorials", "Tutorials", typeof(App).Assembly.Location, typeof(TutorialsCommand).FullName)) as PushButton; + PushButton tutorials = + helpPulldown.AddPushButton( + new PushButtonData("tutorials", "Tutorials", typeof(App).Assembly.Location, typeof(TutorialsCommand).FullName) + ) as PushButton; tutorials.ToolTip = "Check out our tutorials! Opens a page in your web browser."; tutorials.Image = LoadPngImgSource("Speckle.ConnectorRevit.Assets.tutorials16.png", path); tutorials.LargeImage = LoadPngImgSource("Speckle.ConnectorRevit.Assets.tutorials32.png", path); - PushButton docs = helpPulldown.AddPushButton(new PushButtonData("docs", "Docs", typeof(App).Assembly.Location, typeof(DocsCommand).FullName)) as PushButton; + PushButton docs = + helpPulldown.AddPushButton( + new PushButtonData("docs", "Docs", typeof(App).Assembly.Location, typeof(DocsCommand).FullName) + ) as PushButton; docs.ToolTip = "Check out our documentation! Opens a page in your web browser."; docs.Image = LoadPngImgSource("Speckle.ConnectorRevit.Assets.docs16.png", path); docs.LargeImage = LoadPngImgSource("Speckle.ConnectorRevit.Assets.docs32.png", path); - PushButton manager = helpPulldown.AddPushButton(new PushButtonData("manager", "Manager", typeof(App).Assembly.Location, typeof(ManagerCommand).FullName)) as PushButton; + PushButton manager = + helpPulldown.AddPushButton( + new PushButtonData("manager", "Manager", typeof(App).Assembly.Location, typeof(ManagerCommand).FullName) + ) as PushButton; manager.ToolTip = "Manage accounts and connectors. Opens SpeckleManager."; manager.Image = LoadPngImgSource("Speckle.ConnectorRevit.Assets.logo16.png", path); manager.LargeImage = LoadPngImgSource("Speckle.ConnectorRevit.Assets.logo32.png", path); - - - return Result.Succeeded; } - private void ControlledApplication_ApplicationInitialized(object sender, Autodesk.Revit.DB.Events.ApplicationInitializedEventArgs e) + private void ControlledApplication_ApplicationInitialized( + object sender, + Autodesk.Revit.DB.Events.ApplicationInitializedEventArgs e + ) { try { // We need to hook into the AssemblyResolve event before doing anything else // or we'll run into unresolved issues loading dependencies AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(OnAssemblyResolve); + AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; + System.Windows.Forms.Application.ThreadException += Application_ThreadException; + AppInstance = new UIApplication(sender as Application); Setup.Init(ConnectorBindingsRevit.HostAppNameVersion, ConnectorBindingsRevit.HostAppName); @@ -129,7 +155,8 @@ private void ControlledApplication_ApplicationInitialized(object sender, Autodes } else { - td.MainContent = $"Oh no! Something went wrong while loading Speckle, please report it on the forum:\n\n{ex.Message}"; + td.MainContent = + $"Oh no! Something went wrong while loading Speckle, please report it on the forum:\n\n{ex.Message}"; } td.AddCommandLink(TaskDialogCommandLinkId.CommandLink1, "Ask for help on our Community Forum"); @@ -143,6 +170,37 @@ private void ControlledApplication_ApplicationInitialized(object sender, Autodes } } + private void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e) + { + SpeckleLog.Logger.Fatal( + e.Exception, + "Caught thread exception with message {exceptionMessage}", + e.Exception.Message + ); + } + + private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) + { + if (e.ExceptionObject is Exception ex) + { + SpeckleLog.Logger.Fatal( + ex, + "Caught unhandled exception. Is terminating : {isTerminating}. Message : {exceptionMessage}", + e.IsTerminating, + ex.Message + ); + } + else + { + SpeckleLog.Logger.Fatal( + "Caught unhandled exception. Is terminating : {isTerminating}. Exception object is of type : {exceptionObjectType}. Exception object to string : {exceptionObjToString}", + e.IsTerminating, + e.ExceptionObject.GetType(), + e.ExceptionObject.ToString() + ); + } + } + public Result OnShutdown(UIControlledApplication application) { return Result.Succeeded; @@ -154,7 +212,11 @@ private ImageSource LoadPngImgSource(string sourceName, string path) { var assembly = Assembly.LoadFrom(Path.Combine(path)); var icon = assembly.GetManifestResourceStream(sourceName); - PngBitmapDecoder m_decoder = new PngBitmapDecoder(icon, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default); + PngBitmapDecoder m_decoder = new PngBitmapDecoder( + icon, + BitmapCreateOptions.PreservePixelFormat, + BitmapCacheOption.Default + ); ImageSource m_source = m_decoder.Frames[0]; return (m_source); } @@ -177,5 +239,4 @@ static Assembly OnAssemblyResolve(object sender, ResolveEventArgs args) return a; } } - } diff --git a/ConnectorRevit/ConnectorRevit/Entry/SchedulerCommand.cs b/ConnectorRevit/ConnectorRevit/Entry/SchedulerCommand.cs index b657989f02..288c248d47 100644 --- a/ConnectorRevit/ConnectorRevit/Entry/SchedulerCommand.cs +++ b/ConnectorRevit/ConnectorRevit/Entry/SchedulerCommand.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; using Autodesk.Revit.Attributes; using Autodesk.Revit.DB; @@ -13,14 +14,15 @@ namespace Speckle.ConnectorRevit.Entry public class SchedulerCommand : IExternalCommand { [DllImport("user32.dll", SetLastError = true)] + [SuppressMessage("Security", "CA5392:Use DefaultDllImportSearchPaths attribute for P/Invokes")] static extern IntPtr SetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr value); + const int GWL_HWNDPARENT = -8; internal static UIApplication uiapp; public static ConnectorBindingsRevit Bindings { get; set; } - public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements) { uiapp = commandData.Application; @@ -31,11 +33,7 @@ public Result Execute(ExternalCommandData commandData, ref string message, Eleme public static void CreateOrFocusSpeckle() { - - var scheduler = new Scheduler - { - DataContext = new SchedulerViewModel(Bindings) - }; + var scheduler = new Scheduler { DataContext = new SchedulerViewModel(Bindings) }; scheduler.Show(); @@ -45,8 +43,6 @@ public static void CreateOrFocusSpeckle() var hwnd = scheduler.PlatformImpl.Handle.Handle; SetWindowLongPtr(hwnd, GWL_HWNDPARENT, parentHwnd); } - } } - } diff --git a/ConnectorRevit/ConnectorRevit/Entry/SpeckleRevitCommand.cs b/ConnectorRevit/ConnectorRevit/Entry/SpeckleRevitCommand.cs index f787de47ef..bc3391d094 100644 --- a/ConnectorRevit/ConnectorRevit/Entry/SpeckleRevitCommand.cs +++ b/ConnectorRevit/ConnectorRevit/Entry/SpeckleRevitCommand.cs @@ -1,5 +1,6 @@ using System; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; using System.Threading; using Autodesk.Revit.Attributes; @@ -18,42 +19,41 @@ namespace Speckle.ConnectorRevit.Entry [Transaction(TransactionMode.Manual)] public class SpeckleRevitCommand : IExternalCommand { - public static bool UseDockablePanel = true; //window stuff [DllImport("user32.dll", SetLastError = true)] + [SuppressMessage("Security", "CA5392:Use DefaultDllImportSearchPaths attribute for P/Invokes")] static extern IntPtr SetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr value); + const int GWL_HWNDPARENT = -8; public static Window MainWindow { get; private set; } private static Avalonia.Application AvaloniaApp { get; set; } + //end window stuff public static ConnectorBindingsRevit Bindings { get; set; } private static Panel _panel { get; set; } - internal static DockablePaneId PanelId = new DockablePaneId(new Guid("{0A866FB8-8FD5-4DE8-B24B-56F4FA5B0836}")); - public static void InitAvalonia() { BuildAvaloniaApp().SetupWithoutStarting(); } - public static AppBuilder BuildAvaloniaApp() => AppBuilder.Configure() - .UsePlatformDetect() - .With(new SkiaOptions { MaxGpuResourceSizeBytes = 8096000 }) - .With(new Win32PlatformOptions { AllowEglInitialization = true, EnableMultitouch = false }) - .LogToTrace() - .UseReactiveUI(); - - + public static AppBuilder BuildAvaloniaApp() => + AppBuilder + .Configure() + .UsePlatformDetect() + .With(new SkiaOptions { MaxGpuResourceSizeBytes = 8096000 }) + .With(new Win32PlatformOptions { AllowEglInitialization = true, EnableMultitouch = false }) + .LogToTrace() + .UseReactiveUI(); public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements) { - if (UseDockablePanel) { try @@ -70,8 +70,6 @@ public Result Execute(ExternalCommandData commandData, ref string message, Eleme else CreateOrFocusSpeckle(); - - return Result.Succeeded; } @@ -95,10 +93,7 @@ internal static void RegisterPane() { //Register dockable panel var viewModel = new MainViewModel(Bindings); - _panel = new Panel - { - DataContext = viewModel - }; + _panel = new Panel { DataContext = viewModel }; App.AppInstance.RegisterDockablePane(PanelId, "Speckle", _panel); _panel.Init(); } @@ -111,24 +106,23 @@ internal static void RegisterPane() TaskDialog mainDialog = new TaskDialog("Dockable Panel Issue"); mainDialog.MainInstruction = "Dockable Panel Issue"; mainDialog.MainContent = - "Revit cannot properly register Dockable Panels when launched by double-clicking a Revit file. " - + "Please close and re-open Revit without launching a file OR open/create a new project to trigger the Speckle panel registration."; - + "Revit cannot properly register Dockable Panels when launched by double-clicking a Revit file. " + + "Please close and re-open Revit without launching a file OR open/create a new project to trigger the Speckle panel registration."; // Set footer text. Footer text is usually used to link to the help document. mainDialog.FooterText = - "" - + "Click here for more info"; + "" + + "Click here for more info"; mainDialog.Show(); - } } catch (Exception ex) { SpeckleLog.Logger.Fatal(ex, "Failed to load Speckle command for host app"); var td = new TaskDialog("Error"); - td.MainContent = $"Oh no! Something went wrong while loading Speckle, please report it on the forum:\n{ex.Message}"; + td.MainContent = + $"Oh no! Something went wrong while loading Speckle, please report it on the forum:\n{ex.Message}"; td.AddCommandLink(TaskDialogCommandLinkId.CommandLink1, "Report issue on our Community Forum"); TaskDialogResult tResult = td.Show(); @@ -138,31 +132,24 @@ internal static void RegisterPane() Process.Start("https://speckle.community/"); } } - } public static void CreateOrFocusSpeckle(bool showWindow = true) { try { - if (MainWindow == null) { var viewModel = new MainViewModel(Bindings); - MainWindow = new MainWindow - { - DataContext = viewModel - }; + MainWindow = new MainWindow { DataContext = viewModel }; //massive hack: we start the avalonia main loop and stop it immediately (since it's thread blocking) //to avoid an annoying error when closing revit var cts = new CancellationTokenSource(); cts.CancelAfter(100); AvaloniaApp.Run(cts.Token); - } - if (showWindow) { MainWindow.Show(); @@ -185,7 +172,8 @@ public static void CreateOrFocusSpeckle(bool showWindow = true) { SpeckleLog.Logger.Fatal(ex, "Failed to create main window"); var td = new TaskDialog("Error"); - td.MainContent = $"Oh no! Something went wrong while loading Speckle, please report it on the forum:\n{ex.Message}"; + td.MainContent = + $"Oh no! Something went wrong while loading Speckle, please report it on the forum:\n{ex.Message}"; td.AddCommandLink(TaskDialogCommandLinkId.CommandLink1, "Report issue on our Community Forum"); TaskDialogResult tResult = td.Show(); @@ -201,11 +189,5 @@ private static void AppMain(Avalonia.Application app, string[] args) { AvaloniaApp = app; } - - - - - } - } diff --git a/ConnectorRevit/ConnectorRevit/Storage/StreamStateCache.cs b/ConnectorRevit/ConnectorRevit/Storage/StreamStateCache.cs index c59760d6a7..3636dbc7d7 100644 --- a/ConnectorRevit/ConnectorRevit/Storage/StreamStateCache.cs +++ b/ConnectorRevit/ConnectorRevit/Storage/StreamStateCache.cs @@ -12,6 +12,7 @@ public class StreamStateCache : IReceivedObjectIdMap { private StreamState streamState; private Dictionary previousContextObjects; + public StreamStateCache(StreamState state) { streamState = state; @@ -34,14 +35,14 @@ public void AddConvertedElements(IConvertedObjectsCache converted var elements = convertedObjects.GetCreatedObjectsFromConvertedId(@base.applicationId).ToList(); var appObj = new ApplicationObject(@base.id, @base.speckle_type); - newContextObjects.Add(new ApplicationObject(@base.id, @base.speckle_type) - { - applicationId = @base.applicationId, - CreatedIds = elements - .Select(element => element.UniqueId) - .ToList(), - Converted = elements.Cast().ToList() - }); + newContextObjects.Add( + new ApplicationObject(@base.id, @base.speckle_type) + { + applicationId = @base.applicationId, + CreatedIds = elements.Where(element => element.IsValidObject).Select(element => element.UniqueId).ToList(), + Converted = elements.Cast().ToList() + } + ); } streamState.ReceivedObjects = newContextObjects; } diff --git a/ConnectorRevit/ConnectorRevit/TypeMapping/ElementTypeMapper.cs b/ConnectorRevit/ConnectorRevit/TypeMapping/ElementTypeMapper.cs index 2cbfe2fbc0..90360074fe 100644 --- a/ConnectorRevit/ConnectorRevit/TypeMapping/ElementTypeMapper.cs +++ b/ConnectorRevit/ConnectorRevit/TypeMapping/ElementTypeMapper.cs @@ -70,11 +70,13 @@ public ElementTypeMapper(ISpeckleConverter converter, IRevitDocumentAggregateCac ); } } - public async Task Map(ISetting mapOnReceiveSetting) + public async Task Map(ISetting mapOnReceiveSetting, ISetting directShapeStrategySetting) { // Get Settings for recieve on mapping if (mapOnReceiveSetting is not MappingSetting mappingSetting - || mappingSetting.Selection == ConnectorBindingsRevit.noMapping) + || mappingSetting.Selection == ConnectorBindingsRevit.noMapping + //skip mappings dialog always when DS fallback is set to always + || directShapeStrategySetting.Selection == ConnectorBindingsRevit.DsFallbackAways) { return; } @@ -114,11 +116,7 @@ private async Task ShouldShowCustomMappingDialog(string listBoxSelection, { return true; } - else if (listBoxSelection == ConnectorBindingsRevit.forNewTypes && numNewTypes > 0) - { - return true; - } - else if (listBoxSelection == null + else if (listBoxSelection == ConnectorBindingsRevit.forNewTypes && numNewTypes > 0 && await ShowMissingIncomingTypesDialog().ConfigureAwait(false) ) @@ -164,7 +162,7 @@ await Dispatcher.UIThread.InvokeAsync(() => { try { - familyImporter ??= new FamilyImporter(document, revitCategoriesExposer, typeRetriever); + familyImporter ??= new FamilyImporter(document, revitCategoriesExposer, typeRetriever, revitDocumentAggregateCache); await familyImporter.ImportFamilyTypes(hostTypesContainer).ConfigureAwait(false); } catch (SpeckleException ex) diff --git a/ConnectorRevit/ConnectorRevit/TypeMapping/FamilyImporter.cs b/ConnectorRevit/ConnectorRevit/TypeMapping/FamilyImporter.cs index c253a1d602..d339077d30 100644 --- a/ConnectorRevit/ConnectorRevit/TypeMapping/FamilyImporter.cs +++ b/ConnectorRevit/ConnectorRevit/TypeMapping/FamilyImporter.cs @@ -25,11 +25,12 @@ internal sealed class FamilyImporter private readonly IRevitElementTypeRetriever typeRetriever; private readonly IRevitDocumentAggregateCache revitDocumentAggregateCache; - public FamilyImporter(Document document, IAllRevitCategoriesExposer revitCategoriesExposer, IRevitElementTypeRetriever typeRetriever) + public FamilyImporter(Document document, IAllRevitCategoriesExposer revitCategoriesExposer, IRevitElementTypeRetriever typeRetriever, IRevitDocumentAggregateCache revitDocumentAggregateCache) { this.document = document; this.revitCategoriesExposer = revitCategoriesExposer; this.typeRetriever = typeRetriever; + this.revitDocumentAggregateCache = revitDocumentAggregateCache; } /// diff --git a/ConnectorRevit/ConnectorRevit/UI/ConnectorBindingsRevit .Settings.cs b/ConnectorRevit/ConnectorRevit/UI/ConnectorBindingsRevit .Settings.cs deleted file mode 100644 index b3fb789534..0000000000 --- a/ConnectorRevit/ConnectorRevit/UI/ConnectorBindingsRevit .Settings.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using Autodesk.Revit.DB; -using DesktopUI2.Models.Settings; - -namespace Speckle.ConnectorRevit.UI -{ - public partial class ConnectorBindingsRevit - { - // CAUTION: these strings need to have the same values as in the converter - const string InternalOrigin = "Internal Origin (default)"; - const string ProjectBase = "Project Base"; - const string Survey = "Survey"; - - const string defaultValue = "Default"; - const string dxf = "DXF"; - const string familyDxf = "Family DXF"; - - const string StructuralFraming = "Structural Framing"; - const string StructuralWalls = "Structural Walls"; - const string ArchitecturalWalls = "Achitectural Walls"; - - public const string noMapping = "Never"; - public const string everyReceive = "Always"; - public const string forNewTypes = "For New Types"; - - public override List GetSettings() - { - List referencePoints = new List() { InternalOrigin }; - List prettyMeshOptions = new List() { defaultValue, dxf, familyDxf }; - List mappingOptions = new List() { noMapping, everyReceive, forNewTypes }; - - // find project base point and survey point. these don't always have name props, so store them under custom strings - var basePoint = new FilteredElementCollector(CurrentDoc.Document).OfClass(typeof(BasePoint)).Cast().Where(o => o.IsShared == false).FirstOrDefault(); - if (basePoint != null) - referencePoints.Add(ProjectBase); - var surveyPoint = new FilteredElementCollector(CurrentDoc.Document).OfClass(typeof(BasePoint)).Cast().Where(o => o.IsShared == true).FirstOrDefault(); - if (surveyPoint != null) - referencePoints.Add(Survey); - - return new List - { - new ListBoxSetting {Slug = "reference-point", Name = "Reference Point", Icon ="LocationSearching", Values = referencePoints, Selection = InternalOrigin, Description = "Sends or receives stream objects in relation to this document point"}, - new CheckBoxSetting {Slug = "linkedmodels-send", Name = "Send Linked Models", Icon ="Link", IsChecked= false, Description = "Include Linked Models in the selection filters when sending"}, - new CheckBoxSetting {Slug = "linkedmodels-receive", Name = "Receive Linked Models", Icon ="Link", IsChecked= false, Description = "Include Linked Models when receiving NOTE: elements from linked models will be received in the current document"}, - new CheckBoxSetting {Slug = "recieve-objects-mesh", Name = "Receive Objects as Direct Mesh", Icon = "Link", IsChecked = false, Description = "Recieve the stream as a Meshes only"}, - new MultiSelectBoxSetting { Slug = "disallow-join", Name = "Disallow Join For Elements", Icon = "CallSplit", Description = "Determine which objects should not be allowed to join by default when receiving", - Values = new List() { ArchitecturalWalls, StructuralWalls, StructuralFraming } }, - new ListBoxSetting {Slug = "pretty-mesh", Name = "Mesh Import Method", Icon ="ChartTimelineVarient", Values = prettyMeshOptions, Selection = defaultValue, Description = "Determines the display style of imported meshes"}, - new MappingSetting {Slug = "receive-mappings", Name = "Custom Type Mapping", Icon ="LocationSearching", Values = mappingOptions, Description = "Determine how incoming object types are mapped to object types in the host application"}, - }; - } - } -} diff --git a/ConnectorRevit/ConnectorRevit/UI/ConnectorBindingsRevit.Previews.cs b/ConnectorRevit/ConnectorRevit/UI/ConnectorBindingsRevit.Previews.cs index 6d8923c0a7..d81e4cdcc1 100644 --- a/ConnectorRevit/ConnectorRevit/UI/ConnectorBindingsRevit.Previews.cs +++ b/ConnectorRevit/ConnectorRevit/UI/ConnectorBindingsRevit.Previews.cs @@ -15,6 +15,7 @@ using Autodesk.Revit.DB.DirectContext3D; using RevitSharedResources.Interfaces; using RevitSharedResources.Models; +using ConnectorRevit.Revit; namespace Speckle.ConnectorRevit.UI { @@ -34,7 +35,7 @@ public override async Task PreviewReceive(StreamState state, Progre if (commit.id != SelectedReceiveCommit) { - // check for converter + // check for converter var converter = KitManager.GetDefaultKit().LoadConverter(ConnectorRevitUtils.RevitAppName); converter.SetContextDocument(CurrentDoc.Document); @@ -56,18 +57,19 @@ public override async Task PreviewReceive(StreamState state, Progre progress.Report.Log(previewObj); IConvertedObjectsCache convertedObjects = null; - await APIContext.Run( - app => + await APIContext + .Run(app => { using (var t = new Transaction(CurrentDoc.Document, $"Baking stream {state.StreamId}")) { t.Start(); - convertedObjects = ConvertReceivedObjects(converter, progress); + convertedObjects = ConvertReceivedObjects(converter, progress, new TransactionManager(null, null)); t.Commit(); } AddMultipleRevitElementServers(convertedObjects); - }); + }) + .ConfigureAwait(false); } else // just generate the log { @@ -77,11 +79,7 @@ await APIContext.Run( } catch (Exception ex) { - SpeckleLog.Logger.Error( - ex, - "Failed to preview receive: {exceptionMessage}", - ex.Message - ); + SpeckleLog.Logger.Error(ex, "Failed to preview receive: {exceptionMessage}", ex.Message); } return null; @@ -94,8 +92,9 @@ public override void ResetDocument() public void AddMultipleRevitElementServers(IConvertedObjectsCache convertedObjects) { - ExternalService directContext3DService = - ExternalServiceRegistry.GetService(ExternalServices.BuiltInExternalServices.DirectContext3DService); + ExternalService directContext3DService = ExternalServiceRegistry.GetService( + ExternalServices.BuiltInExternalServices.DirectContext3DService + ); MultiServerService msDirectContext3DService = directContext3DService as MultiServerService; IList serverIds = msDirectContext3DService.GetActiveServerIds(); @@ -152,7 +151,8 @@ public override void PreviewSend(StreamState state, ProgressViewModel progress) if (!converter.CanConvertToSpeckle(filterObj)) reportObj.Update( status: ApplicationObject.State.Skipped, - logItem: $"Sending this object type is not supported in Revit"); + logItem: $"Sending this object type is not supported in Revit" + ); else reportObj.Update(status: ApplicationObject.State.Created); progress.Report.Log(reportObj); @@ -162,11 +162,7 @@ public override void PreviewSend(StreamState state, ProgressViewModel progress) } catch (Exception ex) { - SpeckleLog.Logger.Error( - ex, - "Failed to preview send: {exceptionMessage}", - ex.Message - ); + SpeckleLog.Logger.Error(ex, "Failed to preview send: {exceptionMessage}", ex.Message); } } } diff --git a/ConnectorRevit/ConnectorRevit/UI/ConnectorBindingsRevit.Receive.cs b/ConnectorRevit/ConnectorRevit/UI/ConnectorBindingsRevit.Receive.cs index 385b14889f..0654ae5ada 100644 --- a/ConnectorRevit/ConnectorRevit/UI/ConnectorBindingsRevit.Receive.cs +++ b/ConnectorRevit/ConnectorRevit/UI/ConnectorBindingsRevit.Receive.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -21,16 +22,17 @@ using Speckle.Core.Logging; using Speckle.Core.Models; using Speckle.Core.Models.GraphTraversal; +using Speckle.Core.Transports; namespace Speckle.ConnectorRevit.UI { - public partial class ConnectorBindingsRevit { public List Preview { get; set; } = new List(); public Dictionary StoredObjects = new Dictionary(); public CancellationTokenSource CurrentOperationCancellation { get; set; } + /// /// Receives a stream and bakes into the existing revit file. /// @@ -54,7 +56,12 @@ public override async Task ReceiveStream(StreamState state, Progres Commit myCommit = await ConnectorHelpers.GetCommitFromState(state, progress.CancellationToken); state.LastCommit = myCommit; Base commitObject = await ConnectorHelpers.ReceiveCommit(myCommit, state, progress); - await ConnectorHelpers.TryCommitReceived(state, myCommit, ConnectorRevitUtils.RevitAppName, progress.CancellationToken); + await ConnectorHelpers.TryCommitReceived( + state, + myCommit, + ConnectorRevitUtils.RevitAppName, + progress.CancellationToken + ); Preview.Clear(); StoredObjects.Clear(); @@ -63,7 +70,6 @@ public override async Task ReceiveStream(StreamState state, Progres foreach (var previewObj in Preview) progress.Report.Log(previewObj); - converter.ReceiveMode = state.ReceiveMode; // needs to be set for editing to work var previousObjects = new StreamStateCache(state); @@ -77,15 +83,24 @@ public override async Task ReceiveStream(StreamState state, Progres #pragma warning disable CA1031 // Do not catch general exception types try { - var elementTypeMapper = new ElementTypeMapper(converter, revitDocumentAggregateCache, Preview, StoredObjects, CurrentDoc.Document); - await elementTypeMapper.Map(state.Settings.FirstOrDefault(x => x.Slug == "receive-mappings")) + var elementTypeMapper = new ElementTypeMapper( + converter, + revitDocumentAggregateCache, + Preview, + StoredObjects, + CurrentDoc.Document + ); + await elementTypeMapper + .Map(state.Settings.FirstOrDefault(x => x.Slug == "receive-mappings"), state.Settings.FirstOrDefault(x => x.Slug == DsFallbackSlug)) .ConfigureAwait(false); } catch (Exception ex) { var speckleEx = new SpeckleException($"Failed to map incoming types to Revit types. Reason: {ex.Message}", ex); StreamViewModel.HandleCommandException(speckleEx, false, "MapIncomingTypesCommand"); - progress.Report.LogOperationError(new Exception("Could not update receive object with user types. Using default mapping.", ex)); + progress.Report.LogOperationError( + new Exception("Could not update receive object with user types. Using default mapping.", ex) + ); } finally { @@ -93,62 +108,39 @@ await elementTypeMapper.Map(state.Settings.FirstOrDefault(x => x.Slug == "receiv } #pragma warning restore CA1031 // Do not catch general exception types - var (success, exception) = await APIContext.Run(_ => - { - string transactionName = $"Baking stream {state.StreamId}"; - using var g = new TransactionGroup(CurrentDoc.Document, transactionName); - using var t = new Transaction(CurrentDoc.Document, transactionName); - - g.Start(); - var failOpts = t.GetFailureHandlingOptions(); - var errorEater = new ErrorEater(converter); - failOpts.SetFailuresPreprocessor(errorEater); - failOpts.SetClearAfterRollback(true); - t.SetFailureHandlingOptions(failOpts); - t.Start(); - - try + var (success, exception) = await APIContext + .Run(_ => { - converter.SetContextDocument(t); + using var transactionManager = new TransactionManager(state.StreamId, CurrentDoc.Document); + transactionManager.Start(); - var convertedObjects = ConvertReceivedObjects(converter, progress); + try + { + converter.SetContextDocument(transactionManager); - if (state.ReceiveMode == ReceiveMode.Update) - DeleteObjects(previousObjects, convertedObjects); + var convertedObjects = ConvertReceivedObjects(converter, progress, transactionManager); - previousObjects.AddConvertedElements(convertedObjects); - t.Commit(); + if (state.ReceiveMode == ReceiveMode.Update) + DeleteObjects(previousObjects, convertedObjects); - if (t.GetStatus() == TransactionStatus.RolledBack) - { - var numTotalErrors = errorEater.CommitErrorsDict.Sum(kvp => kvp.Value); - var numUniqueErrors = errorEater.CommitErrorsDict.Keys.Count; - - var exception = errorEater.GetException(); - if (exception == null) - SpeckleLog.Logger.Warning("Revit commit failed with {numUniqueErrors} unique errors and {numTotalErrors} total errors, but the ErrorEater did not capture any exceptions", numUniqueErrors, numTotalErrors); - else - SpeckleLog.Logger.Fatal(exception, "The Revit API could not resolve {numUniqueErrors} unique errors and {numTotalErrors} total errors when trying to commit the Speckle model. The whole transaction is being rolled back.", numUniqueErrors, numTotalErrors); - - return (false, exception ?? new SpeckleException($"The Revit API could not resolve {numUniqueErrors} unique errors and {numTotalErrors} total errors when trying to commit the Speckle model. The whole transaction is being rolled back.")); + previousObjects.AddConvertedElements(convertedObjects); + transactionManager.Finish(); + return (true, null); } + catch (Exception ex) + { + SpeckleLog.Logger.Error(ex, "Rolling back connector transaction"); - g.Assimilate(); - return (true, null); - } - catch (Exception ex) - { - SpeckleLog.Logger.Error(ex, "Rolling back connector transaction {transactionName} {transactionType}", transactionName, t.GetType()); - - string message = $"Fatal Error: {ex.Message}"; - if (ex is OperationCanceledException) message = "Receive cancelled"; - progress.Report.LogOperationError(new Exception($"{message} - Changes have been rolled back", ex)); + string message = $"Fatal Error: {ex.Message}"; + if (ex is OperationCanceledException) + message = "Receive cancelled"; + progress.Report.LogOperationError(new Exception($"{message} - Changes have been rolled back", ex)); - t.RollBack(); - g.RollBack(); - return (false, ex); //We can't throw exceptions in from RevitTask, but we can return it along with a success status - } - }).ConfigureAwait(false); + transactionManager.RollbackAll(); + return (false, ex); //We can't throw exceptions in from RevitTask, but we can return it along with a success status + } + }) + .ConfigureAwait(false); revitDocumentAggregateCache.InvalidateAll(); CurrentOperationCancellation = null; @@ -169,10 +161,13 @@ await elementTypeMapper.Map(state.Settings.FirstOrDefault(x => x.Slug == "receiv } //delete previously sent object that are no more in this stream - private void DeleteObjects(IReceivedObjectIdMap previousObjects, IConvertedObjectsCache convertedObjects) + private void DeleteObjects( + IReceivedObjectIdMap previousObjects, + IConvertedObjectsCache convertedObjects + ) { var previousAppIds = previousObjects.GetAllConvertedIds().ToList(); - for (var i = previousAppIds.Count - 1; i >=0; i--) + for (var i = previousAppIds.Count - 1; i >= 0; i--) { var appId = previousAppIds[i]; if (string.IsNullOrEmpty(appId) || convertedObjects.HasConvertedObjectWithId(appId)) @@ -201,13 +196,61 @@ private void DeleteObjects(IReceivedObjectIdMap previousObjects, } } - private IConvertedObjectsCache ConvertReceivedObjects(ISpeckleConverter converter, ProgressViewModel progress) + private IConvertedObjectsCache ConvertReceivedObjects( + ISpeckleConverter converter, + ProgressViewModel progress, + TransactionManager transactionManager + ) { + // Traverses through the `elements` property of the given base + void ConvertNestedElements(Base @base, ApplicationObject appObj, bool receiveDirectMesh) + { + if (@base == null) + return; + + var nestedElements = @base["elements"] ?? @base["@elements"]; + + if (nestedElements == null) + return; + + // set host in converter state. + // assumes host is the first converted object of the appObject + var host = appObj == null || !appObj.Converted.Any() ? null : appObj.Converted.First() as Element; + using var ctx = RevitConverterState.Push(); + ctx.CurrentHostElement = host; + + // traverse each element member and convert + foreach (var obj in GraphTraversal.TraverseMember(nestedElements)) + { + // create the application object and log to reports + var nestedAppObj = Preview.Where(o => o.OriginalId == obj.id)?.FirstOrDefault(); + if (nestedAppObj == null) + { + nestedAppObj = new ApplicationObject(obj.id, ConnectorRevitUtils.SimplifySpeckleType(obj.speckle_type)) + { + applicationId = obj.applicationId, + Convertible = converter.CanConvertToNative(obj) + }; + progress.Report.Log(nestedAppObj); + converter.Report.Log(nestedAppObj); + } + + // convert + var converted = ConvertObject(nestedAppObj, obj, receiveDirectMesh, converter, progress, transactionManager); + + // Check if parent conversion succeeded before attempting the children + if ( + receiveDirectMesh || converted?.Status is ApplicationObject.State.Created or ApplicationObject.State.Updated + ) + // recurse and convert nested elements + ConvertNestedElements(obj, nestedAppObj, receiveDirectMesh); + } + } + using var _d0 = LogContext.PushProperty("converterName", converter.Name); using var _d1 = LogContext.PushProperty("converterAuthor", converter.Author); using var _d2 = LogContext.PushProperty("conversionDirection", nameof(ISpeckleConverter.ConvertToNative)); - var convertedObjectsCache = new ConvertedObjectsCache(); converter.SetContextDocument(convertedObjectsCache); @@ -215,9 +258,29 @@ private IConvertedObjectsCache ConvertReceivedObjects(ISpeckleCon conversionProgressDict["Conversion"] = 1; // Get setting to skip linked model elements if necessary - var receiveLinkedModelsSetting = CurrentSettings.FirstOrDefault(x => x.Slug == "linkedmodels-receive") as CheckBoxSetting; - var receiveLinkedModels = receiveLinkedModelsSetting != null ? receiveLinkedModelsSetting.IsChecked : false; + var receiveLinkedModelsSetting = + CurrentSettings?.FirstOrDefault(x => x.Slug == "linkedmodels-receive") as CheckBoxSetting; + var receiveLinkedModels = receiveLinkedModelsSetting?.IsChecked ?? false; + + var receiveDirectMesh = false; + var fallbackToDirectShape = false; + var directShapeStrategySetting = + CurrentSettings?.FirstOrDefault(x => x.Slug == "direct-shape-strategy") as ListBoxSetting; + switch (directShapeStrategySetting!.Selection) + { + case "Always": + receiveDirectMesh = true; + break; + case "On Error": + fallbackToDirectShape = true; + break; + case "Never": + case null: + // Do nothing, default values will do. + break; + } + // convert var index = -1; while (++index < Preview.Count) { @@ -226,62 +289,164 @@ private IConvertedObjectsCache ConvertReceivedObjects(ISpeckleCon var @base = StoredObjects[obj.OriginalId]; - using var _d3 = LogContext.PushProperty("speckleType", @base.speckle_type); - try + // skip if this object has already been converted from a nested elements loop + if (obj.Status != ApplicationObject.State.Unknown) + continue; + + conversionProgressDict["Conversion"]++; + progress.Update(conversionProgressDict); + + //skip element if is from a linked file and setting is off + if ( + !receiveLinkedModels + && @base["isRevitLinkedModel"] != null + && bool.Parse(@base["isRevitLinkedModel"].ToString()) + ) + continue; + + var converted = ConvertObject(obj, @base, receiveDirectMesh, converter, progress, transactionManager); + // Determine if we should use the fallback DirectShape conversion + // Should only happen when receiveDirectMesh is OFF, fallback is ON and object failed normal conversion. + bool usingFallback = + !receiveDirectMesh && fallbackToDirectShape && converted.Status == ApplicationObject.State.Failed; + if (usingFallback) { - conversionProgressDict["Conversion"]++; - progress.Update(conversionProgressDict); + obj.Log.Add("Conversion to native Revit object failed. Retrying conversion with displayable geometry."); + converted = ConvertObject(obj, @base, true, converter, progress, transactionManager); + if (converted == null) + obj.Update(status: ApplicationObject.State.Failed, logItem: "Conversion returned null."); + } + + RefreshView(); + if (index % 50 == 0) + transactionManager.Commit(); + + // Check if parent conversion succeeded or fallback is enabled before attempting the children + if ( + usingFallback + || receiveDirectMesh + || converted?.Status is ApplicationObject.State.Created or ApplicationObject.State.Updated + ) + // continue traversing for hosted elements + // use DirectShape conversion if the parent was converted using fallback or if the global setting is active. + ConvertNestedElements(@base, converted, usingFallback || receiveDirectMesh); + } + + return convertedObjectsCache; + } - var s = new CancellationTokenSource(); - DispatcherTimer.RunOnce(() => s.Cancel(), TimeSpan.FromMilliseconds(10)); - Dispatcher.UIThread.MainLoop(s.Token); + private ApplicationObject ConvertObject( + ApplicationObject obj, + Base @base, + bool receiveDirectMesh, + ISpeckleConverter converter, + ProgressViewModel progress, + TransactionManager transactionManager + ) + { + progress.CancellationToken.ThrowIfCancellationRequested(); - //skip element if is from a linked file and setting is off - if (!receiveLinkedModels && @base["isRevitLinkedModel"] != null && bool.Parse(@base["isRevitLinkedModel"].ToString())) - continue; + if (obj == null || @base == null) + return obj; - var convRes = converter.ConvertToNative(@base); - RefreshView(); + using var _d3 = LogContext.PushProperty("speckleType", @base.speckle_type); + transactionManager.StartSubtransaction(); - switch (convRes) - { - case ApplicationObject o: - obj.Update(status: o.Status, createdIds: o.CreatedIds, converted: o.Converted, log: o.Log); - progress.Report.UpdateReportObject(obj); - break; - default: - break; - } - } - catch (ConversionNotReadyException ex) - { - var notReadyDataCache = revitDocumentAggregateCache - .GetOrInitializeEmptyCacheOfType(out _); - var notReadyData = notReadyDataCache - .GetOrAdd(@base.id, () => new ConversionNotReadyCacheData(), out _); + try + { + var s = new CancellationTokenSource(); + DispatcherTimer.RunOnce(() => s.Cancel(), TimeSpan.FromMilliseconds(10)); + Dispatcher.UIThread.MainLoop(s.Token); - if (++notReadyData.NumberOfTimesCaught > 2) + ApplicationObject convRes; + if (converter.CanConvertToNative(@base)) + { + if (receiveDirectMesh) { - SpeckleLog.Logger.Warning(ex, $"Speckle object of type {@base.GetType()} was waiting for an object to convert that never did"); - obj.Update(status: ApplicationObject.State.Failed, logItem: ex.Message); - progress.Report.UpdateReportObject(obj); + convRes = converter.ConvertToNativeDisplayable(@base) as ApplicationObject; + if (convRes == null) + { + obj.Update(status: ApplicationObject.State.Failed, logItem: "Conversion returned null."); + return obj; + } } else { - Preview.Add(obj); + convRes = converter.ConvertToNative(@base) as ApplicationObject; + if (convRes == null || convRes.Status == ApplicationObject.State.Failed) + { + var logItem = + convRes == null + ? "Conversion returned null" + : "Conversion failed with errors: " + string.Join("/n", convRes.Log); + obj.Update(status: ApplicationObject.State.Failed, logItem: logItem); + return obj; + } + } + } + else if (converter.CanConvertToNativeDisplayable(@base)) + { + obj.Log.Add("No direct conversion exists. Converting displayable geometry."); + convRes = converter.ConvertToNativeDisplayable(@base) as ApplicationObject; + if (convRes == null) + { + obj.Update(status: ApplicationObject.State.Failed, logItem: "Conversion returned null."); + return obj; } - // the struct must be saved to the cache again or the "numberOfTimesCaught" increment will not persist - notReadyDataCache.Set(@base.id, notReadyData); } - catch (Exception ex) + else { - SpeckleLog.Logger.Warning(ex, "Failed to convert"); + obj.Update( + status: ApplicationObject.State.Skipped, + logItem: "No direct conversion or displayable values can be converted." + ); + return obj; + } + + obj.Update( + status: convRes.Status, + createdIds: convRes.CreatedIds, + converted: convRes.Converted, + log: convRes.Log + ); + + progress.Report.UpdateReportObject(obj); + RefreshView(); + transactionManager.CommitSubtransaction(); + } + catch (ConversionNotReadyException ex) + { + transactionManager.RollbackSubTransaction(); + var notReadyDataCache = + revitDocumentAggregateCache.GetOrInitializeEmptyCacheOfType(out _); + var notReadyData = notReadyDataCache.GetOrAdd(@base.id, () => new ConversionNotReadyCacheData(), out _); + + if (++notReadyData.NumberOfTimesCaught > 2) + { + SpeckleLog.Logger.Warning( + ex, + $"Speckle object of type {@base.GetType()} was waiting for an object to convert that never did" + ); obj.Update(status: ApplicationObject.State.Failed, logItem: ex.Message); progress.Report.UpdateReportObject(obj); } + else + { + Preview.Add(obj); + } + // the struct must be saved to the cache again or the "numberOfTimesCaught" increment will not persist + notReadyDataCache.Set(@base.id, notReadyData); + } + catch (Exception ex) + { + transactionManager.RollbackSubTransaction(); + SpeckleLog.Logger.Warning(ex, "Failed to convert due to unexpected error."); + obj.Update(status: ApplicationObject.State.Failed, logItem: "Failed to convert due to unexpected error."); + obj.Log.Add($"{ex.Message}"); + progress.Report.UpdateReportObject(obj); } - return convertedObjectsCache; + return obj; } private void RefreshView() @@ -310,26 +475,31 @@ private void RefreshView() /// A flattened list of objects to be converted ToNative private List FlattenCommitObject(Base obj, ISpeckleConverter converter) { - ApplicationObject CreateApplicationObject(Base current) { - if (!converter.CanConvertToNative(current)) return null; + // determine if this object is displayable + var isDisplayable = DefaultTraversal.displayValuePropAliases.Any(o => current[o] != null); + + // skip if this object was already stored, if it's not convertible and has no displayables + if (StoredObjects.ContainsKey(current.id)) + return null; + if (!converter.CanConvertToNative(current) && !isDisplayable) + return null; + // create application object and store var appObj = new ApplicationObject(current.id, ConnectorRevitUtils.SimplifySpeckleType(current.speckle_type)) { applicationId = current.applicationId, - Convertible = true + Convertible = converter.CanConvertToNative(current) }; - if (StoredObjects.ContainsKey(current.id)) - return null; - StoredObjects.Add(current.id, current); return appObj; } var traverseFunction = DefaultTraversal.CreateRevitTraversalFunc(converter); - var objectsToConvert = traverseFunction.Traverse(obj) + var objectsToConvert = traverseFunction + .Traverse(obj) .Select(tc => CreateApplicationObject(tc.current)) .Where(appObject => appObject != null) .Reverse() @@ -337,6 +507,5 @@ ApplicationObject CreateApplicationObject(Base current) return objectsToConvert; } - } } diff --git a/ConnectorRevit/ConnectorRevit/UI/ConnectorBindingsRevit.Selection.cs b/ConnectorRevit/ConnectorRevit/UI/ConnectorBindingsRevit.Selection.cs index 9cdeffbe34..a6ee20f194 100644 --- a/ConnectorRevit/ConnectorRevit/UI/ConnectorBindingsRevit.Selection.cs +++ b/ConnectorRevit/ConnectorRevit/UI/ConnectorBindingsRevit.Selection.cs @@ -168,31 +168,26 @@ public override void SelectClientObjects(List args, bool deselect = fals CurrentDoc.ShowElements(selection); } - private List GetLinkedDocuments() + private Dictionary GetLinkedDocuments() { - var docs = new List(); + var linkedDocs = new Dictionary(); // Get settings and return empty list if we should not send linked models var sendLinkedModels = CurrentSettings?.FirstOrDefault(x => x.Slug == "linkedmodels-send") as CheckBoxSetting; if (sendLinkedModels == null || !sendLinkedModels.IsChecked) - return docs; + return linkedDocs; - //TODO: is the name the most safe way to look for it? var linkedRVTs = new FilteredElementCollector(CurrentDoc.Document) .OfCategory(BuiltInCategory.OST_RvtLinks) - .OfClass(typeof(RevitLinkType)) + .OfClass(typeof(RevitLinkInstance)) .ToElements() - .Cast() - .Select(x => x.Name.Replace(".rvt", "")); - foreach (Document revitDoc in RevitApp.Application.Documents) + .Cast(); + foreach (var linkedRVT in linkedRVTs) { - if (revitDoc.IsLinked && linkedRVTs.Contains(revitDoc.Title)) - { - docs.Add(revitDoc); - } + linkedDocs.Add(linkedRVT.Id, linkedRVT.GetLinkDocument()); } - return docs; + return linkedDocs; } private static List FilterHiddenDesignOptions(List selection) @@ -231,9 +226,10 @@ private static List FilterHiddenDesignOptions(List selection) /// private List GetSelectionFilterObjects(ISpeckleConverter converter, ISelectionFilter filter) { - var currentDoc = CurrentDoc.Document; - var allDocs = GetLinkedDocuments(); - allDocs.Add(currentDoc); + var linkedDocs = GetLinkedDocuments(); + + var allDocs = new List { CurrentDoc.Document }; + allDocs.AddRange(linkedDocs.Values); var selection = new List(); try @@ -241,14 +237,14 @@ private List GetSelectionFilterObjects(ISpeckleConverter converter, ISe switch (filter.Slug) { case "manual": - return GetManualSelection(filter, allDocs); + return GetManualSelection(filter, linkedDocs); case "all": - selection = GetEverything(currentDoc, allDocs); + selection = GetEverything(linkedDocs); return FilterHiddenDesignOptions(selection); case "category": - selection = GetSelectionByCategory(filter, currentDoc, allDocs); + selection = GetSelectionByCategory(filter, allDocs); return FilterHiddenDesignOptions(selection); case "filter": @@ -256,8 +252,8 @@ private List GetSelectionFilterObjects(ISpeckleConverter converter, ISe return FilterHiddenDesignOptions(selection); case "view": - var selectedViews = GetSelectedViews(filter, currentDoc); - selection = GetSelectionFromViews(selectedViews, allDocs); + var selectedViews = GetSelectedViews(filter); + selection = GetSelectionByView(selectedViews, linkedDocs); if (selectedViews.Count == 1) { // if the user is sending a single view, then we pass it to the converter in order for the converter @@ -271,18 +267,15 @@ private List GetSelectionFilterObjects(ISpeckleConverter converter, ISe } case "schedule": - return GetScheduleSelection(filter, currentDoc); + return GetSelectionBySchedule(filter); case "project-info": - return GetSelectionByProjectInfo(filter, currentDoc); + return GetSelectionByProjectInfo(filter); case "workset": - selection = GetSelectionByWorkset(filter, currentDoc, allDocs); + selection = GetSelectionByWorkset(filter, allDocs); return FilterHiddenDesignOptions(selection); - case "param": - return GetSelectionByParameter(filter, allDocs, selection); - default: throw new SpeckleException($"Unknown ISelectionFilterSlug, {filter.Slug}"); } @@ -296,25 +289,23 @@ private List GetSelectionFilterObjects(ISpeckleConverter converter, ISe } } - private static List GetManualSelection(ISelectionFilter filter, List allDocs) + private static List GetManualSelection(ISelectionFilter filter, Dictionary linkedDocs) { var selection = filter.Selection.Select(x => CurrentDoc.Document.GetElement(x)).Where(x => x != null).ToList(); - var linkedFiles = selection.Where(x => x is RevitLinkInstance).Cast().ToList(); + var selectedLinkedFiles = selection.Where(x => x is RevitLinkInstance).Cast().ToList(); - foreach (var linkedFile in linkedFiles) + foreach (var selectedLinkedFile in selectedLinkedFiles) { - var match = allDocs.FirstOrDefault( - x => x.Title == linkedFile.Name.Split(new string[] { ".rvt" }, StringSplitOptions.None)[0] - ); - if (match != null) - selection.AddRange(match.GetSupportedElements(revitDocumentAggregateCache)); + if (linkedDocs.ContainsKey(selectedLinkedFile.Id)) + selection.AddRange(linkedDocs[selectedLinkedFile.Id].GetSupportedElements(revitDocumentAggregateCache)); } return selection; } - private static List GetEverything(Document currentDoc, List allDocs) + private static List GetEverything(Dictionary linkedDocs) { + var currentDoc = CurrentDoc.Document; var selection = new List(); //add these only for the current doc if (!currentDoc.IsFamilyDocument) @@ -336,21 +327,24 @@ private static List GetEverything(Document currentDoc, List a selection.AddRange(currentDoc.Views2D()); selection.AddRange(currentDoc.Views3D()); + // We specifically exclude `TableView` elements (Schedules) until schedule extraction has been improved for performance. + var elements = currentDoc.GetSupportedElements(revitDocumentAggregateCache).Where(e => e is not TableView); + selection.AddRange(elements); + selection.AddRange(currentDoc.GetSupportedTypes(revitDocumentAggregateCache)); + //and these for every linked doc - foreach (var doc in allDocs) + foreach (var linkedDoc in linkedDocs.Values) { - selection.AddRange(doc.GetSupportedElements(revitDocumentAggregateCache)); // includes levels - selection.AddRange(doc.GetSupportedTypes(revitDocumentAggregateCache)); + // We specifically exclude `TableView` elements (Schedules) until schedule extraction has been improved for performance. + var linkedElements = linkedDoc.GetSupportedElements(revitDocumentAggregateCache).Where(e => e is not TableView); + selection.AddRange(linkedElements); // includes levels + selection.AddRange(linkedDoc.GetSupportedTypes(revitDocumentAggregateCache)); } return selection; } - private List GetSelectionByCategory( - ISelectionFilter filter, - Document currentDoc, - List allDocs - ) + private List GetSelectionByCategory(ISelectionFilter filter, List allDocs) { var selection = new List(); var catFilter = filter as ListSelectionFilter; @@ -358,24 +352,20 @@ List allDocs foreach (var cat in catFilter.Selection) { - var revitCategory = revitDocumentAggregateCache - .GetOrInitializeWithDefaultFactory() - .TryGet(cat); - if (revitCategory == null) continue; + var revitCategory = revitDocumentAggregateCache.GetOrInitializeWithDefaultFactory().TryGet(cat); + if (revitCategory == null) + continue; catIds.Add(revitCategory.Id); } using var categoryFilter = new ElementMulticategoryFilter(catIds); + foreach (var doc in allDocs) { using var collector = new FilteredElementCollector(doc); selection.AddRange( - collector - .WhereElementIsNotElementType() - .WhereElementIsViewIndependent() - .WherePasses(categoryFilter) - .ToList() + collector.WhereElementIsNotElementType().WhereElementIsViewIndependent().WherePasses(categoryFilter).ToList() ); } return selection; @@ -429,42 +419,62 @@ private static List GetSelectionByFilter(ISelectionFilter filter, List< return selection; } - private static List GetSelectionFromViews( - List views, - List allDocs - ) + private static List GetSelectionByView(List views, Dictionary linkedDocs) { var selection = new List(); foreach (var view in views) { selection.Add(view); - var ids = selection.Select(x => x.UniqueId); - foreach (var doc in allDocs) + using var docCollector = new FilteredElementCollector(CurrentDoc.Document, view.Id); + selection.AddRange( + docCollector + .WhereElementIsNotElementType() + .WhereElementIsViewIndependent() + .Where(x => !selection.Any(s => s.UniqueId == x.UniqueId)) //exclude elements already added from other views + .ToList() + ); + + foreach (var linkedDoc in linkedDocs) { - //NOTE: this logic needs revisiting, this is just to avoid the error: https://github.com/specklesystems/speckle-sharp/issues/2829 - if (doc.GetElement(view.Id) == null) + if (linkedDoc.Value == null) + { continue; - - using var docCollector = new FilteredElementCollector(doc, view.Id); + } + //from Revit 2024 onward we can query linked docs + //for earlier versions we can't: https://github.com/specklesystems/speckle-sharp/issues/2829 +#if !REVIT2020 && !REVIT2021 && !REVIT2022 && !REVIT2023 + using var linkedDocCollector = new FilteredElementCollector(CurrentDoc.Document, view.Id, linkedDoc.Key); selection.AddRange( - docCollector + linkedDocCollector .WhereElementIsNotElementType() .WhereElementIsViewIndependent() //.Where(x => x.IsPhysicalElement()) - .Where(x => !ids.Contains(x.UniqueId)) //exclude elements already added from other views + .Where(x => !selection.Any(s => s.UniqueId == x.UniqueId)) //exclude elements already added from other views .ToList() ); + +#else + //check if linked doc is visible in main doc + var linkedObject = CurrentDoc.Document.GetElement(linkedDoc.Key); + if (linkedObject.IsHidden(view)) + continue; + + //get ALL the linked model objects + selection + .AddRange(linkedDoc.Value.GetSupportedElements(revitDocumentAggregateCache) + .Where(x => !selection.Any(s => s.UniqueId == x.UniqueId))); +#endif } } return selection; } - private static List GetSelectedViews(ISelectionFilter filter, Document currentDoc) + private static List GetSelectedViews(ISelectionFilter filter) { var selection = new List(); var viewFilter = filter as ListSelectionFilter; - using var collector = new FilteredElementCollector(currentDoc); + using var collector = new FilteredElementCollector(CurrentDoc.Document); using var scheduleExclusionFilter = new ElementClassFilter(typeof(ViewSchedule), true); return collector .WhereElementIsNotElementType() @@ -476,12 +486,12 @@ private static List GetSelectedViews(ISelectionFilter filter, Document cur .ToList(); } - private static List GetScheduleSelection(ISelectionFilter filter, Document currentDoc) + private static List GetSelectionBySchedule(ISelectionFilter filter) { var selection = new List(); var scheduleFilter = filter as ListSelectionFilter; - using var collector = new FilteredElementCollector(currentDoc); + using var collector = new FilteredElementCollector(CurrentDoc.Document); var schedules = collector .WhereElementIsNotElementType() .OfClass(typeof(ViewSchedule)) @@ -494,38 +504,34 @@ private static List GetScheduleSelection(ISelectionFilter filter, Docum return selection; } - private static List GetSelectionByProjectInfo(ISelectionFilter filter, Document currentDoc) + private static List GetSelectionByProjectInfo(ISelectionFilter filter) { var selection = new List(); var projectInfoFilter = filter as ListSelectionFilter; if (projectInfoFilter.Selection.Contains("Project Info")) - selection.Add(currentDoc.ProjectInformation); + selection.Add(CurrentDoc.Document.ProjectInformation); if (projectInfoFilter.Selection.Contains("Views 2D")) - selection.AddRange(currentDoc.Views2D()); + selection.AddRange(CurrentDoc.Document.Views2D()); if (projectInfoFilter.Selection.Contains("Views 3D")) - selection.AddRange(currentDoc.Views3D()); + selection.AddRange(CurrentDoc.Document.Views3D()); if (projectInfoFilter.Selection.Contains("Levels")) - selection.AddRange(currentDoc.Levels()); + selection.AddRange(CurrentDoc.Document.Levels()); if (projectInfoFilter.Selection.Contains("Families & Types")) - selection.AddRange(currentDoc.GetSupportedTypes(revitDocumentAggregateCache)); + selection.AddRange(CurrentDoc.Document.GetSupportedTypes(revitDocumentAggregateCache)); return selection; } - private static List GetSelectionByWorkset( - ISelectionFilter filter, - Document currentDoc, - List allDocs - ) + private static List GetSelectionByWorkset(ISelectionFilter filter, List allDocs) { var selection = new List(); var worksetFilter = filter as ListSelectionFilter; - var worksets = new FilteredWorksetCollector(currentDoc) + var worksets = new FilteredWorksetCollector(CurrentDoc.Document) .Where(x => worksetFilter.Selection.Contains(x.Name)) .Select(x => x.Id) .ToList(); @@ -545,74 +551,6 @@ List allDocs return selection; } - private static List GetSelectionByParameter( - ISelectionFilter filter, - List allDocs, - List selection - ) - { - try - { - foreach (var doc in allDocs) - { - var propFilter = filter as PropertySelectionFilter; - using var collector = new FilteredElementCollector(doc); - var query = collector - .WhereElementIsNotElementType() - .WhereElementIsNotElementType() - .WhereElementIsViewIndependent() - .Where(x => x.IsPhysicalElement()) - .Where(fi => fi.LookupParameter(propFilter.PropertyName) != null); - - propFilter.PropertyValue = propFilter.PropertyValue.ToLowerInvariant(); - - switch (propFilter.PropertyOperator) - { - case "equals": - query = query.Where( - fi => GetStringValue(fi.LookupParameter(propFilter.PropertyName)) == propFilter.PropertyValue - ); - break; - case "contains": - query = query.Where( - fi => GetStringValue(fi.LookupParameter(propFilter.PropertyName)).Contains(propFilter.PropertyValue) - ); - break; - case "is greater than": - query = query.Where( - fi => - RevitVersionHelper.ConvertFromInternalUnits( - fi.LookupParameter(propFilter.PropertyName).AsDouble(), - fi.LookupParameter(propFilter.PropertyName) - ) > double.Parse(propFilter.PropertyValue) - ); - break; - case "is less than": - query = query.Where( - fi => - RevitVersionHelper.ConvertFromInternalUnits( - fi.LookupParameter(propFilter.PropertyName).AsDouble(), - fi.LookupParameter(propFilter.PropertyName) - ) < double.Parse(propFilter.PropertyValue) - ); - break; - } - - selection.AddRange(query.ToList()); - } - } - catch (Exception ex) - { - SpeckleLog.Logger.Error( - ex, - "Swallowing exception in {methodName}: {exceptionMessage}", - nameof(GetSelectionFilterObjects), - ex.Message - ); - } - return selection; - } - private static string GetStringValue(Parameter p) { string value = ""; @@ -635,5 +573,35 @@ private static string GetStringValue(Parameter p) return p.AsString().ToLowerInvariant(); } } + + /// Processes the provided list of elements, applying specific validations and transformations based on the element type. + /// + /// A collection of elements to process. + /// + /// A collection of elements after applying the respective validations and transformations. + /// + /// + /// Current Validations and Transformations: + /// - Zones are expanded into their constituent spaces. + /// - [Add additional validations here as they are implemented.] + /// + private static IEnumerable HandleSelectedObjectDescendants(IEnumerable selectedObjects) + { + // Handle the resolution of selected Elements to their convertable states here + foreach (var element in selectedObjects) + { + switch (element) + { + case Autodesk.Revit.DB.Mechanical.Zone zone: + foreach (var space in zone.Spaces.OfType()) + yield return space; + break; + + default: + yield return element; + break; + } + } + } } } diff --git a/ConnectorRevit/ConnectorRevit/UI/ConnectorBindingsRevit.Send.cs b/ConnectorRevit/ConnectorRevit/UI/ConnectorBindingsRevit.Send.cs index 1fe351fe2a..b2bee23f29 100644 --- a/ConnectorRevit/ConnectorRevit/UI/ConnectorBindingsRevit.Send.cs +++ b/ConnectorRevit/ConnectorRevit/UI/ConnectorBindingsRevit.Send.cs @@ -34,6 +34,8 @@ public partial class ConnectorBindingsRevit /// StreamState passed by the UI public override async Task SendStream(StreamState state, ProgressViewModel progress) { + using var ctx = RevitConverterState.Push(); + //make sure to instance a new copy so all values are reset correctly var converter = (ISpeckleConverter)Activator.CreateInstance(Converter.GetType()); converter.SetContextDocument(CurrentDoc.Document); @@ -49,8 +51,15 @@ public override async Task SendStream(StreamState state, ProgressViewMod var streamId = state.StreamId; var client = state.Client; - var selectedObjects = GetSelectionFilterObjects(converter, state.Filter); - state.SelectedObjectIds = selectedObjects.Select(x => x.UniqueId).ToList(); + // The selectedObjects needs to be collected inside the Revit API context or else, in rare cases, + // the filteredElementCollectors will throw a "modification forbidden" exception. This can be reproduced + // by opening Snowdon towers in R24 and immediately sending the default 3D view from the landing page + var selectedObjects = await APIContext + .Run(_ => GetSelectionFilterObjects(converter, state.Filter)) + .ConfigureAwait(false); + + selectedObjects = HandleSelectedObjectDescendants(selectedObjects).ToList(); + state.SelectedObjectIds = selectedObjects.Select(x => x.UniqueId).Distinct().ToList(); if (!selectedObjects.Any()) throw new InvalidOperationException( @@ -67,7 +76,9 @@ public override async Task SendStream(StreamState state, ProgressViewMod if (converter is not IRevitCommitObjectBuilderExposer builderExposer) { - throw new Exception($"Converter {converter.Name} by {converter.Author} does not provide the necessary object, {nameof(IRevitCommitObjectBuilder)}, needed to build the Speckle commit object."); + throw new Exception( + $"Converter {converter.Name} by {converter.Author} does not provide the necessary object, {nameof(IRevitCommitObjectBuilder)}, needed to build the Speckle commit object." + ); } else { @@ -80,71 +91,72 @@ public override async Task SendStream(StreamState state, ProgressViewMod var conversionProgressDict = new ConcurrentDictionary { ["Conversion"] = 0 }; var convertedCount = 0; - await APIContext.Run(() => - { - using var _d0 = LogContext.PushProperty("converterName", converter.Name); - using var _d1 = LogContext.PushProperty("converterAuthor", converter.Author); - using var _d2 = LogContext.PushProperty("conversionDirection", nameof(ISpeckleConverter.ConvertToSpeckle)); - - foreach (var revitElement in selectedObjects) + await APIContext + .Run(() => { - if (progress.CancellationToken.IsCancellationRequested) - break; - - bool isAlreadyConverted = GetOrCreateApplicationObject( - revitElement, - converter.Report, - out ApplicationObject reportObj - ); - if (isAlreadyConverted) - continue; + using var _d0 = LogContext.PushProperty("converterName", converter.Name); + using var _d1 = LogContext.PushProperty("converterAuthor", converter.Author); + using var _d2 = LogContext.PushProperty("conversionDirection", nameof(ISpeckleConverter.ConvertToSpeckle)); - progress.Report.Log(reportObj); + foreach (var revitElement in selectedObjects) + { + if (progress.CancellationToken.IsCancellationRequested) + break; - //Add context to logger - using var _d3 = LogContext.PushProperty("elementType", revitElement.GetType()); - using var _d4 = LogContext.PushProperty("elementCategory", revitElement.Category?.Name); + bool isAlreadyConverted = GetOrCreateApplicationObject( + revitElement, + converter.Report, + out ApplicationObject reportObj + ); + if (isAlreadyConverted) + continue; - try - { - converter.Report.Log(reportObj); // Log object so converter can access + progress.Report.Log(reportObj); - Base result = ConvertToSpeckle(revitElement, converter); + //Add context to logger + using var _d3 = LogContext.PushProperty("elementType", revitElement.GetType()); + using var _d4 = LogContext.PushProperty("elementCategory", revitElement.Category?.Name); - reportObj.Update( - status: ApplicationObject.State.Created, - logItem: $"Sent as {ConnectorRevitUtils.SimplifySpeckleType(result.speckle_type)}" - ); - if (result.applicationId != reportObj.applicationId) + try { - SpeckleLog.Logger.Information( - "Conversion result of type {elementType} has a different application Id ({actualId}) to the report object {expectedId}", - revitElement.GetType(), - result.applicationId, - reportObj.applicationId + converter.Report.Log(reportObj); // Log object so converter can access + + Base result = ConvertToSpeckle(revitElement, converter); + + reportObj.Update( + status: ApplicationObject.State.Created, + logItem: $"Sent as {ConnectorRevitUtils.SimplifySpeckleType(result.speckle_type)}" ); - result.applicationId = reportObj.applicationId; + if (result.applicationId != reportObj.applicationId) + { + SpeckleLog.Logger.Information( + "Conversion result of type {elementType} has a different application Id ({actualId}) to the report object {expectedId}", + revitElement.GetType(), + result.applicationId, + reportObj.applicationId + ); + result.applicationId = reportObj.applicationId; + } + commitObjectBuilder.IncludeObject(result, revitElement); + convertedCount++; + } + catch (ConversionSkippedException ex) + { + reportObj.Update(status: ApplicationObject.State.Skipped, logItem: ex.Message); + } + catch (Exception ex) + { + SpeckleLog.Logger.Error(ex, "Object failed during conversion"); + reportObj.Update(status: ApplicationObject.State.Failed, logItem: $"{ex.Message}"); } - commitObjectBuilder.IncludeObject(result, revitElement); - convertedCount++; - } - catch (ConversionSkippedException ex) - { - reportObj.Update(status: ApplicationObject.State.Skipped, logItem: ex.Message); - } - catch (Exception ex) - { - SpeckleLog.Logger.Error(ex, "Object failed during conversion"); - reportObj.Update(status: ApplicationObject.State.Failed, logItem: $"{ex.Message}"); - } - conversionProgressDict["Conversion"]++; - progress.Update(conversionProgressDict); + conversionProgressDict["Conversion"]++; + progress.Update(conversionProgressDict); - YieldToUIThread(TimeSpan.FromMilliseconds(1)); - } - }) - .ConfigureAwait(false); + YieldToUIThread(TimeSpan.FromMilliseconds(1)); + } + }) + .ConfigureAwait(false); revitDocumentAggregateCache.InvalidateAll(); @@ -213,6 +225,7 @@ out ApplicationObject reportObj } private DateTime timerStarted = DateTime.MinValue; + private void YieldToUIThread(TimeSpan delay) { var currentTime = DateTime.UtcNow; @@ -243,7 +256,9 @@ private static Base ConvertToSpeckle(Element revitElement, ISpeckleConverter con Base conversionResult = converter.ConvertToSpeckle(revitElement); if (conversionResult == null) - throw new SpeckleException($"Conversion of {revitElement.UniqueId} (ToSpeckle) returned null"); + throw new SpeckleException( + $"Conversion of {revitElement.GetType().Name} with id {revitElement.Id} (ToSpeckle) returned null" + ); return conversionResult; } diff --git a/ConnectorRevit/ConnectorRevit/UI/ConnectorBindingsRevit.Settings.cs b/ConnectorRevit/ConnectorRevit/UI/ConnectorBindingsRevit.Settings.cs new file mode 100644 index 0000000000..b7c44df354 --- /dev/null +++ b/ConnectorRevit/ConnectorRevit/UI/ConnectorBindingsRevit.Settings.cs @@ -0,0 +1,126 @@ +using System.Collections.Generic; +using System.Linq; +using Autodesk.Revit.DB; +using DesktopUI2.Models.Settings; + +namespace Speckle.ConnectorRevit.UI +{ + public partial class ConnectorBindingsRevit + { + // CAUTION: these strings need to have the same values as in the converter + const string InternalOrigin = "Internal Origin (default)"; + const string ProjectBase = "Project Base"; + const string Survey = "Survey"; + + const string defaultValue = "Default"; + const string dxf = "DXF"; + const string familyDxf = "Family DXF"; + + const string StructuralFraming = "Structural Framing"; + const string StructuralWalls = "Structural Walls"; + const string ArchitecturalWalls = "Achitectural Walls"; + + public const string noMapping = "Never"; + public const string everyReceive = "Always"; + public const string forNewTypes = "For New Types"; + + public const string DsFallbackSlug = "direct-shape-strategy"; + public const string DsFallbackOnError = "On Error"; + public const string DsFallbackAways = "Always"; + public const string DsFallbackNever = "Never"; + + public override List GetSettings() + { + List referencePoints = new List() { InternalOrigin }; + List prettyMeshOptions = new List() { defaultValue, dxf, familyDxf }; + List mappingOptions = new List() { noMapping, everyReceive, forNewTypes }; + + // find project base point and survey point. these don't always have name props, so store them under custom strings + var basePoint = new FilteredElementCollector(CurrentDoc.Document) + .OfClass(typeof(BasePoint)) + .Cast() + .FirstOrDefault(o => !o.IsShared); + if (basePoint != null) + referencePoints.Add(ProjectBase); + var surveyPoint = new FilteredElementCollector(CurrentDoc.Document) + .OfClass(typeof(BasePoint)) + .Cast() + .FirstOrDefault(o => o.IsShared); + if (surveyPoint != null) + referencePoints.Add(Survey); + + return new List + { + new ListBoxSetting + { + Slug = "reference-point", + Name = "Reference Point", + Icon = "LocationSearching", + Values = referencePoints, + Selection = InternalOrigin, + Description = "Sends or receives stream objects in relation to this document point" + }, + new CheckBoxSetting + { + Slug = "linkedmodels-send", + Name = "Send Linked Models", + Icon = "Link", + IsChecked = false, + Description = "Include Linked Models in the selection filters when sending" + }, + new CheckBoxSetting + { + Slug = "linkedmodels-receive", + Name = "Receive Linked Models", + Icon = "Link", + IsChecked = false, + Description = + "Include Linked Models when receiving NOTE: elements from linked models will be received in the current document" + }, + // new CheckBoxSetting + // { + // Slug = "recieve-objects-mesh", + // Name = "Receive Objects as DirectShape", + // Icon = "Link", + // IsChecked = false, + // Description = "Receive the stream as a Meshes only" + // }, + new ListBoxSetting + { + Slug = DsFallbackSlug, + Name = "Fallback to DirectShape on receive", + Icon = "Link", + Values = new List { DsFallbackAways, DsFallbackOnError, DsFallbackNever }, + Selection = DsFallbackOnError, + Description = "Determines when to fallback to DirectShape on receive.\n\nAways: all objects will be received as DirectShapes\nOn Error: only objects that fail or whose types are missing\nNever: disables the fallback behavior" + }, + new MultiSelectBoxSetting + { + Slug = "disallow-join", + Name = "Disallow Join For Elements", + Icon = "CallSplit", + Description = "Determines which objects should not be allowed to join by default when receiving", + Values = new List() { ArchitecturalWalls, StructuralWalls, StructuralFraming } + }, + new ListBoxSetting + { + Slug = "pretty-mesh", + Name = "Mesh Import Method", + Icon = "ChartTimelineVarient", + Values = prettyMeshOptions, + Selection = defaultValue, + Description = "Determines the display style of imported meshes" + }, + new MappingSetting + { + Slug = "receive-mappings", + Name = "Missing Type Mapping", + Icon = "LocationSearching", + Values = mappingOptions, + Selection = forNewTypes, + Description = "Determines when the missing types dialog is shown\n\nNever: the dialog is never shown\nAlways: the dialog is always shown, useful to edit existing mappings\nFor New Types: the dialog is only shown if there are new unmapped types\n\nNOTE: no dialog is shown if Fallback to DirectShape is set to Always" + }, + }; + } + } +} diff --git a/ConnectorRevit/ConnectorRevit2022/ConnectorRevit2022.csproj b/ConnectorRevit/ConnectorRevit2022/ConnectorRevit2022.csproj index 639ce9b0e4..dcedeef4cb 100644 --- a/ConnectorRevit/ConnectorRevit2022/ConnectorRevit2022.csproj +++ b/ConnectorRevit/ConnectorRevit2022/ConnectorRevit2022.csproj @@ -39,7 +39,7 @@ - + diff --git a/ConnectorRevit/ConnectorRevit/Revit/ErrorEater.cs b/ConnectorRevit/RevitSharedResources/Models/ErrorEater.cs similarity index 94% rename from ConnectorRevit/ConnectorRevit/Revit/ErrorEater.cs rename to ConnectorRevit/RevitSharedResources/Models/ErrorEater.cs index 679be91ea6..5475e60232 100644 --- a/ConnectorRevit/ConnectorRevit/Revit/ErrorEater.cs +++ b/ConnectorRevit/RevitSharedResources/Models/ErrorEater.cs @@ -5,19 +5,13 @@ using Speckle.Core.Kits; using Speckle.Core.Logging; -namespace ConnectorRevit.Revit +namespace RevitSharedResources.Models { public class ErrorEater : IFailuresPreprocessor { - private ISpeckleConverter _converter; private List _exceptions = new(); public Dictionary CommitErrorsDict = new(); - public ErrorEater(ISpeckleConverter converter) - { - _converter = converter; - } - public FailureProcessingResult PreprocessFailures(FailuresAccessor failuresAccessor) { var resolvedFailures = 0; diff --git a/ConnectorRevit/RevitSharedResources/Models/RevitConverterState.cs b/ConnectorRevit/RevitSharedResources/Models/RevitConverterState.cs new file mode 100644 index 0000000000..ca50de316a --- /dev/null +++ b/ConnectorRevit/RevitSharedResources/Models/RevitConverterState.cs @@ -0,0 +1,10 @@ +#nullable enable +using Autodesk.Revit.DB; +using Speckle.Core.Helpers; + +namespace RevitSharedResources.Models; + +public class RevitConverterState : State +{ + public Element? CurrentHostElement { get; set; } +} diff --git a/ConnectorRevit/RevitSharedResources/Models/TransactionManager.cs b/ConnectorRevit/RevitSharedResources/Models/TransactionManager.cs new file mode 100644 index 0000000000..fad2aad1a3 --- /dev/null +++ b/ConnectorRevit/RevitSharedResources/Models/TransactionManager.cs @@ -0,0 +1,254 @@ +using System; +using System.Linq; +using Autodesk.Revit.DB; +using RevitSharedResources.Interfaces; +using Speckle.Core.Logging; + +namespace RevitSharedResources.Models +{ + /// + /// Is responsible for all functionality regarding subtransactions, transactions, and transaction groups. + /// This includes starting, pausing, committing, and rolling back transactions + /// + public class TransactionManager : IDisposable + { + private string streamId; + private Document document; + + public TransactionManager(string streamId, Document document) + { + this.streamId = streamId; + this.document = document; + } + + private ErrorEater errorEater; + private bool isDisposed; + private TransactionGroup transactionGroup; + private Transaction transaction; + private SubTransaction subTransaction; + + public void Finish() + { + try + { + Commit(); + } + finally + { + if (transactionGroup.GetStatus() == TransactionStatus.Started) + { + transactionGroup.Assimilate(); + } + transactionGroup?.Dispose(); + } + } + + public void Start() + { + if (transactionGroup == null) + { + transactionGroup = new TransactionGroup(document, $"Baking stream {streamId}"); + transactionGroup.Start(); + } + + if (transaction == null || !transaction.IsValidObject || transaction.GetStatus() != TransactionStatus.Started) + { + transaction = new Transaction(document, $"Baking stream {streamId}"); + var failOpts = transaction.GetFailureHandlingOptions(); + errorEater = new ErrorEater(); + failOpts.SetFailuresPreprocessor(errorEater); + failOpts.SetClearAfterRollback(true); + transaction.SetFailureHandlingOptions(failOpts); + transaction.Start(); + } + } + + public TransactionStatus Commit() + { + if ( + subTransaction != null + && subTransaction.IsValidObject + && subTransaction.GetStatus() == TransactionStatus.Started + ) + { + HandleFailedCommit(subTransaction.Commit()); + subTransaction.Dispose(); + } + if (transaction != null && transaction.IsValidObject && transaction.GetStatus() == TransactionStatus.Started) + { + var status = transaction.Commit(); + HandleFailedCommit(status); + transaction.Dispose(); + return status; + } + return TransactionStatus.Uninitialized; + } + + private void HandleFailedCommit(TransactionStatus status) + { + if (status == TransactionStatus.RolledBack) + { + var numTotalErrors = errorEater.CommitErrorsDict.Sum(kvp => kvp.Value); + var numUniqueErrors = errorEater.CommitErrorsDict.Keys.Count; + + var exception = errorEater.GetException(); + if (exception == null) + SpeckleLog.Logger.Fatal( + "Revit commit failed with {numUniqueErrors} unique errors and {numTotalErrors} total errors, but the ErrorEater did not capture any exceptions", + numUniqueErrors, + numTotalErrors + ); + else + SpeckleLog.Logger.Fatal( + exception, + "The Revit API could not resolve {numUniqueErrors} unique errors and {numTotalErrors} total errors when trying to commit the Speckle model. The whole transaction is being rolled back.", + numUniqueErrors, + numTotalErrors + ); + + throw exception + ?? new SpeckleException( + $"The Revit API could not resolve {numUniqueErrors} unique errors and {numTotalErrors} total errors when trying to commit the Speckle model. The whole transaction is being rolled back." + ); + } + } + + public void RollbackTransaction() + { + RollbackSubTransaction(); + if (transaction != null && transaction.IsValidObject && transaction.GetStatus() == TransactionStatus.Started) + { + transaction.RollBack(); + } + } + + public void RollbackSubTransaction() + { + if ( + subTransaction != null + && subTransaction.IsValidObject + && subTransaction.GetStatus() == TransactionStatus.Started + ) + { + subTransaction.RollBack(); + } + } + + public void RollbackAll() + { + RollbackTransaction(); + if ( + transactionGroup != null + && transactionGroup.IsValidObject + && transactionGroup.GetStatus() == TransactionStatus.Started + ) + { + transactionGroup.Assimilate(); + } + } + + public void StartSubtransaction() + { + Start(); + if ( + subTransaction == null + || !subTransaction.IsValidObject + || subTransaction.GetStatus() != TransactionStatus.Started + ) + { + subTransaction = new SubTransaction(document); + subTransaction.Start(); + } + } + + public TransactionStatus CommitSubtransaction() + { + if (subTransaction != null && subTransaction.IsValidObject) + { + var status = subTransaction.Commit(); + HandleFailedCommit(status); + subTransaction.Dispose(); + return status; + } + return TransactionStatus.Uninitialized; + } + + public TResult ExecuteInTemporaryTransaction(Func function) + { + return ExecuteInTemporaryTransaction(function, document); + } + + public static TResult ExecuteInTemporaryTransaction(Func function, Document document) + { + TResult result = default; + if (!document.IsModifiable) + { + using var t = new Transaction(document, "This Transaction Will Never Get Committed"); + try + { + t.Start(); + result = function(); + } + catch + { + // ignore because we're just going to rollback + } + finally + { + t.RollBack(); + } + } + else + { + using var t = new SubTransaction(document); + try + { + t.Start(); + result = function(); + } + catch + { + // ignore because we're just going to rollback + } + finally + { + t.RollBack(); + } + } + + return result; + } + + #region disposal + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (isDisposed) + return; + + if (disposing) + { + // free managed resources + if (subTransaction != null && subTransaction.IsValidObject) + subTransaction.Dispose(); + if (transaction != null && transaction.IsValidObject) + transaction.Dispose(); + if (transactionGroup != null && transactionGroup.IsValidObject) + transactionGroup.Dispose(); + } + + isDisposed = true; + } + + ~TransactionManager() + { + Dispose(false); + } + #endregion + } +} diff --git a/ConnectorRevit/RevitSharedResources/RevitSharedResources.projitems b/ConnectorRevit/RevitSharedResources/RevitSharedResources.projitems index 37e74676a9..4f392cebd7 100644 --- a/ConnectorRevit/RevitSharedResources/RevitSharedResources.projitems +++ b/ConnectorRevit/RevitSharedResources/RevitSharedResources.projitems @@ -1,4 +1,4 @@ - + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) @@ -26,8 +26,8 @@ - - - + + + \ No newline at end of file diff --git a/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/ConnectorRhinoShared.projitems b/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/ConnectorRhinoShared.projitems index a3cd4baa53..5169402b04 100644 --- a/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/ConnectorRhinoShared.projitems +++ b/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/ConnectorRhinoShared.projitems @@ -17,6 +17,7 @@ True True + diff --git a/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/ConnectorBindingsRhino.Previews.cs b/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/ConnectorBindingsRhino.Previews.cs index fa073565d1..fda4f2f307 100644 --- a/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/ConnectorBindingsRhino.Previews.cs +++ b/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/ConnectorBindingsRhino.Previews.cs @@ -88,6 +88,7 @@ private static bool IsPreviewIgnore(Base @object) { return @object.speckle_type.Contains("Instance") || @object.speckle_type.Contains("View") + || @object.speckle_type.Contains("Level") || @object.speckle_type.Contains("Collection"); } @@ -103,6 +104,11 @@ public override async Task PreviewReceive(StreamState state, Progre var converter = KitManager.GetDefaultKit().LoadConverter(Utils.RhinoAppName); converter.SetContextDocument(Doc); + // set converter settings + CurrentSettings = state.Settings; + var settings = GetSettingsDict(CurrentSettings); + converter.SetConverterSettings(settings); + var commitObject = await ConnectorHelpers.ReceiveCommit(commit, state, progress); SelectedReceiveCommit = commit.id; diff --git a/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/ConnectorBindingsRhino.Receive.cs b/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/ConnectorBindingsRhino.Receive.cs index 73d4d09906..bf808fad9a 100644 --- a/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/ConnectorBindingsRhino.Receive.cs +++ b/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/ConnectorBindingsRhino.Receive.cs @@ -36,10 +36,16 @@ public override async Task ReceiveStream(StreamState state, Progres converter.SetContextDocument(Doc); converter.ReceiveMode = state.ReceiveMode; + // set converter settings + bool settingsChanged = CurrentSettings != state.Settings; + CurrentSettings = state.Settings; + var settings = GetSettingsDict(CurrentSettings); + converter.SetConverterSettings(settings); + Commit commit = await ConnectorHelpers.GetCommitFromState(state, progress.CancellationToken); state.LastCommit = commit; - if (SelectedReceiveCommit != commit.id) + if (SelectedReceiveCommit != commit.id || settingsChanged) // clear storage if receiving a new commit, or if settings have changed!! { ClearStorage(); SelectedReceiveCommit = commit.id; @@ -64,7 +70,6 @@ public override async Task ReceiveStream(StreamState state, Progres () => { RhinoDoc.ActiveDoc.Notes += "%%%" + commitLayerName; // give converter a way to access commit layer info - // create preview objects if they don't already exist if (Preview.Count == 0) { @@ -138,7 +143,7 @@ public override async Task ReceiveStream(StreamState state, Progres var containers = Preview .Select(o => o.Container) .Distinct() - .ToList() + .Where(o => !string.IsNullOrEmpty(o)) .OrderBy(path => path.Count(c => c == ':')) .ToList(); // if on create mode, make sure the parent commit layer is created first @@ -207,16 +212,27 @@ public override async Task ReceiveStream(StreamState state, Progres if (state.ReceiveMode == ReceiveMode.Update) { toRemove = GetObjectsByApplicationId(previewObj.applicationId); - toRemove.ForEach(o => Doc.Objects.Delete(o)); + toRemove.ForEach(o => Doc.Objects.Delete(o, false, true)); - if (!toRemove.Any()) // if no rhinoobjects were found, this could've been a view + if (!toRemove.Any()) // if no rhinoobjects were found, this could've been a view or level (named construction plane) { - var viewId = Doc.NamedViews.FindByName(previewObj.applicationId); + // Check converter (ViewToNative and LevelToNative) to make sure these names correspond! + var name = + state.ReceiveMode == ReceiveMode.Create + ? $"{commitLayerName} - {StoredObjects[previewObj.OriginalId]["name"] as string}" + : StoredObjects[previewObj.OriginalId]["name"] as string; + var viewId = Doc.NamedViews.FindByName(name); + var planeId = Doc.NamedConstructionPlanes.Find(name); if (viewId != -1) { isUpdate = true; Doc.NamedViews.Delete(viewId); } + else if (planeId != -1) + { + isUpdate = true; + Doc.NamedConstructionPlanes.Delete(planeId); + } } } if (toRemove.Count() > 0) @@ -282,9 +298,7 @@ private List GetObjectsByApplicationId(string applicationId) return new List(); // first try to find the object by app id user string - var match = - Doc.Objects.Where(o => o.Attributes.GetUserString(ApplicationIdKey) == applicationId)?.ToList() - ?? new List(); + var match = Doc.Objects.FindByUserString(ApplicationIdKey, applicationId, true).ToList(); // if nothing is found, look for the geom obj by its guid directly if (!match.Any()) @@ -309,9 +323,7 @@ private List FlattenCommitObject(Base obj, ISpeckleConverter { void StoreObject(Base @base, ApplicationObject appObj, Base parameters = null) { - if (StoredObjects.ContainsKey(@base.id)) - appObj.Update(logItem: "Found another object in this commit with the same id. Skipped other object"); //TODO check if we are actually ignoring duplicates, since we are returning the app object anyway... - else + if (!StoredObjects.ContainsKey(@base.id)) StoredObjects.Add(@base.id, @base); if (parameters != null && !StoredObjectParams.ContainsKey(@base.id)) @@ -323,8 +335,9 @@ ApplicationObject CreateApplicationObject(Base current, string containerId) ApplicationObject NewAppObj() { var speckleType = current.speckle_type - .Split(new[] { ':' }, StringSplitOptions.RemoveEmptyEntries) + .Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries) .LastOrDefault(); + return new ApplicationObject(current.id, speckleType) { applicationId = current.applicationId, @@ -336,18 +349,23 @@ ApplicationObject NewAppObj() if (current is Collection && string.IsNullOrEmpty(containerId)) return null; + // get parameters + var parameters = current["parameters"] as Base; + //Handle convertable objects if (converter.CanConvertToNative(current)) { var appObj = NewAppObj(); appObj.Convertible = true; - StoreObject(current, appObj); + StoreObject(current, appObj, parameters); return appObj; } //Handle objects convertable using displayValues - var fallbackMember = current["displayValue"] ?? current["@displayValue"]; - var parameters = current["parameters"] as Base; + var fallbackMember = DefaultTraversal.displayValuePropAliases + .Where(o => current[o] != null) + ?.Select(o => current[o]) + ?.FirstOrDefault(); if (fallbackMember != null) { var appObj = NewAppObj(); @@ -369,11 +387,28 @@ StringBuilder LayerIdRecurse(TraversalContext context, StringBuilder stringBuild if (context.propName == null) return stringBuilder; // this was probably the base commit collection + // handle elements hosting case from Revit + // WARNING: THIS IS REVIT-SPECIFIC CODE!! + // We are checking for the `Category` prop on children objects to use as the layer + if ( + DefaultTraversal.elementsPropAliases.Contains(context.propName) + && context.parent.current is not Collection + && !string.IsNullOrEmpty((string)context.current["category"]) + ) + { + stringBuilder.Append((string)context.current["category"]); + return stringBuilder; + } + string objectLayerName = string.Empty; - if (context.propName.ToLower() == "elements" && context.current is Collection coll) + + // handle collections case + if (context.current is Collection coll && DefaultTraversal.elementsPropAliases.Contains(context.propName)) objectLayerName = coll.name; - else if (context.propName.ToLower() != "elements") // this is for any other property on the collection. skip elements props in layer structure. + // handle default case + else if (!DefaultTraversal.elementsPropAliases.Contains(context.propName)) objectLayerName = context.propName[0] == '@' ? context.propName.Substring(1) : context.propName; + LayerIdRecurse(context.parent, stringBuilder); if (stringBuilder.Length != 0 && !string.IsNullOrEmpty(objectLayerName)) stringBuilder.Append(Layer.PathSeparator); @@ -461,6 +496,25 @@ private void BakeObject( // handle user info, including application id SetUserInfo(obj, attributes, parent); + // add revit parameters as user strings + var paramId = parent != null ? parent.OriginalId : obj.id; + if (StoredObjectParams.ContainsKey(paramId)) + { + var parameters = StoredObjectParams[paramId]; + foreach (var member in parameters.GetMembers(DynamicBaseMemberType.Dynamic)) + { + if (member.Value is Base parameter) + { + var convertedParameter = converter.ConvertToNative(parameter) as Tuple; + if (convertedParameter is not null) + { + var name = $"{convertedParameter.Item1}({member.Key})"; + attributes.SetUserString(name, convertedParameter.Item2); + } + } + } + } + Guid id = Doc.Objects.Add(o, attributes); if (id == Guid.Empty) { @@ -491,23 +545,25 @@ private void BakeObject( } } break; - case RhinoObject o: // this was prbly a block instance, baked during conversion + case RhinoObject o: // this was prbly a block instance, baked during conversion o.Attributes.LayerIndex = layer.Index; // assign layer SetUserInfo(obj, o.Attributes, parent); // handle user info, including application id o.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 - if (parent != null) - parent.Update(o.Name); - else - appObj.Update(o.Name); + appObj.Update(o.Name); + bakedCount++; + break; + + case ConstructionPlane o: // this is a level, baked during conversion + appObj.Update(o.Name); bakedCount++; break; } @@ -536,20 +592,6 @@ private void SetUserInfo(Base obj, ObjectAttributes attributes, ApplicationObjec } catch { } - // set parameters - if (parent != null) - if (StoredObjectParams.ContainsKey(parent.OriginalId)) - { - var parameters = StoredObjectParams[parent.OriginalId]; - foreach (var member in parameters.GetMembers(DynamicBaseMemberType.Dynamic)) - if (member.Value is Base parameter) - try - { - attributes.SetUserString(member.Key, GetStringFromBaseProp(parameter, "value")); - } - catch { } - } - // set user dictionaries if (obj[UserDictionary] is Base userDictionary) ParseDictionaryToArchivable(attributes.UserDictionary, userDictionary); @@ -622,23 +664,4 @@ private void ParseDictionaryToArchivable(ArchivableDictionary target, Base @base } } } - - private string GetStringFromBaseProp(Base @base, string propName) - { - var val = @base[propName]; - if (val == null) - return null; - switch (val) - { - case double o: - return o.ToString(); - case bool o: - return o.ToString(); - case int o: - return o.ToString(); - case string o: - return o; - } - return null; - } } diff --git a/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/ConnectorBindingsRhino.Send.cs b/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/ConnectorBindingsRhino.Send.cs index 8e0fa642ee..625157eacd 100644 --- a/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/ConnectorBindingsRhino.Send.cs +++ b/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/ConnectorBindingsRhino.Send.cs @@ -24,6 +24,11 @@ public override async Task SendStream(StreamState state, ProgressViewMod var converter = KitManager.GetDefaultKit().LoadConverter(Utils.RhinoAppName); converter.SetContextDocument(Doc); + // set converter settings + CurrentSettings = state.Settings; + var settings = GetSettingsDict(CurrentSettings); + converter.SetConverterSettings(settings); + var streamId = state.StreamId; var client = state.Client; diff --git a/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/ConnectorBindingsRhino.Settings.cs b/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/ConnectorBindingsRhino.Settings.cs new file mode 100644 index 0000000000..694aa2b3fa --- /dev/null +++ b/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/ConnectorBindingsRhino.Settings.cs @@ -0,0 +1,43 @@ +using System.Collections.Generic; +using DesktopUI2; +using DesktopUI2.Models.Settings; +using Rhino; +using Rhino.DocObjects.Tables; + +namespace SpeckleRhino; + +public partial class ConnectorBindingsRhino : ConnectorBindings +{ + const string defaultValue = "Default"; + const string mergeCoplanar = "Merge Coplanar Faces"; + + public override List GetSettings() + { + List meshImportOptions = new List() { defaultValue, mergeCoplanar }; + + return new List + { + new ListBoxSetting + { + Slug = "receive-mesh", + Name = "Mesh Import Method", + Icon = "ChartTimelineVarient", + Values = meshImportOptions, + Selection = defaultValue, + Description = "Determines the method of importing meshes" + } + }; + } + + /// + /// Converts the settings to (setting slug, setting selection) key value pairs + /// + /// + public Dictionary GetSettingsDict(List? currentSettings) + { + var settings = new Dictionary(); + foreach (var setting in currentSettings) + settings.Add(setting.Slug, setting.Selection); + return settings; + } +} diff --git a/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/ConnectorBindingsRhino.cs b/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/ConnectorBindingsRhino.cs index 9bba54181a..8e1201f74b 100644 --- a/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/ConnectorBindingsRhino.cs +++ b/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/ConnectorBindingsRhino.cs @@ -37,6 +37,9 @@ public override List GetReceiveModes() return new List { ReceiveMode.Update, ReceiveMode.Create }; } + // used to store the Stream State settings when sending/receiving + private List? CurrentSettings { get; set; } + #region Local streams I/O with local file public override List GetStreamsInFile() @@ -116,19 +119,6 @@ private void LogUnsupportedObjects(List objs, ISpeckleConverter con RhinoApp.WriteLine($"{entry.Value} of type {entry.Key}"); } - public override List GetSettings() - { - /* - var referencePoints = new List() { "Internal Origin (default)" }; - referencePoints.AddRange(Doc.NamedConstructionPlanes.Select(o => o.Name).ToList()); - return new List() - { - new ListBoxSetting {Slug = "reference-point", Name = "Reference Point", Icon ="LocationSearching", Values = referencePoints, Description = "Receives stream objects in relation to this document point"} - }; - */ - return new List(); - } - public override void ResetDocument() { if (PreviewConduit != null) diff --git a/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/MappingBindingsRhino.cs b/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/MappingBindingsRhino.cs index bd95b91b05..fe2319801a 100644 --- a/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/MappingBindingsRhino.cs +++ b/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/MappingBindingsRhino.cs @@ -73,7 +73,10 @@ private List GetObjectSchemas(RhinoObject obj) result.Add(existingSchema); if (obj is InstanceObject) + { + result.Add(new BlockDefinitionViewModel()); result.Add(new RevitFamilyInstanceViewModel()); + } else switch (obj.Geometry) { @@ -201,6 +204,10 @@ bool HasPlanarBottomEdge(Brep brp) { schemas.Add(new RevitFloorViewModel()); schemas.Add(new RevitDefaultFloorViewModel()); + schemas.Add(new RevitCeilingViewModel()); + schemas.Add(new RevitDefaultCeilingViewModel()); + schemas.Add(new RevitFootprintRoofViewModel()); + schemas.Add(new RevitDefaultRoofViewModel()); } else if (isVertical) { diff --git a/ConnectorRhino/ConnectorRhino7/ConnectorRhino7.csproj b/ConnectorRhino/ConnectorRhino7/ConnectorRhino7.csproj index 8eb0f3ade2..c7425b783b 100644 --- a/ConnectorRhino/ConnectorRhino7/ConnectorRhino7.csproj +++ b/ConnectorRhino/ConnectorRhino7/ConnectorRhino7.csproj @@ -36,40 +36,32 @@ Program - + - + - - - + + + - - - + + + - - + + - - + + \ No newline at end of file diff --git a/ConnectorTeklaStructures/ConnectorTeklaStructuresShared/MainForm.cs b/ConnectorTeklaStructures/ConnectorTeklaStructuresShared/MainForm.cs index 845f8075de..a66ede69eb 100644 --- a/ConnectorTeklaStructures/ConnectorTeklaStructuresShared/MainForm.cs +++ b/ConnectorTeklaStructures/ConnectorTeklaStructuresShared/MainForm.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -21,6 +22,7 @@ public partial class MainForm : PluginFormBase //window owner call [DllImport("user32.dll", SetLastError = true)] + [SuppressMessage("Security", "CA5392:Use DefaultDllImportSearchPaths attribute for P/Invokes")] static extern IntPtr SetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr value); const int GWL_HWNDPARENT = -8; diff --git a/Core/Core/Api/GraphQL/Client.cs b/Core/Core/Api/GraphQL/Client.cs index 03e7f2b787..26f6da51f9 100644 --- a/Core/Core/Api/GraphQL/Client.cs +++ b/Core/Core/Api/GraphQL/Client.cs @@ -35,7 +35,7 @@ public Client(Account account) Account = account; - HttpClient = Http.GetHttpProxyClient(); + HttpClient = Http.GetHttpProxyClient(null, TimeSpan.FromSeconds(30)); Http.AddAuthHeader(HttpClient, account.token); HttpClient.DefaultRequestHeaders.Add("apollographql-client-name", Setup.HostApplication); diff --git a/Core/Core/Core.csproj b/Core/Core/Core.csproj index ae76e567cf..7ee14fe173 100644 --- a/Core/Core/Core.csproj +++ b/Core/Core/Core.csproj @@ -43,6 +43,7 @@ + diff --git a/Core/Core/Credentials/AccountManager.cs b/Core/Core/Credentials/AccountManager.cs index 21fcd0c744..76c706360c 100644 --- a/Core/Core/Credentials/AccountManager.cs +++ b/Core/Core/Credentials/AccountManager.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; @@ -536,9 +536,7 @@ private static async Task GetToken(string accessCode, str { try { - ServicePointManager.SecurityProtocol = - SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls; - var client = Http.GetHttpProxyClient(); + using var client = Http.GetHttpProxyClient(); var body = new { @@ -566,9 +564,7 @@ private static async Task GetRefreshedToken(string refres { try { - ServicePointManager.SecurityProtocol = - SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls; - var client = Http.GetHttpProxyClient(); + using var client = Http.GetHttpProxyClient(); var body = new { diff --git a/Core/Core/Helpers/Http.cs b/Core/Core/Helpers/Http.cs index 1d2d60c942..58eaf08ba0 100644 --- a/Core/Core/Helpers/Http.cs +++ b/Core/Core/Helpers/Http.cs @@ -176,12 +176,14 @@ public static async Task HttpPing(string address) } } - public static HttpClient GetHttpProxyClient(SpeckleHttpClientHandler? handler = null) + public static HttpClient GetHttpProxyClient(SpeckleHttpClientHandler? handler = null, TimeSpan? timeout = null) { IWebProxy proxy = WebRequest.GetSystemWebProxy(); proxy.Credentials = CredentialCache.DefaultCredentials; - return new HttpClient(handler ?? new SpeckleHttpClientHandler()); + var client = new HttpClient(handler ?? new SpeckleHttpClientHandler()); + client.Timeout = timeout ?? TimeSpan.FromSeconds(100); + return client; } public static bool CanAddAuth(string? authToken, out string? bearerHeader) @@ -212,6 +214,7 @@ public class SpeckleHttpClientHandler : HttpClientHandler public SpeckleHttpClientHandler(IEnumerable? delay = null) { _delay = delay ?? Http.DefaultDelay(); + CheckCertificateRevocationList = true; } protected override async Task SendAsync( diff --git a/Core/Core/Kits/ISpeckleConverter.cs b/Core/Core/Kits/ISpeckleConverter.cs index ad41d1ae87..eb46cb4578 100644 --- a/Core/Core/Kits/ISpeckleConverter.cs +++ b/Core/Core/Kits/ISpeckleConverter.cs @@ -55,6 +55,23 @@ public interface ISpeckleConverter /// public List ConvertToNative(List objects); + /// + /// Converts a given speckle objects as a generic native object. + /// This should assume has been called and returned True, + /// or call it within this method's implementation to ensure non-displayable objects are gracefully handled. + /// + /// + /// This method should not try to convert an object to it's native representation (i.e Speckle Wall -> Wall), + /// but rather use the 'displayValue' of that wall to create a geometrically correct representation of that object + /// in the native application. + /// An object may be able to be converted both with and . + /// In this case, deciding which to use is dependent on each connector developer. + /// Preferably, should be used as a fallback to the logic. + /// + /// Speckle object to convert + /// The native object that resulted after converting the input + public object ConvertToNativeDisplayable(Base @object); + /// /// Checks if it can convert a Speckle object to a native one /// @@ -62,6 +79,20 @@ public interface ISpeckleConverter /// public bool CanConvertToNative(Base @object); + /// + /// Checks to verify if a given object is: 1) displayable and 2) can be supported for conversion to the native application. + /// An object is considered "displayable" if it has a 'displayValue' property (defined in its class or dynamically attached to it, detached or not). + /// + /// + /// An object may return "True" for both and + /// In this case, deciding which to use is dependent on each connector developer. + /// Preferably, should be used as a fallback to the logic. + /// Objects found in the 'displayValue' property are assumed to be universally convertible by all converters and the viewer, but are not guaranteed to be so. + /// + /// Speckle object to convert + /// True if the object is "displayable" and the converter supports native conversion of the given speckle object in particular. + public bool CanConvertToNativeDisplayable(Base @object); + /// /// Returns a list of applications serviced by this converter /// diff --git a/Core/Core/Logging/LoggingHelpers.cs b/Core/Core/Logging/LoggingHelpers.cs new file mode 100644 index 0000000000..b1520e0b1a --- /dev/null +++ b/Core/Core/Logging/LoggingHelpers.cs @@ -0,0 +1,16 @@ +using System; +using System.Diagnostics; + +namespace Speckle.Core.Logging; + +public static class LoggingHelpers +{ + private const long TicksPerMillisecond = 10000; + private const long TicksPerSecond = TicksPerMillisecond * 1000; + private static readonly double STickFrequency = (double)TicksPerSecond / Stopwatch.Frequency; + + public static TimeSpan GetElapsedTime(long startingTimestamp, long endingTimestamp) + { + return new TimeSpan((long)((endingTimestamp - startingTimestamp) * STickFrequency)); + } +} diff --git a/Core/Core/Logging/Setup.cs b/Core/Core/Logging/Setup.cs index 66154603c5..01edc37053 100644 --- a/Core/Core/Logging/Setup.cs +++ b/Core/Core/Logging/Setup.cs @@ -63,12 +63,6 @@ public static void Init(string versionedHostApplication, string hostApplication) //start mutex so that Manager can detect if this process is running mutex = new Mutex(false, "SpeckleConnector-" + hostApplication); -#if !NETSTANDARD1_5_OR_GREATER - //needed by older .net frameworks, eg Revit 2019 - ServicePointManager.SecurityProtocol = - SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12; -#endif - SpeckleLog.Initialize(hostApplication, versionedHostApplication); foreach (var account in AccountManager.GetAccounts()) diff --git a/Core/Core/Models/DynamicBase.cs b/Core/Core/Models/DynamicBase.cs index 63955a2f05..2af9c5deb7 100644 --- a/Core/Core/Models/DynamicBase.cs +++ b/Core/Core/Models/DynamicBase.cs @@ -22,12 +22,6 @@ public class DynamicBase : DynamicObject, IDynamicMetaObjectProvider public const DynamicBaseMemberType DefaultIncludeMembers = DynamicBaseMemberType.Instance | DynamicBaseMemberType.Dynamic; - // Rule for multiple leading @. - private static Regex manyLeadingAtChars = new(@"^@{2,}"); - - // Rule for invalid chars. - private static Regex invalidChars = new(@"[\.\/]"); - private static Dictionary> propInfoCache = new(); /// @@ -114,41 +108,33 @@ public override bool TrySetMember(SetMemberBinder binder, object value) return valid; } + private static readonly HashSet DisallowedPropNameChars = new() { '.', '/' }; + public bool IsPropNameValid(string name, out string reason) { - // Existing members - //var members = GetInstanceMembersNames(); + if (string.IsNullOrEmpty(name) || name == "@") + { + reason = "Found empty prop name"; + return false; + } - // TODO: Check for detached/non-detached duplicate names? i.e: '@something' vs 'something' - // TODO: Instance members will not be overwritten, this may cause issues. - var checks = new List<(bool, string)> + if (name.StartsWith("@@")) { - (!(string.IsNullOrEmpty(name) || name == "@"), "Found empty prop name"), - // Checks for multiple leading @ - ( - !manyLeadingAtChars.IsMatch(name), - "Only one leading '@' char is allowed. This signals the property value should be detached." - ), - // Checks for invalid chars - ( - !invalidChars.IsMatch(name), - $"Prop with name '{name}' contains invalid characters. The following characters are not allowed: ./" - ) - // Checks if you are trying to change a member property - //(!members.Contains(name), "Modifying the value of instance member properties is not allowed.") - }; - - var r = ""; - // Prop name is valid if none of the checks are true - var isValid = checks.TrueForAll(v => + reason = "Only one leading '@' char is allowed. This signals the property value should be detached."; + return false; + } + + foreach (char c in name) { - if (!v.Item1) - r = v.Item2; - return v.Item1; - }); + if (DisallowedPropNameChars.Contains(c)) + { + reason = $"Prop with name '{name}' contains invalid characters. The following characters are not allowed: ./"; + return false; + } + } - reason = r; - return isValid; + reason = ""; + return true; } private static void PopulatePropInfoCache(Type type) diff --git a/Core/Core/Models/Extensions.cs b/Core/Core/Models/Extensions.cs index 7103bab560..9f3f0d1ff5 100644 --- a/Core/Core/Models/Extensions.cs +++ b/Core/Core/Models/Extensions.cs @@ -151,4 +151,50 @@ public static string GetDetachedPropName(this Base speckleObject, string propNam { return speckleObject.GetMembers(DynamicBaseMemberType.Instance).ContainsKey(propName) ? propName : $"@{propName}"; } + + /// + /// Checks if an object "is displayable" i.e. has a displayValue property that is a list of base. + /// This is to mirror the selection logic of our viewer package, where any "displayable object" will become + /// a single selectable entity. + /// + /// The Base object to check. + /// True if the object is displayable, false otherwise. + public static bool IsDisplayableObject(this Base speckleObject) + { + return speckleObject.TryGetDisplayValue() != null; + } + + public static IEnumerable? TryGetDisplayValue(this Base obj) + where T : Base + { + var rawDisplayValue = obj["displayValue"] ?? obj["@displayValue"]; + return rawDisplayValue switch + { + T b => new List { b }, + IEnumerable enumerable => enumerable.OfType(), + _ => null + }; + } + + public static IEnumerable? TryGetDisplayValue(this Base obj) + { + return TryGetDisplayValue(obj); + } + + public static string? TryGetName(this Base obj) + { + return obj["name"] as string; + } + + public static IEnumerable? TryGetParameters(this Base obj) + where T : Base + { + var parameters = (obj["parameters"] ?? obj["@parameters"]) as Base; + return parameters?.GetMembers(DynamicBaseMemberType.Dynamic).Values.OfType(); + } + + public static IEnumerable? TryGetParameters(this Base obj) + { + return TryGetParameters(obj); + } } diff --git a/Core/Core/Models/GraphTraversal/DefaultTraversal.cs b/Core/Core/Models/GraphTraversal/DefaultTraversal.cs index 40f5cc0352..c80ec2a97e 100644 --- a/Core/Core/Models/GraphTraversal/DefaultTraversal.cs +++ b/Core/Core/Models/GraphTraversal/DefaultTraversal.cs @@ -40,14 +40,13 @@ public static GraphTraversal CreateTraverseFunc(ISpeckleConverter converter) /// public static GraphTraversal CreateRevitTraversalFunc(ISpeckleConverter converter) { - var convertableRule = TraversalRule.NewTraversalRule().When(converter.CanConvertToNative).ContinueTraversing(None); - - var displayValueRule = TraversalRule + var convertableRule = TraversalRule .NewTraversalRule() + .When(converter.CanConvertToNative) .When(HasDisplayValue) - .ContinueTraversing(_ => displayValueAndElementsPropAliases); + .ContinueTraversing(None); - return new GraphTraversal(convertableRule, displayValueRule, IgnoreResultsRule, DefaultRule); + return new GraphTraversal(convertableRule, IgnoreResultsRule, DefaultRule); } /// diff --git a/Core/Core/Models/GraphTraversal/GraphTraversal.cs b/Core/Core/Models/GraphTraversal/GraphTraversal.cs index 92bd1bb892..4b21e232c4 100644 --- a/Core/Core/Models/GraphTraversal/GraphTraversal.cs +++ b/Core/Core/Models/GraphTraversal/GraphTraversal.cs @@ -23,12 +23,14 @@ protected TraversalContext(Base current, string? propName = null) } } -public class TraversalContext : TraversalContext where T : TraversalContext +public class TraversalContext : TraversalContext + where T : TraversalContext { public override TraversalContext? parent => typedParent; public T? typedParent { get; } - public TraversalContext(Base current, string? propName = null, T? parent = default) : base(current, propName) + public TraversalContext(Base current, string? propName = null, T? parent = default) + : base(current, propName) { this.typedParent = parent; } @@ -36,7 +38,10 @@ public TraversalContext(Base current, string? propName = null, T? parent = defau public class GraphTraversal : GraphTraversal { - public GraphTraversal(params ITraversalRule[] traversalRule) : base(traversalRule) { } + public GraphTraversal(params ITraversalRule[] traversalRule) + : base(traversalRule) { } + + public static readonly string traversalContextId = "traversalContextId"; protected override TraversalContext NewContext(Base current, string? propName, TraversalContext? parent) { @@ -44,7 +49,8 @@ protected override TraversalContext NewContext(Base current, string? propName, T } } -public abstract class GraphTraversal where T : TraversalContext +public abstract class GraphTraversal + where T : TraversalContext { private readonly ITraversalRule[] rules; @@ -68,6 +74,7 @@ public IEnumerable Traverse(Base root) int headIndex = stack.Count - 1; T head = stack[headIndex]; stack.RemoveAt(headIndex); + yield return head; Base current = head.current; diff --git a/Core/Core/Serialisation/BaseObjectDeserializerV2.cs b/Core/Core/Serialisation/BaseObjectDeserializerV2.cs index cf2d11979c..a063b37089 100644 --- a/Core/Core/Serialisation/BaseObjectDeserializerV2.cs +++ b/Core/Core/Serialisation/BaseObjectDeserializerV2.cs @@ -43,6 +43,9 @@ public sealed class BaseObjectDeserializerV2 public string? BlobStorageFolder { get; set; } public TimeSpan Elapsed { get; private set; } + public static int DefaultNumberThreads => Math.Min(Environment.ProcessorCount, 6); //6 threads seems the sweet spot, see performance test project + public int WorkerThreadCount { get; set; } = DefaultNumberThreads; + /// The JSON string of the object to be deserialized /// A typed object deserialized from the /// Thrown when @@ -59,7 +62,7 @@ public Base Deserialize(string rootObjectJson) _busy = true; var stopwatch = Stopwatch.StartNew(); _deserializedObjects = new(); - _workerThreads = new DeserializationWorkerThreads(this); + _workerThreads = new DeserializationWorkerThreads(this, WorkerThreadCount); _workerThreads.Start(); List<(string, int)> closures = GetClosures(rootObjectJson); @@ -144,9 +147,9 @@ public Base Deserialize(string rootObjectJson) reader.DateParseHandling = DateParseHandling.None; doc1 = JObject.Load(reader); } - + object? converted = ConvertJsonElement(doc1); - + lock (_callbackLock) OnProgressAction?.Invoke("DS", 1); return converted; @@ -208,9 +211,10 @@ public Base Deserialize(string rootObjectJson) return retList; case JTokenType.Object: - Dictionary dict = new(); + var jObject = (JContainer)doc; + Dictionary dict = new(jObject.Count); - foreach (JToken propJToken in doc) + foreach (JToken propJToken in jObject) { JProperty prop = (JProperty)propJToken; if (prop.Name == "__closure") @@ -221,9 +225,11 @@ public Base Deserialize(string rootObjectJson) if (!dict.ContainsKey(TypeDiscriminator)) return dict; - if (dict[TypeDiscriminator] as string == "reference" && dict.ContainsKey("referencedId")) + if ( + dict[TypeDiscriminator] as string == "reference" && dict.TryGetValue("referencedId", out object? referencedId) + ) { - var objId = (string)dict["referencedId"]!; + var objId = (string)referencedId!; object deserialized = null; lock (_deserializedObjects) if (_deserializedObjects.TryGetValue(objId, out object? o)) @@ -247,9 +253,10 @@ public Base Deserialize(string rootObjectJson) // This reference was not already deserialized. Do it now in sync mode string objectJson = ReadTransport.GetObject(objId); - if(objectJson is null) throw new Exception($"Failed to fetch object id {objId} from {ReadTransport} "); + if (objectJson is null) + throw new Exception($"Failed to fetch object id {objId} from {ReadTransport} "); deserialized = DeserializeTransportObject(objectJson); - + lock (_deserializedObjects) _deserializedObjects[objId] = deserialized; return deserialized; @@ -288,14 +295,12 @@ private Base Dict2Base(Dictionary dictObj) } Type targetValueType = property.PropertyType; - bool conversionOk = ValueConverter.ConvertValue(targetValueType, entry.Value, out object convertedValue); + bool conversionOk = ValueConverter.ConvertValue(targetValueType, entry.Value, out object? convertedValue); if (conversionOk) property.SetValue(baseObj, convertedValue); else // Cannot convert the value in the json to the static property type - throw new Exception( - $"Cannot deserialize {entry.Value.GetType().FullName} to {targetValueType.FullName}" - ); + throw new Exception($"Cannot deserialize {entry.Value.GetType().FullName} to {targetValueType.FullName}"); } else { diff --git a/Core/Core/Serialisation/BaseObjectSerializerV2.cs b/Core/Core/Serialisation/BaseObjectSerializerV2.cs index dc66ab3b82..3858d3597e 100644 --- a/Core/Core/Serialisation/BaseObjectSerializerV2.cs +++ b/Core/Core/Serialisation/BaseObjectSerializerV2.cs @@ -5,11 +5,12 @@ using System.Drawing; using System.Globalization; using System.Linq; -using System.Numerics; +using System.DoubleNumerics; using System.Reflection; using System.Text.RegularExpressions; using System.Threading; using Speckle.Core.Helpers; +using Speckle.Core.Logging; using Speckle.Core.Models; using Speckle.Core.Transports; using Speckle.Newtonsoft.Json; @@ -21,7 +22,7 @@ public class BaseObjectSerializerV2 { private readonly Stopwatch _stopwatch = new(); private bool _busy; - + private List> ParentClosures = new(); private HashSet ParentObjects = new(); @@ -136,26 +137,52 @@ public object PreserializeObject( return c.ToArgb(); if (obj is DateTime t) return t.ToString("o", CultureInfo.InvariantCulture); - if (obj is Matrix4x4 m) - return new List + if (obj is Matrix4x4 md) + return new List + { + md.M11, + md.M12, + md.M13, + md.M14, + md.M21, + md.M22, + md.M23, + md.M24, + md.M31, + md.M32, + md.M33, + md.M34, + md.M41, + md.M42, + md.M43, + md.M44 + }; + if (obj is System.Numerics.Matrix4x4 ms) //BACKWARDS COMPATIBILITY: matrix4x4 changed from System.Numerics float to System.DoubleNumerics double in release 2.16 + { + SpeckleLog.Logger.Warning( + "This kept for backwards compatibility, no one should be using {this}", + "BaseObjectSerializerV2 serialize System.Numerics.Matrix4x4" + ); + return new List { - m.M11, - m.M12, - m.M13, - m.M14, - m.M21, - m.M22, - m.M23, - m.M24, - m.M31, - m.M32, - m.M33, - m.M34, - m.M41, - m.M42, - m.M43, - m.M44 + ms.M11, + ms.M12, + ms.M13, + ms.M14, + ms.M21, + ms.M22, + ms.M23, + ms.M24, + ms.M31, + ms.M32, + ms.M33, + ms.M34, + ms.M41, + ms.M42, + ms.M43, + ms.M44 }; + } throw new Exception("Unsupported value in serialization: " + type); } diff --git a/Core/Core/Serialisation/DeserializationWorkerThreads.cs b/Core/Core/Serialisation/DeserializationWorkerThreads.cs index 3a25f38723..527b606504 100644 --- a/Core/Core/Serialisation/DeserializationWorkerThreads.cs +++ b/Core/Core/Serialisation/DeserializationWorkerThreads.cs @@ -18,10 +18,10 @@ internal sealed class DeserializationWorkerThreads : ParallelOperationExecutor lMatrix) + { + SpeckleLog.Logger.Warning("This kept for backwards compatibility, no one should be using {this}", "ValueConverter deserialize to System.Numerics.Matrix4x4"); + float I(int index) => Convert.ToSingle(lMatrix[index]); + convertedValue = new Numerics.Matrix4x4( + I(0), + I(1), + I(2), + I(3), + I(4), + I(5), + I(6), + I(7), + I(8), + I(9), + I(10), + I(11), + I(12), + I(13), + I(14), + I(15) + ); + return true; + } + #endregion + if (type == typeof(Matrix4x4) && value is IReadOnlyList l) { - float I(int index) => Convert.ToSingle(l[index]); + double I(int index) => Convert.ToDouble(l[index]); convertedValue = new Matrix4x4( I(0), I(1), diff --git a/Core/Core/Transports/SQLite.cs b/Core/Core/Transports/SQLite.cs index 743354a8a1..4c216c0410 100644 --- a/Core/Core/Transports/SQLite.cs +++ b/Core/Core/Transports/SQLite.cs @@ -10,6 +10,7 @@ using System.Timers; using Microsoft.Data.Sqlite; using Speckle.Core.Helpers; +using Speckle.Core.Logging; using Speckle.Core.Models; using Timer = System.Timers.Timer; @@ -41,7 +42,7 @@ public SQLiteTransport(string? basePath = null, string? applicationName = null, } catch (Exception ex) { - throw new TransportException(this, $"Cound not create {dir}", ex); + throw new TransportException(this, $"Could not create {dir}", ex); } _rootPath = Path.Combine(_basePath, _applicationName, $"{_scope}.db"); @@ -415,7 +416,7 @@ public void SaveObjectSync(string hash, string serializedObject) CancellationToken.ThrowIfCancellationRequested(); lock (ConnectionLock) { - var stopwatch = Stopwatch.StartNew(); + var startTime = Stopwatch.GetTimestamp(); using (var command = new SqliteCommand("SELECT * FROM objects WHERE hash = @hash LIMIT 1 ", Connection)) { command.Parameters.AddWithValue("@hash", id); @@ -425,8 +426,7 @@ public void SaveObjectSync(string hash, string serializedObject) return reader.GetString(1); } } - stopwatch.Stop(); - Elapsed += stopwatch.Elapsed; + Elapsed += LoggingHelpers.GetElapsedTime(startTime, Stopwatch.GetTimestamp()); } return null; // pass on the duty of null checks to consumers } diff --git a/Core/Core/Transports/ServerUtils/ServerAPI.cs b/Core/Core/Transports/ServerUtils/ServerAPI.cs index e864a745c4..3a06de8d1d 100644 --- a/Core/Core/Transports/ServerUtils/ServerAPI.cs +++ b/Core/Core/Transports/ServerUtils/ServerAPI.cs @@ -40,7 +40,7 @@ public ServerApi(string baseUri, string? authorizationToken, string blobStorageF ); _client.BaseAddress = new Uri(baseUri); - _client.Timeout = new TimeSpan(0, 0, timeoutSeconds); + _client.Timeout = TimeSpan.FromSeconds(timeoutSeconds); Http.AddAuthHeader(_client, authorizationToken); } diff --git a/Core/IntegrationTests/Fixtures.cs b/Core/IntegrationTests/Fixtures.cs index 4f9f8f9b87..cfe6e59a21 100644 --- a/Core/IntegrationTests/Fixtures.cs +++ b/Core/IntegrationTests/Fixtures.cs @@ -32,7 +32,10 @@ public static async Task SeedUser() user["password"] = "12ABC3456789DEF0GHO"; user["name"] = $"{seed.Substring(0, 5)} Name"; - var httpClient = new HttpClient(new HttpClientHandler { AllowAutoRedirect = false }); + using var httpClient = new HttpClient( + new HttpClientHandler { AllowAutoRedirect = false, CheckCertificateRevocationList = true } + ); + httpClient.BaseAddress = new Uri(Server.url); string redirectUrl; @@ -85,7 +88,7 @@ await tokenResponse.Content.ReadAsStringAsync().ConfigureAwait(false) }, serverInfo = Server }; - var client = new Client(acc); + using var client = new Client(acc); var user1 = await client.ActiveUserGet().ConfigureAwait(false); acc.userInfo.id = user1.id; diff --git a/Core/Tests/Models/SerializerBreakingChanges.cs b/Core/Tests/Models/SerializerBreakingChanges.cs new file mode 100644 index 0000000000..893661a923 --- /dev/null +++ b/Core/Tests/Models/SerializerBreakingChanges.cs @@ -0,0 +1,48 @@ +using NUnit.Framework; + +namespace TestsUnit.Models; + +/// +/// Test fixture that documents what property typing changes break backwards/cross/forwards compatibility, and are "breaking" changes. +/// This doesn't guarantee things work this way for SpecklePy +/// Nor does it encompass other tricks (like deserialize callback, or computed json ignored properties) +/// +[ + TestFixture, + Description( + "For certain types, changing property from one type to another is a breaking change, and not backwards/forwards compatible" + ) +] +public class SerializerBreakingChanges : PrimitiveTestFixture +{ + [Test] + public void StringToInt_ShouldThrow() + { + var from = new StringValueMock(); + from.value = "testValue"; + Assert.Throws(() => from.SerializeAsTAndDeserialize()); + } + + [Test, TestCaseSource(nameof(MyEnums))] + public void StringToEnum_ShouldThrow(MyEnum testCase) + { + var from = new StringValueMock { value = testCase.ToString() }; + + Assert.Throws(() => + { + var res = from.SerializeAsTAndDeserialize(); + }); + } + + [ + Test, + Description("Deserialization of a JTokenType.Float to a .NET short/int/long should throw exception"), + TestCaseSource(nameof(Float64TestCases)), + TestCase(1e+30) + ] + public void DoubleToInt_ShouldThrow(double testCase) + { + var from = new DoubleValueMock { value = testCase }; + Assert.Throws(() => from.SerializeAsTAndDeserialize()); + } +} diff --git a/Core/Tests/Models/SerializerNonBreakingChanges.cs b/Core/Tests/Models/SerializerNonBreakingChanges.cs index 3295cb9e05..606f6f16f4 100644 --- a/Core/Tests/Models/SerializerNonBreakingChanges.cs +++ b/Core/Tests/Models/SerializerNonBreakingChanges.cs @@ -1,6 +1,8 @@ +using System.DoubleNumerics; using System.Drawing; using NUnit.Framework; using Speckle.Core.Api; +using Speckle.Core.Helpers; using Speckle.Core.Models; namespace TestsUnit.Models; @@ -115,50 +117,49 @@ public void DoubleToDouble(double testCase) var res = from.SerializeAsTAndDeserialize(); Assert.That(res.value, Is.EqualTo(testCase)); } -} -/// -/// Test fixture that documents what property typing changes break backwards/cross/forwards compatibility, and are "breaking" changes. -/// This doesn't guarantee things work this way for SpecklePy -/// Nor does it encompass other tricks (like deserialize callback, or computed json ignored properties) -/// -[ - TestFixture, - Description( - "For certain types, changing property from one type to another is a breaking change, and not backwards/forwards compatible" - ) -] -public class SerializerBreakingChanges : PrimitiveTestFixture -{ [Test] - public void StringToInt_ShouldThrow() + [TestCase(123, 255)] + [TestCase(256, 1)] + [TestCase(256, float.MinValue)] + public void ListToMatrix64(int seed, double scaler) { - var from = new StringValueMock(); - from.value = "testValue"; - Assert.Throws(() => from.SerializeAsTAndDeserialize()); - } + Random rand = new(seed); + List testCase = Enumerable.Range(0, 16).Select(_ => rand.NextDouble() * scaler).ToList(); - [Test, TestCaseSource(nameof(MyEnums))] - public void StringToEnum_ShouldThrow(MyEnum testCase) - { - var from = new StringValueMock { value = testCase.ToString() }; + ListDoubleValueMock from = new() { value = testCase, }; + + //Test List -> Matrix + var res = from.SerializeAsTAndDeserialize(); + Assert.That(res.value.M11, Is.EqualTo(testCase[0])); + Assert.That(res.value.M44, Is.EqualTo(testCase[^1])); - Assert.Throws(() => - { - var res = from.SerializeAsTAndDeserialize(); - }); + //Test Matrix -> List + var backAgain = res.SerializeAsTAndDeserialize(); + Assert.That(backAgain.value, Is.Not.Null); + Assert.That(backAgain.value, Is.EquivalentTo(testCase)); } - [ - Test, - Description("Deserialization of a JTokenType.Float to a .NET short/int/long should throw exception"), - TestCaseSource(nameof(Float64TestCases)), - TestCase(1e+30) - ] - public void DoubleToInt_ShouldThrow(double testCase) + [Test] + [TestCase(123, 255)] + [TestCase(256, 1)] + [DefaultFloatingPointTolerance(Constants.Eps)] + public void Matrix32ToMatrix64(int seed, float scaler) { - var from = new DoubleValueMock { value = testCase }; - Assert.Throws(() => from.SerializeAsTAndDeserialize()); + Random rand = new(seed); + List testCase = Enumerable.Range(0, 16).Select(_ => rand.NextDouble() * scaler).ToList(); + + ListDoubleValueMock from = new() { value = testCase, }; + + //Test List -> Matrix + var res = from.SerializeAsTAndDeserialize(); + Assert.That(res.value.M11, Is.EqualTo(testCase[0])); + Assert.That(res.value.M44, Is.EqualTo(testCase[^1])); + + //Test Matrix -> List + var backAgain = res.SerializeAsTAndDeserialize(); + Assert.That(backAgain.value, Is.Not.Null); + Assert.That(backAgain.value, Is.EquivalentTo(testCase)); } } @@ -192,6 +193,16 @@ public class DoubleValueMock : SerializerMock public double value { get; set; } } +public class Matrix64ValueMock : SerializerMock +{ + public Matrix4x4 value { get; set; } +} + +public class Matrix32ValueMock : SerializerMock +{ + public System.Numerics.Matrix4x4 value { get; set; } +} + public class ColorValueMock : SerializerMock { public Color value { get; set; } diff --git a/Core/TestsPerformance/Api/Operations/ReceiveFromSQLite.cs b/Core/TestsPerformance/Api/Operations/ReceiveFromSQLite.cs new file mode 100644 index 0000000000..a65ecd7e58 --- /dev/null +++ b/Core/TestsPerformance/Api/Operations/ReceiveFromSQLite.cs @@ -0,0 +1,44 @@ +using System.Diagnostics; +using BenchmarkDotNet.Attributes; +using Speckle.Core.Models; + +namespace TestsPerformance.Api.Operations; + +[MemoryDiagnoser] +[RegressionTestConfig(1, 1, 8, nugetVersions: "2.15.2")] +public class ReceiveFromSQLite +{ + [Params(0, 4, 9, 19)] + public int DataComplexity { get; set; } + + private TestDataHelper _dataSource; + + [GlobalSetup] + public async Task Setup() + { + _dataSource = new TestDataHelper(); + await _dataSource.SeedTransport(DataComplexity).ConfigureAwait(false); + } + + [GlobalCleanup] + public virtual void Cleanup() + { + _dataSource.Dispose(); + } + + [Benchmark] + public async Task Receive_FromSQLite() + { + Base? b = await Speckle.Core.Api.Operations + .Receive( + _dataSource.ObjectId, + null, + _dataSource.Transport, + onErrorAction: (message, ex) => throw new Exception(message, ex) + ) + .ConfigureAwait(false); + + Trace.Assert(b is not null); + return b; + } +} diff --git a/Core/TestsPerformance/Api/Operations/TraverseCommit.cs b/Core/TestsPerformance/Api/Operations/TraverseCommit.cs new file mode 100644 index 0000000000..d7bf251d66 --- /dev/null +++ b/Core/TestsPerformance/Api/Operations/TraverseCommit.cs @@ -0,0 +1,38 @@ +using BenchmarkDotNet.Attributes; +using Speckle.Core.Models; +using Speckle.Core.Models.GraphTraversal; + +namespace TestsPerformance.Api.Operations; + +[MemoryDiagnoser] +[RegressionTestConfig(1, 1, 20, nugetVersions:"2.15.2")] +public class TraverseCommit +{ + [Params(0, 4, 9, 19)] + public int DataComplexity { get; set; } + + private Base _testData; + private GraphTraversal _sut; + + [GlobalSetup] + public async Task Setup() + { + using var dataSource = new TestDataHelper(); + await dataSource.SeedTransport(DataComplexity).ConfigureAwait(false); + _testData = await dataSource.DeserializeBase().ConfigureAwait(false); + + var convertableRule = TraversalRule + .NewTraversalRule() + .When(b => b.speckle_type.Contains("Geometry")) + .When(DefaultTraversal.HasDisplayValue) + .ContinueTraversing(_ => DefaultTraversal.elementsPropAliases); + + _sut = new GraphTraversal(convertableRule, DefaultTraversal.DefaultRule); + } + + [Benchmark] + public List Traverse() + { + return _sut.Traverse(_testData).ToList(); + } +} diff --git a/Core/TestsPerformance/Program.cs b/Core/TestsPerformance/Program.cs new file mode 100644 index 0000000000..71913a106f --- /dev/null +++ b/Core/TestsPerformance/Program.cs @@ -0,0 +1,11 @@ +using BenchmarkDotNet.Running; + +namespace TestsPerformance; + +public static class Program +{ + public static async Task Main(string[] args) + { + BenchmarkSwitcher.FromAssemblies(new[] { typeof(Program).Assembly }).Run(args); + } +} diff --git a/Core/TestsPerformance/RegressionTestConfig.cs b/Core/TestsPerformance/RegressionTestConfig.cs new file mode 100644 index 0000000000..06e02982a8 --- /dev/null +++ b/Core/TestsPerformance/RegressionTestConfig.cs @@ -0,0 +1,55 @@ +using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Engines; +using BenchmarkDotNet.Environments; +using BenchmarkDotNet.Jobs; + +namespace TestsPerformance; + +[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class, AllowMultiple = true)] +public sealed class RegressionTestConfigAttribute : Attribute, IConfigSource +{ + public IConfig Config { get; private set; } + + public RegressionTestConfigAttribute( + int launchCount = 1, + int warmupCount = 0, + int iterationCount = 10, + RunStrategy strategy = RunStrategy.Monitoring, + bool includeHead = true, + params string[] nugetVersions + ) + { + List jobs = new(); + + if (includeHead) + { + jobs.Add( + new Job("Head") + .WithRuntime(ClrRuntime.Net481) + .WithStrategy(strategy) + .WithLaunchCount(launchCount) + .WithWarmupCount(warmupCount) + .WithIterationCount(iterationCount) + ); + } + + bool isBaseline = true; + foreach (var version in nugetVersions) + { + jobs.Add( + new Job(version) + .WithRuntime(ClrRuntime.Net481) + .WithStrategy(strategy) + .WithLaunchCount(launchCount) + .WithWarmupCount(warmupCount) + .WithIterationCount(iterationCount) + .WithNuGet("Speckle.Objects", version) + .WithBaseline(isBaseline) + ); + + isBaseline = false; + } + + Config = ManualConfig.CreateEmpty().AddJob(jobs.ToArray()); + } +} diff --git a/Core/TestsPerformance/Serialisation/DeserializationWorkerThreads.cs b/Core/TestsPerformance/Serialisation/DeserializationWorkerThreads.cs new file mode 100644 index 0000000000..931a6e243e --- /dev/null +++ b/Core/TestsPerformance/Serialisation/DeserializationWorkerThreads.cs @@ -0,0 +1,39 @@ +using BenchmarkDotNet.Attributes; +using Speckle.Core.Models; +using Speckle.Core.Serialisation; +using Speckle.Core.Transports; + +namespace TestsPerformance.Serialisation; + +[MemoryDiagnoser] +[RegressionTestConfig(1, 1, 6)] +public class DeserializationWorkerThreads +{ + public static IEnumerable NumThreadsToTest => Enumerable.Range(0, Environment.ProcessorCount + 1); + + [Params(0, 9)] + public int DataComplexity { get; set; } + + private TestDataHelper _dataSource; + + [GlobalSetup] + public async Task Setup() + { + _dataSource = new TestDataHelper(); + await _dataSource.SeedTransport(DataComplexity).ConfigureAwait(false); + } + + [GlobalCleanup] + public virtual void Cleanup() + { + _dataSource.Dispose(); + } + + [Benchmark] + [ArgumentsSource(nameof(NumThreadsToTest))] + public Base RunTest(int numThreads) + { + BaseObjectDeserializerV2 sut = new() { WorkerThreadCount = numThreads, ReadTransport = _dataSource.Transport }; + return sut.Deserialize(_dataSource.Transport.GetObject(_dataSource.ObjectId)!); + } +} diff --git a/Core/TestsPerformance/TestDataHelper.cs b/Core/TestsPerformance/TestDataHelper.cs new file mode 100644 index 0000000000..44a99b68f8 --- /dev/null +++ b/Core/TestsPerformance/TestDataHelper.cs @@ -0,0 +1,57 @@ +using System.Reactive; +using Microsoft.Data.Sqlite; +using Speckle.Core.Api; +using Speckle.Core.Credentials; +using Speckle.Core.Models; +using Speckle.Core.Transports; + +namespace TestsPerformance; + +public sealed class TestDataHelper : IDisposable +{ + private static readonly string BasePath = $"./temp {Guid.NewGuid()}"; + private const string ApplicationName = "Speckle Performance Tests"; + + public SQLiteTransport Transport { get; private set; } + public string ObjectId { get; private set; } + + public async Task SeedTransport(int dataComplexity) + { + Transport = new SQLiteTransport(BasePath, ApplicationName); + + //seed SQLite transport with test data + ObjectId = await SeedTransport(dataComplexity, Transport).ConfigureAwait(false); + } + + public static async Task SeedTransport(int dataComplexity, ITransport transport) + { + //seed SQLite transport with test data + StreamWrapper sw = new($"https://latest.speckle.dev/streams/efd2c6a31d/branches/{dataComplexity}"); + var acc = await sw.GetAccount().ConfigureAwait(false); + using var client = new Client(acc); + var branch = await client.BranchGet(sw.StreamId, sw.BranchName!, 1).ConfigureAwait(false); + var objectId = branch.commits.items[0].referencedObject; + + using ServerTransport remoteTransport = new(acc, sw.StreamId); + transport.BeginWrite(); + await remoteTransport.CopyObjectAndChildren(objectId, transport).ConfigureAwait(false); + transport.EndWrite(); + await transport.WriteComplete().ConfigureAwait(false); + + return objectId; + } + + public async Task DeserializeBase() + { + return await Speckle.Core.Api.Operations + .Receive(ObjectId, null, Transport, onErrorAction: (message, ex) => throw new Exception(message, ex)) + .ConfigureAwait(false) ?? throw new InvalidOperationException("Test data was null"); + } + + public void Dispose() + { + Transport.Dispose(); + SqliteConnection.ClearAllPools(); + Directory.Delete(BasePath, true); + } +} diff --git a/Core/TestsPerformance/TestsPerformance.csproj b/Core/TestsPerformance/TestsPerformance.csproj new file mode 100644 index 0000000000..699be9dd4a --- /dev/null +++ b/Core/TestsPerformance/TestsPerformance.csproj @@ -0,0 +1,18 @@ + + + + net481 + enable + enable + exe + + + + + + + + + + + diff --git a/DesktopUI2/AvaloniaHwndHost/UnmanagedMethods.cs b/DesktopUI2/AvaloniaHwndHost/UnmanagedMethods.cs index 0399f832a9..55a13a9b2c 100644 --- a/DesktopUI2/AvaloniaHwndHost/UnmanagedMethods.cs +++ b/DesktopUI2/AvaloniaHwndHost/UnmanagedMethods.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; namespace AvaloniaHwndHost; @@ -6,5 +7,6 @@ namespace AvaloniaHwndHost; internal static class UnmanagedMethods { [DllImport("user32.dll")] + [SuppressMessage("Security", "CA5392:Use DefaultDllImportSearchPaths attribute for P/Invokes")] public static extern bool SetParent(IntPtr hWnd, IntPtr hWndNewParent); } diff --git a/DesktopUI2/DesktopUI2/ViewModels/HomeViewModel.cs b/DesktopUI2/DesktopUI2/ViewModels/HomeViewModel.cs index 3a52d8edf8..2bf03bcf66 100644 --- a/DesktopUI2/DesktopUI2/ViewModels/HomeViewModel.cs +++ b/DesktopUI2/DesktopUI2/ViewModels/HomeViewModel.cs @@ -204,14 +204,14 @@ private async Task GetStreams() } catch (OperationCanceledException) { - return; + continue; } - catch (Exception e) + catch (Exception ex) { - if (e.InnerException is TaskCanceledException) + if (ex.InnerException is TaskCanceledException) return; - SpeckleLog.Logger.Error(e, "Could not fetch streams"); + SpeckleLog.Logger.Error(ex, "Could not fetch streams"); Dispatcher.UIThread.Post( () => @@ -731,15 +731,17 @@ private async void OpenStreamCommand(object streamAccountWrapper) if (await CheckIsOffline().ConfigureAwait(true)) return; - - if (streamAccountWrapper != null) { var streamState = new StreamState(streamAccountWrapper as StreamAccountWrapper); if (!await streamState.Client.IsStreamAccessible(streamState.StreamId).ConfigureAwait(true)) { - Dialogs.ShowDialog("Stream not found", "Please ensure the stream exists and that you have access to it.", DialogIconKind.Error); + Dialogs.ShowDialog( + "Stream not found", + "Please ensure the stream exists and that you have access to it.", + DialogIconKind.Error + ); return; } @@ -755,16 +757,17 @@ private async void OpenSavedStreamCommand(object streamViewModel) if (await CheckIsOffline().ConfigureAwait(true)) return; - - if (streamViewModel != null && streamViewModel is StreamViewModel svm && !svm.NoAccess) { - try { if (!await svm.Client.IsStreamAccessible(svm.Stream.id).ConfigureAwait(true)) { - Dialogs.ShowDialog("Stream not found", "Please ensure the stream exists and that you have access to it.", DialogIconKind.Error); + Dialogs.ShowDialog( + "Stream not found", + "Please ensure the stream exists and that you have access to it.", + DialogIconKind.Error + ); return; } @@ -778,7 +781,6 @@ private async void OpenSavedStreamCommand(object streamViewModel) SpeckleLog.Logger.Error(ex, "Failed to open saved stream {exceptionMessage}", ex.Message); } } - } public void ToggleDarkThemeCommand() diff --git a/DesktopUI2/DesktopUI2/ViewModels/MappingTool/MappingsViewModel.cs b/DesktopUI2/DesktopUI2/ViewModels/MappingTool/MappingsViewModel.cs index 9b0c601f16..8434ff27bd 100644 --- a/DesktopUI2/DesktopUI2/ViewModels/MappingTool/MappingsViewModel.cs +++ b/DesktopUI2/DesktopUI2/ViewModels/MappingTool/MappingsViewModel.cs @@ -315,9 +315,12 @@ private void AddRevitInfoToSchema(List schemas) //no need to do extra stuff if ( schema is DirectShapeFreeformViewModel + || schema is BlockDefinitionViewModel || schema is RevitTopographyViewModel || schema is RevitDefaultWallViewModel || schema is RevitDefaultFloorViewModel + || schema is RevitDefaultCeilingViewModel + || schema is RevitDefaultRoofViewModel || schema is RevitDefaultBeamViewModel || schema is RevitDefaultBraceViewModel || schema is RevitDefaultColumnViewModel @@ -381,6 +384,32 @@ schema is DirectShapeFreeformViewModel updatedSchemas.Add(o); break; + case RevitCeilingViewModel o: + var ceilingFamilies = AvailableRevitTypes.Where(x => x.category == "Ceilings").ToList(); + if (!ceilingFamilies.Any() || !AvailableRevitLevels.Any()) + break; + var ceilingFamiliesViewModels = ceilingFamilies + .GroupBy(x => x.family) + .Select(g => new RevitFamily(g.Key.ToString(), g.Select(y => y.type).ToList())) + .ToList(); + o.Families = ceilingFamiliesViewModels; + o.Levels = AvailableRevitLevels; + updatedSchemas.Add(o); + break; + + case RevitFootprintRoofViewModel o: + var roofFamilies = AvailableRevitTypes.Where(x => x.category == "Roofs").ToList(); + if (!roofFamilies.Any() || !AvailableRevitLevels.Any()) + break; + var roofFamiliesViewModels = roofFamilies + .GroupBy(x => x.family) + .Select(g => new RevitFamily(g.Key.ToString(), g.Select(y => y.type).ToList())) + .ToList(); + o.Families = roofFamiliesViewModels; + o.Levels = AvailableRevitLevels; + updatedSchemas.Add(o); + break; + case RevitBeamViewModel o: var beamFamilies = AvailableRevitTypes.Where(x => x.category == "Structural Framing").ToList(); if (!beamFamilies.Any() || !AvailableRevitLevels.Any()) diff --git a/DesktopUI2/DesktopUI2/ViewModels/MappingTool/Schemas.cs b/DesktopUI2/DesktopUI2/ViewModels/MappingTool/Schemas.cs index db04d94cc9..a191e8ed7f 100644 --- a/DesktopUI2/DesktopUI2/ViewModels/MappingTool/Schemas.cs +++ b/DesktopUI2/DesktopUI2/ViewModels/MappingTool/Schemas.cs @@ -4,6 +4,8 @@ using System.Runtime.Serialization; using Objects.BuiltElements; using Objects.BuiltElements.Revit; +using Objects.BuiltElements.Revit.RevitRoof; +using Objects.Other; using ReactiveUI; using Speckle.Core.Api; using Speckle.Newtonsoft.Json; @@ -238,6 +240,37 @@ public override string GetSerializedSchema() } } +public class RevitCeilingViewModel : RevitBasicViewModel +{ + public override string Name => "Ceiling"; + + public override string GetSerializedSchema() + { + var obj = new RevitCeiling( + null, + SelectedFamily.Name, + SelectedType, + new RevitLevel(SelectedLevel), + 0, + null, + null, + null + ); + return Operations.Serialize(obj); + } +} + +public class RevitFootprintRoofViewModel : RevitBasicViewModel +{ + public override string Name => "FootprintRoof"; + + public override string GetSerializedSchema() + { + var obj = new RevitFootprintRoof(null, SelectedFamily.Name, SelectedType, new RevitLevel(SelectedLevel)); + return Operations.Serialize(obj); + } +} + public class RevitBeamViewModel : RevitBasicViewModel { public override string Name => "Beam"; @@ -346,7 +379,6 @@ public DirectShapeFreeformViewModel() .Select(x => x.ToString()) .OrderBy(x => x) .ToList(); - ; } public override string Name => "DirectShape"; @@ -405,6 +437,57 @@ public override string GetSerializedSchema() } } +public class BlockDefinitionViewModel : Schema +{ + private List _categories; + + private string _selectedCategory = RevitFamilyCategory.GenericModel.ToString(); + + public BlockDefinitionViewModel() + { + Categories = Enum.GetValues(typeof(RevitFamilyCategory)) + .Cast() + .Select(x => x.ToString()) + .OrderBy(x => x) + .ToList(); + ; + } + + public override string Name => "New Revit Family"; + + public List Categories + { + get => _categories; + set => this.RaiseAndSetIfChanged(ref _categories, value); + } + + [DataMember] + public string SelectedCategory + { + get => _selectedCategory; + set + { + this.RaiseAndSetIfChanged(ref _selectedCategory, value); + this.RaisePropertyChanged(nameof(IsValid)); + } + } + public override string Summary => $"New Revit Family - {SelectedCategory}"; + + public override bool IsValid => !string.IsNullOrEmpty(SelectedCategory); + + public override string GetSerializedSchema() + { + var res = Enum.TryParse(SelectedCategory, out RevitCategory cat); + if (!res) + cat = RevitCategory.GenericModel; + + var ds = new MappedBlockWrapper(); //don't use the constructor + ds.category = cat.ToString(); + + return Operations.Serialize(ds); + } +} + public class RevitDefaultWallViewModel : Schema { public override string Name => "Default Wall"; @@ -435,6 +518,36 @@ public override string GetSerializedSchema() } } +public class RevitDefaultCeilingViewModel : Schema +{ + public override string Name => "Default Ceiling"; + + public override string Summary => Name; + + public override bool IsValid => true; + + public override string GetSerializedSchema() + { + var obj = new Ceiling(); + return Operations.Serialize(obj); + } +} + +public class RevitDefaultRoofViewModel : Schema +{ + public override string Name => "Default Roof"; + + public override string Summary => Name; + + public override bool IsValid => true; + + public override string GetSerializedSchema() + { + var obj = new Roof(); + return Operations.Serialize(obj); + } +} + public class RevitDefaultBeamViewModel : Schema { public override string Name => "Default Beam"; diff --git a/DesktopUI2/DesktopUI2/Views/MappingsControl.xaml b/DesktopUI2/DesktopUI2/Views/MappingsControl.xaml index 7ab164e22b..6054bc9214 100644 --- a/DesktopUI2/DesktopUI2/Views/MappingsControl.xaml +++ b/DesktopUI2/DesktopUI2/Views/MappingsControl.xaml @@ -266,6 +266,8 @@ + + @@ -288,6 +290,14 @@ + + + + + + + + @@ -337,17 +347,25 @@ IsChecked="{Binding Freeform}" />--> - + + + + +