Mantid
Loading...
Searching...
No Matches
MeshObjectCommon.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2020 ISIS Rutherford Appleton Laboratory UKRI,
4// NScD Oak Ridge National Laboratory, European Spallation Source,
5// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6// SPDX - License - Identifier: GPL - 3.0 +
9#include <limits>
10#include <string>
11
19std::vector<double> getVertices(const std::vector<Kernel::V3D> &vertices) {
20 std::vector<double> points;
21 size_t nPoints = vertices.size();
22 if (nPoints > 0) {
23 points.resize(static_cast<std::size_t>(nPoints) * 3);
24 for (size_t i = 0; i < nPoints; ++i) {
25 const auto &pnt = vertices[i];
26 points[i * 3] = pnt.X();
27 points[i * 3 + 1] = pnt.Y();
28 points[i * 3 + 2] = pnt.Z();
29 }
30 }
31 return points;
32}
48double getTriangleSolidAngle(const Kernel::V3D &a, const Kernel::V3D &b, const Kernel::V3D &c,
49 const Kernel::V3D &observer) {
50 const Kernel::V3D ao = a - observer;
51 const Kernel::V3D bo = b - observer;
52 const Kernel::V3D co = c - observer;
53 const double modao = ao.norm();
54 const double modbo = bo.norm();
55 const double modco = co.norm();
56 const double aobo = ao.scalar_prod(bo);
57 const double aoco = ao.scalar_prod(co);
58 const double boco = bo.scalar_prod(co);
59 const double scalTripProd = ao.scalar_prod(bo.cross_prod(co));
60 const double denom = modao * modbo * modco + modco * aobo + modbo * aoco + modao * boco;
61 if (denom != 0.0)
62 return 2.0 * atan2(scalTripProd, denom);
63 else
64 return 0.0; // not certain this is correct
65}
66
75bool isOnTriangle(const Kernel::V3D &point, const Kernel::V3D &v1, const Kernel::V3D &v2, const Kernel::V3D &v3) {
76
77 // p = w*p0 + u*p1 + v*p2, where numbered p refers to vertices of triangle
78 // w+u+v == 1, so w = 1-u-v
79 // p = (1-u-v)p0 + u*p1 + v*p2, rearranging ...
80 // p = u(p1 - p0) + v(p2 - p0) + p0
81 // in change of basis, barycentric coordinates p = p0 + u*e0 + v*e1. e0 and
82 // e1 are basis vectors. e2 = (p - p0)
83 // rewrite as e2 = u*e0 + v*e1
84 // i) e2.e0 = u*e0.e0 + v*e1.e0
85 // ii) e2.e1 = u*e0.e1 + v*e1.e1
86 // solve for u, v and check u and v >= 0 and u+v <=1
87
88 auto e0 = v3 - v1;
89 auto e1 = v2 - v1;
90 auto e2 = point - v1;
91
92 // Compute dot products
93 auto dot00 = e0.scalar_prod(e0);
94 auto dot01 = e0.scalar_prod(e1);
95 auto dot02 = e0.scalar_prod(e2);
96 auto dot11 = e1.scalar_prod(e1);
97 auto dot12 = e1.scalar_prod(e2);
98
99 /* in matrix form
100 M = e0.e0 e1.e0
101 e0.e1 e1.e1
102 U = u
103 v
104 R = e2.e0
105 e2.e1
106 U = R*(M^-1)
107 */
108
109 // Compute barycentric coordinates
110 auto invDenom = 1 / (dot00 * dot11 - dot01 * dot01);
111 auto u = (dot11 * dot02 - dot01 * dot12) * invDenom;
112 auto v = (dot00 * dot12 - dot01 * dot02) * invDenom;
113
114 // Check if point is in or on triangle
115 return (u >= 0) && (v >= 0) && (u + v <= 1);
116}
117
130bool rayIntersectsTriangle(const Kernel::V3D &start, const Kernel::V3D &direction, const Kernel::V3D &v1,
131 const Kernel::V3D &v2, const Kernel::V3D &v3, Kernel::V3D &intersection,
132 TrackDirection &entryExit) {
133 // Implements Moller Trumbore intersection algorithm
134
135 // Eq line x = x0 + tV
136 //
137 // p = w*p0 + u*p1 + v*p2, where numbered p refers to vertices of triangle
138 // w+u+v == 1, so w = 1-u-v
139 // p = (1-u-v)p0 + u*p1 + v*p2, rearranging ...
140 // p = u(p1 - p0) + v(p2 - p0) + p0
141 // in change of basis, barycentric coordinates p = p0 + u*v0 + v*v1. v0 and
142 // v1 are basis vectors.
143
144 // For line to pass through triangle...
145 // (x0 + tV) = u(p1 - p0) + v(p2 - p0) + p0, yields
146 // (x0 - p0) = -tV + u(p1 - p0) + v(p2 - p0)
147
148 // rest is just to solve for u, v, t and check u and v are both >= 0 and <= 1
149 // and u+v <=1
150
151 auto edge1 = v2 - v1;
152 auto edge2 = v3 - v1;
153 auto h = direction.cross_prod(edge2);
154 auto a = edge1.scalar_prod(h);
155
156 const double EPSILON = 0.0000001 * edge1.norm();
157 if (a > -EPSILON && a < EPSILON)
158 return false; // Ray in or parallel to plane of triangle
159 auto f = 1 / a;
160 auto s = start - v1;
161 // Barycentric coordinate offset u
162 auto u = f * (s.scalar_prod(h));
163 if (u < 0.0 || u > 1.0)
164 return false; // Intersection with plane outside triangle
165 auto q = s.cross_prod(edge1);
166 // Barycentric coordinate offset v
167 auto v = f * direction.scalar_prod(q);
168 if (v < 0.0 || u + v > 1.0)
169 return false; // Intersection with plane outside triangle
170
171 // At this stage we can compute t to find out where the intersection point is
172 // on the line.
173 auto t = f * edge2.scalar_prod(q);
174 if (t >= -EPSILON) // ray intersection
175 {
176 intersection = start + direction * t;
177
178 // determine entry exit assuming anticlockwise triangle view from outside
179 Kernel::V3D normalDirection = edge1.cross_prod(edge2);
180 const auto scalar_prod = normalDirection.scalar_prod(direction);
181 if (scalar_prod > 0.) {
182 entryExit = TrackDirection::LEAVING; // exit
183 } else if (scalar_prod < 0.) {
184 entryExit = TrackDirection::ENTERING; // entry
185 } else {
186 throw std::domain_error("Track is in same direction as surface");
187 }
188 return true;
189 }
190 // The triangle is behind the start point. Forward ray does not intersect.
191 return false;
192}
193
194void checkVertexLimit(size_t nVertices) {
195 if (nVertices >= std::numeric_limits<uint32_t>::max()) {
196 throw std::invalid_argument("Too many vertices (" + std::to_string(nVertices) +
197 "). MeshObject cannot have more than 2^32 vertices.");
198 }
199}
200
207const BoundingBox &getBoundingBox(const std::vector<Kernel::V3D> &vertices, BoundingBox &cacheBB) {
208
209 if (cacheBB.isNull()) {
210 static const double MinThickness = 0.001;
211 double minX, maxX, minY, maxY, minZ, maxZ;
212 minX = minY = minZ = std::numeric_limits<double>::max();
213 maxX = maxY = maxZ = std::numeric_limits<double>::lowest();
214
215 // Loop over all vertices and determine minima and maxima on each axis
216 for (const auto &vertex : vertices) {
217 auto vx = vertex.X();
218 auto vy = vertex.Y();
219 auto vz = vertex.Z();
220
221 minX = std::min(minX, vx);
222 maxX = std::max(maxX, vx);
223 minY = std::min(minY, vy);
224 maxY = std::max(maxY, vy);
225 minZ = std::min(minZ, vz);
226 maxZ = std::max(maxZ, vz);
227 }
228 if (minX == maxX)
229 maxX += MinThickness;
230 if (minY == maxY)
231 maxY += MinThickness;
232 if (minZ == maxZ)
233 maxZ += MinThickness;
234
235 // Cache bounding box, so we do not need to repeat calculation
236 cacheBB = BoundingBox(maxX, maxY, maxZ, minX, minY, minZ);
237 }
238
239 return cacheBB;
240}
241
255void getBoundingBox(const std::vector<Kernel::V3D> &vertices, BoundingBox &cacheBB, double &xmax, double &ymax,
256 double &zmax, double &xmin, double &ymin, double &zmin) {
257 auto bb = getBoundingBox(vertices, cacheBB);
258 xmax = bb.xMax();
259 xmin = bb.xMin();
260 ymax = bb.yMax();
261 ymin = bb.yMin();
262 zmax = bb.zMax();
263 zmin = bb.zMin();
264}
265
266} // namespace Mantid::Geometry::MeshObjectCommon
const double EPSILON(1.0E-10)
A simple structure that defines an axis-aligned cuboid shaped bounding box for a geometrical object.
Definition: BoundingBox.h:34
bool isNull() const
Is this a default constructed box?
Definition: BoundingBox.h:104
Class for 3D vectors.
Definition: V3D.h:34
constexpr double scalar_prod(const V3D &v) const noexcept
Calculates the cross product.
Definition: V3D.h:274
constexpr V3D cross_prod(const V3D &v) const noexcept
Cross product (this * argument)
Definition: V3D.h:278
double norm() const noexcept
Definition: V3D.h:263
MeshObjectCommon : Performs functions common to 3D and 2D closed meshes.
MANTID_GEOMETRY_DLL bool rayIntersectsTriangle(const Kernel::V3D &start, const Kernel::V3D &direction, const Kernel::V3D &v1, const Kernel::V3D &v2, const Kernel::V3D &v3, Kernel::V3D &intersection, TrackDirection &entryExit)
Get intersection points and their in out directions on the given ray.
MANTID_GEOMETRY_DLL void checkVertexLimit(size_t nVertices)
MANTID_GEOMETRY_DLL std::vector< double > getVertices(const std::vector< Kernel::V3D > &vertices)
getVertices converts vector Kernel::V3D to vector doubles.
MANTID_GEOMETRY_DLL double getTriangleSolidAngle(const Kernel::V3D &a, const Kernel::V3D &b, const Kernel::V3D &c, const Kernel::V3D &observer)
Find the solid angle of a triangle defined by vectors a,b,c from point "observer".
MANTID_GEOMETRY_DLL const BoundingBox & getBoundingBox(const std::vector< Kernel::V3D > &vertices, BoundingBox &cacheBB)
Takes input vertices and calculates bounding box.
MANTID_GEOMETRY_DLL bool isOnTriangle(const Kernel::V3D &point, const Kernel::V3D &v1, const Kernel::V3D &v2, const Kernel::V3D &v3)
isOnTriangle
bool MANTID_GEOMETRY_DLL intersection(const ConvexPolygon &P, const ConvexPolygon &Q, ConvexPolygon &out)
Compute the instersection of two convex polygons.
std::string to_string(const wide_integer< Bits, Signed > &n)