1 /**************************************************************************\
2 * Copyright (c) Kongsberg Oil & Gas Technologies AS
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
11 *
12 * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * Neither the name of the copyright holder nor the names of its
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 \**************************************************************************/
32
33 /*!
34 \class SbPlane SbPlane.h Inventor/SbLinear.h
35 \brief The SbPlane class represents a plane in 3D space.
36
37 \ingroup base
38
39 SbPlane is used by many other classes in Coin. It provides a way of
40 representing a plane, specified by a plane normal vector and a
41 distance from the origin of the coordinate system.
42 */
43
44 #include <cassert>
45 #include <cstdio>
46 #include <Inventor/SbPlane.h>
47 #include <Inventor/SbLine.h>
48 #include <Inventor/SbVec3d.h>
49 #include <Inventor/SbMatrix.h>
50 #include <cfloat>
51
52 #if COIN_DEBUG
53 #include <Inventor/errors/SoDebugError.h>
54 #endif // COIN_DEBUG
55
56
57 /*!
58 An SbPlane instantiated with the default constructor will be
59 uninitialized.
60 */
SbPlane(void)61 SbPlane::SbPlane(void)
62 {
63 }
64
65 /*!
66 Construct an SbPlane instance with a normal pointing in the given
67 direction and the given shortest distance from the origin of the
68 coordinate system to a point in the plane.
69
70 \a normal must not be a null vector.
71 */
SbPlane(const SbVec3f & normalref,const float D)72 SbPlane::SbPlane(const SbVec3f& normalref, const float D)
73 {
74 #if COIN_DEBUG
75 if (normalref.sqrLength() == 0.0f) {
76 SoDebugError::postWarning("SbPlane::SbPlane",
77 "Plane normal vector is a null vector.");
78 }
79 #endif // COIN_DEBUG
80
81 this->normal = normalref;
82 // we test for a null vector above, just normalize
83 (void) this->normal.normalize();
84 this->distance = D;
85 }
86
87 /*!
88 Construct an SbPlane with three points laying in the plane. Make
89 sure \a p0, \a p1 and \a p2 are actually three distinct points,
90 not on a line, when using this constructor.
91 */
SbPlane(const SbVec3f & p0,const SbVec3f & p1,const SbVec3f & p2)92 SbPlane::SbPlane(const SbVec3f& p0, const SbVec3f& p1, const SbVec3f& p2)
93 {
94 #if COIN_DEBUG
95 if(!(p0 != p1 && p1 != p2 && p0 != p2))
96 SoDebugError::postWarning("SbPlane::SbPlane",
97 "The three points defining the plane cannot "
98 "be coincident.");
99 #endif // COIN_DEBUG
100
101 this->normal = (p1 - p0).cross(p2 - p0);
102 #if COIN_DEBUG
103 if (this->normal.sqrLength() == 0.0f) {
104 SoDebugError::postWarning("SbPlane::SbPlane",
105 "The three points defining the plane cannot "
106 "be on line.");
107 }
108 #endif // COIN_DEBUG
109
110 // we test and warn about a null vector above
111 (void) this->normal.normalize();
112 // N�point
113 // d = -------, |N| == 1
114 // |N|�
115
116 this->distance = this->normal.dot(p0);
117 }
118
119 /*!
120 Construct an SbPlane from a normal and a point laying in the plane.
121
122 \a normal must not be a null vector.
123 */
SbPlane(const SbVec3f & normalref,const SbVec3f & point)124 SbPlane::SbPlane(const SbVec3f& normalref, const SbVec3f& point)
125 {
126 #if COIN_DEBUG
127 if(normalref.sqrLength() == 0.0f)
128 SoDebugError::postWarning("SbPlane::SbPlane",
129 "Plane normal vector is a null vector.");
130 #endif // COIN_DEBUG
131
132 this->normal = normalref;
133
134 // we test and warn about a null vector above
135 (void) this->normal.normalize();
136
137 // N�point
138 // d = -------, |N| == 1
139 // |N|�
140
141 this->distance = this->normal.dot(point);
142 }
143
144
145 /*!
146 Add the given offset \a d to the plane distance from the origin.
147 */
148 void
offset(const float d)149 SbPlane::offset(const float d)
150 {
151 this->distance += d;
152 }
153
154 /*!
155 Find the point on given line \a l intersecting the plane and return
156 it in \a intersection. If the line is parallel to the plane,
157 we return \c FALSE, otherwise \c TRUE.
158
159 Do not pass an invalid line for the \a l parameter (i.e. with a
160 null direction vector).
161 */
162 SbBool
intersect(const SbLine & l,SbVec3f & intersection) const163 SbPlane::intersect(const SbLine& l, SbVec3f& intersection) const
164 {
165 #if COIN_DEBUG
166 if (this->normal.sqrLength() == 0.0f) {
167 SoDebugError::postWarning("SbPlane::intersect",
168 "Normal vector for plane is null vector");
169
170 }
171 if (l.getDirection().sqrLength() == 0.0f) {
172 SoDebugError::postWarning("SbPlane::intersect",
173 "Intersecting line doesn't have a direction.");
174 }
175 #endif // COIN_DEBUG
176
177 // Check if the line is parallel to the plane.
178 if(fabs(l.getDirection().dot(this->normal)) < FLT_EPSILON) return FALSE;
179
180 // From the discussion on SbLine::getClosestPoint() we know that
181 // any point on the line can be expressed as:
182 // Q = P + t*D (1)
183 //
184 // We can also easily see that a point must satisfy this equation to lie
185 // in the plane:
186 // N�(Q - d*N) = 0, where N is the normal vector,
187 // Q is the point and d the offset
188 // from the origin.
189 //
190 // Combining these two equations and simplifying we get:
191 //
192 // d*|N|� - N�P
193 // t = ----------------, |N| == 1
194 // N�D
195 //
196 // Substituting t back in (1), we've solved the problem.
197 // 19980816 mortene.
198
199 float t =
200 (this->distance - this->normal.dot(l.getPosition()))
201 / this->normal.dot(l.getDirection());
202
203 intersection = l.getPosition() + t * l.getDirection();
204
205 return TRUE;
206 }
207
208 /*!
209 Transform the plane by \a matrix.
210
211 \sa offset()
212 */
213 void
transform(const SbMatrix & matrix)214 SbPlane::transform(const SbMatrix& matrix)
215 {
216 SbVec3f ptInPlane = this->normal * this->distance;
217
218 // according to discussions on comp.graphics.algorithms, the inverse
219 // transpose matrix should be used to rotate the plane normal.
220 SbMatrix invtransp = matrix.inverse().transpose();
221 invtransp.multDirMatrix(this->normal, this->normal);
222
223 // the point should be transformed using the original matrix
224 matrix.multVecMatrix(ptInPlane, ptInPlane);
225
226 if (this->normal.normalize() == 0.0f) {
227 #if COIN_DEBUG
228 SoDebugError::postWarning("SbPlane::transform",
229 "The transformation invalidated the plane.");
230 #endif // COIN_DEBUG
231 }
232 this->distance = this->normal.dot(ptInPlane);
233 }
234
235 /*!
236 Check if the given point lies in the halfspace of the plane which the
237 plane normal vector is pointing.
238 */
239 SbBool
isInHalfSpace(const SbVec3f & point) const240 SbPlane::isInHalfSpace(const SbVec3f& point) const
241 {
242 return this->getDistance(point) >= 0.0f;
243 }
244
245 /*!
246 Return the distance from \a point to plane. Positive distance means
247 the point is in the plane's half space.
248
249 This method is an extension specific to Coin versus the original SGI
250 Inventor API.
251 */
252 float
getDistance(const SbVec3f & point) const253 SbPlane::getDistance(const SbVec3f &point) const
254 {
255 // convert to double before doing the dot product to increase precision of the result
256 SbVec3d dp(point);
257 SbVec3d dn(this->normal);
258 return static_cast<float> (dp.dot(dn)) - this->distance;
259 }
260
261 /*!
262 Return the plane's normal vector, which indicates which direction the plane
263 is oriented.
264
265 \sa getDistanceFromOrigin().
266 */
267 const SbVec3f&
getNormal(void) const268 SbPlane::getNormal(void) const
269 {
270 return this->normal;
271 }
272
273 /*!
274 Return distance from origin of coordinate system to the point in the plane
275 which is closest to the origin.
276
277 \sa getNormal().
278 */
279 float
getDistanceFromOrigin(void) const280 SbPlane::getDistanceFromOrigin(void) const
281 {
282 return this->distance;
283 }
284
285 /*!
286 Intersect this plane with \a pl, and return the resulting line in \a
287 line. Returns \c TRUE if an intersection line can be found, and \c
288 FALSE if the planes are parallel.
289
290 Please note that the resulting SbLine must be considered as a
291 \e line intersecting the SbLine's origin, extending infinitely in both
292 directions.
293
294 \COIN_FUNCTION_EXTENSION
295
296 \since Coin 2.0
297 */
298 SbBool
intersect(const SbPlane & pl,SbLine & line) const299 SbPlane::intersect(const SbPlane & pl, SbLine & line) const
300 {
301 // Based on code from Graphics Gems III, Plane-to-Plane Intersection
302 // by Priamos Georgiades
303
304 float invdet; // inverse of 2x2 matrix determinant
305 SbVec3f dir2; // holds the squares of the coordinates of xdir
306
307 SbVec3f xpt;
308 SbVec3f xdir;
309 xdir = this->normal.cross(pl.normal);
310
311 dir2[0] = xdir[0] * xdir[0];
312 dir2[1] = xdir[1] * xdir[1];
313 dir2[2] = xdir[2] * xdir[2];
314
315 const SbVec3f & pl1n = this->normal;
316 const SbVec3f & pl2n = pl.normal;
317 const float pl1w = -this->distance;
318 const float pl2w = -pl.distance;
319
320 if ((dir2[2] > dir2[1]) && (dir2[2] > dir2[0]) && (dir2[2] > FLT_EPSILON)) {
321 // then get a point on the XY plane
322 invdet = 1.0f / xdir[2];
323 xpt = SbVec3f(pl1n[1] * pl2w - pl2n[1] * pl1w,
324 pl2n[0] * pl1w - pl1n[0] * pl2w, 0.0f);
325 }
326 else if ((dir2[1] > dir2[0]) && (dir2[1] > FLT_EPSILON)) {
327 // then get a point on the XZ plane
328 invdet = - 1.0f / xdir[1];
329 xpt = SbVec3f(pl1n[2] * pl2w - pl2n[2] * pl1w, 0.0f,
330 pl2n[0] * pl1w - pl1n[0] * pl2w);
331 }
332 else if (dir2[0] > FLT_EPSILON) {
333 // then get a point on the YZ plane
334 invdet = 1.0f / xdir[0];
335 xpt = SbVec3f(0.0f, pl1n[2] * pl2w - pl2n[2] * pl1w,
336 pl2n[1] * pl1w - pl1n[1] * pl2w);
337 }
338 else // xdir is zero, then no point of intersection exists
339 return FALSE;
340
341 xpt *= invdet;
342 invdet = 1.0f / static_cast<float>(sqrt(dir2[0] + dir2[1] + dir2[2]));
343
344 xdir *= invdet;
345 line.setPosDir(xpt, xdir);
346 return TRUE;
347 }
348
349 /*!
350 \relates SbPlane
351
352 Check the two given planes for equality.
353 */
354 int
operator ==(const SbPlane & p1,const SbPlane & p2)355 operator ==(const SbPlane& p1, const SbPlane& p2)
356 {
357 if(p1.getDistanceFromOrigin() == p2.getDistanceFromOrigin() &&
358 p1.getNormal() == p2.getNormal()) return TRUE;
359 return FALSE;
360 }
361
362 /*!
363 \relates SbPlane
364
365 Check the two given planes for unequality.
366 */
367 int
operator !=(const SbPlane & p1,const SbPlane & p2)368 operator !=(const SbPlane& p1, const SbPlane& p2)
369 {
370 return !(p1 == p2);
371 }
372
373 /*!
374 Dump the state of this object to the \a file stream. Only works in
375 debug version of library, method does nothing in an optimized build.
376 */
377 void
print(FILE * fp) const378 SbPlane::print(FILE * fp) const
379 {
380 #if COIN_DEBUG
381 this->getNormal().print(fp);
382 (void)fprintf(fp, " %f", this->getDistanceFromOrigin());
383 #endif // COIN_DEBUG
384 }
385
386 #ifdef COIN_TEST_SUITE
387 #include <Inventor/SbPlane.h>
388 #include <Inventor/SbLine.h>
389
390 using namespace SIM::Coin::TestSuite;
391
BOOST_AUTO_TEST_CASE(signCorrect)392 BOOST_AUTO_TEST_CASE(signCorrect)
393 {
394 SbPlane plane1(SbVec3f(0.0, 0.0, 1.0), 3.0);
395 SbPlane plane2(SbVec3f(1.0, 0.0, 0.0), 21.0);
396 SbLine line;
397 plane1.intersect(plane2, line);
398
399 SbVec3f intersect = line.getPosition();
400 SbVec3f vec(21, 0, 3);
401
402 check_compare(intersect,vec, "SbPlane SignCorrect", .1f);
403
404 }
405
406 #endif //COIN_TEST_SUITE
407