mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-12 06:32:09 +00:00
Merge branch 'v0.8.0' into tfk-rocksdb-storage
This commit is contained in:
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
>
|
||||
{};
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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_;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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_; }
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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] });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user