mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 09:21:46 +00:00
Fixes for transform and mapped item reuse in taxonomy
This commit is contained in:
@@ -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<ifcopenshell::geometry::NativeElement*> breps;
|
||||
std::vector<ifcopenshell::geometry::Element*> elements;
|
||||
};
|
||||
|
||||
typedef boost::function<bool(IfcUtil::IfcBaseEntity*)> filter_t;
|
||||
|
||||
@@ -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<IfcSchema::IfcProduct>());
|
||||
}
|
||||
|
||||
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<geometry_conversion_task>& 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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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<std::string>("GlobalId");
|
||||
const std::string name = product->get_value_or<std::string>("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<ifcopenshell::geometry::Representation::BRep>(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<std::string>("GlobalId");
|
||||
const std::string name = product->get_value_or<std::string>("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)
|
||||
|
||||
@@ -14,11 +14,13 @@ namespace ifcopenshell { namespace geometry {
|
||||
class NativeElement;
|
||||
|
||||
class Converter {
|
||||
public:
|
||||
typedef boost::shared_ptr<ifcopenshell::geometry::Representation::BRep> brep_ptr;
|
||||
private:
|
||||
abstract_mapping* mapping_;
|
||||
kernels::AbstractKernel* kernel_;
|
||||
ifcopenshell::geometry::settings settings_;
|
||||
std::map<ifcopenshell::geometry::taxonomy::item*, ifcopenshell::geometry::ConversionResults, ifcopenshell::geometry::taxonomy::less_functor> cache_;
|
||||
std::map<ifcopenshell::geometry::taxonomy::item*, brep_ptr, ifcopenshell::geometry::taxonomy::less_functor> 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*);
|
||||
|
||||
@@ -105,6 +105,17 @@
|
||||
#undef max
|
||||
#endif
|
||||
|
||||
namespace ifcopenshell { namespace geometry {
|
||||
|
||||
struct geometry_conversion_result {
|
||||
taxonomy::item* item;
|
||||
std::vector<std::pair<IfcUtil::IfcBaseEntity*, taxonomy::matrix4>> products;
|
||||
std::vector<ifcopenshell::geometry::NativeElement*> breps;
|
||||
std::vector<ifcopenshell::geometry::Element*> 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<ifcopenshell::geometry::TriangulationElement*>(elem));
|
||||
auto elem2 = process_based_on_settings(settings, brep2, dynamic_cast<ifcopenshell::geometry::TriangulationElement*>(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<int> progress_;
|
||||
std::vector<geometry_conversion_task> tasks_;
|
||||
std::vector<geometry_conversion_task>::iterator task_iterator_;
|
||||
std::vector<geometry_conversion_result> tasks_;
|
||||
std::vector<geometry_conversion_result>::iterator task_iterator_;
|
||||
|
||||
std::vector<ifcopenshell::geometry::Element*> all_processed_elements_;
|
||||
std::vector<ifcopenshell::geometry::NativeElement*> 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<geometry_conversion_task> reps;
|
||||
converter_->mapping()->get_representations(reps, filters_, settings_);
|
||||
std::vector<IfcUtil::IfcBaseClass*> products;
|
||||
for (auto& r : reps) {
|
||||
std::copy(r.products->begin(), r.products->end(), std::back_inserter(products));
|
||||
}
|
||||
std::vector<taxonomy::item*> items;
|
||||
std::map<taxonomy::item*, taxonomy::matrix4> 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<std::string>(tasks_.size()) + " tasks for " + boost::lexical_cast<std::string>(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) {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user