Skip to content

Commit bb329af

Browse files
committed
settings: DeferProcessingFirstElement, MaxOffset, MaxOffsetDeviation, ApplyOffset; taxonomy: centroid funcs; iterator get_tasks + items() funcs; geom.map_shape()
1 parent 6c4e4e5 commit bb329af

6 files changed

Lines changed: 306 additions & 8 deletions

File tree

src/ifcgeom/ConversionSettings.h

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,27 @@ namespace ifcopenshell {
441441
static constexpr const char* const description = "Experimental as not all topology hash functions fully implemented";
442442
static constexpr bool defaultvalue = false;
443443
};
444+
445+
struct DeferProcessingFirstElement : public SettingBase<DeferProcessingFirstElement, bool, true> {
446+
static constexpr const char* const name = "defer-processing-first-element";
447+
static constexpr const char* const description = "Don't process first element in Iterator::initialize call()";
448+
static constexpr bool defaultvalue = false;
449+
};
450+
451+
struct MaxOffset : public SettingBase<MaxOffset, double> {
452+
static constexpr const char* const name = "max-offset";
453+
static constexpr const char* const description = "Maximum translation offset to be observed after which median offset in model gets removed and logged. Requires --no-parallel-mapping.";
454+
};
455+
456+
struct MaxOffsetDeviation : public SettingBase<MaxOffsetDeviation, double> {
457+
static constexpr const char* const name = "max-offset-deviation";
458+
static constexpr const char* const description = "To retain field of view, completely remove elements outside of the median offset. Requires --no-parallel-mapping.";
459+
};
460+
461+
struct ApplyOffset : public SettingBase<ApplyOffset, std::vector<double>> {
462+
static constexpr const char* const name = "apply-offset";
463+
static constexpr const char* const description = "Slight variation of --model-offset where large offsets are applied by negating existing large offsets to retain maximum precision. Requires --no-parallel-mapping.";
464+
};
444465
}
445466

446467
namespace impl {
@@ -616,7 +637,7 @@ namespace ifcopenshell {
616637
};
617638

618639
class IFC_GEOM_API Settings : public SettingsContainer<
619-
std::tuple<MesherLinearDeflection, MesherAngularDeflection, ReorientShells, LengthUnit, PlaneUnit, Precision, OutputDimensionality, LayersetFirst, DisableBooleanResult, NoWireIntersectionCheck, NoWireIntersectionTolerance, PrecisionFactor, DebugBooleanOperations, BooleanAttempt2d, SurfaceColour, WeldVertices, UseWorldCoords, UnifyShapes, UseMaterialNames, ConvertBackUnits, ContextIds, ContextTypes, ContextIdentifiers, IteratorOutput, DisableOpeningSubtractions, ApplyDefaultMaterials, DontEmitNormals, GenerateUvs, ApplyLayerSets, UseElementHierarchy, ValidateQuantities, EdgeArrows, BuildingLocalPlacement, SiteLocalPlacement, ForceSpaceTransparency, CircleSegments, KeepBoundingBoxes, ComputeCurvature, FunctionStepType, FunctionStepParam, NoParallelMapping, PermissiveShapeReuse, ModelOffset, ModelRotation, TriangulationType, CgalEmitOriginalEdges, OcctNoCleanTriangulation, CacheShapes>
640+
std::tuple<MesherLinearDeflection, MesherAngularDeflection, ReorientShells, LengthUnit, PlaneUnit, Precision, OutputDimensionality, LayersetFirst, DisableBooleanResult, NoWireIntersectionCheck, NoWireIntersectionTolerance, PrecisionFactor, DebugBooleanOperations, BooleanAttempt2d, SurfaceColour, WeldVertices, UseWorldCoords, UnifyShapes, UseMaterialNames, ConvertBackUnits, ContextIds, ContextTypes, ContextIdentifiers, IteratorOutput, DisableOpeningSubtractions, ApplyDefaultMaterials, DontEmitNormals, GenerateUvs, ApplyLayerSets, UseElementHierarchy, ValidateQuantities, EdgeArrows, BuildingLocalPlacement, SiteLocalPlacement, ForceSpaceTransparency, CircleSegments, KeepBoundingBoxes, ComputeCurvature, FunctionStepType, FunctionStepParam, NoParallelMapping, PermissiveShapeReuse, ModelOffset, ModelRotation, TriangulationType, CgalEmitOriginalEdges, OcctNoCleanTriangulation, CacheShapes, DeferProcessingFirstElement, MaxOffset, MaxOffsetDeviation, ApplyOffset>
620641
>
621642
{};
622643
}

