Merge branch 'v0.8.0' into tfk-rocksdb-storage

This commit is contained in:
Thomas Krijnen
2025-03-25 13:05:45 +01:00
735 changed files with 25354 additions and 10696 deletions
+10 -10
View File
@@ -75,12 +75,12 @@ bool is_valid_for_kernel(const ifcopenshell::geometry::kernels::AbstractKernel*
}
class HybridKernel : public ifcopenshell::geometry::kernels::AbstractKernel {
std::vector<AbstractKernel*> kernels_;
std::vector<std::unique_ptr<AbstractKernel>> kernels_;
ifcopenshell::geometry::abstract_mapping* mapping_;
public:
HybridKernel(const std::string& name, IfcParse::IfcFile* file, Settings& settings, std::vector<AbstractKernel*> kernels)
HybridKernel(const std::string& name, IfcParse::IfcFile* file, Settings& settings, std::vector<std::unique_ptr<AbstractKernel>>&& kernels)
: AbstractKernel(name, settings)
, kernels_(kernels)
, kernels_(std::move(kernels))
, mapping_(ifcopenshell::geometry::impl::mapping_implementations().construct(file, settings))
{}
virtual bool convert(const taxonomy::ptr item, IfcGeom::ConversionResults& rs) {
@@ -88,7 +88,7 @@ public:
bool has_openings = ops && ops->size();
for (auto& k : kernels_) {
#ifdef IFOPSH_WITH_CGAL
if (has_openings && dynamic_cast<ifcopenshell::geometry::kernels::SimpleCgalKernel*>(k)) {
if (has_openings && dynamic_cast<ifcopenshell::geometry::kernels::SimpleCgalKernel*>(k.get())) {
// @todo this would fail later on in the find_openings() call, because we have a
// SimpleCgalShape which cannot be used on a kernel that supports booleans.
// @todo 1 implement the translation between various conversion result shapes
@@ -138,7 +138,7 @@ public:
for (auto& k : kernels_) {
bool is_valid = true;
for (auto& s : entity_shapes) {
if (!is_valid_for_kernel(k, s)) {
if (!is_valid_for_kernel(k.get(), s)) {
is_valid = false;
break;
}
@@ -179,7 +179,7 @@ ifcopenshell::geometry::kernels::AbstractKernel* ifcopenshell::geometry::kernels
if (geometry_library_lower.rfind("hybrid-", 0) == 0) {
geometry_library_lower = geometry_library_lower.substr(strlen("hybrid"));
std::vector<AbstractKernel*> kernels;
std::vector<std::unique_ptr<AbstractKernel>> kernels;
while (!geometry_library_lower.empty()) {
if (geometry_library_lower.find("-", 0) == 0) {
geometry_library_lower = geometry_library_lower.substr(strlen("-"));
@@ -189,19 +189,19 @@ ifcopenshell::geometry::kernels::AbstractKernel* ifcopenshell::geometry::kernels
auto n = kernels.size();
#ifdef IFOPSH_WITH_OPENCASCADE
if (geometry_library_lower.find("opencascade", 0) == 0) {
kernels.push_back(new IfcGeom::OpenCascadeKernel(conv_settings));
kernels.emplace_back(new IfcGeom::OpenCascadeKernel(conv_settings));
geometry_library_lower = geometry_library_lower.substr(strlen("opencascade"));
}
#endif
#ifdef IFOPSH_WITH_CGAL
if (geometry_library_lower.find("cgal-simple", 0) == 0) {
kernels.push_back(new SimpleCgalKernel(conv_settings));
kernels.emplace_back(new SimpleCgalKernel(conv_settings));
geometry_library_lower = geometry_library_lower.substr(strlen("cgal-simple"));
}
if (geometry_library_lower.find("cgal", 0) == 0) {
kernels.push_back(new CgalKernel(conv_settings));
kernels.emplace_back(new CgalKernel(conv_settings));
geometry_library_lower = geometry_library_lower.substr(strlen("cgal"));
}
#endif
@@ -215,7 +215,7 @@ ifcopenshell::geometry::kernels::AbstractKernel* ifcopenshell::geometry::kernels
}
if (!kernels.empty()) {
return new HybridKernel(geometry_library, file, conv_settings, kernels);
return new HybridKernel(geometry_library, file, conv_settings, std::move(kernels));
}
}
+106 -10
View File
@@ -47,7 +47,9 @@ namespace ifcopenshell {
// 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;
// tfk: we no longer do this because negative values can not be passed like this as boost confuses them with options
// std::conditional_t<std::is_same_v<T, std::vector<double>>, T, boost::optional<T>> value;
boost::optional<T> value;
SettingBase() {}
@@ -64,14 +66,15 @@ namespace ifcopenshell {
value.emplace();
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);
// these options have to be supplied manually in IfcConvert.cpp
// 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 constexpr (std::is_same_v<T, std::vector<double>>) {
if constexpr (false && std::is_same_v<T, std::vector<double>>) {
return value;
} else {
if (value) {
@@ -85,7 +88,7 @@ namespace ifcopenshell {
}
bool has() const {
if constexpr (std::is_same_v<T, std::vector<double>>) {
if constexpr (false && std::is_same_v<T, std::vector<double>>) {
return !value.empty();
} else {
// @todo this is not reliable, better use vmap[...].defaulted()
@@ -365,6 +368,12 @@ namespace ifcopenshell {
static constexpr bool defaultvalue = false;
};
struct ComputeCurvature : public SettingBase<ComputeCurvature, bool> {
static constexpr const char* const name = "compute-curvature";
static constexpr const char* const description = "Specifies whether function_item_evaluator.evaluate() computes curvature.";
static constexpr bool defaultvalue = false;
};
enum FunctionStepMethod {
MAXSTEPSIZE,
MINSTEPS };
@@ -385,12 +394,12 @@ namespace ifcopenshell {
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.";
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.";
static constexpr const char* const description = "Applies an arbitrary quaternion rotation of form x,y,z,w to all placements.";
};
enum TriangulationMethod {
@@ -407,7 +416,73 @@ namespace ifcopenshell {
static constexpr TriangulationMethod defaultvalue = TRIANGLE_MESH;
};
struct CgalEmitOriginalEdges : public SettingBase<CgalEmitOriginalEdges, bool> {
static constexpr const char* const name = "cgal-original-edges";
static constexpr const char* const description = "Try to emit original edge face boundary edges instead of recomputed ones based on face normal. Falls back to triangulated data in case of boolean operands and faces with holes.";
static constexpr bool defaultvalue = false;
};
}
namespace impl {
template <typename T>
struct readable_name {
static constexpr const char* name = "Unknown Type";
};
template <>
struct readable_name<bool> {
static constexpr const char* name = "bool";
};
template <>
struct readable_name<int> {
static constexpr const char* name = "int";
};
template <>
struct readable_name<double> {
static constexpr const char* name = "double";
};
template <>
struct readable_name<std::string> {
static constexpr const char* name = "std::string";
};
template <>
struct readable_name<std::set<int>> {
static constexpr const char* name = "std::set<int>";
};
template <>
struct readable_name<std::set<std::string>> {
static constexpr const char* name = "std::set<std::string>";
};
template <>
struct readable_name<std::vector<double>> {
static constexpr const char* name = "std::vector<double>";
};
template <>
struct readable_name<IteratorOutputOptions> {
static constexpr const char* name = "IteratorOutputOptions";
};
template <>
struct readable_name<FunctionStepMethod> {
static constexpr const char* name = "FunctionStepMethod";
};
template <>
struct readable_name<OutputDimensionalityTypes> {
static constexpr const char* name = "OutputDimensionalityTypes";
};
template <>
struct readable_name<TriangulationMethod> {
static constexpr const char* name = "TriangulationMethod";
};
}
template <typename settings_t>
@@ -437,17 +512,34 @@ namespace ifcopenshell {
}
}
template <std::size_t Index>
std::string get_type_(const std::string& name) const {
if (std::tuple_element_t<Index, settings_t>::name == name) {
return impl::readable_name<typename std::tuple_element_t<Index, settings_t>::base_type>::name;
}
if constexpr (Index + 1 < std::tuple_size_v<settings_t>) {
return get_type_<Index + 1>(name);
} else {
throw std::runtime_error("Setting not available");
}
}
template <std::size_t Index>
void set_option_(const std::string& name, const value_variant_t& val) {
if (std::tuple_element_t<Index, settings_t>::name == name) {
if constexpr (std::is_enum_v<typename std::tuple_element_t<Index, settings_t>::base_type>) {
if (val.which() == 1) {
auto val_as_enum = (typename std::tuple_element_t<Index, settings_t>::base_type) boost::get<int>(val);
if (auto* val_ptr = boost::get<int>(&val)) {
auto val_as_enum = (typename std::tuple_element_t<Index, settings_t>::base_type) *val_ptr;
std::get<Index>(settings).value = val_as_enum;
return;
}
}
std::get<Index>(settings).value = boost::get<typename std::tuple_element_t<Index, settings_t>::base_type>(val);
try {
std::get<Index>(settings).value = boost::get<typename std::tuple_element_t<Index, settings_t>::base_type>(val);
} catch (const boost::bad_get&) {
std::string ty = impl::readable_name<typename std::tuple_element_t<Index, settings_t>::base_type>::name;
throw std::runtime_error("Expected a value of type <" + ty + "> for setting '" + name + "'");
}
} else if constexpr (Index + 1 < std::tuple_size_v<settings_t>) {
set_option_<Index + 1>(name, val);
} else {
@@ -492,6 +584,10 @@ namespace ifcopenshell {
set_option_<0>(name, val);
}
std::string get_type(const std::string& name) {
return get_type_<0>(name);
}
std::vector<std::string> setting_names() const {
std::vector<std::string> r;
get_setting_names_<0>(r);
@@ -500,7 +596,7 @@ namespace ifcopenshell {
};
class IFC_GEOM_API Settings : public SettingsContainer<
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, FunctionStepType, FunctionStepParam, NoParallelMapping, ModelOffset, ModelRotation, TriangulationType>
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, ModelOffset, ModelRotation, TriangulationType, CgalEmitOriginalEdges>
>
{};
}
+10
View File
@@ -13,6 +13,16 @@ ifcopenshell::geometry::Converter::Converter(const std::string& geometry_library
settings_ = mapping_->settings();
}
ifcopenshell::geometry::Converter::~Converter()
{
if (kernel_ != nullptr) {
delete kernel_;
}
if (mapping_ != nullptr) {
delete mapping_;
}
}
namespace {
void substitute_with_box_based_on_density(IfcGeom::ConversionResults& items, double& density) {
int nv = 0;
+1 -1
View File
@@ -28,7 +28,7 @@ namespace ifcopenshell { namespace geometry {
Converter(const std::string& geometry_library, IfcParse::IfcFile* file, ifcopenshell::geometry::Settings& settings);
~Converter() {}
~Converter();
ifcopenshell::geometry::abstract_mapping* mapping() const { return mapping_; }
+14 -15
View File
@@ -150,10 +150,6 @@ namespace IfcGeom {
int done;
int total;
// @todo these appear uninitialized?
std::string unit_name_;
double unit_magnitude_;
ifcopenshell::geometry::taxonomy::point3 bounds_min_;
ifcopenshell::geometry::taxonomy::point3 bounds_max_;
@@ -166,8 +162,8 @@ namespace IfcGeom {
public:
void set_cache(GeometrySerializer* cache) { cache_ = cache; }
const std::string& unit_name() const { return unit_name_; }
double unit_magnitude() const { return unit_magnitude_; }
const std::string& unit_name() const { return converter_->mapping()->get_length_unit_name(); }
double unit_magnitude() const { return converter_->mapping()->get_length_unit(); }
// Check if error occurred during iterator initialization or iteration over elements.
bool had_error_processing_elements() const { return had_error_processing_elements_; }
@@ -667,6 +663,10 @@ namespace IfcGeom {
/// Use get() to retrieve the created geometry.
const IfcUtil::IfcBaseClass* next() {
using std::chrono::high_resolution_clock;
delete *task_result_iterator_;
delete *native_task_result_iterator_;
if (num_threads_ != 1) {
if (!wait_for_element()) {
Logger::SetProduct(boost::none);
@@ -889,20 +889,19 @@ namespace IfcGeom {
init_future_.wait();
}
}
if (settings_.get<ifcopenshell::geometry::settings::IteratorOutput>().get() != ifcopenshell::geometry::settings::NATIVE) {
for (auto& p : all_processed_native_elements_) {
delete p;
}
}
for (auto& k : kernel_pool) {
delete k;
}
for (auto& p : all_processed_elements_) {
delete p;
if (task_result_ptr_initialized) {
while (task_result_iterator_ != --all_processed_elements_.end()) {
delete *task_result_iterator_++;
delete *native_task_result_iterator_++;
}
}
delete converter_;
}
};
}
+1
View File
@@ -45,6 +45,7 @@ namespace geometry {
virtual const IfcUtil::IfcBaseEntity* get_product_type(const IfcUtil::IfcBaseEntity*) = 0;
virtual const IfcUtil::IfcBaseEntity* get_single_material_association(const IfcUtil::IfcBaseEntity*) = 0;
virtual double get_length_unit() const = 0;
virtual const std::string& get_length_unit_name() const = 0;
virtual IfcUtil::IfcBaseEntity* representation_of(const IfcUtil::IfcBaseEntity* product) = 0;
const Settings& settings() const { return settings_; }
+52 -2
View File
@@ -1,8 +1,25 @@
#include "function_item_evaluator.h"
#include "profile_helper.h"
#include <boost/math/quadrature/trapezoidal.hpp>
using namespace ifcopenshell::geometry;
std::vector<double> ifcopenshell::geometry::helmert_curve_point(double A0, double A1, double A2, double s) {
auto theta = [A0, A1, A2](double t) -> double {
auto a0 = A0 ? t / A0 : 0.0;
auto a1 = A1 ? A1 * std::pow(t, 2) / (2 * fabs(std::pow(A1, 3))) : 0.0;
auto a2 = A2 ? std::pow(t, 3) / (3 * std::pow(A2, 3)) : 0.0;
return a0 + a1 + a2;
};
auto fn_x = [theta](double t) -> double { return cos(theta(t)); };
auto fn_y = [theta](double t) -> double { return sin(theta(t)); };
auto x = boost::math::quadrature::trapezoidal(fn_x, 0.0, s);
auto y = boost::math::quadrature::trapezoidal(fn_y, 0.0, s);
auto angle = theta(x);
return {x, y, angle};
}
struct functor_fn_evaluator : public fn_evaluator {
functor_fn_evaluator(taxonomy::functor_item::const_ptr fn, const ifcopenshell::geometry::Settings& settings) : fn_evaluator(settings),
@@ -89,12 +106,27 @@ struct gradient_fn_evaluator : public fn_evaluator {
auto xy = horizontal_evaluator_.evaluate(u + start_);
auto uz = vertical_evaluator_.evaluate(u);
uz.col(3)(0) = 0.0; // x is distance along. zero it out so it doesn't add to the x from horizontal
// curvature is stored in row 3 - capture it and remove it from the xy and uz matrices
// so the matrix operations (ie multiplication) works correct.y
auto horizontal_curvature = xy.row(3);
xy.row(3) = Eigen::Vector4d(0, 0, 0, 1);
auto vertical_curvature = uz.row(3);
uz.row(3) = Eigen::Vector4d(0, 0, 0, 1);
uz(0, 3) = 0.0; // x is distance along. zero it out so it doesn't add to the x from horizontal
uz.col(1).swap(uz.col(2)); // uz is 2D in distance along - y plane, swap y and z so elevations become z
uz.row(1).swap(uz.row(2));
Eigen::Matrix4d m;
m = xy * uz; // combine horizontal and vertical
// Put curvature back into the solution matrix
// curvature for vertical is in column 0, need it to be in column 1
// so it doesn't add to curvature for horizontal
std::swap(vertical_curvature(3, 0), vertical_curvature(3, 1));
m.row(3) = horizontal_curvature + vertical_curvature;
return m;
}
@@ -121,6 +153,15 @@ struct cant_fn_evaluator : public fn_evaluator {
auto g = gradient_evaluator_.evaluate(u + start_);
auto c = cant_evaluator_.evaluate(u);
// curvature is stored in row 3 - capture it and remove it from the xy and uz matrices
// so the matrix operations (ie multiplication) works correctly
auto gradient_curvature = g.row(3);
g.row(3) = Eigen::Vector4d(0, 0, 0, 1);
auto cant_curvature = c.row(3);
c.row(3) = Eigen::Vector4d(0, 0, 0, 1);
// Need to multiply g and c so the axis vectors
// from cant have the correct rotation applied so
// they are relative to the gradient curve coordinate system
@@ -147,6 +188,11 @@ struct cant_fn_evaluator : public fn_evaluator {
m(1, 3) = y;
m(2, 3) = z + s;
// reinstate values for curvature.
// cant_curvature is cant alone. this needs to be combined with gradient in column 3
gradient_curvature[3] = gradient_curvature[2] + cant_curvature[3];
m.row(3) = gradient_curvature;
return m;
}
@@ -266,5 +312,9 @@ taxonomy::item::ptr function_item_evaluator::evaluate(const std::vector<double>&
}
Eigen::Matrix4d function_item_evaluator::evaluate(double u) const {
return fn_evaluator_->evaluate(u);
Eigen::Matrix4d m = fn_evaluator_->evaluate(u);
if (!fn_evaluator_->settings_.get<ifcopenshell::geometry::settings::ComputeCurvature>().get()) {
m.row(3) = Eigen::Vector4d(0, 0, 0, 1);
}
return m;
}
+5 -1
View File
@@ -7,6 +7,10 @@
namespace ifcopenshell { namespace geometry {
/// @brief Computes a point on a helmert curve at s.
/// Returns (x,y,theta) at L/2. The results are in a vector so they can be returned to python
std::vector<double> helmert_curve_point(double A0, double A1, double A2, double s);
/// @brief Abstract class for evaluating a function_item. This class is specialized for each of the function_item types.
struct fn_evaluator {
fn_evaluator(const ifcopenshell::geometry::Settings& settings) : settings_(settings) {
@@ -55,7 +59,7 @@ class function_item_evaluator {
/// @brief evaluates the function at u
/// @param u u is constrained to be between start_ and start_+length
/// @return 4x4 placement matrix
/// @return 4x4 placement matrix. Curvature values for horizontal, vertical, and vertical + cant are stored in the last row.
Eigen::Matrix4d evaluate(double u) const;
private:
+27 -6
View File
@@ -63,31 +63,37 @@ taxonomy::loft::ptr ifcopenshell::geometry::make_loft(const Settings& settings_,
auto relative_dist_along = (dist_along - *profile_index) / (*(profile_index + 1) - *profile_index);
const auto& profile_a = cross_sections[std::distance(longitudes.begin(), profile_index)].section_geometry;
const auto& offset_a = cross_sections[std::distance(longitudes.begin(), profile_index)].offset;
const auto& rotation_a = cross_sections[std::distance(longitudes.begin(), profile_index)].rotation;
taxonomy::geom_item::ptr interpolated = nullptr;
// Only interpolate if:
// - there is a profile ahead of us, and
// - we're not exactly at the location of the current profile or whether there is an offset involved.
// - we're not exactly at the location of the current profile or whether there is an offset involved
bool should_interpolate =
(profile_index + 1 < longitudes.end()) &&
(relative_dist_along >= 1.e-9 || offset_a.cwiseAbs().maxCoeff() > 0.);
(relative_dist_along >= 1.e-9 || offset_a.cwiseAbs().maxCoeff() > 0. || rotation_a);
boost::optional<Eigen::Matrix3d> interpolated_rotation;
if (should_interpolate) {
taxonomy::geom_item::ptr profile_b;
Eigen::Vector3d offset_b;
boost::optional<Eigen::Matrix3d> rotation_b;
if ((profile_index + 1 < longitudes.end())) {
profile_b = cross_sections[std::distance(longitudes.begin(), profile_index) + 1].section_geometry;
offset_b = cross_sections[std::distance(longitudes.begin(), profile_index) + 1].offset;
rotation_b = cross_sections[std::distance(longitudes.begin(), profile_index) + 1].rotation;
} else {
profile_b = profile_a;
offset_b = offset_a;
rotation_b = rotation_a;
}
// Only interpolate if the profiles are different or either of the offsets is non-zero
bool should_interpolate2 =
(profile_a->instance != profile_b->instance) ||
(offset_a.cwiseAbs().maxCoeff() > 0. || offset_b.cwiseAbs().maxCoeff() > 0.);
(offset_a.cwiseAbs().maxCoeff() > 0. || offset_b.cwiseAbs().maxCoeff() > 0. || rotation_b);
if (should_interpolate2) {
@@ -130,6 +136,13 @@ taxonomy::loft::ptr ifcopenshell::geometry::make_loft(const Settings& settings_,
}
auto interpolated_offset = lerp(offset_a, offset_b, relative_dist_along);
if (rotation_a && rotation_b) {
// @todo we don't support an overridden rotation on only one of the placements
// in which case we would need to lerp with the rotation component below in m4b.
interpolated_rotation = lerp(*rotation_a, *rotation_b, relative_dist_along);
} else {
Logger::Error("Direction vectors on cross section placements only supported when used consistently");
}
taxonomy::loop::ptr w1, w2;
taxonomy::edge::ptr e1, e2;
for (auto tmp_ : boost::combine(loops_a, loops_b)) {
@@ -149,6 +162,7 @@ taxonomy::loft::ptr ifcopenshell::geometry::make_loft(const Settings& settings_,
auto& p2 = boost::get<taxonomy::point3::ptr>(e2->start);
auto p3 = (lerp(p1->ccomponents(), p2->ccomponents(), relative_dist_along) + interpolated_offset).eval();
// auto p4 = (interpolated_rotation * p3).eval();
points.push_back(taxonomy::make<taxonomy::point3>(p3));
}
if (!points.empty()) {
@@ -173,9 +187,16 @@ taxonomy::loft::ptr ifcopenshell::geometry::make_loft(const Settings& settings_,
}*/
Eigen::Matrix4d m4b = Eigen::Matrix4d::Identity();
m4b.col(0).head<3>() = m4.col(1).head<3>().normalized();
m4b.col(1).head<3>() = m4.col(2).head<3>().normalized();
m4b.col(2).head<3>() = m4.col(0).head<3>().normalized();
if (interpolated_rotation) {
// direction vectors on the linear placement overwrite the placement otherwise inferred from the tangent
m4b.col(0).head<3>() = interpolated_rotation->col(1);
m4b.col(1).head<3>() = interpolated_rotation->col(2);
m4b.col(2).head<3>() = interpolated_rotation->col(0);
} else {
m4b.col(0).head<3>() = m4.col(1).head<3>().normalized();
m4b.col(1).head<3>() = m4.col(2).head<3>().normalized();
m4b.col(2).head<3>() = m4.col(0).head<3>().normalized();
}
m4b.col(3).head<3>() = m4.col(3).head<3>();
if (interpolated) {
+1
View File
@@ -12,6 +12,7 @@ namespace ifcopenshell {
double dist_along;
taxonomy::geom_item::ptr section_geometry;
Eigen::Vector3d offset;
boost::optional<Eigen::Matrix3d> rotation;
bool operator <(const cross_section& other) const {
return dist_along < other.dist_along;
@@ -144,10 +144,15 @@ ifcopenshell::geometry::CgalShape::CgalShape(const cgal_shape_t& shape, bool con
}
if (shape.size_of_facets() != 1) {
// this is for handling the specical case of storing a single point in a polyhedron,
// the size_of_facets() == 1 check is for handling the specical case of
// storing a single point in a polyhedron as a degenerate triangle
//
// @todo come up with a proper variant for storing lower dimensional entities
CGAL::Polygon_mesh_processing::triangulate_faces(*shape_);
CGAL::Polygon_mesh_processing::remove_degenerate_faces(*shape_);
// @todo we don't have access to settings here so we don't know whether we should triangulate
// remove_degenerate_faces() is also called in the triangulate() call below though...
// CGAL::Polygon_mesh_processing::triangulate_faces(*shape_);
// CGAL::Polygon_mesh_processing::remove_degenerate_faces(*shape_);
}
}
@@ -183,6 +188,15 @@ void ifcopenshell::geometry::CgalShape::Triangulate(ifcopenshell::geometry::Sett
// ... also becuase of transforming the vertex positions, right?
cgal_shape_t s = *this;
const bool setting_use_original_edges = settings.get<ifcopenshell::geometry::settings::CgalEmitOriginalEdges>().get();
std::set<std::set<Kernel_::Point_3>> original_edges;
if (setting_use_original_edges) {
for (auto it = s.edges_begin(); it != s.edges_end(); ++it) {
original_edges.insert({ it->vertex()->point(), it->prev()->vertex()->point() });
}
}
if (!place.is_identity()) {
const auto& m = place.ccomponents();
@@ -199,14 +213,11 @@ void ifcopenshell::geometry::CgalShape::Triangulate(ifcopenshell::geometry::Sett
}
if (!std::all_of(s.facets_begin(), s.facets_end(), [](auto f) { return f.is_triangle(); })) {
if (!s.is_valid()) {
Logger::Message(Logger::LOG_ERROR, "Invalid Polyhedron_3 in object (before triangulation)");
return;
}
CGAL::Polygon_mesh_processing::remove_degenerate_faces(s);
bool success = false;
try {
success = CGAL::Polygon_mesh_processing::triangulate_faces(s);
@@ -215,27 +226,29 @@ void ifcopenshell::geometry::CgalShape::Triangulate(ifcopenshell::geometry::Sett
return;
}
CGAL::Polygon_mesh_processing::remove_degenerate_faces(s);
if (!success) {
Logger::Message(Logger::LOG_ERROR, "Triangulation failed");
return;
}
// std::cout << "Triangulated model: " << s.size_of_facets() << " facets and " << s.size_of_vertices() << " vertices" << std::endl;
if (!s.is_valid()) {
Logger::Message(Logger::LOG_ERROR, "Invalid Polyhedron_3 in object (after triangulation)");
// return;
}
}
// Facet -> planar component map for determining which
// edges are to be registered.
std::vector<std::set<Facet_const_handle>> components;
partition_coplanar_components(s, components);
std::map<Facet_const_handle, typename decltype(components)::const_iterator> facet_to_component;
for (auto it = components.begin(); it != components.end(); ++it) {
for (auto& f : *it) {
facet_to_component[f] = it;
if (!setting_use_original_edges) {
partition_coplanar_components(s, components);
for (auto it = components.begin(); it != components.end(); ++it) {
for (auto& f : *it) {
facet_to_component[f] = it;
}
}
}
@@ -305,8 +318,10 @@ void ifcopenshell::geometry::CgalShape::Triangulate(ifcopenshell::geometry::Sett
}
vertexidx[i] = (int)vidx;
is_face_boundary[i] = facet_to_component[face] != facet_to_component[current_halfedge->opposite()->face()];
is_face_boundary[i] = setting_use_original_edges
? original_edges.find({ current_halfedge->vertex()->point(), current_halfedge->prev()->vertex()->point() }) != original_edges.end()
: facet_to_component[face] != facet_to_component[current_halfedge->opposite()->face()];
++i;
++num_vertices;
++current_halfedge;
@@ -929,11 +929,11 @@ bool IfcGeom::util::boolean_operation(const boolean_settings& settings, const To
bool is_2d = count(a, TopAbs_FACE) > 0 && count(a, TopAbs_SHELL) == 0;
bool success = false;
BRepAlgoAPI_BooleanOperation* builder;
std::unique_ptr<BRepAlgoAPI_BooleanOperation> builder;
TopTools_ListOfShape b_tmp;
if (op == BOPAlgo_CUT) {
builder = new BRepAlgoAPI_Cut();
builder.reset(new BRepAlgoAPI_Cut());
if (do_subtraction_eliminate_disjoint_bbox) {
PERF("boolean subtraction: eliminate disjoint bbox");
@@ -968,9 +968,9 @@ bool IfcGeom::util::boolean_operation(const boolean_settings& settings, const To
}
} else if (op == BOPAlgo_COMMON) {
builder = new BRepAlgoAPI_Common();
builder.reset(new BRepAlgoAPI_Common());
} else if (op == BOPAlgo_FUSE) {
builder = new BRepAlgoAPI_Fuse();
builder.reset(new BRepAlgoAPI_Fuse());
} else {
return false;
}
@@ -1429,7 +1429,6 @@ bool IfcGeom::util::boolean_operation(const boolean_settings& settings, const To
Logger::Notice(str_str);
}
}
delete builder;
if (!success) {
if (allow_retry) {
return boolean_operation(settings, a, b, op, result, new_fuzziness);
@@ -26,6 +26,10 @@ using namespace ifcopenshell::geometry;
taxonomy::ptr mapping::map_impl(const IfcSchema::IfcArbitraryClosedProfileDef* inst) {
auto loop = taxonomy::cast<taxonomy::loop>(map(inst->OuterCurve()));
if (loop) {
if (inst->ProfileType() == IfcSchema::IfcProfileTypeEnum::IfcProfileType_CURVE) {
return loop;
}
auto face = taxonomy::make<taxonomy::face>();
loop->external = true;
face->children = { loop };
+246 -132
View File
@@ -100,18 +100,15 @@ typedef boost::mpl::vector<
struct parent_curve_function {
parent_curve_function() = default;
parent_curve_function(const parent_curve_function&) = default;
parent_curve_function(std::function<Eigen::Matrix4d(double)> fn) : fn_(fn) {
}
parent_curve_function& operator=(std::function<Eigen::Matrix4d(double)> fn) {
fn_ = fn;
return *this;
parent_curve_function(std::function<Eigen::Matrix4d(double)> fn, std::function<Eigen::Matrix4d(double)> cfn) : fn_(fn), cfn_(cfn) {
}
virtual Eigen::Matrix4d operator()(double u) const { return fn_(u); }
virtual Eigen::Matrix4d curvature(double u) const { return cfn_(u); }
private:
std::function<Eigen::Matrix4d(double)> fn_;
std::function<Eigen::Matrix4d(double)> cfn_;
};
struct polynomial_parent_curve : public parent_curve_function {
@@ -142,7 +139,7 @@ struct curve_segment_function {
Eigen::Matrix4d operator()(double u) const {
Eigen::Matrix4d parent_curve_point = (*parent_curve_fn_)(u);
Eigen::Matrix4d curve_segment_point = curve_segment_placement_ * remove_parent_curve_rotation_ * remove_parent_curve_translation_ * parent_curve_point;
return curve_segment_point;
return curve_segment_point + parent_curve_fn_->curvature(u);
}
private:
@@ -167,7 +164,7 @@ struct cant_curve_segment_function {
Eigen::Matrix4d parent_curve_point = (*parent_curve_fn_)(u);
Eigen::Matrix4d cant_increment = parent_curve_point - parent_curve_start_point_;
Eigen::Matrix4d curve_segment_point = curve_segment_placement_ + cant_increment;
return curve_segment_point;
return curve_segment_point + parent_curve_fn_->curvature(u);
}
private:
@@ -246,11 +243,12 @@ class curve_segment_evaluator {
Logger::Error(std::runtime_error("multiple uses of IfcSegmentCurve not supported"), inst_);
}
segment_type_ = is_horizontal ? ST_HORIZONTAL : is_vertical ? ST_VERTICAL : ST_CANT;
segment_type_ = is_horizontal ? ST_HORIZONTAL : is_vertical ? ST_VERTICAL : is_cant ? ST_CANT : ST_HORIZONTAL;
start_ = translate_if_param_value(inst->ParentCurve(), inst->SegmentStart()) * length_unit;
length_ = translate_if_param_value(inst->ParentCurve(), inst->SegmentLength()) * length_unit;
projected_length_ = length_; // initialize with something reasonable
if (inst) {
curve_segment_placement_ = taxonomy::cast<taxonomy::matrix4>(mapping_->map(inst->Placement()))->ccomponents();
@@ -335,10 +333,8 @@ class curve_segment_evaluator {
}
}
void set_spiral_function(double s, std::function<double(double)> fnX, std::function<double(double)> fnY) {
void set_spiral_function(double s, std::function<double(double)> fnX, std::function<double(double)> fnY, std::function<double(double)> curvature) {
if (segment_type_ == ST_HORIZONTAL || segment_type_ == ST_VERTICAL) {
projected_length_ = length_;
// start of trimmed curve
double pcStartX = 0.0, pcStartY = 0.0;
double pcStartDx = 1.0, pcStartDy = 0.0;
@@ -382,30 +378,71 @@ class curve_segment_evaluator {
};
}
parent_curve_fn_ = std::make_shared<spiral_parent_curve>([start=start_, s, convert_u, fnX, fnY](double u) {
u = convert_u(u+start);
parent_curve_fn_ = std::make_shared<spiral_parent_curve>(
[start=start_, s, convert_u, fnX, fnY](double u)->Eigen::Matrix4d {
u = convert_u(u+start);
// integration limits, integrate from a to b
auto b = s ? u / s : 0.0;
// integration limits, integrate from a to b
auto b = s ? u / s : 0.0;
// point on parent curve
auto x = boost::math::quadrature::trapezoidal(fnX, 0.0, b);
auto y = boost::math::quadrature::trapezoidal(fnY, 0.0, b);
auto dx = s ? fnX(b) / s : 1.0;
auto dy = s ? fnY(b) / s : 0.0;
// point on parent curve
auto x = boost::math::quadrature::trapezoidal(fnX, 0.0, b);
auto y = boost::math::quadrature::trapezoidal(fnY, 0.0, b);
auto dx = s ? fnX(b) / s : 1.0;
auto dy = s ? fnY(b) / s : 0.0;
Eigen::Matrix4d m = Eigen::Matrix4d::Identity();
m.col(0) = Eigen::Vector4d(dx, dy, 0, 0);
m.col(1) = Eigen::Vector4d(-dy, dx, 0, 0);
m.col(3) = Eigen::Vector4d(x, y, 0, 1);
return m;
});
Eigen::Matrix4d m = Eigen::Matrix4d::Identity();
m.col(0) = Eigen::Vector4d(dx, dy, 0, 0);
m.col(1) = Eigen::Vector4d(-dy, dx, 0, 0);
m.col(3) = Eigen::Vector4d(x, y, 0, 1);
return m;
},
[start = start_, convert_u, curvature](double u) -> Eigen::Matrix4d {
u = convert_u(u + start);
Eigen::Matrix4d c = Eigen::Matrix4d::Zero();
c(3, 0) = curvature(u);
return c;
}
);
if (segment_type_ == ST_VERTICAL) {
// for vertical, the input curve length is measured along the spiral.
// projected_length_ is the domain of the curve, measured in the horizontal "Distance Along" coordinate
// The quickest way to get the projected length is the difference of the i-ordinates at the start and end of the spiral.
// Evalute the parent curve at the start and end
Eigen::Matrix4d m1 = (*parent_curve_fn_)(start_);
Eigen::Matrix4d m2 = (*parent_curve_fn_)(start_ + length_);
// parent curve point at start
double x1 = m1(0, 3);
double y1 = m1(1, 3);
// parent curve point at end
double x2 = m2(0, 3);
double y2 = m2(1, 3);
// direction of tangent at start of parent curve in curve coordinates
auto dx = (*curve_segment_placement_)(0, 0);
auto dy = -(*curve_segment_placement_)(1, 0); // -1 to rotation in opposite direction
auto X1 = x1 * dx - y1 * dy; // X of start point in global coordinates
auto X2 = x2 * dx - y2 * dy; // X of end point in global coordinates
projected_length_ = X2 - X1; // distance between points on the global X-axis
} else {
projected_length_ = length_;
}
} else if (segment_type_ == ST_CANT) {
Logger::Error(std::runtime_error("Unexpected segment type encountered - cant is handled in set_cant_spiral_function - should never get here"));
parent_curve_fn_ = std::make_shared<parent_curve_function>([](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); });
parent_curve_fn_ = std::make_shared<parent_curve_function>(
[](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); },
[](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); }
);
} else {
Logger::Error(std::runtime_error("Unexpected segment type encountered"));
parent_curve_fn_ = std::make_shared<parent_curve_function>([](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); });
parent_curve_fn_ = std::make_shared<parent_curve_function>(
[](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); },
[](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); }
);
}
}
@@ -424,33 +461,40 @@ class curve_segment_evaluator {
auto end_cant = Cant(/* start_ + */ length_);
auto delta_cant = end_cant - start_cant;
parent_curve_fn_ = std::make_shared<spiral_parent_curve>([start_angle,delta_angle,start_cant,delta_cant,Superelevation, SuperelevationSlope, Cant](double u) -> Eigen::Matrix4d {
// departure of the curve segment from the base curve (superelevation)
auto super_elevation = Superelevation(u);
auto slope = SuperelevationSlope(u);
parent_curve_fn_ = std::make_shared<spiral_parent_curve>(
[start_angle,delta_angle,start_cant,delta_cant,Superelevation, SuperelevationSlope, Cant](double u) -> Eigen::Matrix4d {
// departure of the curve segment from the base curve (superelevation)
auto super_elevation = Superelevation(u);
auto slope = SuperelevationSlope(u);
// direction along curve segment
auto angle = atan(slope);
auto dx = cos(angle);
auto dy = sin(angle);
Eigen::Vector4d ref_dir(dx, dy, 0.0, 0.0);
// direction along curve segment
auto angle = atan(slope);
auto dx = cos(angle);
auto dy = sin(angle);
Eigen::Vector4d ref_dir(dx, dy, 0.0, 0.0);
// tilt angle in the plane of the cross section
auto cant = Cant(u);
auto tilt_angle = start_angle + delta_angle * (cant - start_cant) / delta_cant;
Eigen::Vector4d z(0.0, cos(tilt_angle), sin(tilt_angle), 0.0);
// tilt angle in the plane of the cross section
auto cant = Cant(u);
auto tilt_angle = start_angle + delta_angle * (cant - start_cant) / delta_cant;
Eigen::Vector4d z(0.0, cos(tilt_angle), sin(tilt_angle), 0.0);
// compute axis direction
Eigen::Vector4d y = z.cross3(ref_dir);
Eigen::Vector4d axis = ref_dir.cross3(y);
// compute axis direction
Eigen::Vector4d y = z.cross3(ref_dir);
Eigen::Vector4d axis = ref_dir.cross3(y);
Eigen::Matrix4d m = Eigen::Matrix4d::Identity();
m.col(0) = ref_dir;
m.col(1) = y;
m.col(2) = axis;
m.col(3) = Eigen::Vector4d(u, super_elevation, 0.0, 1.0);
return m;
});
Eigen::Matrix4d m = Eigen::Matrix4d::Identity();
m.col(0) = ref_dir;
m.col(1) = y;
m.col(2) = axis;
m.col(3) = Eigen::Vector4d(u, super_elevation, 0.0, 1.0);
return m;
},
[Cant](double u) -> Eigen::Matrix4d {
Eigen::Matrix4d c = Eigen::Matrix4d::Zero();
c(3, 0) = Cant(u);
return c;
}
);
parent_curve_start_point_ = (*parent_curve_fn_)(0.0);
}
@@ -500,7 +544,8 @@ class curve_segment_evaluator {
auto s = fabs(A * sqrt(PI)); // curve length when u = 1.0
auto fn_x = [A, s](double t) -> double { return A ? s * cos(PI * A * t * t / (2 * fabs(A))) : 0.0; };
auto fn_y = [A, s](double t) -> double { return A ? s * sin(PI * A * t * t / (2 * fabs(A))) : 0.0; };
set_spiral_function(s, fn_x, fn_y);
auto curvature = [A](double t) -> double { return A ? A * t / fabs(A * A * A) : 0.0; };
set_spiral_function(s, fn_x, fn_y, curvature);
}
}
#endif
@@ -522,8 +567,13 @@ class curve_segment_evaluator {
};
auto fn_x = [theta](double t) -> double { return cos(theta(t)); };
auto fn_y = [theta](double t) -> double { return sin(theta(t)); };
auto curvature = [constant_term, cosine_term, L](double t) -> double {
auto a0 = constant_term.has_value() ? L / constant_term.value() : 0.0;
auto a1 = (L / cosine_term) * cos((PI / L) * t);
return a0 + a1;
};
double s = 1.0;
set_spiral_function(s, fn_x, fn_y);
set_spiral_function(s, fn_x, fn_y, curvature);
} else if (segment_type_ == ST_CANT) {
boost::optional<std::function<double(double)>> super, slope;
std::tie(super, slope) = get_superelevation_functions();
@@ -548,10 +598,16 @@ class curve_segment_evaluator {
set_cant_spiral_function(*super, *slope, cant);
} else if (segment_type_ == ST_VERTICAL) {
Logger::Error(std::runtime_error("IfcCosineSpiral cannot be used for vertical alignment"));
parent_curve_fn_ = std::make_shared<parent_curve_function>([](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); });
parent_curve_fn_ = std::make_shared<parent_curve_function>(
[](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); },
[](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); }
);
} else {
Logger::Error(std::runtime_error("Unexpected segment type encountered"));
parent_curve_fn_ = std::make_shared<parent_curve_function>([](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); });
parent_curve_fn_ = std::make_shared<parent_curve_function>(
[](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); },
[](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); }
);
}
}
#endif
@@ -577,8 +633,14 @@ class curve_segment_evaluator {
};
auto fn_x = [theta](double t) -> double { return cos(theta(t)); };
auto fn_y = [theta](double t) -> double { return sin(theta(t)); };
auto curvature = [constant_term, linear_term, sine_term, L](double t) -> double {
auto a0 = constant_term.has_value() ? L / constant_term.value() : 0.0;
auto a1 = linear_term.has_value() ? sign(linear_term.value()) * pow(L / linear_term.value(), 2.0)*(t/L) : 0.0;
auto a2 = (L / sine_term) * sin(2 * PI * t / L);
return a0 + a1 + a2;
};
double s = 1.0;
set_spiral_function(s, fn_x, fn_y);
set_spiral_function(s, fn_x, fn_y, curvature);
} else if (segment_type_ == ST_CANT) {
boost::optional<std::function<double(double)>> super, slope;
std::tie(super, slope) = get_superelevation_functions();
@@ -605,16 +667,20 @@ class curve_segment_evaluator {
set_cant_spiral_function(*super, *slope, cant);
} else if (segment_type_ == ST_VERTICAL) {
Logger::Error(std::runtime_error("IfcSineSpiral cannot be used for vertical alignment"));
parent_curve_fn_ = std::make_shared<parent_curve_function>([](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); });
parent_curve_fn_ = std::make_shared<parent_curve_function>(
[](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); },
[](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); });
} else {
Logger::Error(std::runtime_error("Unexpected segment type encountered"));
parent_curve_fn_ = std::make_shared<parent_curve_function>([](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); });
parent_curve_fn_ = std::make_shared<parent_curve_function>(
[](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); },
[](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); });
}
}
#endif
void polynomial_spiral(boost::optional<double> A0, boost::optional<double> A1, boost::optional<double> A2, boost::optional<double> A3, boost::optional<double> A4, boost::optional<double> A5, boost::optional<double> A6, boost::optional<double> A7) {
auto theta = [A0, A1, A2, A3, A4, A5, A6, A7, start = start_ * length_unit_, lu = length_unit_](double t) {
auto theta = [A0, A1, A2, A3, A4, A5, A6, A7, start = start_ * length_unit_, lu = length_unit_](double t) -> double {
auto a0 = A0.has_value() ? t / (A0.value() * lu) : 0.0;
auto a1 = A1.has_value() ? A1.value() * lu * std::pow(t, 2) / (2 * fabs(std::pow(A1.value() * lu, 3))) : 0.0;
auto a2 = A2.has_value() ? std::pow(t, 3) / (3 * std::pow(A2.value() * lu, 3)) : 0.0;
@@ -629,15 +695,31 @@ class curve_segment_evaluator {
auto fn_x = [theta](double t) -> double { return cos(theta(t)); };
auto fn_y = [theta](double t) -> double { return sin(theta(t)); };
// this is same as cant function in polynomial_cant_spiral
auto curvature = [A0, A1, A2, A3, A4, A5, A6, A7, start = start_, L = length_, lu = length_unit_, length = length_](double t) -> double {
t += start;
auto a0 = A0.has_value() ? 1 / (A0.value() * lu) : 0.0;
auto a1 = A1.has_value() ? A1.value() * lu * t / fabs(std::pow(A1.value() * lu, 3)) : 0.0;
auto a2 = A2.has_value() ? std::pow(t, 2) / std::pow(A2.value() * lu, 3) : 0.0;
auto a3 = A3.has_value() ? A3.value() * lu * std::pow(t, 3) / fabs(std::pow(A3.value() * lu, 5)) : 0.0;
auto a4 = A4.has_value() ? std::pow(t, 4) / std::pow(A4.value() * lu, 5) : 0.0;
auto a5 = A5.has_value() ? A5.value() * lu * std::pow(t, 5) / fabs(std::pow(A5.value() * lu, 7)) : 0.0;
auto a6 = A6.has_value() ? std::pow(t, 6) / std::pow(A6.value() * lu, 7) : 0.0;
auto a7 = A7.has_value() ? A7.value() * lu * std::pow(t, 7) / fabs(std::pow(A7.value() * lu, 9)) : 0.0;
return L * (a0 + a1 + a2 + a3 + a4 + a5 + a6 + a7);
};
double s = 1.0;
set_spiral_function(s, fn_x, fn_y);
set_spiral_function(s, fn_x, fn_y, curvature);
}
void polynomial_cant_spiral(boost::optional<double> A0, boost::optional<double> A1, boost::optional<double> A2, boost::optional<double> A3, boost::optional<double> A4, boost::optional<double> A5, boost::optional<double> A6, boost::optional<double> A7) {
boost::optional<std::function<double(double)>> super, slope;
std::tie(super, slope) = get_superelevation_functions();
auto cant = [A0, A1, A2, A3, A4, A5, A6, A7, start = start_, L = length_, lu = length_unit_, length = length_](double t) {
auto cant = [A0, A1, A2, A3, A4, A5, A6, A7, start = start_, L = length_, lu = length_unit_, length = length_](double t) -> double {
t += start;
auto a0 = A0.has_value() ? 1 / (A0.value() * lu) : 0.0;
auto a1 = A1.has_value() ? A1.value() * lu * t / fabs(std::pow(A1.value() * lu, 3)) : 0.0;
@@ -655,7 +737,7 @@ class curve_segment_evaluator {
}
if (!slope.has_value()) {
slope = [A1, A2, A3, A4, A5, A6, A7, start = start_, L = length_, lu = length_unit_, length = length_](double t) {
slope = [A1, A2, A3, A4, A5, A6, A7, start = start_, L = length_, lu = length_unit_, length = length_](double t) -> double {
t += start;
auto a1 = A1.has_value() ? A1.value() * lu / fabs(std::pow(A1.value() * lu, 3)) : 0.0;
auto a2 = A2.has_value() ? 2 * t / std::pow(A2.value() * lu, 3) : 0.0;
@@ -787,29 +869,36 @@ class curve_segment_evaluator {
};
}
parent_curve_fn_ = std::make_shared<circle_parent_curve>([segment_type = segment_type_, R, pcCenterX, pcCenterY, start_angle, sign_l, convert_u](double u) {
u = convert_u(u);
parent_curve_fn_ = std::make_shared<circle_parent_curve>(
[segment_type = segment_type_, R, pcCenterX, pcCenterY, start_angle, sign_l, convert_u](double u)->Eigen::Matrix4d {
u = convert_u(u);
// u is measured along the circle
// angle from the X=0 axis to the current point
auto delta = R ? sign_l * u / R : 0.0;
auto sweep_angle = start_angle + delta;
auto cos_sweep_angle = cos(sweep_angle);
auto sin_sweep_angle = sin(sweep_angle);
// u is measured along the circle
// angle from the X=0 axis to the current point
auto delta = R ? sign_l * u / R : 0.0;
auto sweep_angle = start_angle + delta;
auto cos_sweep_angle = cos(sweep_angle);
auto sin_sweep_angle = sin(sweep_angle);
// point on the parent curve
auto pcX = R * cos_sweep_angle + pcCenterX;
auto pcY = R * sin_sweep_angle + pcCenterY;
// point on the parent curve
auto pcX = R * cos_sweep_angle + pcCenterX;
auto pcY = R * sin_sweep_angle + pcCenterY;
auto pcDx = -sign_l * sin_sweep_angle;
auto pcDy = sign_l * cos_sweep_angle;
auto pcDx = -sign_l * sin_sweep_angle;
auto pcDy = sign_l * cos_sweep_angle;
Eigen::Matrix4d m = Eigen::Matrix4d::Identity();
m.col(0) = Eigen::Vector4d(pcDx, pcDy, 0, 0);
m.col(1) = Eigen::Vector4d(-pcDy, pcDx, 0, 0);
m.col(3) = Eigen::Vector4d(pcX, pcY, 0.0, 1.0);
return m;
});
Eigen::Matrix4d m = Eigen::Matrix4d::Identity();
m.col(0) = Eigen::Vector4d(pcDx, pcDy, 0, 0);
m.col(1) = Eigen::Vector4d(-pcDy, pcDx, 0, 0);
m.col(3) = Eigen::Vector4d(pcX, pcY, 0.0, 1.0);
return m;
},
[R](double) -> Eigen::Matrix4d {
Eigen::Matrix4d c = Eigen::Matrix4d::Zero();
c(3, 0) = 1 / R;
return c;
}
);
if (segment_type_ == ST_HORIZONTAL) {
parent_curve_start_point_ = (*parent_curve_fn_)(start_);
@@ -839,10 +928,14 @@ class curve_segment_evaluator {
} else if (segment_type_ == ST_CANT) {
Logger::Warning(std::runtime_error("Use of IfcCircle for cant is not supported"));
parent_curve_fn_ = std::make_shared<parent_curve_function>([](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); });
parent_curve_fn_ = std::make_shared<parent_curve_function>(
[](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); },
[](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); });
} else {
Logger::Error(std::runtime_error("Unexpected segment type encountered"));
parent_curve_fn_ = std::make_shared<parent_curve_function>([](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); });
parent_curve_fn_ = std::make_shared<parent_curve_function>(
[](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); },
[](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); });
}
}
@@ -890,23 +983,32 @@ class curve_segment_evaluator {
convert_u = [pcDx](double u) { return u/pcDx; };
}
parent_curve_fn_ = std::make_shared<line_parent_curve>([pcX, pcY, pcDx, pcDy, convert_u](double u) {
u = convert_u(u);
parent_curve_fn_ = std::make_shared<line_parent_curve>(
[pcX, pcY, pcDx, pcDy, convert_u](double u)->Eigen::Matrix4d {
u = convert_u(u);
auto x = pcX + pcDx * u;
auto y = pcY + pcDy * u;
auto x = pcX + pcDx * u;
auto y = pcY + pcDy * u;
Eigen::Matrix4d m = Eigen::Matrix4d::Identity();
m.col(0) = Eigen::Vector4d(pcDx, pcDy, 0, 0);
m.col(1) = Eigen::Vector4d(-pcDy, pcDx, 0, 0);
m.col(3) = Eigen::Vector4d(x, y, 0.0, 1.0);
return m;
});
Eigen::Matrix4d m = Eigen::Matrix4d::Identity();
m.col(0) = Eigen::Vector4d(pcDx, pcDy, 0, 0);
m.col(1) = Eigen::Vector4d(-pcDy, pcDx, 0, 0);
m.col(3) = Eigen::Vector4d(x, y, 0.0, 1.0);
return m;
},
[](double /*u*/) -> Eigen::Matrix4d {
// curvature is zero for a line. identity initializes c(3,0) = 0
Eigen::Matrix4d c = Eigen::Matrix4d::Zero();
return c;
}
);
parent_curve_start_point_ = (*parent_curve_fn_)(start_);
} else {
Logger::Warning(std::runtime_error("Unexpected segment type encountered"));
parent_curve_fn_ = std::make_shared<parent_curve_function>([](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); });
Logger::Warning(std::runtime_error("Unexpected segment type encountered"));
parent_curve_fn_ = std::make_shared<parent_curve_function>(
[](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); },
[](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); });
}
}
@@ -996,50 +1098,62 @@ class curve_segment_evaluator {
}
// This functor evaluates the polynomial at a distance u along the curve
parent_curve_fn_ = std::make_shared<polynomial_parent_curve>([start = start_, lu = length_unit_, coeffX, coeffY, convert_u](double u)->Eigen::Matrix4d {
auto x = convert_u(u + start); // find x for u
// evaluate the polynomial at x
std::array<const std::vector<double>*, 2> coefficients{&coeffX, &coeffY};
std::array<double, 2> position{0.0, 0.0}; // = SUM(coeff*u^pos)
std::array<double, 2> slope{0.0, 0.0}; // slope is derivative of the curve = SUM( coeff*pos*u^(pos-1) )
for (int i = 0; i < 2; i++) { // loop over X and Y
auto begin = coefficients[i]->cbegin();
auto end = coefficients[i]->cend();
for (auto iter = begin; iter != end; iter++) {
auto exp = std::distance(begin, iter);
auto coeff = (*iter);
position[i] += coeff * pow(lu, 1-exp) * pow(x, exp);
parent_curve_fn_ = std::make_shared<polynomial_parent_curve>(
[start = start_, lu = length_unit_, coeffX, coeffY, convert_u](double u)->Eigen::Matrix4d {
auto x = convert_u(u + start); // find x for u
// evaluate the polynomial at x
std::array<const std::vector<double>*, 2> coefficients{&coeffX, &coeffY};
std::array<double, 2> position{0.0, 0.0}; // = SUM(coeff*u^pos)
std::array<double, 2> slope{0.0, 0.0}; // slope is derivative of the curve = SUM( coeff*pos*u^(pos-1) )
for (int i = 0; i < 2; i++) { // loop over X and Y
auto begin = coefficients[i]->cbegin();
auto end = coefficients[i]->cend();
for (auto iter = begin; iter != end; iter++) {
auto exp = std::distance(begin, iter);
auto coeff = (*iter);
position[i] += coeff * pow(lu, 1-exp) * pow(x, exp);
if (iter != begin) {
slope[i] += exp * coeff * pow(lu, 1-exp) * pow(x, exp - 1);
}
}
if (iter != begin) {
slope[i] += exp * coeff * pow(lu, 1-exp) * pow(x, exp - 1);
}
}
}
auto X = position[0];
auto Y = position[1];
auto Dx = slope[0];
auto Dy = slope[1];
auto angle = atan2(Dy, Dx);
Dx = cos(angle);
Dy = sin(angle);
Eigen::Matrix4d m = Eigen::Matrix4d::Identity();
m.col(0) = Eigen::Vector4d(Dx, Dy, 0, 0);
m.col(1) = Eigen::Vector4d(-Dy, Dx, 0, 0);
m.col(3) = Eigen::Vector4d(X, Y, 0.0, 1.0);
return m;
},
[start = start_, lu = length_unit_, coeffX, coeffY, convert_u](double u) -> Eigen::Matrix4d {
auto x = convert_u(u + start); // find x for u
Eigen::Matrix4d c = Eigen::Matrix4d::Zero();
c(3, 0) = coeffY[2]; // this may need a unit conversion (also assume there is only 3 coefficients)
return c;
}
auto X = position[0];
auto Y = position[1];
auto Dx = slope[0];
auto Dy = slope[1];
auto angle = atan2(Dy, Dx);
Dx = cos(angle);
Dy = sin(angle);
Eigen::Matrix4d m = Eigen::Matrix4d::Identity();
m.col(0) = Eigen::Vector4d(Dx, Dy, 0, 0);
m.col(1) = Eigen::Vector4d(-Dy, Dx, 0, 0);
m.col(3) = Eigen::Vector4d(X, Y, 0.0, 1.0);
return m;
});
);
parent_curve_start_point_ = (*parent_curve_fn_)(0.0); // start is added to u in parent_curve_fn_, so use 0.0 here
} else if (segment_type_ == ST_CANT) {
Logger::Warning(std::runtime_error("Use of IfcPolynomialCurve for cant is not supported"));
parent_curve_fn_ = std::make_shared<parent_curve_function>([](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); });
parent_curve_fn_ = std::make_shared<parent_curve_function>(
[](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); },
[](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); });
} else {
Logger::Error(std::runtime_error("Unexpected segment type encountered"));
parent_curve_fn_ = std::make_shared<parent_curve_function>([](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); });
parent_curve_fn_ = std::make_shared<parent_curve_function>(
[](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); },
[](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); });
}
}
};
@@ -39,7 +39,12 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcOffsetCurveByDistances* inst
auto first_offset_value = *(offset_values->begin());
auto basis_curve = inst->BasisCurve();
auto curve = taxonomy::dcast<taxonomy::piecewise_function>(map(basis_curve));
auto curve = taxonomy::dcast<taxonomy::function_item>(map(basis_curve));
if (!curve) {
// Only implement on alignment curves
Logger::Warning("IfcOffsetCurveByDistances is only implemented for BasisCurves curves based on taxonomy::function_item", inst);
return nullptr;
}
double start = curve->start();
double basis_curve_length = curve->length();
@@ -31,10 +31,10 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcSectionedSolidHorizontal* in
std::vector<cross_section> cross_sections;
auto dir = map(inst->Directrix());
auto pwf = taxonomy::dcast<taxonomy::piecewise_function>(dir);
if (!pwf) {
auto fn = taxonomy::dcast<taxonomy::function_item>(dir);
if (!fn) {
// Only implement on alignment curves
Logger::Warning("IfcSectionedSolidHorizontal is only implemented for piecewise function Directrix curves", inst);
Logger::Warning("IfcSectionedSolidHorizontal is only implemented for Directrix curves based on taxonomy::function_item", inst);
return nullptr;
}
@@ -43,12 +43,13 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcSectionedSolidHorizontal* in
auto csps = inst->CrossSectionPositions();
std::vector<taxonomy::face::ptr> faces;
// The PointByDistanceExpressesions are factored out into (a) a cartesian offset relative to the
// The PointByDistanceExpressions are factored out into (a) a cartesian offset relative to the
// reference frame along a certain curve location (b) the longitude.
// The longitudes determine the range of the sweep and the offsets are interpolated in between
// sweep segments.
std::vector<Eigen::Vector3d> profile_offsets;
std::vector<boost::optional<Eigen::Matrix3d>> profile_rotations;
std::vector<double> longitudes;
for (auto& cs : *css) {
@@ -69,10 +70,20 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcSectionedSolidHorizontal* in
);
profile_offsets.push_back(po);
boost::optional<Eigen::Matrix3d> rot;
if (csp->Axis() && csp->RefDirection()) {
rot = taxonomy::matrix4(
Eigen::Vector3d(0, 0, 0),
taxonomy::cast<taxonomy::direction3>(map(csp->Axis()))->ccomponents(),
taxonomy::cast<taxonomy::direction3>(map(csp->RefDirection()))->ccomponents()).ccomponents().block<3,3>(0,0);
} else if (csp->Axis()) {
rot = taxonomy::matrix4(
Eigen::Vector3d(0, 0, 0),
taxonomy::cast<taxonomy::direction3>(map(csp->Axis()))->ccomponents()).ccomponents().block<3, 3>(0, 0);
}
profile_rotations.push_back(rot);
}
#else
return nullptr;
#endif
if (faces.size() != profile_offsets.size()) {
Logger::Warning("Expected CrossSections and CrossSectionPositions to be equal length, but got " + std::to_string(faces.size()) + " and " + std::to_string(profile_offsets.size()) + " respectively", inst);
return nullptr;
@@ -83,11 +94,14 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcSectionedSolidHorizontal* in
}
for (size_t i = 0; i < faces.size(); ++i) {
cross_sections.push_back({ longitudes[i], faces[i], profile_offsets[i] });
}
cross_sections.push_back({ longitudes[i], faces[i], profile_offsets[i], profile_rotations[i]});
}
#else
return nullptr;
#endif
}
return make_loft(settings_, inst, pwf, cross_sections);
return make_loft(settings_, inst, fn, cross_sections);
}
#endif
+15 -1
View File
@@ -49,6 +49,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcSectionedSurface* inst) {
// The longitudes determine the range of the sweep and the offsets are interpolated in between
// sweep segments.
std::vector<Eigen::Vector3d> profile_offsets;
std::vector<boost::optional<Eigen::Matrix3d>> profile_rotations;
std::vector<double> longitudes;
for (auto& cs : *css) {
@@ -69,6 +70,19 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcSectionedSurface* inst) {
);
profile_offsets.push_back(po);
boost::optional<Eigen::Matrix3d> rot;
if (csp->Axis() && csp->RefDirection()) {
rot = taxonomy::matrix4(
Eigen::Vector3d(0, 0, 0),
taxonomy::cast<taxonomy::direction3>(map(csp->Axis()))->ccomponents(),
taxonomy::cast<taxonomy::direction3>(map(csp->RefDirection()))->ccomponents()).ccomponents().block<3, 3>(0, 0);
} else if (csp->Axis()) {
rot = taxonomy::matrix4(
Eigen::Vector3d(0, 0, 0),
taxonomy::cast<taxonomy::direction3>(map(csp->Axis()))->ccomponents()).ccomponents().block<3, 3>(0, 0);
}
profile_rotations.push_back(rot);
}
#else
return nullptr;
@@ -83,7 +97,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcSectionedSurface* inst) {
}
for (size_t i = 0; i < faces.size(); ++i) {
cross_sections.push_back({ longitudes[i], faces[i], profile_offsets[i] });
cross_sections.push_back({ longitudes[i], faces[i], profile_offsets[i], profile_rotations[i] });
}
}
+6 -2
View File
@@ -58,8 +58,12 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcSweptDiskSolid* inst) {
auto ep = inst->EndParam();
#else
boost::optional<double> sp, ep;
sp = inst->StartParam();
ep = inst->EndParam();
try {
sp = inst->StartParam();
ep = inst->EndParam();
} catch (const IfcParse::IfcException& e) {
Logger::Warning(e);
}
#endif
const double tol = settings_.get<settings::Precision>().get();
+1 -1
View File
@@ -874,7 +874,7 @@ void mapping::initialize_units_() {
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();
auto m3 = Eigen::Quaterniond(vs[0], vs[1], vs[2], vs[3]).normalized().matrix();
Eigen::Matrix4d m4 = Eigen::Matrix4d::Identity();
m4 << m3;
offset_and_rotation_ *= m4;
+1
View File
@@ -86,6 +86,7 @@ namespace geometry {
virtual std::map<std::string, IfcUtil::IfcBaseEntity*> get_layers(IfcUtil::IfcBaseEntity*);
virtual void initialize_settings();
virtual double get_length_unit() const { return length_unit_; }
virtual const std::string& get_length_unit_name() const { return length_unit_name_; }
virtual aggregate_of_instance::ptr find_openings(const IfcUtil::IfcBaseEntity*);
virtual IfcUtil::IfcBaseEntity* representation_of(const IfcUtil::IfcBaseEntity* product);
+73 -23
View File
@@ -799,38 +799,88 @@ boost::optional<face::ptr> ifcopenshell::geometry::taxonomy::curve_to_face_upgra
return face_;
}
namespace {
// @todo eliminate redundancy with cgal kernel
void evaluate_curve(const circle::ptr& c, double u, point3& p) {
Eigen::Vector4d xy{ c->radius * std::cos(u), c->radius * std::sin(u), 0, 1. };
p.components() = (c->matrix->ccomponents() * xy).head<3>();
}
boost::optional<piecewise_function::ptr> ifcopenshell::geometry::taxonomy::loop_to_piecewise_function_upgrade_impl(ptr item) {
boost::optional<piecewise_function::ptr> pwf_;
// @todo eliminate redundancy with cgal kernel
void evaluate_curve_d1(const circle::ptr& c, double u, direction3& p) {
Eigen::Vector4d xy{ -std::sin(u), cos(u), 0, 0. };
p.components() = (c->matrix->ccomponents() * xy).head<3>();
}
double project_onto_curve(const circle::ptr& c, const point3& p) {
Eigen::Vector2d xy = (c->matrix->ccomponents().inverse() * p.ccomponents().homogeneous()).head<2>();
return std::atan2(xy(1), xy(0));
}
}
boost::optional<function_item::ptr> ifcopenshell::geometry::taxonomy::loop_to_function_item_upgrade_impl(ptr item) {
boost::optional<function_item::ptr> fi_;
auto loop_ = dcast<loop>(item);
if (loop_) {
if (loop_->pwf.is_initialized()) {
pwf_ = loop_->pwf;
if (loop_->fi.is_initialized()) {
fi_ = loop_->fi;
} else {
// piecewise_function is a specialization of function_item - callers don't need to know this detail
piecewise_function::spans_t spans;
spans.reserve(loop_->children.size());
for (auto& edge_ : loop_->children) {
// the edge could be an arc or trimmed circle in the case of IfcIndexPolyCurve - support for this isn't implemented yet
if (edge_->basis) {
Logger::Message(Logger::Severity::LOG_NOTICE, "Shape of basis curve ignored - edge is treated as a straight line edge");
}
if (edge_->basis && edge_->basis->kind() == CIRCLE) {
const circle::ptr circ = std::static_pointer_cast<circle>(edge_->basis);
const auto& s = boost::get<point3::ptr>(edge_->start)->ccomponents();
const auto& e = boost::get<point3::ptr>(edge_->end)->ccomponents();
Eigen::Vector3d v = e - s;
auto l = v.norm(); // the norm of a vector is a measure of its length
v.normalize(); // normalize the vector so that it is a unit direction vector
std::function<Eigen::Matrix4d(double)> fn = [s, v](double u) {
Eigen::Vector3d o(s + u * v), axis(0, 0, 1), refDirection(v);
auto Y = axis.cross(refDirection).normalized();
axis = refDirection.cross(Y).normalized();
return make<matrix4>(o, axis, refDirection)->components();
};
spans.emplace_back(taxonomy::make<taxonomy::functor_item>(l, fn));
auto* s_pnt = boost::get<point3::ptr>(&edge_->start);
auto* e_pnt = boost::get<point3::ptr>(&edge_->end);
auto* s_param = boost::get<double>(&edge_->start);
auto* e_param = boost::get<double>(&edge_->end);
if (!s_pnt && !s_param) {
return boost::none;
}
if (!e_pnt && !e_param) {
return boost::none;
}
double s = s_pnt ? project_onto_curve(circ, **s_pnt) : *s_param;
double e = e_pnt ? project_onto_curve(circ, **e_pnt) : *e_param;
auto l = std::fabs(s - e) * circ->radius;
std::function<Eigen::Matrix4d(double)> fn = [circ, s](double u) {
point3 P;
direction3 d;
evaluate_curve(circ, u / circ->radius + s, P);
evaluate_curve_d1(circ, u / circ->radius + s, d);
return matrix4(P.ccomponents(), circ->matrix->ccomponents().col(2).head<3>(), d.ccomponents()).components();
};
spans.emplace_back(taxonomy::make<taxonomy::functor_item>(l, fn));
} else if (edge_->start.which() == 1 && edge_->end.which() == 1) {
if (edge_->basis && edge_->basis->kind() != LINE) {
Logger::Message(Logger::Severity::LOG_WARNING, "Basis curve not supported - edge is treated as a straight line edge");
}
const auto& s = boost::get<point3::ptr>(edge_->start)->ccomponents();
const auto& e = boost::get<point3::ptr>(edge_->end)->ccomponents();
Eigen::Vector3d v = e - s;
auto l = v.norm(); // the norm of a vector is a measure of its length
v.normalize(); // normalize the vector so that it is a unit direction vector
std::function<Eigen::Matrix4d(double)> fn = [s, v](double u) {
Eigen::Vector3d o(s + u * v), axis(0, 0, 1), refDirection(v);
auto Y = axis.cross(refDirection).normalized();
axis = refDirection.cross(Y).normalized();
return make<matrix4>(o, axis, refDirection)->components();
};
spans.emplace_back(taxonomy::make<taxonomy::functor_item>(l, fn));
} else {
Logger::Message(Logger::Severity::LOG_ERROR, "Basis curve not supported");
return boost::none;
}
}
pwf_ = make<piecewise_function>(0.0,spans);
loop_->pwf = pwf_;
fi_ = make<piecewise_function>(0.0,spans);
loop_->fi = fi_;
}
}
return pwf_;
return fi_;
}
+13 -13
View File
@@ -892,7 +892,7 @@ typedef item const* ptr;
DECLARE_PTR(loop)
boost::optional<bool> external, closed;
boost::optional<taxonomy::piecewise_function::ptr> pwf;
boost::optional<taxonomy::function_item::ptr> fi;
bool is_polyhedron() const {
for (auto& e : children) {
@@ -1374,27 +1374,27 @@ typedef item const* ptr;
}
};
boost::optional<piecewise_function::ptr> loop_to_piecewise_function_upgrade_impl(ptr item);
boost::optional<function_item::ptr> loop_to_function_item_upgrade_impl(ptr item);
template <typename T>
class loop_to_piecewise_function_upgrade {
class loop_to_function_item_upgrade {
private:
boost::optional<taxonomy::piecewise_function::ptr> pwf_;
boost::optional<taxonomy::function_item::ptr> fi_;
public:
loop_to_piecewise_function_upgrade(taxonomy::ptr item) {
if constexpr (std::is_same_v<T, piecewise_function>) {
pwf_ = loop_to_piecewise_function_upgrade_impl(item);
loop_to_function_item_upgrade(taxonomy::ptr item) {
if constexpr (std::is_same_v<T, function_item>) {
fi_ = loop_to_function_item_upgrade_impl(item);
}
}
operator bool() const {
return pwf_.is_initialized();
return fi_.is_initialized();
}
operator typename T::ptr() const {
if constexpr (std::is_same_v<T, piecewise_function>) {
if (pwf_) {
return *pwf_;
if constexpr (std::is_same_v<T, function_item>) {
if (fi_) {
return *fi_;
}
}
return nullptr;
@@ -1435,7 +1435,7 @@ typedef item const* ptr;
}
}
{
loop_to_piecewise_function_upgrade<T> upg(u);
loop_to_function_item_upgrade<T> upg(u);
if (upg) {
return upg;
}
@@ -1479,7 +1479,7 @@ typedef item const* ptr;
}
}
{
loop_to_piecewise_function_upgrade<T> upg(u);
loop_to_function_item_upgrade<T> upg(u);
if (upg) {
return upg;
}