Refactors alignment geometry

This commit is contained in:
Richard Brice
2025-01-02 11:10:56 -08:00
committed by GitHub
parent bb9be861c8
commit 26ba761c68
23 changed files with 948 additions and 555 deletions
+8 -7
View File
@@ -23,7 +23,7 @@ using namespace ifcopenshell::geometry;
taxonomy::ptr mapping::map_impl(const IfcSchema::IfcCompositeCurve* inst) {
auto loop = taxonomy::make<taxonomy::loop>();
std::vector<taxonomy::piecewise_function::ptr> pwfs;
taxonomy::piecewise_function::spans_t spans;
#ifdef SCHEMA_HAS_IfcSegment
// 4x3
@@ -74,17 +74,18 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcCompositeCurve* inst) {
for (auto& s : taxonomy::cast<taxonomy::loop>(crv)->children) {
loop->children.push_back(s);
}
}
else if (crv && crv->kind() == taxonomy::PIECEWISE_FUNCTION) {
pwfs.push_back(taxonomy::cast<taxonomy::piecewise_function>(crv));
} else if (!crv) {
} else if (auto fi = taxonomy::dcast<taxonomy::function_item>(crv); crv && fi /*crv->kind() == taxonomy::FUNCTION_ITEM*/) {
// crv->kind() is polymorphic and the kind of the actual function_item is returned. PWF can have spans of any FUNCTION_ITEM
// for this reason, a dynamic cast is used and if crv is a function_item it is added to the span
spans.push_back(fi);
} else if (!crv) {
return nullptr;
}
}
#endif
}
if (pwfs.empty()) {
if (spans.empty()) {
aggregate_of_instance::ptr profile = inst->file_->getInverse(inst->id(), &IfcSchema::IfcProfileDef::Class(), -1);
const bool force_close = profile && profile->size() > 0;
loop->closed = force_close;
@@ -92,7 +93,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcCompositeCurve* inst) {
return loop;
}
else {
auto pwf = taxonomy::make<taxonomy::piecewise_function>(0.0,pwfs,inst);
auto pwf = taxonomy::make<taxonomy::piecewise_function>(0.0,spans,inst);
return pwf;
}
}
+210 -155
View File
@@ -96,6 +96,87 @@ typedef boost::mpl::vector<
#endif
> curve_seg_types;
struct parent_curve_function {
parent_curve_function() = default;
parent_curve_function(const parent_curve_function&) = default;
parent_curve_function(std::function<Eigen::Matrix4d(double)> fn) : fn_(fn) {
}
parent_curve_function& operator=(std::function<Eigen::Matrix4d(double)> fn) {
fn_ = fn;
return *this;
}
virtual Eigen::Matrix4d operator()(double u) const { return fn_(u); }
private:
std::function<Eigen::Matrix4d(double)> fn_;
};
struct polynomial_parent_curve : public parent_curve_function {
using parent_curve_function::parent_curve_function;
};
struct line_parent_curve : public parent_curve_function {
using parent_curve_function::parent_curve_function;
};
struct circle_parent_curve : public parent_curve_function {
using parent_curve_function::parent_curve_function;
};
struct spiral_parent_curve : public parent_curve_function {
using parent_curve_function::parent_curve_function;
};
// this is the piecewise curve segment function for horizontal and vertical
struct curve_segment_function {
curve_segment_function(const Eigen::Matrix4d& curve_segment_placement, const Eigen::Matrix4d& remove_parent_curve_rotation, const Eigen::Matrix4d& remove_parent_curve_translation, std::shared_ptr<parent_curve_function> parent_curve_fn) :
curve_segment_placement_(curve_segment_placement),
remove_parent_curve_rotation_(remove_parent_curve_rotation),
remove_parent_curve_translation_(remove_parent_curve_translation),
parent_curve_fn_(parent_curve_fn) {
}
Eigen::Matrix4d operator()(double u) const {
Eigen::Matrix4d parent_curve_point = (*parent_curve_fn_)(u);
Eigen::Matrix4d curve_segment_point = curve_segment_placement_ * remove_parent_curve_rotation_ * remove_parent_curve_translation_ * parent_curve_point;
return curve_segment_point;
}
private:
Eigen::Matrix4d curve_segment_placement_;
Eigen::Matrix4d remove_parent_curve_rotation_;
Eigen::Matrix4d remove_parent_curve_translation_;
std::shared_ptr<parent_curve_function> parent_curve_fn_;
};
// this is the piecewise curve segment function for cant
struct cant_curve_segment_function {
cant_curve_segment_function(const Eigen::Matrix4d& curve_segment_placement, const Eigen::Matrix4d& parent_curve_start_point, std::shared_ptr<parent_curve_function> parent_curve_fn) :
curve_segment_placement_(curve_segment_placement),
parent_curve_start_point_(parent_curve_start_point),
parent_curve_fn_(parent_curve_fn) {
}
Eigen::Matrix4d operator()(double u) const {
// 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 cant_increment = parent_curve_point - parent_curve_start_point_;
Eigen::Matrix4d curve_segment_point = curve_segment_placement_ + cant_increment;
return curve_segment_point;
}
private:
Eigen::Matrix4d curve_segment_placement_;
Eigen::Matrix4d parent_curve_start_point_;
std::shared_ptr<parent_curve_function> parent_curve_fn_;
};
// evaluates a IfcCurveSegment to set up the placement and parent curve function
class curve_segment_evaluator {
private:
mapping* mapping_ = nullptr;
@@ -108,24 +189,71 @@ class curve_segment_evaluator {
double projected_length_; // for vertical segments, this is the length of curve projected onto the "Distance Along" axis
std::optional<std::function<Eigen::Matrix4d(double)>> parent_curve_fn_; // function for the parent curve. Function takes distances along, u, and returns the 4x4 position matrix
std::optional<Eigen::Matrix4d> parent_curve_start_point_; // placement matrix for the parent curve
std::shared_ptr<parent_curve_function> parent_curve_fn_; // function for the parent curve. Function takes distances along, u, and returns the 4x4 position matrix
std::optional<Eigen::Matrix4d> parent_curve_start_point_; // placement matrix for the parent curve
std::optional<Eigen::Matrix4d> placement_; // placement of this segment
std::optional<Eigen::Matrix4d> curve_segment_placement_; // placement of this segment
std::optional<Eigen::Matrix4d> next_segment_placement_; // placement of the next segment
public:
curve_segment_evaluator(mapping* mapping, const IfcSchema::IfcCurveSegment* inst, const IfcSchema::IfcCurveSegment* next_inst, double length_unit, segment_type_t segment_type)
curve_segment_evaluator(mapping* mapping, const IfcSchema::IfcCurveSegment* inst, double length_unit)
: mapping_(mapping),
inst_(inst),
length_unit_(length_unit),
segment_type_(segment_type),
parent_curve_(inst->ParentCurve()) {
auto composite_curves = inst->UsingCurves();
// Find the next segment after inst
const IfcSchema::IfcCurveSegment* next_inst = nullptr;
if (composite_curves) {
if (composite_curves->size() == 1) {
auto segments = (*composite_curves->begin())->as<IfcSchema::IfcCompositeCurve>()->Segments();
bool emit_next = false;
for (auto& s : *segments) {
if (emit_next) {
next_inst = s->as<IfcSchema::IfcCurveSegment>();
break;
}
if (s == inst) {
emit_next = true;
}
}
} else {
Logger::Warning("IfcCurveSegment belongs to multiple IfcCompositeCurve instances. Cannot determine the next segment.");
}
}
bool is_horizontal = false;
bool is_vertical = false;
bool is_cant = false;
if (composite_curves) {
for (auto& cc : *composite_curves) {
if (cc->as<IfcSchema::IfcSegmentedReferenceCurve>()) {
is_cant = true;
} else if (cc->as<IfcSchema::IfcGradientCurve>()) {
is_vertical = true;
} else {
is_horizontal = true;
}
}
}
if ((is_horizontal + is_vertical + is_cant) != 1) {
// We have to choose the correct functor based on usage. We can't
// support multiple, because we don't know the caller at this point.
Logger::Error(std::runtime_error("multiple uses of IfcSegmentCurve not supported"), inst_);
}
segment_type_ = is_horizontal ? ST_HORIZONTAL : is_vertical ? ST_VERTICAL : ST_CANT;
start_ = translate_if_param_value(inst->ParentCurve(), inst->SegmentStart()) * length_unit;
length_ = translate_if_param_value(inst->ParentCurve(), inst->SegmentLength()) * length_unit;
if (inst) {
placement_ = taxonomy::cast<taxonomy::matrix4>(mapping_->map(inst->Placement()))->ccomponents();
curve_segment_placement_ = taxonomy::cast<taxonomy::matrix4>(mapping_->map(inst->Placement()))->ccomponents();
}
if (next_inst) {
@@ -133,7 +261,6 @@ class curve_segment_evaluator {
} 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();
@@ -165,16 +292,47 @@ class curve_segment_evaluator {
return (segment_type_ == ST_HORIZONTAL || segment_type_ == ST_CANT) ? length_ : projected_length_;
}
const std::optional<std::function<Eigen::Matrix4d(double)>>& parent_curve_function() const {
return parent_curve_fn_;
}
taxonomy::ptr get_segment_curve_function() {
if (!parent_curve_fn_ || !parent_curve_start_point_) {
Logger::Error(std::runtime_error(inst_->ParentCurve()->declaration().name() + " not implemented"), inst_);
}
const std::optional<Eigen::Matrix4d>& parent_curve_start_point() const {
return parent_curve_start_point_;
}
auto length = fabs(this->length());
const std::optional<Eigen::Matrix4d>& segment_placement() const {
return placement_;
if (segment_type_ == ST_CANT) {
auto fn = cant_curve_segment_function(*curve_segment_placement_, *parent_curve_start_point_, parent_curve_fn_);
return taxonomy::make<taxonomy::functor_item>(length, fn);
} 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
auto fn = curve_segment_function(*curve_segment_placement_, remove_parent_curve_rotation, remove_parent_curve_translation, parent_curve_fn_);
return taxonomy::make<taxonomy::functor_item>(length, fn);
}
}
void set_spiral_function(double s, std::function<double(double)> fnX, std::function<double(double)> fnY) {
@@ -224,7 +382,7 @@ class curve_segment_evaluator {
};
}
parent_curve_fn_ = [start=start_, s, convert_u, fnX, fnY](double u) {
parent_curve_fn_ = std::make_shared<spiral_parent_curve>([start=start_, s, convert_u, fnX, fnY](double u) {
u = convert_u(u+start);
// integration limits, integrate from a to b
@@ -241,20 +399,20 @@ class curve_segment_evaluator {
m.col(1) = Eigen::Vector4d(-dy, dx, 0, 0);
m.col(3) = Eigen::Vector4d(x, y, 0, 1);
return m;
};
});
} else if (segment_type_ == ST_CANT) {
Logger::Error(std::runtime_error("Unexpected segment type encountered - cant is handled in set_cant_spiral_function - should never get here"));
parent_curve_fn_ = [](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); };
parent_curve_fn_ = std::make_shared<parent_curve_function>([](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); });
} else {
Logger::Error(std::runtime_error("Unexpected segment type encountered"));
parent_curve_fn_ = [](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); };
parent_curve_fn_ = std::make_shared<parent_curve_function>([](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); });
}
}
// defines the parent_curve_fn_ functor for cant segments.
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 dy = (*curve_segment_placement_)(1, 2); // placement dy
auto dz = (*curve_segment_placement_)(2, 2); // placement dz
auto start_angle = atan2(dz, dy);
dy = (next_segment_placement_.has_value() ? (*next_segment_placement_)(1, 2) : 0.0);
@@ -266,7 +424,7 @@ class curve_segment_evaluator {
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 {
parent_curve_fn_ = std::make_shared<spiral_parent_curve>([start_angle,delta_angle,start_cant,delta_cant,Superelevation, SuperelevationSlope, Cant](double u) -> Eigen::Matrix4d {
// departure of the curve segment from the base curve (superelevation)
auto super_elevation = Superelevation(u);
auto slope = SuperelevationSlope(u);
@@ -292,7 +450,7 @@ class curve_segment_evaluator {
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);
}
@@ -304,8 +462,8 @@ class curve_segment_evaluator {
boost::optional<std::function<double(double)>> superelevation_fn;
boost::optional<std::function<double(double)>> superelevation_slope_fn;
if (placement_.has_value() && next_segment_placement_.has_value()) {
double y1 = (*placement_)(1, 3);
if (curve_segment_placement_.has_value() && next_segment_placement_.has_value()) {
double y1 = (*curve_segment_placement_)(1, 3);
double y2 = (*next_segment_placement_)(1, 3);
// if y2-y1 = 0, the super elevation is constant
@@ -390,10 +548,10 @@ class curve_segment_evaluator {
set_cant_spiral_function(*super, *slope, cant);
} else if (segment_type_ == ST_VERTICAL) {
Logger::Error(std::runtime_error("IfcCosineSpiral cannot be used for vertical alignment"));
parent_curve_fn_ = [](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); };
parent_curve_fn_ = std::make_shared<parent_curve_function>([](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); });
} else {
Logger::Error(std::runtime_error("Unexpected segment type encountered"));
parent_curve_fn_ = [](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); };
parent_curve_fn_ = std::make_shared<parent_curve_function>([](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); });
}
}
#endif
@@ -447,10 +605,10 @@ class curve_segment_evaluator {
set_cant_spiral_function(*super, *slope, cant);
} else if (segment_type_ == ST_VERTICAL) {
Logger::Error(std::runtime_error("IfcSineSpiral cannot be used for vertical alignment"));
parent_curve_fn_ = [](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); };
parent_curve_fn_ = std::make_shared<parent_curve_function>([](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); });
} else {
Logger::Error(std::runtime_error("Unexpected segment type encountered"));
parent_curve_fn_ = [](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); };
parent_curve_fn_ = std::make_shared<parent_curve_function>([](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); });
}
}
#endif
@@ -623,7 +781,7 @@ class curve_segment_evaluator {
};
}
parent_curve_fn_ = [segment_type = segment_type_, R, pcCenterX, pcCenterY, start_angle, sign_l, convert_u](double u) {
parent_curve_fn_ = std::make_shared<circle_parent_curve>([segment_type = segment_type_, R, pcCenterX, pcCenterY, start_angle, sign_l, convert_u](double u) {
u = convert_u(u);
// u is measured along the circle
@@ -645,7 +803,7 @@ class curve_segment_evaluator {
m.col(1) = Eigen::Vector4d(-pcDy, pcDx, 0, 0);
m.col(3) = Eigen::Vector4d(pcX, pcY, 0.0, 1.0);
return m;
};
});
if (segment_type_ == ST_HORIZONTAL) {
parent_curve_start_point_ = (*parent_curve_fn_)(start_);
@@ -675,10 +833,10 @@ class curve_segment_evaluator {
} else if (segment_type_ == ST_CANT) {
Logger::Warning(std::runtime_error("Use of IfcCircle for cant is not supported"));
parent_curve_fn_ = [](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); };
parent_curve_fn_ = std::make_shared<parent_curve_function>([](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); });
} else {
Logger::Error(std::runtime_error("Unexpected segment type encountered"));
parent_curve_fn_ = [](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); };
parent_curve_fn_ = std::make_shared<parent_curve_function>([](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); });
}
}
@@ -706,12 +864,12 @@ class curve_segment_evaluator {
auto pcDx = dr[0];
auto pcDy = dr[1];
if (segment_type_ == ST_VERTICAL && placement_) {
if (segment_type_ == ST_VERTICAL && curve_segment_placement_) {
// the general algorithm for mapping parent curve onto curve segment doesn't
// exactly work for IfcLine. This is easily overcome by using the curve segment
// placement for the IfcLine direction
pcDx = (*placement_)(0, 0);
pcDy = (*placement_)(1, 0);
pcDx = (*curve_segment_placement_)(0, 0);
pcDy = (*curve_segment_placement_)(1, 0);
}
if (segment_type_ == ST_HORIZONTAL || segment_type_ == ST_VERTICAL || segment_type_ == ST_CANT) {
@@ -723,7 +881,7 @@ class curve_segment_evaluator {
convert_u = [pcDx](double u) { return u/pcDx; };
}
parent_curve_fn_ = [pcX, pcY, pcDx, pcDy, convert_u](double u) {
parent_curve_fn_ = std::make_shared<line_parent_curve>([pcX, pcY, pcDx, pcDy, convert_u](double u) {
u = convert_u(u);
auto x = pcX + pcDx * u;
@@ -734,13 +892,13 @@ class curve_segment_evaluator {
m.col(1) = Eigen::Vector4d(-pcDy, pcDx, 0, 0);
m.col(3) = Eigen::Vector4d(x, y, 0.0, 1.0);
return m;
};
});
parent_curve_start_point_ = (*parent_curve_fn_)(start_);
} else {
Logger::Warning(std::runtime_error("Unexpected segment type encountered"));
parent_curve_fn_ = [](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); };
}
parent_curve_fn_ = std::make_shared<parent_curve_function>([](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); });
}
}
void operator()(const IfcSchema::IfcPolynomialCurve* pc) {
@@ -755,7 +913,7 @@ class curve_segment_evaluator {
if (segment_type_ == ST_HORIZONTAL || segment_type_ == ST_VERTICAL) {
projected_length_ = length_;
// There is one significant difference between IfcPolynomalCurve used for horizontal and vertical alignments.
// There is one significant difference between IfcPolynomialCurve used for horizontal and vertical alignments.
// For horizontal alignment, u is the distance along the curve. For vertical alignment, u is the horizontal distance.
// From 4.2.2.2.8 the polynomial curve equation is in the form of y = Ax^3 for horizontal parabolic transition segments.
// To evaluate the horizontal function, the value of x that corresponds to the distance along the curve u is needed.
@@ -775,7 +933,11 @@ class curve_segment_evaluator {
for (; iter != end; iter++) {
auto exp = std::distance(begin, iter);
auto coeff = (*iter);
value += (double)exp * coeff * pow(lu, lu_exp--) * pow(x, exp - 1);
double a = (double)exp * coeff * pow(lu, lu_exp--);
double b = x ? pow(x, exp - 1) : 0; // when x and exp are zero? 0^-1 is infinite and the result is undefined
double v = a * b;
//double v = (double)exp * coeff * pow(lu, lu_exp--) * pow(x, exp - 1);
value += v;
}
return value;
};
@@ -815,7 +977,7 @@ class curve_segment_evaluator {
}
// This functor evaluates the polynomial at a distance u along the curve
parent_curve_fn_ = [start = start_, lu = length_unit_, coeffX, coeffY, convert_u](double u) -> Eigen::Matrix4d {
parent_curve_fn_ = std::make_shared<polynomial_parent_curve>([start = start_, lu = length_unit_, coeffX, coeffY, convert_u](double u)->Eigen::Matrix4d {
auto x = convert_u(u + start); // find x for u
// evaluate the polynomial at x
std::array<const std::vector<double>*, 2> coefficients{&coeffX, &coeffY};
@@ -851,131 +1013,24 @@ class curve_segment_evaluator {
m.col(1) = Eigen::Vector4d(-Dy, Dx, 0, 0);
m.col(3) = Eigen::Vector4d(X, Y, 0.0, 1.0);
return m;
};
});
parent_curve_start_point_ = (*parent_curve_fn_)(0.0); // start is added to u in parent_curve_fn_, so use 0.0 here
} else if (segment_type_ == ST_CANT) {
Logger::Warning(std::runtime_error("Use of IfcPolynomialCurve for cant is not supported"));
parent_curve_fn_ = [](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); };
parent_curve_fn_ = std::make_shared<parent_curve_function>([](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); });
} else {
Logger::Error(std::runtime_error("Unexpected segment type encountered"));
parent_curve_fn_ = [](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); };
parent_curve_fn_ = std::make_shared<parent_curve_function>([](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); });
}
}
};
} // namespace
taxonomy::ptr mapping::map_impl(const IfcSchema::IfcCurveSegment* inst) {
auto composite_curves = inst->UsingCurves();
// Find the next segment after inst
const IfcSchema::IfcCurveSegment* next_inst = nullptr;
if (composite_curves) {
if (composite_curves->size() == 1) {
auto segments = (*composite_curves->begin())->as<IfcSchema::IfcCompositeCurve>()->Segments();
bool emit_next = false;
for (auto& s : *segments) {
if (emit_next) {
next_inst = s->as<IfcSchema::IfcCurveSegment>();
break;
}
if (s == inst) {
emit_next = true;
}
}
} else {
Logger::Warning("IfcCurveSegment belongs to multiple IfcCompositeCurve instances. Cannot determine the next segment.");
}
}
bool is_horizontal = false;
bool is_vertical = false;
bool is_cant = false;
if (composite_curves) {
for (auto& cc : *composite_curves) {
if (cc->as<IfcSchema::IfcSegmentedReferenceCurve>()) {
is_cant = true;
} else if (cc->as<IfcSchema::IfcGradientCurve>()) {
is_vertical = true;
} else {
is_horizontal = true;
}
}
}
if ((is_horizontal + is_vertical + is_cant) != 1) {
// We have to choose the correct functor based on usage. We can't
// support multiple, because we don't know the caller at this point.
return nullptr;
}
auto segment_type = is_horizontal ? ST_HORIZONTAL : is_vertical ? ST_VERTICAL : ST_CANT;
curve_segment_evaluator cse(this, inst, next_inst, length_unit_, segment_type);
curve_segment_evaluator cse(this, inst, length_unit_);
boost::mpl::for_each<curve_seg_types, boost::type<boost::mpl::_>>(std::ref(cse));
const auto& parent_curve_fn = cse.parent_curve_function();
const auto& parent_curve_start_point = cse.parent_curve_start_point();
if (!parent_curve_fn || !parent_curve_start_point) {
Logger::Error(std::runtime_error(inst->ParentCurve()->declaration().name() + " not implemented"), inst);
}
const auto& curve_segment_placement = cse.segment_placement();
std::function<Eigen::Matrix4d(double u)> fn;
if (segment_type == ST_CANT)
{
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 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 {
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;
};
}
auto length = cse.length();
taxonomy::piecewise_function::spans_t spans;
spans.emplace_back(fabs(length), fn);
auto pwf = taxonomy::make<taxonomy::piecewise_function>(0.0, spans,inst);
return pwf;
return cse.get_segment_curve_function();
}
#endif
@@ -18,7 +18,7 @@
********************************************************************************/
#include "mapping.h"
#include "../piecewise_function_evaluator.h"
#include "../function_item_evaluator.h"
#define mapping POSTFIX_SCHEMA(mapping)
using namespace ifcopenshell::geometry;
@@ -34,10 +34,10 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcFixedReferenceSweptAreaSolid
loft->axis = nullptr;
// @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_);
if (auto fn = taxonomy::dcast<taxonomy::function_item>(dir)) {
function_item_evaluator evaluator(settings_,fn);
double start = 0;
double end = pwf->length();
double end = fn->length();
#ifdef SCHEMA_HAS_IfcDirectrixCurveSweptAreaSolid
// IfcDirectrixCurveSweptAreaSolid introduced in 4.3 changed attribute type
// from optional IfcParamValue to optional IfcCurveMeasureSelect.
+16 -33
View File
@@ -18,7 +18,7 @@
********************************************************************************/
#include "mapping.h"
#include "../piecewise_function_evaluator.h"
#include "../function_item_evaluator.h"
#define mapping POSTFIX_SCHEMA(mapping)
using namespace ifcopenshell::geometry;
@@ -30,15 +30,17 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcGradientCurve* inst) {
auto segments = inst->Segments();
std::vector<taxonomy::piecewise_function::ptr> pwfs;
taxonomy::piecewise_function::spans_t spans;
for (auto& segment : *segments) {
if (segment->as<IfcSchema::IfcCurveSegment>()) {
// @todo check that we don't get a mixture of implicit and explicit definitions
auto crv = map(segment->as<IfcSchema::IfcCurveSegment>());
if (crv && crv->kind() == taxonomy::PIECEWISE_FUNCTION) {
pwfs.push_back(taxonomy::cast<taxonomy::piecewise_function>(crv));
} else {
if (auto fi = taxonomy::dcast<taxonomy::function_item>(crv); crv && fi /*crv->kind() == taxonomy::FUNCTION_ITEM*/) {
// crv->kind() is polymorphic and the kind of the actual function_item is returned. PWF can have spans of any FUNCTION_ITEM
// for this reason, a dynamic cast is used and if crv is a function_item it is added to the span
spans.push_back(fi);
} else {
Logger::Error("Unsupported");
return nullptr;
}
@@ -56,40 +58,21 @@ 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);
auto vertical = taxonomy::make<taxonomy::piecewise_function>(gradient_start, spans);
// Determine the valid domain of the PWF... the valid domain is where both
// the base curve and gradient curves are defined
// create the horizontal pwf
auto horizontal = taxonomy::cast<taxonomy::piecewise_function>(map(inst->BaseCurve()));
double start = std::max(horizontal->start(),vertical->start());
double end = std::min(horizontal->end(), vertical->end());
double length = end - start;
if (!(0 < length)) {
// create the composite gradient curve function
auto gradient_function = taxonomy::make<taxonomy::gradient_function>(horizontal, vertical, inst);
// check to see if there is valid overlap of the horizontal and vertical domains
if (!(0 < gradient_function->length())) {
Logger::Error("IfcGradientCurve does not have a common domain with BaseCurve");
gradient_function = nullptr; // not valid
}
// define the callback function for the gradient curve
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_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
uz.row(1).swap(uz.row(2));
Eigen::Matrix4d m;
m = xy * uz; // combine horizontal and vertical
return m;
};
taxonomy::piecewise_function::spans_t spans;
spans.emplace_back(length, composition);
auto pwf = taxonomy::make<taxonomy::piecewise_function>(start, spans, inst);
return pwf;
return gradient_function;
}
#endif
@@ -19,7 +19,7 @@
#include "mapping.h"
#include "../profile_helper.h"
#include "../piecewise_function_evaluator.h"
#include "../function_item_evaluator.h"
#define mapping POSTFIX_SCHEMA(mapping)
using namespace ifcopenshell::geometry;
@@ -39,10 +39,10 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcOffsetCurveByDistances* inst
auto first_offset_value = *(offset_values->begin());
auto basis_curve = inst->BasisCurve();
auto pw_curve = taxonomy::dcast<taxonomy::piecewise_function>(map(basis_curve));
auto curve = taxonomy::dcast<taxonomy::piecewise_function>(map(basis_curve));
double start = pw_curve->start();
double basis_curve_length = pw_curve->length();
double start = curve->start();
double basis_curve_length = curve->length();
taxonomy::piecewise_function::spans_t offset_spans;
@@ -70,7 +70,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcOffsetCurveByDistances* inst
m.col(3)(1) = py;
m.col(3)(2) = pz;
return m; };
offset_spans.emplace_back(first_distance, fn);
offset_spans.emplace_back(taxonomy::make<taxonomy::functor_item>(first_distance, fn));
}
auto iter = offset_values->begin();
@@ -114,7 +114,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcOffsetCurveByDistances* inst
m.col(3)(2) = (l == 0.0 ? zp : (zp + (zn - zp) * u / l));
return m;
};
offset_spans.emplace_back(l, fn);
offset_spans.emplace_back(taxonomy::make<taxonomy::functor_item>(l, fn));
}
// at this point, next == end and prev == end-1
@@ -142,25 +142,13 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcOffsetCurveByDistances* inst
m.col(3)(2) = pz;
return m; };
offset_spans.emplace_back(l, fn);
offset_spans.emplace_back(taxonomy::make<taxonomy::functor_item>(l, fn));
}
auto offsets = taxonomy::make<taxonomy::piecewise_function>(start,offset_spans);
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;
};
// current implementation assumes that composition is equal to the full length of basis curve
// 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,inst);
return pwf;
auto fn = taxonomy::make<taxonomy::offset_function>(curve, offsets);
return fn;
}
#endif
@@ -19,7 +19,7 @@
#include "mapping.h"
#include "../profile_helper.h"
#include "../piecewise_function_evaluator.h"
#include "../function_item_evaluator.h"
#define mapping POSTFIX_SCHEMA(mapping)
using namespace ifcopenshell::geometry;
@@ -28,11 +28,15 @@ using namespace ifcopenshell::geometry;
taxonomy::ptr mapping::map_impl(const IfcSchema::IfcPointByDistanceExpression* inst) {
auto u = (*inst->DistanceAlong()->as<IfcSchema::IfcLengthMeasure>()) * length_unit_;
//auto basis_curve = inst->BasisCurve();
//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()));
piecewise_function_evaluator evaluator(pw_curve,&settings_);
auto basis_curve = map(inst->BasisCurve());
taxonomy::function_item::ptr curve = taxonomy::dcast<taxonomy::function_item>(basis_curve);
if (!curve) {
// if the basis curve is not a function_item, the cast it to piecewise_function. the casting operator
// calls loop_to_piecewise_function_upgrade and will convert loops to a piecewise function
curve = taxonomy::dcast<taxonomy::piecewise_function>(basis_curve);
}
function_item_evaluator evaluator(settings_,curve);
auto m = evaluator.evaluate(u);
auto o = m.col(3).head<3>();
@@ -21,7 +21,7 @@
#define mapping POSTFIX_SCHEMA(mapping)
using namespace ifcopenshell::geometry;
#include "../piecewise_function_evaluator.h"
#include "../function_item_evaluator.h"
#ifdef SCHEMA_HAS_IfcSegmentedReferenceCurve
@@ -31,14 +31,16 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcSegmentedReferenceCurve* ins
auto segments = inst->Segments();
std::vector<taxonomy::piecewise_function::ptr> pwfs;
taxonomy::piecewise_function::spans_t spans;
for (auto& segment : *segments) {
if (segment->as<IfcSchema::IfcCurveSegment>()) {
// @todo check that we don't get a mixture of implicit and explicit definitions
auto crv = map(segment->as<IfcSchema::IfcCurveSegment>());
if (crv && crv->kind() == taxonomy::PIECEWISE_FUNCTION) {
pwfs.push_back(taxonomy::cast<taxonomy::piecewise_function>(crv));
} else {
if (auto fi = taxonomy::dcast<taxonomy::function_item>(crv); crv && fi /*crv->kind() == taxonomy::FUNCTION_ITEM*/) {
// crv->kind() is polymorphic and the kind of the actual function_item is returned. PWF can have spans of any FUNCTION_ITEM
// for this reason, a dynamic cast is used and if crv is a function_item it is added to the span
spans.push_back(fi);
} else {
Logger::Error("Unsupported");
return nullptr;
}
@@ -55,61 +57,15 @@ 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);
auto cant = taxonomy::make<taxonomy::piecewise_function>(cant_start,spans);
auto gradient = taxonomy::cast<taxonomy::gradient_function>(map(inst->BaseCurve()));
// Determine the valid domain of the PWF... the valid domain is where
// horizontal, gradient and cant curves are defined
auto gradient = taxonomy::cast<taxonomy::piecewise_function>(map(inst->BaseCurve()));
auto horizontal = taxonomy::cast<taxonomy::piecewise_function>(map(inst->BaseCurve()->as<IfcSchema::IfcGradientCurve>()->BaseCurve()));
double start = std::max(std::max(cant->start(), gradient->start()), horizontal->start());
double end = std::min(std::min(cant->end(), gradient->end()), horizontal->end());
double length = end - start;
if (!(0 < length)) {
Logger::Error("IfcSegmentedReferenceCurve does not have a common domain with BaseCurve");
}
// define the callback function for the segmented reference curve
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_evaluator.evaluate(u + start);
auto c = cant_evaluator.evaluate(u);
// 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;
return m;
};
taxonomy::piecewise_function::spans_t spans;
spans.emplace_back(length, composition);
auto pwf = taxonomy::make<taxonomy::piecewise_function>(start, spans, inst);
return pwf;
auto cant_function = taxonomy::make<taxonomy::cant_function>(gradient, cant, inst);
if (!(0 < cant_function->length())) {
Logger::Error("IfcSegmentedReferenceCurve does not have a common domain with BaseCurve");
cant_function = nullptr;
}
return cant_function;
}
#endif