Apply skew in infra lofts #6386

This commit is contained in:
Thomas Krijnen
2025-03-20 16:13:56 +01:00
parent 35fe79dfb2
commit 4c2844292e
4 changed files with 58 additions and 8 deletions
+27 -6
View File
@@ -63,31 +63,37 @@ taxonomy::loft::ptr ifcopenshell::geometry::make_loft(const Settings& settings_,
auto relative_dist_along = (dist_along - *profile_index) / (*(profile_index + 1) - *profile_index);
const auto& profile_a = cross_sections[std::distance(longitudes.begin(), profile_index)].section_geometry;
const auto& offset_a = cross_sections[std::distance(longitudes.begin(), profile_index)].offset;
const auto& rotation_a = cross_sections[std::distance(longitudes.begin(), profile_index)].rotation;
taxonomy::geom_item::ptr interpolated = nullptr;
// Only interpolate if:
// - there is a profile ahead of us, and
// - we're not exactly at the location of the current profile or whether there is an offset involved.
// - we're not exactly at the location of the current profile or whether there is an offset involved
bool should_interpolate =
(profile_index + 1 < longitudes.end()) &&
(relative_dist_along >= 1.e-9 || offset_a.cwiseAbs().maxCoeff() > 0.);
(relative_dist_along >= 1.e-9 || offset_a.cwiseAbs().maxCoeff() > 0. || rotation_a);
boost::optional<Eigen::Matrix3d> interpolated_rotation;
if (should_interpolate) {
taxonomy::geom_item::ptr profile_b;
Eigen::Vector3d offset_b;
boost::optional<Eigen::Matrix3d> rotation_b;
if ((profile_index + 1 < longitudes.end())) {
profile_b = cross_sections[std::distance(longitudes.begin(), profile_index) + 1].section_geometry;
offset_b = cross_sections[std::distance(longitudes.begin(), profile_index) + 1].offset;
rotation_b = cross_sections[std::distance(longitudes.begin(), profile_index) + 1].rotation;
} else {
profile_b = profile_a;
offset_b = offset_a;
rotation_b = rotation_a;
}
// Only interpolate if the profiles are different or either of the offsets is non-zero
bool should_interpolate2 =
(profile_a->instance != profile_b->instance) ||
(offset_a.cwiseAbs().maxCoeff() > 0. || offset_b.cwiseAbs().maxCoeff() > 0.);
(offset_a.cwiseAbs().maxCoeff() > 0. || offset_b.cwiseAbs().maxCoeff() > 0. || rotation_b);
if (should_interpolate2) {
@@ -130,6 +136,13 @@ taxonomy::loft::ptr ifcopenshell::geometry::make_loft(const Settings& settings_,
}
auto interpolated_offset = lerp(offset_a, offset_b, relative_dist_along);
if (rotation_a && rotation_b) {
// @todo we don't support an overridden rotation on only one of the placements
// in which case we would need to lerp with the rotation component below in m4b.
interpolated_rotation = lerp(*rotation_a, *rotation_b, relative_dist_along);
} else {
Logger::Error("Direction vectors on cross section placements only supported when used consistently");
}
taxonomy::loop::ptr w1, w2;
taxonomy::edge::ptr e1, e2;
for (auto tmp_ : boost::combine(loops_a, loops_b)) {
@@ -149,6 +162,7 @@ taxonomy::loft::ptr ifcopenshell::geometry::make_loft(const Settings& settings_,
auto& p2 = boost::get<taxonomy::point3::ptr>(e2->start);
auto p3 = (lerp(p1->ccomponents(), p2->ccomponents(), relative_dist_along) + interpolated_offset).eval();
// auto p4 = (interpolated_rotation * p3).eval();
points.push_back(taxonomy::make<taxonomy::point3>(p3));
}
if (!points.empty()) {
@@ -173,9 +187,16 @@ taxonomy::loft::ptr ifcopenshell::geometry::make_loft(const Settings& settings_,
}*/
Eigen::Matrix4d m4b = Eigen::Matrix4d::Identity();
m4b.col(0).head<3>() = m4.col(1).head<3>().normalized();
m4b.col(1).head<3>() = m4.col(2).head<3>().normalized();
m4b.col(2).head<3>() = m4.col(0).head<3>().normalized();
if (interpolated_rotation) {
// direction vectors on the linear placement overwrite the placement otherwise inferred from the tangent
m4b.col(0).head<3>() = interpolated_rotation->col(1);
m4b.col(1).head<3>() = interpolated_rotation->col(2);
m4b.col(2).head<3>() = interpolated_rotation->col(0);
} else {
m4b.col(0).head<3>() = m4.col(1).head<3>().normalized();
m4b.col(1).head<3>() = m4.col(2).head<3>().normalized();
m4b.col(2).head<3>() = m4.col(0).head<3>().normalized();
}
m4b.col(3).head<3>() = m4.col(3).head<3>();
if (interpolated) {
+1
View File
@@ -12,6 +12,7 @@ namespace ifcopenshell {
double dist_along;
taxonomy::geom_item::ptr section_geometry;
Eigen::Vector3d offset;
boost::optional<Eigen::Matrix3d> rotation;
bool operator <(const cross_section& other) const {
return dist_along < other.dist_along;
@@ -49,6 +49,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcSectionedSolidHorizontal* in
// The longitudes determine the range of the sweep and the offsets are interpolated in between
// sweep segments.
std::vector<Eigen::Vector3d> profile_offsets;
std::vector<boost::optional<Eigen::Matrix3d>> profile_rotations;
std::vector<double> longitudes;
for (auto& cs : *css) {
@@ -69,6 +70,19 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcSectionedSolidHorizontal* in
);
profile_offsets.push_back(po);
boost::optional<Eigen::Matrix3d> rot;
if (csp->Axis() && csp->RefDirection()) {
rot = taxonomy::matrix4(
Eigen::Vector3d(0, 0, 0),
taxonomy::cast<taxonomy::direction3>(map(csp->Axis()))->ccomponents(),
taxonomy::cast<taxonomy::direction3>(map(csp->RefDirection()))->ccomponents()).ccomponents().block<3,3>(0,0);
} else if (csp->Axis()) {
rot = taxonomy::matrix4(
Eigen::Vector3d(0, 0, 0),
taxonomy::cast<taxonomy::direction3>(map(csp->Axis()))->ccomponents()).ccomponents().block<3, 3>(0, 0);
}
profile_rotations.push_back(rot);
}
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);
@@ -80,7 +94,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcSectionedSolidHorizontal* in
}
for (size_t i = 0; i < faces.size(); ++i) {
cross_sections.push_back({ longitudes[i], faces[i], profile_offsets[i] });
cross_sections.push_back({ longitudes[i], faces[i], profile_offsets[i], profile_rotations[i]});
}
#else
return nullptr;
+15 -1
View File
@@ -49,6 +49,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcSectionedSurface* inst) {
// The longitudes determine the range of the sweep and the offsets are interpolated in between
// sweep segments.
std::vector<Eigen::Vector3d> profile_offsets;
std::vector<boost::optional<Eigen::Matrix3d>> profile_rotations;
std::vector<double> longitudes;
for (auto& cs : *css) {
@@ -69,6 +70,19 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcSectionedSurface* inst) {
);
profile_offsets.push_back(po);
boost::optional<Eigen::Matrix3d> rot;
if (csp->Axis() && csp->RefDirection()) {
rot = taxonomy::matrix4(
Eigen::Vector3d(0, 0, 0),
taxonomy::cast<taxonomy::direction3>(map(csp->Axis()))->ccomponents(),
taxonomy::cast<taxonomy::direction3>(map(csp->RefDirection()))->ccomponents()).ccomponents().block<3, 3>(0, 0);
} else if (csp->Axis()) {
rot = taxonomy::matrix4(
Eigen::Vector3d(0, 0, 0),
taxonomy::cast<taxonomy::direction3>(map(csp->Axis()))->ccomponents()).ccomponents().block<3, 3>(0, 0);
}
profile_rotations.push_back(rot);
}
#else
return nullptr;
@@ -83,7 +97,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcSectionedSurface* inst) {
}
for (size_t i = 0; i < faces.size(); ++i) {
cross_sections.push_back({ longitudes[i], faces[i], profile_offsets[i] });
cross_sections.push_back({ longitudes[i], faces[i], profile_offsets[i], profile_rotations[i] });
}
}