Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Frustum.ipp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file Frustum.ipp
1 // This file is part of the Acts project.
2 //
3 // Copyright (C) 2018-2019 CERN for the benefit of the Acts project
4 //
5 // This Source Code Form is subject to the terms of the Mozilla Public
6 // License, v. 2.0. If a copy of the MPL was not distributed with this
7 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 
10 
11 template <typename value_t, size_t DIM, size_t SIDES>
12 template <size_t D, std::enable_if_t<D == 2, int>>
14  const VertexType& dir,
15  value_type opening_angle)
16  : m_origin(origin) {
17  using rotation_t = Eigen::Rotation2D<value_type>;
18 
19  static_assert(SIDES == 2, "2D frustum can only have 2 sides");
20  assert(opening_angle < M_PI);
21 
22  translation_t translation(origin);
23  value_type angle = VectorHelpers::phi(dir);
24  Eigen::Rotation2D<value_type> rot(angle);
25 
26  value_type normal_angle = 0.5 * M_PI - 0.5 * opening_angle;
27  VertexType normal1 = rotation_t(normal_angle) * VertexType::UnitX();
28  VertexType normal2 = rotation_t(-normal_angle) * VertexType::UnitX();
29 
30  m_normals = {rot * VertexType::UnitX(), rot * normal1, rot * normal2};
31 }
32 
33 template <typename value_t, size_t DIM, size_t SIDES>
34 template <size_t D, std::enable_if_t<D == 3, int>>
36  const VertexType& dir,
37  value_type opening_angle)
38  : m_origin(origin) {
39  static_assert(SIDES > 2, "3D frustum must have 3 or more sides");
40  assert(opening_angle < M_PI);
41  using angle_axis_t = Eigen::AngleAxis<value_type>;
42 
43  const VertexType ldir = VertexType::UnitZ();
44  const VertexType lup = VertexType::UnitX();
45 
47  transform = (Eigen::Quaternion<value_type>().setFromTwoVectors(ldir, dir));
48 
49  m_normals[0] = ldir;
50 
51  const value_type phi_sep = 2 * M_PI / sides;
52  transform_type rot;
53  rot = angle_axis_t(phi_sep, ldir);
54 
55  value_type half_opening_angle = opening_angle / 2.;
56  auto calculate_normal =
57  [&ldir, &half_opening_angle](const VertexType& out) -> VertexType {
58  const VertexType tilt_axis = -1 * out.cross(ldir);
59  return (-1 * (angle_axis_t(half_opening_angle, tilt_axis) * out))
60  .normalized();
61  };
62 
63  VertexType current_outward = lup;
64  m_normals[1] = calculate_normal(current_outward);
65 
66  for (size_t i = 1; i < sides; i++) {
67  current_outward = rot * current_outward;
68  m_normals[i + 1] = calculate_normal(current_outward);
69  }
70 
71  for (auto& normal : m_normals) {
72  normal = transform * normal;
73  }
74 }
75 
76 template <typename value_t, size_t DIM, size_t SIDES>
77 template <size_t D, std::enable_if_t<D == 3, int>>
79  value_type far_distance) const {
80  static_assert(DIM == 3, "Drawing is only supported in 3D");
81 
82  // Iterate around normals, calculate cross with "far" plane
83  // to get intersection lines.
84  // Work in local reference frame of the frustum, and only convert to global
85  // right before drawing.
86  VertexType far_normal = m_normals[0]; // far has same normal as pseudo-near
87  VertexType far_center = m_normals[0] * far_distance;
88  std::array<std::pair<VertexType, VertexType>, SIDES> planeFarIXs;
89 
90  auto ixPlanePlane = [](const auto& n1, const auto& p1, const auto& n2,
91  const auto& p2) -> std::pair<VertexType, VertexType> {
92  const VertexType m = n1.cross(n2).normalized();
93  const double j = (n2.dot(p2 - p1)) / (n2.dot(n1.cross(m)));
94  const VertexType q = p1 + j * n1.cross(m);
95  return {m, q};
96  };
97 
98  auto ixLineLine = [](const auto& p1, const auto& d1, const auto& p2,
99  const auto& d2) -> VertexType {
100  return p1 + (((p2 - p1).cross(d2)).norm() / (d1.cross(d2)).norm()) * d1;
101  };
102 
103  // skip i=0 <=> pseudo-near
104  for (size_t i = 1; i < n_normals; i++) {
105  const auto ixLine =
106  ixPlanePlane(far_normal, far_center, m_normals[i], VertexType::Zero());
107  planeFarIXs.at(i - 1) = ixLine;
108  }
109 
110  std::array<VertexType, SIDES> points;
111 
112  for (size_t i = 0; i < std::size(planeFarIXs); i++) {
113  size_t j = (i + 1) % std::size(planeFarIXs);
114  const auto& l1 = planeFarIXs.at(i);
115  const auto& l2 = planeFarIXs.at(j);
116  const VertexType ix =
117  m_origin + ixLineLine(l1.second, l1.first, l2.second, l2.first);
118  points.at(i) = ix;
119  }
120 
121  for (size_t i = 0; i < std::size(points); i++) {
122  size_t j = (i + 1) % std::size(points);
123  helper.face(
124  std::vector<VertexType>({m_origin, points.at(i), points.at(j)}));
125  }
126 }
127 
128 template <typename value_t, size_t DIM, size_t SIDES>
129 template <size_t D, std::enable_if_t<D == 2, int>>
130 std::ostream& Acts::Frustum<value_t, DIM, SIDES>::svg(std::ostream& os,
131  value_type w,
132  value_type h,
133  value_type far_distance,
134  value_type unit) const {
135  static_assert(DIM == 2, "SVG is only supported in 2D");
136 
137  VertexType mid(w / 2., h / 2.);
138 
139  // set up transform for svg. +y is down, normally, and unit is pixels.
140  // We flip the y axis, and scale up by `unit`.
141  transform_type trf = transform_type::Identity();
142  trf.translate(mid);
143  trf = trf * Eigen::Scaling(VertexType(1, -1));
144  trf.scale(unit);
145 
146  std::array<std::string, 3> colors({"orange", "blue", "red"});
147 
148  auto draw_line = [&](const VertexType& left_, const VertexType& right_,
149  const std::string& color, size_t width) {
150  VertexType left = trf * left_;
151  VertexType right = trf * right_;
152  os << "<line ";
153 
154  os << "x1=\"" << left.x() << "\" ";
155  os << "y1=\"" << left.y() << "\" ";
156  os << "x2=\"" << right.x() << "\" ";
157  os << "y2=\"" << right.y() << "\" ";
158 
159  os << " stroke=\"" << color << "\" stroke-width=\"" << width << "\"/>\n";
160  };
161 
162  auto draw_point = [&](const VertexType& p_, const std::string& color,
163  size_t r) {
164  VertexType p = trf * p_;
165  os << "<circle ";
166  os << "cx=\"" << p.x() << "\" cy=\"" << p.y() << "\" r=\"" << r << "\"";
167  os << " fill=\"" << color << "\"";
168  os << "/>\n";
169  };
170 
171  using vec3 = Eigen::Matrix<value_type, 3, 1>;
172  auto ixLineLine = [](const VertexType& p1_2, const VertexType& d1_2,
173  const VertexType& p2_2,
174  const VertexType& d2_2) -> VertexType {
175  const vec3 p1(p1_2.x(), p1_2.y(), 0);
176  const vec3 p2(p2_2.x(), p2_2.y(), 0);
177  const vec3 d1(d1_2.x(), d1_2.y(), 0);
178  const vec3 d2(d2_2.x(), d2_2.y(), 0);
179 
180  vec3 num = (p2 - p1).cross(d2);
181  vec3 den = d1.cross(d2);
182 
183  value_type f = 1.;
184  value_type dot = num.normalized().dot(den.normalized());
185  if (std::abs(dot) - 1 < 1e-9 && dot < 0) {
186  f = -1.;
187  }
188 
189  const vec3 p = p1 + f * (num.norm() / den.norm()) * d1;
190  assert(std::abs(p.z()) < 1e-9);
191  return {p.x(), p.y()};
192  };
193 
194  const VertexType far_dir = {m_normals[0].y(), -m_normals[0].x()};
195  const VertexType far_point = m_normals[0] * far_distance;
196 
197  std::array<VertexType, 2> points;
198 
199  for (size_t i = 1; i < n_normals; i++) {
200  VertexType plane_dir(m_normals[i].y(), -m_normals[i].x());
201 
202  const VertexType ix = ixLineLine(far_point, far_dir, {0, 0}, plane_dir);
203  draw_point(m_origin + ix, "black", 3);
204  draw_line(m_origin, m_origin + ix, "black", 2);
205  points.at(i - 1) = ix;
206  }
207 
208  draw_line(m_origin + points.at(0), m_origin + points.at(1), "black", 2);
209 
210  draw_line(m_origin, m_origin + m_normals[0] * 2, colors[0], 3);
211 
212  draw_point({0, 0}, "yellow", 5);
213  draw_point(m_origin, "green", 5);
214 
215  return os;
216 }
217 
218 template <typename value_t, size_t DIM, size_t SIDES>
221  const transform_type& trf) const {
222  const auto& rot = trf.rotation();
223 
224  std::array<VertexType, n_normals> new_normals;
225  for (size_t i = 0; i < n_normals; i++) {
226  new_normals[i] = rot * m_normals[i];
227  }
228 
229  return Frustum<value_t, DIM, SIDES>(trf * m_origin, std::move(new_normals));
230 }