From d879bf9f8968ab7339016b4ec19114dc6c2aee05 Mon Sep 17 00:00:00 2001 From: Thomas Krijnen Date: Fri, 13 Sep 2024 11:37:05 +0200 Subject: [PATCH] Reintroduce --model-offset/-rotation MODEL_OFFSET/_ROTATION setting #5231 --- src/ifcconvert/IfcConvert.cpp | 4 - src/ifcgeom/ConversionSettings.cpp | 31 ++-- src/ifcgeom/ConversionSettings.h | 46 ++++-- src/ifcgeom/mapping.cpp | 169 --------------------- src/ifcgeom/mapping/IfcObjectPlacement.cpp | 14 +- src/ifcgeom/mapping/mapping.cpp | 22 +++ src/ifcgeom/mapping/mapping.h | 2 + src/ifcwrap/IfcGeomWrapper.i | 3 + src/ifcwrap/utils/type_conversion.i | 2 + 9 files changed, 87 insertions(+), 206 deletions(-) delete mode 100644 src/ifcgeom/mapping.cpp diff --git a/src/ifcconvert/IfcConvert.cpp b/src/ifcconvert/IfcConvert.cpp index d1ce1dceeb..fac54d6025 100644 --- a/src/ifcconvert/IfcConvert.cpp +++ b/src/ifcconvert/IfcConvert.cpp @@ -297,10 +297,6 @@ int main(int argc, char** argv) { "Can take several minutes on large models.") ("center-model-geometry", "Centers the elements by applying the center point of all mesh vertices as an offset.") - ("model-offset", po::value(&offset_str), - "Applies an arbitrary offset of form 'x;y;z' to all placements.") - ("model-rotation", po::value(&rotation_str), - "Applies an arbitrary quaternion rotation of form 'x;y;z;w' to all placements.") ("include", po::value(&include_filter)->multitoken(), "Specifies that the instances that match a specific filtering criteria are to be included in the geometrical output:\n" "1) 'entities': the following list of types should be included. SVG output defaults " diff --git a/src/ifcgeom/ConversionSettings.cpp b/src/ifcgeom/ConversionSettings.cpp index 3841191f80..0af9262474 100644 --- a/src/ifcgeom/ConversionSettings.cpp +++ b/src/ifcgeom/ConversionSettings.cpp @@ -1,37 +1,34 @@ #include "ConversionSettings.h" -/* -void ifcopenshell::geometry::ConversionSettings::setValue(GeomValue var, double value) { - values_[var] = value; -} - -double ifcopenshell::geometry::ConversionSettings::getValue(GeomValue var) const { - return values_[var]; -} -*/ - template -void istream_helper(std::istream& in, std::set& ints) { +void istream_helper(std::istream& in, T& vs) { std::string tokens; in >> tokens; std::vector strs; boost::split(strs, tokens, boost::is_any_of(",")); for (auto& s : strs) { - if constexpr (std::is_same_v) { - ints.insert(s); - } else { - ints.insert(boost::lexical_cast(s)); + if constexpr (std::is_same_v, std::set>) { + vs.insert(s); + } else if constexpr (std::is_same_v, std::set>) { + vs.insert(boost::lexical_cast(s)); + } else if constexpr (std::is_same_v, std::vector>) { + vs.push_back(boost::lexical_cast(s)); } } } std::istream& std::operator>>(istream& in, set& ints) { - istream_helper(in, ints); + istream_helper>(in, ints); return in; } std::istream& std::operator>>(istream& in, set& strs) { - istream_helper(in, strs); + istream_helper>(in, strs); + return in; +} + +std::istream& std::operator>>(istream& in, vector& ds) { + istream_helper>(in, ds); return in; } diff --git a/src/ifcgeom/ConversionSettings.h b/src/ifcgeom/ConversionSettings.h index 544c04ffe3..b1f6fe18b4 100644 --- a/src/ifcgeom/ConversionSettings.h +++ b/src/ifcgeom/ConversionSettings.h @@ -24,6 +24,7 @@ namespace po = boost::program_options; namespace std { istream& operator>>(istream& in, set& ints); istream& operator>>(istream& in, set& ints); + istream& operator>>(istream& in, vector& vs); } #endif @@ -43,7 +44,10 @@ namespace ifcopenshell { struct SettingBase { typedef T base_type; - boost::optional value; + // boost program options does not seem to handle optional types, so in case + // of vector settings we need to strip away the optional and detect argument presence + // with !vector::empty() + std::conditional_t>, T, boost::optional> value; SettingBase() {} @@ -59,24 +63,34 @@ namespace ifcopenshell { // @todo bool_switch doesn't work with optional unfortunately... value.emplace(); desc.add_options()(Derived::name, apply_default(po::bool_switch(&*value)), Derived::description); + } else if constexpr (std::is_same_v>) { + desc.add_options()(Derived::name, apply_default(po::value(&value)->multitoken()), Derived::description); } else { desc.add_options()(Derived::name, apply_default(po::value(&value)), Derived::description); } } T get() const { - if (value) { - return value.get(); + if constexpr (std::is_same_v>) { + return value; + } else { + if (value) { + return value.get(); + } + if constexpr (HasDefault()) { + return Derived::defaultvalue; + } + throw std::runtime_error("Setting not set"); } - if constexpr (HasDefault()) { - return Derived::defaultvalue; - } - throw std::runtime_error("Setting not set"); } bool has() const { - // @todo this is not reliable, better use vmap[...].defaulted() - return !!value; + if constexpr (std::is_same_v>) { + return !value.empty(); + } else { + // @todo this is not reliable, better use vmap[...].defaulted() + return !!value; + } } }; @@ -354,12 +368,22 @@ namespace ifcopenshell { static constexpr const char* const description = "Indicates the parameter value for defining step size when evaluating piecewise curves."; static constexpr double defaultvalue = 0.5; // ceiling of this value is used when PiecewiseStepMethod is MinSteps }; + + struct ModelOffset : public SettingBase> { + static constexpr const char* const name = "model-offset"; + static constexpr const char* const description = "Applies an arbitrary offset of form 'x,y,z' to all placements."; + }; + + struct ModelRotation : public SettingBase> { + static constexpr const char* const name = "model-rotation"; + static constexpr const char* const description = "Applies an arbitrary quaternion rotation of form 'x,y,z,w' to all placements."; + }; } template class IFC_GEOM_API SettingsContainer { public: - typedef boost::variant, std::set, IteratorOutputOptions, PiecewiseStepMethod, OutputDimensionalityTypes> value_variant_t; + typedef boost::variant, std::set, std::vector, IteratorOutputOptions, PiecewiseStepMethod, OutputDimensionalityTypes> value_variant_t; private: settings_t settings; @@ -446,7 +470,7 @@ namespace ifcopenshell { }; class IFC_GEOM_API Settings : public SettingsContainer< - std::tuple + std::tuple > {}; } diff --git a/src/ifcgeom/mapping.cpp b/src/ifcgeom/mapping.cpp deleted file mode 100644 index c472e8dd1e..0000000000 --- a/src/ifcgeom/mapping.cpp +++ /dev/null @@ -1,169 +0,0 @@ -/******************************************************************************** -* * -* This file is part of IfcOpenShell. * -* * -* IfcOpenShell is free software: you can redistribute it and/or modify * -* it under the terms of the Lesser GNU General Public License as published by * -* the Free Software Foundation, either version 3.0 of the License, or * -* (at your option) any later version. * -* * -* IfcOpenShell is distributed in the hope that it will be useful, * -* but WITHOUT ANY WARRANTY; without even the implied warranty of * -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * -* Lesser GNU General Public License for more details. * -* * -* You should have received a copy of the Lesser GNU General Public License * -* along with this program. If not, see . * -* * -********************************************************************************/ - -/* -#include "IfcGeom.h" -#include "../ifcgeom_schema_agnostic/IfcGeomShapeType.h" -#include "../ifcgeom_schema_agnostic/wire_utils.h" - -#include -#include - -#define Kernel POSTFIX_SCHEMA(Kernel) - -using namespace IfcUtil; - -bool IfcGeom::Kernel::convert_shapes(const IfcBaseInterface* l, ConversionResults& r) { - if (shape_type(l) != ST_SHAPELIST) { - TopoDS_Shape shp; - if (convert_shape(l, shp)) { - std::shared_ptr style; - if (l->as()) { - style = get_style(l->as()); - } - r.push_back(IfcGeom::ConversionResult(l->data().id(), shp, style)); - return true; - } - return false; - } - -#include "mapping_shapes.i" - Logger::Message(Logger::LOG_ERROR,"No operation defined for:",l); - return false; -} - -IfcGeom::ShapeType IfcGeom::Kernel::shape_type(const IfcBaseInterface* l) { -#include "mapping_shape_type.i" - return ST_OTHER; -} - -bool IfcGeom::Kernel::convert_shape(const IfcBaseInterface* l, TopoDS_Shape& r) { - const unsigned int id = l->data().id(); - bool success = false; - bool processed = false; - bool ignored = false; - -#ifndef NO_CACHE - std::map::const_iterator it = cache.Shape.find(id); - if ( it != cache.Shape.end() ) { r = it->second; return true; } -#endif - const bool include_curves = getValue(GV_DIMENSIONALITY) != +1; - const bool include_solids_and_surfaces = getValue(GV_DIMENSIONALITY) != -1; - - IfcGeom::ShapeType st = shape_type(l); - ignored = (!include_solids_and_surfaces && (st == ST_SHAPE || st == ST_FACE)) || (!include_curves && (st == ST_WIRE || st == ST_CURVE)); - if (st == ST_SHAPELIST) { - processed = true; - ConversionResults items; - success = convert_shapes(l, items) && util::flatten_shape_list(items, r, false, getValue(GV_PRECISION)); - } else if (st == ST_SHAPE && include_solids_and_surfaces) { -#include "mapping_shape.i" - } else if (st == ST_FACE && include_solids_and_surfaces) { - processed = true; - success = convert_face(l, r); - } else if (st == ST_WIRE && include_curves) { - processed = true; - TopoDS_Wire w; - success = convert_wire(l, w); - if (success) { - r = w; - } - } else if (st == ST_CURVE && include_curves) { - processed = true; - Handle(Geom_Curve) crv; - TopoDS_Wire w; - success = convert_curve(l, crv) && util::convert_curve_to_wire(crv, w); - if (success) { - r = w; - } - } - - if ( processed && success ) { -#ifndef NO_CACHE - cache.Shape[id] = r; -#endif - - if (Logger::LOG_DEBUG >= Logger::Verbosity()) { - std::stringstream ss; - - BRepCheck_Analyzer ana(r); - - std::function traverse_subshapes; - - traverse_subshapes = [&traverse_subshapes, &ana, &ss](const TopoDS_Shape& shape) { - if (shape.IsNull()) - return; - - TopoDS_Iterator it(shape); - for (; it.More(); it.Next()) { - const TopoDS_Shape& subs = it.Value(); - - auto rs = ana.Result(subs); - if (rs) { - for (auto& msg : rs->Status()) { - if (msg != BRepCheck_NoError) { - ss << " "; - std::stringstream sst; - BRepCheck::Print(msg, sst); - auto sss = sst.str(); - // remove trailing newline added by Print() - ss << sss.substr(0, sss.size() - 1); - } - } - } - - traverse_subshapes(subs); - } - }; - - traverse_subshapes(r); - - Logger::Notice((ana.IsValid() ? "Valid shape" : "Invalid shape with:") + ss.str(), l); - } - } else if (!ignored) { - const char* const msg = processed - ? "Failed to convert:" - : "No operation defined for:"; - Logger::Message(Logger::LOG_ERROR, msg, l); - } - return success; -} - -bool IfcGeom::Kernel::convert_wire(const IfcBaseInterface* l, TopoDS_Wire& r) { -#include "mapping_wire.i" - Handle(Geom_Curve) curve; - if (IfcGeom::Kernel::convert_curve(l, curve)) { - return util::convert_curve_to_wire(curve, r); - } - Logger::Message(Logger::LOG_ERROR,"No operation defined for:",l); - return false; -} - -bool IfcGeom::Kernel::convert_face(const IfcBaseInterface* l, TopoDS_Shape& r) { -#include "mapping_face.i" - Logger::Message(Logger::LOG_ERROR,"No operation defined for:",l); - return false; -} - -bool IfcGeom::Kernel::convert_curve(const IfcBaseInterface* l, Handle(Geom_Curve)& r) { -#include "mapping_curve.i" - Logger::Message(Logger::LOG_ERROR,"No operation defined for:",l); - return false; -} -*/ \ No newline at end of file diff --git a/src/ifcgeom/mapping/IfcObjectPlacement.cpp b/src/ifcgeom/mapping/IfcObjectPlacement.cpp index f041c43145..3faf046335 100644 --- a/src/ifcgeom/mapping/IfcObjectPlacement.cpp +++ b/src/ifcgeom/mapping/IfcObjectPlacement.cpp @@ -65,16 +65,21 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcObjectPlacement* inst) { } } - taxonomy::ptr result; + taxonomy::matrix4::ptr result; if (!parent_placement_ignored && relative_to) { result = taxonomy::make( + // @nb this is a bit silly, in 0.7 we didn't have a recursive function + // but a while loop to apply the hierarchical placements, so after the + // loop we could apply the global offset. Since we have a recursive + // function now we need to undo the global offset when recursing. + offset_and_rotation_.inverse() * taxonomy::cast(map(relative_to))->ccomponents() * taxonomy::cast(map(transform))->ccomponents() ); } else { // The parent placement of the current is a placement for a type that is // being ignored (Site or Building) or it is the host element of an opening. - result = map(transform); + result = taxonomy::cast(map(transform)); } if (fallback) { @@ -84,10 +89,9 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcObjectPlacement* inst) { } } - return result; + result->components() = offset_and_rotation_ * result->ccomponents(); - // @todo - // m4->components() = offset_and_rotation_ * m4->components(); + return result; } /* diff --git a/src/ifcgeom/mapping/mapping.cpp b/src/ifcgeom/mapping/mapping.cpp index b709c1696b..2f4d977557 100644 --- a/src/ifcgeom/mapping/mapping.cpp +++ b/src/ifcgeom/mapping/mapping.cpp @@ -807,6 +807,28 @@ void mapping::initialize_units_() { if (settings_.get().get()) { placement_rel_to_type_ = file_->schema()->declaration_by_name("IfcSite"); } + + // Translation is applied first, then rotation. + if (settings_.get().has()) { + auto vs = settings_.get().get(); + if (vs.size() == 3) { + offset_and_rotation_ *= Eigen::Affine3d(Eigen::Translation3d(vs[0], vs[1], vs[2])).matrix(); + } else { + Logger::Error("Expected 3 values for model-offset setting"); + } + } + + if (settings_.get().has()) { + auto vs = settings_.get().get(); + if (vs.size() == 4) { + auto m3 = Eigen::Quaterniond(vs[0], vs[1], vs[2], vs[3]).matrix(); + Eigen::Matrix4d m4 = Eigen::Matrix4d::Identity(); + m4 << m3; + offset_and_rotation_ *= m4; + } else { + Logger::Error("Expected 4 values for model-rotation setting"); + } + } } void mapping::initialize_settings() { diff --git a/src/ifcgeom/mapping/mapping.h b/src/ifcgeom/mapping/mapping.h index 96053a0414..770bc3aa19 100644 --- a/src/ifcgeom/mapping/mapping.h +++ b/src/ifcgeom/mapping/mapping.h @@ -30,6 +30,8 @@ namespace geometry { const IfcParse::declaration* placement_rel_to_type_; const IfcUtil::IfcBaseEntity* placement_rel_to_instance_; + + Eigen::Matrix4d offset_and_rotation_ = Eigen::Matrix4d::Identity(); void initialize_units_(); void addRepresentationsFromContextIds(IfcSchema::IfcRepresentation::list::ptr&); diff --git a/src/ifcwrap/IfcGeomWrapper.i b/src/ifcwrap/IfcGeomWrapper.i index ca0851324f..03adf23494 100644 --- a/src/ifcwrap/IfcGeomWrapper.i +++ b/src/ifcwrap/IfcGeomWrapper.i @@ -375,6 +375,9 @@ assign_matrix_access(revolve); void set_(const std::string& name, const std::set& val) { return $self->set(name, val); } + void set_(const std::string& name, const std::vector& val) { + return $self->set(name, val); + } ifcopenshell::geometry::Settings::value_variant_t get_(const std::string& name) { return $self->get(name); } diff --git a/src/ifcwrap/utils/type_conversion.i b/src/ifcwrap/utils/type_conversion.i index 5c90179b4d..85907f78db 100644 --- a/src/ifcwrap/utils/type_conversion.i +++ b/src/ifcwrap/utils/type_conversion.i @@ -228,6 +228,8 @@ } else if constexpr (std::is_same_v>, std::set>) { std::vector vs(t.begin(), t.end()); return pythonize_vector(vs); + } else if constexpr (std::is_same_v>, std::vector>) { + return pythonize_vector(t); } else { return pythonize(t); }