src/ifcgeom/Iterator.cpp

Lines changed: 184 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ bool IfcGeom::Iterator::initialize() {
5353
if (settings_.get<ifcopenshell::geometry::settings::NoParallelMapping>().get() && settings_.get<ifcopenshell::geometry::settings::PermissiveShapeReuse>().get()) {
5454
std::unordered_map<
5555
ifcopenshell::geometry::taxonomy::item::ptr,
56-
std::vector<std::pair<const IfcUtil::IfcBaseEntity*, ifcopenshell::geometry::taxonomy::matrix4::ptr>>> folded;
56+
std::vector<std::pair<IfcUtil::IfcBaseEntity*, ifcopenshell::geometry::taxonomy::matrix4::ptr>>> folded;
5757

5858
for (auto& r : tasks_) {
5959
auto i = r.item;
@@ -94,6 +94,10 @@ bool IfcGeom::Iterator::initialize() {
9494
}
9595
}
9696

97+
if (settings_.get<ifcopenshell::geometry::settings::NoParallelMapping>().get()) {
98+
remove_offset_();
99+
}
100+
97101
size_t num_products = 0;
98102
for (auto& r : tasks_) {
99103
num_products += !settings_.get<ifcopenshell::geometry::settings::NoParallelMapping>().get() ? r.products_2->size() : r.products.size();
@@ -136,7 +140,7 @@ bool IfcGeom::Iterator::initialize() {
136140
if (tasks_.size() == 0) {
137141
Logger::Warning("No representations encountered, aborting");
138142
initialization_outcome_.reset(false);
139-
} else {
143+
} else if (!settings_.get<ifcopenshell::geometry::settings::DeferProcessingFirstElement>().get()) {
140144

141145
task_iterator_ = tasks_.begin();
142146

@@ -153,6 +157,8 @@ bool IfcGeom::Iterator::initialize() {
153157
} else {
154158
initialization_outcome_ = create();
155159
}
160+
} else {
161+
initialization_outcome_.reset(true);
156162
}
157163

158164
return *initialization_outcome_;
@@ -505,6 +511,10 @@ IfcGeom::Element* IfcGeom::Iterator::get()
505511
throw std::runtime_error("Iterator not initialized");
506512
}
507513

514+
if (settings_.get<ifcopenshell::geometry::settings::DeferProcessingFirstElement>().get() && !task_result_ptr_initialized) {
515+
throw std::runtime_error("No elements processed");
516+
}
517+
508518
auto ret = *task_result_iterator_;
509519

510520
// If we want to organize the element considering their hierarchy
@@ -625,6 +635,178 @@ const IfcUtil::IfcBaseClass* IfcGeom::Iterator::create() {
625635
return product;
626636
}
627637

638+
ifcopenshell::geometry::taxonomy::direction3::ptr IfcGeom::Iterator::remove_offset_() {
639+
640+
using namespace ifcopenshell::geometry::taxonomy;
641+
using namespace ifcopenshell::geometry::settings;
642+
643+
if (!settings_.get<MaxOffset>().has()) {
644+
return nullptr;
645+
}
646+
647+
if (!settings_.get<NoParallelMapping>().get()) {
648+
throw std::runtime_error("remove_offset() can only be called with defer-processing-first-element and no-parallel-mapping settings");
649+
}
650+
651+
auto collect_offset = [&](const item::ptr& itm, const std::vector<std::pair<IfcUtil::IfcBaseEntity*, matrix4::ptr>>& pr) -> std::pair<double, Eigen::Vector3d> {
652+
std::function<std::pair<double, Eigen::Vector3d>(const item::ptr&, Eigen::Matrix4d)> traverse;
653+
traverse = [&](const item::ptr& node, Eigen::Matrix4d m4) -> std::pair<double, Eigen::Vector3d> {
654+
if (auto shl = std::dynamic_pointer_cast<shell>(node)) {
655+
auto p = shl->centroid();
656+
Eigen::Vector4d v;
657+
v << p->components()(0), p->components()(1), p->components()(2), 1.0;
658+
Eigen::Vector3d translation_part = (m4 * v).head<3>();
659+
double translation_amnt = translation_part.norm();
660+
if (translation_amnt > settings_.get<MaxOffset>().get()) {
661+
return { translation_amnt, translation_part };
662+
} else {
663+
return { 0.0, Eigen::Vector3d::Zero() };
664+
}
665+
} else {
666+
if (auto gi = std::dynamic_pointer_cast<geom_item>(node)) {
667+
if (gi->matrix) {
668+
m4 = m4 * gi->matrix->ccomponents();
669+
}
670+
}
671+
Eigen::Vector3d translation_part = m4.block<3, 1>(0, 3);
672+
double translation_amnt = translation_part.norm();
673+
if (translation_amnt > settings_.get<MaxOffset>().get()) {
674+
return { translation_amnt, translation_part };
675+
} else if (auto col = std::dynamic_pointer_cast<collection>(node)) {
676+
std::vector<std::pair<double, Eigen::Vector3d>> child_transforms;
677+
for (const auto& child : col->children) {
678+
child_transforms.push_back(traverse(child, m4));
679+
}
680+
if (!child_transforms.empty()) {
681+
return *std::max_element(child_transforms.begin(), child_transforms.end(),
682+
[](const auto& a, const auto& b) { return a.first < b.first; });
683+
}
684+
}
685+
return { 0.0, Eigen::Vector3d::Zero() };
686+
}
687+
};
688+
689+
Eigen::Matrix4d m4 = Eigen::Matrix4d::Identity();
690+
if (pr.size() == 1 && pr[0].second) {
691+
m4 = pr[0].second->ccomponents();
692+
}
693+
return traverse(itm, m4);
694+
};
695+
696+
Eigen::Vector3d vec;
697+
698+
if (settings_.get<ApplyOffset>().has()) {
699+
auto vs = settings_.get<ApplyOffset>().get();
700+
if (vs.size() != 3) {
701+
throw std::runtime_error("ApplyOffset setting must be a vector of size 3");
702+
}
703+
vec = Eigen::Vector3d(vs[0], vs[1], vs[2]);
704+
} else {
705+
// Collect all norms and vectors
706+
std::vector<double> norms;
707+
std::vector<Eigen::Vector3d> vectors;
708+
for (const auto& task : tasks_) {
709+
auto result = collect_offset(task.item, task.products);
710+
norms.push_back(result.first);
711+
vectors.push_back(result.second);
712+
}
713+
714+
// Find the median norm index
715+
std::vector<double> sorted_norms = norms;
716+
std::nth_element(sorted_norms.begin(), sorted_norms.begin() + sorted_norms.size() / 2, sorted_norms.end());
717+
double median = sorted_norms[sorted_norms.size() / 2];
718+
auto median_it = std::find(norms.begin(), norms.end(), median);
719+
size_t median_index = std::distance(norms.begin(), median_it);
720+
721+
if (median_index >= vectors.size()) {
722+
return nullptr;
723+
}
724+
725+
vec = -vectors[median_index];
726+
}
727+
728+
Eigen::Matrix4d translation_matrix = Eigen::Matrix4d::Identity();
729+
translation_matrix.block<3, 1>(0, 3) = vec;
730+
731+
auto remove_offset = [&](const item::ptr& itm, const std::vector<std::pair<IfcUtil::IfcBaseEntity*, matrix4::ptr>>& pr) -> bool {
732+
std::function<bool(const item::ptr&, Eigen::Matrix4d)> traverse;
733+
traverse = [&](const item::ptr& node, Eigen::Matrix4d m4) -> bool {
734+
if (auto shl = std::dynamic_pointer_cast<shell>(node)) {
735+
auto p = shl->centroid();
736+
Eigen::Vector4d v;
737+
v << p->components()(0), p->components()(1), p->components()(2), 1.0;
738+
Eigen::Vector3d translation_part = (m4 * v).head<3>();
739+
double translation_amnt = translation_part.norm();
740+
if (translation_amnt > settings_.get<MaxOffset>().get()) {
741+
shl->matrix = make<matrix4>(translation_matrix);
742+
}
743+
return true;
744+
} else {
745+
auto m4b = m4;
746+
if (auto gi = std::dynamic_pointer_cast<geom_item>(node)) {
747+
if (gi->matrix) {
748+
m4b = m4 * gi->matrix->ccomponents();
749+
}
750+
Eigen::Vector3d translation_part = m4b.block<3, 1>(0, 3);
751+
double translation_amnt = translation_part.norm();
752+
if (translation_amnt > settings_.get<MaxOffset>().get()) {
753+
auto inverted_rot_scale3 = m4.block<3, 3>(0, 0).inverse();
754+
Eigen::Matrix4d inverted_rot_scale = Eigen::Matrix4d::Identity();
755+
inverted_rot_scale.block<3, 3>(0, 0) = inverted_rot_scale3;
756+
if (!gi->matrix) {
757+
gi->matrix = make<matrix4>();
758+
}
759+
gi->matrix->components() = (inverted_rot_scale * translation_matrix) * gi->matrix->ccomponents();
760+
return true;
761+
}
762+
}
763+
bool b = true;
764+
if (auto col = std::dynamic_pointer_cast<collection>(node)) {
765+
for (const auto& child : col->children) {
766+
if (!traverse(child, m4b)) {
767+
b = false;
768+
}
769+
}
770+
}
771+
return b;
772+
}
773+
};
774+
775+
Eigen::Matrix4d m4 = Eigen::Matrix4d::Identity();
776+
if (pr.size() == 1 && pr[0].second) {
777+
m4 = pr[0].second->ccomponents();
778+
}
779+
return traverse(itm, m4);
780+
};
781+
782+
size_t num_offset_applied = 0;
783+
for (auto& task : tasks_) {
784+
bool all_applied = true;
785+
for (auto& p : task.products) {
786+
auto bb = p.second->components().block<3, 1>(0, 3);
787+
double translation_amnt = bb.norm();
788+
if (translation_amnt > settings_.get<MaxOffset>().get()) {
789+
// block has an underlying mutable ref to the matrix
790+
bb -= vec;
791+
} else {
792+
all_applied = false;
793+
}
794+
}
795+
if (all_applied) {
796+
num_offset_applied += 1;
797+
continue;
798+
}
799+
if (remove_offset(task.item, task.products)) {
800+
num_offset_applied += 1;
801+
}
802+
}
803+
804+
Logger::Notice("Removed large offsets within " + std::to_string(num_offset_applied) + " products");
805+
Logger::Notice("Offset applied (" + std::to_string(vec(0)) + "," + std::to_string(vec(1)) + "," + std::to_string(vec(2)) + ")");
806+
807+
return make<direction3>(vec);
808+
}
809+
628810
IfcGeom::Iterator::~Iterator() {
629811
if (num_threads_ != 1) {
630812
terminating_ = true;

src/ifcgeom/Iterator.h

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ namespace IfcGeom {
9191

9292
// For NoParallelMapping==true
9393
ifcopenshell::geometry::taxonomy::ptr item;
94-
std::vector<std::pair<const IfcUtil::IfcBaseEntity*, ifcopenshell::geometry::taxonomy::matrix4::ptr>> products;
94+
std::vector<std::pair<IfcUtil::IfcBaseEntity*, ifcopenshell::geometry::taxonomy::matrix4::ptr>> products;
9595

9696
// For NoParallelMapping==false
9797
IfcUtil::IfcBaseEntity* representation;
@@ -209,7 +209,7 @@ namespace IfcGeom {
209209

210210
void log_timepoints() const;
211211

212-
/// @todo public/private sections all over the place: move all public to the beginning of the class
212+
ifcopenshell::geometry::taxonomy::direction3::ptr remove_offset_();
213213
public:
214214
Iterator(const std::string& geometry_library, const ifcopenshell::geometry::Settings& settings, IfcParse::IfcFile* file, const std::vector<IfcGeom::filter_t>& filters, int num_threads)
215215
: settings_(settings)
@@ -262,6 +262,31 @@ namespace IfcGeom {
262262

263263
void set_cache(GeometrySerializer* cache) { cache_ = cache; }
264264

265+
std::vector<ifcopenshell::geometry::taxonomy::item::ptr> get_task_items() const {
266+
std::vector<ifcopenshell::geometry::taxonomy::item::ptr> items;
267+
items.reserve(tasks_.size());
268+
for (const auto& task : tasks_) {
269+
items.push_back(task.item);
270+
}
271+
return items;
272+
}
273+
274+
aggregate_of_aggregate_of_instance::ptr get_task_products() const {
275+
aggregate_of_aggregate_of_instance::ptr products = aggregate_of_aggregate_of_instance::ptr(new aggregate_of_aggregate_of_instance);
276+
for (const auto& task : tasks_) {
277+
if (task.products_2) {
278+
products->push(task.products_2);
279+
} else {
280+
for (auto& product : task.products) {
281+
aggregate_of_instance::ptr p(new aggregate_of_instance);
282+
p->push(product.first);
283+
products->push(p);
284+
}
285+
}
286+
}
287+
return products;
288+
}
289+
265290
const std::string& unit_name() const { return converter_->mapping()->get_length_unit_name(); }
266291
double unit_magnitude() const { return converter_->mapping()->get_length_unit(); }
267292
// Check if error occurred during iterator initialization or iteration over elements.

src/ifcgeom/taxonomy.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -952,6 +952,21 @@ typedef item const* ptr;
952952
auto v = std::make_tuple(static_cast<size_t>(LOOP), hash_elements(), external ? *external ? 2 : 1 : 0, closed ? *closed ? 2 : 1 : 0);
953953
return boost::hash<decltype(v)>{}(v);
954954
}
955+
956+
// nb only takes into account explicit points
957+
taxonomy::point3::ptr centroid() const {
958+
Eigen::Vector3d c(0, 0, 0);
959+
for (auto& e : children) {
960+
if (e->start.which() == 1) {
961+
c += boost::get<point3::ptr>(e->start)->ccomponents();
962+
}
963+
if (e->end.which() == 1) {
964+
c += boost::get<point3::ptr>(e->end)->ccomponents();
965+
}
966+
}
967+
c /= static_cast<double>(children.size());
968+
return make<taxonomy::point3>(c);
969+
}
955970
};
956971

957972
struct face : public collection_base<loop> {
@@ -992,6 +1007,25 @@ typedef item const* ptr;
9921007
auto v = std::make_tuple(static_cast<size_t>(SHELL), hash_elements(), closed ? *closed ? 2 : 1 : 0);
9931008
return boost::hash<decltype(v)>{}(v);
9941009
}
1010+
1011+
// nb only takes into account explicit points
1012+
taxonomy::point3::ptr centroid() const {
1013+
Eigen::Vector3d c(0, 0, 0);
1014+
for (auto& f : children) {
1015+
for (auto& l : f->children) {
1016+
for (auto& e : l->children) {
1017+
if (e->start.which() == 1) {
1018+
c += boost::get<point3::ptr>(e->start)->ccomponents();
1019+
}
1020+
if (e->end.which() == 1) {
1021+
c += boost::get<point3::ptr>(e->end)->ccomponents();
1022+
}
1023+
}
1024+
}
1025+
}
1026+
c /= static_cast<double>(children.size());
1027+
return make<taxonomy::point3>(c);
1028+
}
9951029
};
9961030

9971031
struct solid : public collection_base<shell> {

0 commit comments

Comments
 (0)