From 0e2bac5e3b4b60564ac24fa05f33cafd2b66caad Mon Sep 17 00:00:00 2001 From: Thomas Krijnen Date: Wed, 8 Jul 2020 12:07:10 +0200 Subject: [PATCH] Shape reuse based on std::less --- src/ifcgeom/schema_agnostic/Converter.cpp | 25 ++- src/ifcgeom/schema_agnostic/Converter.h | 2 + src/ifcgeom/taxonomy.cpp | 220 ++++++++++++++++++++++ src/ifcgeom/taxonomy.h | 17 +- 4 files changed, 254 insertions(+), 10 deletions(-) create mode 100644 src/ifcgeom/taxonomy.cpp diff --git a/src/ifcgeom/schema_agnostic/Converter.cpp b/src/ifcgeom/schema_agnostic/Converter.cpp index b037ad6c29..3fd94452e2 100644 --- a/src/ifcgeom/schema_agnostic/Converter.cpp +++ b/src/ifcgeom/schema_agnostic/Converter.cpp @@ -54,18 +54,29 @@ ifcopenshell::geometry::NativeElement* ifcopenshell::geometry::Converter::create } std::clock_t geom_start = std::clock(); - - auto place = taxonomy::matrix4(); - std::swap(place, product_node->matrix); - try { - kernel_->convert(product_node, shapes); - } catch (...) { + if (false) { std::ostringstream oss; product_node->print(oss); std::string s = oss.str(); std::wcout << s.c_str() << std::endl; - return nullptr; + } + + auto place = taxonomy::matrix4(); + std::swap(place, product_node->matrix); + + auto it = cache_.find(product_node); + if (it == cache_.end()) { + try { + kernel_->convert(product_node, shapes); + } catch (...) { + return nullptr; + } + cache_.insert(it, { product_node, shapes }); + } else { + Logger::Notice("Reusing geometry for", product); + Logger::Notice("Found", it->first->instance); + shapes = it->second; } shape = new ifcopenshell::geometry::Representation::BRep(s, representation_id_builder.str(), shapes); diff --git a/src/ifcgeom/schema_agnostic/Converter.h b/src/ifcgeom/schema_agnostic/Converter.h index 310bdc5785..676bb15e3f 100644 --- a/src/ifcgeom/schema_agnostic/Converter.h +++ b/src/ifcgeom/schema_agnostic/Converter.h @@ -18,6 +18,8 @@ namespace ifcopenshell { namespace geometry { abstract_mapping* mapping_; kernels::AbstractKernel* kernel_; ifcopenshell::geometry::settings settings_; + std::map cache_; + public: kernels::AbstractKernel* kernel() { return kernel_; } diff --git a/src/ifcgeom/taxonomy.cpp b/src/ifcgeom/taxonomy.cpp new file mode 100644 index 0000000000..b44f8858ed --- /dev/null +++ b/src/ifcgeom/taxonomy.cpp @@ -0,0 +1,220 @@ +#include "taxonomy.h" + +using namespace ifcopenshell::geometry::taxonomy; + +namespace { + template + bool compare(const eigen_base& t, const eigen_base& u) { + auto t_begin = t.components->data(); + auto t_end = t.components->data() + t.components->size(); + + auto u_begin = u.components->data(); + auto u_end = u.components->data() + u.components->size(); + + return std::lexicographical_compare(t_begin, t_end, u_begin, u_end); + } + + bool compare(const line& a, const line& b) { + return compare(a.matrix, b.matrix); + } + + bool compare(const plane& a, const plane& b) { + return compare(a.matrix, b.matrix); + } + + bool compare(const circle& a, const circle& b) { + if (a.radius == b.radius) { + return compare(a.matrix, b.matrix); + } + return a.radius < b.radius; + } + + bool compare(const ellipse& a, const ellipse& b) { + if (a.radius == b.radius && a.radius2 == b.radius2) { + return compare(a.matrix, b.matrix); + } + return + std::tie(a.radius, a.radius2) < + std::tie(b.radius, b.radius2); + } + + bool compare(const bspline_curve&, const bspline_curve&) { + throw std::runtime_error("not implemented"); + } + + template + typename std::enable_if::value, int>::type less_to_order(const T& a, const T& b) { + const bool a_lt_b = compare(a, b); + const bool b_lt_a = compare(b, a); + return a_lt_b ? + -1 : (!b_lt_a ? 0 : 1); + } + + template + typename std::enable_if::value, int>::type less_to_order(const T& a, const T& b) { + const bool a_lt_b = a < b; + const bool b_lt_a = b < a; + return a_lt_b ? + -1 : (!b_lt_a ? 0 : 1); + } + + template + int less_to_order_optional(const boost::optional& a, const boost::optional& b) { + if (a && b) { + return less_to_order(*a, *b); + } else if (!a && !b) { + return 0; + } else if (a) { + return 1; + } else { + return -1; + } + } + + int compare(const boost::variant& a, const boost::variant& b) { + bool a_lt_b, b_lt_a; + if (a.which() == 0) { + a_lt_b = compare(boost::get(a), boost::get(b)); + b_lt_a = compare(boost::get(b), boost::get(a)); + } else { + a_lt_b = std::less()(boost::get(a), boost::get(b)); + b_lt_a = std::less()(boost::get(b), boost::get(a)); + } + return a_lt_b ? + -1 : (!b_lt_a ? 0 : 1); + } + + bool compare(const trimmed_curve& a, const trimmed_curve& b); + + bool compare(const collection& a, const collection& b); + + bool compare(const extrusion& a, const extrusion& b) { + const int order[3] = { + less_to_order(a.basis, b.basis), + less_to_order(a.direction, b.direction), + a.depth < b.depth ? -1 : (a.depth == b.depth ? 0 : 1) + }; + auto it = std::find_if(std::begin(order), std::end(order), [](int x) { return x; }); + if (it == std::end(order)) return false; + return *it == -1; + } + + bool compare(const style& a, const style& b) { + const int order[5] = { + less_to_order_optional(a.name, b.name), + less_to_order_optional(a.diffuse, b.diffuse), + less_to_order_optional(a.specular, b.specular), + less_to_order_optional(a.specularity, b.specularity), + less_to_order_optional(a.transparency, b.transparency) + }; + auto it = std::find_if(std::begin(order), std::end(order), [](int x) { return x; }); + if (it == std::end(order)) return false; + return *it == -1; + } + + /* A compile-time for loop over the taxonomy kinds */ + template + struct dispatch_comparison { + static bool dispatch(const item* a, const item* b) { + if (N == a->kind() && N == b->kind()) { + auto A = static_cast*>(a); + auto B = static_cast*>(b); + return compare(*A, *B); + } else { + return dispatch_comparison::dispatch(a, b); + } + } + }; + + template <> + struct dispatch_comparison { + static bool dispatch(const item*, const item*) { + return false; + } + }; +} + +bool ifcopenshell::geometry::taxonomy::less(const item* a, const item* b) { + if (a == b) { + return false; + } + + int a_kind = a->kind(); + int b_kind = b->kind(); + + if (a_kind != b_kind) { + return a_kind < b_kind; + } + + return dispatch_comparison<0>::dispatch(a, b); +} + + +namespace { + bool compare(const trimmed_curve& a, const trimmed_curve& b) { + int a_which_start = a.start.which(); + int a_which_end = a.end.which(); + int b_which_start = b.start.which(); + int b_which_end = b.end.which(); + if (std::tie(a.orientation, a_which_start, a_which_end) == + std::tie(b.orientation, b_which_start, b_which_end)) { + + int start_state = compare(a.start, b.start); + + if (start_state == 0) { + + int end_state = compare(a.end, b.end); + + if (end_state == 0) { + + int a_has_basis = !!a.basis; + int b_has_basis = !!a.basis; + + if (a_has_basis == b_has_basis) { + + if (!a_has_basis) { + // Finally, equality + return false; + } else { + return less(a.basis, b.basis); + } + + } else { + return a_has_basis < b_has_basis; + } + + } else { + return end_state == -1; + } + + } else { + return start_state == -1; + } + + } else { + return + std::tie(a.orientation, a_which_start, a_which_end) < + std::tie(b.orientation, b_which_start, b_which_end); + } + } + + bool compare(const collection& a, const collection& b) { + if (a.children.size() == b.children.size()) { + auto at = a.children.begin(); + auto bt = b.children.begin(); + for (; at != a.children.end(); ++at, ++bt) { + const bool a_lt_b = less(*at, *bt); + const bool b_lt_a = less(*bt, *at); + if (!a_lt_b && !b_lt_a) { + // Elements equal. + continue; + } + return a_lt_b; + } + // Vectors equal. + return false; + } else { + return a.children.size() < b.children.size(); + } + } +} \ No newline at end of file diff --git a/src/ifcgeom/taxonomy.h b/src/ifcgeom/taxonomy.h index 98a8da2ca7..5c78592078 100644 --- a/src/ifcgeom/taxonomy.h +++ b/src/ifcgeom/taxonomy.h @@ -37,6 +37,14 @@ struct item { item(const IfcUtil::IfcBaseClass* instance = nullptr) : instance(instance) {} }; +bool less(const item*, const item*); + +struct less_functor { + bool operator()(const item* a, const item* b) const { + return less(a, b); + } +}; + template struct eigen_base { T* components; @@ -258,7 +266,7 @@ struct trimmed_curve : public curve { basis->print(o, indent + 4); } - const boost::variant const * start_end[2] = { &start, &end }; + const boost::variant * const start_end[2] = { &start, &end }; for (int i = 0; i < 2; ++i) { o << std::string(indent + 4, ' ') << (i == 0 ? "start" : "end") << std::endl; if (start_end[i]->which() == 0) { @@ -307,6 +315,9 @@ struct collection : public geom_item { void print(std::ostream& o, int indent = 0) const { o << std::string(indent, ' ') << "collection" << std::endl; + if (!matrix.components->isIdentity()) { + matrix.print(o, indent + 4); + } for (auto& c : children) { c->print(o, indent + 4); } @@ -326,7 +337,7 @@ struct plane : public surface { virtual item* clone() const { return new plane(*this); } virtual kinds kind() const { return PLANE; } - void print(std::ostream& o, int indent = 0) const { + void print(std::ostream& o, int) const { o << "not implemented"; } }; @@ -386,7 +397,7 @@ struct boolean_result : public collection { }; namespace impl { - typedef std::tuple KindsTuple; + typedef std::tuple KindsTuple; typedef std::tuple CurvesTuple; }