From f5b5ff648787f7a84f813a2b4393cc722b9fb51f Mon Sep 17 00:00:00 2001 From: David Kekesi Date: Mon, 21 Oct 2024 09:41:55 +0200 Subject: [PATCH 1/3] create Beam and supporting classes --- .../Element/ArchicadElementDBaseEngine.cpp | 7 +- SpeckleLib/Speckle/Record/Element/Beam.cpp | 174 ++++++++++++++++++ SpeckleLib/Speckle/Record/Element/Beam.h | 137 ++++++++++++++ .../Speckle/Record/Element/BeamSegment.cpp | 159 ++++++++++++++++ .../Speckle/Record/Element/BeamSegment.h | 130 +++++++++++++ .../Element/Interface/SegmentedBeam.cpp | 160 ++++++++++++++++ .../Record/Element/Interface/SegmentedBeam.h | 103 +++++++++++ SpeckleLib/SpeckleLib17.vcxproj | 6 + SpeckleLib/SpeckleLib17.vcxproj.filters | 18 ++ 9 files changed, 893 insertions(+), 1 deletion(-) create mode 100644 SpeckleLib/Speckle/Record/Element/Beam.cpp create mode 100644 SpeckleLib/Speckle/Record/Element/Beam.h create mode 100644 SpeckleLib/Speckle/Record/Element/BeamSegment.cpp create mode 100644 SpeckleLib/Speckle/Record/Element/BeamSegment.h create mode 100644 SpeckleLib/Speckle/Record/Element/Interface/SegmentedBeam.cpp create mode 100644 SpeckleLib/Speckle/Record/Element/Interface/SegmentedBeam.h diff --git a/SpeckleLib/Speckle/Database/Storage/ArchicadDBase/Element/ArchicadElementDBaseEngine.cpp b/SpeckleLib/Speckle/Database/Storage/ArchicadDBase/Element/ArchicadElementDBaseEngine.cpp index a1d7fd5..bb22560 100644 --- a/SpeckleLib/Speckle/Database/Storage/ArchicadDBase/Element/ArchicadElementDBaseEngine.cpp +++ b/SpeckleLib/Speckle/Database/Storage/ArchicadDBase/Element/ArchicadElementDBaseEngine.cpp @@ -13,6 +13,8 @@ #include "Speckle/Record/Element/Column.h" #include "Speckle/Record/Element/ColumnSegment.h" #include "Speckle/Record/Element/GenericElement.h" +#include "Speckle/Record/Element/Beam.h" +#include "Speckle/Record/Element/BeamSegment.h" #include "Speckle/Record/Element/Memo.h" #include "Speckle/Utility/Guid.h" #include "Speckle/Utility/String.h" @@ -76,11 +78,14 @@ namespace { return std::make_unique(elementData, tableID); case API_ColumnSegmentID: return std::make_unique(elementData, tableID); + case API_BeamID: + return std::make_unique(elementData, tableID); + case API_BeamSegmentID: + return std::make_unique(elementData, tableID); default: return std::make_unique(elementData, tableID); } } - } /*-------------------------------------------------------------------- diff --git a/SpeckleLib/Speckle/Record/Element/Beam.cpp b/SpeckleLib/Speckle/Record/Element/Beam.cpp new file mode 100644 index 0000000..1cad612 --- /dev/null +++ b/SpeckleLib/Speckle/Record/Element/Beam.cpp @@ -0,0 +1,174 @@ +#include "Speckle/Record/Element/Beam.h" + +#include "Active/Serialise/Item/Wrapper/ValueWrap.h" +#include "Active/Serialise/Package/Wrapper/PackageWrap.h" +#include "Active/Serialise/Package/Wrapper/ContainerWrap.h" +#include "Speckle/Environment/Addon.h" +#include "Speckle/Primitive/Mesh/Mesh.h" +#include "Speckle/SpeckleResource.h" +#include "Speckle/Utility/Guid.h" + +using namespace active::serialise; +using namespace speckle::environment; +using namespace speckle::record::attribute; +using namespace speckle::record::element; +using namespace speckle::utility; + +#include +#include + +namespace speckle::record::element { + + class Beam::Data { + public: + friend class Beam; + +#ifdef ARCHICAD + Data(const API_Element& elem) : root{ std::make_unique(elem.beam) } {} + Data(const Data& source) : root{ std::make_unique(*source.root) } {} +#endif + + private: + std::unique_ptr root; + }; + +} + +namespace { + + ///Serialisation fields + enum FieldIndex { + segmentID, + }; + + ///Serialisation field IDs + static std::array fieldID = { + Identity{"segments"}, + }; + +} + +/*-------------------------------------------------------------------- + Default constructor + --------------------------------------------------------------------*/ +Beam::Beam() { +} //Beam::Beam + + +#ifdef ARCHICAD +/*-------------------------------------------------------------------- + Constructor + + elemData: Archicad element data + tableID: The element table ID (AC database, e.g. floor plan, 3D) + --------------------------------------------------------------------*/ +Beam::Beam(const API_Element& elemData, const speckle::utility::Guid& tableID) : base{ elemData.header.guid, tableID } { + m_data = std::make_unique(elemData); +} //Beam::Beam +#endif + + +/*-------------------------------------------------------------------- + Copy constructor + + source: The object to copy + --------------------------------------------------------------------*/ +Beam::Beam(const Beam& source) : base{ source } { + m_data = source.m_data ? std::make_unique(*m_data) : nullptr; +} //Beam::Beam + + +/*-------------------------------------------------------------------- + Destructor + --------------------------------------------------------------------*/ +Beam::~Beam() {} + + +#ifdef ARCHICAD +/*-------------------------------------------------------------------- + Get the (immutable) API element header data + + return: The element header data (only use this data for low-level operations - for normal code, call getters/setters) + --------------------------------------------------------------------*/ +const API_Elem_Head& Beam::getHead() const { + return m_data->root->head; +} //Beam::getHead + +/*-------------------------------------------------------------------- + Get the (mutable) API element header data + + return: The element header data (only use this data for low-level operations - for normal code, call getters/setters) + --------------------------------------------------------------------*/ +API_Elem_Head& Beam::getHead() { + return m_data->root->head; +} //Beam::getHead + + +/*-------------------------------------------------------------------- + Load the element memo structure (elements must override according to requirements) + + filter: Filter bits specifying memo requirements + --------------------------------------------------------------------*/ +void Beam::loadMemo(filter_bits filter, std::unique_ptr& memo) const { + //Establish the memo filter for this element + if (!SegmentedBeam::isMemoLoaded()) + filter |= SegmentedBeam::getPartFilter(); + Element::loadMemo(filter, memo); + //Receive the memo data into the element (when available) + if (memo) { + if (filter & SegmentedBeam::getPartFilter()) + SegmentedBeam::receive(*memo); + } + SegmentedBeam::setMemoLoaded(true); //Always mark the data as loaded to prevent repeated attempts on error +} //Beam::loadMemo +#endif + + +/*-------------------------------------------------------------------- + Fill an inventory with the package items + + inventory: The inventory to receive the package items + + return: True if the package has added items to the inventory + --------------------------------------------------------------------*/ +bool Beam::fillInventory(Inventory& inventory) const { + using enum Entry::Type; + inventory.merge(Inventory{ + { + { fieldID[segmentID], segmentID, getSegmentCount(), std::nullopt }, //TODO: implement other fields + }, + }.withType(&typeid(Beam))); + return base::fillInventory(inventory); +} //Beam::fillInventory + + +/*-------------------------------------------------------------------- + Get the specified cargo + + item: The inventory item to retrieve + + return: The requested cargo (nullptr on failure) + --------------------------------------------------------------------*/ +Cargo::Unique Beam::getCargo(const Inventory::Item& item) const { + if (item.ownerType != &typeid(Beam)) + return base::getCargo(item); + using namespace active::serialise; + switch (item.index) { + case segmentID: + if (auto segment = getSegment(item.available); segment != nullptr) { + return Cargo::Unique{new PackageWrap{*segment}}; + } else + return nullptr; + default: + return nullptr; //Requested an unknown index + } +} //Beam::getCargo + + +/*-------------------------------------------------------------------- + Set to the default package content + --------------------------------------------------------------------*/ +void Beam::setDefault() { + base::setDefault(); + m_data.reset(); +} //Beam::setDefault diff --git a/SpeckleLib/Speckle/Record/Element/Beam.h b/SpeckleLib/Speckle/Record/Element/Beam.h new file mode 100644 index 0000000..3c3b062 --- /dev/null +++ b/SpeckleLib/Speckle/Record/Element/Beam.h @@ -0,0 +1,137 @@ +#ifndef SPECKLE_RECORD_ELEMENT_BEAM +#define SPECKLE_RECORD_ELEMENT_BEAM + +#include "Speckle/Record/Element/BeamSegment.h" +#include "Speckle/Record/Element/Element.h" +#include "Speckle/Record/Element/Interface/Assembly/Path.h" +#include "Speckle/Record/Element/Interface/SegmentedBeam.h" + +namespace speckle::record::element { + + class BeamSegment; + + /*! + BIM beam class + */ + class Beam : public Element, public SegmentedBeam, public assembly::Path { + public: + + // MARK: - Types + + using base = Element; + ///Unique pointer + using Unique = std::unique_ptr; + ///Shared pointer + using Shared = std::shared_ptr; + ///Optional + using Option = std::optional; + + // MARK: - Constructors + + using base::base; + + /*! + Default constructor + */ + Beam(); +#ifdef ARCHICAD + /*! + Constructor + @param elemData Archicad element data + @param tableID The beam element ID (AC database, e.g. floor plan, 3D) + */ + Beam(const API_Element& elemData, const speckle::utility::Guid& tableID); +#endif + /*! + Copy constructor + @param source The object to copy + */ + Beam(const Beam& source); + /*! + Destructor + */ + ~Beam(); + + /*! + Object cloning + @return A clone of this object + */ + Beam* clonePtr() const override { return new Beam{*this}; } + + + // MARK: - Functions (const) + + /*! + Get the BIM application parent table ID + @return The BIM table ID + */ + virtual database::BIMRecordID getTableID() const override { return Element::getTableID(); } + /*! + Get the element body + @return nullptr (Beams don't explicitly have a 3D body - this comes from its child segments) + */ + virtual Body* getBody() const override { return nullptr; } + /*! + Get the number of segments in the path (elements must override according to requirements) + @return The segment count + */ + virtual size_t getSegmentCount() const override { return SegmentedBeam::getSegmentCount(); } + /*! + Get a segment from the path (elements must override according to requirements) + @param index The index of the required segment + @return The requested segment (nullptr on failure) + */ + virtual BeamSegment* getSegment(size_t index) const override { return SegmentedBeam::getSegment(index); } +#ifdef ARCHICAD + /*! + Get the (immutable) API element header data + @return The element header data (only use this data for low-level operations - for normal code, call getters/setters) + */ + const API_Elem_Head& getHead() const override; +#endif + + // MARK: - Functions (mutating) + +#ifdef ARCHICAD + /*! + Get the (mutable) API element header data + @return The element header data (only use this data for low-level operations - for normal code, call getters/setters) + */ + API_Elem_Head& getHead() override; +#endif + + // MARK: - Serialisation + + /*! + Fill an inventory with the package items + @param inventory The inventory to receive the package items + @return True if the package has added items to the inventory + */ + bool fillInventory(active::serialise::Inventory& inventory) const override; + /*! + Get the specified cargo + @param item The inventory item to retrieve + @return The requested cargo (nullptr on failure) + */ + Cargo::Unique getCargo(const active::serialise::Inventory::Item& item) const override; + /*! + Set to the default package content + */ + void setDefault() override; + + protected: + /*! + Load the element memo structure (elements must override according to requirements) + @param filter Filter bits specifying memo requirements + */ + virtual void loadMemo(filter_bits filter, std::unique_ptr& memo) const override; + + private: + class Data; + ///The beam data + std::unique_ptr m_data; + }; + +} + +#endif //SPECKLE_RECORD_ELEMENT_BEAM diff --git a/SpeckleLib/Speckle/Record/Element/BeamSegment.cpp b/SpeckleLib/Speckle/Record/Element/BeamSegment.cpp new file mode 100644 index 0000000..b9bf172 --- /dev/null +++ b/SpeckleLib/Speckle/Record/Element/BeamSegment.cpp @@ -0,0 +1,159 @@ +#include "Speckle/Record/Element/BeamSegment.h" + +#include "Active/Serialise/Item/Wrapper/ValueWrap.h" +#include "Active/Serialise/Package/Wrapper/PackageWrap.h" +#include "Active/Serialise/Package/Wrapper/ContainerWrap.h" +#include "Speckle/Environment/Addon.h" +#include "Speckle/Primitive/Mesh/Mesh.h" +#include "Speckle/SpeckleResource.h" +#include "Speckle/Utility/Guid.h" + +#ifdef ARCHICAD +#include +#endif + +using namespace active::serialise; +using namespace speckle::environment; +using namespace speckle::record::attribute; +using namespace speckle::record::element; +using namespace speckle::utility; + +#include +#include + +namespace speckle::record::element { + + class BeamSegment::Data { + public: + friend class BeamSegment; + +#ifdef ARCHICAD + Data(const API_BeamSegmentType& seg) : root{ std::make_unique(seg) } {} + Data(const Data& source) : root{ std::make_unique(*source.root) } {} + + private: + std::unique_ptr root; +#endif + }; + +} + +/*-------------------------------------------------------------------- + Default constructor + --------------------------------------------------------------------*/ +BeamSegment::BeamSegment() { +} //BeamSegment::BeamSegment + + +#ifdef ARCHICAD +/*-------------------------------------------------------------------- + Constructor + + elemData: Archicad element data + tableID: The element table ID (AC database, e.g. floor plan, 3D) + --------------------------------------------------------------------*/ +BeamSegment::BeamSegment(const API_Element& elemData, const speckle::utility::Guid& tableID) : base{ elemData.header.guid, tableID } { + m_data = std::make_unique(elemData.beamSegment); +} //BeamSegment::BeamSegment + + +/*-------------------------------------------------------------------- + Constructor + + segment: The segment element data + tableID: The parent table ID + cutOrigin: Cut at the segment origin + cutEnd: Cut at the segment end + scheme: The segment scheme + profile: The segment profile (nullptr = none) + --------------------------------------------------------------------*/ +BeamSegment::BeamSegment(const API_BeamSegmentType& segment, const speckle::utility::Guid& tableID, const API_AssemblySegmentCutData& cutOrigin, + const API_AssemblySegmentCutData& cutEnd, const API_AssemblySegmentSchemeData& scheme, + const API_AssemblySegmentProfileData* profile) : + base{segment.head.guid, tableID}, assembly::Segment{cutOrigin, cutEnd, scheme, profile} { + m_data = std::make_unique(segment); +} //BeamSegment::BeamSegment +#endif + + +/*-------------------------------------------------------------------- + Copy constructor + + source: The object to copy + --------------------------------------------------------------------*/ +BeamSegment::BeamSegment(const BeamSegment& source) : base{ source } { + m_data = source.m_data ? std::make_unique(*source.m_data) : nullptr; +} //BeamSegment::BeamSegment + + +/*-------------------------------------------------------------------- + Move constructor + + source: The object to move + --------------------------------------------------------------------*/ +BeamSegment::BeamSegment(BeamSegment&& source) noexcept : base{source} { + m_data = std::move(source.m_data); +} //BeamSegment::BeamSegment + + +/*-------------------------------------------------------------------- + Destructor + --------------------------------------------------------------------*/ +BeamSegment::~BeamSegment() {} + + +#ifdef ARCHICAD +/*-------------------------------------------------------------------- + Get the (immutable) API element header data + + return: The element header data (only use this data for low-level operations - for normal code, call getters/setters) + --------------------------------------------------------------------*/ +const API_Elem_Head& BeamSegment::getHead() const { + return m_data->root->head; +} //BeamSegment::getHead + + +/*-------------------------------------------------------------------- + Get the (mutable) API element header data + + return: The element header data (only use this data for low-level operations - for normal code, call getters/setters) + --------------------------------------------------------------------*/ +API_Elem_Head& BeamSegment::getHead() { + return m_data->root->head; +} //BeamSegment::getHead +#endif + + +/*-------------------------------------------------------------------- + Fill an inventory with the package items + + inventory: The inventory to receive the package items + + return: True if the package has added items to the inventory + --------------------------------------------------------------------*/ +bool BeamSegment::fillInventory(Inventory& inventory) const { + using enum Entry::Type; + //TODO: Implement other fields as required + return base::fillInventory(inventory); +} //BeamSegment::fillInventory + + +/*-------------------------------------------------------------------- + Get the specified cargo + + item: The inventory item to retrieve + + return: The requested cargo (nullptr on failure) + --------------------------------------------------------------------*/ +Cargo::Unique BeamSegment::getCargo(const Inventory::Item& item) const { + //TODO: Implement other fields as required + return base::getCargo(item); +} //BeamSegment::getCargo + + +/*-------------------------------------------------------------------- + Set to the default package content + --------------------------------------------------------------------*/ +void BeamSegment::setDefault() { + m_data.reset(); +} //BeamSegment::setDefault diff --git a/SpeckleLib/Speckle/Record/Element/BeamSegment.h b/SpeckleLib/Speckle/Record/Element/BeamSegment.h new file mode 100644 index 0000000..58020dc --- /dev/null +++ b/SpeckleLib/Speckle/Record/Element/BeamSegment.h @@ -0,0 +1,130 @@ +#ifndef SPECKLE_RECORD_ELEMENT_BEAM_SEGMENT +#define SPECKLE_RECORD_ELEMENT_BEAM_SEGMENT + +#include "Speckle/Record/Element/Element.h" +#include "Speckle/Record/Element/Interface/Assembly/Segment.h" + +namespace speckle::record::element { + + class SegmentedBeam; + + /*! + BIM beam class + */ + class BeamSegment : public Element, public assembly::Segment { + public: + + // MARK: - Types + + using base = Element; + ///Unique pointer + using Unique = std::unique_ptr; + ///Shared pointer + using Shared = std::shared_ptr; + ///Optional + using Option = std::optional; + + // MARK: - Constructors + + using base::base; + + /*! + Default constructor + */ + BeamSegment(); +#ifdef ARCHICAD + /*! + Constructor + @param elemData Archicad element data + @param tableID The element table ID (AC database, e.g. floor plan, 3D) + */ + BeamSegment(const API_Element& elemData, const speckle::utility::Guid& tableID); +#endif + /*! + Copy constructor + @param source The object to copy + */ + BeamSegment(const BeamSegment& source); + /*! + Move constructor + @param source The object to move + */ + BeamSegment(BeamSegment&& source) noexcept; + /*! + Destructor + */ + ~BeamSegment(); + + /*! + Object cloning + @return A clone of this object + */ + BeamSegment* clonePtr() const override { return new BeamSegment{*this}; } + + + // MARK: - Functions (const) + +#ifdef ARCHICAD + /*! + Get the (immutable) API element header data + @return The element header data (only use this data for low-level operations - for normal code, call getters/setters) + */ + const API_Elem_Head& getHead() const override; +#endif + + // MARK: - Functions (mutating) + +#ifdef ARCHICAD + /*! + Get the (mutable) API element header data + @return The element header data (only use this data for low-level operations - for normal code, call getters/setters) + */ + API_Elem_Head& getHead() override; +#endif + + // MARK: - Serialisation + + /*! + Fill an inventory with the package items + @param inventory The inventory to receive the package items + @return True if the package has added items to the inventory + */ + bool fillInventory(active::serialise::Inventory& inventory) const override; + /*! + Get the specified cargo + @param item The inventory item to retrieve + @return The requested cargo (nullptr on failure) + */ + Cargo::Unique getCargo(const active::serialise::Inventory::Item& item) const override; + /*! + Set to the default package content + */ + void setDefault() override; + + protected: + friend class SegmentedBeam; + +#ifdef ARCHICAD + /*! + Constructor + @param segment The segment element data + @param tableID The parent table ID + @param cutOrigin Cut at the segment origin + @param cutEnd Cut at the segment end + @param scheme The segment scheme + @param profile The segment profile (nullptr = none) + */ + BeamSegment(const API_BeamSegmentType& segment, const speckle::utility::Guid& tableID, const API_AssemblySegmentCutData& cutOrigin, + const API_AssemblySegmentCutData& cutEnd, const API_AssemblySegmentSchemeData& scheme, + const API_AssemblySegmentProfileData* profile = nullptr); +#endif + + private: + class Data; + ///The beam data + std::unique_ptr m_data; + }; + +} + +#endif //SPECKLE_RECORD_ELEMENT_BEAM_SEGMENT diff --git a/SpeckleLib/Speckle/Record/Element/Interface/SegmentedBeam.cpp b/SpeckleLib/Speckle/Record/Element/Interface/SegmentedBeam.cpp new file mode 100644 index 0000000..8c14b60 --- /dev/null +++ b/SpeckleLib/Speckle/Record/Element/Interface/SegmentedBeam.cpp @@ -0,0 +1,160 @@ +#include "Speckle/Record/Element/Interface/SegmentedBeam.h" + +#include "Speckle/Record/Element/BeamSegment.h" +#include "Speckle/Record/Element/Memo.h" +#include "Speckle/Record/Element/Interface/Assembly/Path.h" +#include "Speckle/Utility/BIMMemory.h" + +using namespace active::serialise; +using namespace speckle::record::attribute; +using namespace speckle::record::element; +using namespace speckle::utility; + +namespace speckle::record::element { + + class SegmentedBeam::Data { + public: + friend class SegmentedBeam; + + std::vector segments; + }; + +} + +/*-------------------------------------------------------------------- + Default constructor + --------------------------------------------------------------------*/ +SegmentedBeam::SegmentedBeam() { +} //SegmentedBeam::SegmentedBeam + + +/*-------------------------------------------------------------------- + Copy constructor + + source: The object to copy + --------------------------------------------------------------------*/ +SegmentedBeam::SegmentedBeam(const SegmentedBeam& source) : base{ source } { + m_data = source.m_data ? std::make_unique(*m_data) : nullptr; +} //SegmentedBeam::SegmentedBeam + + +/*-------------------------------------------------------------------- + Destructor + --------------------------------------------------------------------*/ +SegmentedBeam::~SegmentedBeam() {} + + +/*-------------------------------------------------------------------- + Get the number of segments + + return. The number of segments (0 on error) + --------------------------------------------------------------------*/ +size_t SegmentedBeam::getSegmentCount() const { + confirmPart(getPartFilter()); + return m_data ? m_data->segments.size() : 0; +} //SegmentedBeam::getSegmentCount + + +/*-------------------------------------------------------------------- + Get a beam segment + + index: The index of the required segment + + return: The requested segment, nullptr on error + --------------------------------------------------------------------*/ +BeamSegment* SegmentedBeam::getSegment(size_t index) const { + confirmPart(getPartFilter()); + return (m_data && (index < m_data->segments.size())) ? &m_data->segments[index] : nullptr; +} //SegmentedBeam::getSegment + + +/*-------------------------------------------------------------------- + Return the bits for the part filter required to load the data necessary to build this object + + return: The required filter bits + --------------------------------------------------------------------*/ +Part::filter_bits SegmentedBeam::getPartFilter() const { +#ifdef ARCHICAD + return APIMemoMask_BeamSegment | APIMemoMask_AssemblySegmentCut | APIMemoMask_AssemblySegmentScheme | APIMemoMask_AssemblySegmentProfile; +#endif +} //SegmentedBeam::getPartFilter + + +/*-------------------------------------------------------------------- + Determine if the element memo content has been validated (elements must override according to requirements) + + return: True if the element memo content is valid + --------------------------------------------------------------------*/ +bool SegmentedBeam::isPartValid() const { + return m_data && !m_data->segments.empty(); +} //SegmentedBeam::isPartValid + + +/*-------------------------------------------------------------------- + Load the element memo structure (elements must override according to requirements) + + filter: Filter bits specifying memo requirements + --------------------------------------------------------------------*/ +void SegmentedBeam::loadMemo(filter_bits filter, std::unique_ptr& memo) const { + +} //SegmentedBeam::loadMemo + + +/*-------------------------------------------------------------------- + Send the element part back to a memo structure for storage (elements must override according to requirements) + + memo: The memo to carry the data + + return: True if the data was successfully sent + --------------------------------------------------------------------*/ +bool SegmentedBeam::send(Memo* memo) const { + //TODO: Complete when required + return false; +} //SegmentedBeam::send + + +/*-------------------------------------------------------------------- + Receive the element memo data from a memo structure (elements must override according to requirements) + + memo: The memo carrying the data + + return: True if the data was successfully received + --------------------------------------------------------------------*/ +bool SegmentedBeam::receive(const Memo& memo) const { +#ifdef ARCHICAD + if (!memo || (memo.root()->beamSegments == nullptr)) + return false; + if (m_data) + m_data->segments.clear(); + else + m_data = std::make_unique(); + //Confirm that required data is present in the memo + auto segmentPtr = memo.root()->beamSegments; + auto cutPtr = memo.root()->assemblySegmentCuts; + auto schemePtr = memo.root()->assemblySegmentSchemes; + auto profilePtr = memo.root()->assemblySegmentProfiles; + if ((segmentPtr == nullptr) || (cutPtr == nullptr) || (schemePtr == nullptr)) + return false; + //Determine available item count + auto segmentCount = BIMMemory::getPtrSize(segmentPtr) / sizeof(API_BeamSegmentType); + auto cutCount = BIMMemory::getPtrSize(cutPtr) / sizeof(API_AssemblySegmentCutData); + auto schemeCount = BIMMemory::getPtrSize(schemePtr) / sizeof(API_AssemblySegmentSchemeData); + auto profileCount = BIMMemory::getPtrSize(profilePtr) / sizeof(API_AssemblySegmentProfileData); + if ((segmentCount == 0) || (cutCount != (segmentCount + 1)) || (schemeCount != segmentCount)) + return false; + auto path = dynamic_cast(this); + for (size_t n = 0 ; n < segmentCount; ++n) { + const API_AssemblySegmentProfileData* thisProfile = nullptr; + for (size_t i = 0; i < profileCount; ++i) { + if (profilePtr[i].segmentIndex == n) { + thisProfile = profilePtr + i; + break; + } + } + m_data->segments.emplace_back(BeamSegment{segmentPtr[n], getTableID(), cutPtr[n], cutPtr[n + 1], schemePtr[n], thisProfile}); + m_data->segments.back().setPath(path); + } + setMemoLoaded(true); +#endif + return true; +} //SegmentedBeam::receive diff --git a/SpeckleLib/Speckle/Record/Element/Interface/SegmentedBeam.h b/SpeckleLib/Speckle/Record/Element/Interface/SegmentedBeam.h new file mode 100644 index 0000000..1c7a5fd --- /dev/null +++ b/SpeckleLib/Speckle/Record/Element/Interface/SegmentedBeam.h @@ -0,0 +1,103 @@ +#ifndef SPECKLE_RECORD_ELEMENT_SEGMENTED_BEAM +#define SPECKLE_RECORD_ELEMENT_SEGMENTED_BEAM + +#include "Speckle/Database/Identity/BIMRecordID.h" +#include "Speckle/Record/Element/Interface/Part.h" + +namespace speckle::record::element { + + class BeamSegment; + + /*! + Interface for a beam type that is made up of consecutive segments + + Note that the child segments use lazy loading to avoid high overheads when accessing data relevant to the parent beam only + */ + class SegmentedBeam : public Part { + public: + + // MARK: - Types + + using base = Part; + ///Unique pointer + using Unique = std::unique_ptr; + ///Shared pointer + using Shared = std::shared_ptr; + ///Optional + using Option = std::optional; + + // MARK: - Constructors + + /*! + Default constructor + */ + SegmentedBeam(); + /*! + Copy constructor + @param source The object to copy + */ + SegmentedBeam(const SegmentedBeam& source); + /*! + Destructor + */ + ~SegmentedBeam(); + + // MARK: - Functions (const) + + /*! + Get the BIM application parent table ID + @return The BIM table ID + */ + virtual database::BIMRecordID getTableID() const = 0; + /*! + Get the number of segments + @return The number of segments (0 on error) + */ + size_t getSegmentCount() const; + /*! + Get a beam segment + @param index The index of the required segment + @return The requested segment, nullptr on error + */ + BeamSegment* getSegment(size_t index) const; + + // MARK: - Functions (mutating) + + protected: + /*! + Return the bits for the part filter required to load the data necessary to build this object + @return The required filter bits */ + filter_bits getPartFilter() const; + /*! + Determine if the element part content has been validated (elements must override according to requirements) + @return True if the element part content is valid + */ + bool isPartValid() const override; + /*! + Load the element memo structure (elements must override according to requirements) + @param filter Filter bits specifying memo requirements + */ + void loadMemo(filter_bits filter, std::unique_ptr& memo) const override; + + /*! + Send the element part back to a memo structure for storage (elements must override according to requirements) + @param memo The memo to carry the data + @return True if the data was successfully sent + */ + bool send(Memo* memo) const override; + /*! + Receive the element memo data from a memo structure (elements must override according to requirements) + @param memo The memo carrying the data + @return True if the data was successfully received + */ + bool receive(const Memo& memo) const override; + + private: + class Data; + ///The segment data - mutable to support lazy loading + mutable std::unique_ptr m_data; + }; + +} + +#endif //SPECKLE_RECORD_ELEMENT_SEGMENTED_BEAM diff --git a/SpeckleLib/SpeckleLib17.vcxproj b/SpeckleLib/SpeckleLib17.vcxproj index 22f73ca..f452892 100644 --- a/SpeckleLib/SpeckleLib17.vcxproj +++ b/SpeckleLib/SpeckleLib17.vcxproj @@ -71,6 +71,8 @@ + + @@ -78,6 +80,7 @@ + @@ -133,6 +136,8 @@ + + @@ -140,6 +145,7 @@ + diff --git a/SpeckleLib/SpeckleLib17.vcxproj.filters b/SpeckleLib/SpeckleLib17.vcxproj.filters index 6550add..8388696 100644 --- a/SpeckleLib/SpeckleLib17.vcxproj.filters +++ b/SpeckleLib/SpeckleLib17.vcxproj.filters @@ -343,6 +343,15 @@ Speckle\Record\Element + + Speckle\Record\Element\Interface + + + Speckle\Record\Element + + + Speckle\Record\Element + @@ -498,6 +507,15 @@ Speckle\Record\Element + + Speckle\Record\Element\Interface + + + Speckle\Record\Element + + + Speckle\Record\Element + From 3368e7a6dca6ed99817459c3e31d21671e0e6b6f Mon Sep 17 00:00:00 2001 From: David Kekesi Date: Tue, 22 Oct 2024 10:34:04 +0200 Subject: [PATCH 2/3] one mesh per material, Mesh::appendFace --- SpeckleLib/Speckle/Primitive/Mesh/Mesh.cpp | 14 +++ SpeckleLib/Speckle/Primitive/Mesh/Mesh.h | 6 + SpeckleLib/Speckle/Record/Element/Element.cpp | 108 +++++++++++++++++- 3 files changed, 123 insertions(+), 5 deletions(-) diff --git a/SpeckleLib/Speckle/Primitive/Mesh/Mesh.cpp b/SpeckleLib/Speckle/Primitive/Mesh/Mesh.cpp index 23b0f83..44cf936 100644 --- a/SpeckleLib/Speckle/Primitive/Mesh/Mesh.cpp +++ b/SpeckleLib/Speckle/Primitive/Mesh/Mesh.cpp @@ -30,6 +30,20 @@ namespace { } +/*-------------------------------------------------------------------- + Append a single face to the Mesh given by the vertices + --------------------------------------------------------------------*/ +void Mesh::appendFace(const std::vector& vertices) { + m_vertices.insert(m_vertices.end(), vertices.begin(), vertices.end()); + int lastVertexIndex = m_faces.back(); + int faceSize = vertices.size() / 3; + m_faces.push_back(faceSize); + for (int i = 0; i < faceSize; i++) + { + m_faces.push_back(++lastVertexIndex); + } +} + /*-------------------------------------------------------------------- Fill an inventory with the package items diff --git a/SpeckleLib/Speckle/Primitive/Mesh/Mesh.h b/SpeckleLib/Speckle/Primitive/Mesh/Mesh.h index 7c4853a..0604730 100644 --- a/SpeckleLib/Speckle/Primitive/Mesh/Mesh.h +++ b/SpeckleLib/Speckle/Primitive/Mesh/Mesh.h @@ -45,6 +45,12 @@ namespace speckle::primitive { @return The speckle type (relevant objects should override as required) */ speckle::utility::String getSpeckleType() const override { return "Objects.Geometry.Mesh"; } + + /*! + Append a single face to the Mesh given by the vertices + @param vertices The vertices to append + */ + void appendFace(const std::vector& vertices); // MARK: - Serialisation diff --git a/SpeckleLib/Speckle/Record/Element/Element.cpp b/SpeckleLib/Speckle/Record/Element/Element.cpp index 5aa28e4..91a6fb8 100644 --- a/SpeckleLib/Speckle/Record/Element/Element.cpp +++ b/SpeckleLib/Speckle/Record/Element/Element.cpp @@ -135,7 +135,7 @@ String Element::getTypeName() const { return: A pointer to the element body --------------------------------------------------------------------*/ -Element::Body* Element::getBody() const { +/*Element::Body* Element::getBody() const { #ifdef ARCHICAD if (m_data->m_cache) { return m_data->m_cache.get(); @@ -183,6 +183,8 @@ Element::Body* Element::getBody() const { ModelerAPI::Material material{}; polygon.GetMaterial(&material); + auto materialName = material.GetName(); + Int32 convexPolyCount = polygon.GetConvexPolygonCount(); for (Int32 convPolyIndex = 1; convPolyIndex <= convexPolyCount; ++convPolyIndex) @@ -206,10 +208,6 @@ Element::Body* Element::getBody() const { vertices.push_back(vertex.y); vertices.push_back(vertex.z); - //double alpha = material.GetTransparency(); - //ModelerAPI::Color color = material.GetSurfaceColor(); - //colors.push_back(ARGBToInt(alpha, color.red, color.green, color.blue)); - faces.push_back(vertexIndex - 1); } elementBody->push_back(primitive::Mesh(std::move(vertices), std::move(faces), std::move(colors), material)); @@ -220,9 +218,109 @@ Element::Body* Element::getBody() const { m_data->m_cache.reset(elementBody); return m_data->m_cache.get(); #endif +}*/ + +Element::Body* Element::getBody() const { +#ifdef ARCHICAD + if (m_data->m_cache) { + return m_data->m_cache.get(); + } + + void* dummy = nullptr; + GSErrCode err = ACAPI_Sight_GetCurrentWindowSight(&dummy); + if (err != NoError) + { + // TODO: should this throw? + } + + Modeler::SightPtr currentSightPtr((Modeler::Sight*)dummy); // init the shared ptr with the raw pointer + ModelerAPI::Model acModel; + Modeler::IAttributeReader* attrReader = ACAPI_Attribute_GetCurrentAttributeSetReader(); + + err = EXPGetModel(currentSightPtr, &acModel, attrReader); + if (err != NoError) + { + // TODO: should this throw? + } + + auto elementBody = new Element::Body(); + + // Map to collect meshes per material name + std::map materialMeshMap; + + Int32 nElements = acModel.GetElementCount(); + for (Int32 iElement = 1; iElement <= nElements; iElement++) + { + ModelerAPI::Element elem{}; + acModel.GetElement(iElement, &elem); + if (elem.GetElemGuid() != getHead().guid) + continue; + + Int32 nBodies = elem.GetTessellatedBodyCount(); + for (Int32 bodyIndex = 1; bodyIndex <= nBodies; ++bodyIndex) + { + ModelerAPI::MeshBody body{}; + elem.GetTessellatedBody(bodyIndex, &body); + + Int32 polyCount = body.GetPolygonCount(); + for (Int32 polyIndex = 1; polyIndex <= polyCount; ++polyIndex) + { + ModelerAPI::Polygon polygon{}; + body.GetPolygon(polyIndex, &polygon); + + ModelerAPI::Material material{}; + polygon.GetMaterial(&material); + auto materialName = material.GetName(); + + Int32 convexPolyCount = polygon.GetConvexPolygonCount(); + + for (Int32 convPolyIndex = 1; convPolyIndex <= convexPolyCount; ++convPolyIndex) + { + std::vector vertices; + std::vector faces; + std::vector colors; + + ModelerAPI::ConvexPolygon convexPolygon{}; + polygon.GetConvexPolygon(convPolyIndex, &convexPolygon); + Int32 vertexCount = convexPolygon.GetVertexCount(); + + faces.push_back(vertexCount); + for (Int32 vertexIndex = 1; vertexIndex <= vertexCount; ++vertexIndex) + { + ModelerAPI::Vertex vertex{}; + body.GetVertex(convexPolygon.GetVertexIndex(vertexIndex), &vertex); + + // Collect vertices (as doubles for now, but should be changed to Vertex type) + vertices.push_back(vertex.x); + vertices.push_back(vertex.y); + vertices.push_back(vertex.z); + + faces.push_back(vertexIndex - 1); + } + + if (materialMeshMap.find(materialName) != materialMeshMap.end()) { + materialMeshMap[materialName].appendFace(std::move(vertices)); + } + else { + materialMeshMap[materialName] = primitive::Mesh(std::move(vertices), std::move(faces), std::move(colors), material); + } + } + } + } + } + + for (auto& [materialName, mesh] : materialMeshMap) + { + elementBody->push_back(std::move(mesh)); + } + + m_data->m_cache.reset(elementBody); + return m_data->m_cache.get(); +#endif } + /*-------------------------------------------------------------------- Fill an inventory with the package items From 73b04a75889aab0c80c8a87cf8b2134f12f9729f Mon Sep 17 00:00:00 2001 From: David Kekesi Date: Tue, 22 Oct 2024 12:38:22 +0200 Subject: [PATCH 3/3] code cleanup --- SpeckleLib/Speckle/Primitive/Mesh/Mesh.cpp | 7 +- SpeckleLib/Speckle/Primitive/Mesh/Mesh.h | 11 +- SpeckleLib/Speckle/Record/Element/Element.cpp | 101 +----------------- 3 files changed, 18 insertions(+), 101 deletions(-) diff --git a/SpeckleLib/Speckle/Primitive/Mesh/Mesh.cpp b/SpeckleLib/Speckle/Primitive/Mesh/Mesh.cpp index 44cf936..fa060e0 100644 --- a/SpeckleLib/Speckle/Primitive/Mesh/Mesh.cpp +++ b/SpeckleLib/Speckle/Primitive/Mesh/Mesh.cpp @@ -34,14 +34,15 @@ namespace { Append a single face to the Mesh given by the vertices --------------------------------------------------------------------*/ void Mesh::appendFace(const std::vector& vertices) { + if (vertices.empty()) + return; + m_vertices.insert(m_vertices.end(), vertices.begin(), vertices.end()); - int lastVertexIndex = m_faces.back(); + int lastVertexIndex = m_faces.empty() ? -1 : m_faces.back(); int faceSize = vertices.size() / 3; m_faces.push_back(faceSize); for (int i = 0; i < faceSize; i++) - { m_faces.push_back(++lastVertexIndex); - } } /*-------------------------------------------------------------------- diff --git a/SpeckleLib/Speckle/Primitive/Mesh/Mesh.h b/SpeckleLib/Speckle/Primitive/Mesh/Mesh.h index 0604730..c9a8e9a 100644 --- a/SpeckleLib/Speckle/Primitive/Mesh/Mesh.h +++ b/SpeckleLib/Speckle/Primitive/Mesh/Mesh.h @@ -29,10 +29,19 @@ namespace speckle::primitive { Mesh(active::measure::LengthType unit = active::measure::LengthType::metre) : base{utility::Guid{true}, utility::Guid{}, unit} {} /*! Constructor + @param unit The mesh unit type + @param material The mesh material + */ + Mesh(const ModelerAPI::Material& material, + active::measure::LengthType unit = active::measure::LengthType::metre) : + base{ utility::Guid{true}, utility::Guid{}, unit }, m_material{ material } {} + /*! + Constructor + @param unit The mesh unit type @param vertices The mesh vertices @param faces The mesh faces (the number of indices in the face followed by the vertex indices) @param colors The mesh face colours - @param unit The mesh unit type + @param material The mesh material */ Mesh(std::vector&& vertices, std::vector&& faces, std::vector&& colors, const ModelerAPI::Material& material, active::measure::LengthType unit = active::measure::LengthType::metre) : diff --git a/SpeckleLib/Speckle/Record/Element/Element.cpp b/SpeckleLib/Speckle/Record/Element/Element.cpp index 91a6fb8..805cc8d 100644 --- a/SpeckleLib/Speckle/Record/Element/Element.cpp +++ b/SpeckleLib/Speckle/Record/Element/Element.cpp @@ -135,91 +135,6 @@ String Element::getTypeName() const { return: A pointer to the element body --------------------------------------------------------------------*/ -/*Element::Body* Element::getBody() const { -#ifdef ARCHICAD - if (m_data->m_cache) { - return m_data->m_cache.get(); - } - - - void* dummy = nullptr; - GSErrCode err = ACAPI_Sight_GetCurrentWindowSight(&dummy); - if (err != NoError) - { - // TODO: should this throw? - } - - Modeler::SightPtr currentSightPtr((Modeler::Sight*)dummy); // init the shared ptr with the raw pointer - ModelerAPI::Model acModel; - Modeler::IAttributeReader* attrReader = ACAPI_Attribute_GetCurrentAttributeSetReader(); - - err = EXPGetModel(currentSightPtr, &acModel, attrReader); - if (err != NoError) - { - // TODO: should this throw? - } - - auto elementBody = new Element::Body(); - - Int32 nElements = acModel.GetElementCount(); - for (Int32 iElement = 1; iElement <= nElements; iElement++) - { - ModelerAPI::Element elem{}; - acModel.GetElement(iElement, &elem); - if (elem.GetElemGuid() != getHead().guid) - continue; - - Int32 nBodies = elem.GetTessellatedBodyCount(); - for (Int32 bodyIndex = 1; bodyIndex <= nBodies; ++bodyIndex) - { - ModelerAPI::MeshBody body{}; - elem.GetTessellatedBody(bodyIndex, &body); - - Int32 polyCount = body.GetPolygonCount(); - for (Int32 polyIndex = 1; polyIndex <= polyCount; ++polyIndex) - { - ModelerAPI::Polygon polygon{}; - body.GetPolygon(polyIndex, &polygon); - - ModelerAPI::Material material{}; - polygon.GetMaterial(&material); - auto materialName = material.GetName(); - - Int32 convexPolyCount = polygon.GetConvexPolygonCount(); - - for (Int32 convPolyIndex = 1; convPolyIndex <= convexPolyCount; ++convPolyIndex) - { - std::vector vertices; - std::vector faces; - std::vector colors; - - ModelerAPI::ConvexPolygon convexPolygon{}; - polygon.GetConvexPolygon(convPolyIndex, &convexPolygon); - Int32 vertexCount = convexPolygon.GetVertexCount(); - - faces.push_back(vertexCount); - for (Int32 vertexIndex = 1; vertexIndex <= vertexCount; ++vertexIndex) - { - ModelerAPI::Vertex vertex{}; - body.GetVertex(convexPolygon.GetVertexIndex(vertexIndex), &vertex); - - // TODO: change vertices array to hold Vertex instead of double values - vertices.push_back(vertex.x); - vertices.push_back(vertex.y); - vertices.push_back(vertex.z); - - faces.push_back(vertexIndex - 1); - } - elementBody->push_back(primitive::Mesh(std::move(vertices), std::move(faces), std::move(colors), material)); - } - } - } - } - m_data->m_cache.reset(elementBody); - return m_data->m_cache.get(); -#endif -}*/ - Element::Body* Element::getBody() const { #ifdef ARCHICAD if (m_data->m_cache) { @@ -271,20 +186,19 @@ Element::Body* Element::getBody() const { ModelerAPI::Material material{}; polygon.GetMaterial(&material); auto materialName = material.GetName(); + if (materialMeshMap.find(materialName) == materialMeshMap.end()) { + materialMeshMap[materialName] = primitive::Mesh(material); + } Int32 convexPolyCount = polygon.GetConvexPolygonCount(); for (Int32 convPolyIndex = 1; convPolyIndex <= convexPolyCount; ++convPolyIndex) { std::vector vertices; - std::vector faces; - std::vector colors; - ModelerAPI::ConvexPolygon convexPolygon{}; polygon.GetConvexPolygon(convPolyIndex, &convexPolygon); Int32 vertexCount = convexPolygon.GetVertexCount(); - faces.push_back(vertexCount); for (Int32 vertexIndex = 1; vertexIndex <= vertexCount; ++vertexIndex) { ModelerAPI::Vertex vertex{}; @@ -294,16 +208,9 @@ Element::Body* Element::getBody() const { vertices.push_back(vertex.x); vertices.push_back(vertex.y); vertices.push_back(vertex.z); - - faces.push_back(vertexIndex - 1); } - if (materialMeshMap.find(materialName) != materialMeshMap.end()) { - materialMeshMap[materialName].appendFace(std::move(vertices)); - } - else { - materialMeshMap[materialName] = primitive::Mesh(std::move(vertices), std::move(faces), std::move(colors), material); - } + materialMeshMap[materialName].appendFace(std::move(vertices)); } } }