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 SbCylinderSectionProjector SbCylinderSectionProjector.h Inventor/projectors/SbCylinderSectionProjector.h
35   \brief The SbCylinderSectionProjector projects 2D points to a sliced cylinder.
36 
37   \ingroup projectors
38 
39   The projection cylinder for this class is sliced by a clipping plane
40   parallel to its height axis. Projections will be mapped to the
41   remaining cylinder part.
42 
43   \sa SbSphereSectionProjector
44 */
45 
46 #include <Inventor/projectors/SbCylinderSectionProjector.h>
47 #include <cfloat>
48 
49 #if COIN_DEBUG
50 #include <Inventor/errors/SoDebugError.h>
51 #endif // COIN_DEBUG
52 
53 /*! \var SbCylinderSectionProjector::tolerance
54   Tolerance value, deciding how much of the half-cylinder to do
55   projections against.
56 */
57 /*! \var SbCylinderSectionProjector::tolDist
58   Tolerance value multiplied with the cylinder radius.
59 */
60 /*! \var SbCylinderSectionProjector::tolPlane
61   Defines the plane cutting the cylinder into a projection part.
62 */
63 /*! \var SbCylinderSectionProjector::planeDir
64   Direction of cutting plane.
65 */
66 /*! \var SbCylinderSectionProjector::planeLine
67   A line within the plane which is parallel to the cylinder axis.
68 */
69 /*! \var SbCylinderSectionProjector::planeDist
70   Distance from plane to cylinder axis.
71 */
72 
73 
74 /*!
75   Default constructor. See SbCylinderProjector::SbCylinderProjector().
76 
77   The \a edgetol value should be within <0, 1], and specifies how much
78   of the cylinder is used as a projection surface. 1.0 means the full
79   front half is used.
80 */
SbCylinderSectionProjector(const float edgetol,const SbBool orienttoeye)81 SbCylinderSectionProjector::SbCylinderSectionProjector(const float edgetol,
82                                                        const SbBool orienttoeye)
83   : SbCylinderProjector(orienttoeye),
84     tolerance(edgetol)
85 {
86   this->needSetup = TRUE;
87 }
88 
89 /*!
90   Constructor with explicit setting of the projection cylinder.
91 */
SbCylinderSectionProjector(const SbCylinder & cyl,const float edgetol,const SbBool orienttoeye)92 SbCylinderSectionProjector::SbCylinderSectionProjector(const SbCylinder & cyl,
93                                                        const float edgetol,
94                                                        const SbBool orienttoeye)
95   : inherited(cyl, orienttoeye),
96     tolerance(edgetol)
97 {
98   this->needSetup = TRUE;
99 }
100 
101 // Documented in superclass.
102 SbProjector *
copy(void) const103 SbCylinderSectionProjector::copy(void) const
104 {
105   return new SbCylinderSectionProjector(*this);
106 }
107 
108 // Documented in superclass.
109 SbVec3f
project(const SbVec2f & point)110 SbCylinderSectionProjector::project(const SbVec2f & point)
111 {
112   if (this->needSetup) this->setupTolerance();
113 
114   SbLine projline = this->getWorkingLine(point);
115   SbVec3f projpt;
116 
117   SbBool tst = this->intersectCylinderFront(projline, projpt);
118   if (!tst || !this->isWithinTolerance(projpt)) {
119     if (!this->tolPlane.intersect(projline, projpt)) {
120 #if COIN_DEBUG
121       SoDebugError::postWarning("SbCylinderSectionProjector::project",
122                                 "working line is parallel to cylinder axis.");
123 #endif // COIN_DEBUG
124       // set to 0, 0, 0 to avoid crazy rotations. lastPoint will then
125       // never change, and there will be no rotation in getRotation()
126       projpt = SbVec3f(0.0f, 0.0f, 0.0f);
127     }
128     else {
129       SbVec3f ptOnLine = this->planeLine.getClosestPoint(projpt);
130       SbLine myLine(projpt, ptOnLine);
131       if (!this->cylinder.intersect(myLine, projpt)) {
132         // shouldn't happen, but be robust if it does
133         projpt = SbVec3f(0.0f, 0.0f, 0.0f);
134       }
135     }
136   }
137 
138   this->lastPoint = projpt;
139   return projpt;
140 }
141 
142 // Documented in superclass.
143 SbRotation
getRotation(const SbVec3f & point1,const SbVec3f & point2)144 SbCylinderSectionProjector::getRotation(const SbVec3f & point1,
145                                         const SbVec3f & point2)
146 {
147   const SbLine & axis = this->cylinder.getAxis();
148   SbVec3f v1 = point1 - axis.getClosestPoint(point1);
149   SbVec3f v2 = point2 - axis.getClosestPoint(point2);
150 
151   SbRotation rot(v1, v2); // rotate vector v1 into vector v2
152 
153   // this is to make sure rotation only happens around cylinder axis
154   SbVec3f dummy;
155   float angle;
156   rot.getValue(dummy, angle);
157 
158   if (dummy.dot(axis.getDirection()) > 0.0f)
159     return SbRotation(axis.getDirection(), angle);
160   else
161     return SbRotation(axis.getDirection(), -angle);
162 }
163 
164 /*!
165   The \a edgetol value decides how much of the surface of the cylinder
166   is used for projection. 1.0 means the full cylinder half is used.
167 */
168 void
setTolerance(const float edgetol)169 SbCylinderSectionProjector::setTolerance(const float edgetol)
170 {
171 #if COIN_DEBUG // COIN_DEBUG
172   if (edgetol <= 0.0f || edgetol > 1.0f) {
173     SoDebugError::postWarning("SbCylinderSectionProjector::setTolerance",
174                               "edge tolerance should be within <0, 1].");
175   }
176 #endif // COIN_DEBUG
177   this->tolerance = edgetol;
178   this->needSetup = TRUE;
179 }
180 
181 /*!
182   Returns edge tolerance for the cylinder half.
183 */
184 float
getTolerance(void) const185 SbCylinderSectionProjector::getTolerance(void) const
186 {
187   return this->tolerance;
188 }
189 
190 /*!
191   Check if \a point is within the part of the cylinder used for
192   projections.
193 */
194 SbBool
isWithinTolerance(const SbVec3f & point)195 SbCylinderSectionProjector::isWithinTolerance(const SbVec3f & point)
196 {
197   if (this->needSetup) this->setupTolerance();
198 
199   // check if behind tolerance plane
200   if (!this->tolPlane.isInHalfSpace(point)) return FALSE;
201 
202   SbVec3f ptonline = this->planeLine.getClosestPoint(point);
203   if ((ptonline-point).sqrLength() > this->sqrtoldist) return FALSE;
204   return TRUE;
205 }
206 
207 /*!
208   Recalculate the internal projection surface settings. Needs to be
209   done if any of the parameters influencing the projection surface
210   have been changed from subclasses without using the access methods.
211 */
212 void
setupTolerance(void)213 SbCylinderSectionProjector::setupTolerance(void)
214 {
215   SbVec3f refdir;
216   if (this->orientToEye) {
217     refdir = -this->viewVol.getProjectionDirection();
218     this->worldToWorking.multDirMatrix(refdir, refdir);
219   }
220   else {
221     refdir = SbVec3f(0.0f, 0.0f, 1.0f);
222   }
223   float radius = this->cylinder.getRadius();
224   this->tolDist = this->tolerance * radius;
225   this->sqrtoldist = this->tolDist * this->tolDist;
226   const SbLine & axis = this->cylinder.getAxis();
227   SbVec3f somept = axis.getPosition() + refdir;
228   SbVec3f ptonaxis = axis.getClosestPoint(somept);
229 
230   // find plane direction perpendicular to line
231   this->planeDir = somept - ptonaxis;
232   if (this->planeDir.normalize() < FLT_EPSILON) {
233     // the cylinder axis is parallel to the view direction. This is a
234     // special case, and not really supported by the projector. Just
235     // create a tilted plane to make it possible to rotate the
236     // cylinder even for this case.
237     this->planeDir = this->viewVol.getViewUp() +
238       this->viewVol.getProjectionDirection();
239     this->worldToWorking.multDirMatrix(this->planeDir, this->planeDir);
240     (void) this->planeDir.normalize();
241   }
242 
243   if (!this->intersectFront) {
244     this->planeDir = -this->planeDir;
245   }
246   // distance from plane to cylinder axis
247   this->planeDist = (float)sqrt(radius * radius - this->tolDist * this->tolDist);
248 
249   // create line parallel to axis, but in plane
250   SbVec3f linept = axis.getPosition()+this->planeDir * this->planeDist;
251   this->planeLine = SbLine(linept, linept + axis.getDirection());
252   this->tolPlane = SbPlane(this->planeDir, linept);
253 
254   this->needSetup = FALSE;
255 }
256