@@ -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+
628810IfcGeom::Iterator::~Iterator () {
629811 if (num_threads_ != 1 ) {
630812 terminating_ = true ;
0 commit comments