diff --git a/SpeckleLib/Speckle/Database/Storage/ArchicadDBase/Element/ArchicadElementDBaseEngine.cpp b/SpeckleLib/Speckle/Database/Storage/ArchicadDBase/Element/ArchicadElementDBaseEngine.cpp index 0f67bef..874fc0e 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/ModelElement.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/Primitive/Mesh/Mesh.cpp b/SpeckleLib/Speckle/Primitive/Mesh/Mesh.cpp index 23b0f83..fa060e0 100644 --- a/SpeckleLib/Speckle/Primitive/Mesh/Mesh.cpp +++ b/SpeckleLib/Speckle/Primitive/Mesh/Mesh.cpp @@ -30,6 +30,21 @@ 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.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); +} + /*-------------------------------------------------------------------- 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..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) : @@ -45,6 +54,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/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/Element.cpp b/SpeckleLib/Speckle/Record/Element/Element.cpp index 02ca22a..7b8a8ad 100644 --- a/SpeckleLib/Speckle/Record/Element/Element.cpp +++ b/SpeckleLib/Speckle/Record/Element/Element.cpp @@ -109,7 +109,7 @@ bool Element::fillInventory(Inventory& inventory) const { return: The requested cargo (nullptr on failure) --------------------------------------------------------------------*/ Cargo::Unique Element::getCargo(const Inventory::Item& item) const { - return base::getCargo(item); + return base::getCargo(item); } //Element::getCargo 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/Speckle/Record/Element/ModelElement.cpp b/SpeckleLib/Speckle/Record/Element/ModelElement.cpp index ea7fcd5..0d6407f 100644 --- a/SpeckleLib/Speckle/Record/Element/ModelElement.cpp +++ b/SpeckleLib/Speckle/Record/Element/ModelElement.cpp @@ -129,7 +129,6 @@ ModelElement::Body* ModelElement::getBody() const { return m_data->m_cache.get(); } - void* dummy = nullptr; GSErrCode err = ACAPI_Sight_GetCurrentWindowSight(&dummy); if (err != NoError) @@ -149,6 +148,9 @@ ModelElement::Body* ModelElement::getBody() const { auto elementBody = new ModelElement::Body(); + // Map to collect meshes per material name + std::map materialMeshMap; + Int32 nElements = acModel.GetElementCount(); for (Int32 iElement = 1; iElement <= nElements; iElement++) { @@ -166,49 +168,51 @@ ModelElement::Body* ModelElement::getBody() const { Int32 polyCount = body.GetPolygonCount(); for (Int32 polyIndex = 1; polyIndex <= polyCount; ++polyIndex) { - ModelerAPI::Polygon polygon{}; + ModelerAPI::Polygon polygon{}; body.GetPolygon(polyIndex, &polygon); 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{}; body.GetVertex(convexPolygon.GetVertexIndex(vertexIndex), &vertex); - // TODO: change vertices array to hold Vertex instead of double values + // 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); - - //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)); + + materialMeshMap[materialName].appendFace(std::move(vertices)); } } } } + + for (auto& [materialName, mesh] : materialMeshMap) + { + elementBody->push_back(std::move(mesh)); + } + m_data->m_cache.reset(elementBody); return m_data->m_cache.get(); #endif -} +} //ModelElement::getBody #ifdef ARCHICAD diff --git a/SpeckleLib/SpeckleLib17.vcxproj b/SpeckleLib/SpeckleLib17.vcxproj index d49baa3..820b0da 100644 --- a/SpeckleLib/SpeckleLib17.vcxproj +++ b/SpeckleLib/SpeckleLib17.vcxproj @@ -76,6 +76,8 @@ + + @@ -85,6 +87,7 @@ + @@ -155,6 +158,8 @@ + + @@ -164,6 +169,7 @@ + diff --git a/SpeckleLib/SpeckleLib17.vcxproj.filters b/SpeckleLib/SpeckleLib17.vcxproj.filters index d285087..41f7b01 100644 --- a/SpeckleLib/SpeckleLib17.vcxproj.filters +++ b/SpeckleLib/SpeckleLib17.vcxproj.filters @@ -1,4 +1,4 @@ - + @@ -406,6 +406,15 @@ Speckle\Record\Property + + Speckle\Record\Element\Interface + + + Speckle\Record\Element + + + Speckle\Record\Element + @@ -612,6 +621,15 @@ Speckle\Record\Property + + Speckle\Record\Element\Interface + + + Speckle\Record\Element + + + Speckle\Record\Element +