mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-10 06:00:51 +00:00
Merge branch 'v0.8.0' into v0.8.0
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
#include "../ifcgeom/IfcGeomElement.h"
|
||||
#include "../ifcgeom/ConversionSettings.h"
|
||||
#include "../ifcgeom/abstract_mapping.h"
|
||||
#include "../ifcgeom/piecewise_function_evaluator.h"
|
||||
|
||||
#ifdef IFOPSH_WITH_OPENCASCADE
|
||||
#include "../ifcgeom/kernels/opencascade/OpenCascadeKernel.h"
|
||||
@@ -29,8 +30,15 @@ bool ifcopenshell::geometry::kernels::AbstractKernel::convert(const taxonomy::pt
|
||||
try {
|
||||
return dispatch_conversion<0>::dispatch(this, item->kind(), item, results);
|
||||
} catch (std::exception& e) {
|
||||
Logger::Error(e, item->instance);
|
||||
return false;
|
||||
std::string prev_exception = std::string(e.what());
|
||||
try {
|
||||
return dispatch_with_upgrade<0>::dispatch(this, item, results);
|
||||
} catch (std::exception& e) {
|
||||
Logger::Error(prev_exception + " Conversion for upgraded element failed with: " + std::string(e.what()), item->instance);
|
||||
return false;
|
||||
} catch (...) {
|
||||
return false;
|
||||
}
|
||||
} catch (...) {
|
||||
// @todo we can't log OCCT exceptions here, can we do some reraising to solve this?
|
||||
return false;
|
||||
@@ -220,7 +228,8 @@ bool ifcopenshell::geometry::kernels::AbstractKernel::convert_impl(const taxonom
|
||||
}
|
||||
|
||||
bool ifcopenshell::geometry::kernels::AbstractKernel::convert_impl(const taxonomy::piecewise_function::ptr item, IfcGeom::ConversionResults& cs) {
|
||||
auto expl = item->evaluate();
|
||||
piecewise_function_evaluator evaluator(item);
|
||||
auto expl = evaluator.evaluate();
|
||||
expl->instance = item->instance;
|
||||
return convert(expl, cs);
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ namespace {
|
||||
struct dispatch_conversion {
|
||||
static bool dispatch(ifcopenshell::geometry::kernels::AbstractKernel* kernel, ifcopenshell::geometry::taxonomy::kinds item_kind, const ifcopenshell::geometry::taxonomy::ptr item, IfcGeom::ConversionResults& results) {
|
||||
if (N == item_kind) {
|
||||
auto concrete_item = ifcopenshell::geometry::taxonomy::template cast<ifcopenshell::geometry::taxonomy::type_by_kind::type<N>>(item);
|
||||
auto concrete_item = std::static_pointer_cast<ifcopenshell::geometry::taxonomy::type_by_kind::type<N>>(item);
|
||||
return kernel->convert_impl(concrete_item, results);
|
||||
} else {
|
||||
return dispatch_conversion<N + 1>::dispatch(kernel, item_kind, item, results);
|
||||
@@ -102,6 +102,26 @@ namespace {
|
||||
}
|
||||
};
|
||||
|
||||
template <size_t N>
|
||||
struct dispatch_with_upgrade {
|
||||
static bool dispatch(ifcopenshell::geometry::kernels::AbstractKernel* kernel, const ifcopenshell::geometry::taxonomy::ptr item, IfcGeom::ConversionResults& results) {
|
||||
auto concrete_item = ifcopenshell::geometry::taxonomy::template dcast<ifcopenshell::geometry::taxonomy::upgrades::type<N>>(item);
|
||||
if (concrete_item) {
|
||||
return kernel->convert_impl(concrete_item, results);
|
||||
} else {
|
||||
return dispatch_with_upgrade<N + 1>::dispatch(kernel, item, results);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct dispatch_with_upgrade<ifcopenshell::geometry::taxonomy::upgrades::max> {
|
||||
static bool dispatch(ifcopenshell::geometry::kernels::AbstractKernel*, const ifcopenshell::geometry::taxonomy::ptr item, IfcGeom::ConversionResults&) {
|
||||
Logger::Error("No conversion with upgrade for " + std::to_string(item->kind()));
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
template <class T, class Tuple>
|
||||
struct TupleTypeIndex;
|
||||
|
||||
|
||||
@@ -1,37 +1,34 @@
|
||||
#include "ConversionSettings.h"
|
||||
|
||||
/*
|
||||
void ifcopenshell::geometry::ConversionSettings::setValue(GeomValue var, double value) {
|
||||
values_[var] = value;
|
||||
}
|
||||
|
||||
double ifcopenshell::geometry::ConversionSettings::getValue(GeomValue var) const {
|
||||
return values_[var];
|
||||
}
|
||||
*/
|
||||
|
||||
template <typename T>
|
||||
void istream_helper(std::istream& in, std::set<T>& ints) {
|
||||
void istream_helper(std::istream& in, T& vs) {
|
||||
std::string tokens;
|
||||
in >> tokens;
|
||||
std::vector<std::string> strs;
|
||||
boost::split(strs, tokens, boost::is_any_of(","));
|
||||
for (auto& s : strs) {
|
||||
if constexpr (std::is_same_v<T, std::string>) {
|
||||
ints.insert(s);
|
||||
} else {
|
||||
ints.insert(boost::lexical_cast<T>(s));
|
||||
if constexpr (std::is_same_v<std::decay_t<T>, std::set<std::string>>) {
|
||||
vs.insert(s);
|
||||
} else if constexpr (std::is_same_v<std::decay_t<T>, std::set<int>>) {
|
||||
vs.insert(boost::lexical_cast<typename T::value_type>(s));
|
||||
} else if constexpr (std::is_same_v<std::decay_t<T>, std::vector<double>>) {
|
||||
vs.push_back(boost::lexical_cast<typename T::value_type>(s));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::istream& std::operator>>(istream& in, set<int>& ints) {
|
||||
istream_helper<int>(in, ints);
|
||||
istream_helper<std::set<int>>(in, ints);
|
||||
return in;
|
||||
}
|
||||
|
||||
std::istream& std::operator>>(istream& in, set<string>& strs) {
|
||||
istream_helper<std::string>(in, strs);
|
||||
istream_helper<std::set<std::string>>(in, strs);
|
||||
return in;
|
||||
}
|
||||
|
||||
std::istream& std::operator>>(istream& in, vector<double>& ds) {
|
||||
istream_helper<std::vector<double>>(in, ds);
|
||||
return in;
|
||||
}
|
||||
|
||||
@@ -81,3 +78,19 @@ std::istream& ifcopenshell::geometry::settings::operator>>(std::istream& in, Out
|
||||
}
|
||||
return in;
|
||||
}
|
||||
|
||||
std::istream& ifcopenshell::geometry::settings::operator>>(std::istream& in, TriangulationMethod& v) {
|
||||
std::string token;
|
||||
in >> token;
|
||||
boost::to_upper(token);
|
||||
if (token == "TRIANGLE_MESH") {
|
||||
v = TRIANGLE_MESH;
|
||||
} else if (token == "POLYHEDRON_WITHOUT_HOLES") {
|
||||
v = POLYHEDRON_WITHOUT_HOLES;
|
||||
} else if (token == "POLYHEDRON_WITH_HOLES") {
|
||||
v = POLYHEDRON_WITH_HOLES;
|
||||
} else {
|
||||
in.setstate(std::ios_base::failbit);
|
||||
}
|
||||
return in;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ namespace po = boost::program_options;
|
||||
namespace std {
|
||||
istream& operator>>(istream& in, set<int>& ints);
|
||||
istream& operator>>(istream& in, set<string>& ints);
|
||||
istream& operator>>(istream& in, vector<double>& vs);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -43,7 +44,10 @@ namespace ifcopenshell {
|
||||
struct SettingBase {
|
||||
typedef T base_type;
|
||||
|
||||
boost::optional<T> value;
|
||||
// boost program options does not seem to handle optional<vector> types, so in case
|
||||
// of vector settings we need to strip away the optional and detect argument presence
|
||||
// with !vector::empty()
|
||||
std::conditional_t<std::is_same_v<T, std::vector<double>>, T, boost::optional<T>> value;
|
||||
|
||||
SettingBase() {}
|
||||
|
||||
@@ -59,24 +63,34 @@ namespace ifcopenshell {
|
||||
// @todo bool_switch doesn't work with optional unfortunately...
|
||||
value.emplace();
|
||||
desc.add_options()(Derived::name, apply_default(po::bool_switch(&*value)), Derived::description);
|
||||
} else if constexpr (std::is_same_v<T, std::vector<double>>) {
|
||||
desc.add_options()(Derived::name, apply_default(po::value(&value)->multitoken()), Derived::description);
|
||||
} else {
|
||||
desc.add_options()(Derived::name, apply_default(po::value(&value)), Derived::description);
|
||||
}
|
||||
}
|
||||
|
||||
T get() const {
|
||||
if (value) {
|
||||
return value.get();
|
||||
if constexpr (std::is_same_v<T, std::vector<double>>) {
|
||||
return value;
|
||||
} else {
|
||||
if (value) {
|
||||
return value.get();
|
||||
}
|
||||
if constexpr (HasDefault<Derived>()) {
|
||||
return Derived::defaultvalue;
|
||||
}
|
||||
throw std::runtime_error("Setting not set");
|
||||
}
|
||||
if constexpr (HasDefault<Derived>()) {
|
||||
return Derived::defaultvalue;
|
||||
}
|
||||
throw std::runtime_error("Setting not set");
|
||||
}
|
||||
|
||||
bool has() const {
|
||||
// @todo this is not reliable, better use vmap[...].defaulted()
|
||||
return !!value;
|
||||
if constexpr (std::is_same_v<T, std::vector<double>>) {
|
||||
return !value.empty();
|
||||
} else {
|
||||
// @todo this is not reliable, better use vmap[...].defaulted()
|
||||
return !!value;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -361,12 +375,38 @@ namespace ifcopenshell {
|
||||
static constexpr const char* const description = "Indicates the parameter value for defining step size when evaluating piecewise curves.";
|
||||
static constexpr double defaultvalue = 0.5; // ceiling of this value is used when PiecewiseStepMethod is MinSteps
|
||||
};
|
||||
|
||||
struct ModelOffset : public SettingBase<ModelOffset, std::vector<double>> {
|
||||
static constexpr const char* const name = "model-offset";
|
||||
static constexpr const char* const description = "Applies an arbitrary offset of form 'x,y,z' to all placements.";
|
||||
};
|
||||
|
||||
struct ModelRotation : public SettingBase<ModelRotation, std::vector<double>> {
|
||||
static constexpr const char* const name = "model-rotation";
|
||||
static constexpr const char* const description = "Applies an arbitrary quaternion rotation of form 'x,y,z,w' to all placements.";
|
||||
};
|
||||
|
||||
enum TriangulationMethod {
|
||||
TRIANGLE_MESH,
|
||||
POLYHEDRON_WITHOUT_HOLES,
|
||||
POLYHEDRON_WITH_HOLES
|
||||
};
|
||||
|
||||
std::istream& operator>>(std::istream& in, TriangulationMethod& ioo);
|
||||
|
||||
struct TriangulationType : public SettingBase<TriangulationType, TriangulationMethod> {
|
||||
static constexpr const char* const name = "triangulation-type";
|
||||
static constexpr const char* const description = "Type of planar facet to be emitted";
|
||||
static constexpr TriangulationMethod defaultvalue = TRIANGLE_MESH;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
template <typename settings_t>
|
||||
class IFC_GEOM_API SettingsContainer {
|
||||
public:
|
||||
typedef boost::variant<bool, int, double, std::string, std::set<int>, std::set<std::string>, IteratorOutputOptions, PiecewiseStepMethod, OutputDimensionalityTypes> value_variant_t;
|
||||
typedef boost::variant<bool, int, double, std::string, std::set<int>, std::set<std::string>, std::vector<double>, IteratorOutputOptions, PiecewiseStepMethod, OutputDimensionalityTypes, TriangulationMethod> value_variant_t;
|
||||
private:
|
||||
settings_t settings;
|
||||
|
||||
@@ -453,73 +493,12 @@ 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, UseMaterialNames, ConvertBackUnits, ContextIds, ContextTypes, ContextIdentifiers, IteratorOutput, DisableOpeningSubtractions, ApplyDefaultMaterials, DontEmitNormals, GenerateUvs, ApplyLayerSets, UseElementHierarchy, ValidateQuantities, EdgeArrows, BuildingLocalPlacement, SiteLocalPlacement, ForceSpaceTransparency, CircleSegments, KeepBoundingBoxes, PiecewiseStepType, PiecewiseStepParam, NoParallelMapping>
|
||||
std::tuple<MesherLinearDeflection, MesherAngularDeflection, ReorientShells, LengthUnit, PlaneUnit, Precision, OutputDimensionality, LayersetFirst, DisableBooleanResult, NoWireIntersectionCheck, NoWireIntersectionTolerance, PrecisionFactor, DebugBooleanOperations, BooleanAttempt2d, SurfaceColour, WeldVertices, UseWorldCoords, UseMaterialNames, ConvertBackUnits, ContextIds, ContextTypes, ContextIdentifiers, IteratorOutput, DisableOpeningSubtractions, ApplyDefaultMaterials, DontEmitNormals, GenerateUvs, ApplyLayerSets, UseElementHierarchy, ValidateQuantities, EdgeArrows, BuildingLocalPlacement, SiteLocalPlacement, ForceSpaceTransparency, CircleSegments, KeepBoundingBoxes, PiecewiseStepType, PiecewiseStepParam, NoParallelMapping, ModelOffset, ModelRotation, TriangulationType>
|
||||
>
|
||||
{};
|
||||
}
|
||||
}
|
||||
|
||||
// namespace ifcopenshell {
|
||||
// namespace geometry {
|
||||
//
|
||||
// class IFC_GEOM_API ConversionSettings {
|
||||
// public:
|
||||
// // Tolerances and settings for various geometrical operations:
|
||||
// enum GeomValue {
|
||||
// //
|
||||
// // Default: 0.001m / 1mm
|
||||
// GV_DEFLECTION_TOLERANCE,
|
||||
//
|
||||
// // The length unit used the creation of TopoDS_Shapes, primarily affects the
|
||||
// // interpretation of IfcCartesianPoints and IfcVector magnitudes
|
||||
// // DefaultL 1.0
|
||||
// GV_LENGTH_UNIT,
|
||||
// // The plane angle unit used for the creation of TopoDS_Shapes, primarily affects
|
||||
// // the interpretation of IfcParamaterValues of IfcTrimmedCurves
|
||||
// // Default: -1.0 (= not set, fist try degrees, then radians)
|
||||
// GV_PLANEANGLE_UNIT,
|
||||
// // The precision used in boolean operations, setting this value too low results
|
||||
// // in artefacts and potentially modelling failures
|
||||
// // Default: 0.00001 (obtained from IfcGeometricRepresentationContext if available)
|
||||
// GV_PRECISION,
|
||||
// // Whether to process shapes of type Face or higher (1) Wire or lower (-1) or all (0)
|
||||
// GV_DIMENSIONALITY,
|
||||
// GV_LAYERSET_FIRST,
|
||||
// GV_DISABLE_BOOLEAN_RESULT,
|
||||
// GV_NO_WIRE_INTERSECTION_CHECK,
|
||||
// GV_PRECISION_FACTOR,
|
||||
// GV_NO_WIRE_INTERSECTION_TOLERANCE,
|
||||
// GV_DEBUG_BOOLEAN,
|
||||
// GV_BOOLEAN_ATTEMPT_2D,
|
||||
// NUM_SETTINGS
|
||||
// };
|
||||
//
|
||||
// void setValue(GeomValue var, double value);
|
||||
//
|
||||
// double getValue(GeomValue var) const;
|
||||
//
|
||||
// private:
|
||||
// std::array<double, NUM_SETTINGS> values_ = {
|
||||
// /* deflection_tolerance = */ 0.001,
|
||||
// // @todo make sure these 'read-only' variables work.
|
||||
// /* minimal_face_area = */ std::numeric_limits<double>::quiet_NaN(),
|
||||
// /* max_faces_to_orient = */ -1.0,
|
||||
// /* ifc_length_unit = */ 1.0,
|
||||
// /* ifc_planeangle_unit = */ -1.0,
|
||||
// /* modelling_precision = */ 0.00001,
|
||||
// /* dimensionality = */ 1.,
|
||||
// /* layerset_first = */ -1.,
|
||||
// /* disable_boolean_result = */ -1.
|
||||
// /* no_wire_intersection_check = */ -1.,
|
||||
// /* precision_factor = */ 10.,
|
||||
// /* no_wire_intersection_tolerance = */ -1.,
|
||||
// /* boolean_debug_setting = */ -1.,
|
||||
// /* boolean_attempt_2d = */ 1.
|
||||
// };
|
||||
// };
|
||||
// }
|
||||
// }
|
||||
|
||||
// @todo find a place
|
||||
namespace IfcGeom {
|
||||
class IFC_GEOM_API geometry_exception : public std::exception {
|
||||
|
||||
@@ -142,8 +142,7 @@ namespace {
|
||||
#endif
|
||||
|
||||
IfcGeom::Representation::Serialization::Serialization(const BRep& brep)
|
||||
: Representation(brep.settings(), brep.entity())
|
||||
, id_(brep.id())
|
||||
: Representation(brep.settings(), brep.entity(), brep.id())
|
||||
{
|
||||
for (auto it = brep.begin(); it != brep.end(); ++it) {
|
||||
int sid = -1;
|
||||
@@ -323,8 +322,7 @@ bool IfcGeom::Representation::BRep::calculate_projected_surface_area(const ifcop
|
||||
}
|
||||
|
||||
IfcGeom::Representation::Triangulation::Triangulation(const BRep& shape_model)
|
||||
: Representation(shape_model.settings(), shape_model.entity())
|
||||
, id_(shape_model.id())
|
||||
: Representation(shape_model.settings(), shape_model.entity(), shape_model.id())
|
||||
, weld_offset_(0)
|
||||
{
|
||||
for (IfcGeom::ConversionResults::const_iterator iit = shape_model.begin(); iit != shape_model.end(); ++iit) {
|
||||
@@ -334,23 +332,23 @@ IfcGeom::Representation::Triangulation::Triangulation(const BRep& shape_model)
|
||||
|
||||
int surface_style_id = -1;
|
||||
if (iit->hasStyle()) {
|
||||
auto jt = std::find(_materials.begin(), _materials.end(), iit->StylePtr());
|
||||
if (jt == _materials.end()) {
|
||||
surface_style_id = (int)_materials.size();
|
||||
_materials.push_back(iit->StylePtr());
|
||||
auto jt = std::find(materials_.begin(), materials_.end(), iit->StylePtr());
|
||||
if (jt == materials_.end()) {
|
||||
surface_style_id = (int)materials_.size();
|
||||
materials_.push_back(iit->StylePtr());
|
||||
} else {
|
||||
surface_style_id = (int)(jt - _materials.begin());
|
||||
surface_style_id = (int)(jt - materials_.begin());
|
||||
}
|
||||
}
|
||||
|
||||
if (settings().get<ifcopenshell::geometry::settings::ApplyDefaultMaterials>().get() && surface_style_id == -1) {
|
||||
const auto& material = IfcGeom::get_default_style(shape_model.entity());
|
||||
auto mit = std::find(_materials.begin(), _materials.end(), material);
|
||||
if (mit == _materials.end()) {
|
||||
surface_style_id = (int)_materials.size();
|
||||
_materials.push_back(material);
|
||||
auto mit = std::find(materials_.begin(), materials_.end(), material);
|
||||
if (mit == materials_.end()) {
|
||||
surface_style_id = (int)materials_.size();
|
||||
materials_.push_back(material);
|
||||
} else {
|
||||
surface_style_id = (int)(mit - _materials.begin());
|
||||
surface_style_id = (int)(mit - materials_.begin());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -395,7 +393,7 @@ int IfcGeom::Representation::Triangulation::addVertex(int item_id, int material_
|
||||
const double X = convert ? (pX /unit_magnitude) : pX;
|
||||
const double Y = convert ? (pY /unit_magnitude) : pY;
|
||||
const double Z = convert ? (pZ /unit_magnitude) : pZ;
|
||||
int i = (int)_verts.size() / 3;
|
||||
int i = (int)verts_.size() / 3;
|
||||
if (settings().get<ifcopenshell::geometry::settings::WeldVertices>().get()) {
|
||||
const VertexKey key = std::make_tuple(item_id, material_index, X, Y, Z);
|
||||
typename VertexKeyMap::const_iterator it = welds.find(key);
|
||||
@@ -403,13 +401,13 @@ int IfcGeom::Representation::Triangulation::addVertex(int item_id, int material_
|
||||
i = (int)(welds.size() + weld_offset_);
|
||||
welds[key] = i;
|
||||
}
|
||||
_verts.push_back(X);
|
||||
_verts.push_back(Y);
|
||||
_verts.push_back(Z);
|
||||
verts_.push_back(X);
|
||||
verts_.push_back(Y);
|
||||
verts_.push_back(Z);
|
||||
return i;
|
||||
}
|
||||
|
||||
void IfcGeom::Representation::Triangulation::addEdge(int n1, int n2, std::map<std::pair<int, int>, int>& edgecount) {
|
||||
void IfcGeom::Representation::Triangulation::registerEdgeCount(int n1, int n2, std::map<std::pair<int, int>, int>& edgecount) {
|
||||
const Edge e = Edge((std::min)(n1, n2), (std::max)(n1, n2));
|
||||
edgecount[e] ++;
|
||||
}
|
||||
|
||||
@@ -35,35 +35,39 @@ namespace IfcGeom {
|
||||
protected:
|
||||
const ifcopenshell::geometry::Settings settings_;
|
||||
const std::string entity_;
|
||||
std::string id_;
|
||||
public:
|
||||
explicit Representation(const ifcopenshell::geometry::Settings& settings, const std::string& entity)
|
||||
explicit Representation(const ifcopenshell::geometry::Settings& settings, const std::string& entity, const std::string& id)
|
||||
: settings_(settings)
|
||||
, entity_(entity)
|
||||
, id_(id)
|
||||
{}
|
||||
const ifcopenshell::geometry::Settings& settings() const { return settings_; }
|
||||
const std::string& entity() const {
|
||||
return entity_;
|
||||
}
|
||||
// id starts with representation id and then it may have the following dash separated elements:
|
||||
// - layerset-layerset_id
|
||||
// - material-material_id
|
||||
// - openings-opening0_id-...-openingN_id
|
||||
const std::string& id() const { return id_; }
|
||||
virtual ~Representation() {}
|
||||
};
|
||||
|
||||
class IFC_GEOM_API BRep : public Representation {
|
||||
private:
|
||||
std::string id_;
|
||||
const IfcGeom::ConversionResults shapes_;
|
||||
BRep(const BRep& other);
|
||||
BRep& operator=(const BRep& other);
|
||||
public:
|
||||
BRep(const ifcopenshell::geometry::Settings& settings, const std::string& entity, const std::string& id, const IfcGeom::ConversionResults& shapes)
|
||||
: Representation(settings, entity)
|
||||
, id_(id)
|
||||
: Representation(settings, entity, id)
|
||||
, shapes_(shapes)
|
||||
{}
|
||||
virtual ~BRep() {}
|
||||
IfcGeom::ConversionResults::const_iterator begin() const { return shapes_.begin(); }
|
||||
IfcGeom::ConversionResults::const_iterator end() const { return shapes_.end(); }
|
||||
const IfcGeom::ConversionResults& shapes() const { return shapes_; }
|
||||
const std::string& id() const { return id_; }
|
||||
IfcGeom::ConversionResultShape* as_compound(bool force_meters = false) const;
|
||||
|
||||
bool calculate_volume(double&) const;
|
||||
@@ -77,7 +81,6 @@ namespace IfcGeom {
|
||||
|
||||
class IFC_GEOM_API Serialization : public Representation {
|
||||
private:
|
||||
std::string id_;
|
||||
std::string brep_data_;
|
||||
std::vector<double> surface_styles_;
|
||||
std::vector<int> surface_style_ids_;
|
||||
@@ -87,7 +90,6 @@ namespace IfcGeom {
|
||||
const std::vector<int>& surface_style_ids() const { return surface_style_ids_; }
|
||||
Serialization(const BRep& brep);
|
||||
virtual ~Serialization() {}
|
||||
const std::string& id() const { return id_; }
|
||||
private:
|
||||
Serialization();
|
||||
Serialization(const Serialization&);
|
||||
@@ -101,34 +103,42 @@ namespace IfcGeom {
|
||||
typedef std::map<VertexKey, int> VertexKeyMap;
|
||||
typedef std::pair<int, int> Edge;
|
||||
|
||||
std::string id_;
|
||||
std::vector<double> _verts;
|
||||
std::vector<int> _faces;
|
||||
std::vector<int> _edges;
|
||||
std::vector<double> _normals;
|
||||
std::vector<double> verts_;
|
||||
|
||||
// @nb only one of these is populated based on settings, we didn't want to go
|
||||
// all in with templates or subtypes because of reduced ease of use.
|
||||
std::vector<int> faces_;
|
||||
std::vector<std::vector<int>> polyhedral_faces_without_holes_;
|
||||
std::vector<std::vector<std::vector<int>>> polyhedral_faces_with_holes_;
|
||||
|
||||
std::vector<int> edges_;
|
||||
std::vector<double> normals_;
|
||||
std::vector<double> uvs_;
|
||||
std::vector<int> _material_ids;
|
||||
std::vector<ifcopenshell::geometry::taxonomy::style::ptr> _materials;
|
||||
std::vector<int> _item_ids;
|
||||
std::vector<int> material_ids_;
|
||||
std::vector<ifcopenshell::geometry::taxonomy::style::ptr> materials_;
|
||||
std::vector<int> item_ids_;
|
||||
std::vector<int> edges_item_ids_;
|
||||
size_t weld_offset_;
|
||||
VertexKeyMap welds;
|
||||
|
||||
Triangulation(const ifcopenshell::geometry::Settings& settings, const std::string& entity)
|
||||
: Representation(settings, entity)
|
||||
Triangulation(const ifcopenshell::geometry::Settings& settings, const std::string& entity, const std::string& id)
|
||||
: Representation(settings, entity, id)
|
||||
, weld_offset_(0)
|
||||
{}
|
||||
|
||||
public:
|
||||
const std::string& id() const { return id_; }
|
||||
const std::vector<double>& verts() const { return _verts; }
|
||||
const std::vector<int>& faces() const { return _faces; }
|
||||
const std::vector<int>& edges() const { return _edges; }
|
||||
const std::vector<double>& normals() const { return _normals; }
|
||||
const std::vector<double>& verts() const { return verts_; }
|
||||
const std::vector<int>& faces() const { return faces_; }
|
||||
const std::vector<std::vector<int>>& polyhedral_faces_without_holes() const { return polyhedral_faces_without_holes_; }
|
||||
const std::vector<std::vector<std::vector<int>>>& polyhedral_faces_with_holes() const { return polyhedral_faces_with_holes_; }
|
||||
const std::vector<int>& edges() const { return edges_; }
|
||||
const std::vector<double>& normals() const { return normals_; }
|
||||
std::vector<double>& uvs() { return uvs_; }
|
||||
const std::vector<double>& uvs() const { return uvs_; }
|
||||
const std::vector<int>& material_ids() const { return _material_ids; }
|
||||
const std::vector<ifcopenshell::geometry::taxonomy::style::ptr>& materials() const { return _materials; }
|
||||
const std::vector<int>& item_ids() const { return _item_ids; }
|
||||
const std::vector<int>& material_ids() const { return material_ids_; }
|
||||
const std::vector<ifcopenshell::geometry::taxonomy::style::ptr>& materials() const { return materials_; }
|
||||
const std::vector<int>& item_ids() const { return item_ids_; }
|
||||
const std::vector<int>& edges_item_ids() const { return edges_item_ids_; }
|
||||
|
||||
Triangulation(const BRep& shape_model);
|
||||
|
||||
@@ -144,17 +154,18 @@ namespace IfcGeom {
|
||||
const std::vector<int>& material_ids,
|
||||
const std::vector<ifcopenshell::geometry::taxonomy::style::ptr>& materials,
|
||||
const std::vector<int>& item_ids
|
||||
, const std::vector<int>& edges_item_ids
|
||||
)
|
||||
: Representation(settings, entity)
|
||||
, id_(id)
|
||||
, _verts(verts)
|
||||
, _faces(faces)
|
||||
, _edges(edges)
|
||||
, _normals(normals)
|
||||
: Representation(settings, entity, id)
|
||||
, verts_(verts)
|
||||
, faces_(faces)
|
||||
, edges_(edges)
|
||||
, normals_(normals)
|
||||
, uvs_(uvs)
|
||||
, _material_ids(material_ids)
|
||||
, _materials(materials)
|
||||
, _item_ids(item_ids)
|
||||
, material_ids_(material_ids)
|
||||
, materials_(materials)
|
||||
, item_ids_(item_ids)
|
||||
, edges_item_ids_(edges_item_ids)
|
||||
{}
|
||||
|
||||
virtual ~Triangulation() {}
|
||||
@@ -163,39 +174,55 @@ namespace IfcGeom {
|
||||
/// @todo Very simple impl. Assumes that input vertices and normals match 1:1.
|
||||
static std::vector<double> box_project_uvs(const std::vector<double> &vertices, const std::vector<double> &normals);
|
||||
|
||||
static Triangulation* empty(const ifcopenshell::geometry::Settings& settings) { return new Triangulation(settings, ""); }
|
||||
static Triangulation* empty(const ifcopenshell::geometry::Settings& settings) { return new Triangulation(settings, "", ""); }
|
||||
|
||||
/// Welds vertices that belong to different faces
|
||||
int addVertex(int item_index, int material_index, double X, double Y, double Z);
|
||||
|
||||
void addNormal(double X, double Y, double Z) {
|
||||
_normals.push_back(X);
|
||||
_normals.push_back(Y);
|
||||
_normals.push_back(Z);
|
||||
normals_.push_back(X);
|
||||
normals_.push_back(Y);
|
||||
normals_.push_back(Z);
|
||||
}
|
||||
|
||||
void addFace(int item_id, int style, int i0, int i1, int i2) {
|
||||
_faces.push_back(i0);
|
||||
_faces.push_back(i1);
|
||||
_faces.push_back(i2);
|
||||
faces_.push_back(i0);
|
||||
faces_.push_back(i1);
|
||||
faces_.push_back(i2);
|
||||
|
||||
_item_ids.push_back(item_id);
|
||||
_material_ids.push_back(style);
|
||||
item_ids_.push_back(item_id);
|
||||
material_ids_.push_back(style);
|
||||
}
|
||||
|
||||
void addEdge(int style, int i0, int i1) {
|
||||
_edges.push_back(i0);
|
||||
_edges.push_back(i1);
|
||||
void addFace(int item_id, int style, const std::vector<int>& outer_bound) {
|
||||
polyhedral_faces_without_holes_.push_back(outer_bound);
|
||||
|
||||
_material_ids.push_back(style);
|
||||
item_ids_.push_back(item_id);
|
||||
material_ids_.push_back(style);
|
||||
}
|
||||
|
||||
void registerEdge(int i0, int i1) {
|
||||
_edges.push_back(i0);
|
||||
_edges.push_back(i1);
|
||||
void addFace(int item_id, int style, const std::vector<std::vector<int>>& bounds) {
|
||||
polyhedral_faces_with_holes_.push_back(bounds);
|
||||
|
||||
item_ids_.push_back(item_id);
|
||||
material_ids_.push_back(style);
|
||||
}
|
||||
|
||||
void addEdge(int n1, int n2, std::map<std::pair<int, int>, int>& edgecount);
|
||||
void addEdge(int item_id, int style, int i0, int i1) {
|
||||
edges_.push_back(i0);
|
||||
edges_.push_back(i1);
|
||||
|
||||
material_ids_.push_back(style);
|
||||
edges_item_ids_.push_back(item_id);
|
||||
}
|
||||
|
||||
void registerEdge(int item_id, int i0, int i1) {
|
||||
edges_.push_back(i0);
|
||||
edges_.push_back(i1);
|
||||
edges_item_ids_.push_back(item_id);
|
||||
}
|
||||
|
||||
void registerEdgeCount(int n1, int n2, std::map<std::pair<int, int>, int>& edgecount);
|
||||
|
||||
void resetWelds() {
|
||||
weld_offset_ += welds.size();
|
||||
|
||||
@@ -701,6 +701,10 @@ namespace IfcGeom {
|
||||
/// Gets the representation of the current geometrical entity.
|
||||
Element* get()
|
||||
{
|
||||
if (!initialization_outcome_) {
|
||||
throw std::runtime_error("Iterator not initialized");
|
||||
}
|
||||
|
||||
auto ret = *task_result_iterator_;
|
||||
|
||||
// If we want to organize the element considering their hierarchy
|
||||
|
||||
@@ -0,0 +1,203 @@
|
||||
#include "profile_helper.h"
|
||||
#include "infra_sweep_helper.h"
|
||||
#include "piecewise_function_evaluator.h"
|
||||
|
||||
#include <boost/range/combine.hpp>
|
||||
|
||||
using namespace ifcopenshell::geometry;
|
||||
|
||||
namespace {
|
||||
// std::lerp when upgrading to C++ 20
|
||||
template <typename T>
|
||||
T lerp(const T& a, const T& b, double t) {
|
||||
return a + t * (b - a);
|
||||
}
|
||||
}
|
||||
|
||||
taxonomy::loft::ptr ifcopenshell::geometry::make_loft(const Settings& settings_, const IfcUtil::IfcBaseClass* inst, const taxonomy::piecewise_function::ptr& pwf, std::vector<cross_section>& cross_sections)
|
||||
{
|
||||
std::sort(cross_sections.begin(), cross_sections.end());
|
||||
|
||||
auto loft = taxonomy::make<taxonomy::loft>();
|
||||
// @todo intialize as default
|
||||
loft->axis = nullptr;
|
||||
|
||||
// @todo currently only the case is handled where directrix returns a piecewise_function
|
||||
// @todo this "if" statement is not really required because the function returns at the start if the Directrix is not a piecewise function
|
||||
if (pwf) {
|
||||
piecewise_function_evaluator evaluator(pwf, &settings_);
|
||||
double start = std::max(0., cross_sections.front().dist_along);
|
||||
double end = std::min(pwf->length(), cross_sections.back().dist_along);
|
||||
|
||||
if (end - start < 1.e-9) {
|
||||
Logger::Warning("Empty sweep domain with start at " + std::to_string(cross_sections.front().dist_along) + " end at " + std::to_string(cross_sections.back().dist_along) + " and curve domain length " + std::to_string(pwf->length()), inst);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto curve_length = end - start;
|
||||
auto param_type = settings_.get<ifcopenshell::geometry::settings::PiecewiseStepType>().get();
|
||||
auto param = settings_.get<ifcopenshell::geometry::settings::PiecewiseStepParam>().get();
|
||||
size_t num_steps = 0;
|
||||
if (param_type == ifcopenshell::geometry::settings::PiecewiseStepMethod::MAXSTEPSIZE) {
|
||||
// parameter is max step size
|
||||
num_steps = (size_t)std::ceil(curve_length / param);
|
||||
} else {
|
||||
// parameter is minimum number of steps
|
||||
num_steps = (size_t)std::ceil(param);
|
||||
}
|
||||
std::vector<double> longitudes;
|
||||
for (auto& x : cross_sections) {
|
||||
longitudes.push_back(x.dist_along);
|
||||
}
|
||||
longitudes.push_back(std::numeric_limits<double>::infinity());
|
||||
auto profile_index = longitudes.begin();
|
||||
for (size_t i = 0; i <= num_steps; ++i) {
|
||||
auto dist_along = start + curve_length / num_steps * i;
|
||||
while (dist_along > *(profile_index + 1)) {
|
||||
profile_index++;
|
||||
if (profile_index == longitudes.end()) {
|
||||
// @todo handle this?
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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.
|
||||
bool should_interpolate =
|
||||
(profile_index + 1 < longitudes.end()) &&
|
||||
(relative_dist_along >= 1.e-9 || offset_a.cwiseAbs().maxCoeff() > 0.);
|
||||
|
||||
if (should_interpolate) {
|
||||
taxonomy::geom_item::ptr profile_b;
|
||||
Eigen::Vector3d offset_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;
|
||||
} else {
|
||||
profile_b = profile_a;
|
||||
offset_b = offset_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.);
|
||||
|
||||
if (should_interpolate2) {
|
||||
|
||||
std::vector<taxonomy::loop::ptr> loops_a, loops_b;
|
||||
|
||||
if (profile_a->kind() == taxonomy::FACE) {
|
||||
interpolated = taxonomy::make<taxonomy::face>();
|
||||
|
||||
auto profile_a_f = std::static_pointer_cast<taxonomy::face>(profile_a);
|
||||
auto profile_b_f = std::static_pointer_cast<taxonomy::face>(profile_b);
|
||||
|
||||
if (profile_a_f->children.size() != profile_b_f->children.size()) {
|
||||
Logger::Warning("Mismatching number of face boundaries: " +
|
||||
std::to_string(profile_a_f->children.size()) + " vs " +
|
||||
std::to_string(profile_b_f->children.size()),
|
||||
inst
|
||||
);
|
||||
return nullptr;
|
||||
}
|
||||
loops_a = profile_a_f->children;
|
||||
loops_b = profile_b_f->children;
|
||||
} else {
|
||||
loops_a = { std::static_pointer_cast<taxonomy::loop>(profile_a) };
|
||||
loops_b = { std::static_pointer_cast<taxonomy::loop>(profile_b) };
|
||||
interpolated = taxonomy::make<taxonomy::loop>();
|
||||
}
|
||||
|
||||
// @todo should_interpolate should also be informed based by different face matrices.
|
||||
if (profile_a->matrix || profile_b->matrix) {
|
||||
interpolated->matrix = taxonomy::make<taxonomy::matrix4>();
|
||||
Eigen::Matrix4d m4a = Eigen::Matrix4d::Identity();
|
||||
Eigen::Matrix4d m4b = Eigen::Matrix4d::Identity();
|
||||
if (profile_a->matrix) {
|
||||
m4a = profile_a->matrix->ccomponents();
|
||||
}
|
||||
if (profile_b->matrix) {
|
||||
m4b = profile_b->matrix->ccomponents();
|
||||
}
|
||||
interpolated->matrix->components() = lerp(m4a, m4b, relative_dist_along);
|
||||
}
|
||||
|
||||
auto interpolated_offset = lerp(offset_a, offset_b, relative_dist_along);
|
||||
taxonomy::loop::ptr w1, w2;
|
||||
taxonomy::edge::ptr e1, e2;
|
||||
for (auto tmp_ : boost::combine(loops_a, loops_b)) {
|
||||
boost::tie(w1, w2) = tmp_;
|
||||
if (w1->children.size() != w2->children.size()) {
|
||||
Logger::Warning("Mismatching number of edges: " +
|
||||
std::to_string(w1->children.size()) + " vs " +
|
||||
std::to_string(w2->children.size()),
|
||||
inst
|
||||
);
|
||||
return nullptr;
|
||||
}
|
||||
std::vector<taxonomy::point3::ptr> points;
|
||||
for (auto tmp__ : boost::combine(w1->children, w2->children)) {
|
||||
boost::tie(e1, e2) = tmp__;
|
||||
auto& p1 = boost::get<taxonomy::point3::ptr>(e1->start);
|
||||
auto& p2 = boost::get<taxonomy::point3::ptr>(e2->start);
|
||||
|
||||
auto p3 = (lerp(p1->ccomponents(), p2->ccomponents(), relative_dist_along) + interpolated_offset).eval();
|
||||
points.push_back(taxonomy::make<taxonomy::point3>(p3));
|
||||
}
|
||||
if (!points.empty()) {
|
||||
// close polygon by referencing first point
|
||||
// @todo add a closed=true|false to polygon_from_points()?
|
||||
points.push_back(points.front());
|
||||
}
|
||||
|
||||
auto interpolated_loop = polygon_from_points(points);
|
||||
if (interpolated->kind() == taxonomy::FACE) {
|
||||
std::static_pointer_cast<taxonomy::face>(interpolated)->children.push_back(interpolated_loop);
|
||||
} else {
|
||||
std::static_pointer_cast<taxonomy::loop>(interpolated)->children = interpolated_loop->children;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto m4 = evaluator.evaluate(dist_along);
|
||||
/* {
|
||||
std::wcout << "#" << pwf->instance->data().id() << " " << dist_along << ": " << m4.col(3).row(2).value() << std::endl;
|
||||
}*/
|
||||
|
||||
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();
|
||||
m4b.col(3).head<3>() = m4.col(3).head<3>();
|
||||
|
||||
if (interpolated) {
|
||||
loft->children.push_back(interpolated);
|
||||
} else {
|
||||
if (profile_a->kind() == taxonomy::FACE) {
|
||||
loft->children.push_back(std::static_pointer_cast<taxonomy::face>(taxonomy::item::ptr(profile_a->clone_())));
|
||||
} else {
|
||||
loft->children.push_back(std::static_pointer_cast<taxonomy::loop>(taxonomy::item::ptr(profile_a->clone_())));
|
||||
}
|
||||
if (profile_a->matrix) {
|
||||
loft->children.back()->matrix = taxonomy::matrix4::ptr(profile_a->matrix->clone_());
|
||||
}
|
||||
}
|
||||
if (!loft->children.back()->matrix) {
|
||||
// @todo should this not be initialized by default? matrix4 already has a 'lazy identity' mechanism.
|
||||
loft->children.back()->matrix = taxonomy::make<taxonomy::matrix4>();
|
||||
}
|
||||
auto m = (m4b * loft->children.back()->matrix->ccomponents()).eval();
|
||||
loft->children.back()->matrix->components() = m;
|
||||
}
|
||||
}
|
||||
|
||||
return loft;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
#ifndef LINEAR_SWEEP_HELPER_H
|
||||
#define LINEAR_SWEEP_HELPER_H
|
||||
|
||||
#include "taxonomy.h"
|
||||
#include "ConversionSettings.h"
|
||||
|
||||
namespace ifcopenshell {
|
||||
|
||||
namespace geometry {
|
||||
|
||||
struct cross_section {
|
||||
double dist_along;
|
||||
taxonomy::geom_item::ptr section_geometry;
|
||||
Eigen::Vector3d offset;
|
||||
|
||||
bool operator <(const cross_section& other) const {
|
||||
return dist_along < other.dist_along;
|
||||
}
|
||||
};
|
||||
|
||||
taxonomy::loft::ptr make_loft(const Settings& settings_, const IfcUtil::IfcBaseClass* inst, const taxonomy::piecewise_function::ptr& directrix, std::vector<cross_section>& cross_sections);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -22,6 +22,40 @@ using ifcopenshell::geometry::NumberEpeck;
|
||||
ifcopenshell::geometry::CgalShape::CgalShape(const cgal_shape_t & shape, bool convex) {
|
||||
shape_ = shape;
|
||||
convex_tag_ = convex;
|
||||
|
||||
for (const auto& face : CGAL::faces(*shape_)) {
|
||||
// @todo O^2 alert! Use aabb tree or box intersections
|
||||
bool has_self_intersection = false;
|
||||
for (auto& he1 : CGAL::halfedges_around_face(face->halfedge(), *shape_)) {
|
||||
CGAL::Segment_3<Kernel_> s1;
|
||||
{
|
||||
const auto& source = he1->vertex()->point();
|
||||
const auto& target = he1->next()->vertex()->point();
|
||||
s1 = { source, target };
|
||||
}
|
||||
for (auto& he2 : CGAL::halfedges_around_face(face->halfedge(), *shape_)) {
|
||||
if (he1 == he2 || he1->next() == he2 || he2->next() == he1) {
|
||||
// skip topologically connected edges
|
||||
continue;
|
||||
}
|
||||
CGAL::Segment_3<Kernel_> s2;
|
||||
{
|
||||
const auto& source = he2->vertex()->point();
|
||||
const auto& target = he2->next()->vertex()->point();
|
||||
s2 = { source, target };
|
||||
}
|
||||
if (CGAL::do_intersect(s1, s2)) {
|
||||
has_self_intersection = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (has_self_intersection) {
|
||||
throw std::runtime_error("Self-intersection in facet boundary, not attempting triangulation");
|
||||
}
|
||||
}
|
||||
|
||||
if (shape.size_of_facets() != 1) {
|
||||
// this is for handling the specical case of storing a single point in a polyhedron,
|
||||
// @todo come up with a proper variant for storing lower dimensional entities
|
||||
|
||||
@@ -153,6 +153,12 @@ CGAL::Nef_polyhedron_3<Kernel_> ifcopenshell::geometry::utils::create_nef_polyhe
|
||||
#endif
|
||||
|
||||
bool CgalKernel::convert(const taxonomy::shell::ptr l, cgal_shape_t& shape) {
|
||||
for (auto& f : l->children) {
|
||||
if (f->basis && f->basis->kind() != taxonomy::PLANE) {
|
||||
Logger::Error("Non-planar faces not supported at the moment");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (false && l->children.size() > 100) {
|
||||
static double inf = 1.e9; // std::numeric_limits<double>::infinity();
|
||||
std::pair<Eigen::Vector3d, Eigen::Vector3d> minmax(
|
||||
@@ -784,12 +790,16 @@ bool CgalKernel::convert_impl(const taxonomy::shell::ptr shell, ConversionResult
|
||||
}
|
||||
|
||||
bool CgalKernel::convert_impl(const taxonomy::solid::ptr solid, ConversionResults& results) {
|
||||
if (solid->children.size() > 1) {
|
||||
Logger::Error("Multiple shells in solid not supported at the moment");
|
||||
return false;
|
||||
}
|
||||
cgal_shape_t shape;
|
||||
if (solid->children.empty()) {
|
||||
return false;
|
||||
}
|
||||
// @todo
|
||||
if (!convert((taxonomy::shell::ptr)solid->children[0], shape)) {
|
||||
if (!convert(solid->children[0], shape)) {
|
||||
return false;
|
||||
}
|
||||
if (shape.size_of_facets() == 0) {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <BRepGProp.hxx>
|
||||
#include <GProp_GProps.hxx>
|
||||
#include <Geom_SphericalSurface.hxx>
|
||||
#include <Geom_Plane.hxx>
|
||||
|
||||
#include "OpenCascadeConversionResult.h"
|
||||
|
||||
@@ -15,6 +16,12 @@
|
||||
|
||||
#include <Standard_Version.hxx>
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <tuple>
|
||||
#include <algorithm>
|
||||
|
||||
#if OCC_VERSION_HEX >= 0x70600
|
||||
#include <TopTools_FormatVersion.hxx>
|
||||
#endif
|
||||
@@ -24,6 +31,44 @@ using IfcGeom::OpaqueCoordinate;
|
||||
using IfcGeom::NumberNativeDouble;
|
||||
using IfcGeom::ConversionResultShape;
|
||||
|
||||
struct EdgeKey {
|
||||
int v1, v2;
|
||||
|
||||
// These are not part of the hash or equality,
|
||||
// but retained to easily created a directed
|
||||
// graph of the original boundary edges. Since
|
||||
// the boundary edges are exactly those with
|
||||
// count=1 we don't need to worry about
|
||||
// conflicting original vertex indices.
|
||||
int ov1, ov2;
|
||||
|
||||
EdgeKey(int a, int b)
|
||||
: ov1(a)
|
||||
, ov2(b)
|
||||
{
|
||||
if (a < b) {
|
||||
v1 = a;
|
||||
v2 = b;
|
||||
} else {
|
||||
v1 = b;
|
||||
v2 = a;
|
||||
}
|
||||
}
|
||||
|
||||
bool operator==(const EdgeKey& other) const {
|
||||
return v1 == other.v1 && v2 == other.v2;
|
||||
}
|
||||
};
|
||||
|
||||
namespace std {
|
||||
template <>
|
||||
struct hash<EdgeKey> {
|
||||
std::size_t operator()(const EdgeKey& ek) const {
|
||||
return std::hash<int>()(ek.v1) ^ std::hash<int>()(ek.v2);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
namespace {
|
||||
// We bypass the conversion to gp_GTrsf, because it does not work
|
||||
void taxonomy_transform(const Eigen::Matrix4d* m, gp_XYZ& xyz) {
|
||||
@@ -35,6 +80,78 @@ namespace {
|
||||
xyz.ChangeData()[2] = v2(2);
|
||||
}
|
||||
}
|
||||
|
||||
// Function to find boundary loops from triangles
|
||||
std::vector<std::vector<int>> find_boundary_loops(const std::vector<double>& positions, const std::vector<std::tuple<int, int, int>>& triangles) {
|
||||
std::unordered_map<EdgeKey, int> edge_count;
|
||||
|
||||
// Count how many triangles each edge belongs to
|
||||
for (const auto& triangle : triangles) {
|
||||
int v1, v2, v3;
|
||||
std::tie(v1, v2, v3) = triangle;
|
||||
|
||||
edge_count[{v1, v2}]++;
|
||||
edge_count[{v2, v3}]++;
|
||||
edge_count[{v3, v1}]++;
|
||||
}
|
||||
|
||||
// Boundary edges have count 1
|
||||
std::vector<EdgeKey> boundary_edges;
|
||||
for (auto& p : edge_count) {
|
||||
if (p.second == 1) {
|
||||
boundary_edges.push_back(p.first);
|
||||
}
|
||||
}
|
||||
|
||||
// We retained original directed edges so we build
|
||||
// a mapping out of these directed edges.
|
||||
std::unordered_map<int, int> vertex_successors;
|
||||
for (const auto& e : boundary_edges) {
|
||||
vertex_successors[e.ov1] = e.ov2;
|
||||
}
|
||||
|
||||
std::vector<std::vector<int>> loops;
|
||||
while (!vertex_successors.empty()) {
|
||||
loops.emplace_back();
|
||||
auto it = vertex_successors.begin();
|
||||
loops.back() = { it->first, it->second };
|
||||
vertex_successors.erase(it);
|
||||
|
||||
int current = loops.back().back();
|
||||
while (!vertex_successors.empty() && current != loops.back().front()) {
|
||||
auto next = vertex_successors[current];
|
||||
if (loops.back().front() != next) {
|
||||
loops.back().push_back(next);
|
||||
}
|
||||
vertex_successors.erase(current);
|
||||
current = next;
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the loops by smallest x-coord of their constituent positions
|
||||
// In order to put the outermost loop in front
|
||||
if (loops.size() > 1) {
|
||||
std::vector<std::pair<double, size_t>> min_xs;
|
||||
for (auto& l : loops) {
|
||||
double min_x = std::numeric_limits<double>::infinity();
|
||||
for (auto& i : l) {
|
||||
const auto& x = positions[i * 3];
|
||||
if (x < min_x) {
|
||||
min_x = x;
|
||||
}
|
||||
}
|
||||
min_xs.push_back({ min_x, min_xs.size() });
|
||||
}
|
||||
std::sort(min_xs.begin(), min_xs.end());
|
||||
decltype(loops) loops_copy;
|
||||
for (auto& p : min_xs) {
|
||||
loops_copy.emplace_back(std::move(loops[p.second]));
|
||||
}
|
||||
std::swap(loops, loops_copy);
|
||||
}
|
||||
|
||||
return loops;
|
||||
}
|
||||
}
|
||||
|
||||
void ifcopenshell::geometry::OpenCascadeShape::Triangulate(ifcopenshell::geometry::Settings settings, const ifcopenshell::geometry::taxonomy::matrix4& place, IfcGeom::Representation::Triangulation* t, int item_id, int surface_style_id) const {
|
||||
@@ -71,6 +188,18 @@ void ifcopenshell::geometry::OpenCascadeShape::Triangulate(ifcopenshell::geometr
|
||||
TopExp_Explorer exp;
|
||||
for (exp.Init(shape_, TopAbs_FACE); exp.More(); exp.Next(), ++num_faces) {
|
||||
TopoDS_Face face = TopoDS::Face(exp.Current());
|
||||
|
||||
size_t num_bounds = 0;
|
||||
for (TopoDS_Iterator it(face); it.More(); it.Next(), ++num_bounds) {}
|
||||
|
||||
const bool is_planar = BRep_Tool::Surface(face) && BRep_Tool::Surface(face)->DynamicType() == STANDARD_TYPE(Geom_Plane);
|
||||
const bool has_inner_bounds = num_bounds > 1;
|
||||
|
||||
const bool polyhedral_output_with_holes = settings.get<settings::TriangulationType>().get() == settings::POLYHEDRON_WITH_HOLES && is_planar;
|
||||
const bool polyhedral_output_without_holes = settings.get<settings::TriangulationType>().get() == settings::POLYHEDRON_WITHOUT_HOLES && is_planar && !has_inner_bounds;
|
||||
|
||||
std::vector<std::tuple<int, int, int>> triangle_indices;
|
||||
|
||||
TopLoc_Location loc;
|
||||
Handle_Poly_Triangulation tri = BRep_Tool::Triangulation(face, loc);
|
||||
|
||||
@@ -154,17 +283,27 @@ void ifcopenshell::geometry::OpenCascadeShape::Triangulate(ifcopenshell::geometr
|
||||
_normals.push_back((float)normal.Z());
|
||||
*/
|
||||
|
||||
t->addFace(item_id, surface_style_id, dict[n1], dict[n2], dict[n3]);
|
||||
if (polyhedral_output_without_holes || polyhedral_output_with_holes) {
|
||||
triangle_indices.push_back({ dict[n1], dict[n2], dict[n3] });
|
||||
} else {
|
||||
if (settings.get<settings::TriangulationType>().get() == settings::POLYHEDRON_WITHOUT_HOLES) {
|
||||
t->addFace(item_id, surface_style_id, std::vector<int>{ dict[n1], dict[n2], dict[n3] });
|
||||
} else if (settings.get<settings::TriangulationType>().get() == settings::POLYHEDRON_WITH_HOLES) {
|
||||
t->addFace(item_id, surface_style_id, std::vector<std::vector<int>>{{ dict[n1], dict[n2], dict[n3] }});
|
||||
} else {
|
||||
t->addFace(item_id, surface_style_id, dict[n1], dict[n2], dict[n3]);
|
||||
|
||||
t->addEdge(dict[n1], dict[n2], edgecount);
|
||||
t->addEdge(dict[n2], dict[n3], edgecount);
|
||||
t->addEdge(dict[n3], dict[n1], edgecount);
|
||||
t->registerEdgeCount(dict[n1], dict[n2], edgecount);
|
||||
t->registerEdgeCount(dict[n2], dict[n3], edgecount);
|
||||
t->registerEdgeCount(dict[n3], dict[n1], edgecount);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto& p : edgecount) {
|
||||
// @todo should be != 2?
|
||||
if (p.second == 1 && emitted_edges.find(p.first) == emitted_edges.end()) {
|
||||
// non manifold edge, face boundary
|
||||
t->registerEdge(p.first.first, p.first.second);
|
||||
t->registerEdge(item_id, p.first.first, p.first.second);
|
||||
if (settings.get<settings::WeldVertices>().get()) {
|
||||
// only relevant while welding, because otherwise vertices are not shared among distinct faces
|
||||
emitted_edges.insert(p.first);
|
||||
@@ -172,6 +311,19 @@ void ifcopenshell::geometry::OpenCascadeShape::Triangulate(ifcopenshell::geometr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (polyhedral_output_without_holes || polyhedral_output_with_holes) {
|
||||
auto loops = find_boundary_loops(t->verts(), triangle_indices);
|
||||
if (polyhedral_output_without_holes) {
|
||||
if (!loops.empty() && !loops[0].empty()) {
|
||||
t->addFace(item_id, surface_style_id, loops[0]);
|
||||
}
|
||||
} else {
|
||||
if (!loops.empty()) {
|
||||
t->addFace(item_id, surface_style_id, loops);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!t->normals().empty() && settings.get<settings::GenerateUvs>().get()) {
|
||||
@@ -234,7 +386,7 @@ void ifcopenshell::geometry::OpenCascadeShape::Triangulate(ifcopenshell::geometr
|
||||
}
|
||||
|
||||
for (auto& sgmt : segments) {
|
||||
t->addEdge(surface_style_id, sgmt.first, sgmt.second);
|
||||
t->addEdge(item_id, surface_style_id, sgmt.first, sgmt.second);
|
||||
}
|
||||
|
||||
previous = current;
|
||||
|
||||
@@ -978,7 +978,7 @@ bool IfcGeom::util::boolean_operation(const boolean_settings& settings, const To
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Logger::LOG_NOTICE >= Logger::Verbosity()) {
|
||||
if (!is_2d && Logger::LOG_NOTICE >= Logger::Verbosity()) {
|
||||
PERF("preliminary manifoldness check");
|
||||
|
||||
if (!a.IsNull()) {
|
||||
|
||||
@@ -442,7 +442,7 @@ bool OpenCascadeKernel::convert(const taxonomy::face::ptr face, TopoDS_Shape& re
|
||||
kt.Value().Original().ToUTF8CString(c);
|
||||
std::string message = c;
|
||||
delete[] c;
|
||||
#if OCC_VERSION_MAJOR==7 && OCC_VERSION_MINOR == 7
|
||||
#if OCC_VERSION_MAJOR==7 && OCC_VERSION_MINOR >= 7
|
||||
if (!reversed_surface && !fd.surface().IsNull() && fd.surface()->IsUPeriodic() && message == "Unknown message invoked with the keyword FixAdvFace.FixOrientation.MSG0") {
|
||||
Logger::Notice("Detected reversed wire, reattempting with reversed basis surface");
|
||||
TopoDS_Face reversed_result;
|
||||
|
||||
@@ -115,6 +115,7 @@ IfcGeom::OpenCascadeKernel::faceset_helper::faceset_helper(
|
||||
eps_ = Precision::Confusion();
|
||||
}
|
||||
|
||||
std::vector<bool> retained(pnts.size());
|
||||
for (int pnt_i = 0; pnt_i < (int)pnts.size(); ++pnt_i) {
|
||||
if (pnts[pnt_i]) {
|
||||
std::set<int> vs;
|
||||
@@ -125,7 +126,9 @@ IfcGeom::OpenCascadeKernel::faceset_helper::faceset_helper(
|
||||
// NB: insert() ignores duplicate keys
|
||||
// v-1?
|
||||
// @todo this reliable also in case of tesselations?
|
||||
vertex_mapping_.insert({ pt.identity(), pnt_i });
|
||||
if (vertex_mapping_.insert({ pt.identity(), pnt_i }).second) {
|
||||
retained[pnt_i] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -141,8 +144,10 @@ IfcGeom::OpenCascadeKernel::faceset_helper::faceset_helper(
|
||||
}
|
||||
}
|
||||
|
||||
if (unique.size() != vertex_mapping_.size()) {
|
||||
Logger::Notice("Collapsed vertices from " + std::to_string(pnts.size()) + " (" + std::to_string(unique.size()) + " unique) to " + std::to_string(vertex_mapping_.size()));
|
||||
auto num_retained = std::count(retained.begin(), retained.end(), true);
|
||||
|
||||
if (unique.size() != num_retained) {
|
||||
Logger::Notice("Collapsed vertices from " + std::to_string(pnts.size()) + " (" + std::to_string(unique.size()) + " unique) to " + std::to_string(num_retained));
|
||||
}
|
||||
|
||||
typedef std::array<int, 2> edge_t;
|
||||
|
||||
@@ -48,26 +48,43 @@ bool OpenCascadeKernel::convert(const taxonomy::loft::ptr loft, TopoDS_Shape& re
|
||||
|
||||
for (auto it = loft->children.begin(); it < loft->children.end() - 1; ++it) {
|
||||
auto jt = it + 1;
|
||||
std::array<taxonomy::face::ptr, 2> fa = { *it, *jt };
|
||||
std::array<taxonomy::item::ptr, 2> fa = { *it, *jt };
|
||||
std::array<TopoDS_Shape, 2> shps;
|
||||
std::array<TopoDS_Wire, 2> ws;
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
if (!convert(fa[i], shps[i])) {
|
||||
return false;
|
||||
if (fa[i]->kind() == taxonomy::FACE) {
|
||||
if (!convert(std::static_pointer_cast<taxonomy::face>(fa[i]), shps[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (shps[i].ShapeType() != TopAbs_FACE) {
|
||||
if (fa[i]->kind() == taxonomy::LOOP) {
|
||||
TopoDS_Wire w;
|
||||
if (!convert(std::static_pointer_cast<taxonomy::loop>(fa[i]), w)) {
|
||||
return false;
|
||||
}
|
||||
shps[i] = w;
|
||||
}
|
||||
if (shps[i].ShapeType() != TopAbs_FACE && shps[i].ShapeType() != TopAbs_WIRE) {
|
||||
return false;
|
||||
}
|
||||
// @todo this is only outer wire
|
||||
ws[i] = BRepTools::OuterWire(TopoDS::Face(shps[i]));
|
||||
if (shps[i].ShapeType() == TopAbs_FACE) {
|
||||
ws[i] = BRepTools::OuterWire(TopoDS::Face(shps[i]));
|
||||
} else {
|
||||
ws[i] = TopoDS::Wire(shps[i]);
|
||||
}
|
||||
}
|
||||
if (it == loft->children.begin()) {
|
||||
// faces.Append(shps[0]);
|
||||
BB.Add(comp, shps[0]);
|
||||
}
|
||||
if (jt == loft->children.end() - 1) {
|
||||
// faces.Append(shps[1]);
|
||||
BB.Add(comp, shps[1]);
|
||||
if (shps[0].ShapeType() == TopAbs_FACE) {
|
||||
// When processing a sectioned *surface* there are no
|
||||
// begin and end caps that need to be added.
|
||||
if (it == loft->children.begin()) {
|
||||
// faces.Append(shps[0]);
|
||||
BB.Add(comp, shps[0]);
|
||||
}
|
||||
if (jt == loft->children.end() - 1) {
|
||||
// faces.Append(shps[1]);
|
||||
BB.Add(comp, shps[1]);
|
||||
}
|
||||
}
|
||||
BRepTools_WireExplorer a(ws[0]);
|
||||
BRepTools_WireExplorer b(ws[1]);
|
||||
|
||||
@@ -1,169 +0,0 @@
|
||||
/********************************************************************************
|
||||
* *
|
||||
* This file is part of IfcOpenShell. *
|
||||
* *
|
||||
* IfcOpenShell is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the Lesser GNU General Public License as published by *
|
||||
* the Free Software Foundation, either version 3.0 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* IfcOpenShell is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* Lesser GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the Lesser GNU General Public License *
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
/*
|
||||
#include "IfcGeom.h"
|
||||
#include "../ifcgeom_schema_agnostic/IfcGeomShapeType.h"
|
||||
#include "../ifcgeom_schema_agnostic/wire_utils.h"
|
||||
|
||||
#include <BRepCheck.hxx>
|
||||
#include <BRepCheck_Analyzer.hxx>
|
||||
|
||||
#define Kernel POSTFIX_SCHEMA(Kernel)
|
||||
|
||||
using namespace IfcUtil;
|
||||
|
||||
bool IfcGeom::Kernel::convert_shapes(const IfcBaseInterface* l, ConversionResults& r) {
|
||||
if (shape_type(l) != ST_SHAPELIST) {
|
||||
TopoDS_Shape shp;
|
||||
if (convert_shape(l, shp)) {
|
||||
std::shared_ptr<const IfcGeom::SurfaceStyle> style;
|
||||
if (l->as<IfcSchema::IfcRepresentationItem>()) {
|
||||
style = get_style(l->as<IfcSchema::IfcRepresentationItem>());
|
||||
}
|
||||
r.push_back(IfcGeom::ConversionResult(l->data().id(), shp, style));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#include "mapping_shapes.i"
|
||||
Logger::Message(Logger::LOG_ERROR,"No operation defined for:",l);
|
||||
return false;
|
||||
}
|
||||
|
||||
IfcGeom::ShapeType IfcGeom::Kernel::shape_type(const IfcBaseInterface* l) {
|
||||
#include "mapping_shape_type.i"
|
||||
return ST_OTHER;
|
||||
}
|
||||
|
||||
bool IfcGeom::Kernel::convert_shape(const IfcBaseInterface* l, TopoDS_Shape& r) {
|
||||
const unsigned int id = l->data().id();
|
||||
bool success = false;
|
||||
bool processed = false;
|
||||
bool ignored = false;
|
||||
|
||||
#ifndef NO_CACHE
|
||||
std::map<int,TopoDS_Shape>::const_iterator it = cache.Shape.find(id);
|
||||
if ( it != cache.Shape.end() ) { r = it->second; return true; }
|
||||
#endif
|
||||
const bool include_curves = getValue(GV_DIMENSIONALITY) != +1;
|
||||
const bool include_solids_and_surfaces = getValue(GV_DIMENSIONALITY) != -1;
|
||||
|
||||
IfcGeom::ShapeType st = shape_type(l);
|
||||
ignored = (!include_solids_and_surfaces && (st == ST_SHAPE || st == ST_FACE)) || (!include_curves && (st == ST_WIRE || st == ST_CURVE));
|
||||
if (st == ST_SHAPELIST) {
|
||||
processed = true;
|
||||
ConversionResults items;
|
||||
success = convert_shapes(l, items) && util::flatten_shape_list(items, r, false, getValue(GV_PRECISION));
|
||||
} else if (st == ST_SHAPE && include_solids_and_surfaces) {
|
||||
#include "mapping_shape.i"
|
||||
} else if (st == ST_FACE && include_solids_and_surfaces) {
|
||||
processed = true;
|
||||
success = convert_face(l, r);
|
||||
} else if (st == ST_WIRE && include_curves) {
|
||||
processed = true;
|
||||
TopoDS_Wire w;
|
||||
success = convert_wire(l, w);
|
||||
if (success) {
|
||||
r = w;
|
||||
}
|
||||
} else if (st == ST_CURVE && include_curves) {
|
||||
processed = true;
|
||||
Handle(Geom_Curve) crv;
|
||||
TopoDS_Wire w;
|
||||
success = convert_curve(l, crv) && util::convert_curve_to_wire(crv, w);
|
||||
if (success) {
|
||||
r = w;
|
||||
}
|
||||
}
|
||||
|
||||
if ( processed && success ) {
|
||||
#ifndef NO_CACHE
|
||||
cache.Shape[id] = r;
|
||||
#endif
|
||||
|
||||
if (Logger::LOG_DEBUG >= Logger::Verbosity()) {
|
||||
std::stringstream ss;
|
||||
|
||||
BRepCheck_Analyzer ana(r);
|
||||
|
||||
std::function<void(const TopoDS_Shape&)> traverse_subshapes;
|
||||
|
||||
traverse_subshapes = [&traverse_subshapes, &ana, &ss](const TopoDS_Shape& shape) {
|
||||
if (shape.IsNull())
|
||||
return;
|
||||
|
||||
TopoDS_Iterator it(shape);
|
||||
for (; it.More(); it.Next()) {
|
||||
const TopoDS_Shape& subs = it.Value();
|
||||
|
||||
auto rs = ana.Result(subs);
|
||||
if (rs) {
|
||||
for (auto& msg : rs->Status()) {
|
||||
if (msg != BRepCheck_NoError) {
|
||||
ss << " ";
|
||||
std::stringstream sst;
|
||||
BRepCheck::Print(msg, sst);
|
||||
auto sss = sst.str();
|
||||
// remove trailing newline added by Print()
|
||||
ss << sss.substr(0, sss.size() - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
traverse_subshapes(subs);
|
||||
}
|
||||
};
|
||||
|
||||
traverse_subshapes(r);
|
||||
|
||||
Logger::Notice((ana.IsValid() ? "Valid shape" : "Invalid shape with:") + ss.str(), l);
|
||||
}
|
||||
} else if (!ignored) {
|
||||
const char* const msg = processed
|
||||
? "Failed to convert:"
|
||||
: "No operation defined for:";
|
||||
Logger::Message(Logger::LOG_ERROR, msg, l);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
bool IfcGeom::Kernel::convert_wire(const IfcBaseInterface* l, TopoDS_Wire& r) {
|
||||
#include "mapping_wire.i"
|
||||
Handle(Geom_Curve) curve;
|
||||
if (IfcGeom::Kernel::convert_curve(l, curve)) {
|
||||
return util::convert_curve_to_wire(curve, r);
|
||||
}
|
||||
Logger::Message(Logger::LOG_ERROR,"No operation defined for:",l);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IfcGeom::Kernel::convert_face(const IfcBaseInterface* l, TopoDS_Shape& r) {
|
||||
#include "mapping_face.i"
|
||||
Logger::Message(Logger::LOG_ERROR,"No operation defined for:",l);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IfcGeom::Kernel::convert_curve(const IfcBaseInterface* l, Handle(Geom_Curve)& r) {
|
||||
#include "mapping_curve.i"
|
||||
Logger::Message(Logger::LOG_ERROR,"No operation defined for:",l);
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
@@ -34,5 +34,6 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcCircle* inst) {
|
||||
auto c = taxonomy::make<taxonomy::circle>();
|
||||
c->radius = r;
|
||||
c->matrix = taxonomy::cast<taxonomy::matrix4>(map(placement));
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcCompositeCurve* inst) {
|
||||
return loop;
|
||||
}
|
||||
else {
|
||||
auto pwf = taxonomy::make<taxonomy::piecewise_function>(0.0,pwfs,&settings_,inst);
|
||||
auto pwf = taxonomy::make<taxonomy::piecewise_function>(0.0,pwfs,inst);
|
||||
return pwf;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ double translate_to_length_measure(const IfcSchema::IfcCurve* crv, double param_
|
||||
return fabs(clothoid->ClothoidConstant()*sqrt(PI))*param_value;
|
||||
} else if (auto circ = crv->as<IfcSchema::IfcCircle>()) {
|
||||
return circ->Radius() * param_value;
|
||||
} else if (auto circ = crv->as<IfcSchema::IfcPolynomialCurve>()) {
|
||||
} else if (auto poly = crv->as<IfcSchema::IfcPolynomialCurve>()) {
|
||||
return param_value;
|
||||
} else {
|
||||
throw std::runtime_error("Unsupported curve measure type");
|
||||
@@ -130,6 +130,26 @@ class curve_segment_evaluator {
|
||||
|
||||
if (next_inst) {
|
||||
next_segment_placement_ = taxonomy::cast<taxonomy::matrix4>(mapping_->map(next_inst->Placement()))->ccomponents();
|
||||
} else {
|
||||
// there is not a next segment, however IfcGradientCurve and IfcSegmentReferenceCurve have an
|
||||
// optional EndPoint which services the same purpose as the zero-length last segment.
|
||||
auto composite_curves = inst->UsingCurves();
|
||||
IfcSchema::IfcPlacement* end_point = nullptr;
|
||||
if (composite_curves->size() == 1) {
|
||||
auto& cc = *(composite_curves)->begin();
|
||||
if (segment_type_ == ST_VERTICAL) {
|
||||
auto gradient_curve = cc->as<IfcSchema::IfcGradientCurve>();
|
||||
end_point = gradient_curve->EndPoint();
|
||||
} else if (segment_type_ == ST_CANT) {
|
||||
auto segmented_reference_curve = cc->as<IfcSchema::IfcSegmentedReferenceCurve>();
|
||||
end_point = segmented_reference_curve->EndPoint();
|
||||
}
|
||||
} else {
|
||||
Logger::Warning("IfcCurveSegment belongs to multiple IfcCompositeCurve instances. Cannot determine the end point.");
|
||||
}
|
||||
if (end_point) {
|
||||
next_segment_placement_ = taxonomy::cast<taxonomy::matrix4>(mapping_->map(end_point))->ccomponents();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,43 +252,71 @@ class curve_segment_evaluator {
|
||||
}
|
||||
|
||||
// defines the parent_curve_fn_ functor for cant segments.
|
||||
// Cant returns D at a distance along the curve, u.
|
||||
// CantSlope returns the slope of the Cant function at u. CantSlope(u) is the derivative of Cant(u)
|
||||
void set_cant_spiral_function(std::function<double(double)> Cant, std::function<double(double)> CantSlope) {
|
||||
parent_curve_fn_ = [Cant, CantSlope](double u) -> Eigen::Matrix4d {
|
||||
auto cant = Cant(u);
|
||||
auto slope = CantSlope(u);
|
||||
void set_cant_spiral_function(std::function<double(double)> Superelevation, std::function<double(double)> SuperelevationSlope, std::function<double(double)> Cant) {
|
||||
auto dy = (*placement_)(1, 2); // placement dy
|
||||
auto dz = (*placement_)(2, 2); // placement dz
|
||||
auto start_angle = atan2(dz, dy);
|
||||
|
||||
auto angle = atan(slope);
|
||||
auto dx = cos(angle);
|
||||
auto dy = sin(angle);
|
||||
dy = (next_segment_placement_.has_value() ? (*next_segment_placement_)(1, 2) : 0.0);
|
||||
dz = (next_segment_placement_.has_value() ? (*next_segment_placement_)(2, 2) : 1.0);
|
||||
auto end_angle = atan2(dz, dy);
|
||||
|
||||
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(0.0, cant, 0.0, 1.0);
|
||||
return m;
|
||||
auto delta_angle = end_angle - start_angle;
|
||||
auto start_cant = Cant(0.0 /*start_*/);
|
||||
auto end_cant = Cant(/* start_ + */ length_);
|
||||
auto delta_cant = end_cant - start_cant;
|
||||
|
||||
parent_curve_fn_ = [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);
|
||||
|
||||
// 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);
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
parent_curve_start_point_ = (*parent_curve_fn_)(0.0);
|
||||
}
|
||||
|
||||
boost::optional<std::function<double(double)>> get_cant_superelevation_function() {
|
||||
boost::optional<std::function<double(double)>> fn;
|
||||
// returns function for super elevation and the slope of the super elevation curve if the super elevation is constant
|
||||
// over the length of the segment. otherwise, no functions are returned because they are the same as the cant tilt angle
|
||||
// functions.
|
||||
std::pair<boost::optional<std::function<double(double)>>, boost::optional<std::function<double(double)>>> get_superelevation_functions() {
|
||||
boost::optional<std::function<double(double)>> superelevation_fn;
|
||||
boost::optional<std::function<double(double)>> superelevation_slope_fn;
|
||||
|
||||
if (next_segment_placement_.has_value() && placement_.has_value()) {
|
||||
// cant superelevation should be y value (row 1)
|
||||
if (placement_.has_value() && next_segment_placement_.has_value()) {
|
||||
double y1 = (*placement_)(1, 3);
|
||||
double y2 = (*next_segment_placement_)(1, 3);
|
||||
|
||||
// if y2-y1 = 0, there is no superelevation
|
||||
// so we need a Cant function that always returns zero
|
||||
// if y2-y1 = 0, the super elevation is constant
|
||||
// so we need a function that always returns the constant value
|
||||
if (!(y2 - y1)) {
|
||||
fn = [](double)->double { return 0.0; };
|
||||
superelevation_fn = [y1](double) -> double { return y1; };
|
||||
superelevation_slope_fn = [](double) -> double { return 0.0; };
|
||||
}
|
||||
}
|
||||
|
||||
return fn;
|
||||
return std::make_pair(superelevation_fn,superelevation_slope_fn);
|
||||
}
|
||||
|
||||
#ifdef SCHEMA_HAS_IfcClothoid
|
||||
@@ -276,15 +324,19 @@ class curve_segment_evaluator {
|
||||
auto A = c->ClothoidConstant();
|
||||
|
||||
if (segment_type_ == ST_CANT) {
|
||||
boost::optional<std::function<double(double)>> super, slope;
|
||||
std::tie(super, slope) = get_superelevation_functions();
|
||||
auto cant = [A, L = length_ * length_unit_](double t) -> double { return A ? L * A * t / fabs(pow(A, 3)) : 0.0; };
|
||||
|
||||
auto Cant = get_cant_superelevation_function(); // fn that always returns zero if there is no superelevation
|
||||
if (!Cant.has_value()) {
|
||||
// function not provided so there must be a superelevation - this function provides the superelevation transition
|
||||
Cant = [A, L = length_ * length_unit_](double t) -> double { return A ? L * A * t / fabs(pow(A, 3)) : 0.0; };
|
||||
if (!super.has_value()) {
|
||||
super = cant;
|
||||
}
|
||||
|
||||
auto CantSlope = [A, L = length_ * length_unit_](double /*t*/) -> double { return A ? L * A / fabs(pow(A, 3)) : 0.0; };
|
||||
set_cant_spiral_function(*Cant, CantSlope);
|
||||
if (!slope.has_value()) {
|
||||
slope= [A, L = length_ * length_unit_](double /*t*/) -> double { return A ? L * A / fabs(pow(A, 3)) : 0.0; };
|
||||
}
|
||||
|
||||
set_cant_spiral_function(*super,*slope, cant);
|
||||
} else {
|
||||
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; };
|
||||
@@ -311,21 +363,27 @@ class curve_segment_evaluator {
|
||||
double s = 1.0;
|
||||
set_spiral_function(s, fn_x, fn_y);
|
||||
} else if (segment_type_ == ST_CANT) {
|
||||
auto Cant = get_cant_superelevation_function(); // fn that always returns zero if there is no superelevation
|
||||
if (!Cant.has_value()) {
|
||||
// function not provided so there must be a superelevation - this function provides the superelevation transition
|
||||
Cant = [constant_term, cosine_term, L, lu = length_unit_](double t) -> double {
|
||||
auto a0 = constant_term.has_value() ? L / (constant_term.value() * lu) : 0.0;
|
||||
auto a1 = (L / (cosine_term * lu)) * cos(PI * t * lu / L);
|
||||
return a0 + a1;
|
||||
boost::optional<std::function<double(double)>> super, slope;
|
||||
std::tie(super, slope) = get_superelevation_functions();
|
||||
|
||||
auto cant = [constant_term, cosine_term, L, lu = length_unit_](double t) -> double {
|
||||
auto a0 = constant_term.has_value() ? L / (constant_term.value() * lu) : 0.0;
|
||||
auto a1 = (L / (cosine_term * lu)) * cos(PI * t * lu / L);
|
||||
return a0 + a1;
|
||||
};
|
||||
|
||||
if (!super.has_value()) {
|
||||
super = cant;
|
||||
}
|
||||
|
||||
if (!slope.has_value()) {
|
||||
slope = [cosine_term, L, lu = length_unit_](double t) -> double {
|
||||
auto a1 = -(PI / L) * (L / (cosine_term * lu)) * sin(PI * t * lu / L);
|
||||
return a1;
|
||||
};
|
||||
}
|
||||
|
||||
auto CantSlope = [cosine_term, L, lu = length_unit_](double t) -> double {
|
||||
auto a1 = -(PI / L) * (L / (cosine_term * lu)) * sin(PI * t * lu / L);
|
||||
return a1;
|
||||
};
|
||||
set_cant_spiral_function(*Cant, CantSlope);
|
||||
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_ = [](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); };
|
||||
@@ -354,23 +412,29 @@ class curve_segment_evaluator {
|
||||
double s = 1.0;
|
||||
set_spiral_function(s, fn_x, fn_y);
|
||||
} else if (segment_type_ == ST_CANT) {
|
||||
auto Cant = get_cant_superelevation_function(); // fn that always returns zero if there is no superelevation
|
||||
if (!Cant.has_value()) {
|
||||
// function not provided so there must be a superelevation - this function provides the superelevation transition
|
||||
Cant = [constant_term, linear_term, sine_term, L, lu = length_unit_](double t) -> double {
|
||||
auto a0 = constant_term.has_value() ? L / (constant_term.value() * lu) : 0.0;
|
||||
auto a1 = linear_term.has_value() ? sign(linear_term.value()) * pow(L / (linear_term.value() * lu), 2.0) * (t / L) : 0.0;
|
||||
auto a2 = (L / (sine_term * lu)) * sin(2 * PI * t / L);
|
||||
return a0 + a1 + a2;
|
||||
boost::optional<std::function<double(double)>> super, slope;
|
||||
std::tie(super, slope) = get_superelevation_functions();
|
||||
|
||||
auto cant = [constant_term, linear_term, sine_term, L, lu = length_unit_](double t) -> double {
|
||||
auto a0 = constant_term.has_value() ? L / (constant_term.value() * lu) : 0.0;
|
||||
auto a1 = linear_term.has_value() ? sign(linear_term.value()) * pow(L / (linear_term.value() * lu), 2.0) * (t / L) : 0.0;
|
||||
auto a2 = (L / (sine_term * lu)) * sin(2 * PI * t / L);
|
||||
return a0 + a1 + a2;
|
||||
};
|
||||
|
||||
if (!super.has_value()) {
|
||||
super = cant;
|
||||
}
|
||||
|
||||
if (!slope.has_value()) {
|
||||
slope = [linear_term, sine_term, L, lu = length_unit_](double t) -> double {
|
||||
auto a1 = linear_term.has_value() ? sign(linear_term.value()) * pow(L / (linear_term.value() * lu), 2.0) * (1.0 / L) : 0.0;
|
||||
auto a2 = (2 * PI / L) * (L / (sine_term * lu)) * cos(2 * PI * t / L);
|
||||
return a1 + a2;
|
||||
};
|
||||
}
|
||||
|
||||
auto CantSlope = [linear_term, sine_term, L, lu = length_unit_](double t) -> double {
|
||||
auto a1 = linear_term.has_value() ? sign(linear_term.value()) * pow(L / (linear_term.value() * lu), 2.0) * (1.0 / L) : 0.0;
|
||||
auto a2 = (2 * PI / L) * (L / (sine_term * lu)) * cos(2 * PI * t / L);
|
||||
return a1 + a2;
|
||||
};
|
||||
set_cant_spiral_function(*Cant, CantSlope);
|
||||
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_ = [](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); };
|
||||
@@ -402,36 +466,41 @@ class curve_segment_evaluator {
|
||||
}
|
||||
|
||||
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) {
|
||||
auto Cant = get_cant_superelevation_function(); // fn that always returns zero if there is no superelevation
|
||||
if (!Cant.has_value()) {
|
||||
// function not provided so there must be a superelevation - this function provides the superelevation transition
|
||||
Cant = [A0, A1, A2, A3, A4, A5, A6, A7, start = start_ * length_unit_, L = length_ * length_unit_, lu = length_unit_, length = length_](double t) {
|
||||
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_ * length_unit_, L = length_ * length_unit_, lu = length_unit_, length = length_](double t) {
|
||||
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);
|
||||
};
|
||||
|
||||
if (!super.has_value()) {
|
||||
super = cant;
|
||||
}
|
||||
|
||||
if (!slope.has_value()) {
|
||||
slope = [A1, A2, A3, A4, A5, A6, A7, start = start_ * length_unit_, L = length_ * length_unit_, lu = length_unit_, length = length_](double t) {
|
||||
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);
|
||||
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;
|
||||
auto a3 = A3.has_value() ? 3 * A3.value() * lu * std::pow(t, 2) / fabs(std::pow(A3.value() * lu, 5)) : 0.0;
|
||||
auto a4 = A4.has_value() ? 4 * std::pow(t, 3) / std::pow(A4.value() * lu, 5) : 0.0;
|
||||
auto a5 = A5.has_value() ? 5 * A5.value() * lu * std::pow(t, 4) / fabs(std::pow(A5.value() * lu, 7)) : 0.0;
|
||||
auto a6 = A6.has_value() ? 6 * std::pow(t, 5) / std::pow(A6.value() * lu, 7) : 0.0;
|
||||
auto a7 = A7.has_value() ? 7 * A7.value() * lu * std::pow(t, 6) / fabs(std::pow(A7.value() * lu, 9)) : 0.0;
|
||||
return L * (a1 + a2 + a3 + a4 + a5 + a6 + a7);
|
||||
};
|
||||
}
|
||||
|
||||
auto CantSlope = [A1, A2, A3, A4, A5, A6, A7, start = start_ * length_unit_, L = length_ * length_unit_, lu = length_unit_, length = length_](double t) {
|
||||
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;
|
||||
auto a3 = A3.has_value() ? 3 * A3.value() * lu * std::pow(t, 2) / fabs(std::pow(A3.value() * lu, 5)) : 0.0;
|
||||
auto a4 = A4.has_value() ? 4 * std::pow(t, 3) / std::pow(A4.value() * lu, 5) : 0.0;
|
||||
auto a5 = A5.has_value() ? 5 * A5.value() * lu * std::pow(t, 4) / fabs(std::pow(A5.value() * lu, 7)) : 0.0;
|
||||
auto a6 = A6.has_value() ? 6 * std::pow(t, 5) / std::pow(A6.value() * lu, 7) : 0.0;
|
||||
auto a7 = A7.has_value() ? 7 * A7.value() * lu * std::pow(t, 6) / fabs(std::pow(A7.value() * lu, 9)) : 0.0;
|
||||
return L * (a1 + a2 + a3 + a4 + a5 + a6 + a7);
|
||||
};
|
||||
|
||||
set_cant_spiral_function(*Cant, CantSlope);
|
||||
set_cant_spiral_function(*super, *slope, cant);
|
||||
}
|
||||
|
||||
#ifdef SCHEMA_HAS_IfcSecondOrderPolynomialSpiral
|
||||
@@ -839,50 +908,52 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcCurveSegment* inst) {
|
||||
Logger::Error(std::runtime_error(inst->ParentCurve()->declaration().name() + " not implemented"), inst);
|
||||
}
|
||||
|
||||
// Do a negative translation of the parent curve point relative to the start of the parent curve.
|
||||
// This moves parent_curve_fn(u=0.0) to coordinate (0,0).
|
||||
// This is done so the curve_segment_placement is applied relative to (0,0)
|
||||
Eigen::Matrix4d remove_parent_curve_translation = Eigen::Matrix4d::Identity();
|
||||
remove_parent_curve_translation.col(3) = -1.0 * (*parent_curve_start_point).col(3);
|
||||
remove_parent_curve_translation(3, 3) = 1.0;
|
||||
|
||||
// Do a rotation so that the tangent of the parent curve is in the direction (1,0)
|
||||
// Example: if the parent curve IfcLine is at a 30 degree clockwise angle, this does
|
||||
// a 30 degree counter-clockwise rotation
|
||||
// Clockwise rotation matrix = [cos(angle) -sin(angle)]
|
||||
// [sin(angle) cos(angle)]
|
||||
//
|
||||
// Counter-clockwise rotation = [ cos(angle) sin(angle)]
|
||||
// [-sin(angle) cos(angle)]
|
||||
//
|
||||
// That's just a sign flip in positions (0,1) and (1,0)
|
||||
Eigen::Matrix4d remove_parent_curve_rotation = *parent_curve_start_point;
|
||||
remove_parent_curve_rotation(0, 1) *= -1.0;
|
||||
remove_parent_curve_rotation(1, 0) *= -1.0;
|
||||
remove_parent_curve_rotation.col(3) = Eigen::Vector4d(0, 0, 0, 1); // remove the parent curve placement point
|
||||
|
||||
const auto& curve_segment_placement = cse.segment_placement();
|
||||
|
||||
std::function<Eigen::Matrix4d(double u)> fn;
|
||||
if (segment_type == ST_CANT)
|
||||
{
|
||||
// not sure if this is correct, but when applying my general formula to compute the 4x4 matrix of a point on curve segment,
|
||||
// p = curve_segment_placement * remove_parent_curve_rotation * remove_parent_curve_translation * parent_curve_point,
|
||||
// the directional vectors of curve_segment_placement are multiplied with the cant value (eg parent_curve_point(3,3)) and
|
||||
// cause the resulting z value to be slightly off. My solution is to change the upper 3x3 of the curve_segment_placement
|
||||
// matrix to identity. This results in correct cant values, but I think it messes up the resulting direction vectors
|
||||
Eigen::Matrix4d c = Eigen::Matrix4d::Identity();
|
||||
c.col(3) = (*curve_segment_placement).col(3);
|
||||
|
||||
fn = [c, remove_parent_curve_rotation, remove_parent_curve_translation, parent_curve_fn](double u) -> Eigen::Matrix4d {
|
||||
fn = [curve_segment_placement, parent_curve_start_point, parent_curve_fn](double u) -> Eigen::Matrix4d {
|
||||
// The parent curve function returns the cant rotation and superelevation for the parent curve.
|
||||
// Subtract the parent_curve_start_point to get the incremental cant rotation and superelevation
|
||||
// Add the incremental cant rotation and superelevation to curve_segment_placement to get the curve_segment_point
|
||||
Eigen::Matrix4d parent_curve_point = (*parent_curve_fn)(u);
|
||||
Eigen::Matrix4d p = c * remove_parent_curve_rotation * remove_parent_curve_translation * parent_curve_point;
|
||||
return p;
|
||||
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;
|
||||
};
|
||||
} else {
|
||||
// The parent curve function returns the 4x4 matrix for the parent curve.
|
||||
// Subtract the parent curve start point (remove the translation and rotation)
|
||||
// to get the incremental translation and rotation. Apply the incremental
|
||||
// translation and rotation to the curve_segment_placement to get the curve_segment_point
|
||||
|
||||
// Do a negative translation of the parent curve point relative to the start of the parent curve.
|
||||
// This moves parent_curve_fn(u=0.0) to coordinate (0,0).
|
||||
// This is done so the curve_segment_placement is applied relative to (0,0)
|
||||
Eigen::Matrix4d remove_parent_curve_translation = Eigen::Matrix4d::Identity();
|
||||
remove_parent_curve_translation.col(3) = -1.0 * (*parent_curve_start_point).col(3);
|
||||
remove_parent_curve_translation(3, 3) = 1.0;
|
||||
|
||||
// Do a rotation so that the tangent of the parent curve is in the direction (1,0)
|
||||
// Example: if the parent curve IfcLine is at a 30 degree clockwise angle, this does
|
||||
// a 30 degree counter-clockwise rotation
|
||||
// Clockwise rotation matrix = [cos(angle) -sin(angle)]
|
||||
// [sin(angle) cos(angle)]
|
||||
//
|
||||
// Counter-clockwise rotation = [ cos(angle) sin(angle)]
|
||||
// [-sin(angle) cos(angle)]
|
||||
//
|
||||
// That's just a sign flip in positions (0,1) and (1,0)
|
||||
Eigen::Matrix4d remove_parent_curve_rotation = *parent_curve_start_point;
|
||||
remove_parent_curve_rotation(0, 1) *= -1.0;
|
||||
remove_parent_curve_rotation(1, 0) *= -1.0;
|
||||
remove_parent_curve_rotation.col(3) = Eigen::Vector4d(0, 0, 0, 1); // remove the parent curve placement point
|
||||
|
||||
fn = [curve_segment_placement, remove_parent_curve_rotation, remove_parent_curve_translation, parent_curve_fn](double u) -> Eigen::Matrix4d {
|
||||
auto parent_curve_point = (*parent_curve_fn)(u);
|
||||
return (*curve_segment_placement) * remove_parent_curve_rotation * remove_parent_curve_translation * parent_curve_point;
|
||||
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;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -890,7 +961,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcCurveSegment* inst) {
|
||||
|
||||
taxonomy::piecewise_function::spans_t spans;
|
||||
spans.emplace_back(fabs(length), fn);
|
||||
auto pwf = taxonomy::make<taxonomy::piecewise_function>(0.0, spans,&settings_,inst);
|
||||
auto pwf = taxonomy::make<taxonomy::piecewise_function>(0.0, spans,inst);
|
||||
return pwf;
|
||||
}
|
||||
|
||||
|
||||
@@ -41,8 +41,8 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcEllipse* inst) {
|
||||
// @todo is a copy necesary here or can this be done in place?
|
||||
auto m4_copy = *el->matrix;
|
||||
el->matrix->components() <<
|
||||
-m4_copy.components().col(1),
|
||||
m4_copy.components().col(0),
|
||||
m4_copy.components().col(1),
|
||||
-m4_copy.components().col(0),
|
||||
m4_copy.components().col(2),
|
||||
m4_copy.components().col(3);
|
||||
std::swap(x, y);
|
||||
|
||||
@@ -39,6 +39,9 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcEllipseProfileDef* inst) {
|
||||
#endif
|
||||
if (has_position) {
|
||||
m4 = taxonomy::cast<taxonomy::matrix4>(map(inst->Position()));
|
||||
} else {
|
||||
// matrix needs to be set on elementary curves.
|
||||
m4 = taxonomy::make<taxonomy::matrix4>();
|
||||
}
|
||||
|
||||
if (ry > rx) {
|
||||
@@ -58,9 +61,9 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcEllipseProfileDef* inst) {
|
||||
auto el = taxonomy::make<taxonomy::ellipse>();
|
||||
el->radius = rx;
|
||||
el->radius2 = ry;
|
||||
el->matrix = m4;
|
||||
ed->basis = el;
|
||||
lp->children.push_back(ed);
|
||||
fc->children.push_back(lp);
|
||||
fc->matrix = m4;
|
||||
return fc;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
********************************************************************************/
|
||||
|
||||
#include "mapping.h"
|
||||
#include "../piecewise_function_evaluator.h"
|
||||
#define mapping POSTFIX_SCHEMA(mapping)
|
||||
using namespace ifcopenshell::geometry;
|
||||
|
||||
@@ -34,6 +35,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcFixedReferenceSweptAreaSolid
|
||||
|
||||
// @todo currently only the case is handled where directrix returns a piecewise_function
|
||||
if (auto pwf = taxonomy::dcast<taxonomy::piecewise_function>(dir)) {
|
||||
piecewise_function_evaluator evaluator(pwf,&settings_);
|
||||
double start = 0;
|
||||
double end = pwf->length();
|
||||
#ifdef SCHEMA_HAS_IfcDirectrixCurveSweptAreaSolid
|
||||
@@ -53,20 +55,9 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcFixedReferenceSweptAreaSolid
|
||||
}
|
||||
}
|
||||
#endif
|
||||
auto curve_length = end - start;
|
||||
auto param_type = settings_.get<ifcopenshell::geometry::settings::PiecewiseStepType>().get();
|
||||
auto param = settings_.get<ifcopenshell::geometry::settings::PiecewiseStepParam>().get();
|
||||
size_t num_steps = 0;
|
||||
if (param_type == ifcopenshell::geometry::settings::PiecewiseStepMethod::MAXSTEPSIZE) {
|
||||
// parameter is max step size
|
||||
num_steps = (size_t) std::ceil(curve_length / param);
|
||||
} else {
|
||||
// parameter is minimum number of steps
|
||||
num_steps = (size_t) std::ceil(param);
|
||||
}
|
||||
for (size_t i = 0; i <= num_steps; ++i) {
|
||||
auto distalong = start + curve_length / num_steps * i;
|
||||
auto m4 = pwf->evaluate(distalong);
|
||||
auto evaluation_points = evaluator.evaluation_points();
|
||||
for (const auto& dist_along : evaluation_points) {
|
||||
auto m4 = evaluator.evaluate(dist_along);
|
||||
|
||||
/*
|
||||
std::stringstream ss;
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
********************************************************************************/
|
||||
|
||||
#include "mapping.h"
|
||||
#include "../piecewise_function_evaluator.h"
|
||||
#define mapping POSTFIX_SCHEMA(mapping)
|
||||
using namespace ifcopenshell::geometry;
|
||||
|
||||
@@ -55,7 +56,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcGradientCurve* inst) {
|
||||
double gradient_start = m(0, 3); // start of vertical (row 0, col 3) - "Distance Along" horizontal curve
|
||||
|
||||
// create the vertical pwf
|
||||
auto vertical = taxonomy::make<taxonomy::piecewise_function>(gradient_start, pwfs, &settings_);
|
||||
auto vertical = taxonomy::make<taxonomy::piecewise_function>(gradient_start, pwfs);
|
||||
|
||||
// Determine the valid domain of the PWF... the valid domain is where both
|
||||
// the base curve and gradient curves are defined
|
||||
@@ -69,11 +70,12 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcGradientCurve* inst) {
|
||||
}
|
||||
|
||||
// define the callback function for the gradient curve
|
||||
auto composition = [horizontal, vertical](double u)->Eigen::Matrix4d {
|
||||
piecewise_function_evaluator horizontal_evaluator(horizontal, &settings_), vertical_evaluator(vertical, &settings_);
|
||||
auto composition = [horizontal_evaluator, vertical_evaluator,start=vertical->start()](double u) -> Eigen::Matrix4d {
|
||||
// u is distance from start of gradient curve (vertical)
|
||||
// add vertical->start() to u to get distance from start of horizontal
|
||||
auto xy = horizontal->evaluate(u + vertical->start());
|
||||
auto uz = vertical->evaluate(u);
|
||||
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
|
||||
uz.col(1).swap(uz.col(2)); // uz is 2D in distance along - y plane, swap y and z so elevations become z
|
||||
@@ -86,7 +88,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcGradientCurve* inst) {
|
||||
|
||||
taxonomy::piecewise_function::spans_t spans;
|
||||
spans.emplace_back(length, composition);
|
||||
auto pwf = taxonomy::make<taxonomy::piecewise_function>(start, spans, &settings_, inst);
|
||||
auto pwf = taxonomy::make<taxonomy::piecewise_function>(start, spans, inst);
|
||||
return pwf;
|
||||
}
|
||||
|
||||
|
||||
@@ -65,16 +65,21 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcObjectPlacement* inst) {
|
||||
}
|
||||
}
|
||||
|
||||
taxonomy::ptr result;
|
||||
taxonomy::matrix4::ptr result;
|
||||
if (!parent_placement_ignored && relative_to) {
|
||||
result = taxonomy::make<taxonomy::matrix4>(
|
||||
// @nb this is a bit silly, in 0.7 we didn't have a recursive function
|
||||
// but a while loop to apply the hierarchical placements, so after the
|
||||
// loop we could apply the global offset. Since we have a recursive
|
||||
// function now we need to undo the global offset when recursing.
|
||||
offset_and_rotation_.inverse() *
|
||||
taxonomy::cast<taxonomy::matrix4>(map(relative_to))->ccomponents() *
|
||||
taxonomy::cast<taxonomy::matrix4>(map(transform))->ccomponents()
|
||||
);
|
||||
} else {
|
||||
// The parent placement of the current is a placement for a type that is
|
||||
// being ignored (Site or Building) or it is the host element of an opening.
|
||||
result = map(transform);
|
||||
result = taxonomy::cast<taxonomy::matrix4>(map(transform));
|
||||
}
|
||||
|
||||
if (fallback) {
|
||||
@@ -84,10 +89,9 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcObjectPlacement* inst) {
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
result->components() = offset_and_rotation_ * result->ccomponents();
|
||||
|
||||
// @todo
|
||||
// m4->components() = offset_and_rotation_ * m4->components();
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include "mapping.h"
|
||||
#include "../profile_helper.h"
|
||||
#include "../piecewise_function_evaluator.h"
|
||||
#define mapping POSTFIX_SCHEMA(mapping)
|
||||
using namespace ifcopenshell::geometry;
|
||||
|
||||
@@ -144,11 +145,12 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcOffsetCurveByDistances* inst
|
||||
offset_spans.emplace_back(l, fn);
|
||||
}
|
||||
|
||||
auto offsets = taxonomy::make<taxonomy::piecewise_function>(start,offset_spans,&settings_);
|
||||
auto offsets = taxonomy::make<taxonomy::piecewise_function>(start,offset_spans);
|
||||
|
||||
auto composition = [pw_curve, offsets](double u) -> Eigen::Matrix4d {
|
||||
auto p = pw_curve->evaluate(u);
|
||||
auto offset = offsets->evaluate(u);
|
||||
piecewise_function_evaluator pw_evaluator(pw_curve, &settings_), offsets_evaluator(offsets, &settings_);
|
||||
auto composition = [pw_evaluator, offsets_evaluator](double u) -> Eigen::Matrix4d {
|
||||
auto p = pw_evaluator.evaluate(u);
|
||||
auto offset = offsets_evaluator.evaluate(u);
|
||||
Eigen::Matrix4d m = p * offset;
|
||||
return m;
|
||||
};
|
||||
@@ -157,7 +159,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcOffsetCurveByDistances* inst
|
||||
// this may change depending on decisions in the bSI-IF
|
||||
taxonomy::piecewise_function::spans_t spans;
|
||||
spans.emplace_back(basis_curve_length, composition);
|
||||
auto pwf = taxonomy::make<taxonomy::piecewise_function>(start,spans,&settings_,inst);
|
||||
auto pwf = taxonomy::make<taxonomy::piecewise_function>(start,spans,inst);
|
||||
return pwf;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include "mapping.h"
|
||||
#include "../profile_helper.h"
|
||||
#include "../piecewise_function_evaluator.h"
|
||||
|
||||
#define mapping POSTFIX_SCHEMA(mapping)
|
||||
using namespace ifcopenshell::geometry;
|
||||
@@ -31,7 +32,8 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcPointByDistanceExpression* i
|
||||
//auto item = map(basis_curve);
|
||||
//auto pw_curve = ifcopenshell::geometry::piecewise_from_item(item);
|
||||
auto pw_curve = taxonomy::dcast<taxonomy::piecewise_function>(map(inst->BasisCurve()));
|
||||
auto m = pw_curve->evaluate(u);
|
||||
piecewise_function_evaluator evaluator(pw_curve,&settings_);
|
||||
auto m = evaluator.evaluate(u);
|
||||
|
||||
auto o = m.col(3).head<3>();
|
||||
auto z = m.col(2).head<3>();
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
#define mapping POSTFIX_SCHEMA(mapping)
|
||||
using namespace ifcopenshell::geometry;
|
||||
|
||||
#include "../profile_helper.h"
|
||||
|
||||
taxonomy::ptr mapping::map_impl(const IfcSchema::IfcRectangularPyramid* inst) {
|
||||
const double dx = inst->XLength() * length_unit_;
|
||||
const double dy = inst->YLength() * length_unit_;
|
||||
@@ -33,96 +35,77 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcRectangularPyramid* inst) {
|
||||
// Base
|
||||
{
|
||||
auto face = taxonomy::make<taxonomy::face>();
|
||||
auto loop = taxonomy::make<taxonomy::loop>();
|
||||
face->children.push_back(loop);
|
||||
loop->external = true;
|
||||
shell->children.push_back(face);
|
||||
|
||||
std::array<taxonomy::point3::ptr, 4> points{
|
||||
std::vector<taxonomy::point3::ptr> points{
|
||||
taxonomy::make<taxonomy::point3>(0, 0, 0),
|
||||
taxonomy::make<taxonomy::point3>(dx, 0, 0),
|
||||
taxonomy::make<taxonomy::point3>(0, dy, 0),
|
||||
taxonomy::make<taxonomy::point3>(dx, dy, 0),
|
||||
taxonomy::make<taxonomy::point3>(0, dy, 0)
|
||||
taxonomy::make<taxonomy::point3>(dx, 0, 0),
|
||||
};
|
||||
|
||||
loop->children.push_back(taxonomy::make<taxonomy::edge>(points[0], points[1]));
|
||||
loop->children.push_back(taxonomy::make<taxonomy::edge>(points[1], points[2]));
|
||||
loop->children.push_back(taxonomy::make<taxonomy::edge>(points[2], points[3]));
|
||||
loop->children.push_back(taxonomy::make<taxonomy::edge>(points[3], points[0]));
|
||||
points.push_back(points.front());
|
||||
face->children.push_back(polygon_from_points(points));
|
||||
}
|
||||
|
||||
// Lateral faces
|
||||
{
|
||||
auto face = taxonomy::make<taxonomy::face>();
|
||||
auto loop = taxonomy::make<taxonomy::loop>();
|
||||
face->children.push_back(loop);
|
||||
loop->external = true;
|
||||
shell->children.push_back(face);
|
||||
|
||||
std::array<taxonomy::point3::ptr, 3> points{
|
||||
std::vector<taxonomy::point3::ptr> points{
|
||||
taxonomy::make<taxonomy::point3>(0, 0, 0),
|
||||
taxonomy::make<taxonomy::point3>(0, dy, 0),
|
||||
taxonomy::make<taxonomy::point3>(0.5*dx, 0.5*dy, dz)
|
||||
};
|
||||
|
||||
loop->children.push_back(taxonomy::make<taxonomy::edge>(points[0], points[1]));
|
||||
loop->children.push_back(taxonomy::make<taxonomy::edge>(points[1], points[2]));
|
||||
loop->children.push_back(taxonomy::make<taxonomy::edge>(points[2], points[0]));
|
||||
points.push_back(points.front());
|
||||
face->children.push_back(polygon_from_points(points));
|
||||
}
|
||||
|
||||
{
|
||||
auto face = taxonomy::make<taxonomy::face>();
|
||||
auto loop = taxonomy::make<taxonomy::loop>();
|
||||
face->children.push_back(loop);
|
||||
loop->external = true;
|
||||
shell->children.push_back(face);
|
||||
|
||||
std::array<taxonomy::point3::ptr, 3> points{
|
||||
std::vector<taxonomy::point3::ptr> points{
|
||||
taxonomy::make<taxonomy::point3>(0, dy, 0),
|
||||
taxonomy::make<taxonomy::point3>(dx, dy, 0),
|
||||
taxonomy::make<taxonomy::point3>(0.5*dx, 0.5*dy, dz)
|
||||
};
|
||||
|
||||
loop->children.push_back(taxonomy::make<taxonomy::edge>(points[0], points[1]));
|
||||
loop->children.push_back(taxonomy::make<taxonomy::edge>(points[1], points[2]));
|
||||
loop->children.push_back(taxonomy::make<taxonomy::edge>(points[2], points[0]));
|
||||
points.push_back(points.front());
|
||||
face->children.push_back(polygon_from_points(points));
|
||||
}
|
||||
|
||||
{
|
||||
auto face = taxonomy::make<taxonomy::face>();
|
||||
auto loop = taxonomy::make<taxonomy::loop>();
|
||||
face->children.push_back(loop);
|
||||
loop->external = true;
|
||||
shell->children.push_back(face);
|
||||
|
||||
std::array<taxonomy::point3::ptr, 3> points{
|
||||
std::vector<taxonomy::point3::ptr> points{
|
||||
taxonomy::make<taxonomy::point3>(dx, dy, 0),
|
||||
taxonomy::make<taxonomy::point3>(dx, 0, 0),
|
||||
taxonomy::make<taxonomy::point3>(0.5*dx, 0.5*dy, dz)
|
||||
};
|
||||
|
||||
loop->children.push_back(taxonomy::make<taxonomy::edge>(points[0], points[1]));
|
||||
loop->children.push_back(taxonomy::make<taxonomy::edge>(points[1], points[2]));
|
||||
loop->children.push_back(taxonomy::make<taxonomy::edge>(points[2], points[0]));
|
||||
points.push_back(points.front());
|
||||
face->children.push_back(polygon_from_points(points));
|
||||
}
|
||||
|
||||
{
|
||||
auto face = taxonomy::make<taxonomy::face>();
|
||||
auto loop = taxonomy::make<taxonomy::loop>();
|
||||
face->children.push_back(loop);
|
||||
loop->external = true;
|
||||
shell->children.push_back(face);
|
||||
|
||||
std::array<taxonomy::point3::ptr, 3> points{
|
||||
std::vector<taxonomy::point3::ptr> points{
|
||||
taxonomy::make<taxonomy::point3>(dx, 0, 0),
|
||||
taxonomy::make<taxonomy::point3>(0, 0, 0),
|
||||
taxonomy::make<taxonomy::point3>(0.5*dx, 0.5*dy, dz)
|
||||
};
|
||||
|
||||
loop->children.push_back(taxonomy::make<taxonomy::edge>(points[0], points[1]));
|
||||
loop->children.push_back(taxonomy::make<taxonomy::edge>(points[1], points[2]));
|
||||
loop->children.push_back(taxonomy::make<taxonomy::edge>(points[2], points[0]));
|
||||
points.push_back(points.front());
|
||||
face->children.push_back(polygon_from_points(points));
|
||||
}
|
||||
|
||||
solid->matrix = taxonomy::cast<taxonomy::matrix4>(map(inst->Position()));
|
||||
|
||||
return solid;
|
||||
}
|
||||
|
||||
@@ -22,28 +22,10 @@
|
||||
using namespace ifcopenshell::geometry;
|
||||
|
||||
#include "../../ifcgeom/profile_helper.h"
|
||||
|
||||
#include <boost/range/combine.hpp>
|
||||
#include "../../ifcgeom/infra_sweep_helper.h"
|
||||
|
||||
#ifdef SCHEMA_HAS_IfcSectionedSolidHorizontal
|
||||
|
||||
namespace {
|
||||
// std::lerp when upgrading to C++ 20
|
||||
template <typename T>
|
||||
T lerp(const T& a, const T& b, double t) {
|
||||
return a + t * (b - a);
|
||||
}
|
||||
|
||||
struct cross_section {
|
||||
double dist_along;
|
||||
taxonomy::face::ptr section_geometry;
|
||||
Eigen::Vector3d offset;
|
||||
|
||||
bool operator <(const cross_section& other) const {
|
||||
return dist_along < other.dist_along;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
taxonomy::ptr mapping::map_impl(const IfcSchema::IfcSectionedSolidHorizontal* inst) {
|
||||
std::vector<cross_section> cross_sections;
|
||||
@@ -105,162 +87,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcSectionedSolidHorizontal* in
|
||||
}
|
||||
}
|
||||
|
||||
std::sort(cross_sections.begin(), cross_sections.end());
|
||||
|
||||
auto loft = taxonomy::make<taxonomy::loft>();
|
||||
// @todo intialize as default
|
||||
loft->axis = nullptr;
|
||||
|
||||
// @todo currently only the case is handled where directrix returns a piecewise_function
|
||||
// @todo this "if" statement is not really required because the function returns at the start if the Directrix is not a piecewise function
|
||||
if (pwf) {
|
||||
double start = std::max(0., cross_sections.front().dist_along);
|
||||
double end = std::min(pwf->length(), cross_sections.back().dist_along);
|
||||
|
||||
if (end - start < 1.e-9) {
|
||||
Logger::Warning("Empty sweep domain with start at " + std::to_string(cross_sections.front().dist_along) + " end at " + std::to_string(cross_sections.back().dist_along) + " and curve domain length " + std::to_string(pwf->length()), inst);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto curve_length = end - start;
|
||||
auto param_type = settings_.get<ifcopenshell::geometry::settings::PiecewiseStepType>().get();
|
||||
auto param = settings_.get<ifcopenshell::geometry::settings::PiecewiseStepParam>().get();
|
||||
size_t num_steps = 0;
|
||||
if (param_type == ifcopenshell::geometry::settings::PiecewiseStepMethod::MAXSTEPSIZE) {
|
||||
// parameter is max step size
|
||||
num_steps = (size_t) std::ceil(curve_length / param);
|
||||
} else {
|
||||
// parameter is minimum number of steps
|
||||
num_steps = (size_t) std::ceil(param);
|
||||
}
|
||||
std::vector<double> longitudes;
|
||||
for (auto& x : cross_sections) {
|
||||
longitudes.push_back(x.dist_along);
|
||||
}
|
||||
longitudes.push_back(std::numeric_limits<double>::infinity());
|
||||
auto profile_index = longitudes.begin();
|
||||
for (size_t i = 0; i <= num_steps; ++i) {
|
||||
auto dist_along = start + curve_length / num_steps * i;
|
||||
while (dist_along > *(profile_index+1)) {
|
||||
profile_index++;
|
||||
if (profile_index == longitudes.end()) {
|
||||
// @todo handle this?
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
taxonomy::face::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.
|
||||
bool should_interpolate =
|
||||
(profile_index + 1 < longitudes.end()) &&
|
||||
(relative_dist_along >= 1.e-9 || offset_a.cwiseAbs().maxCoeff() > 0.);
|
||||
|
||||
if (should_interpolate) {
|
||||
taxonomy::face::ptr profile_b;
|
||||
Eigen::Vector3d offset_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;
|
||||
} else {
|
||||
profile_b = profile_a;
|
||||
offset_b = offset_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.);
|
||||
|
||||
if (should_interpolate2) {
|
||||
if (profile_a->children.size() != profile_b->children.size()) {
|
||||
Logger::Warning("Mismatching number of face boundaries: " +
|
||||
std::to_string(profile_a->children.size()) + " vs " +
|
||||
std::to_string(profile_b->children.size()),
|
||||
inst
|
||||
);
|
||||
return nullptr;
|
||||
}
|
||||
interpolated = taxonomy::make<taxonomy::face>();
|
||||
// @todo should_interpolate should also be informed based by different face matrices.
|
||||
if (profile_a->matrix || profile_b->matrix) {
|
||||
interpolated->matrix = taxonomy::make<taxonomy::matrix4>();
|
||||
Eigen::Matrix4d m4a = Eigen::Matrix4d::Identity();
|
||||
Eigen::Matrix4d m4b = Eigen::Matrix4d::Identity();
|
||||
if (profile_a->matrix) {
|
||||
m4a = profile_a->matrix->ccomponents();
|
||||
}
|
||||
if (profile_b->matrix) {
|
||||
m4b = profile_b->matrix->ccomponents();
|
||||
}
|
||||
interpolated->matrix->components() = lerp(m4a, m4b, relative_dist_along);
|
||||
}
|
||||
auto interpolated_offset = lerp(offset_a, offset_b, relative_dist_along);
|
||||
taxonomy::loop::ptr w1, w2;
|
||||
taxonomy::edge::ptr e1, e2;
|
||||
for (auto tmp_ : boost::combine(profile_a->children, profile_b->children)) {
|
||||
boost::tie(w1, w2) = tmp_;
|
||||
if (w1->children.size() != w2->children.size()) {
|
||||
Logger::Warning("Mismatching number of edges for face boundary: " +
|
||||
std::to_string(w1->children.size()) + " vs " +
|
||||
std::to_string(w2->children.size()),
|
||||
inst
|
||||
);
|
||||
return nullptr;
|
||||
}
|
||||
std::vector<taxonomy::point3::ptr> points;
|
||||
for (auto tmp__ : boost::combine(w1->children, w2->children)) {
|
||||
boost::tie(e1, e2) = tmp__;
|
||||
auto& p1 = boost::get<taxonomy::point3::ptr>(e1->start);
|
||||
auto& p2 = boost::get<taxonomy::point3::ptr>(e2->start);
|
||||
|
||||
auto p3 = (lerp(p1->ccomponents(), p2->ccomponents(), relative_dist_along) + interpolated_offset).eval();
|
||||
points.push_back(taxonomy::make<taxonomy::point3>(p3));
|
||||
}
|
||||
if (!points.empty()) {
|
||||
// close polygon by referencing first point
|
||||
// @todo add a closed=true|false to polygon_from_points()?
|
||||
points.push_back(points.front());
|
||||
}
|
||||
interpolated->children.push_back(polygon_from_points(points));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto m4 = pwf->evaluate(dist_along);
|
||||
/* {
|
||||
std::wcout << "#" << pwf->instance->data().id() << " " << dist_along << ": " << m4.col(3).row(2).value() << std::endl;
|
||||
}*/
|
||||
|
||||
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();
|
||||
m4b.col(3).head<3>() = m4.col(3).head<3>();
|
||||
|
||||
if (interpolated) {
|
||||
loft->children.push_back(interpolated);
|
||||
} else {
|
||||
loft->children.push_back(taxonomy::face::ptr(profile_a->clone_()));
|
||||
if (profile_a->matrix) {
|
||||
loft->children.back()->matrix = taxonomy::matrix4::ptr(profile_a->matrix->clone_());
|
||||
}
|
||||
}
|
||||
if (!loft->children.back()->matrix) {
|
||||
// @todo should this not be initialized by default? matrix4 already has a 'lazy identity' mechanism.
|
||||
loft->children.back()->matrix = taxonomy::make<taxonomy::matrix4>();
|
||||
}
|
||||
auto m = (m4b * loft->children.back()->matrix->ccomponents()).eval();
|
||||
loft->children.back()->matrix->components() = m;
|
||||
}
|
||||
}
|
||||
|
||||
return loft;
|
||||
return make_loft(settings_, inst, pwf, cross_sections);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
/********************************************************************************
|
||||
* *
|
||||
* This file is part of IfcOpenShell. *
|
||||
* *
|
||||
* IfcOpenShell is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the Lesser GNU General Public License as published by *
|
||||
* the Free Software Foundation, either version 3.0 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* IfcOpenShell is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* Lesser GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the Lesser GNU General Public License *
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
#include "mapping.h"
|
||||
#define mapping POSTFIX_SCHEMA(mapping)
|
||||
using namespace ifcopenshell::geometry;
|
||||
|
||||
#include "../../ifcgeom/profile_helper.h"
|
||||
#include "../../ifcgeom/infra_sweep_helper.h"
|
||||
|
||||
#ifdef SCHEMA_HAS_IfcSectionedSurface
|
||||
|
||||
|
||||
taxonomy::ptr mapping::map_impl(const IfcSchema::IfcSectionedSurface* inst) {
|
||||
std::vector<cross_section> cross_sections;
|
||||
|
||||
auto dir = map(inst->Directrix());
|
||||
auto pwf = taxonomy::dcast<taxonomy::piecewise_function>(dir);
|
||||
if (!pwf) {
|
||||
// Only implement on alignment curves
|
||||
Logger::Warning("IfcSectionedSurface is only implemented for piecewise function Directrix curves", inst);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
{
|
||||
auto css = inst->CrossSections();
|
||||
auto csps = inst->CrossSectionPositions();
|
||||
std::vector<taxonomy::geom_item::ptr> faces;
|
||||
|
||||
// The PointByDistanceExpressesions 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<double> longitudes;
|
||||
|
||||
for (auto& cs : *css) {
|
||||
faces.push_back(std::move(taxonomy::cast<taxonomy::geom_item>(map(cs))));
|
||||
}
|
||||
#ifdef SCHEMA_HAS_IfcPointByDistanceExpression
|
||||
for (auto& csp : *csps) {
|
||||
auto pbde = csp->Location()->as<IfcSchema::IfcPointByDistanceExpression>(true);
|
||||
|
||||
longitudes.push_back(*pbde->DistanceAlong()->as<IfcSchema::IfcLengthMeasure>(true) * length_unit_);
|
||||
|
||||
// Corresponds to the profile X, Y directions (hopefully).
|
||||
Eigen::Vector3d po(
|
||||
pbde->OffsetLateral().get_value_or(0.),
|
||||
// @todo I don't understand whether vertical is an offset relative to the tangent plane or to the global XY plane
|
||||
pbde->OffsetVertical().get_value_or(0.),
|
||||
0.
|
||||
);
|
||||
|
||||
profile_offsets.push_back(po);
|
||||
}
|
||||
#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;
|
||||
}
|
||||
if (faces.size() < 2) {
|
||||
Logger::Warning("Expected at least two cross sections, but got " + std::to_string(faces.size()), inst);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < faces.size(); ++i) {
|
||||
cross_sections.push_back({ longitudes[i], faces[i], profile_offsets[i] });
|
||||
}
|
||||
}
|
||||
|
||||
return make_loft(settings_, inst, pwf, cross_sections);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -21,6 +21,8 @@
|
||||
#define mapping POSTFIX_SCHEMA(mapping)
|
||||
using namespace ifcopenshell::geometry;
|
||||
|
||||
#include "../piecewise_function_evaluator.h"
|
||||
|
||||
#ifdef SCHEMA_HAS_IfcSegmentedReferenceCurve
|
||||
|
||||
taxonomy::ptr mapping::map_impl(const IfcSchema::IfcSegmentedReferenceCurve* inst) {
|
||||
@@ -53,7 +55,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcSegmentedReferenceCurve* ins
|
||||
const Eigen::Matrix4d& m = p->ccomponents();
|
||||
double cant_start = m(0, 3); // start of cant curve
|
||||
|
||||
auto cant = taxonomy::make<taxonomy::piecewise_function>(cant_start,pwfs,&settings_);
|
||||
auto cant = taxonomy::make<taxonomy::piecewise_function>(cant_start,pwfs);
|
||||
|
||||
// Determine the valid domain of the PWF... the valid domain is where
|
||||
// horizontal, gradient and cant curves are defined
|
||||
@@ -68,24 +70,45 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcSegmentedReferenceCurve* ins
|
||||
}
|
||||
|
||||
// define the callback function for the segmented reference curve
|
||||
auto composition = [gradient, cant](double u)->Eigen::Matrix4d {
|
||||
piecewise_function_evaluator gradient_evaluator(gradient, &settings_), cant_evaluator(cant, &settings_);
|
||||
auto composition = [gradient_evaluator, cant_evaluator, start = cant->start()](double u) -> Eigen::Matrix4d {
|
||||
// u is distance from start of cant curve
|
||||
// add cant->start() to u to get the distance from start of gradient curve
|
||||
auto g = gradient->evaluate(u+cant->start());
|
||||
auto c = cant->evaluate(u);
|
||||
auto g = gradient_evaluator.evaluate(u + start);
|
||||
auto c = cant_evaluator.evaluate(u);
|
||||
|
||||
c.col(3)(0) = 0.0; // x is distance along. zero it out so it doesn't add to the x from gradient curve
|
||||
c.col(1).swap(c.col(2)); // c is 2D in distance along - y plane, swap y and z so elevations become z
|
||||
c.row(1).swap(c.row(2));
|
||||
// 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
|
||||
//
|
||||
// However, the coordinate points don't need to have the rotations
|
||||
// of g applied. Save off the x,y,z and cant values
|
||||
auto x = g(0, 3);
|
||||
auto y = g(1, 3);
|
||||
auto z = g(2, 3);
|
||||
auto s = c(1, 3); // superelevation
|
||||
|
||||
// change column 3 to (0,0,0,1)
|
||||
Eigen::Vector4d p(0, 0, 0, 1);
|
||||
g.col(3) = p;
|
||||
c.col(3) = p;
|
||||
|
||||
// multiply g and c to get the axes in the correct orientation
|
||||
Eigen::Matrix4d m = g * c;
|
||||
|
||||
// reinstate the values for x and y.
|
||||
// z is the gradient curve z value plus the superelevation
|
||||
// that comes from the cant.
|
||||
m(0, 3) = x;
|
||||
m(1, 3) = y;
|
||||
m(2, 3) = z + s;
|
||||
|
||||
Eigen::Matrix4d m;
|
||||
m = g * c;
|
||||
return m;
|
||||
};
|
||||
|
||||
taxonomy::piecewise_function::spans_t spans;
|
||||
spans.emplace_back(length, composition);
|
||||
auto pwf = taxonomy::make<taxonomy::piecewise_function>(start, spans, &settings_, inst);
|
||||
auto pwf = taxonomy::make<taxonomy::piecewise_function>(start, spans, inst);
|
||||
return pwf;
|
||||
}
|
||||
|
||||
|
||||
@@ -165,10 +165,8 @@ aggregate_of_instance::ptr mapping::find_openings(const IfcUtil::IfcBaseEntity*
|
||||
return openings;
|
||||
}
|
||||
|
||||
auto product = inst->as<IfcSchema::IfcProduct>();
|
||||
|
||||
if (product->as<IfcSchema::IfcElement>() && !product->as<IfcSchema::IfcFeatureElementSubtraction>()) {
|
||||
const IfcSchema::IfcElement* element = product->as<IfcSchema::IfcElement>();
|
||||
if (inst->as<IfcSchema::IfcElement>() && !inst->as<IfcSchema::IfcFeatureElementSubtraction>()) {
|
||||
const IfcSchema::IfcElement* element = inst->as<IfcSchema::IfcElement>();
|
||||
auto rels = element->HasOpenings();
|
||||
for (auto& rel : *rels) {
|
||||
openings->push(rel->RelatedOpeningElement());
|
||||
@@ -176,20 +174,22 @@ aggregate_of_instance::ptr mapping::find_openings(const IfcUtil::IfcBaseEntity*
|
||||
}
|
||||
|
||||
// Is the IfcElement a decomposition of an IfcElement with any IfcOpeningElements?
|
||||
const IfcSchema::IfcObjectDefinition* obdef = product->as<IfcSchema::IfcObjectDefinition>();
|
||||
for (;;) {
|
||||
auto decomposes = obdef->Decomposes()->generalize();
|
||||
if (decomposes->size() != 1) break;
|
||||
IfcSchema::IfcObjectDefinition* rel_obdef = (*decomposes->begin())->as<IfcSchema::IfcRelAggregates>()->RelatingObject();
|
||||
if (rel_obdef->as<IfcSchema::IfcElement>() && !rel_obdef->as<IfcSchema::IfcFeatureElementSubtraction>()) {
|
||||
IfcSchema::IfcElement* element = rel_obdef->as<IfcSchema::IfcElement>();
|
||||
auto rels = element->HasOpenings();
|
||||
for (auto& rel : *rels) {
|
||||
openings->push(rel->RelatedOpeningElement());
|
||||
const IfcSchema::IfcObjectDefinition* obdef = inst->as<IfcSchema::IfcObjectDefinition>();
|
||||
if (obdef != nullptr) {
|
||||
for (;;) {
|
||||
auto decomposes = obdef->Decomposes()->generalize();
|
||||
if (decomposes->size() != 1) break;
|
||||
IfcSchema::IfcObjectDefinition* rel_obdef = (*decomposes->begin())->as<IfcSchema::IfcRelAggregates>()->RelatingObject();
|
||||
if (rel_obdef->as<IfcSchema::IfcElement>() && !rel_obdef->as<IfcSchema::IfcFeatureElementSubtraction>()) {
|
||||
IfcSchema::IfcElement* element = rel_obdef->as<IfcSchema::IfcElement>();
|
||||
auto rels = element->HasOpenings();
|
||||
for (auto& rel : *rels) {
|
||||
openings->push(rel->RelatedOpeningElement());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
obdef = rel_obdef;
|
||||
obdef = rel_obdef;
|
||||
}
|
||||
}
|
||||
|
||||
return openings;
|
||||
@@ -810,6 +810,28 @@ void mapping::initialize_units_() {
|
||||
if (settings_.get<settings::SiteLocalPlacement>().get()) {
|
||||
placement_rel_to_type_ = file_->schema()->declaration_by_name("IfcSite");
|
||||
}
|
||||
|
||||
// Translation is applied first, then rotation.
|
||||
if (settings_.get<ModelOffset>().has()) {
|
||||
auto vs = settings_.get<ModelOffset>().get();
|
||||
if (vs.size() == 3) {
|
||||
offset_and_rotation_ *= Eigen::Affine3d(Eigen::Translation3d(vs[0], vs[1], vs[2])).matrix();
|
||||
} else {
|
||||
Logger::Error("Expected 3 values for model-offset setting");
|
||||
}
|
||||
}
|
||||
|
||||
if (settings_.get<ModelRotation>().has()) {
|
||||
auto vs = settings_.get<ModelRotation>().get();
|
||||
if (vs.size() == 4) {
|
||||
auto m3 = Eigen::Quaterniond(vs[0], vs[1], vs[2], vs[3]).matrix();
|
||||
Eigen::Matrix4d m4 = Eigen::Matrix4d::Identity();
|
||||
m4 << m3;
|
||||
offset_and_rotation_ *= m4;
|
||||
} else {
|
||||
Logger::Error("Expected 4 values for model-rotation setting");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void mapping::initialize_settings() {
|
||||
|
||||
@@ -30,6 +30,8 @@ namespace geometry {
|
||||
|
||||
const IfcParse::declaration* placement_rel_to_type_;
|
||||
const IfcUtil::IfcBaseEntity* placement_rel_to_instance_;
|
||||
|
||||
Eigen::Matrix4d offset_and_rotation_ = Eigen::Matrix4d::Identity();
|
||||
|
||||
void initialize_units_();
|
||||
void addRepresentationsFromContextIds(IfcSchema::IfcRepresentation::list::ptr&);
|
||||
@@ -52,7 +54,7 @@ namespace geometry {
|
||||
try {
|
||||
if (inst->as<IfcSchema::IfcRepresentationItem>() && !inst->as<IfcSchema::IfcStyledItem>() &&
|
||||
/* @todo */
|
||||
(item->kind() == taxonomy::SOLID || item->kind() == taxonomy::SHELL || item->kind() == taxonomy::COLLECTION || item->kind() == taxonomy::EXTRUSION || item->kind() == taxonomy::LOFT || item->kind() == taxonomy::BOOLEAN_RESULT || item->kind() == taxonomy::REVOLVE || item->kind() == taxonomy::SWEEP_ALONG_CURVE)
|
||||
(item->kind() == taxonomy::SOLID || item->kind() == taxonomy::SHELL || item->kind() == taxonomy::COLLECTION || item->kind() == taxonomy::EXTRUSION || item->kind() == taxonomy::LOFT || item->kind() == taxonomy::BOOLEAN_RESULT || item->kind() == taxonomy::REVOLVE || item->kind() == taxonomy::SWEEP_ALONG_CURVE || item->kind() == taxonomy::FACE)
|
||||
) {
|
||||
auto style = find_style(inst->as<IfcSchema::IfcRepresentationItem>());
|
||||
if (style) {
|
||||
|
||||
@@ -135,6 +135,9 @@ BIND(IfcFixedReferenceSweptAreaSolid)
|
||||
#ifdef SCHEMA_HAS_IfcSectionedSolidHorizontal
|
||||
BIND(IfcSectionedSolidHorizontal)
|
||||
#endif
|
||||
#ifdef SCHEMA_HAS_IfcSectionedSurface
|
||||
BIND(IfcSectionedSurface)
|
||||
#endif
|
||||
|
||||
BIND(IfcCircle);
|
||||
BIND(IfcEllipse);
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
#include "piecewise_function_evaluator.h"
|
||||
#include "profile_helper.h"
|
||||
|
||||
using namespace ifcopenshell::geometry;
|
||||
|
||||
|
||||
piecewise_function_evaluator::piecewise_function_evaluator(taxonomy::piecewise_function::const_ptr pwf, const ifcopenshell::geometry::Settings* settings) : pwf_(pwf) {
|
||||
if (settings) {
|
||||
settings_ = *settings;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<double> piecewise_function_evaluator::evaluation_points() const {
|
||||
if (!eval_points_.has_value()) {
|
||||
double curve_length = pwf_->length();
|
||||
|
||||
auto param_type = settings_.get<ifcopenshell::geometry::settings::PiecewiseStepType>().get();
|
||||
auto param = settings_.get<ifcopenshell::geometry::settings::PiecewiseStepParam>().get();
|
||||
unsigned num_steps = 0;
|
||||
if (param_type == ifcopenshell::geometry::settings::PiecewiseStepMethod::MAXSTEPSIZE) {
|
||||
// parameter is max step size
|
||||
num_steps = (unsigned)std::ceil(curve_length / param);
|
||||
} else {
|
||||
// parameter is minimum number of steps
|
||||
num_steps = (unsigned)std::ceil(param);
|
||||
}
|
||||
|
||||
eval_points_ = evaluation_points(pwf_->start(), pwf_->start() + curve_length, num_steps);
|
||||
}
|
||||
return *eval_points_;
|
||||
}
|
||||
|
||||
std::vector<double> piecewise_function_evaluator::evaluation_points(double ustart, double uend, unsigned nsteps) const {
|
||||
double curve_length = pwf_->length();
|
||||
ustart = std::max(pwf_->start(), ustart);
|
||||
uend = std::min(uend, pwf_->start() + curve_length);
|
||||
|
||||
nsteps = std::max(1u, nsteps); // never have fewer than 1 step
|
||||
|
||||
auto resolution = (uend - ustart) / nsteps;
|
||||
|
||||
std::vector<double> u_values;
|
||||
u_values.reserve(nsteps);
|
||||
|
||||
for (unsigned i = 0; i <= nsteps; ++i) {
|
||||
auto u = resolution * i + ustart;
|
||||
u_values.push_back(u);
|
||||
}
|
||||
|
||||
return u_values;
|
||||
}
|
||||
|
||||
taxonomy::item::ptr piecewise_function_evaluator::evaluate() const {
|
||||
return evaluate(evaluation_points());
|
||||
}
|
||||
|
||||
taxonomy::item::ptr piecewise_function_evaluator::evaluate(double ustart, double uend, unsigned nsteps) const {
|
||||
return evaluate(evaluation_points(ustart, uend, nsteps));
|
||||
}
|
||||
|
||||
Eigen::Matrix4d piecewise_function_evaluator::evaluate(double u) const {
|
||||
// assume monotonic evaluation and store last evaluated segment
|
||||
if (current_span_fn_ == nullptr || (u < current_span_start_ || current_span_end_ < u)) {
|
||||
// there isn't a current span or u is outside the range of the current span
|
||||
// get a new "current span"
|
||||
std::tie(current_span_start_, current_span_end_, current_span_fn_) = get_span(u);
|
||||
}
|
||||
|
||||
u -= current_span_start_; // make u relative to start of span
|
||||
return (*current_span_fn_)(u);
|
||||
}
|
||||
|
||||
taxonomy::item::ptr piecewise_function_evaluator::evaluate(const std::vector<double>& dist) const {
|
||||
std::vector<taxonomy::point3::ptr> polygon;
|
||||
polygon.reserve(dist.size());
|
||||
for (auto& u : dist) {
|
||||
Eigen::Matrix4d m = evaluate(u);
|
||||
polygon.push_back(taxonomy::make<taxonomy::point3>(m(0, 3), m(1, 3), m(2, 3)));
|
||||
}
|
||||
|
||||
return polygon_from_points(polygon);
|
||||
}
|
||||
|
||||
std::tuple<double, double, const std::function<Eigen::Matrix4d(double u)>*> piecewise_function_evaluator::get_span(double u) const {
|
||||
// force u to be within bounds of the curve
|
||||
double s = pwf_->start();
|
||||
double e = pwf_->end();
|
||||
u = std::max(s, u);
|
||||
u = std::min(u, e);
|
||||
|
||||
double span_start = s;
|
||||
for (auto& [length, fn] : pwf_->spans()) {
|
||||
double span_end = span_start + length;
|
||||
auto tolerance = settings_.get<ifcopenshell::geometry::settings::Precision>().get();
|
||||
if (span_start <= u && u < span_end + tolerance) {
|
||||
return {span_start, span_end, &fn};
|
||||
}
|
||||
span_start += length;
|
||||
}
|
||||
|
||||
Logger::Error("piecewise_function_impl::get_span span not found.");
|
||||
return {0, 0, nullptr};
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
#ifndef ITERATOR_PWF_EVALUATOR_H
|
||||
#define ITERATOR_PWF_EVALUATOR_H
|
||||
|
||||
#include "../ifcgeom/taxonomy.h"
|
||||
|
||||
#include <boost/function.hpp>
|
||||
|
||||
namespace ifcopenshell { namespace geometry {
|
||||
|
||||
/// @brief utility class to evaluate piecewise_function objects
|
||||
class piecewise_function_evaluator {
|
||||
public:
|
||||
piecewise_function_evaluator(taxonomy::piecewise_function::const_ptr pwf, const ifcopenshell::geometry::Settings* settings=nullptr);
|
||||
|
||||
/// @brief returns a vector of "distance along" points where the evaluate function computes loop points
|
||||
std::vector<double> evaluation_points() const;
|
||||
|
||||
/// @brief returns a vector of "distance along" points between ustart and uend
|
||||
/// @param ustart starting location
|
||||
/// @param uend ending location
|
||||
/// @param nsteps number of steps to evaluate
|
||||
std::vector<double> evaluation_points(double ustart, double uend, unsigned nsteps) const;
|
||||
|
||||
/// @brief evaluates the piecewise function between start and end
|
||||
/// evaluation point step size is taken from the settings object
|
||||
taxonomy::item::ptr evaluate() const;
|
||||
|
||||
/// @brief evaluates the piecewise function between ustart and uend
|
||||
/// if ustart and uend are out of range, the range of values evaluated
|
||||
/// are constrained to start_ and start_+length_
|
||||
/// @param ustart starting location
|
||||
/// @param uend ending location
|
||||
/// @param nsteps number of steps to evaluate
|
||||
/// @return taxonomy::loop::ptr
|
||||
taxonomy::item::ptr evaluate(double ustart, double uend, unsigned nsteps) const;
|
||||
|
||||
/// @brief evaluates the piecewise function at u
|
||||
/// @param u u is constrained to be between start_ and start_+length
|
||||
/// @return 4x4 placement matrix
|
||||
Eigen::Matrix4d evaluate(double u) const;
|
||||
|
||||
private:
|
||||
taxonomy::item::ptr evaluate(const std::vector<double>& dist) const;
|
||||
std::tuple<double, double, const std::function<Eigen::Matrix4d(double u)>*> get_span(double u) const;
|
||||
|
||||
taxonomy::piecewise_function::const_ptr pwf_;
|
||||
|
||||
ifcopenshell::geometry::Settings settings_;
|
||||
|
||||
mutable double current_span_start_ = 0;
|
||||
mutable double current_span_end_ = 0;
|
||||
mutable const std::function<Eigen::Matrix4d(double u)>* current_span_fn_ = nullptr;
|
||||
mutable boost::optional<std::vector<double>> eval_points_;
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif
|
||||
@@ -7,97 +7,40 @@ namespace geometry {
|
||||
|
||||
namespace taxonomy {
|
||||
|
||||
std::vector<double> ifcopenshell::geometry::taxonomy::piecewise_function_impl::evaluation_points() const {
|
||||
if (!eval_points_.has_value()) {
|
||||
double curve_length = length();
|
||||
piecewise_function_impl::piecewise_function_impl(double start, const spans_t& s) : start_(start), spans_(s) {
|
||||
}
|
||||
|
||||
auto param_type = settings_ ? settings_->get<ifcopenshell::geometry::settings::PiecewiseStepType>().get() : ifcopenshell::geometry::settings::PiecewiseStepMethod::MAXSTEPSIZE;
|
||||
auto param = settings_ ? settings_->get<ifcopenshell::geometry::settings::PiecewiseStepParam>().get() : 0.5;
|
||||
unsigned num_steps = 0;
|
||||
if (param_type == ifcopenshell::geometry::settings::PiecewiseStepMethod::MAXSTEPSIZE) {
|
||||
// parameter is max step size
|
||||
num_steps = (unsigned)std::ceil(curve_length / param);
|
||||
} else {
|
||||
// parameter is minimum number of steps
|
||||
num_steps = (unsigned)std::ceil(param);
|
||||
}
|
||||
|
||||
eval_points_ = evaluation_points(start_, start_ + curve_length, num_steps);
|
||||
piecewise_function_impl::piecewise_function_impl(double start, const std::vector<piecewise_function::ptr>& pwfs) : start_(start) {
|
||||
for (auto& pwf : pwfs) {
|
||||
spans_.insert(spans_.end(), pwf->spans().begin(), pwf->spans().end());
|
||||
}
|
||||
return *eval_points_;
|
||||
}
|
||||
|
||||
std::vector<double> ifcopenshell::geometry::taxonomy::piecewise_function_impl::evaluation_points(double ustart, double uend, unsigned nsteps) const {
|
||||
double curve_length = length();
|
||||
ustart = std::max(start_, ustart);
|
||||
uend = std::min(uend, start_ + curve_length);
|
||||
const piecewise_function_impl::spans_t& piecewise_function_impl::spans() const { return spans_; }
|
||||
|
||||
nsteps = std::max(1u, nsteps); // never have fewer than 1 step
|
||||
bool piecewise_function_impl::is_empty() const { return spans_.empty(); }
|
||||
|
||||
auto resolution = (uend - ustart) / nsteps;
|
||||
|
||||
std::vector<double> u_values;
|
||||
u_values.reserve(nsteps);
|
||||
|
||||
for (unsigned i = 0; i <= nsteps; ++i) {
|
||||
auto u = resolution * i + ustart;
|
||||
u_values.push_back(u);
|
||||
}
|
||||
|
||||
return u_values;
|
||||
double piecewise_function_impl::start() const {
|
||||
return start_;
|
||||
}
|
||||
|
||||
ifcopenshell::geometry::taxonomy::item::ptr ifcopenshell::geometry::taxonomy::piecewise_function_impl::evaluate() const {
|
||||
return evaluate(evaluation_points());
|
||||
double piecewise_function_impl::end() const {
|
||||
return start_ + length();
|
||||
}
|
||||
|
||||
item::ptr ifcopenshell::geometry::taxonomy::piecewise_function_impl::evaluate(double ustart, double uend, unsigned nsteps) const {
|
||||
return evaluate(evaluation_points(ustart, uend, nsteps));
|
||||
double piecewise_function_impl::length() const {
|
||||
return std::accumulate(spans_.begin(), spans_.end(), 0.0, [](const auto& v, const auto& s) { return v + s.first; });
|
||||
|
||||
// this is a secondary option where we only compute length once and cache it.
|
||||
// mutex is needed to prevent interruption of the accumulation if there is multi-threading
|
||||
// skipping this detail for now and just adding up the span lengths every time
|
||||
//if (!length_.has_value()) {
|
||||
// length_ = std::accumulate(spans_.begin(), spans_.end(), 0.0, [](const auto& v, const auto& s) { return v + s.first; });
|
||||
//}
|
||||
//return *length_;
|
||||
}
|
||||
|
||||
item::ptr ifcopenshell::geometry::taxonomy::piecewise_function_impl::evaluate(const std::vector<double>& dist) const {
|
||||
std::vector<taxonomy::point3::ptr> polygon;
|
||||
polygon.reserve(dist.size());
|
||||
for (auto& u : dist) {
|
||||
Eigen::Matrix4d m = evaluate(u);
|
||||
polygon.push_back(taxonomy::make<taxonomy::point3>(m.col(3)(0), m.col(3)(1), m.col(3)(2)));
|
||||
}
|
||||
|
||||
return polygon_from_points(polygon);
|
||||
}
|
||||
|
||||
Eigen::Matrix4d ifcopenshell::geometry::taxonomy::piecewise_function_impl::evaluate(double u) const {
|
||||
// assume monotonic evaluation and store last evaluated segment
|
||||
if (current_span_fn_ == nullptr || (u < current_span_start_ || current_span_end_ < u)) {
|
||||
// there isn't a current span or u is outside the range of the current span
|
||||
// get a new "current span"
|
||||
std::tie(current_span_start_, current_span_end_, current_span_fn_) = get_span(u);
|
||||
}
|
||||
|
||||
u -= current_span_start_; // make u relative to start of span
|
||||
return (*current_span_fn_)(u);
|
||||
}
|
||||
|
||||
std::tuple<double, double, const std::function<Eigen::Matrix4d(double u)>*> ifcopenshell::geometry::taxonomy::piecewise_function_impl::get_span(double u) const {
|
||||
// force u to be within bounds of the curve
|
||||
double s = start();
|
||||
double e = end();
|
||||
u = std::max(s, u);
|
||||
u = std::min(u, e);
|
||||
|
||||
double span_start = s;
|
||||
for (auto& [length, fn] : spans_) {
|
||||
double span_end = span_start + length;
|
||||
auto tolerance = settings_ ? settings_->get<ifcopenshell::geometry::settings::Precision>().get() : 0.001;
|
||||
if (span_start <= u && u < span_end + tolerance) {
|
||||
return {span_start, span_end, &fn};
|
||||
}
|
||||
span_start += length;
|
||||
}
|
||||
|
||||
Logger::Error("piecewise_function_impl::get_span span not found.");
|
||||
return {0, 0, nullptr};
|
||||
}
|
||||
piecewise_function_impl* piecewise_function_impl::clone_() const { return new piecewise_function_impl(*this); }
|
||||
|
||||
} // namespace taxonomy
|
||||
|
||||
|
||||
@@ -12,79 +12,23 @@ namespace taxonomy {
|
||||
struct piecewise_function_impl {
|
||||
using spans_t = std::vector<std::pair<double, std::function<Eigen::Matrix4d(double u)>>>;
|
||||
|
||||
piecewise_function_impl(double start, const spans_t& s, ifcopenshell::geometry::Settings* settings = nullptr) : start_(start),
|
||||
settings_(settings),
|
||||
spans_(s){};
|
||||
piecewise_function_impl(double start, const std::vector<piecewise_function::ptr>& pwfs, ifcopenshell::geometry::Settings* settings = nullptr) : start_(start),
|
||||
settings_(settings) {
|
||||
for (auto& pwf : pwfs) {
|
||||
spans_.insert(spans_.end(), pwf->spans().begin(), pwf->spans().end());
|
||||
}
|
||||
};
|
||||
piecewise_function_impl(double start, const spans_t& s);
|
||||
piecewise_function_impl(double start, const std::vector<piecewise_function::ptr>& pwfs);
|
||||
piecewise_function_impl(piecewise_function_impl&&) = default;
|
||||
piecewise_function_impl(const piecewise_function_impl&) = default;
|
||||
|
||||
const ifcopenshell::geometry::Settings* settings_ = nullptr;
|
||||
|
||||
const spans_t& spans() const { return spans_; }
|
||||
|
||||
bool is_empty() const { return spans_.empty(); }
|
||||
|
||||
double start() const {
|
||||
return start_;
|
||||
}
|
||||
|
||||
double end() const {
|
||||
return start_ + length();
|
||||
}
|
||||
|
||||
double length() const {
|
||||
if (!length_.has_value()) {
|
||||
length_ = std::accumulate(spans_.begin(), spans_.end(), 0.0, [](const auto& v, const auto& s) { return v + s.first; });
|
||||
}
|
||||
return *length_;
|
||||
}
|
||||
|
||||
piecewise_function_impl* clone_() const { return new piecewise_function_impl(*this); }
|
||||
|
||||
/// @brief returns a vector of "distance along" points where the evaluate function computes loop points
|
||||
std::vector<double> evaluation_points() const;
|
||||
|
||||
/// @brief returns a vector of "distance along" points between ustart and uend
|
||||
/// @param ustart starting location
|
||||
/// @param uend ending location
|
||||
/// @param nsteps number of steps to evaluate
|
||||
std::vector<double> evaluation_points(double ustart, double uend, unsigned nsteps) const;
|
||||
|
||||
/// @brief evaluates the piecewise function between start and end
|
||||
/// evaluation point step size is taken from the settings object
|
||||
item::ptr evaluate() const;
|
||||
|
||||
/// @brief evaluates the piecewise function between ustart and uend
|
||||
/// if ustart and uend are out of range, the range of values evaluated
|
||||
/// are constrained to start_ and start_+length_
|
||||
/// @param ustart starting location
|
||||
/// @param uend ending location
|
||||
/// @param nsteps number of steps to evaluate
|
||||
/// @return taxonomy::loop::ptr
|
||||
item::ptr evaluate(double ustart, double uend, unsigned nsteps) const;
|
||||
|
||||
/// @brief evaluates the piecewise function at u
|
||||
/// @param u u is constrained to be between start_ and start_+length
|
||||
/// @return 4x4 placement matrix
|
||||
Eigen::Matrix4d evaluate(double u) const;
|
||||
const spans_t& spans() const;
|
||||
bool is_empty() const;
|
||||
double start() const;
|
||||
double end() const;
|
||||
double length() const;
|
||||
piecewise_function_impl* clone_() const;
|
||||
|
||||
private:
|
||||
item::ptr evaluate(const std::vector<double>& dist) const;
|
||||
std::tuple<double, double, const std::function<Eigen::Matrix4d(double u)>*> get_span(double u) const;
|
||||
double start_ = 0.0; // starting value of the pwf
|
||||
spans_t spans_;
|
||||
|
||||
mutable double current_span_start_ = 0;
|
||||
mutable double current_span_end_ = 0;
|
||||
mutable const std::function<Eigen::Matrix4d(double u)>* current_span_fn_ = nullptr;
|
||||
mutable boost::optional<double> length_;
|
||||
mutable boost::optional<std::vector<double>> eval_points_;
|
||||
//mutable boost::optional<double> length_; // used for length() method
|
||||
};
|
||||
|
||||
} // namespace taxonomy
|
||||
|
||||
+162
-10
@@ -313,7 +313,7 @@ namespace {
|
||||
}
|
||||
|
||||
bool compare(const loft& a, const loft& b) {
|
||||
return compare_collection<face>(a, b);
|
||||
return compare_collection<geom_item>(a, b);
|
||||
}
|
||||
|
||||
bool compare(const collection& a, const collection& b) {
|
||||
@@ -464,12 +464,12 @@ ifcopenshell::geometry::taxonomy::solid::ptr ifcopenshell::geometry::create_box(
|
||||
}
|
||||
|
||||
///////////////////
|
||||
piecewise_function::piecewise_function(double start, const spans_t& s, ifcopenshell::geometry::Settings* settings, const IfcUtil::IfcBaseInterface* instance) : implicit_item(instance) {
|
||||
impl_ = new piecewise_function_impl(start, s, settings);
|
||||
piecewise_function::piecewise_function(double start, const spans_t& s, const IfcUtil::IfcBaseInterface* instance) : implicit_item(instance) {
|
||||
impl_ = new piecewise_function_impl(start, s);
|
||||
}
|
||||
|
||||
piecewise_function::piecewise_function(double start, const std::vector<piecewise_function::ptr>& pwfs, ifcopenshell::geometry::Settings* settings, const IfcUtil::IfcBaseInterface* instance) : implicit_item(instance) {
|
||||
impl_ = new piecewise_function_impl(start, pwfs, settings);
|
||||
piecewise_function::piecewise_function(double start, const std::vector<piecewise_function::ptr>& pwfs, const IfcUtil::IfcBaseInterface* instance) : implicit_item(instance) {
|
||||
impl_ = new piecewise_function_impl(start, pwfs);
|
||||
};
|
||||
|
||||
piecewise_function::piecewise_function(const piecewise_function& other) : implicit_item(other) {
|
||||
@@ -486,11 +486,6 @@ double piecewise_function::start() const { return impl_->start(); }
|
||||
double piecewise_function::end() const { return impl_->end(); }
|
||||
double piecewise_function::length() const { return impl_->length(); }
|
||||
|
||||
std::vector<double> piecewise_function::evaluation_points() const { return impl_->evaluation_points(); }
|
||||
std::vector<double> piecewise_function::evaluation_points(double ustart, double uend, unsigned nsteps) const { return impl_->evaluation_points(ustart, uend, nsteps); }
|
||||
item::ptr piecewise_function::evaluate() const { return impl_->evaluate(); }
|
||||
item::ptr piecewise_function::evaluate(double ustart, double uend, unsigned nsteps) const { return impl_->evaluate(ustart, uend, nsteps); }
|
||||
Eigen::Matrix4d piecewise_function::evaluate(double u) const { return impl_->evaluate(u); }
|
||||
|
||||
ifcopenshell::geometry::taxonomy::collection::ptr ifcopenshell::geometry::flatten(const taxonomy::collection::ptr& deep) {
|
||||
auto flat = make<taxonomy::collection>();
|
||||
@@ -599,3 +594,160 @@ void ifcopenshell::geometry::taxonomy::extrusion::print(std::ostream& o, int ind
|
||||
direction->print(o, indent + 4);
|
||||
basis->print(o, indent + 4);
|
||||
}
|
||||
|
||||
boost::optional<face::ptr> ifcopenshell::geometry::taxonomy::loop_to_face_upgrade_impl(ptr item) {
|
||||
boost::optional<face::ptr> face_;
|
||||
auto loop_ = dcast<loop>(item);
|
||||
if (loop_) {
|
||||
loop_->external = true;
|
||||
|
||||
face_ = make<face>();
|
||||
(*face_)->instance = loop_->instance;
|
||||
(*face_)->matrix = loop_->matrix;
|
||||
(*face_)->children = { clone(loop_) };
|
||||
}
|
||||
return face_;
|
||||
}
|
||||
|
||||
boost::optional<edge::ptr> ifcopenshell::geometry::taxonomy::curve_to_edge_upgrade_impl(ptr item) {
|
||||
boost::optional<edge::ptr> edge_;
|
||||
auto circle_ = dcast<circle>(item);
|
||||
auto ellipse_ = dcast<ellipse>(item);
|
||||
auto line_ = dcast<line>(item);
|
||||
auto bspline_curve_ = dcast<bspline_curve>(item);
|
||||
if (circle_ || ellipse_ || line_ || bspline_curve_) {
|
||||
edge_ = make<edge>();
|
||||
if (circle_) {
|
||||
(*edge_)->basis = circle_;
|
||||
(*edge_)->instance = circle_->instance;
|
||||
} else if (ellipse_) {
|
||||
(*edge_)->basis = ellipse_;
|
||||
(*edge_)->instance = ellipse_->instance;
|
||||
} else if (line_) {
|
||||
(*edge_)->basis = line_;
|
||||
(*edge_)->instance = line_->instance;
|
||||
} else if (bspline_curve_) {
|
||||
(*edge_)->basis = bspline_curve_;
|
||||
(*edge_)->instance = bspline_curve_->instance;
|
||||
}
|
||||
|
||||
if (circle_ || ellipse_) {
|
||||
// @todo
|
||||
(*edge_)->start = 0.;
|
||||
(*edge_)->end = 2 * boost::math::constants::pi<double>();
|
||||
}
|
||||
}
|
||||
return edge_;
|
||||
}
|
||||
|
||||
boost::optional<loop::ptr> ifcopenshell::geometry::taxonomy::curve_to_loop_upgrade_impl(ptr item) {
|
||||
boost::optional<loop::ptr> loop_;
|
||||
auto circle_ = dcast<circle>(item);
|
||||
auto ellipse_ = dcast<ellipse>(item);
|
||||
auto line_ = dcast<line>(item);
|
||||
auto bspline_curve_ = dcast<bspline_curve>(item);
|
||||
if (circle_ || ellipse_ || line_ || bspline_curve_) {
|
||||
auto edge_ = make<edge>();
|
||||
if (circle_) {
|
||||
edge_->basis = circle_;
|
||||
} else if (ellipse_) {
|
||||
edge_->basis = ellipse_;
|
||||
} else if (line_) {
|
||||
edge_->basis = line_;
|
||||
} else if (bspline_curve_) {
|
||||
edge_->basis = bspline_curve_;
|
||||
}
|
||||
|
||||
if (circle_ || ellipse_) {
|
||||
// @todo
|
||||
edge_->start = 0.;
|
||||
edge_->end = 2 * boost::math::constants::pi<double>();
|
||||
}
|
||||
|
||||
loop_ = make<loop>();
|
||||
(*loop_)->children.push_back(edge_);
|
||||
}
|
||||
return loop_;
|
||||
}
|
||||
|
||||
boost::optional<loop::ptr> ifcopenshell::geometry::taxonomy::edge_to_loop_upgrade_impl(ptr item) {
|
||||
boost::optional<loop::ptr> loop_;
|
||||
auto edge_ = dcast<edge>(item);
|
||||
if (edge_) {
|
||||
loop_ = make<loop>();
|
||||
(*loop_)->children.push_back(edge_);
|
||||
}
|
||||
return loop_;
|
||||
}
|
||||
|
||||
boost::optional<face::ptr> ifcopenshell::geometry::taxonomy::curve_to_face_upgrade_impl(ptr item) {
|
||||
boost::optional<face::ptr> face_;
|
||||
auto circle_ = dcast<circle>(item);
|
||||
auto ellipse_ = dcast<ellipse>(item);
|
||||
auto line_ = dcast<line>(item);
|
||||
auto bspline_curve_ = dcast<bspline_curve>(item);
|
||||
|
||||
if (circle_ || ellipse_ || line_ || bspline_curve_) {
|
||||
auto edge_ = make<edge>();
|
||||
if (circle_) {
|
||||
edge_->basis = circle_;
|
||||
} else if (ellipse_) {
|
||||
edge_->basis = ellipse_;
|
||||
} else if (line_) {
|
||||
edge_->basis = line_;
|
||||
} else if (bspline_curve_) {
|
||||
edge_->basis = bspline_curve_;
|
||||
}
|
||||
|
||||
if (circle_ || ellipse_) {
|
||||
// @todo
|
||||
edge_->start = 0.;
|
||||
edge_->end = 2 * boost::math::constants::pi<double>();
|
||||
}
|
||||
|
||||
auto loop_ = make<loop>();
|
||||
loop_->children.push_back(edge_);
|
||||
|
||||
face_ = make<face>();
|
||||
(*face_)->instance = loop_->instance;
|
||||
(*face_)->matrix = loop_->matrix;
|
||||
(*face_)->children = { clone(loop_) };
|
||||
}
|
||||
return face_;
|
||||
}
|
||||
|
||||
|
||||
boost::optional<piecewise_function::ptr> ifcopenshell::geometry::taxonomy::loop_to_piecewise_function_upgrade_impl(ptr item) {
|
||||
boost::optional<piecewise_function::ptr> pwf_;
|
||||
auto loop_ = dcast<loop>(item);
|
||||
if (loop_) {
|
||||
if (loop_->pwf.is_initialized()) {
|
||||
pwf_ = loop_->pwf;
|
||||
} else {
|
||||
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");
|
||||
}
|
||||
|
||||
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(l, fn);
|
||||
}
|
||||
pwf_ = make<piecewise_function>(0.0,spans);
|
||||
loop_->pwf = pwf_;
|
||||
}
|
||||
}
|
||||
return pwf_;
|
||||
}
|
||||
|
||||
+31
-159
@@ -356,8 +356,6 @@ typedef item const* ptr;
|
||||
struct implicit_item : public geom_item {
|
||||
DECLARE_PTR(implicit_item)
|
||||
using geom_item::geom_item;
|
||||
|
||||
virtual item::ptr evaluate() const = 0;
|
||||
};
|
||||
|
||||
struct piecewise_function_impl; // forward declaration
|
||||
@@ -366,14 +364,12 @@ typedef item const* ptr;
|
||||
|
||||
using spans_t = std::vector<std::pair<double, std::function<Eigen::Matrix4d(double u)>>>;
|
||||
|
||||
piecewise_function(double start, const spans_t& s, ifcopenshell::geometry::Settings* settings = nullptr, const IfcUtil::IfcBaseInterface* instance = nullptr);
|
||||
piecewise_function(double start, const std::vector<piecewise_function::ptr>& pwfs, ifcopenshell::geometry::Settings* settings = nullptr, const IfcUtil::IfcBaseInterface* instance = nullptr);
|
||||
piecewise_function(double start, const spans_t& s, const IfcUtil::IfcBaseInterface* instance = nullptr);
|
||||
piecewise_function(double start, const std::vector<piecewise_function::ptr>& pwfs, const IfcUtil::IfcBaseInterface* instance = nullptr);
|
||||
piecewise_function(piecewise_function&&) = default;
|
||||
piecewise_function(const piecewise_function&);
|
||||
virtual ~piecewise_function();
|
||||
|
||||
const ifcopenshell::geometry::Settings* settings_ = nullptr;
|
||||
|
||||
const spans_t& spans() const;
|
||||
bool is_empty() const;
|
||||
double start() const;
|
||||
@@ -388,33 +384,6 @@ typedef item const* ptr;
|
||||
return boost::hash<decltype(v)>{}(v);
|
||||
}
|
||||
|
||||
/// @brief returns a vector of "distance along" points where the evaluate function computes loop points
|
||||
std::vector<double> evaluation_points() const;
|
||||
|
||||
/// @brief returns a vector of "distance along" points between ustart and uend
|
||||
/// @param ustart starting location
|
||||
/// @param uend ending location
|
||||
/// @param nsteps number of steps to evaluate
|
||||
std::vector<double> evaluation_points(double ustart, double uend, unsigned nsteps) const;
|
||||
|
||||
/// @brief evaluates the piecewise function between start and end
|
||||
/// evaluation point step size is taken from the settings object
|
||||
item::ptr evaluate() const override;
|
||||
|
||||
/// @brief evaluates the piecewise function between ustart and uend
|
||||
/// if ustart and uend are out of range, the range of values evaluated
|
||||
/// are constrained to start_ and start_+length_
|
||||
/// @param ustart starting location
|
||||
/// @param uend ending location
|
||||
/// @param nsteps number of steps to evaluate
|
||||
/// @return taxonomy::loop::ptr
|
||||
item::ptr evaluate(double ustart, double uend, unsigned nsteps) const;
|
||||
|
||||
/// @brief evaluates the piecewise function at u
|
||||
/// @param u u is constrained to be between start_ and start_+length
|
||||
/// @return 4x4 placement matrix
|
||||
Eigen::Matrix4d evaluate(double u) const;
|
||||
|
||||
private:
|
||||
// note: it would be better if this were a std::unique_ptr, but that requires having the full definition
|
||||
// of piecewise_function_impl in this header file, which defeats the purpose of the PIMPL idiom.
|
||||
@@ -850,7 +819,7 @@ typedef item const* ptr;
|
||||
}
|
||||
};
|
||||
|
||||
struct loft : public collection_base<face> {
|
||||
struct loft : public collection_base<geom_item> {
|
||||
DECLARE_PTR(loft)
|
||||
|
||||
item::ptr axis;
|
||||
@@ -1075,6 +1044,7 @@ typedef item const* ptr;
|
||||
typedef std::tuple<matrix4, point3, direction3, line, circle, ellipse, bspline_curve, offset_curve, plane, cylinder, sphere, torus, bspline_surface, edge, loop, face, shell, solid, loft, extrusion, revolve, sweep_along_curve, node, collection, boolean_result, piecewise_function> KindsTuple;
|
||||
typedef std::tuple<line, circle, ellipse, bspline_curve, offset_curve, loop, edge> CurvesTuple;
|
||||
typedef std::tuple<plane, cylinder, sphere, torus, bspline_surface, extrusion, revolve> SurfacesTuple;
|
||||
typedef std::tuple<edge, loop, face, piecewise_function> UpgradesTuple;
|
||||
}
|
||||
|
||||
struct type_by_kind {
|
||||
@@ -1098,6 +1068,14 @@ typedef item const* ptr;
|
||||
static const size_t max = std::tuple_size<impl::SurfacesTuple>::value;
|
||||
};
|
||||
|
||||
struct upgrades {
|
||||
template <std::size_t N>
|
||||
using type = typename std::tuple_element<N, impl::UpgradesTuple>::type;
|
||||
|
||||
static const size_t max = std::tuple_size<impl::UpgradesTuple>::value;
|
||||
};
|
||||
|
||||
boost::optional<face::ptr> loop_to_face_upgrade_impl(ptr item);
|
||||
template <typename T>
|
||||
class loop_to_face_upgrade {
|
||||
private:
|
||||
@@ -1105,15 +1083,7 @@ typedef item const* ptr;
|
||||
public:
|
||||
loop_to_face_upgrade(taxonomy::ptr item) {
|
||||
if constexpr (std::is_same_v<T, face>) {
|
||||
auto loop = taxonomy::dcast<taxonomy::loop>(item);
|
||||
if (loop) {
|
||||
loop->external = true;
|
||||
|
||||
face_ = taxonomy::make<taxonomy::face>();
|
||||
(*face_)->instance = loop->instance;
|
||||
(*face_)->matrix = loop->matrix;
|
||||
(*face_)->children = { taxonomy::clone(loop) };
|
||||
}
|
||||
face_ = loop_to_face_upgrade_impl(item);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1131,6 +1101,7 @@ typedef item const* ptr;
|
||||
}
|
||||
};
|
||||
|
||||
boost::optional<edge::ptr> curve_to_edge_upgrade_impl(ptr item);
|
||||
template <typename T>
|
||||
class curve_to_edge_upgrade {
|
||||
private:
|
||||
@@ -1138,28 +1109,7 @@ typedef item const* ptr;
|
||||
public:
|
||||
curve_to_edge_upgrade(taxonomy::ptr item) {
|
||||
if constexpr (std::is_same_v<T, edge>) {
|
||||
auto circle = taxonomy::dcast<taxonomy::circle>(item);
|
||||
auto ellipse = taxonomy::dcast<taxonomy::ellipse>(item);
|
||||
auto line = taxonomy::dcast<taxonomy::line>(item);
|
||||
auto bspline_curve = taxonomy::dcast<taxonomy::bspline_curve>(item);
|
||||
if (circle || ellipse || line || bspline_curve) {
|
||||
edge_ = taxonomy::make<taxonomy::edge>();
|
||||
if (circle) {
|
||||
(*edge_)->basis = circle;
|
||||
} else if (ellipse) {
|
||||
(*edge_)->basis = ellipse;
|
||||
} else if (line) {
|
||||
(*edge_)->basis = line;
|
||||
} else if (bspline_curve) {
|
||||
(*edge_)->basis = bspline_curve;
|
||||
}
|
||||
|
||||
if (circle || ellipse) {
|
||||
// @todo
|
||||
(*edge_)->start = 0.;
|
||||
(*edge_)->end = 2 * boost::math::constants::pi<double>();
|
||||
}
|
||||
}
|
||||
edge_ = taxonomy::curve_to_edge_upgrade_impl(item);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1177,7 +1127,7 @@ typedef item const* ptr;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
boost::optional<loop::ptr> curve_to_loop_upgrade_impl(ptr item);
|
||||
template <typename T>
|
||||
class curve_to_loop_upgrade {
|
||||
private:
|
||||
@@ -1185,31 +1135,7 @@ typedef item const* ptr;
|
||||
public:
|
||||
curve_to_loop_upgrade(taxonomy::ptr item) {
|
||||
if constexpr (std::is_same_v<T, loop>) {
|
||||
auto circle = taxonomy::dcast<taxonomy::circle>(item);
|
||||
auto ellipse = taxonomy::dcast<taxonomy::ellipse>(item);
|
||||
auto line = taxonomy::dcast<taxonomy::line>(item);
|
||||
auto bspline_curve = taxonomy::dcast<taxonomy::bspline_curve>(item);
|
||||
if (circle || ellipse || line || bspline_curve) {
|
||||
auto edge = taxonomy::make<taxonomy::edge>();
|
||||
if (circle) {
|
||||
edge->basis = circle;
|
||||
} else if (ellipse) {
|
||||
edge->basis = ellipse;
|
||||
} else if (line) {
|
||||
edge->basis = line;
|
||||
} else if (bspline_curve) {
|
||||
edge->basis = bspline_curve;
|
||||
}
|
||||
|
||||
if (circle || ellipse) {
|
||||
// @todo
|
||||
edge->start = 0.;
|
||||
edge->end = 2 * boost::math::constants::pi<double>();
|
||||
}
|
||||
|
||||
loop_ = taxonomy::make<taxonomy::loop>();
|
||||
(*loop_)->children.push_back(edge);
|
||||
}
|
||||
loop_ = curve_to_loop_upgrade_impl(item);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1227,6 +1153,7 @@ typedef item const* ptr;
|
||||
}
|
||||
};
|
||||
|
||||
boost::optional<loop::ptr> edge_to_loop_upgrade_impl(ptr item);
|
||||
template <typename T>
|
||||
class edge_to_loop_upgrade {
|
||||
private:
|
||||
@@ -1234,11 +1161,7 @@ typedef item const* ptr;
|
||||
public:
|
||||
edge_to_loop_upgrade(taxonomy::ptr item) {
|
||||
if constexpr (std::is_same_v<T, loop>) {
|
||||
auto edge = taxonomy::dcast<taxonomy::edge>(item);
|
||||
if (edge) {
|
||||
loop_ = taxonomy::make<taxonomy::loop>();
|
||||
(*loop_)->children.push_back(edge);
|
||||
}
|
||||
loop_ = edge_to_loop_upgrade_impl(item);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1256,7 +1179,7 @@ typedef item const* ptr;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
boost::optional<face::ptr> curve_to_face_upgrade_impl(ptr item);
|
||||
template <typename T>
|
||||
class curve_to_face_upgrade {
|
||||
private:
|
||||
@@ -1264,36 +1187,7 @@ typedef item const* ptr;
|
||||
public:
|
||||
curve_to_face_upgrade(taxonomy::ptr item) {
|
||||
if constexpr (std::is_same_v<T, edge>) {
|
||||
auto circle = taxonomy::dcast<taxonomy::circle>(item);
|
||||
auto ellipse = taxonomy::dcast<taxonomy::ellipse>(item);
|
||||
auto line = taxonomy::dcast<taxonomy::line>(item);
|
||||
auto bspline_curve = taxonomy::dcast<taxonomy::bspline_curve>(item);
|
||||
if (circle || ellipse || line || bspline_curve) {
|
||||
auto edge = taxonomy::make<taxonomy::edge>();
|
||||
if (circle) {
|
||||
edge->basis = circle;
|
||||
} else if (ellipse) {
|
||||
edge->basis = ellipse;
|
||||
} else if (line) {
|
||||
edge->basis = line;
|
||||
} else if (bspline_curve) {
|
||||
edge->basis = bspline_curve;
|
||||
}
|
||||
|
||||
if (circle || ellipse) {
|
||||
// @todo
|
||||
edge->start = 0.;
|
||||
edge->end = 2 * boost::math::constants::pi<double>();
|
||||
}
|
||||
|
||||
auto loop = taxonomy::make<taxonomy::loop>();
|
||||
loop->children.push_back(edge);
|
||||
|
||||
face_ = taxonomy::make<taxonomy::face>();
|
||||
(*face_)->instance = loop->instance;
|
||||
(*face_)->matrix = loop->matrix;
|
||||
(*face_)->children = { taxonomy::clone(loop) };
|
||||
}
|
||||
face_ = curve_to_face_upgrade_impl(item);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1311,6 +1205,7 @@ typedef item const* ptr;
|
||||
}
|
||||
};
|
||||
|
||||
boost::optional<piecewise_function::ptr> loop_to_piecewise_function_upgrade_impl(ptr item);
|
||||
template <typename T>
|
||||
class loop_to_piecewise_function_upgrade {
|
||||
private:
|
||||
@@ -1319,36 +1214,7 @@ typedef item const* ptr;
|
||||
public:
|
||||
loop_to_piecewise_function_upgrade(taxonomy::ptr item) {
|
||||
if constexpr (std::is_same_v<T, piecewise_function>) {
|
||||
auto loop = taxonomy::dcast<taxonomy::loop>(item);
|
||||
if (loop) {
|
||||
if (loop->pwf.is_initialized()) {
|
||||
pwf_ = loop->pwf;
|
||||
} else {
|
||||
taxonomy::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");
|
||||
}
|
||||
|
||||
const auto& s = boost::get<taxonomy::point3::ptr>(edge->start)->ccomponents();
|
||||
const auto& e = boost::get<taxonomy::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 taxonomy::make<taxonomy::matrix4>(o, axis, refDirection)->components();
|
||||
};
|
||||
spans.emplace_back(l, fn);
|
||||
}
|
||||
pwf_ = taxonomy::make<taxonomy::piecewise_function>(0.0,spans);
|
||||
loop->pwf = pwf_;
|
||||
}
|
||||
}
|
||||
pwf_ = loop_to_piecewise_function_upgrade_impl(item);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1382,13 +1248,13 @@ typedef item const* ptr;
|
||||
}
|
||||
}
|
||||
{
|
||||
edge_to_loop_upgrade<T> upg(u);
|
||||
curve_to_face_upgrade<T> upg(u);
|
||||
if (upg) {
|
||||
return upg;
|
||||
}
|
||||
}
|
||||
{
|
||||
curve_to_face_upgrade<T> upg(u);
|
||||
edge_to_loop_upgrade<T> upg(u);
|
||||
if (upg) {
|
||||
return upg;
|
||||
}
|
||||
@@ -1419,6 +1285,12 @@ typedef item const* ptr;
|
||||
return upg;
|
||||
}
|
||||
}
|
||||
{
|
||||
curve_to_loop_upgrade<T> upg(u);
|
||||
if (upg) {
|
||||
return upg;
|
||||
}
|
||||
}
|
||||
{
|
||||
curve_to_face_upgrade<T> upg(u);
|
||||
if (upg) {
|
||||
|
||||
Reference in New Issue
Block a user