1 | // Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). |
2 | // Copyright (C) 2015 Klaralvdalens Datakonsult AB (KDAB). |
3 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
4 | |
5 | #include <Qt3DRender/private/qray3d_p.h> |
6 | #include <QtCore/qdebug.h> |
7 | |
8 | QT_BEGIN_NAMESPACE |
9 | |
10 | namespace Qt3DRender { |
11 | namespace RayCasting { |
12 | |
13 | /*! |
14 | \namespace Qt3DRender::RayCasting |
15 | \internal |
16 | */ |
17 | |
18 | /*! |
19 | \internal |
20 | \class Qt3DRender::RayCasting::QRay3D |
21 | \inmodule Qt3DRender |
22 | \brief The QRay3D class defines a directional line in 3D space extending through an origin point. |
23 | \since 5.5 |
24 | \ingroup qt3d |
25 | \ingroup qt3d::math |
26 | |
27 | A ray is defined by the origin() point and the direction() vector. |
28 | Rays are infinite in length, extending out from origin() in |
29 | both directions. If the direction() is zero length, then the |
30 | behavior of the class is undefined. |
31 | |
32 | A ray can be thought of as a one-dimensional co-ordinate system. |
33 | If the co-ordinate is \b t then the origin() point is at |
34 | \b t = 0, the point origin() + direction() is at \b t = 1, |
35 | and the point origin() - direction() is at \b t = -1. |
36 | The point() method can be used to obtain the position of a point |
37 | within this one-dimensional co-ordinate system. The projectedDistance() |
38 | method can be used to convert a point into a value in this |
39 | one-dimensional co-ordinate system. |
40 | */ |
41 | |
42 | /*! |
43 | \fn Qt3DRender::RayCasting::QRay3D::QRay3D() |
44 | |
45 | Construct a default ray with an origin() of (0, 0, 0), a |
46 | direction() of (0, 0, 1) and a distance of 1. |
47 | */ |
48 | QRay3D::QRay3D() |
49 | : m_direction(0.0f, 0.0f, 1.0f) |
50 | , m_distance(1.0f) |
51 | { |
52 | } |
53 | |
54 | /*! |
55 | \fn Qt3DRender::RayCasting::QRay3D::QRay3D(const Vector3D &origin, const Vector3D &direction, float distance) |
56 | |
57 | Construct a ray given its defining \a origin, \a direction and \a distance. |
58 | The \a direction does not need to be normalized. |
59 | |
60 | To construct a ray that passes through two points, use the following: |
61 | |
62 | \code |
63 | QRay3D thruAB(pointA, pointB - pointA); |
64 | \endcode |
65 | */ |
66 | QRay3D::QRay3D(const Vector3D &origin, const Vector3D &direction, float distance) |
67 | : m_origin(origin) |
68 | , m_direction(direction.normalized()) |
69 | , m_distance(distance) |
70 | {} |
71 | |
72 | QRay3D::~QRay3D() |
73 | { |
74 | } |
75 | |
76 | /*! |
77 | \fn QVector3D Qt3DRender::RayCasting::QRay3D::origin() const |
78 | |
79 | Returns the origin of this ray. The default value is (0, 0, 0). |
80 | |
81 | \sa setOrigin(), direction() |
82 | */ |
83 | Vector3D QRay3D::origin() const |
84 | { |
85 | return m_origin; |
86 | } |
87 | |
88 | /*! |
89 | \fn void Qt3DRender::RayCasting::QRay3D::setOrigin(const Vector3D &value) |
90 | |
91 | Sets the origin point of this ray to \a value. |
92 | |
93 | \sa origin(), setDirection() |
94 | */ |
95 | void QRay3D::setOrigin(const Vector3D &value) |
96 | { |
97 | m_origin = value; |
98 | } |
99 | |
100 | /*! |
101 | \fn QVector3D Qt3DRender::RayCasting::QRay3D::direction() const |
102 | |
103 | Returns the direction vector of this ray. The default value is (0, 0, 1). |
104 | |
105 | \sa setDirection(), origin() |
106 | */ |
107 | Vector3D QRay3D::direction() const |
108 | { |
109 | return m_direction; |
110 | } |
111 | |
112 | /*! |
113 | \fn void Qt3DRender::RayCasting::QRay3D::setDirection(const Vector3D &direction) |
114 | |
115 | Sets the direction vector of this ray to \a direction. |
116 | |
117 | \sa direction(), setOrigin() |
118 | */ |
119 | void QRay3D::setDirection(const Vector3D &value) |
120 | { |
121 | if (value.isNull()) |
122 | return; |
123 | |
124 | m_direction = value.normalized(); |
125 | } |
126 | |
127 | float QRay3D::distance() const |
128 | { |
129 | return m_distance; |
130 | } |
131 | |
132 | void QRay3D::setDistance(float distance) |
133 | { |
134 | m_distance = distance; |
135 | } |
136 | |
137 | Vector3D QRay3D::point(float t) const |
138 | { |
139 | return m_origin + t * m_direction; |
140 | } |
141 | |
142 | QRay3D &QRay3D::transform(const Matrix4x4 &matrix) |
143 | { |
144 | Vector3D pointAtDistance = point(t: distance()); |
145 | m_origin = matrix.map(point: m_origin); |
146 | pointAtDistance = matrix.map(point: pointAtDistance); |
147 | m_direction = (pointAtDistance - m_origin).normalized(); |
148 | m_distance = (pointAtDistance - m_origin).length(); |
149 | |
150 | return *this; |
151 | } |
152 | |
153 | QRay3D QRay3D::transformed(const Matrix4x4 &matrix) const |
154 | { |
155 | QRay3D cpy(m_origin, m_direction, m_distance); |
156 | return cpy.transform(matrix); |
157 | } |
158 | |
159 | bool QRay3D::operator==(const QRay3D &other) const |
160 | { |
161 | return m_origin == other.origin() && m_direction == other.direction(); |
162 | } |
163 | |
164 | bool QRay3D::operator!=(const QRay3D &other) const |
165 | { |
166 | return !(*this == other); |
167 | } |
168 | |
169 | /*! |
170 | Returns \c true if \a point lies on this ray; \c false otherwise. |
171 | */ |
172 | bool QRay3D::contains(const Vector3D &point) const |
173 | { |
174 | Vector3D ppVec(point - m_origin); |
175 | if (ppVec.isNull()) // point coincides with origin |
176 | return true; |
177 | const float dot = Vector3D ::dotProduct(a: ppVec, b: m_direction); |
178 | if (qFuzzyIsNull(f: dot)) |
179 | return false; |
180 | return qFuzzyCompare(p1: dot*dot, p2: ppVec.lengthSquared() * m_direction.lengthSquared()); |
181 | } |
182 | |
183 | /*! |
184 | Returns \c true if \a ray lies on this ray; \c false otherwise. If true, |
185 | this implies that the two rays are actually the same, but with |
186 | different origin() points or an inverted direction(). |
187 | */ |
188 | bool QRay3D::contains(const QRay3D &ray) const |
189 | { |
190 | const float dot = Vector3D ::dotProduct(a: m_direction, b: ray.direction()); |
191 | if (!qFuzzyCompare(p1: dot*dot, p2: m_direction.lengthSquared() * ray.direction().lengthSquared())) |
192 | return false; |
193 | return contains(point: ray.origin()); |
194 | } |
195 | |
196 | /*! |
197 | \fn QVector3D Qt3DRender::RayCasting::QRay3D::point(float t) const |
198 | |
199 | Returns the point on the ray defined by moving \a t units |
200 | along the ray in the direction of the direction() vector. |
201 | Note that \a t may be negative in which case the point returned |
202 | will lie behind the origin() point with respect to the |
203 | direction() vector. |
204 | |
205 | The units for \a t are defined by direction(). The return value |
206 | is precisely origin() + t * direction(). |
207 | |
208 | \sa projectedDistance(), distance() |
209 | */ |
210 | |
211 | /*! |
212 | Returns the number of direction() units along the ray from origin() |
213 | to \a point. Essentially, this function computes the value t, where |
214 | \a point = origin() + t * direction(). If \a point is not on the ray, |
215 | then the closest point that is on the ray will be used instead. |
216 | |
217 | If the return value is positive, then \a point lies in front of |
218 | the origin() with respect to the direction() vector. If the return |
219 | value is negative, then \a point lies behind the origin() with |
220 | respect to the direction() vector. |
221 | |
222 | \sa point(), project() |
223 | */ |
224 | float QRay3D::projectedDistance(const Vector3D &point) const |
225 | { |
226 | Q_ASSERT(!m_direction.isNull()); |
227 | |
228 | return Vector3D ::dotProduct(a: point - m_origin, b: m_direction) / |
229 | m_direction.lengthSquared(); |
230 | } |
231 | |
232 | /*! |
233 | Returns the projection of \a vector onto this ray. In the |
234 | following diagram, the dotted line is the ray, and V is the |
235 | \a vector. The return value will be the vector V': |
236 | |
237 | \image qray3d-project.png |
238 | |
239 | \sa projectedDistance() |
240 | */ |
241 | Vector3D QRay3D::project(const Vector3D &vector) const |
242 | { |
243 | Vector3D norm = m_direction.normalized(); |
244 | return Vector3D ::dotProduct(a: vector, b: norm) * norm; |
245 | } |
246 | |
247 | /*! |
248 | Returns the minimum distance from this ray to \a point, or equivalently |
249 | the length of a line perpendicular to this ray which passes through |
250 | \a point. If \a point is on the ray, then this function will return zero. |
251 | |
252 | \sa point() |
253 | */ |
254 | float QRay3D::distance(const Vector3D &point) const |
255 | { |
256 | float t = projectedDistance(point); |
257 | return (point - (m_origin + t * m_direction)).length(); |
258 | } |
259 | |
260 | /*! |
261 | \fn Qt3DRender::RayCasting::QRay3D &Qt3DRender::RayCasting::QRay3D::transform(const Matrix4x4 &matrix) |
262 | |
263 | Transforms this ray using \a matrix, replacing origin() and |
264 | direction() with the transformed versions. |
265 | |
266 | \sa transformed() |
267 | */ |
268 | |
269 | /*! |
270 | \fn Qt3DRender::RayCasting::QRay3D Qt3DRender::RayCasting::QRay3D::transformed(const Matrix4x4 &matrix) const |
271 | |
272 | Returns a new ray that is formed by transforming origin() |
273 | and direction() using \a matrix. |
274 | |
275 | \sa transform() |
276 | */ |
277 | |
278 | /*! |
279 | \fn bool Qt3DRender::RayCasting::QRay3D::operator==(const QRay3D &other) const |
280 | |
281 | Returns \c true if this ray is the same as \a other; \c false otherwise. |
282 | |
283 | \sa operator!=() |
284 | */ |
285 | |
286 | /*! |
287 | \fn bool Qt3DRender::RayCasting::QRay3D::operator!=(const QRay3D &other) const |
288 | |
289 | Returns \c true if this ray is not the same as \a other; \c false otherwise. |
290 | |
291 | \sa operator==() |
292 | */ |
293 | |
294 | /*! |
295 | \fn bool qFuzzyCompare(const Qt3DRender::RayCasting::QRay3D &ray1, const Qt3DRender::RayCasting::QRay3D &ray2) |
296 | \relates Qt3DRender::RayCasting::QRay3D |
297 | |
298 | Returns \c true if \a ray1 and \a ray2 are almost equal; \c false |
299 | otherwise. |
300 | */ |
301 | |
302 | #ifndef QT_NO_DEBUG_STREAM |
303 | |
304 | QDebug operator<<(QDebug dbg, const QRay3D &ray) |
305 | { |
306 | QDebugStateSaver saver(dbg); |
307 | dbg.nospace() << "QRay3D(origin(" |
308 | << ray.origin().x() << ", " << ray.origin().y() << ", " |
309 | << ray.origin().z() << ") - direction(" |
310 | << ray.direction().x() << ", " << ray.direction().y() << ", " |
311 | << ray.direction().z() << ") - distance(" << ray.distance() << "))" ; |
312 | return dbg; |
313 | } |
314 | |
315 | #endif |
316 | |
317 | #ifndef QT_NO_DATASTREAM |
318 | |
319 | /*! |
320 | \relates Qt3DRender::RayCasting::QRay3D |
321 | |
322 | Writes the given \a ray to the given \a stream and returns a |
323 | reference to the stream. |
324 | */ |
325 | QDataStream &operator<<(QDataStream &stream, const QRay3D &ray) |
326 | { |
327 | stream << convertToQVector3D(v: ray.origin()); |
328 | stream << convertToQVector3D(v: ray.direction()); |
329 | if (stream.version() >= QDataStream::Qt_5_11) |
330 | stream << ray.distance(); |
331 | return stream; |
332 | } |
333 | |
334 | /*! |
335 | \relates Qt3DRender::RayCasting::QRay3D |
336 | |
337 | Reads a 3D ray from the given \a stream into the given \a ray |
338 | and returns a reference to the stream. |
339 | */ |
340 | QDataStream &operator>>(QDataStream &stream, QRay3D &ray) |
341 | { |
342 | QVector3D origin, direction; |
343 | float distance = 1.f; |
344 | |
345 | stream >> origin; |
346 | stream >> direction; |
347 | if (stream.version() >= QDataStream::Qt_5_11) |
348 | stream >> distance; |
349 | ray = QRay3D(Vector3D(origin), Vector3D(direction), distance); |
350 | return stream; |
351 | } |
352 | |
353 | #endif // QT_NO_DATASTREAM |
354 | |
355 | } // namespace RayCasting |
356 | } // namespace Qt3DRender |
357 | |
358 | QT_END_NAMESPACE |
359 | |