OpenBEM
Open-source framework for electromagnetic simulation with the boundary element method.
Loading...
Searching...
No Matches
operations.cpp
Go to the documentation of this file.
1// OpenBEM - Copyright (C) 2026 Shashwat Sharma
2
3// This file is part of OpenBEM.
4
5// OpenBEM is free software: you can redistribute it and/or modify it under the terms of the
6// GNU General Public License as published by the Free Software Foundation, either version 3
7// of the License, or (at your option) any later version.
8
9// You should have received a copy of the GNU General Public License along with OpenBEM.
10// If not, see <https://www.gnu.org/licenses/>.
11
12
19
20#include <stdexcept>
21
22#include <external/Eigen/Dense>
23
24#include "types.hpp"
25#include "constants.hpp"
28
29
30namespace bem
31{
32
33template <uint8_t dim>
35{
36 if constexpr (dim != 2 && dim != 3)
37 throw std::invalid_argument("GeometryOps::polar_to_cartesian(): `dim` must be 2 or 3.");
38
40 points_out.row(0) = points.row(0).array() * Eigen::cos(points.row(1).array());
41 points_out.row(1) = points.row(0).array() * Eigen::sin(points.row(1).array());
42 if (dim == 3)
43 {
44 points_out.row(0).array() *= Eigen::sin(points.row(2).array());
45 points_out.row(1).array() *= Eigen::sin(points.row(2).array());
46 points_out.row(2) = points.row(0).array() * Eigen::cos(points.row(2).array());
47 }
48 return points_out;
49}
50
51
52template <uint8_t dim>
56 )
57{
58 if constexpr (dim != 2 && dim != 3)
59 throw std::invalid_argument("GeometryOps::cartesian_to_polar_field(): `dim` must be 2 or 3.");
60
63 for (Index ii = 0; ii < points.cols(); ++ii)
64 {
65 Float phi = std::atan2(points(1, ii), points(0, ii));
66 sin_phi[ii] = std::sin(phi);
67 cos_phi[ii] = std::cos(phi);
68 }
69
71 if constexpr (dim == 3)
72 {
73 EigRowVec<Float> theta = Eigen::acos(points.row(2).array() / points.colwise().norm().array());
74 sin_theta = Eigen::sin(theta.array());
75 cos_theta = Eigen::cos(theta.array());
76 }
77
79 if (dim == 3)
80 {
81 field_out.row(0).array() = field.row(0).array() * cos_phi.array() * sin_theta.array() +
82 field.row(1).array() * sin_phi.array() * sin_theta.array() +
83 field.row(2).array() * cos_theta.array();
84 field_out.row(1).array() = -field.row(0).array() * sin_phi.array() +
85 field.row(1).array() * cos_phi.array();
86 field_out.row(2).array() = field.row(0).array() * cos_phi.array() * cos_theta.array() +
87 field.row(1).array() * sin_phi.array() * cos_theta.array() -
88 field.row(2).array() * sin_theta.array();
89 }
90 else if (dim == 2)
91 {
92 field_out.row(0).array() = field.row(0).array() * cos_phi.array() +
93 field.row(1).array() * sin_phi.array();
94 field_out.row(1).array() = -field.row(0).array() * sin_phi.array() +
95 field.row(1).array() * cos_phi.array();
96 }
97
98 return field_out;
99}
100
101
102template <uint8_t dim>
111
112
113template <uint8_t dim>
117 )
118{
119 if constexpr (dim != 2 && dim != 3)
120 throw std::invalid_argument("GeometryOps::angle_between_vectors(): `dim` must be 2 or 3.");
121
122 if (v1.cols() != v2.cols())
123 throw std::invalid_argument(
124 "angle_between_vectors(): number of vectors in set v1 does not equal the number of vectors in set v2."
125 );
126
127 return Eigen::acos(
128 (v1.cwiseProduct(v2)).colwise().sum().array() /
129 v1.colwise().norm().array() /
130 v2.colwise().norm().array()
131 );
132}
133
134
135template <uint8_t dim>
139 )
140{
141 if constexpr (dim != 2 && dim != 3)
142 throw std::invalid_argument("GeometryOps::directed_angle_between_vectors(): `dim` must be 2 or 3.");
143
144 if (v1.cols() != v2.cols())
145 throw std::invalid_argument(
146 "directed_angle_between_vectors(): number of vectors in set v1 does not equal the number of vectors in set v2."
147 );
148
149 EigRowVec<Float> dot = (v1.cwiseProduct(v2)).colwise().sum();
151 if constexpr (dim == 2)
152 det.noalias() = v1.row(0) * v2.row(1).asDiagonal() - v2.row(0) * v1.row(1).asDiagonal();
153 else if constexpr (dim == 3)
154 for (uint32_t ii = 0; ii < v1.cols(); ii++)
155 det[ii] = (v1.col(ii).cross(v1.col(ii))).norm();
156
158 for (uint32_t rr = 0; rr < v1.cols(); ++rr)
159 angles[rr] = std::atan2(det[rr], dot[rr]);
160
161 Float tol = std::max(
162 v1.colwise().norm().maxCoeff(), v2.colwise().norm().maxCoeff()
163 ) * GEOMETRY_DEFAULT_TOL;
164 angles = (Eigen::abs(dot.array()) == 0.0 || Eigen::abs(dot.array()) < tol).select(
166 );
167
168 return angles;
169}
170
171
172template <uint8_t dim>
176 const Float tol
177 )
178{
179
180 // The tolerance is measured relative to the smaller mean edge length of both triangles
181 Float toln = tol * std::min(triangle1.mean_edge_length(), triangle2.mean_edge_length());
182
184
185 for (uint8_t ii = 0; ii < 3; ii++)
186 for (uint8_t jj = 0; jj < 3; jj++)
187 if ((triangle2.v(jj) - triangle1.v(ii)).norm() < toln)
188 {
190 break;
191 }
192
193 assert(num_common_vertices <= 3 && "Triangle::common_vertices(): Result out of range.");
194
195 return num_common_vertices;
196
197}
198
199
200template <uint8_t dim>
206 const Float tol
207 )
208{
209
210 // The tolerance is measured relative to the smaller mean edge length of both triangles
211 Float toln = tol * std::min(triangle1.mean_edge_length(), triangle2.mean_edge_length());
212
214 idx1.setConstant(10);
215 idx2.setConstant(10);
216
217 for (uint8_t ii = 0; ii < 3; ii++)
218 for (uint8_t jj = 0; jj < 3; jj++)
219 if ((triangle2.v(jj) - triangle1.v(ii)).norm() < toln)
220 {
224 break;
225 }
226
227 assert(num_common_vertices <= 3 && "Triangle::common_vertices(): Result out of range.");
228
229 return num_common_vertices;
230
231}
232
233
234template <uint8_t dim>
236 const Triangle<3>& triangle1,
237 const Triangle<3>& triangle2,
238 const Float tol
239 )
240{
241 int8_t parallel = 0;
242 if (std::abs(triangle1.normal().dot(triangle2.normal()) - 1.0) <= tol)
243 parallel = 1;
244 if (std::abs(triangle1.normal().dot(triangle2.normal()) + 1.0) <= tol)
245 parallel = -1;
246 return parallel;
247}
248
249
250template <uint8_t dim>
252 const Triangle<3>& triangle1,
253 const Triangle<3>& triangle2,
254 const Float tol
255 )
256{
257 bool perpendicular = false;
258 if (std::abs(triangle1.normal().dot(triangle2.normal())) <= tol)
259 perpendicular = true;
260 return perpendicular;
261}
262
263
264template <uint8_t dim>
266 const Triangle<3>& triangle1,
267 const Triangle<3>& triangle2,
268 const Float tol
269 )
270{
271 bool coplanar = false;
272 if (check_parallel_triangles(triangle1, triangle2, tol) != 0)
273 {
274 const EigColVecN<Float, 3> dist_vec = (triangle2.centroid() - triangle1.centroid()).normalized();
275 if (std::abs(triangle1.normal().dot(dist_vec)) <= tol)
276 coplanar = true;
277 }
278 return coplanar;
279}
280
281
282template <uint8_t dim>
284 const Edge<dim>& edge1,
285 const Edge<dim>& edge2,
286 const Float tol
287 )
288{
289 if (std::abs(edge1.unit_vec().dot(edge2.unit_vec()) - 1.0) <= tol)
290 return 1;
291 if (std::abs(edge1.unit_vec().dot(edge2.unit_vec()) + 1.0) <= tol)
292 return -1;
293 return 0;
294}
295
296
297template <uint8_t dim>
299 const Edge<dim>& edge1,
300 const Edge<dim>& edge2,
301 const Float tol
302 )
303{
304 if (edge1.unit_vec().dot(edge2.unit_vec()) <= tol)
305 return true;
306 return false;
307}
308
309
310template <uint8_t dim>
314 const Float tol
315 )
316{
317
318 Float mean_edge_length = (polygon.rightCols(polygon.cols() - 1) + polygon.leftCols(polygon.cols() - 1)).colwise().norm().mean();
319 Float abs_tol = tol * mean_edge_length;
320
321 if (polygon.cols() < 3)
322 throw std::invalid_argument("GeometryOps::point_in_polygon(): `polygon` must have at least three vertices.");
323
326
327 if constexpr (dim == 1)
328 throw std::invalid_argument("GeometryOps::point_in_polygon(): `dim` must be 2 or 3.");
329
330 else if constexpr (dim == 3)
331 {
332 EigColVecN<Float, 3> normal = (polygon.col(1) - polygon.col(0)).cross(
333 polygon.col(2) - polygon.col(0)).normalized();
334 Float dist_to_plane = std::abs((point - polygon.col(0)).dot(normal));
335
337 return false;
338
339 EigColVecN<Float, 3> u = (polygon.col(1) - polygon.col(0)).normalized();
340 EigColVecN<Float, 3> v = normal.cross(u).normalized();
341
342 for (uint8_t ii = 0; ii < polygon.cols(); ++ii)
343 {
344 EigColVecN<Float, 3> vec = polygon.col(ii) - polygon.col(0);
345 polygon_2d(0, ii) = vec.dot(u);
346 polygon_2d(1, ii) = vec.dot(v);
347 }
348
350 point_2d[0] = vec.dot(u);
351 point_2d[1] = vec.dot(v);
352 }
353
354 else if constexpr (dim == 2)
355 {
357 point_2d = point;
358 }
359
360 uint8_t crossings = 0;
361 for (uint8_t ii = 0; ii < polygon_2d.cols(); ++ii)
362 {
364 const EigColVecN<Float, 2> v2 = polygon_2d.col((ii + 1) % polygon_2d.cols());
365
366 Edge<2> edge (v1, v2);
367 if (edge.point_on_edge(point_2d))
368 return true;
369
370 if (point_2d[1] > std::min(v1[1], v2[1]) - abs_tol)
371 {
372 if (point_2d[1] < std::max(v1[1], v2[1]) + abs_tol)
373 {
374 if (point_2d[0] < std::max(v1[0], v2[0]) + abs_tol)
375 {
376 if (std::abs(v1[1] - v2[1]) >= abs_tol)
377 {
378 Float xinters = (point_2d[1] - v1[1]) * (v2[0] - v1[0]) / (v2[1] - v1[1]) + v1[0];
379 if (std::abs(v1[0] - v2[0]) < abs_tol || point_2d[0] < xinters + abs_tol)
380 crossings++;
381 }
382 }
383 }
384 }
385 }
386
387 return (crossings % 2) == 1;
388
389}
390
391
392template class GeometryOps<1>;
393template class GeometryOps<2>;
394template class GeometryOps<3>;
395
396}
static EigMatNX< Complex, dim > cartesian_to_polar_field(ConstEigRef< EigMatNX< Float, dim > > points, ConstEigRef< EigMatNX< Complex, dim > > field)
Transforms a vector field in Cartesian space to polar (2D) or spherical (3D) space.
static EigRowVec< Float > directed_angle_between_vectors(ConstEigRef< EigMatNX< Float, dim > > v1, ConstEigRef< EigMatNX< Float, dim > > v2)
Returns the directional angles between pairs of vectors in a right-hand system where counter-clockwis...
static bool point_in_polygon(ConstEigRef< EigColVecN< Float, dim > > &point, ConstEigRef< EigMatNX< Float, dim > > &polygon, const Float tol=GEOMETRY_DEFAULT_TOL)
Checks if a point is inside a polygon using ray casting.
static EigRowVec< Float > angle_between_vectors(ConstEigRef< EigMatNX< Float, dim > > v1, ConstEigRef< EigMatNX< Float, dim > > v2)
Returns the angles between pairs of vectors in radians.
static bool check_coplanar_triangles(const Triangle< 3 > &triangle1, const Triangle< 3 > &triangle2, const Float tol=GEOMETRY_DEFAULT_TOL)
Checks if two triangles are coplanar.
static uint8_t common_vertices(const Triangle< dim > &triangle1, const Triangle< dim > &triangle2, const Float tol=GEOMETRY_DEFAULT_TOL)
Returns the number of vertices common or coinciding between two Triangle objects.
static int8_t check_parallel_triangles(const Triangle< 3 > &triangle1, const Triangle< 3 > &triangle2, const Float tol=GEOMETRY_DEFAULT_TOL)
Checks if the normal vectors of two triangles are (anti-)parallel.
static EigMatNX< Float, dim > transform_coordinate_system(ConstEigRef< EigMatNX< Float, dim > > v_in, ConstEigRef< EigColVecN< Float, dim > > new_origin, ConstEigRef< EigMatMN< Float, dim, dim > > new_uvw)
Transforms the given coordinates to a new coordinate system defined by a new origin and orthogonal un...
static EigMatNX< Float, dim > polar_to_cartesian(ConstEigRef< EigMatNX< Float, dim > > points)
Transforms polar (2D) or spherical (3D) coordinates to Cartesian.
static bool check_perpendicular_triangles(const Triangle< 3 > &triangle1, const Triangle< 3 > &triangle2, const Float tol=GEOMETRY_DEFAULT_TOL)
Checks if the normal vectors of two triangles are perpendicular.
static bool check_perpendicular_edges(const Edge< dim > &edge1, const Edge< dim > &edge2, const Float tol=GEOMETRY_DEFAULT_TOL)
Checks if two edges are perpendicular.
static int8_t check_parallel_edges(const Edge< dim > &edge1, const Edge< dim > &edge2, const Float tol=GEOMETRY_DEFAULT_TOL)
Checks if two edges are (anti-)parallel.
Eigen::Ref< EigObj > EigRef
Writable reference to an Eigen object.
Definition types.hpp:102
const Eigen::Ref< const EigObj > ConstEigRef
Read-only reference to an Eigen object.
Definition types.hpp:98
Eigen::Matrix< T, 1, Eigen::Dynamic > EigRowVec
Dynamic-size row vector containing type T.
Definition types.hpp:90
double Float
Floating point number.
Definition types.hpp:47
Eigen::Matrix< T, N, 1 > EigColVecN
Fixed-size column vector of size N containing type T.
Definition types.hpp:86
Eigen::Matrix< T, N, Eigen::Dynamic > EigMatNX
Fixed-height matrix with N rows containing type T.
Definition types.hpp:78
std::size_t Index
Unsigned integer type for indices and container sizes.
Definition types.hpp:54
Primary namespace for the OpenBEM library.
Definition constants.hpp:31