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