Merge branch 'v0.8.0' into v0.8.0

This commit is contained in:
csritter
2024-09-25 11:20:47 -03:00
committed by GitHub
520 changed files with 22152 additions and 6424 deletions
+1
View File
@@ -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;
}
+1 -1
View File
@@ -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;
}
}
+188 -117
View File
@@ -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;
}
+2 -2
View File
@@ -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);
+4 -1
View File
@@ -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;
+7 -5
View File
@@ -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;
}
+9 -5
View File
@@ -65,16 +65,21 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcObjectPlacement* inst) {
}
}
taxonomy::ptr result;
taxonomy::matrix4::ptr result;
if (!parent_placement_ignored && relative_to) {
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 -38
View File
@@ -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;
}
+38 -16
View File
@@ -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() {
+3 -1
View File
@@ -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) {
+3
View File
@@ -135,6 +135,9 @@ BIND(IfcFixedReferenceSweptAreaSolid)
#ifdef SCHEMA_HAS_IfcSectionedSolidHorizontal
BIND(IfcSectionedSolidHorizontal)
#endif
#ifdef SCHEMA_HAS_IfcSectionedSurface
BIND(IfcSectionedSurface)
#endif
BIND(IfcCircle);
BIND(IfcEllipse);