diff --git a/SpeckleConnector/Connector.xcodeproj/project.pbxproj b/SpeckleConnector/Connector.xcodeproj/project.pbxproj index 3eef8bc..c3709a9 100644 --- a/SpeckleConnector/Connector.xcodeproj/project.pbxproj +++ b/SpeckleConnector/Connector.xcodeproj/project.pbxproj @@ -320,6 +320,7 @@ 2199BB512CDA481A00A4BEEC /* ConnectorFix.grc */ = {isa = PBXFileReference; lastKnownFileType = text; name = ConnectorFix.grc; path = RFIX/ConnectorFix.grc; sourceTree = ""; }; 2199BB522CDA4B1700A4BEEC /* ConnectorProject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ConnectorProject.cpp; sourceTree = ""; }; 2199BB532CDA4B1700A4BEEC /* ConnectorProject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ConnectorProject.h; sourceTree = ""; }; + 2199BB5C2CDA93CE00A4BEEC /* ConnectorImagesFix.grc */ = {isa = PBXFileReference; lastKnownFileType = text; name = ConnectorImagesFix.grc; path = RFIX/ConnectorImagesFix.grc; sourceTree = ""; }; 219F30352C768F0A009834E9 /* Connector-AC27-Test.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Connector-AC27-Test.bundle"; sourceTree = BUILT_PRODUCTS_DIR; }; 219F30402C769282009834E9 /* ConfigTests.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; path = ConfigTests.cpp; sourceTree = ""; }; 219F30432C7693B6009834E9 /* Connector-AC27-Debug.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = "Connector-AC27-Debug.xctestplan"; sourceTree = SOURCE_ROOT; }; @@ -975,6 +976,7 @@ isa = PBXGroup; children = ( 2199BB512CDA481A00A4BEEC /* ConnectorFix.grc */, + 2199BB5C2CDA93CE00A4BEEC /* ConnectorImagesFix.grc */, ); name = Fixed; sourceTree = ""; diff --git a/SpeckleLib/Speckle/Database/BIMElementDatabase.cpp b/SpeckleLib/Speckle/Database/BIMElementDatabase.cpp index 0d44627..63d3fa7 100644 --- a/SpeckleLib/Speckle/Database/BIMElementDatabase.cpp +++ b/SpeckleLib/Speckle/Database/BIMElementDatabase.cpp @@ -33,8 +33,9 @@ namespace speckle::database { }; ///Element database storage declaration - class BIMElementDatabase::Store : public Storage { - using base = Storage; + class BIMElementDatabase::Store : public Storage { + using base = Storage; using base::base; }; @@ -75,6 +76,28 @@ BIMElementDatabase::BIMElementDatabase() { BIMElementDatabase::~BIMElementDatabase() {} +/*-------------------------------------------------------------------- + Get the available element tables + + targetType: An optional filtr for table type to retrieve, e.g. get all sections (nullopt = all table types) + + return: A list of available tables + --------------------------------------------------------------------*/ +BIMRecordIDList BIMElementDatabase::getTables(std::optional targetType) const { + return m_engine->getTables(targetType); +} //BIMElementDatabase::getTables + + +/*-------------------------------------------------------------------- + Bring the view of this database to the front (i.e. so the user sees it) + + tableID: The ID of the table to bring to the front + --------------------------------------------------------------------*/ +void BIMElementDatabase::bringViewToFront(BIMRecordID tableID) const { + m_engine->bringViewToFront(tableID); +} //BIMElementDatabase::bringViewToFront + + /*-------------------------------------------------------------------- Get the current user element selection @@ -110,7 +133,7 @@ void BIMElementDatabase::clearSelection() const { return: A list containing IDs of found elements (empty if none found) --------------------------------------------------------------------*/ -std::vector BIMElementDatabase::findElements(const Filter& filter, std::optional tableID, +BIMRecordIDList BIMElementDatabase::findElements(const Filter& filter, std::optional tableID, std::optional documentID) const { return m_engine->findObjects(filter, tableID, documentID); } //BIMElementDatabase::findElements @@ -120,6 +143,8 @@ std::vector BIMElementDatabase::findElements(const Filter& filter, Get a specified element elementID: The ID of the target element + tableID: Optional table ID (defaults to the first table) + documentID: Optional document ID (filter for this document only - nullopt = all objects) return: The requested element (nullptr on failure) --------------------------------------------------------------------*/ @@ -132,10 +157,14 @@ Element::Unique BIMElementDatabase::getElement(const BIMRecordID& elementID, std /*-------------------------------------------------------------------- Get all elements + tableID: Optional table ID (defaults to the first table) + documentID: Optional document ID (filter for this document only - nullopt = all objects) + return: All the elements --------------------------------------------------------------------*/ -Vector BIMElementDatabase::getElements() const { - return m_store->getObjects(); +Vector BIMElementDatabase::getElements(std::optional tableID, + std::optional documentID) const { + return m_store->getObjects(tableID, documentID); } //BIMElementDatabase::getElements diff --git a/SpeckleLib/Speckle/Database/BIMElementDatabase.h b/SpeckleLib/Speckle/Database/BIMElementDatabase.h index c9a9ad2..08a217d 100644 --- a/SpeckleLib/Speckle/Database/BIMElementDatabase.h +++ b/SpeckleLib/Speckle/Database/BIMElementDatabase.h @@ -2,6 +2,7 @@ #define CONNECTOR_DATABASE_BIM_ELEMENT_DATABASE #include "Speckle/Database/Identity/BIMLink.h" +#include "Speckle/Database/Storage/Element/ElementStorage.h" #include "Speckle/Record/Element/Element.h" #include "Speckle/Record/Element/Interface/Part.h" #include "Speckle/Utility/Guid.h" @@ -19,13 +20,8 @@ namespace speckle::database { /*! Database of model elements relating to a specific project */ - class BIMElementDatabase { + class BIMElementDatabase : public ElementStorage { public: - - // MARK: - Types - - ///Element filter (NB: expand in future to support optimised filtering) - using Filter = std::function; // MARK: - Constructors @@ -41,6 +37,17 @@ namespace speckle::database { // MARK: - Functions (const) + /*! + Get the available element tables + @param targetType An optional filtr for table type to retrieve, e.g. get all sections (nullopt = all table types) + @return A set of available tables + */ + BIMRecordIDList getTables(std::optional targetType) const; + /*! + Bring the view of this database to the front (i.e. so the user sees it) + @param tableID The ID of the table to bring to the front + */ + void bringViewToFront(BIMRecordID tableID) const; /*! Get the current user element selection @return A list of selected element IDs @@ -61,7 +68,7 @@ namespace speckle::database { @param documentID Optional document ID (filter for this document only - nullopt = all objects) @return A list containing IDs of found elements (empty if none found) */ - std::vector findElements(const Filter& filter = nullptr, std::optional tableID = std::nullopt, + BIMRecordIDList findElements(const Filter& filter = nullptr, std::optional tableID = std::nullopt, std::optional documentID = std::nullopt) const; /*! Get a specified element @@ -80,9 +87,12 @@ namespace speckle::database { std::unique_ptr getElement(const BIMLink& link) const { return getElement(link, link.tableID, link.docID); } /*! Get all model elements + @param tableID Optional table ID (defaults to the first table) + @param documentID Optional document ID (filter for this document only - nullopt = all objects) @return All the elements */ - active::container::Vector getElements() const; + active::container::Vector getElements(std::optional tableID = std::nullopt, + std::optional documentID = std::nullopt) const; /*! Get memo memo (supplementary) data for a specified element @param elementID The of the source element diff --git a/SpeckleLib/Speckle/Database/Storage/ArchicadDBase/Attribute/ArchicadAttributeDBaseEngine.h b/SpeckleLib/Speckle/Database/Storage/ArchicadDBase/Attribute/ArchicadAttributeDBaseEngine.h index 454172b..6b13c59 100644 --- a/SpeckleLib/Speckle/Database/Storage/ArchicadDBase/Attribute/ArchicadAttributeDBaseEngine.h +++ b/SpeckleLib/Speckle/Database/Storage/ArchicadDBase/Attribute/ArchicadAttributeDBaseEngine.h @@ -34,6 +34,7 @@ namespace speckle::database { using Attribute = record::attribute::Attribute; using Filter = base::Filter; using Outline = base::Outline; + using ObjIDList = base::ObjIDList; // MARK: - Constructors @@ -58,8 +59,8 @@ namespace speckle::database { @param documentID Optional document ID (filter for this document only - nullopt = all objects) @return A list containing IDs of found elements (empty if none found) */ - virtual std::vector findObjects(const Filter& filter = nullptr, std::optional tableID = std::nullopt, - std::optional documentID = std::nullopt) const override { return {}; } //Implement when required + virtual ObjIDList findObjects(const Filter& filter = nullptr, std::optional tableID = std::nullopt, + std::optional documentID = std::nullopt) const override { return {}; } //Implement when required /*! Get an object by ID @param objID The object ID diff --git a/SpeckleLib/Speckle/Database/Storage/ArchicadDBase/Element/ArchicadElementDBaseEngine.cpp b/SpeckleLib/Speckle/Database/Storage/ArchicadDBase/Element/ArchicadElementDBaseEngine.cpp index 9d73d60..ba51137 100644 --- a/SpeckleLib/Speckle/Database/Storage/ArchicadDBase/Element/ArchicadElementDBaseEngine.cpp +++ b/SpeckleLib/Speckle/Database/Storage/ArchicadDBase/Element/ArchicadElementDBaseEngine.cpp @@ -41,14 +41,24 @@ using enum ArchicadDBaseCore::Status; namespace { + //ID for the floor plan view + static const Guid primary2DViewID{String{"ddad27c0-c17b-4ad3-b76b-53d1e176d5ef"}}; + //ID for the 3D view + static const Guid primary3DViewID{String{"ec368939-fb7d-4d8c-bc88-6d29806d9212"}}; + /*! Get information about a specified Archicad table @param tableID The ID of the target table @return The requested table info (nullopt on failure) */ std::optional getTableInfo(const BIMRecordID& tableID) { - API_DatabaseInfo dbaseInfo; - dbaseInfo.databaseUnId.elemSetId = tableID; + API_DatabaseInfo dbaseInfo{}; + if (tableID == primary2DViewID) + dbaseInfo.typeID = APIWind_FloorPlanID; + else if (tableID == primary3DViewID) + dbaseInfo.typeID = APIWind_3DModelID; + else + dbaseInfo.databaseUnId.elemSetId = tableID; if (auto err = ACAPI_Window_GetDatabaseInfo(&dbaseInfo); err == NoError) return dbaseInfo; return std::nullopt; @@ -62,7 +72,7 @@ namespace { */ bool setActiveTable(const BIMRecordID& tableID) { if (!tableID) - return false; //Null guid doens't point to anything + return false; //Null guid doesn't point to anything if (auto activeTable = ArchicadElementDBaseEngine::getActiveTable(); activeTable && *activeTable == tableID) return true; auto dbaseInfo = getTableInfo(tableID); @@ -114,12 +124,34 @@ namespace { std::optional ArchicadElementDBaseEngine::getActiveTable() { API_WindowInfo dbaseInfo; active::utility::Memory::erase(dbaseInfo); - if (auto err = ACAPI_Database_GetCurrentDatabase(&dbaseInfo); err == NoError) + if (auto err = ACAPI_Database_GetCurrentDatabase(&dbaseInfo); err == NoError) { + if (dbaseInfo.typeID == APIWind_FloorPlanID) + return primary2DViewID; + else if (dbaseInfo.typeID == APIWind_3DModelID) + return primary3DViewID; return dbaseInfo.databaseUnId.elemSetId; + } return std::nullopt; } //ArchicadElementDBaseEngine::getActiveTable +/*-------------------------------------------------------------------- + Bring the view of this database to the front (i.e. so the user sees it) + + tableID: The ID of the table to bring to the front + --------------------------------------------------------------------*/ +void ArchicadElementDBaseEngine::bringViewToFront(BIMRecordID tableID) const { + auto dbaseInfo = getTableInfo(tableID); + if (!dbaseInfo) + return; + API_WindowInfo windowInfo{}; + windowInfo.typeID = dbaseInfo->typeID; + if ((windowInfo.typeID != APIWind_FloorPlanID) && (windowInfo.typeID != APIWind_3DModelID)) + windowInfo.databaseUnId = dbaseInfo->databaseUnId; + ACAPI_Window_ChangeWindow(&windowInfo); +} //ArchicadElementDBaseEngine::bringViewToFront + + /*-------------------------------------------------------------------- Get the current user element selection @@ -162,6 +194,24 @@ void ArchicadElementDBaseEngine::clearSelection() const { } //ArchicadElementDBaseEngine::clearSelection +/*-------------------------------------------------------------------- + Get the available dbase tables + + targetType: An optional filtr for table type/group to retrieve + + return: A list of available tables + --------------------------------------------------------------------*/ +ArchicadElementDBaseEngine::TableIDList ArchicadElementDBaseEngine::getTables(std::optional targetType) const { + using enum ElementStorage::TableType; + TableIDList result; + if (!targetType || (targetType == primary2D)) + result.insert(primary2DViewID); + if (!targetType || (targetType == primary3D)) + result.insert(primary3DViewID); + return result; +} //ArchicadElementDBaseEngine::getTables + + /*-------------------------------------------------------------------- Find a filtered list of objects @@ -171,16 +221,19 @@ void ArchicadElementDBaseEngine::clearSelection() const { return: A list containing IDs of found elements (empty if none found) --------------------------------------------------------------------*/ -std::vector ArchicadElementDBaseEngine::findObjects(const Filter& filter, std::optional tableID, +BIMRecordIDList ArchicadElementDBaseEngine::findObjects(const Filter& filter, std::optional tableID, std::optional documentID) const { + //Switch to the target table (when specified). Otherwise the currently active table will be used + if (tableID) + setActiveTable(*tableID); //First check for no filter (in which case we return all objects) if (filter == nullptr) { GS::Array found; if ((ACAPI_Element_GetElemList({}, &found) != NoError) || found.IsEmpty()) return {}; - std::vector result; + BIMRecordIDList result; for (const auto& item : found) - result.emplace_back(item); + result.insert(item); return result; } //Implement other filtering as required - ideally identify characteristics supported by API, e.g. filter by type/renovation etc diff --git a/SpeckleLib/Speckle/Database/Storage/ArchicadDBase/Element/ArchicadElementDBaseEngine.h b/SpeckleLib/Speckle/Database/Storage/ArchicadDBase/Element/ArchicadElementDBaseEngine.h index 18101d1..de703a8 100644 --- a/SpeckleLib/Speckle/Database/Storage/ArchicadDBase/Element/ArchicadElementDBaseEngine.h +++ b/SpeckleLib/Speckle/Database/Storage/ArchicadDBase/Element/ArchicadElementDBaseEngine.h @@ -4,6 +4,7 @@ #include "Active/Database/Storage/DBaseEngine.h" #include "Active/Serialise/UnboxedTransport.h" #include "Speckle/Database/Storage/ArchicadDBase/ArchicadDBaseCore.h" +#include "Speckle/Database/Storage/Element/ElementStorage.h" #include "Speckle/Database/Identity/BIMLink.h" #include "Speckle/Record/Element/Element.h" #include "Speckle/Utility/Guid.h" @@ -17,16 +18,17 @@ namespace speckle::database { /*! A database engine to read/write elements in an Archicad project database (local file or cloud-based) */ - class ArchicadElementDBaseEngine : public ArchicadDBaseCore, - public active::database::DBaseEngine { + class ArchicadElementDBaseEngine : public ArchicadDBaseCore, public ElementStorage, + public active::database::DBaseEngine { public: // MARK: - Types using base = active::database::DBaseEngine; using Element = record::element::Element; - using Filter = base::Filter; + using Filter = ElementStorage::Filter; using Outline = base::Outline; + using ObjIDList = base::ObjIDList; // MARK: - Constants @@ -53,6 +55,11 @@ namespace speckle::database { // MARK: - Functions (const) + /*! + Bring the view of this database to the front (i.e. so the user sees it) + @param tableID The ID of the table to bring to the front + */ + void bringViewToFront(BIMRecordID tableID) const; /*! Get the current user element selection @return A list of selected element IDs @@ -66,6 +73,12 @@ namespace speckle::database { Clear the element selection */ void clearSelection() const; + /*! + Get the available dbase tables + @param targetType An optional filtr for table type/group to retrieve + @return A list of available tables + */ + TableIDList getTables(std::optional targetType) const override; /*! Find a filtered list of objects @param filter The object filter (nullptr = find all objects) @@ -73,8 +86,8 @@ namespace speckle::database { @param documentID Optional document ID (filter for this document only - nullopt = all objects) @return A list containing IDs of found elements (empty if none found) */ - virtual std::vector findObjects(const Filter& filter = nullptr, std::optional tableID = std::nullopt, - std::optional documentID = std::nullopt) const override; + virtual BIMRecordIDList findObjects(const Filter& filter = nullptr, std::optional tableID = std::nullopt, + std::optional documentID = std::nullopt) const override; /*! Get an object by index @param objID The object ID diff --git a/SpeckleLib/Speckle/Database/Storage/ArchicadDBase/Property/ArchicadGroupDBaseEngine.h b/SpeckleLib/Speckle/Database/Storage/ArchicadDBase/Property/ArchicadGroupDBaseEngine.h index 92fc16b..629ad0b 100644 --- a/SpeckleLib/Speckle/Database/Storage/ArchicadDBase/Property/ArchicadGroupDBaseEngine.h +++ b/SpeckleLib/Speckle/Database/Storage/ArchicadDBase/Property/ArchicadGroupDBaseEngine.h @@ -29,6 +29,7 @@ namespace speckle::database { using Group = record::property::Group; using Filter = base::Filter; using Outline = base::Outline; + using ObjIDList = base::ObjIDList; // MARK: - Constructors @@ -53,8 +54,8 @@ namespace speckle::database { @param documentID Optional document ID (filter for this document only - nullopt = all objects) @return A list containing IDs of found elements (empty if none found) */ - virtual std::vector findObjects(const Filter& filter = nullptr, std::optional tableID = std::nullopt, - std::optional documentID = std::nullopt) const override { return {}; } //Implement when required + virtual ObjIDList findObjects(const Filter& filter = nullptr, std::optional tableID = std::nullopt, + std::optional documentID = std::nullopt) const override { return {}; } //Implement when required /*! Get an object by ID @param objID The object ID diff --git a/SpeckleLib/Speckle/Database/Storage/ArchicadDBase/Property/ArchicadPropertyDBaseEngine.h b/SpeckleLib/Speckle/Database/Storage/ArchicadDBase/Property/ArchicadPropertyDBaseEngine.h index a681ca5..05a5d92 100644 --- a/SpeckleLib/Speckle/Database/Storage/ArchicadDBase/Property/ArchicadPropertyDBaseEngine.h +++ b/SpeckleLib/Speckle/Database/Storage/ArchicadDBase/Property/ArchicadPropertyDBaseEngine.h @@ -30,6 +30,7 @@ namespace speckle::database { using Template = record::property::Template; using Filter = base::Filter; using Outline = base::Outline; + using ObjIDList = base::ObjIDList; // MARK: - Constructors @@ -54,8 +55,8 @@ namespace speckle::database { @param documentID Optional document ID (filter for this document only - nullopt = all objects) @return A list containing IDs of found elements (empty if none found) */ - virtual std::vector findObjects(const Filter& filter = nullptr, std::optional tableID = std::nullopt, - std::optional documentID = std::nullopt) const override { return {}; } //Implement when required + virtual ObjIDList findObjects(const Filter& filter = nullptr, std::optional tableID = std::nullopt, + std::optional documentID = std::nullopt) const override { return {}; } //Implement when required /*! Find all property templates linked to specified classifications @param classifications The classifications diff --git a/SpeckleLib/Speckle/Database/Storage/DocumentStore/DocumentStoreEngine.h b/SpeckleLib/Speckle/Database/Storage/DocumentStore/DocumentStoreEngine.h index 4e6d684..92dd4d7 100644 --- a/SpeckleLib/Speckle/Database/Storage/DocumentStore/DocumentStoreEngine.h +++ b/SpeckleLib/Speckle/Database/Storage/DocumentStore/DocumentStoreEngine.h @@ -46,6 +46,7 @@ namespace speckle::database { using base = active::database::DBaseEngine; using Filter = base::Filter; using Outline = base::Outline; + using ObjIDList = base::ObjIDList; using Cache = active::database::RecordCache; // MARK: - Constructors @@ -66,8 +67,8 @@ namespace speckle::database { @param documentID Optional document ID (filter for this document only - nullopt = all objects) @return A list containing IDs of found elements (empty if none found) */ - virtual std::vector findObjects(const Filter& filter = nullptr, std::optional tableID = std::nullopt, - std::optional documentID = std::nullopt) const override { return {}; } //Implement when required + virtual ObjIDList findObjects(const Filter& filter = nullptr, std::optional tableID = std::nullopt, + std::optional documentID = std::nullopt) const override { return {}; } //Implement when required /*! Get an object by index @param ID The object ID @@ -181,7 +182,7 @@ namespace speckle::database { auto storedData = readStore(); m_cache = std::make_unique(); if (!storedData) { - m_cache->setID(speckle::utility::Guid{true}.operator String()); //Needs an ID - used as substitute for the Speckle 'document ID' + m_cache->setID(speckle::utility::Guid{true}.operator speckle::utility::String()); //Needs an ID - substitute for the Speckle 'document ID' return m_cache.get(); //Return an empty container if there's no data } //Import the document data into the record cache diff --git a/SpeckleLib/Speckle/Database/Storage/Element/ElementStorage.h b/SpeckleLib/Speckle/Database/Storage/Element/ElementStorage.h new file mode 100644 index 0000000..d691b86 --- /dev/null +++ b/SpeckleLib/Speckle/Database/Storage/Element/ElementStorage.h @@ -0,0 +1,29 @@ +#ifndef SPECKLE_DATABASE_ELEMENT_STORAGE +#define SPECKLE_DATABASE_ELEMENT_STORAGE + +#include "Speckle/Record/Element/Element.h" + +namespace speckle::database { + + /*! + Fundamental concepts for element storage + */ + class ElementStorage { + public: + + // MARK: - Types + + ///Element storage table types + enum class TableType { + primary2D, + primary3D, + section, + elevation, + }; + ///Element filter + using Filter = std::function; + }; + +} + +#endif //SPECKLE_DATABASE_ELEMENT_STORAGE diff --git a/SpeckleLib/SpeckleLib.xcodeproj/project.pbxproj b/SpeckleLib/SpeckleLib.xcodeproj/project.pbxproj index c65f283..b24116a 100644 --- a/SpeckleLib/SpeckleLib.xcodeproj/project.pbxproj +++ b/SpeckleLib/SpeckleLib.xcodeproj/project.pbxproj @@ -303,6 +303,7 @@ 2196F3022CB57E7F00450DFC /* Storey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Storey.h; sourceTree = ""; }; 2196F3032CB57E7F00450DFC /* Storey.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Storey.cpp; sourceTree = ""; }; 219712682BE7E2D500D9EF7E /* Serialisation.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = Serialisation.md; sourceTree = ""; }; + 2199BB5F2CDB761B00A4BEEC /* ElementStorage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ElementStorage.h; sourceTree = ""; }; 21A0FB9F2CB880690023F24E /* FinishCollector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FinishCollector.h; sourceTree = ""; }; 21A0FBA92CB9324A0023F24E /* FinishProxy.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FinishProxy.h; sourceTree = ""; }; 21A0FBB42CBA5E380023F24E /* Str256.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Str256.h; sourceTree = ""; }; @@ -708,6 +709,14 @@ path = SpeckleLibDoctest; sourceTree = ""; }; + 2199BB602CDB761B00A4BEEC /* Element */ = { + isa = PBXGroup; + children = ( + 2199BB5F2CDB761B00A4BEEC /* ElementStorage.h */, + ); + path = Element; + sourceTree = ""; + }; 21A0FBA02CB880690023F24E /* Collection */ = { isa = PBXGroup; children = ( @@ -886,6 +895,7 @@ 21D0BDB02C8F8AB60077E104 /* Storage */ = { isa = PBXGroup; children = ( + 2199BB602CDB761B00A4BEEC /* Element */, 215F088A2CA195EC00CD343B /* ArchicadDBase */, 21D0BDAF2C8F8AB60077E104 /* DocumentStore */, );