Reintroduce --model-offset/-rotation MODEL_OFFSET/_ROTATION setting #5231

This commit is contained in:
Thomas Krijnen
2024-09-13 11:37:05 +02:00
parent 81399ffd19
commit d879bf9f89
9 changed files with 87 additions and 206 deletions
-4
View File
@@ -297,10 +297,6 @@ int main(int argc, char** argv) {
"Can take several minutes on large models.") "Can take several minutes on large models.")
("center-model-geometry", ("center-model-geometry",
"Centers the elements by applying the center point of all mesh vertices as an offset.") "Centers the elements by applying the center point of all mesh vertices as an offset.")
("model-offset", po::value<std::string>(&offset_str),
"Applies an arbitrary offset of form 'x;y;z' to all placements.")
("model-rotation", po::value<std::string>(&rotation_str),
"Applies an arbitrary quaternion rotation of form 'x;y;z;w' to all placements.")
("include", po::value<inclusion_filter>(&include_filter)->multitoken(), ("include", po::value<inclusion_filter>(&include_filter)->multitoken(),
"Specifies that the instances that match a specific filtering criteria are to be included in the geometrical output:\n" "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 " "1) 'entities': the following list of types should be included. SVG output defaults "
+14 -17
View File
@@ -1,37 +1,34 @@
#include "ConversionSettings.h" #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 <typename T> template <typename T>
void istream_helper(std::istream& in, std::set<T>& ints) { void istream_helper(std::istream& in, T& vs) {
std::string tokens; std::string tokens;
in >> tokens; in >> tokens;
std::vector<std::string> strs; std::vector<std::string> strs;
boost::split(strs, tokens, boost::is_any_of(",")); boost::split(strs, tokens, boost::is_any_of(","));
for (auto& s : strs) { for (auto& s : strs) {
if constexpr (std::is_same_v<T, std::string>) { if constexpr (std::is_same_v<std::decay_t<T>, std::set<std::string>>) {
ints.insert(s); vs.insert(s);
} else { } else if constexpr (std::is_same_v<std::decay_t<T>, std::set<int>>) {
ints.insert(boost::lexical_cast<T>(s)); vs.insert(boost::lexical_cast<typename T::value_type>(s));
} else if constexpr (std::is_same_v<std::decay_t<T>, std::vector<double>>) {
vs.push_back(boost::lexical_cast<typename T::value_type>(s));
} }
} }
} }
std::istream& std::operator>>(istream& in, set<int>& ints) { std::istream& std::operator>>(istream& in, set<int>& ints) {
istream_helper<int>(in, ints); istream_helper<std::set<int>>(in, ints);
return in; return in;
} }
std::istream& std::operator>>(istream& in, set<string>& strs) { std::istream& std::operator>>(istream& in, set<string>& strs) {
istream_helper<std::string>(in, strs); istream_helper<std::set<std::string>>(in, strs);
return in;
}
std::istream& std::operator>>(istream& in, vector<double>& ds) {
istream_helper<std::vector<double>>(in, ds);
return in; return in;
} }
+35 -11
View File
@@ -24,6 +24,7 @@ namespace po = boost::program_options;
namespace std { namespace std {
istream& operator>>(istream& in, set<int>& ints); istream& operator>>(istream& in, set<int>& ints);
istream& operator>>(istream& in, set<string>& ints); istream& operator>>(istream& in, set<string>& ints);
istream& operator>>(istream& in, vector<double>& vs);
} }
#endif #endif
@@ -43,7 +44,10 @@ namespace ifcopenshell {
struct SettingBase { struct SettingBase {
typedef T base_type; typedef T base_type;
boost::optional<T> value; // boost program options does not seem to handle optional<vector> types, so in case
// of vector settings we need to strip away the optional and detect argument presence
// with !vector::empty()
std::conditional_t<std::is_same_v<T, std::vector<double>>, T, boost::optional<T>> value;
SettingBase() {} SettingBase() {}
@@ -59,24 +63,34 @@ namespace ifcopenshell {
// @todo bool_switch doesn't work with optional unfortunately... // @todo bool_switch doesn't work with optional unfortunately...
value.emplace(); value.emplace();
desc.add_options()(Derived::name, apply_default(po::bool_switch(&*value)), Derived::description); desc.add_options()(Derived::name, apply_default(po::bool_switch(&*value)), Derived::description);
} else if constexpr (std::is_same_v<T, std::vector<double>>) {
desc.add_options()(Derived::name, apply_default(po::value(&value)->multitoken()), Derived::description);
} else { } else {
desc.add_options()(Derived::name, apply_default(po::value(&value)), Derived::description); desc.add_options()(Derived::name, apply_default(po::value(&value)), Derived::description);
} }
} }
T get() const { T get() const {
if (value) { if constexpr (std::is_same_v<T, std::vector<double>>) {
return value.get(); return value;
} else {
if (value) {
return value.get();
}
if constexpr (HasDefault<Derived>()) {
return Derived::defaultvalue;
}
throw std::runtime_error("Setting not set");
} }
if constexpr (HasDefault<Derived>()) {
return Derived::defaultvalue;
}
throw std::runtime_error("Setting not set");
} }
bool has() const { bool has() const {
// @todo this is not reliable, better use vmap[...].defaulted() if constexpr (std::is_same_v<T, std::vector<double>>) {
return !!value; 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 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 static constexpr double defaultvalue = 0.5; // ceiling of this value is used when PiecewiseStepMethod is MinSteps
}; };
struct ModelOffset : public SettingBase<ModelOffset, std::vector<double>> {
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<ModelRotation, std::vector<double>> {
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 <typename settings_t> template <typename settings_t>
class IFC_GEOM_API SettingsContainer { class IFC_GEOM_API SettingsContainer {
public: public:
typedef boost::variant<bool, int, double, std::string, std::set<int>, std::set<std::string>, IteratorOutputOptions, PiecewiseStepMethod, OutputDimensionalityTypes> value_variant_t; typedef boost::variant<bool, int, double, std::string, std::set<int>, std::set<std::string>, std::vector<double>, IteratorOutputOptions, PiecewiseStepMethod, OutputDimensionalityTypes> value_variant_t;
private: private:
settings_t settings; settings_t settings;
@@ -446,7 +470,7 @@ namespace ifcopenshell {
}; };
class IFC_GEOM_API Settings : public SettingsContainer< class IFC_GEOM_API Settings : public SettingsContainer<
std::tuple<MesherLinearDeflection, MesherAngularDeflection, ReorientShells, LengthUnit, PlaneUnit, Precision, OutputDimensionality, LayersetFirst, DisableBooleanResult, NoWireIntersectionCheck, NoWireIntersectionTolerance, PrecisionFactor, DebugBooleanOperations, BooleanAttempt2d, WeldVertices, UseWorldCoords, UseMaterialNames, ConvertBackUnits, ContextIds, ContextTypes, ContextIdentifiers, IteratorOutput, DisableOpeningSubtractions, ApplyDefaultMaterials, DontEmitNormals, GenerateUvs, ApplyLayerSets, UseElementHierarchy, ValidateQuantities, EdgeArrows, BuildingLocalPlacement, SiteLocalPlacement, ForceSpaceTransparency, CircleSegments, KeepBoundingBoxes, PiecewiseStepType, PiecewiseStepParam, NoParallelMapping> std::tuple<MesherLinearDeflection, MesherAngularDeflection, ReorientShells, LengthUnit, PlaneUnit, Precision, OutputDimensionality, LayersetFirst, DisableBooleanResult, NoWireIntersectionCheck, NoWireIntersectionTolerance, PrecisionFactor, DebugBooleanOperations, BooleanAttempt2d, WeldVertices, UseWorldCoords, UseMaterialNames, ConvertBackUnits, ContextIds, ContextTypes, ContextIdentifiers, IteratorOutput, DisableOpeningSubtractions, ApplyDefaultMaterials, DontEmitNormals, GenerateUvs, ApplyLayerSets, UseElementHierarchy, ValidateQuantities, EdgeArrows, BuildingLocalPlacement, SiteLocalPlacement, ForceSpaceTransparency, CircleSegments, KeepBoundingBoxes, PiecewiseStepType, PiecewiseStepParam, NoParallelMapping, ModelOffset, ModelRotation>
> >
{}; {};
} }
-169
View File
@@ -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 <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
/*
#include "IfcGeom.h"
#include "../ifcgeom_schema_agnostic/IfcGeomShapeType.h"
#include "../ifcgeom_schema_agnostic/wire_utils.h"
#include <BRepCheck.hxx>
#include <BRepCheck_Analyzer.hxx>
#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<const IfcGeom::SurfaceStyle> style;
if (l->as<IfcSchema::IfcRepresentationItem>()) {
style = get_style(l->as<IfcSchema::IfcRepresentationItem>());
}
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<int,TopoDS_Shape>::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<void(const TopoDS_Shape&)> 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;
}
*/
+9 -5
View File
@@ -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) { if (!parent_placement_ignored && relative_to) {
result = taxonomy::make<taxonomy::matrix4>( result = taxonomy::make<taxonomy::matrix4>(
// @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<taxonomy::matrix4>(map(relative_to))->ccomponents() * taxonomy::cast<taxonomy::matrix4>(map(relative_to))->ccomponents() *
taxonomy::cast<taxonomy::matrix4>(map(transform))->ccomponents() taxonomy::cast<taxonomy::matrix4>(map(transform))->ccomponents()
); );
} else { } else {
// The parent placement of the current is a placement for a type that is // 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. // being ignored (Site or Building) or it is the host element of an opening.
result = map(transform); result = taxonomy::cast<taxonomy::matrix4>(map(transform));
} }
if (fallback) { 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 return result;
// m4->components() = offset_and_rotation_ * m4->components();
} }
/* /*
+22
View File
@@ -807,6 +807,28 @@ void mapping::initialize_units_() {
if (settings_.get<settings::SiteLocalPlacement>().get()) { if (settings_.get<settings::SiteLocalPlacement>().get()) {
placement_rel_to_type_ = file_->schema()->declaration_by_name("IfcSite"); placement_rel_to_type_ = file_->schema()->declaration_by_name("IfcSite");
} }
// Translation is applied first, then rotation.
if (settings_.get<ModelOffset>().has()) {
auto vs = settings_.get<ModelOffset>().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<ModelRotation>().has()) {
auto vs = settings_.get<ModelRotation>().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() { void mapping::initialize_settings() {
+2
View File
@@ -31,6 +31,8 @@ namespace geometry {
const IfcParse::declaration* placement_rel_to_type_; const IfcParse::declaration* placement_rel_to_type_;
const IfcUtil::IfcBaseEntity* placement_rel_to_instance_; const IfcUtil::IfcBaseEntity* placement_rel_to_instance_;
Eigen::Matrix4d offset_and_rotation_ = Eigen::Matrix4d::Identity();
void initialize_units_(); void initialize_units_();
void addRepresentationsFromContextIds(IfcSchema::IfcRepresentation::list::ptr&); void addRepresentationsFromContextIds(IfcSchema::IfcRepresentation::list::ptr&);
void addRepresentationsFromDefaultContexts(IfcSchema::IfcRepresentation::list::ptr&); void addRepresentationsFromDefaultContexts(IfcSchema::IfcRepresentation::list::ptr&);
+3
View File
@@ -375,6 +375,9 @@ assign_matrix_access(revolve);
void set_(const std::string& name, const std::set<std::string>& val) { void set_(const std::string& name, const std::set<std::string>& val) {
return $self->set(name, val); return $self->set(name, val);
} }
void set_(const std::string& name, const std::vector<double>& val) {
return $self->set(name, val);
}
ifcopenshell::geometry::Settings::value_variant_t get_(const std::string& name) { ifcopenshell::geometry::Settings::value_variant_t get_(const std::string& name) {
return $self->get(name); return $self->get(name);
} }
+2
View File
@@ -228,6 +228,8 @@
} else if constexpr (std::is_same_v<std::remove_cv_t<std::remove_reference_t<T>>, std::set<std::string>>) { } else if constexpr (std::is_same_v<std::remove_cv_t<std::remove_reference_t<T>>, std::set<std::string>>) {
std::vector<std::string> vs(t.begin(), t.end()); std::vector<std::string> vs(t.begin(), t.end());
return pythonize_vector(vs); return pythonize_vector(vs);
} else if constexpr (std::is_same_v<std::remove_cv_t<std::remove_reference_t<T>>, std::vector<double>>) {
return pythonize_vector(t);
} else { } else {
return pythonize(t); return pythonize(t);
} }