diff --git a/src/ifcgeom/abstract_mapping.h b/src/ifcgeom/abstract_mapping.h index 97f6254f6c..9a39fb55d0 100644 --- a/src/ifcgeom/abstract_mapping.h +++ b/src/ifcgeom/abstract_mapping.h @@ -15,15 +15,10 @@ namespace ifcopenshell { namespace geometry { - class Element; - class NativeElement; - struct geometry_conversion_task { int index; IfcUtil::IfcBaseEntity* representation; IfcEntityList::ptr products; - std::vector breps; - std::vector elements; }; typedef boost::function filter_t; diff --git a/src/ifcgeom/schema/mapping.cpp b/src/ifcgeom/schema/mapping.cpp index 77d57a6d02..046bb399ec 100644 --- a/src/ifcgeom/schema/mapping.cpp +++ b/src/ifcgeom/schema/mapping.cpp @@ -513,12 +513,14 @@ taxonomy::item* mapping::map_impl(const IfcSchema::IfcCartesianTransformationOpe scale3 = nu->hasScale3() ? nu->Scale3() : scale1; } - *m->components << + Eigen::Matrix4d tmp; + tmp << axis1 * scale1, axis2 * scale2, axis3 * scale3, origin; + *m->components = tmp.inverse(); m->components->transposeInPlace(); // @todo tag identity? @@ -560,7 +562,7 @@ taxonomy::item* mapping::map_impl(const IfcSchema::IfcLocalPlacement* inst) { return m4; } -IfcSchema::IfcProduct::list::ptr mapping::products_represented_by(const IfcSchema::IfcRepresentation* representation) { +IfcSchema::IfcProduct::list::ptr mapping::products_represented_by(const IfcSchema::IfcRepresentation* representation, bool only_direct) { IfcSchema::IfcProduct::list::ptr products(new IfcSchema::IfcProduct::list); IfcSchema::IfcProductRepresentation::list::ptr prodreps = representation->OfProductRepresentation(); @@ -575,6 +577,10 @@ IfcSchema::IfcProduct::list::ptr mapping::products_represented_by(const IfcSchem products->push((*it)->data().getInverse((&IfcSchema::IfcProduct::Class()), -1)->as()); } + if (only_direct) { + return products; + } + IfcSchema::IfcRepresentationMap::list::ptr maps = representation->RepresentationMap(); if (maps->size() == 1) { IfcSchema::IfcRepresentationMap* rmap = *maps->begin(); @@ -777,46 +783,16 @@ void mapping::get_representations(std::vector& tasks, int task_index = 0; for (auto representation : *representations) { + // There used to be a whole lot of magic in here to pair multiple products with, + // representations but this is now handled at a later stage where equivalent + // taxonomy::items (sorted based on std::less) are grouped. - // Init. the list of filtered IfcProducts for this representation - - // Include only the desired products for processing. - IfcSchema::IfcProduct::list::ptr ifcproducts = filter_products(products_represented_by(representation), filters); + IfcSchema::IfcProduct::list::ptr ifcproducts = filter_products(products_represented_by(representation, true), filters); if (ifcproducts->size() == 0) { continue; } - auto geometry_reuse_ok_for_current_representation_ = reuse_ok_(s, ifcproducts); - - IfcSchema::IfcRepresentationMap::list::ptr maps = representation->RepresentationMap(); - - if (!geometry_reuse_ok_for_current_representation_ && maps->size() == 1) { - // unfiltered_products contains products represented by this representation by means of mapped items. - // For example because of openings applied to products, reuse might not be acceptable and then the - // products will be processed by means of their immediate representation and not the mapped representation. - - // IfcRepresentationMaps are also used for IfcTypeProducts, so an additional check is performed whether the map - // is indeed used by IfcMappedItems. - IfcSchema::IfcRepresentationMap* map = *maps->begin(); - if (map->MapUsage()->size() > 0) { - continue; - } - } - - // Check if this represenation has (or will be) processed as part its mapped representation - bool representation_processed_as_mapped_item = false; - IfcSchema::IfcRepresentation* rep_mapped_to = representation_mapped_to(representation); - if (rep_mapped_to) { - representation_processed_as_mapped_item = geometry_reuse_ok_for_current_representation_ && ( - ok_mapped_representations->contains(rep_mapped_to) || reuse_ok_(s, filter_products(products_represented_by(rep_mapped_to), filters))); - } - - if (representation_processed_as_mapped_item) { - ok_mapped_representations->push(rep_mapped_to); - continue; - } - // @todo, fix this properly by considering the mapped geometry types in the representation. if (representation->hasRepresentationIdentifier() && representation->RepresentationIdentifier() == "Body") { geometry_conversion_task task; diff --git a/src/ifcgeom/schema/mapping.h b/src/ifcgeom/schema/mapping.h index 597826cf33..232ee8e848 100644 --- a/src/ifcgeom/schema/mapping.h +++ b/src/ifcgeom/schema/mapping.h @@ -31,7 +31,7 @@ namespace geometry { const IfcSchema::IfcMaterial* get_single_material_association(const IfcSchema::IfcProduct* product); IfcSchema::IfcRepresentation* representation_mapped_to(const IfcSchema::IfcRepresentation* representation); - IfcSchema::IfcProduct::list::ptr products_represented_by(const IfcSchema::IfcRepresentation* representation); + IfcSchema::IfcProduct::list::ptr products_represented_by(const IfcSchema::IfcRepresentation* representation, bool only_direct=false); bool reuse_ok_(settings& s, const IfcSchema::IfcProduct::list::ptr& products); IfcEntityList::ptr find_openings(const IfcSchema::IfcProduct* product); IfcUtil::IfcBaseEntity* get_decomposing_entity(IfcUtil::IfcBaseEntity* product, bool include_openings); diff --git a/src/ifcgeom/schema_agnostic/Converter.cpp b/src/ifcgeom/schema_agnostic/Converter.cpp index 3fd94452e2..8b1a87de12 100644 --- a/src/ifcgeom/schema_agnostic/Converter.cpp +++ b/src/ifcgeom/schema_agnostic/Converter.cpp @@ -9,15 +9,14 @@ ifcopenshell::geometry::Converter::Converter(const std::string& geometry_library mapping_ = impl::mapping_implementations().construct(file, settings_); } -ifcopenshell::geometry::NativeElement* ifcopenshell::geometry::Converter::create_brep_for_representation_and_product( - IfcUtil::IfcBaseEntity* representation, IfcUtil::IfcBaseEntity* product) { - - std::stringstream representation_id_builder; - +ifcopenshell::geometry::NativeElement* ifcopenshell::geometry::Converter::create_brep_for_representation_and_product(taxonomy::item* product_node, const taxonomy::matrix4& place) { + auto product = (IfcUtil::IfcBaseEntity*) product_node->instance; const std::string product_type = product->declaration().name(); // @todo element_settings s(settings_, 1.0 /*getValue(GV_LENGTH_UNIT) */, product_type); + std::stringstream representation_id_builder; + int parent_id = -1; try { IfcUtil::IfcBaseEntity* parent_object = mapping_->get_decomposing_entity(product); @@ -30,62 +29,34 @@ ifcopenshell::geometry::NativeElement* ifcopenshell::geometry::Converter::create const std::string guid = product->get_value("GlobalId"); const std::string name = product->get_value_or("Name", ""); - - representation_id_builder << representation->data().id(); - ifcopenshell::geometry::Representation::BRep* shape; - ifcopenshell::geometry::ConversionResults shapes; + // @todo should be rep id. + representation_id_builder << product->data().id(); - /* - auto rep_item = mapping_->map(representation); - // @todo should map() throw an exception instead? - if (rep_item == nullptr) { - return nullptr; - } - */ + brep_ptr shape; - - std::clock_t map_start = std::clock(); - - // @todo how to combine product_node and rep_item? - auto product_node = (taxonomy::geom_item*) mapping_->map(product); - if (product_node == nullptr) { - return nullptr; - } - - std::clock_t geom_start = std::clock(); - - if (false) { - std::ostringstream oss; - product_node->print(oss); - std::string s = oss.str(); - std::wcout << s.c_str() << std::endl; - } - - auto place = taxonomy::matrix4(); - std::swap(place, product_node->matrix); - - auto it = cache_.find(product_node); + auto it = cache_.end(); // cache_.find(product_node); if (it == cache_.end()) { try { + ifcopenshell::geometry::ConversionResults shapes; + + std::clock_t geom_start = std::clock(); kernel_->convert(product_node, shapes); + std::clock_t geom_end = std::clock(); + + total_geom_time += (geom_end - geom_start) / (double)CLOCKS_PER_SEC; + + shape = brep_ptr(new ifcopenshell::geometry::Representation::BRep(s, representation_id_builder.str(), shapes)); } catch (...) { return nullptr; } - cache_.insert(it, { product_node, shapes }); + cache_.insert(it, { product_node, shape }); } else { Logger::Notice("Reusing geometry for", product); Logger::Notice("Found", it->first->instance); - shapes = it->second; + shape = it->second; } - shape = new ifcopenshell::geometry::Representation::BRep(s, representation_id_builder.str(), shapes); - - std::clock_t geom_end = std::clock(); - - total_map_time += (geom_start - map_start) / (double) CLOCKS_PER_SEC; - total_geom_time += (geom_end - geom_start) / (double) CLOCKS_PER_SEC; - return new NativeElement( product->data().id(), parent_id, @@ -96,9 +67,32 @@ ifcopenshell::geometry::NativeElement* ifcopenshell::geometry::Converter::create "", place, // product_node->matrix, - boost::shared_ptr(shape), + shape, product ); +} + +ifcopenshell::geometry::NativeElement* ifcopenshell::geometry::Converter::create_brep_for_representation_and_product( + // @todo representation is not used yet. + IfcUtil::IfcBaseEntity*, IfcUtil::IfcBaseEntity* product) { + + std::clock_t map_start = std::clock(); + + // @todo how to combine product_node and rep_item? + auto product_node = (taxonomy::geom_item*) mapping_->map(product); + if (product_node == nullptr) { + return nullptr; + } + + std::clock_t map_end = std::clock(); + + auto place = taxonomy::matrix4(); + std::swap(place, product_node->matrix); + + total_map_time += (map_end - map_start) / (double)CLOCKS_PER_SEC; + + return create_brep_for_representation_and_product(product_node, place); + /* std::stringstream representation_id_builder; @@ -240,6 +234,47 @@ ifcopenshell::geometry::NativeElement* ifcopenshell::geometry::Converter::create */ } +ifcopenshell::geometry::NativeElement* ifcopenshell::geometry::Converter::create_brep_for_processed_representation( + IfcUtil::IfcBaseEntity* product, const taxonomy::matrix4& place, ifcopenshell::geometry::NativeElement* brep) { + + int parent_id = -1; + try { + IfcUtil::IfcBaseEntity* parent_object = mapping_->get_decomposing_entity(product); + if (parent_object) { + parent_id = parent_object->data().id(); + } + } catch (const std::exception& e) { + Logger::Error(e); + } + + const std::string guid = product->get_value("GlobalId"); + const std::string name = product->get_value_or("Name", ""); + + /* + std::string context_string = ""; + if (representation->hasRepresentationIdentifier()) { + context_string = representation->RepresentationIdentifier(); + } else if (representation->ContextOfItems()->hasContextType()) { + context_string = representation->ContextOfItems()->ContextType(); + } + */ + + const std::string product_type = product->declaration().name(); + + return new NativeElement( + product->data().id(), + parent_id, + name, + product_type, + guid, + // @todo + "", + place, + brep->geometry_pointer(), + product + ); +} + ifcopenshell::geometry::NativeElement* ifcopenshell::geometry::Converter::create_brep_for_processed_representation( IfcUtil::IfcBaseEntity* /* representation */, IfcUtil::IfcBaseEntity* product, ifcopenshell::geometry::NativeElement* brep) diff --git a/src/ifcgeom/schema_agnostic/Converter.h b/src/ifcgeom/schema_agnostic/Converter.h index 676bb15e3f..5a1118f3d3 100644 --- a/src/ifcgeom/schema_agnostic/Converter.h +++ b/src/ifcgeom/schema_agnostic/Converter.h @@ -14,11 +14,13 @@ namespace ifcopenshell { namespace geometry { class NativeElement; class Converter { + public: + typedef boost::shared_ptr brep_ptr; private: abstract_mapping* mapping_; kernels::AbstractKernel* kernel_; ifcopenshell::geometry::settings settings_; - std::map cache_; + std::map cache_; public: kernels::AbstractKernel* kernel() { return kernel_; } @@ -96,6 +98,9 @@ namespace ifcopenshell { namespace geometry { ifcopenshell::geometry::NativeElement* create_brep_for_representation_and_product(IfcUtil::IfcBaseEntity* representation, IfcUtil::IfcBaseEntity* product); ifcopenshell::geometry::NativeElement* create_brep_for_processed_representation(IfcUtil::IfcBaseEntity* representation, IfcUtil::IfcBaseEntity* product, ifcopenshell::geometry::NativeElement* brep); + ifcopenshell::geometry::NativeElement* create_brep_for_representation_and_product(taxonomy::item*, const taxonomy::matrix4&); + ifcopenshell::geometry::NativeElement* create_brep_for_processed_representation(IfcUtil::IfcBaseEntity*, const taxonomy::matrix4&, ifcopenshell::geometry::NativeElement*); + /* static int count(const ifcopenshell::geometry::ConversionResultShape*, int, bool unique=false); static int surface_genus(const ifcopenshell::geometry::ConversionResultShape*); diff --git a/src/ifcgeom/schema_agnostic/IfcGeomIterator.h b/src/ifcgeom/schema_agnostic/IfcGeomIterator.h index 2a9420c426..076fef099d 100644 --- a/src/ifcgeom/schema_agnostic/IfcGeomIterator.h +++ b/src/ifcgeom/schema_agnostic/IfcGeomIterator.h @@ -105,6 +105,17 @@ #undef max #endif +namespace ifcopenshell { namespace geometry { + +struct geometry_conversion_result { + taxonomy::item* item; + std::vector> products; + std::vector breps; + std::vector elements; +}; + +} } + namespace { ifcopenshell::geometry::Element* process_based_on_settings( const ifcopenshell::geometry::settings& settings, @@ -137,11 +148,12 @@ namespace { void create_element( ifcopenshell::geometry::Converter* converter, const ifcopenshell::geometry::settings& settings, - ifcopenshell::geometry::geometry_conversion_task* rep) + ifcopenshell::geometry::geometry_conversion_result* rep) { - IfcUtil::IfcBaseEntity* representation = rep->representation; - IfcUtil::IfcBaseEntity* product = (IfcUtil::IfcBaseEntity*) *rep->products->begin(); - auto brep = converter->create_brep_for_representation_and_product(representation, product); + ifcopenshell::geometry::taxonomy::item* representation = rep->item; + auto place = rep->products.front().second; + auto brep = converter->create_brep_for_representation_and_product(representation, place); + if (!brep) { return; } @@ -154,10 +166,11 @@ namespace { rep->breps = { brep }; rep->elements = { elem }; - for (auto it = rep->products->begin() + 1; it != rep->products->end(); ++it) { - auto brep2 = converter->create_brep_for_processed_representation(representation, (IfcUtil::IfcBaseEntity*) *it, brep); + for (auto it = rep->products.begin() + 1; it != rep->products.end(); ++it) { + const auto& p = *it; + auto brep2 = converter->create_brep_for_processed_representation(p.first, p.second, brep); if (brep2) { - auto elem2 = process_based_on_settings(settings, brep, dynamic_cast(elem)); + auto elem2 = process_based_on_settings(settings, brep2, dynamic_cast(elem)); if (elem2) { rep->breps.push_back(brep2); rep->elements.push_back(elem2); @@ -174,8 +187,8 @@ namespace ifcopenshell { namespace geometry { int num_threads_; std::atomic progress_; - std::vector tasks_; - std::vector::iterator task_iterator_; + std::vector tasks_; + std::vector::iterator task_iterator_; std::vector all_processed_elements_; std::vector all_processed_native_elements_; @@ -210,7 +223,34 @@ namespace ifcopenshell { namespace geometry { bool initialize() { converter_ = new Converter(geometry_library_, ifc_file, settings_); - converter_->mapping()->get_representations(tasks_, filters_, settings_); + std::vector reps; + converter_->mapping()->get_representations(reps, filters_, settings_); + std::vector products; + for (auto& r : reps) { + std::copy(r.products->begin(), r.products->end(), std::back_inserter(products)); + } + std::vector items; + std::map placements; + std::transform(products.begin(), products.end(), std::back_inserter(items), [this, &placements](IfcUtil::IfcBaseClass* p) { + auto item = converter_->mapping()->map(p); + // Product placements do not affect item reuse and should temporarily be swapped to identity + std::swap(placements[item], ((taxonomy::geom_item*)item)->matrix); + return item; + }); + std::sort(items.begin(), items.end(), taxonomy::less); + auto it = items.begin(); + while (it < items.end()) { + auto jt = std::upper_bound(it, items.end(), *it, taxonomy::less); + geometry_conversion_result r; + r.item = *it; + std::transform(it, jt, std::back_inserter(r.products), [&r, &placements](taxonomy::item* product_node) { + return std::make_pair((IfcUtil::IfcBaseEntity*) product_node->instance, placements[product_node]); + }); + tasks_.push_back(r); + it = jt; + } + + Logger::Notice("Created " + boost::lexical_cast(tasks_.size()) + " tasks for " + boost::lexical_cast(products.size()) + " products"); if (tasks_.size() == 0) { Logger::Warning("No representations encountered, aborting"); @@ -386,8 +426,8 @@ namespace ifcopenshell { namespace geometry { ++done; } - IfcUtil::IfcBaseClass* create_shape_model_for_next_entity() { - geometry_conversion_task* task = nullptr; + const IfcUtil::IfcBaseClass* create_shape_model_for_next_entity() { + geometry_conversion_result* task = nullptr; while (task_iterator_ != tasks_.end()) { task = &*task_iterator_++; create_element(converter_, settings_, task); @@ -400,7 +440,7 @@ namespace ifcopenshell { namespace geometry { if (task) { all_processed_elements_.insert(all_processed_elements_.end(), task->elements.begin(), task->elements.end()); all_processed_native_elements_.insert(all_processed_native_elements_.end(), task->breps.begin(), task->breps.end()); - return (*task->products)[0]; + return task->item->instance; } else { return nullptr; } @@ -410,7 +450,7 @@ namespace ifcopenshell { namespace geometry { /// Moves to the next shape representation, create its geometry, and returns the associated product. /// Use get() to retrieve the created geometry. - IfcUtil::IfcBaseClass* next() { + const IfcUtil::IfcBaseClass* next() { if (num_threads_ != 1) { task_result_index_++; if (task_result_index_ == all_processed_elements_.size()) { @@ -555,8 +595,8 @@ namespace ifcopenshell { namespace geometry { */ } - IfcUtil::IfcBaseClass* create() { - IfcUtil::IfcBaseClass* product = nullptr; + const IfcUtil::IfcBaseClass* create() { + const IfcUtil::IfcBaseClass* product = nullptr; try { product = create_shape_model_for_next_entity(); } catch (const std::exception& e) { diff --git a/src/ifcgeom/taxonomy.cpp b/src/ifcgeom/taxonomy.cpp index b44f8858ed..cf33e1c96a 100644 --- a/src/ifcgeom/taxonomy.cpp +++ b/src/ifcgeom/taxonomy.cpp @@ -89,6 +89,8 @@ namespace { bool compare(const collection& a, const collection& b); bool compare(const extrusion& a, const extrusion& b) { + // @todo extrusions can also have non-identity matrices right? perhaps it's time + // for a dedicated transform node and not on the abstract geom_item. const int order[3] = { less_to_order(a.basis, b.basis), less_to_order(a.direction, b.direction), @@ -211,8 +213,8 @@ namespace { } return a_lt_b; } - // Vectors equal. - return false; + // Vectors equal, compare matrix (in case of mapped items). + return compare(a.matrix, b.matrix); } else { return a.children.size() < b.children.size(); }