From 4e49b640a72ee0045ad325cfc02c9336ecefc18c Mon Sep 17 00:00:00 2001 From: Thomas Krijnen Date: Sat, 8 Aug 2026 15:18:51 +0200 Subject: [PATCH] Own iterator geometry results Return independent geometry copies with unique ownership, preserve parent lifetimes, and teach the Python wrapper to own derived results. Keep serializer inputs non-owning and replace Collada's deferred object with copied triangulation elements.\n\nGenerated with the assistance of an AI coding tool. --- src/ifcconvert/IfcConvert.cpp | 8 +- .../validate_storey_containment.cpp | 2 +- src/ifcconvert/validation_utils.h | 2 +- src/ifcgeom/element.h | 38 +++++++--- src/ifcgeom/iterator.cpp | 31 ++++---- src/ifcgeom/iterator.h | 11 +-- src/ifcgeom/kernels/opencascade/tree.h | 3 +- .../kernels/opencascade/tree_backends.h | 6 +- src/ifcgeom/tree.cpp | 3 +- src/ifcgeomserver/IfcGeomServer.cpp | 8 +- src/ifcviewer/GeometryStreamer.cpp | 3 +- src/ifcwrap/IfcGeomWrapper.i | 22 ++++++ src/serializers/collada_serializer.cpp | 75 +++++++++++-------- src/serializers/collada_serializer.h | 56 +------------- 14 files changed, 134 insertions(+), 134 deletions(-) diff --git a/src/ifcconvert/IfcConvert.cpp b/src/ifcconvert/IfcConvert.cpp index 808c20dd80..f2459850fb 100644 --- a/src/ifcconvert/IfcConvert.cpp +++ b/src/ifcconvert/IfcConvert.cpp @@ -933,15 +933,15 @@ int main(int argc, char** argv) { while (true) { - ifcopenshell::geom::element* geom_object = context_iterator->get(); + auto geom_object = context_iterator->get(); if (is_tesselated) { - serializer->write(static_cast(geom_object)); + serializer->write(static_cast(geom_object.get())); } else { - serializer->write(static_cast(geom_object)); + serializer->write(static_cast(geom_object.get())); } if (!no_progress) { @@ -1412,7 +1412,7 @@ void fix_quantities(ifcopenshell::file& f, bool no_progress, bool quiet, bool st if (num_created) { has_more = context_iterator.next(); } - ifcopenshell::geom::brep_element* geom_object = nullptr; + std::unique_ptr geom_object; if (has_more) { geom_object = context_iterator.get_native(); } diff --git a/src/ifcconvert/validate_storey_containment.cpp b/src/ifcconvert/validate_storey_containment.cpp index 5d4606458e..b8ea68534c 100644 --- a/src/ifcconvert/validate_storey_containment.cpp +++ b/src/ifcconvert/validate_storey_containment.cpp @@ -111,7 +111,7 @@ void fix_storeycontainment(ifcopenshell::file& f, bool no_progress, bool quiet, if (num_created) { has_more = context_iterator.next(); } - ifcopenshell::geom::brep_element* geom_object = nullptr; + std::unique_ptr geom_object; if (has_more) { geom_object = context_iterator.get_native(); } diff --git a/src/ifcconvert/validation_utils.h b/src/ifcconvert/validation_utils.h index fa7e24ba5d..e4cf8201be 100644 --- a/src/ifcconvert/validation_utils.h +++ b/src/ifcconvert/validation_utils.h @@ -477,7 +477,7 @@ struct intersection_validator { if (num_created) { has_more = context_iterator.next(); } - ifcopenshell::geom::brep_element* geom_object = nullptr; + std::unique_ptr geom_object; if (has_more) { geom_object = context_iterator.get_native(); } diff --git a/src/ifcgeom/element.h b/src/ifcgeom/element.h index 2839fd8783..a939a0663b 100644 --- a/src/ifcgeom/element.h +++ b/src/ifcgeom/element.h @@ -22,6 +22,7 @@ #include #include +#include #include "../ifcparse/argument.h" #include "../ifcparse/global_id.h" @@ -73,8 +74,20 @@ namespace ifcopenshell::geom { std::string _context; std::string _unique_id; ifcopenshell::geom::transformation _transformation; - const express::entity product_; + const express::entity product_; std::vector _parents; + std::vector> _parent_storage; + + friend class iterator; + virtual std::unique_ptr clone() const { return std::make_unique(*this); } + void set_parents(std::vector>&& newparents) { + _parents.clear(); + _parent_storage.clear(); + for (auto& parent : newparents) { + _parents.push_back(parent.get()); + _parent_storage.emplace_back(std::move(parent)); + } + } public: friend bool operator == (const element& element1, const element& element2) { @@ -108,9 +121,12 @@ namespace ifcopenshell::geom { const std::string& context() const { return _context; } const std::string& unique_id() const { return _unique_id; } const ifcopenshell::geom::transformation& transformation() const { return _transformation; } - const express::entity& product() const { return product_; } + const express::entity& product() const { return product_; } const std::vector& parents() const { return _parents; } - void SetParents(std::vector& newparents) { _parents = newparents; } + void SetParents(std::vector& newparents) { + _parent_storage.clear(); + _parents = newparents; + } element(const ifcopenshell::geom::settings& settings, int id, int parent_id, const std::string& name, const std::string& type, const std::string& guid, const std::string& context, const ifcopenshell::geom::taxonomy::matrix4::ptr& trsf, const express::entity& product) @@ -158,9 +174,10 @@ namespace ifcopenshell::geom { bool calculate_projected_surface_area(double& along_x, double& along_y, double& along_z) const { return geometry().calculate_projected_surface_area(this->transformation().data(), along_x, along_y, along_z); } + brep_element(const brep_element& other) = default; private: - brep_element(const brep_element& other); brep_element& operator=(const brep_element& other); + std::unique_ptr clone() const override { return std::make_unique(*this); } }; class triangulation_element : public element { @@ -177,26 +194,25 @@ namespace ifcopenshell::geom { : element(source) , _geometry(geometry) {} + triangulation_element(const triangulation_element& other) = default; private: - triangulation_element(const triangulation_element& other); triangulation_element& operator=(const triangulation_element& other); + std::unique_ptr clone() const override { return std::make_unique(*this); } }; class serialized_element : public element { private: - ifcopenshell::geom::Representation::serialization* _geometry; + boost::shared_ptr _geometry; public: const ifcopenshell::geom::Representation::serialization& geometry() const { return *_geometry; } serialized_element(const brep_element& shape_model) : element(shape_model) - , _geometry(new ifcopenshell::geom::Representation::serialization(shape_model.geometry())) + , _geometry(boost::shared_ptr(new ifcopenshell::geom::Representation::serialization(shape_model.geometry()))) {} - virtual ~serialized_element() { - delete _geometry; - } + serialized_element(const serialized_element& other) = default; private: - serialized_element(const serialized_element& other); serialized_element& operator=(const serialized_element& other); + std::unique_ptr clone() const override { return std::make_unique(*this); } }; } diff --git a/src/ifcgeom/iterator.cpp b/src/ifcgeom/iterator.cpp index e4569c97a4..aeeadf1820 100644 --- a/src/ifcgeom/iterator.cpp +++ b/src/ifcgeom/iterator.cpp @@ -301,8 +301,8 @@ void ifcopenshell::geom::iterator::compute_bounds(bool with_geometry) if (with_geometry) { size_t num_created = 0; do { - ifcopenshell::geom::element* geom_object = get(); - const ifcopenshell::geom::triangulation_element* o = static_cast(geom_object); + auto geom_object = get(); + const ifcopenshell::geom::triangulation_element* o = static_cast(geom_object.get()); const ifcopenshell::geom::Representation::triangulation& mesh = o->geometry(); auto mat = o->transformation().data()->ccomponents(); Eigen::Vector4d vec, transformed; @@ -548,7 +548,7 @@ express::base ifcopenshell::geom::iterator::next() { } /// Gets the representation of the current geometrical entity. -ifcopenshell::geom::element* ifcopenshell::geom::iterator::get() +std::unique_ptr ifcopenshell::geom::iterator::get() { validate_iterator_state(); @@ -558,24 +558,24 @@ ifcopenshell::geom::element* ifcopenshell::geom::iterator::get() if (settings_.get().get()) { // We are going to build a vector with the element parents. // First, create the parent vector - std::vector parents; + std::vector> parents; // if the element has a parent if (ret->parent_id() != -1) { - const ifcopenshell::geom::element* parent_object = NULL; + ifcopenshell::geom::element* parent_object = NULL; bool hasParent = true; // get the parent try { - parent_object = get_object(ret->parent_id()); + auto parent = get_object(ret->parent_id()); + parent_object = parent.get(); + parents.insert(parents.begin(), std::move(parent)); } catch (const std::exception& e) { logger_.error("GEO", 56, e); hasParent = false; } // Add the previously found parent to the vector - if (hasParent) parents.insert(parents.begin(), parent_object); - // We need to find all the parents while (parent_object != NULL && hasParent && parent_object->parent_id() != -1) { // Find the next parent @@ -585,7 +585,9 @@ ifcopenshell::geom::element* ifcopenshell::geom::iterator::get() hasParent = false; } else { try { - parent_object = get_object(pid); + auto parent = get_object(pid); + parent_object = parent.get(); + parents.insert(parents.begin(), std::move(parent)); } catch (const std::exception& e) { logger_.error("GEO", 57, e); hasParent = false; @@ -593,20 +595,18 @@ ifcopenshell::geom::element* ifcopenshell::geom::iterator::get() } // Add the previously found parent to the vector - if (hasParent) parents.insert(parents.begin(), parent_object); - hasParent = hasParent && parent_object->parent_id() != -1; } // when done push the parent list in the element object - ret->SetParents(parents); + ret->set_parents(std::move(parents)); } } - return ret; + return ret->clone(); } -const ifcopenshell::geom::element* ifcopenshell::geom::iterator::get_object(int id) { +std::unique_ptr ifcopenshell::geom::iterator::get_object(int id) { ifcopenshell::geom::taxonomy::matrix4::ptr m4; int parent_id = -1; std::string instance_type, product_name, product_guid; @@ -639,8 +639,7 @@ const ifcopenshell::geom::element* ifcopenshell::geom::iterator::get_object(int logger_.error("GEO", 59, "Unknown error returning product"); } - element* ifc_object = new element(settings_, id, parent_id, product_name, instance_type, product_guid, "", m4, ifc_product.as()); - return ifc_object; + return std::make_unique(settings_, id, parent_id, product_name, instance_type, product_guid, "", m4, ifc_product.as()); } express::base ifcopenshell::geom::iterator::create() { diff --git a/src/ifcgeom/iterator.h b/src/ifcgeom/iterator.h index d6306b7b35..953d12a64f 100644 --- a/src/ifcgeom/iterator.h +++ b/src/ifcgeom/iterator.h @@ -44,7 +44,7 @@ * at least a single representation will process successfully * * * * ifcopenshell::geom::iterator::get() * - * returns a pointer to the current ifcopenshell::geom::element * + * returns an owned copy of the current ifcopenshell::geom::element * * * * ifcopenshell::geom::iterator::next() * * returns true iff a following entity is available for a successive call to * @@ -291,15 +291,16 @@ namespace ifcopenshell::geom { express::base next(); /// Gets the representation of the current geometrical entity. - element* get(); + std::unique_ptr get(); /// Gets the native (Open Cascade or CGAL) representation of the current geometrical entity. - brep_element* get_native() + std::unique_ptr get_native() { - return *native_task_result_iterator_; + validate_iterator_state(); + return std::make_unique(**native_task_result_iterator_); } - const element* get_object(int id); + std::unique_ptr get_object(int id); express::base create(); }; diff --git a/src/ifcgeom/kernels/opencascade/tree.h b/src/ifcgeom/kernels/opencascade/tree.h index efc91bc228..087090c4c1 100644 --- a/src/ifcgeom/kernels/opencascade/tree.h +++ b/src/ifcgeom/kernels/opencascade/tree.h @@ -1496,7 +1496,8 @@ namespace ifcopenshell::geom { void add_file(ifcopenshell::geom::iterator& it) { if (it.initialize()) { do { - add_element(dynamic_cast(it.get())); + auto element = it.get(); + add_element(dynamic_cast(element.get())); } while (it.next()); } } diff --git a/src/ifcgeom/kernels/opencascade/tree_backends.h b/src/ifcgeom/kernels/opencascade/tree_backends.h index c75ee3ecc1..42df370c01 100644 --- a/src/ifcgeom/kernels/opencascade/tree_backends.h +++ b/src/ifcgeom/kernels/opencascade/tree_backends.h @@ -56,7 +56,8 @@ namespace ifcopenshell { ifcopenshell::geom::iterator iterator(ifcopenshell::geom::kernels::construct(&file, "opencascade", settings_), settings_, &file, {}, 1); if (iterator.initialize()) { do { - add_element(iterator.get()); + auto element = iterator.get(); + add_element(element.get()); } while (iterator.next()); } } @@ -139,7 +140,8 @@ namespace ifcopenshell { ifcopenshell::geom::iterator iterator(ifcopenshell::geom::kernels::construct(&file, "opencascade", settings_), settings_, &file, {}, 1); if (iterator.initialize()) { do { - add_element(iterator.get()); + auto element = iterator.get(); + add_element(element.get()); } while (iterator.next()); } } diff --git a/src/ifcgeom/tree.cpp b/src/ifcgeom/tree.cpp index 292d0750b5..919d580a2b 100644 --- a/src/ifcgeom/tree.cpp +++ b/src/ifcgeom/tree.cpp @@ -151,7 +151,8 @@ void ifcopenshell::geom::tree::add_file(ifcopenshell::geom::iterator& iterator) } do { - add_element(iterator.get()); + auto element = iterator.get(); + add_element(element.get()); } while (iterator.next()); } diff --git a/src/ifcgeomserver/IfcGeomServer.cpp b/src/ifcgeomserver/IfcGeomServer.cpp index f52ee5feeb..1891bb1799 100644 --- a/src/ifcgeomserver/IfcGeomServer.cpp +++ b/src/ifcgeomserver/IfcGeomServer.cpp @@ -617,12 +617,14 @@ int main () { exit_code = 1; break; } - const ifcopenshell::geom::triangulation_element* geom = static_cast(iterator->get()); + auto element = iterator->get(); + const ifcopenshell::geom::triangulation_element* geom = static_cast(element.get()); + auto native_element = iterator->get_native(); std::unique_ptr eext; if (emit_quantities) { - eext.reset(new QuantityWriter_v1(iterator->get_native())); + eext.reset(new QuantityWriter_v1(native_element.get())); } else { - eext.reset(new QuantityWriter_v0(iterator->get_native())); + eext.reset(new QuantityWriter_v0(native_element.get())); } Entity(geom, eext.get()).write(std::cout); continue; diff --git a/src/ifcviewer/GeometryStreamer.cpp b/src/ifcviewer/GeometryStreamer.cpp index 036b20b712..262b7630ac 100644 --- a/src/ifcviewer/GeometryStreamer.cpp +++ b/src/ifcviewer/GeometryStreamer.cpp @@ -539,7 +539,8 @@ void GeometryStreamer::run(const std::string& path, int num_threads) { do { if (cancel_requested_.load()) break; - const ifcopenshell::geom::element* elem = iterator->get(); + auto element = iterator->get(); + const ifcopenshell::geom::element* elem = element.get(); if (!elem) continue; const auto* tri_elem = dynamic_cast(elem); diff --git a/src/ifcwrap/IfcGeomWrapper.i b/src/ifcwrap/IfcGeomWrapper.i index e6efee9e52..95e5a8785a 100644 --- a/src/ifcwrap/IfcGeomWrapper.i +++ b/src/ifcwrap/IfcGeomWrapper.i @@ -103,6 +103,28 @@ } } +// Iterator results are copies owned by the caller. Release the unique pointer +// into the most specific Python proxy and transfer deletion to Python. +%typemap(out) std::unique_ptr { + ifcopenshell::geom::element* elem = $1.release(); + ifcopenshell::geom::serialized_element* serialized_elem = dynamic_cast(elem); + ifcopenshell::geom::triangulation_element* triangulation_elem = dynamic_cast(elem); + ifcopenshell::geom::brep_element* brep_elem = dynamic_cast(elem); + if (triangulation_elem) { + $result = SWIG_NewPointerObj(SWIG_as_voidptr(triangulation_elem), SWIGTYPE_p_ifcopenshell__geom__triangulation_element, SWIG_POINTER_OWN); + } else if (serialized_elem) { + $result = SWIG_NewPointerObj(SWIG_as_voidptr(serialized_elem), SWIGTYPE_p_ifcopenshell__geom__serialized_element, SWIG_POINTER_OWN); + } else if (brep_elem) { + $result = SWIG_NewPointerObj(SWIG_as_voidptr(brep_elem), SWIGTYPE_p_ifcopenshell__geom__brep_element, SWIG_POINTER_OWN); + } else { + $result = SWIG_NewPointerObj(SWIG_as_voidptr(elem), SWIGTYPE_p_ifcopenshell__geom__element, SWIG_POINTER_OWN); + } +} + +%typemap(out) std::unique_ptr { + $result = SWIG_NewPointerObj(SWIG_as_voidptr($1.release()), SWIGTYPE_p_ifcopenshell__geom__brep_element, SWIG_POINTER_OWN); +} + %newobject ifcopenshell::geom::Representation::brep::item; %newobject ifcopenshell::geom::Representation::brep::as_compound; diff --git a/src/serializers/collada_serializer.cpp b/src/serializers/collada_serializer.cpp index a752cfc3c5..27e6efa359 100644 --- a/src/serializers/collada_serializer.cpp +++ b/src/serializers/collada_serializer.cpp @@ -387,29 +387,10 @@ void collada_serializer::collada_exporter::startDocument(const std::string& unit void collada_serializer::collada_exporter::write(const ifcopenshell::geom::triangulation_element* o) { const ifcopenshell::geom::Representation::triangulation& mesh = o->geometry(); - - std::string name = serializer->object_id(o); - collada_id(name); - - std::string representation_id = "representation-" + o->geometry().id(); - collada_id(representation_id); - - std::vector material_references; BOOST_FOREACH(const ifcopenshell::geom::taxonomy::style::ptr& material, mesh.materials()) { materials.add(material); - - std::string material_name = materials.getMaterialUri(material); - material_references.push_back(material_name); } - - deferred_object deferred(name, representation_id, o->type(), o->transformation(), mesh.verts(), mesh.normals(), - mesh.faces(), mesh.edges(), mesh.material_ids(), mesh.materials(), material_references, mesh.uvs()); - - if (serializer->settings().get().get()) { - deferred.parents() = o->parents(); - } - - deferreds.push_back(deferred); + deferreds.push_back(std::make_unique(*o)); } std::string collada_serializer::differentiateSlabTypes(const express::entity& slab) @@ -467,25 +448,53 @@ void collada_serializer::collada_exporter::endDocument() { //if the setting USE_ELEMENT_HIERARCHY is in use, we sort the deferreds objects by their parents. if (use_hierarchy) { - std::sort(deferreds.begin(), deferreds.end()); + std::sort(deferreds.begin(), deferreds.end(), [](const auto& first, const auto& second) { + const auto& first_parents = first->parents(); + const auto& second_parents = second->parents(); + const size_t size = (std::min)(first_parents.size(), second_parents.size()); + size_t index = 0; + while (index < size && *first_parents[index] == *second_parents[index]) { + ++index; + } + return index >= size + ? first_parents.size() < second_parents.size() + : *first_parents[index] < *second_parents[index]; + }); } - for (std::vector::const_iterator it = deferreds.begin(); it != deferreds.end(); ++it) { - if (geometries_written.find(it->representation_id) != geometries_written.end()) { + for (const auto& object_pointer : deferreds) { + const auto& object = *object_pointer; + std::string representation_id = "representation-" + object.geometry().id(); + collada_id(representation_id); + if (geometries_written.find(representation_id) != geometries_written.end()) { continue; } - geometries_written.insert(it->representation_id); - geometries.write(it->representation_id, it->type, it->vertices, it->normals, it->faces, it->edges, - it->material_ids, it->materials, it->uvs, it->material_references); + geometries_written.insert(representation_id); + const auto& mesh = object.geometry(); + std::vector material_references; + for (const auto& material : mesh.materials()) { + material_references.push_back(materials.getMaterialUri(material)); + } + geometries.write(representation_id, object.type(), mesh.verts(), mesh.normals(), mesh.faces(), mesh.edges(), + mesh.material_ids(), mesh.materials(), mesh.uvs(), material_references); } geometries.close(); - for (std::vector::const_iterator it = deferreds.begin(); it != deferreds.end(); ++it){ - const std::string object_name = it->unique_id; + for (const auto& object_pointer : deferreds) { + const auto& object = *object_pointer; + std::string object_name = serializer->object_id(&object); + collada_id(object_name); + std::string representation_id = "representation-" + object.geometry().id(); + collada_id(representation_id); + std::vector material_references; + for (const auto& material : object.geometry().materials()) { + material_references.push_back(materials.getMaterialUri(material)); + } if (use_hierarchy) { - size_t parentsNumber = it->parents_.size(); + const auto& parents = object.parents(); + size_t parentsNumber = parents.size(); bool finished = false; // If we have no parent in the stack and the object has no parent, nothing to do : skip the loop @@ -496,17 +505,17 @@ void collada_serializer::collada_exporter::endDocument() { // If we need to add a parent if (serializer->parentStackId.size() <= parentsNumber) { - if (serializer->parentStackId.empty()) { scene.addParent(*(it->parents_.at(0))); } + if (serializer->parentStackId.empty()) { scene.addParent(*parents.at(0)); } else { size_t diff = parentsNumber - serializer->parentStackId.size(); // If we have the wrong parent in the list - if (serializer->parentStackId.top() != it->parents_.at(parentsNumber - diff - 1)->id()) { + if (serializer->parentStackId.top() != parents.at(parentsNumber - diff - 1)->id()) { scene.closeParent(); } else { // So far we have the right parents, we just need to add the missing ones - for (size_t i = parentsNumber - diff; i < parentsNumber; i++) { scene.addParent(*(it->parents_.at(i))); } + for (size_t i = parentsNumber - diff; i < parentsNumber; i++) { scene.addParent(*parents.at(i)); } // if diff == 0, we can leave the loop. In fact we have the right number of parents, and the last one is ok if (diff == 0) { finished = true; } @@ -520,7 +529,7 @@ void collada_serializer::collada_exporter::endDocument() { } /// @todo redundant information using ID as both ID and Name, maybe omit Name or allow specifying what would be used as the name - scene.add(object_name, object_name, it->representation_id, it->material_references, it->transformation); + scene.add(object_name, object_name, representation_id, material_references, object.transformation()); } //close the remaining parent tags. diff --git a/src/serializers/collada_serializer.h b/src/serializers/collada_serializer.h index 3381c8d2b9..cc478dc68d 100644 --- a/src/serializers/collada_serializer.h +++ b/src/serializers/collada_serializer.h @@ -70,7 +70,6 @@ private: {} void addFloatSource(const std::string& mesh_id, const std::string& suffix, const std::vector& floats, const char* coords = "XYZ"); - /// @todo pass simply deferred_object? void write( const std::string &mesh_id, const std::string &default_material_name, const std::vector& positions, const std::vector& normals, @@ -138,59 +137,6 @@ private: collada_effects effects; }; - class deferred_object { - - friend bool operator < (const deferred_object& def_obj1, const deferred_object& def_obj2) { - size_t size = (def_obj1.parents_.size() < def_obj2.parents_.size() ? def_obj1.parents_.size() : def_obj2.parents_.size()); - size_t cpt = 0; - - // Skip the shared parents - while (cpt < size && *(def_obj1.parents_.at(cpt)) == *(def_obj2.parents_.at(cpt))) { - cpt++; - } - - // If a parent list container the other one - if (cpt >= size) { - return def_obj1.parents_.size() < def_obj2.parents_.size(); - } else { - return *(def_obj1.parents_.at(cpt)) < *(def_obj2.parents_.at(cpt)); - } - } - - public: - std::string unique_id, representation_id, type; - ifcopenshell::geom::transformation transformation; - std::vector vertices; - std::vector normals; - std::vector faces; - std::vector edges; - std::vector material_ids; - std::vector materials; - std::vector material_references; - std::vector uvs; - std::vector parents_; - - deferred_object(const std::string& unique_id, const std::string& representation_id, const std::string& type, const ifcopenshell::geom::transformation& transformation, - const std::vector& vertices, const std::vector& normals, const std::vector& faces, - const std::vector& edges, const std::vector& material_ids, const std::vector& materials, - const std::vector& material_references, const std::vector& uvs) - : unique_id(unique_id) - , representation_id(representation_id) - , type(type) - , transformation(transformation) - , vertices(vertices) - , normals(normals) - , faces(faces) - , edges(edges) - , material_ids(material_ids) - , materials(materials) - , material_references(material_references) - , uvs(uvs) - {} - - std::vector& parents() { return parents_; } - const std::vector& parents() const { return parents_; } - }; COLLADABU::NativeString filename; COLLADASW::StreamWriter stream; collada_scene scene; @@ -209,7 +155,7 @@ private: collada_materials materials; collada_geometries geometries; collada_serializer *serializer; - std::vector deferreds; + std::vector> deferreds; virtual ~collada_exporter() {} void startDocument(const std::string& unit_name, float unit_magnitude); void write(const ifcopenshell::geom::triangulation_element* o);