File indexing completed on 2025-01-18 09:11:10
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include "Acts/Utilities/VectorHelpers.hpp"
0010
0011 #include <numbers>
0012
0013 template <typename value_t, std::size_t DIM, std::size_t SIDES>
0014 Acts::Frustum<value_t, DIM, SIDES>::Frustum(const VertexType& origin,
0015 const VertexType& dir,
0016 value_type opening_angle)
0017 requires(DIM == 2)
0018 : m_origin(origin) {
0019 using rotation_t = Eigen::Rotation2D<value_type>;
0020
0021 static_assert(SIDES == 2, "2D frustum can only have 2 sides");
0022 assert(opening_angle < std::numbers::pi_v<value_type>);
0023
0024 translation_t translation(origin);
0025 value_type angle = VectorHelpers::phi(dir);
0026 Eigen::Rotation2D<value_type> rot(angle);
0027
0028 value_type normal_angle = std::numbers::pi / 2. - opening_angle / 2.;
0029 VertexType normal1 = rotation_t(normal_angle) * VertexType::UnitX();
0030 VertexType normal2 = rotation_t(-normal_angle) * VertexType::UnitX();
0031
0032 m_normals = {rot * VertexType::UnitX(), rot * normal1, rot * normal2};
0033 }
0034
0035 template <typename value_t, std::size_t DIM, std::size_t SIDES>
0036 Acts::Frustum<value_t, DIM, SIDES>::Frustum(const VertexType& origin,
0037 const VertexType& dir,
0038 value_type opening_angle)
0039 requires(DIM == 3)
0040 : m_origin(origin) {
0041 static_assert(SIDES > 2, "3D frustum must have 3 or more sides");
0042 assert(opening_angle < std::numbers::pi_v<value_type>);
0043 using angle_axis_t = Eigen::AngleAxis<value_type>;
0044
0045 const VertexType ldir = VertexType::UnitZ();
0046 const VertexType lup = VertexType::UnitX();
0047
0048 transform_type transform;
0049 transform = (Eigen::Quaternion<value_type>().setFromTwoVectors(ldir, dir));
0050
0051 m_normals[0] = ldir;
0052
0053 const value_type phi_sep = 2. * std::numbers::pi_v<value_type> / sides;
0054 transform_type rot;
0055 rot = angle_axis_t(phi_sep, ldir);
0056
0057 value_type half_opening_angle = opening_angle / 2.;
0058 auto calculate_normal =
0059 [&ldir, &half_opening_angle](const VertexType& out) -> VertexType {
0060 const VertexType tilt_axis = -1 * out.cross(ldir);
0061 return (-1 * (angle_axis_t(half_opening_angle, tilt_axis) * out))
0062 .normalized();
0063 };
0064
0065 VertexType current_outward = lup;
0066 m_normals[1] = calculate_normal(current_outward);
0067
0068 for (std::size_t i = 1; i < sides; i++) {
0069 current_outward = rot * current_outward;
0070 m_normals[i + 1] = calculate_normal(current_outward);
0071 }
0072
0073 for (auto& normal : m_normals) {
0074 normal = transform * normal;
0075 }
0076 }
0077
0078 template <typename value_t, std::size_t DIM, std::size_t SIDES>
0079 void Acts::Frustum<value_t, DIM, SIDES>::draw(IVisualization3D& helper,
0080 value_type far_distance) const
0081 requires(DIM == 3)
0082 {
0083 static_assert(DIM == 3, "Drawing is only supported in 3D");
0084
0085
0086
0087
0088
0089 VertexType far_normal = m_normals[0];
0090 VertexType far_center = m_normals[0] * far_distance;
0091 std::array<std::pair<VertexType, VertexType>, SIDES> planeFarIXs;
0092
0093 auto ixPlanePlane = [](const auto& n1, const auto& p1, const auto& n2,
0094 const auto& p2) -> std::pair<VertexType, VertexType> {
0095 const VertexType m = n1.cross(n2).normalized();
0096 const double j = (n2.dot(p2 - p1)) / (n2.dot(n1.cross(m)));
0097 const VertexType q = p1 + j * n1.cross(m);
0098 return {m, q};
0099 };
0100
0101 auto ixLineLine = [](const auto& p1, const auto& d1, const auto& p2,
0102 const auto& d2) -> VertexType {
0103 return p1 + (((p2 - p1).cross(d2)).norm() / (d1.cross(d2)).norm()) * d1;
0104 };
0105
0106
0107 for (std::size_t i = 1; i < n_normals; i++) {
0108 const auto ixLine =
0109 ixPlanePlane(far_normal, far_center, m_normals[i], VertexType::Zero());
0110 planeFarIXs.at(i - 1) = ixLine;
0111 }
0112
0113 std::array<VertexType, SIDES> points;
0114
0115 for (std::size_t i = 0; i < std::size(planeFarIXs); i++) {
0116 std::size_t j = (i + 1) % std::size(planeFarIXs);
0117 const auto& l1 = planeFarIXs.at(i);
0118 const auto& l2 = planeFarIXs.at(j);
0119 const VertexType ix =
0120 m_origin + ixLineLine(l1.second, l1.first, l2.second, l2.first);
0121 points.at(i) = ix;
0122 }
0123
0124 for (std::size_t i = 0; i < std::size(points); i++) {
0125 std::size_t j = (i + 1) % std::size(points);
0126 helper.face(
0127 std::vector<VertexType>({m_origin, points.at(i), points.at(j)}));
0128 }
0129 }
0130
0131 template <typename value_t, std::size_t DIM, std::size_t SIDES>
0132 std::ostream& Acts::Frustum<value_t, DIM, SIDES>::svg(std::ostream& os,
0133 value_type w,
0134 value_type h,
0135 value_type far_distance,
0136 value_type unit) const
0137 requires(DIM == 2)
0138 {
0139 static_assert(DIM == 2, "SVG is only supported in 2D");
0140
0141 VertexType mid(w / 2., h / 2.);
0142
0143
0144
0145 transform_type trf = transform_type::Identity();
0146 trf.translate(mid);
0147 trf = trf * Eigen::Scaling(VertexType(1, -1));
0148 trf.scale(unit);
0149
0150 std::array<std::string, 3> colors({"orange", "blue", "red"});
0151
0152 auto draw_line = [&](const VertexType& left_, const VertexType& right_,
0153 const std::string& color, std::size_t width) {
0154 VertexType left = trf * left_;
0155 VertexType right = trf * right_;
0156 os << "<line ";
0157
0158 os << "x1=\"" << left.x() << "\" ";
0159 os << "y1=\"" << left.y() << "\" ";
0160 os << "x2=\"" << right.x() << "\" ";
0161 os << "y2=\"" << right.y() << "\" ";
0162
0163 os << " stroke=\"" << color << "\" stroke-width=\"" << width << "\"/>\n";
0164 };
0165
0166 auto draw_point = [&](const VertexType& p_, const std::string& color,
0167 std::size_t r) {
0168 VertexType p = trf * p_;
0169 os << "<circle ";
0170 os << "cx=\"" << p.x() << "\" cy=\"" << p.y() << "\" r=\"" << r << "\"";
0171 os << " fill=\"" << color << "\"";
0172 os << "/>\n";
0173 };
0174
0175 using vec3 = Eigen::Matrix<value_type, 3, 1>;
0176 auto ixLineLine = [](const VertexType& p1_2, const VertexType& d1_2,
0177 const VertexType& p2_2,
0178 const VertexType& d2_2) -> VertexType {
0179 const vec3 p1(p1_2.x(), p1_2.y(), 0);
0180 const vec3 p2(p2_2.x(), p2_2.y(), 0);
0181 const vec3 d1(d1_2.x(), d1_2.y(), 0);
0182 const vec3 d2(d2_2.x(), d2_2.y(), 0);
0183
0184 vec3 num = (p2 - p1).cross(d2);
0185 vec3 den = d1.cross(d2);
0186
0187 value_type f = 1.;
0188 value_type dot = num.normalized().dot(den.normalized());
0189 if (std::abs(dot) - 1 < 1e-9 && dot < 0) {
0190 f = -1.;
0191 }
0192
0193 const vec3 p = p1 + f * (num.norm() / den.norm()) * d1;
0194 assert(std::abs(p.z()) < 1e-9);
0195 return {p.x(), p.y()};
0196 };
0197
0198 const VertexType far_dir = {m_normals[0].y(), -m_normals[0].x()};
0199 const VertexType far_point = m_normals[0] * far_distance;
0200
0201 std::array<VertexType, 2> points;
0202
0203 for (std::size_t i = 1; i < n_normals; i++) {
0204 VertexType plane_dir(m_normals[i].y(), -m_normals[i].x());
0205
0206 const VertexType ix = ixLineLine(far_point, far_dir, {0, 0}, plane_dir);
0207 draw_point(m_origin + ix, "black", 3);
0208 draw_line(m_origin, m_origin + ix, "black", 2);
0209 points.at(i - 1) = ix;
0210 }
0211
0212 draw_line(m_origin + points.at(0), m_origin + points.at(1), "black", 2);
0213
0214 draw_line(m_origin, m_origin + m_normals[0] * 2, colors[0], 3);
0215
0216 draw_point({0, 0}, "yellow", 5);
0217 draw_point(m_origin, "green", 5);
0218
0219 return os;
0220 }
0221
0222 template <typename value_t, std::size_t DIM, std::size_t SIDES>
0223 Acts::Frustum<value_t, DIM, SIDES>
0224 Acts::Frustum<value_t, DIM, SIDES>::transformed(
0225 const transform_type& trf) const {
0226 const auto& rot = trf.rotation();
0227
0228 std::array<VertexType, n_normals> new_normals;
0229 for (std::size_t i = 0; i < n_normals; i++) {
0230 new_normals[i] = rot * m_normals[i];
0231 }
0232
0233 return Frustum<value_t, DIM, SIDES>(trf * m_origin, std::move(new_normals));
0234 }