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