4ff7d68516
Updates to allow attribute changes to be written Workaround to force VS to recognise template specialisations
493 lines
18 KiB
C++
493 lines
18 KiB
C++
#include "Speckle/Database/Storage/ArchicadDBase/Element/ArchicadElementDBaseEngine.h"
|
|
|
|
#ifdef ARCHICAD
|
|
|
|
#include "Active/Utility/Defer.h"
|
|
#include "Active/Utility/Memory.h"
|
|
#include "Active/Utility/String.h"
|
|
#include "Speckle/Database/Identity/BIMLink.h"
|
|
#include "Speckle/Environment/Addon.h"
|
|
#include "Speckle/Environment/Project.h"
|
|
#include "Speckle/Event/Type/DocStoreMergeEvent.h"
|
|
#include "Speckle/Event/Type/ProjectEvent.h"
|
|
#include "Speckle/Record/Element/Column.h"
|
|
#include "Speckle/Record/Element/ColumnSegment.h"
|
|
#include "Speckle/Record/Element/GenericModelElement.h"
|
|
#include "Speckle/Record/Element/Beam.h"
|
|
#include "Speckle/Record/Element/BeamSegment.h"
|
|
#include "Speckle/Record/Element/Memo.h"
|
|
#include "Speckle/Record/Element/MeshElem.h"
|
|
#include "Speckle/Record/Element/Morph.h"
|
|
#include "Speckle/Record/Element/Roof.h"
|
|
#include "Speckle/Record/Element/Shell.h"
|
|
#include "Speckle/Record/Element/Slab.h"
|
|
#include "Speckle/Record/Element/Stair.h"
|
|
#include "Speckle/Record/Element/StairRiser.h"
|
|
#include "Speckle/Record/Element/StairStructure.h"
|
|
#include "Speckle/Record/Element/StairTread.h"
|
|
#include "Speckle/Record/Element/Wall.h"
|
|
#include "Speckle/Utility/Guid.h"
|
|
#include "Speckle/Utility/String.h"
|
|
|
|
#include <ACAPinc.h>
|
|
#include <BM.hpp>
|
|
#ifdef ServerMainVers_2700
|
|
#include <ACAPI_Database.h>
|
|
#endif
|
|
|
|
using namespace active::event;
|
|
using namespace active::setting;
|
|
using namespace speckle::database;
|
|
using namespace speckle::environment;
|
|
using namespace speckle::event;
|
|
using namespace speckle::record::element;
|
|
using namespace speckle::utility;
|
|
|
|
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<API_DatabaseInfo> getTableInfo(const BIMRecordID& tableID) {
|
|
API_DatabaseInfo dbaseInfo{};
|
|
if (tableID == primary2DViewID)
|
|
dbaseInfo.typeID = APIWind_FloorPlanID;
|
|
else if (tableID == primary3DViewID)
|
|
dbaseInfo.typeID = APIWind_3DModelID;
|
|
else
|
|
dbaseInfo.databaseUnId.elemSetId = tableID;
|
|
#ifdef ServerMainVers_2700
|
|
if (auto err = ACAPI_Window_GetDatabaseInfo(&dbaseInfo); err == NoError)
|
|
#else
|
|
if (auto err = ACAPI_Database(APIDb_GetDatabaseInfoID, &dbaseInfo, 0, 0); err == NoError)
|
|
#endif
|
|
return dbaseInfo;
|
|
return std::nullopt;
|
|
} //getTableInfo
|
|
|
|
|
|
/*!
|
|
Set the active Archicad table
|
|
@param tableID The target table ID
|
|
@return True on success
|
|
*/
|
|
bool setActiveTable(const BIMRecordID& tableID) {
|
|
if (!tableID)
|
|
return false; //Null guid doesn't point to anything
|
|
if (auto activeTable = ArchicadElementDBaseEngine::getActiveTable(); activeTable && *activeTable == tableID)
|
|
return true;
|
|
auto dbaseInfo = getTableInfo(tableID);
|
|
if (!dbaseInfo)
|
|
return false;
|
|
#ifdef ServerMainVers_2700
|
|
return ACAPI_Database_ChangeCurrentDatabase(&*dbaseInfo) == NoError;
|
|
#else
|
|
return ACAPI_Database(APIDb_ChangeCurrentDatabaseID, &dbaseInfo, 0, 0) == NoError;
|
|
#endif
|
|
} //setActiveTable
|
|
|
|
|
|
/*!
|
|
Find indices of all elements in an Archicad database. NB: It is assumed that the active database has already been set
|
|
@return A list of all element IDs in the active database
|
|
*/
|
|
BIMRecordIDList getAllElementIDs() {
|
|
GS::Array<API_Guid> found;
|
|
if ((ACAPI_Element_GetElemList({}, &found) != NoError) || found.IsEmpty())
|
|
return {};
|
|
BIMRecordIDList result;
|
|
for (const auto& item : found)
|
|
result.insert(item);
|
|
return result;
|
|
} //getAllElementIDs
|
|
|
|
|
|
/*!
|
|
Make a new element object
|
|
@param elementData The API element representation
|
|
@param tableID The ID of the parent table (defaults to the active drawing)
|
|
@return A new element object (nullptr on failure)
|
|
*/
|
|
Element::Unique makeElement(const API_Element& elementData, const BIMRecordID& tableID) {
|
|
#ifdef ServerMainVers_2600
|
|
switch (elementData.header.type.typeID) {
|
|
#else
|
|
switch (elementData.header.typeID) {
|
|
#endif
|
|
case API_ColumnID:
|
|
return std::make_unique<Column>(elementData, tableID);
|
|
case API_ColumnSegmentID:
|
|
return std::make_unique<ColumnSegment>(elementData, tableID);
|
|
case API_BeamID:
|
|
return std::make_unique<Beam>(elementData, tableID);
|
|
case API_BeamSegmentID:
|
|
return std::make_unique<BeamSegment>(elementData, tableID);
|
|
case API_MeshID:
|
|
return std::make_unique<Mesh>(elementData, tableID);
|
|
case API_MorphID:
|
|
return std::make_unique<Morph>(elementData, tableID);
|
|
case API_RiserID:
|
|
return std::make_unique<StairRiser>(elementData, tableID);
|
|
case API_StairStructureID:
|
|
return std::make_unique<StairStructure>(elementData, tableID);
|
|
case API_TreadID:
|
|
return std::make_unique<StairTread>(elementData, tableID);
|
|
case API_RoofID:
|
|
return std::make_unique<Roof>(elementData, tableID);
|
|
case API_ShellID:
|
|
return std::make_unique<Shell>(elementData, tableID);
|
|
case API_SlabID:
|
|
return std::make_unique<Slab>(elementData, tableID);
|
|
case API_StairID:
|
|
return std::make_unique<Stair>(elementData, tableID);
|
|
case API_WallID:
|
|
return std::make_unique<Wall>(elementData, tableID);
|
|
case API_ObjectID:
|
|
// POC: change this case once we are ready to convert Grid Elements
|
|
#ifdef ServerMainVers_2600
|
|
if (elementData.header.type.variationID == APIVarId_GridElement)
|
|
#else
|
|
if (elementData.header.variationID == APIVarId_GridElement)
|
|
#endif
|
|
return nullptr;
|
|
default:
|
|
return std::make_unique<GenericModelElement>(elementData, tableID);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*--------------------------------------------------------------------
|
|
Get the ID of the active Archicad table
|
|
|
|
return; The active table ID (nullopt on failure)
|
|
--------------------------------------------------------------------*/
|
|
std::optional<BIMRecordID> ArchicadElementDBaseEngine::getActiveTable() {
|
|
API_WindowInfo dbaseInfo;
|
|
active::utility::Memory::erase(dbaseInfo);
|
|
#ifdef ServerMainVers_2700
|
|
if (auto err = ACAPI_Database_GetCurrentDatabase(&dbaseInfo); err == NoError)
|
|
#else
|
|
if (auto err = ACAPI_Database(APIDb_GetCurrentDatabaseID, &dbaseInfo); err == NoError)
|
|
#endif
|
|
{
|
|
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;
|
|
#ifdef ServerMainVers_2700
|
|
ACAPI_Window_ChangeWindow(&windowInfo);
|
|
#else
|
|
ACAPI_Automate(APIDo_ChangeWindowID, &windowInfo);
|
|
#endif
|
|
} //ArchicadElementDBaseEngine::bringViewToFront
|
|
|
|
|
|
/*--------------------------------------------------------------------
|
|
Get the current user element selection
|
|
|
|
return: A list of selected element IDs
|
|
--------------------------------------------------------------------*/
|
|
BIMLinkList ArchicadElementDBaseEngine::getSelection() const {
|
|
auto tableID = getActiveTable();
|
|
if (!tableID)
|
|
return {};
|
|
BIMLinkList result;
|
|
API_SelectionInfo selectionInfo;
|
|
active::utility::Memory::erase(selectionInfo);
|
|
GS::Array<API_Neig> selection;
|
|
if (auto err = ACAPI_Selection_Get(&selectionInfo, &selection, true); err == NoError) {
|
|
for (const auto& item : selection)
|
|
result.push_back(BIMLink{item, *tableID});
|
|
}
|
|
return result;
|
|
} //ArchicadElementDBaseEngine::getSelection
|
|
|
|
|
|
/*--------------------------------------------------------------------
|
|
Set the element selection
|
|
--------------------------------------------------------------------*/
|
|
void ArchicadElementDBaseEngine::setSelection(const BIMLinkList& elementIDs) const {
|
|
GS::Array<API_Neig> selNeigs;
|
|
for (const auto& elemID : elementIDs) {
|
|
API_Neig neig(elemID);
|
|
selNeigs.Push(neig);
|
|
}
|
|
#ifdef ServerMainVers_2700
|
|
ACAPI_Selection_Select(selNeigs, true);
|
|
#else
|
|
ACAPI_Element_Select(selNeigs, true);
|
|
#endif
|
|
} //ArchicadElementDBaseEngine::setSelection
|
|
|
|
|
|
/*--------------------------------------------------------------------
|
|
Clear the element selection
|
|
--------------------------------------------------------------------*/
|
|
void ArchicadElementDBaseEngine::clearSelection() const {
|
|
#ifdef ServerMainVers_2700
|
|
ACAPI_Selection_DeselectAll();
|
|
#else
|
|
ACAPI_Element_DeselectAll();
|
|
#endif
|
|
} //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<TableType> 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
|
|
|
|
|
|
/*--------------------------------------------------------------------
|
|
Get the default dbase table
|
|
|
|
return: The default dbase table (nullopt if no table is available)
|
|
--------------------------------------------------------------------*/
|
|
std::optional<BIMRecordID> ArchicadElementDBaseEngine::getDefaultTable() const {
|
|
return ArchicadElementDBaseEngine::getActiveTable();
|
|
} //ArchicadElementDBaseEngine::getDefaultTable
|
|
|
|
|
|
/*--------------------------------------------------------------------
|
|
Set the default dbase table
|
|
|
|
tableID: The new default dbase table
|
|
--------------------------------------------------------------------*/
|
|
void ArchicadElementDBaseEngine::setDefaultTable(const BIMRecordID& tableID) const {
|
|
setActiveTable(tableID);
|
|
} //ArchicadElementDBaseEngine::setDefaultTable
|
|
|
|
|
|
/*--------------------------------------------------------------------
|
|
Find a filtered list of objects
|
|
|
|
filter: The object filter (nullptr = find all objects)
|
|
subset: A subset of the database content to search (specified by record ID)
|
|
tableID: Optional table ID (defaults to the first table)
|
|
documentID: Optional document ID (filter for this document only - nullopt = all objects)
|
|
|
|
return: A list containing IDs of found elements (empty if none found)
|
|
--------------------------------------------------------------------*/
|
|
BIMRecordIDList ArchicadElementDBaseEngine::findObjects(const Filter* filter, const BIMRecordIDList& subset, std::optional<BIMRecordID> tableID,
|
|
std::optional<BIMRecordID> 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) {
|
|
if (!subset.empty())
|
|
return subset;
|
|
return getAllElementIDs();
|
|
}
|
|
BIMRecordIDList buffer, result;
|
|
//Pick either all records or the specified subset
|
|
auto source = ⊂
|
|
if (subset.empty()) {
|
|
buffer = getAllElementIDs();
|
|
source = &buffer;
|
|
}
|
|
//Run the filter on the specified elements
|
|
for (const auto& elemID : *source) {
|
|
if (auto element = getObject(elemID); element) {
|
|
if ((*filter)(*element))
|
|
result.insert(elemID);
|
|
}
|
|
}
|
|
return result;
|
|
} //ArchicadElementDBaseEngine::findObjects
|
|
|
|
|
|
/*--------------------------------------------------------------------
|
|
Get an object by index
|
|
|
|
index: The object index
|
|
tableID: Optional table ID (defaults to the floor plan)
|
|
documentID: Optional document ID (when the object is bound to a specific document)
|
|
|
|
return: The requested object (nullptr on failure)
|
|
--------------------------------------------------------------------*/
|
|
std::unique_ptr<Element> ArchicadElementDBaseEngine::getObject(const BIMRecordID& ID, std::optional<BIMRecordID> tableID,
|
|
std::optional<BIMRecordID> documentID) const {
|
|
//Check for memo table requests
|
|
if (tableID == memoTable) {
|
|
auto memo = std::make_unique<API_ElementMemo>();
|
|
active::utility::Memory::erase(*memo);
|
|
//Use memo filtering when requested
|
|
uint64_t filter = documentID ? Guid::toInt(*documentID) : APIMemoMask_All;
|
|
if (auto err = ACAPI_Element_GetMemo(ID, memo.get(), filter); err != NoError)
|
|
ACAPI_DisposeElemMemoHdls(memo.get());
|
|
else {
|
|
auto result = std::make_unique<Memo>();
|
|
result->set(std::move(memo));
|
|
return result;
|
|
}
|
|
}
|
|
if (!tableID) {
|
|
//Use the active table if none is specified
|
|
tableID = getActiveTable();
|
|
if (!tableID)
|
|
return nullptr;
|
|
}
|
|
API_Element element;
|
|
active::utility::Memory::erase(element);
|
|
API_Guid guid{ID.operator API_Guid()};
|
|
#ifdef ServerMainVers_2700
|
|
if (ACAPI_Element_GetElementFromAnywhere(&guid, &element) != NoError)
|
|
#else
|
|
if (ACAPI_Database(APIDb_GetElementFromAnywhereID, &guid, &element, 0) != NoError)
|
|
#endif
|
|
return nullptr;
|
|
return makeElement(element, *tableID);
|
|
} //ArchicadElementDBaseEngine::getObject
|
|
|
|
|
|
/*--------------------------------------------------------------------
|
|
Get an object in a transportable form, e.g. packaged for serialisation
|
|
|
|
index: The object index
|
|
tableID: Optional table ID (defaults to the floor plan)
|
|
documentID: Optional document ID (when the object is bound to a specific document)
|
|
|
|
return: The requested wrapped cargo (nullptr on failure)
|
|
--------------------------------------------------------------------*/
|
|
active::serialise::Cargo::Unique ArchicadElementDBaseEngine::getObjectCargo(const BIMRecordID& ID, std::optional<BIMRecordID> tableID,
|
|
std::optional<BIMRecordID> documentID) const {
|
|
return nullptr; //TODO: Implement
|
|
} //ArchicadElementDBaseEngine::getObject
|
|
|
|
|
|
/*--------------------------------------------------------------------
|
|
Get all objects
|
|
|
|
tableID: Optional table ID (defaults to the floor plan)
|
|
documentID: Optional document ID (filter for this document only - nullopt = all objects)
|
|
|
|
return: The requested objects (nullptr on failure)
|
|
--------------------------------------------------------------------*/
|
|
active::container::Vector<Element> ArchicadElementDBaseEngine::getObjects(std::optional<BIMRecordID> tableID,
|
|
std::optional<BIMRecordID> documentID) const {
|
|
if (tableID)
|
|
setActiveTable(*tableID);
|
|
else {
|
|
//Use the active table if none is specified
|
|
tableID = getActiveTable();
|
|
if (!tableID)
|
|
return {};
|
|
}
|
|
//Retrieve the element objects to build the result
|
|
active::container::Vector<Element> result;
|
|
auto objectIDs = findObjects();
|
|
for (const auto& ID : objectIDs)
|
|
if (auto element = getObject(ID, tableID); element)
|
|
result.emplace_back(std::move(element));
|
|
return result;
|
|
} //ArchicadElementDBaseEngine::getObjects
|
|
|
|
|
|
/*--------------------------------------------------------------------
|
|
Get all objects
|
|
|
|
filter: The object filter
|
|
tableID: Optional table ID (defaults to the floor plan)
|
|
documentID: Optional document ID (filter for this document only - nullopt = all objects)
|
|
|
|
return: The requested objects (nullptr on failure)
|
|
--------------------------------------------------------------------*/
|
|
active::container::Vector<Element> ArchicadElementDBaseEngine::getObjects(const Filter& filter, std::optional<BIMRecordID> tableID,
|
|
std::optional<BIMRecordID> documentID) const {
|
|
return {};
|
|
} //ArchicadElementDBaseEngine::getObjects
|
|
|
|
|
|
/*--------------------------------------------------------------------
|
|
Write an object to the database
|
|
|
|
object: The object to write
|
|
objID: The object ID
|
|
objDocID: The object document-specific ID (unique within a specific document - nullopt if not document-bound)
|
|
tableID: Optional table ID (defaults to the floor plan)
|
|
documentID: Optional document ID (when the object is bound to a specific document)
|
|
--------------------------------------------------------------------*/
|
|
void ArchicadElementDBaseEngine::write(Element& object, const BIMRecordID& objID, std::optional<BIMRecordID> objDocID,
|
|
std::optional<BIMRecordID> tableID, std::optional<BIMRecordID> documentID) const {
|
|
|
|
} //ArchicadElementDBaseEngine::write
|
|
|
|
|
|
/*--------------------------------------------------------------------
|
|
Erase an object by index
|
|
|
|
objID: The object ID
|
|
tableID: Optional table ID (defaults to the floor plan)
|
|
documentID: Optional document ID (when the object is bound to a specific document)
|
|
|
|
return: True if the object was successfully erased
|
|
--------------------------------------------------------------------*/
|
|
void ArchicadElementDBaseEngine::erase(const BIMRecordID& ID, std::optional<BIMRecordID> tableID,
|
|
std::optional<BIMRecordID> documentID) const {
|
|
|
|
} //ArchicadElementDBaseEngine::erase
|
|
|
|
|
|
/*--------------------------------------------------------------------
|
|
Erase all objects
|
|
|
|
tableID: Optional table ID (defaults to the floor plan)
|
|
documentID: Optional document ID (filter for this document only - nullopt = all objects)
|
|
--------------------------------------------------------------------*/
|
|
void ArchicadElementDBaseEngine::erase(std::optional<BIMRecordID> tableID, std::optional<BIMRecordID> documentID) const {
|
|
|
|
} //ArchicadElementDBaseEngine::erase
|
|
|
|
|
|
/*--------------------------------------------------------------------
|
|
Get the database outline
|
|
|
|
return: The database outline
|
|
--------------------------------------------------------------------*/
|
|
ArchicadElementDBaseEngine::Outline ArchicadElementDBaseEngine::getOutline() const {
|
|
return {};
|
|
} //ArchicadElementDBaseEngine::getOutline
|
|
|
|
#endif
|