1 //
2 // SPDX-License-Identifier: BSD-3-Clause
3 // Copyright Contributors to the OpenEXR Project.
4 //
5
6 //
7 // Algorithms applied to or in conjunction with Imath::Line class
8 //
9
10 #ifndef INCLUDED_IMATHLINEALGO_H
11 #define INCLUDED_IMATHLINEALGO_H
12
13 #include "ImathFun.h"
14 #include "ImathLine.h"
15 #include "ImathNamespace.h"
16 #include "ImathVecAlgo.h"
17
18 IMATH_INTERNAL_NAMESPACE_HEADER_ENTER
19
20 ///
21 /// Compute point1 and point2 such that point1 is on line1, point2
22 /// is on line2 and the distance between point1 and point2 is minimal.
23 ///
24 /// This function returns true if point1 and point2 can be computed,
25 /// or false if line1 and line2 are parallel or nearly parallel.
26 /// This function assumes that line1.dir and line2.dir are normalized.
27 ///
28
29 template <class T>
30 IMATH_CONSTEXPR14 bool
closestPoints(const Line3<T> & line1,const Line3<T> & line2,Vec3<T> & point1,Vec3<T> & point2)31 closestPoints (const Line3<T>& line1, const Line3<T>& line2, Vec3<T>& point1, Vec3<T>& point2) IMATH_NOEXCEPT
32 {
33 Vec3<T> w = line1.pos - line2.pos;
34 T d1w = line1.dir ^ w;
35 T d2w = line2.dir ^ w;
36 T d1d2 = line1.dir ^ line2.dir;
37 T n1 = d1d2 * d2w - d1w;
38 T n2 = d2w - d1d2 * d1w;
39 T d = 1 - d1d2 * d1d2;
40 T absD = abs (d);
41
42 if ((absD > 1) || (abs (n1) < std::numeric_limits<T>::max() * absD && abs (n2) < std::numeric_limits<T>::max() * absD))
43 {
44 point1 = line1 (n1 / d);
45 point2 = line2 (n2 / d);
46 return true;
47 }
48 else
49 {
50 return false;
51 }
52 }
53
54 ///
55 /// Given a line and a triangle (v0, v1, v2), the intersect() function
56 /// finds the intersection of the line and the plane that contains the
57 /// triangle.
58 ///
59 /// If the intersection point cannot be computed, either because the
60 /// line and the triangle's plane are nearly parallel or because the
61 /// triangle's area is very small, intersect() returns false.
62 ///
63 /// If the intersection point is outside the triangle, intersect
64 /// returns false.
65 ///
66 /// If the intersection point, pt, is inside the triangle, intersect()
67 /// computes a front-facing flag and the barycentric coordinates of
68 /// the intersection point, and returns true.
69 ///
70 /// The front-facing flag is true if the dot product of the triangle's
71 /// normal, (v2-v1)%(v1-v0), and the line's direction is negative.
72 ///
73 /// The barycentric coordinates have the following property:
74 ///
75 /// pt = v0 * barycentric.x + v1 * barycentric.y + v2 * barycentric.z
76 ///
77
78 template <class T>
79 IMATH_CONSTEXPR14 bool
intersect(const Line3<T> & line,const Vec3<T> & v0,const Vec3<T> & v1,const Vec3<T> & v2,Vec3<T> & pt,Vec3<T> & barycentric,bool & front)80 intersect (const Line3<T>& line,
81 const Vec3<T>& v0,
82 const Vec3<T>& v1,
83 const Vec3<T>& v2,
84 Vec3<T>& pt,
85 Vec3<T>& barycentric,
86 bool& front) IMATH_NOEXCEPT
87 {
88 Vec3<T> edge0 = v1 - v0;
89 Vec3<T> edge1 = v2 - v1;
90 Vec3<T> normal = edge1 % edge0;
91
92 T l = normal.length();
93
94 if (l != 0)
95 normal /= l;
96 else
97 return false; // zero-area triangle
98
99 //
100 // d is the distance of line.pos from the plane that contains the triangle.
101 // The intersection point is at line.pos + (d/nd) * line.dir.
102 //
103
104 T d = normal ^ (v0 - line.pos);
105 T nd = normal ^ line.dir;
106
107 if (abs (nd) > 1 || abs (d) < std::numeric_limits<T>::max() * abs (nd))
108 pt = line (d / nd);
109 else
110 return false; // line and plane are nearly parallel
111
112 //
113 // Compute the barycentric coordinates of the intersection point.
114 // The intersection is inside the triangle if all three barycentric
115 // coordinates are between zero and one.
116 //
117
118 {
119 Vec3<T> en = edge0.normalized();
120 Vec3<T> a = pt - v0;
121 Vec3<T> b = v2 - v0;
122 Vec3<T> c = (a - en * (en ^ a));
123 Vec3<T> d = (b - en * (en ^ b));
124 T e = c ^ d;
125 T f = d ^ d;
126
127 if (e >= 0 && e <= f)
128 barycentric.z = e / f;
129 else
130 return false; // outside
131 }
132
133 {
134 Vec3<T> en = edge1.normalized();
135 Vec3<T> a = pt - v1;
136 Vec3<T> b = v0 - v1;
137 Vec3<T> c = (a - en * (en ^ a));
138 Vec3<T> d = (b - en * (en ^ b));
139 T e = c ^ d;
140 T f = d ^ d;
141
142 if (e >= 0 && e <= f)
143 barycentric.x = e / f;
144 else
145 return false; // outside
146 }
147
148 barycentric.y = 1 - barycentric.x - barycentric.z;
149
150 if (barycentric.y < 0)
151 return false; // outside
152
153 front = ((line.dir ^ normal) < 0);
154 return true;
155 }
156
157 ///
158 /// Return the vertex that is closest to the given line. The returned
159 /// point is either v0, v1, or v2.
160 ///
161
162 template <class T>
163 IMATH_CONSTEXPR14 Vec3<T>
closestVertex(const Vec3<T> & v0,const Vec3<T> & v1,const Vec3<T> & v2,const Line3<T> & l)164 closestVertex (const Vec3<T>& v0, const Vec3<T>& v1, const Vec3<T>& v2, const Line3<T>& l) IMATH_NOEXCEPT
165 {
166 Vec3<T> nearest = v0;
167 T neardot = (v0 - l.closestPointTo (v0)).length2();
168
169 T tmp = (v1 - l.closestPointTo (v1)).length2();
170
171 if (tmp < neardot)
172 {
173 neardot = tmp;
174 nearest = v1;
175 }
176
177 tmp = (v2 - l.closestPointTo (v2)).length2();
178 if (tmp < neardot)
179 {
180 neardot = tmp;
181 nearest = v2;
182 }
183
184 return nearest;
185 }
186
187 ///
188 /// Rotate the point p around the line l by the given angle.
189 ///
190
191 template <class T>
192 IMATH_CONSTEXPR14 Vec3<T>
rotatePoint(const Vec3<T> p,Line3<T> l,T angle)193 rotatePoint (const Vec3<T> p, Line3<T> l, T angle) IMATH_NOEXCEPT
194 {
195 //
196 // Form a coordinate frame with <x,y,a>. The rotation is the in xy
197 // plane.
198 //
199
200 Vec3<T> q = l.closestPointTo (p);
201 Vec3<T> x = p - q;
202 T radius = x.length();
203
204 x.normalize();
205 Vec3<T> y = (x % l.dir).normalize();
206
207 T cosangle = std::cos (angle);
208 T sinangle = std::sin (angle);
209
210 Vec3<T> r = q + x * radius * cosangle + y * radius * sinangle;
211
212 return r;
213 }
214
215 IMATH_INTERNAL_NAMESPACE_HEADER_EXIT
216
217 #endif // INCLUDED_IMATHLINEALGO_H
218