1 #ifndef SimTK_SimTKCOMMON_COORDINATE_AXIS_H_
2 #define SimTK_SimTKCOMMON_COORDINATE_AXIS_H_
3
4 /* -------------------------------------------------------------------------- *
5 * Simbody(tm): SimTKcommon *
6 * -------------------------------------------------------------------------- *
7 * This is part of the SimTK biosimulation toolkit originating from *
8 * Simbios, the NIH National Center for Physics-Based Simulation of *
9 * Biological Structures at Stanford, funded under the NIH Roadmap for *
10 * Medical Research, grant U54 GM072970. See https://simtk.org/home/simbody. *
11 * *
12 * Portions copyright (c) 2005-12 Stanford University and the Authors. *
13 * Authors: Michael Sherman *
14 * Contributors: *
15 * *
16 * Licensed under the Apache License, Version 2.0 (the "License"); you may *
17 * not use this file except in compliance with the License. You may obtain a *
18 * copy of the License at http://www.apache.org/licenses/LICENSE-2.0. *
19 * *
20 * Unless required by applicable law or agreed to in writing, software *
21 * distributed under the License is distributed on an "AS IS" BASIS, *
22 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
23 * See the License for the specific language governing permissions and *
24 * limitations under the License. *
25 * -------------------------------------------------------------------------- */
26
27 /** @file
28 Defines the CoordinateAxis and CoordinateDirection classes. **/
29
30 #include "SimTKcommon/internal/common.h"
31 #include <cassert>
32
33 namespace SimTK {
34
35 /** This class, along with its sister class CoordinateDirection, provides
36 convenient manipulation of the three coordinate axes via the definition of
37 three constants XAxis, YAxis, and ZAxis each with a unique subtype and implicit
38 conversion to the integers 0, 1, and 2 whenever necessary.\ Methods are
39 provided to allow code to be written once that can be used to work with the
40 axes in any order.
41
42 There are also three CoordinateDirection constants NegXAxis, NegYAxis, and
43 NegZAxis, also with unique types permitting efficient compile time
44 manipulation. These do not correspond to integers, however. Instead, they are
45 objects containing one of the CoordinateAxis objects combined with an integer
46 that is 1 or -1 to indicate the direction along that axis. The unary negation
47 operator is overloaded so that -XAxis is NegXAxis and -NegZAxis is ZAxis.
48 There are implicit conversions to UnitVec3 for any CoordinateAxis or
49 CoordinateDirection object, yielding the equivalent (normalized) unit vector
50 corresponding to any of the six directions, without doing any computation
51 (and in particular, without normalizing).
52 @see CoordinateDirection **/
53 class CoordinateAxis {
54 public:
55 /** Explicit construction of a CoordinateAxis from a calculated integer
56 that must be 0, 1, or 2 representing XAxis, YAxis, or ZAxis. **/
CoordinateAxis(int i)57 explicit CoordinateAxis( int i ) : m_myAxisId(i)
58 { assert(isIndexInRange(i)); }
59
60 /** Implicit conversion of a CoordinateAxis to int 0, 1, or 2. **/
61 operator int() const {return m_myAxisId;}
62
63 /** Return the "next" coordinate axis after this one:
64 - XAxis.getNextAxis() returns YAxis
65 - YAxis.getNextAxis() returns ZAxis
66 - ZAxis.getNextAxis() returns XAxis **/
getNextAxis()67 CoordinateAxis getNextAxis() const
68 { return CoordinateAxis((m_myAxisId+1) % 3); }
69
70 /** Return the "previous" coordinate axis before this one:
71 - XAxis.getPreviousAxis() returns ZAxis
72 - YAxis.getPreviousAxis() returns XAxis
73 - ZAxis.getPreviousAxis() returns YAxis **/
getPreviousAxis()74 CoordinateAxis getPreviousAxis() const
75 { return CoordinateAxis((m_myAxisId+2) % 3); }
76
77 /** Given this coordinate axis and one other, return the missing one:
78 - XAxis.getThirdAxis(YAxis) returns ZAxis (and vice versa)
79 - XAxis.getThirdAxis(ZAxis) returns YAxis (and vice versa)
80 - YAxis.getThirdAxis(ZAxis) returns XAxis (and vice versa)
81 @param[in] axis2 A coordinate axis that must be distinct from the
82 current one; it is a fatal error to provide the same axis.
83 @return The unmentioned third axis. **/
getThirdAxis(const CoordinateAxis & axis2)84 CoordinateAxis getThirdAxis( const CoordinateAxis& axis2 ) const {
85 assert( isDifferentAxis(axis2) );
86 CoordinateAxis nextAxis = getNextAxis();
87 return nextAxis.isDifferentAxis(axis2) ? nextAxis : axis2.getNextAxis();
88 }
89
90 /** Return true if this is the X axis. **/
isXAxis()91 bool isXAxis() const {return m_myAxisId == 0;}
92 /** Return true if this is the Y axis. **/
isYAxis()93 bool isYAxis() const {return m_myAxisId == 1;}
94 /** Return true if this is the Z axis. **/
isZAxis()95 bool isZAxis() const {return m_myAxisId == 2;}
96 /** Return true if the given \a axis2 is the one following this one as
97 would be reported by getNextAxis(). **/
isNextAxis(const CoordinateAxis & axis2)98 bool isNextAxis( const CoordinateAxis& axis2 ) const
99 { return int(getNextAxis()) == int(axis2); }
100 /** Return true if the given \a axis2 is the one preceding this one as
101 would be reported by getPreviousAxis(). **/
isPreviousAxis(const CoordinateAxis & axis2)102 bool isPreviousAxis( const CoordinateAxis& axis2 ) const
103 { return int(getPreviousAxis()) == int(axis2); }
104 /** Return true if the given \a axis2 is the same as this one.\ You
105 can use operator==() to perform the same comparison. **/
isSameAxis(const CoordinateAxis & axis2)106 bool isSameAxis( const CoordinateAxis& axis2 ) const
107 { return m_myAxisId == int(axis2); }
108 /** Return true if both \a axis2 and \a axis3 are the same as this one. **/
areAllSameAxes(const CoordinateAxis & axis2,const CoordinateAxis & axis3)109 bool areAllSameAxes( const CoordinateAxis& axis2,
110 const CoordinateAxis &axis3 ) const
111 { return isSameAxis(axis2) && isSameAxis(axis3); }
112 /** Return true if the given \a axis2 is not the same one as this
113 one.\ You can use operator!=() to perform the same comparison. **/
isDifferentAxis(const CoordinateAxis & axis2)114 bool isDifferentAxis( const CoordinateAxis& axis2 ) const
115 { return m_myAxisId != int(axis2); }
116 /** Return true if neither \a axis2 nor \a axis3 is the same as this
117 axis nor each other; that is, (this,axis2,axis3) together cover all three
118 axes. **/
areAllDifferentAxes(const CoordinateAxis & axis2,const CoordinateAxis & axis3)119 bool areAllDifferentAxes( const CoordinateAxis& axis2,
120 const CoordinateAxis& axis3 ) const
121 { return isDifferentAxis(axis2) && isDifferentAxis(axis3)
122 && axis2.isDifferentAxis(axis3); }
123 /** Return true if the given \a axis2 is the one following this one in a
124 forward cyclical direction, that is, if \a axis2 is the one that would be
125 reported by getNextAxis(). **/
isForwardCyclical(const CoordinateAxis & axis2)126 bool isForwardCyclical( const CoordinateAxis& axis2 ) const
127 { return isNextAxis(axis2); }
128 /** Return true if the given \a axis2 is the one following this one in a
129 reverse cyclical direction, that is, if \a axis2 is the one that would be
130 reported by getPreviousAxis(). **/
isReverseCyclical(const CoordinateAxis & axis2)131 bool isReverseCyclical( const CoordinateAxis& axis2 ) const
132 { return isPreviousAxis(axis2); }
133
134 /** Perform a specialized dot product between this axis and \a axis2;
135 returning one if they are the same axis and zero otherwise, without
136 performing any floating point operations. **/
dotProduct(const CoordinateAxis & axis2)137 int dotProduct( const CoordinateAxis& axis2 ) const
138 { return isSameAxis(axis2) ? 1 : 0; }
139 /** Return the sign that would result from a cross product between this
140 axis and \a axis2: zero if \a axis2 is the same as this axis; one if the
141 result would be in the positive direction along the third axis; -1 if it
142 would be in the negative direction. No floating point computations are
143 performed. @see crossProductAxis() **/
crossProductSign(const CoordinateAxis & axis2)144 int crossProductSign( const CoordinateAxis& axis2 ) const
145 { return isSameAxis(axis2) ? 0 : (isNextAxis(axis2) ? 1 : -1); }
146 /** Return the coordinate axis along which the cross product of this axis
147 and \a axis2 would lie: same as this if \a axis2 is the same as this axis
148 (doesn't matter because the sign would be zero); otherwise, the third
149 axis that is neither this one nor \a axis2. But note that the actual
150 result may be along that axis or in the negative direction along that
151 axis. No floating point computations are performed.
152 @see crossProductSign(). **/
crossProductAxis(const CoordinateAxis & axis2)153 CoordinateAxis crossProductAxis( const CoordinateAxis& axis2 ) const
154 { return isSameAxis(axis2) ? CoordinateAxis(m_myAxisId)
155 : getThirdAxis(axis2); }
156 /** Return the axis and sign along that axis that would result from a
157 cross product between this axis and \a axis2; this combines the functions
158 of both crossProductAxis() and crossProductSign(). Note that if \a axis2 is
159 the same as this axis we'll just return this as the axis but the sign is
160 zero since the magnitude of the result would be zero. No floating point
161 calculations are performed.
162 @see crossProductSign(), crossProductAxis() **/
crossProduct(const CoordinateAxis & axis2,int & sign)163 CoordinateAxis crossProduct( const CoordinateAxis& axis2, int& sign ) const
164 { sign = crossProductSign(axis2); return crossProductAxis(axis2); }
165
166 /** Return a reference to the CoordinateAxis constant XAxis, YAxis, or
167 ZAxis corresponding to the given integer index which must be 0, 1, or 2. **/
168 static const CoordinateAxis& getCoordinateAxis( int i );
169
170 /** Return true if the given integer is suitable as a coordinate axis,
171 meaning it is one of 0, 1, or 2 designating XAxis, YAxis, or ZAxis,
172 respectively. **/
isIndexInRange(int i)173 static bool isIndexInRange( int i ) { return 0<=i && i<=2; }
174
175 // Forward declarations for subsequent helper classes
176 class XCoordinateAxis; class YCoordinateAxis; class ZCoordinateAxis;
177 protected:
178 /** @cond **/ // turn off doxygen here; these aren't for users
179 class XTypeAxis{};
180 class YTypeAxis{};
181 class ZTypeAxis{};
182
CoordinateAxis(const XTypeAxis &)183 CoordinateAxis( const XTypeAxis& ) : m_myAxisId(0) {}
CoordinateAxis(const YTypeAxis &)184 CoordinateAxis( const YTypeAxis& ) : m_myAxisId(1) {}
CoordinateAxis(const ZTypeAxis &)185 CoordinateAxis( const ZTypeAxis& ) : m_myAxisId(2) {}
186 /** @endcond **/
187 private:
188
189 int m_myAxisId;
190 };
191
192
193 // Helper classes that allow compile time recognition of axis directions.
194 class CoordinateAxis::XCoordinateAxis : public CoordinateAxis {
XCoordinateAxis()195 public: XCoordinateAxis() : CoordinateAxis(XTypeAxis()) {}
196 };
197 class CoordinateAxis::YCoordinateAxis : public CoordinateAxis {
YCoordinateAxis()198 public: YCoordinateAxis() : CoordinateAxis(YTypeAxis()) {}
199 };
200 class CoordinateAxis::ZCoordinateAxis : public CoordinateAxis {
ZCoordinateAxis()201 public: ZCoordinateAxis() : CoordinateAxis(ZTypeAxis()) {}
202 };
203
204 /** Constant representing the X coordinate axis; will implicitly convert to
205 the integer 0 when used in a context requiring an integer. **/
206 extern SimTK_SimTKCOMMON_EXPORT const CoordinateAxis::XCoordinateAxis XAxis;
207 /** Constant representing the Y coordinate axis; will implicitly convert to
208 the integer 1 when used in a context requiring an integer. **/
209 extern SimTK_SimTKCOMMON_EXPORT const CoordinateAxis::YCoordinateAxis YAxis;
210 /** Constant representing the Z coordinate axis; will implicitly convert to
211 the integer 2 when used in a context requiring an integer. **/
212 extern SimTK_SimTKCOMMON_EXPORT const CoordinateAxis::ZCoordinateAxis ZAxis;
213
getCoordinateAxis(int i)214 inline const CoordinateAxis& CoordinateAxis::getCoordinateAxis(int i) {
215 assert(isIndexInRange(i));
216 return (i==0 ? static_cast<const CoordinateAxis&>(XAxis)
217 : (i==1 ? static_cast<const CoordinateAxis&>(YAxis)
218 : static_cast<const CoordinateAxis&>(ZAxis)));
219 }
220
221 /// Compare two CoordinateAxis objects. @relates CoordinateAxis
222 inline bool operator==(const CoordinateAxis& a1, const CoordinateAxis& a2)
223 { return a1.isSameAxis(a2); }
224
225 /// Compare two CoordinateAxis objects. @relates CoordinateAxis
226 inline bool operator!=(const CoordinateAxis& a1, const CoordinateAxis& a2)
227 { return a1.isDifferentAxis(a2); }
228
229
230 /** A CoordinateDirection is a CoordinateAxis plus a direction indicating the
231 positive or negative direction along that axis. There are only six possible
232 values for a CoordinateDirection, and there are predefined constants available
233 covering all of them:
234 - XAxis, YAxis, ZAxis are the CoordinateAxis types; they will implicitly
235 convert to positive axis directions.
236 - NegXAxis, NegYAxis, NegZAxis are the negative directions.
237 - The unary negation operator is overloaded so that -XAxis produces
238 NegXAxis and -NegYAxis produces YAxis.
239 - The unary plus operator is overloaded for the CoordinateAxis objects
240 so that +XAxis and so on are the positive CoordinateDirection objects.
241 You can also produce CoordinateDirections at compile time or run time from
242 calculated axes and directions.
243 @see CoordinateAxis **/
244 class CoordinateDirection {
245 public:
246 /** Use for compile-time construction of a negative CoordinateDirection
247 along one of the coordinate axes. **/
248 class Negative {};
249
250 /** Implicit conversion of a CoordinateAxis to a positive
251 CoordinateDirection along that axis. **/
CoordinateDirection(const CoordinateAxis & axis)252 CoordinateDirection(const CoordinateAxis& axis)
253 : m_axis(axis), m_direction(1) {}
254
255 /** Explicit creation of a negative CoordinateDirection from a
256 CoordinateAxis. **/
CoordinateDirection(const CoordinateAxis & axis,Negative)257 CoordinateDirection(const CoordinateAxis& axis, Negative)
258 : m_axis(axis), m_direction(-1) {}
259
260 /** Explicit creation of a CoordinateDirection from a CoordinateAxis
261 and a direction calculated at run time.
262 @param[in] axis XAxis, YAxis, or ZAxis
263 @param[in] direction Must be -1 or 1.
264 @note Zero is not allowed for \a direction, meaning that
265 you must not try to produce one of these from the "sign" result of one of
266 the cross product methods, because there the sign can be -1, 0, or 1. **/
CoordinateDirection(const CoordinateAxis & axis,int direction)267 CoordinateDirection(const CoordinateAxis& axis, int direction)
268 : m_axis(axis), m_direction(direction)
269 { assert(direction==1 || direction==-1); }
270
271 /** This is the coordinate axis XAxis, YAxis, or ZAxis contained in this
272 CoordinateDirection.\ Use getDirection() to determine whether this is the
273 positive or negative direction. **/
getAxis()274 CoordinateAxis getAxis() const {return m_axis;}
275 /** Returns 1 or -1 to indicate the direction along the coordinate
276 axis returned by getAxis(). **/
getDirection()277 int getDirection() const {return m_direction;}
278
279 /** Return true if this direction and \a dir2 are along the same axis,
280 even if the direction along that axis is not the same. **/
hasSameAxis(const CoordinateDirection & dir2)281 bool hasSameAxis(const CoordinateDirection& dir2) const
282 { return m_axis.isSameAxis(dir2.getAxis()); }
283
284 /** Return true if this direction and \a dir2 are along the same axis,
285 and in the same direction along that axis.\ You can also
286 use operator==() for this comparison. **/
isSameAxisAndDirection(const CoordinateDirection & dir2)287 bool isSameAxisAndDirection(const CoordinateDirection& dir2) const
288 { return m_axis==dir2.getAxis() && m_direction==dir2.getDirection(); }
289
290 /** Perform a specialized dot product between this coordinate direction
291 and \a dir2; returning 1 or -1 if they contain the same axis and 0
292 otherwise, without performing any floating point operations. **/
dotProduct(const CoordinateDirection & dir2)293 int dotProduct( const CoordinateDirection& dir2 ) const
294 { if (m_axis != dir2.getAxis()) return 0;
295 return m_direction == dir2.getDirection() ? 1 : -1; }
296
297 /** Return the sign that would result from a cross product between this
298 coordinate direction and \a dir2: 0 if they are along the same axis;
299 1 if the result would be in the positive direction along the third axis;
300 -1 if it would be in the negative direction. No floating point
301 computations are performed. @see crossProductAxis() **/
crossProductSign(const CoordinateDirection & dir2)302 int crossProductSign( const CoordinateDirection& dir2 ) const
303 { if (m_axis == dir2.getAxis()) return 0;
304 return m_axis.crossProductSign(dir2.getAxis())
305 * m_direction * dir2.getDirection(); }
306
307 /** Return the coordinate axis along which the cross product of this
308 coordinate direction and \a dir2 would lie: same as this if both contain
309 the same axis (doesn't matter because the sign would be zero); otherwise,
310 the third axis that neither this one nor \a dir2 contains. But note that
311 the actual result may be along that axis or in the negative direction
312 along that axis. No floating point computations are performed.
313 @see crossProductSign(). **/
crossProductAxis(const CoordinateDirection & dir2)314 CoordinateAxis crossProductAxis( const CoordinateDirection& dir2 ) const
315 { return m_axis.crossProductAxis(dir2.getAxis()); }
316
317 /** Return the axis and sign along that axis that would result from a
318 cross product between this coordinate direction and \a dir2; this
319 combines the functions of both crossProductAxis() and crossProductSign().
320 Note that if \a dir2 is along the same axis as this one, we'll just
321 return this as the axis but the sign is zero since the magnitude of the
322 result would be zero. No floating point calculations are
323 performed. @see crossProductSign(), crossProductAxis() **/
crossProduct(const CoordinateDirection & dir2,int & sign)324 CoordinateAxis crossProduct( const CoordinateDirection& dir2,
325 int& sign ) const
326 { sign = crossProductSign(dir2); return crossProductAxis(dir2); }
327
328 // Local class declarations for helper classes.
329 class NegXDirection; class NegYDirection; class NegZDirection;
330 private:
331 CoordinateAxis m_axis; // XAxis, YAxis, or ZAxis
332 int m_direction; // 1 or -1
333 };
334
335
336 // Helper classes that allow compile time recognition of negative axis
337 // directions.
338 class CoordinateDirection::NegXDirection : public CoordinateDirection {
NegXDirection()339 public: NegXDirection() : CoordinateDirection(XAxis,Negative()) {}
340 };
341 class CoordinateDirection::NegYDirection : public CoordinateDirection {
NegYDirection()342 public: NegYDirection() : CoordinateDirection(YAxis,Negative()) {}
343 };
344 class CoordinateDirection::NegZDirection : public CoordinateDirection {
NegZDirection()345 public: NegZDirection() : CoordinateDirection(ZAxis,Negative()) {}
346 };
347
348 // Predefine constants for the negative X,Y,Z directions.
349 extern SimTK_SimTKCOMMON_EXPORT const CoordinateDirection::NegXDirection
350 NegXAxis; ///< Global constant indicating -X coordinate direction.
351 extern SimTK_SimTKCOMMON_EXPORT const CoordinateDirection::NegYDirection
352 NegYAxis; ///< Global constant indicating -Y coordinate direction.
353 extern SimTK_SimTKCOMMON_EXPORT const CoordinateDirection::NegZDirection
354 NegZAxis; ///< Global constant indicating -Z coordinate direction.
355
356 /// Compare two CoordinateDirection objects. @relates CoordinateDirection
357 inline bool operator==(const CoordinateDirection& d1,
358 const CoordinateDirection& d2)
359 { return d1.isSameAxisAndDirection(d2); }
360
361 /// Compare two CoordinateDirection objects. @relates CoordinateDirection
362 inline bool operator!=(const CoordinateDirection& d1,
363 const CoordinateDirection& d2)
364 { return !d1.isSameAxisAndDirection(d2); }
365
366 /// Create the NegXAxis direction by negating XAxis. No computation
367 /// is necessary. @relates CoordinateAxis
368 inline const CoordinateDirection::NegXDirection&
369 operator-(const CoordinateAxis::XCoordinateAxis&){return NegXAxis;}
370 /// Create the NegYAxis direction by negating YAxis. No computation
371 /// is necessary. @relates CoordinateAxis
372 inline const CoordinateDirection::NegYDirection&
373 operator-(const CoordinateAxis::YCoordinateAxis&){return NegYAxis;}
374 /// Create the NegZAxis direction by negating ZAxis. No computation
375 /// is necessary. @relates CoordinateAxis
376 inline const CoordinateDirection::NegZDirection&
377 operator-(const CoordinateAxis::ZCoordinateAxis&){return NegZAxis;}
378
379 /// Create the negative direction along the given axis. No computation
380 /// is necessary. @relates CoordinateAxis
381 inline CoordinateDirection
382 operator-(const CoordinateAxis& axis)
383 { return CoordinateDirection(axis,CoordinateDirection::Negative()); }
384
385 /// Create the positive direction along the given axis. No computation
386 /// is necessary. @relates CoordinateAxis
387 inline CoordinateDirection
388 operator+(const CoordinateAxis& axis)
389 { return CoordinateDirection(axis); }
390
391 /// Create the XAxis direction by negating NegXAxis. No computation
392 /// is necessary. @relates CoordinateDirection
393 inline const CoordinateAxis::XCoordinateAxis&
394 operator-(const CoordinateDirection::NegXDirection&){return XAxis;}
395 /// Create the YAxis direction by negating NegYAxis. No computation
396 /// is necessary. @relates CoordinateDirection
397 inline const CoordinateAxis::YCoordinateAxis&
398 operator-(const CoordinateDirection::NegYDirection&){return YAxis;}
399 /// Create the ZAxis direction by negating NegZAxis. No computation
400 /// is necessary. @relates CoordinateDirection
401 inline const CoordinateAxis::ZCoordinateAxis&
402 operator-(const CoordinateDirection::NegZDirection&){return ZAxis;}
403
404 /// Create the opposite direction from the given direction. No computation
405 /// is necessary. @relates CoordinateDirection
406 inline CoordinateDirection
407 operator-(const CoordinateDirection& dir)
408 { return CoordinateDirection(dir.getAxis(), -dir.getDirection()); }
409
410 } // End of namespace
411
412 #endif // SimTK_SimTKCOMMON_COORDINATE_AXIS_H_
413
414
415
416