1 #ifndef OPENSIM_JOINT_H_
2 #define OPENSIM_JOINT_H_
3 /* -------------------------------------------------------------------------- *
4  *                            OpenSim:  Joint.h                               *
5  * -------------------------------------------------------------------------- *
6  * The OpenSim API is a toolkit for musculoskeletal modeling and simulation.  *
7  * See http://opensim.stanford.edu and the NOTICE file for more information.  *
8  * OpenSim is developed at Stanford University and supported by the US        *
9  * National Institutes of Health (U54 GM072970, R24 HD065690) and by DARPA    *
10  * through the Warrior Web program.                                           *
11  *                                                                            *
12  * Copyright (c) 2005-2017 Stanford University and the Authors                *
13  * Author(s): Frank C. Anderson, Peter Loan, Ajay Seth                        *
14  *                                                                            *
15  * Licensed under the Apache License, Version 2.0 (the "License"); you may    *
16  * not use this file except in compliance with the License. You may obtain a  *
17  * copy of the License at http://www.apache.org/licenses/LICENSE-2.0.         *
18  *                                                                            *
19  * Unless required by applicable law or agreed to in writing, software        *
20  * distributed under the License is distributed on an "AS IS" BASIS,          *
21  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   *
22  * See the License for the specific language governing permissions and        *
23  * limitations under the License.                                             *
24  * -------------------------------------------------------------------------- */
25 // INCLUDE
26 #include <OpenSim/Simulation/Model/ModelComponent.h>
27 #include <OpenSim/Simulation/Model/PhysicalOffsetFrame.h>
28 #include <OpenSim/Simulation/SimbodyEngine/Body.h>
29 #include <OpenSim/Simulation/SimbodyEngine/Coordinate.h>
30 #include <simbody/internal/MobilizedBody.h>
31 
32 namespace OpenSim {
33 
34 class Model;
35 
36 class JointFramesAreTheSame : public Exception {
37 public:
JointFramesAreTheSame(const std::string & file,size_t line,const std::string & func,const std::string & thisName,const std::string & sameName)38     JointFramesAreTheSame(const std::string& file,
39         size_t line,
40         const std::string& func,
41         const std::string& thisName,
42         const std::string& sameName) :
43         Exception(file, line, func) {
44         std::string msg = "Joint '" + thisName + "' cannot connect frame '" +
45             sameName + "' to itself.";
46         addMessage(msg);
47     }
48 };
49 
50 
51 /**
52 An OpenSim Joint is an OpenSim::ModelComponent which connects two PhysicalFrames
53 together and specifies their relative permissible motion as described in
54 internal coordinates. The base Joint specifies two frames (e.g. one per body),
55 which the joint spans. The relative motion (including the # of coordinates)
56 are defined by concrete Joints, which specify the permissible kinematics of
57 a child joint frame (on a child body) with respect to a parent joint frame
58 (on a parent body). The designation of parent and child are used only to
59 identify the directionality of the joint and in which frame the joint
60 coordinates are expressed.
61 
62 For example, A PinJoint between a parent frame, P, and a child frame, B,
63 has a coordinate value of zero when the two frames are aligned and
64 positive coordinate values are the angle between the frames' X-axes given
65 a positive Z-rotation of the child frame about the coincident Z-axis in
66 the parent frame.
67 
68 Note: the parent and child frames must be added to the model by the time
69       you call initSystem() on the model.
70 
71 Concrete Joints can specify relative translations and even coupled
72 rotations and translations (see EllipsoidJoint and CustomJoint). For more
73 details on how the underlying formulation supports coupled curvilinear
74 joints, see "Minimal formulation of joint motion for biomechanisms", 2010
75 A Seth, M Sherman, P Eastman, S Delp; Nonlinear dynamics 62 (1), 291-303
76 
77 <b>C++ example</b>
78 \code{.cpp}
79 // Define a pin joint that attaches pendulum (an OpenSim::Body) to ground.
80 PinJoint* myPin = new PinJoint("pendulumToGround", myModel.getGround(),
81                                pendulum);
82 \endcode
83 
84 <b>Python example</b>
85 \code{.py}
86     # Define a ball joint between blockA and blockB.
87     abJoint = osim.BallJoint('JointName', blockA, blockB)
88 \endcode
89 
90 If you want to connect to an existing PhysicalFrame (e.g., a Body or Ground)
91 but not to its origin, you can create and connect to a PhysicalOffsetFrame; the
92 following convenience constructor does this for you:
93 
94 <b>C++ example</b>
95 \code{.cpp}
96 // Define a pin joint that attaches the end of pendulum to the ground origin.
97 PinJoint* myPin = new PinJoint("pendulumToGround",
98                                myModel.getGround(),   //parent PhysicalFrame
99                                Vec3(0),               //location in parent
100                                Vec3(0),               //orientation in parent
101                                pendulum,              //child PhysicalFrame
102                                Vec3(0,-length/2.,0),  //location in child
103                                Vec3(0));              //orientation in child
104 \endcode
105 
106 
107 @author Ajay Seth
108 */
109 class OSIMSIMULATION_API Joint : public ModelComponent {
110 OpenSim_DECLARE_ABSTRACT_OBJECT(Joint, ModelComponent);
111 
112 public:
113 //==============================================================================
114 // PROPERTIES
115 //==============================================================================
116     OpenSim_DECLARE_LIST_PROPERTY(coordinates, Coordinate,
117         "List containing the generalized coordinates (q's) that parameterize "
118         "this joint.");
119 
120     OpenSim_DECLARE_LIST_PROPERTY(frames, PhysicalOffsetFrame,
121         "Physical offset frames owned by the Joint that are typically used to "
122         "satisfy the owning Joint's parent and child frame connections "
123         "(sockets). PhysicalOffsetFrames are often used to describe the fixed "
124         "transformation from a Body's origin to another location of interest "
125         "on the Body (e.g., the joint center). When the joint is deleted, so "
126         "are the PhysicalOffsetFrame components in this list.");
127 
128 //==============================================================================
129 // SOCKETS
130 //==============================================================================
131     OpenSim_DECLARE_SOCKET(parent_frame, PhysicalFrame,
132         "The parent frame for the joint.");
133     OpenSim_DECLARE_SOCKET(child_frame, PhysicalFrame,
134         "The child frame for the joint.");
135 
136 //==============================================================================
137 // OUTPUTS
138 //==============================================================================
139     OpenSim_DECLARE_OUTPUT(power, double, calcPower, SimTK::Stage::Acceleration);
140     OpenSim_DECLARE_OUTPUT(reaction_on_parent, SimTK::SpatialVec,
141         calcReactionOnParentExpressedInGround, SimTK::Stage::Acceleration);
142     OpenSim_DECLARE_OUTPUT(reaction_on_child, SimTK::SpatialVec,
143         calcReactionOnChildExpressedInGround, SimTK::Stage::Acceleration);
144 
145 //==============================================================================
146 // METHODS
147 //==============================================================================
148     /** Default constructor. Create an unnamed Joint with parent and child frame
149         sockets that are unsatisfied. */
150     Joint();
151 
152     /** Convenience constructor. Create a Joint by specifying the parent and
153         child frames.
154 
155         @param[in] name    the name associated with this joint (should be unique
156                            from other joints in the same model)
157         @param[in] parent  the parent PhysicalFrame to which this joint connects
158         @param[in] child   the child PhysicalFrame to which this joint connects
159     */
160     Joint(const std::string&    name,
161           const PhysicalFrame&  parent,
162           const PhysicalFrame&  child);
163 
164     /** Backwards-compatible convenience constructor. Construct a Joint where
165         the parent and child frames are specified as well as the location and
166         orientation of the parent and child frames in their respective physical
167         frames.
168 
169         @param[in] name    the name associated with this joint (should be unique
170                            from other joints in the same model)
171         @param[in] parent  the parent PhysicalFrame to which this joint connects
172         @param[in] locationInParent     Vec3 of the location of the joint in the
173                                         parent frame.
174         @param[in] orientationInParent  Vec3 of the XYZ body-fixed Euler angles
175                                         of the joint frame orientation in the
176                                         parent frame.
177         @param[in] child   the child PhysicalFrame to which this joint connects
178         @param[in] locationInChild      Vec3 of the location of the joint in the
179                                         child frame.
180         @param[in] orientationInChild   Vec3 of the XYZ body-fixed Euler angles
181                                         of the joint frame orientation in the
182                                         child frame.
183     */
184     Joint(const std::string&    name,
185           const PhysicalFrame&  parent,
186           const SimTK::Vec3&    locationInParent,
187           const SimTK::Vec3&    orientationInParent,
188           const PhysicalFrame&  child,
189           const SimTK::Vec3&    locationInChild,
190           const SimTK::Vec3&    orientationInChild);
191 
192     virtual ~Joint();
193 
194     // GET & SET
195     /**
196      * Get the child joint frame.
197      *
198      * @return const PhysicalFrame reference.
199      */
200     const PhysicalFrame& getChildFrame() const;
201 
202     /**
203      * Get the parent frame to which this joint attaches.
204      *
205      * @return const ref to parent PhysicalFrame.
206      */
207     const OpenSim::PhysicalFrame& getParentFrame() const;
208 
209     /** Convenience method to get a const reference to the Coordinate associated
210         with a single-degree-of-freedom Joint. If the Joint has more than one
211         Coordinate, you must use get_coordinates() or provide the appropriate
212         argument to the getCoordinate() method defined in the derived class. */
213     const Coordinate& getCoordinate() const;
214 
215     /** Convenience method to get a writable reference to the Coordinate
216         associated with a single-degree-of-freedom Joint. If the Joint has more
217         than one Coordinate, you must use upd_coordinates() or provide the
218         appropriate argument to the updCoordinate() method defined in the
219         derived class. */
220     Coordinate& updCoordinate();
221 
222     // Model building
numCoordinates()223     int numCoordinates() const { return getProperty_coordinates().size(); }
224 
225     // Utility
226     bool isCoordinateUsed(const Coordinate& aCoordinate) const;
227 
228     /** Add a frame to the *frames* property in this Joint. The frame is
229      * adopted, and should have been dynamically allocated.
230      * Use this function instead of append_frames(). */
231     void addFrame(PhysicalOffsetFrame* frame);
232 
233     // Computation
234     /** Given some system mobility (generalized) forces, calculate the
235     equivalent spatial body force for this Joint. Keep in mind that there are
236     typically nm < 6 mobilities per joint with an infinite set of solutions that
237     can map nm gen forces to 6 spatial force components (3 for torque + 3 for
238     force). The solution returned provides the "most" effective force and torque
239     in the joint frame. This means the smallest magnitude force and/or torque
240     that will result in the same generalized force. If a generalized force is
241     defined along/about a joint axis, then this should be evident in the
242     reported results as a force or torque on the same axis.  NOTE: Joints
243     comprised of multiple mobilizers and/or constraints, should override this
244     method and account for multiple internal components.
245 
246     @param state containing the generalized coordinate and speed values
247     @param mobilityForces for the system as computed by inverse dynamics,
248                           for example
249     @return spatial force, FB_G, acting on the body connected by this joint at
250     its location B, expressed in ground.  */
251     SimTK::SpatialVec
252         calcEquivalentSpatialForce(const SimTK::State& state,
253                                const SimTK::Vector &mobilityForces) const;
254 
255     /// Joint Reaction forces
256     /** Calculate the joint reaction force and moment acting on the parent frame
257         and expressed in Ground.
258     @param[in]  state containing the generalized coordinate and speed values
259     @return     SpatialVec of reaction force, RP_G, acting on parent frame, P,
260                 and expressed in ground, G.  */
261     SimTK::SpatialVec
calcReactionOnParentExpressedInGround(const SimTK::State & state)262         calcReactionOnParentExpressedInGround(const SimTK::State &state) const {
263         return getChildFrame().getMobilizedBody()
264             .findMobilizerReactionOnParentAtFInGround(state);
265     }
266     /** Calculate the joint reaction force and moment acting on the child frame
267         and expressed in Ground.
268     @param[in]  state containing the generalized coordinate and speed values
269     @return     SpatialVec of reaction force, RP_G, acting on child frame, C,
270                 and expressed in ground, G.  */
271     SimTK::SpatialVec
calcReactionOnChildExpressedInGround(const SimTK::State & state)272         calcReactionOnChildExpressedInGround(const SimTK::State &state) const {
273         return getChildFrame().getMobilizedBody()
274             .findMobilizerReactionOnBodyAtMInGround(state);
275     }
276 
277     /** Joints in general do not contribute power since the reaction space
278         forces are orthogonal to the mobility space. However, when joint motion
279         is prescribed, the internal forces that move the joint will do work. In
280         that case, the power is non-zero and the supplied SimTK::State
281         must already have been realized to %Acceleration stage so that
282         constraint forces are available. */
283     virtual double calcPower(const SimTK::State &s) const;
284 
285 #ifndef SWIG
286     /// @class CoordinateIndex
287     /// Unique integer type for local Coordinate indexing
288     SimTK_DEFINE_UNIQUE_INDEX_TYPE(CoordinateIndex);
289 
290     /** Get the MotionType for a Coordinate that this Joint has enabled by
291         its CoordinateIndex (in the Joints list of Coordinates). */
292     Coordinate::MotionType getMotionType(CoordinateIndex cix) const;
293 #endif //SWIG
294 protected:
295     /** A CoordinateIndex member is created by constructCoordinate(). E.g.:
296     \code{.cpp}
297     class My2DofJoint : public Joint {
298     public:
299         enum class Coord: unsigned {
300             RotationX,
301             TranslationX
302         };
303 
304     private:
305         CoordinateIndex rx{ constructCoordinate(Coordinate::MotionType::Rotational,
306                             static_cast<unsigned>(Coord::RotationX)) };
307         CoordinateIndex tx{ constructCoordinate(Coordinate::MotionType::Translational,
308                             static_cast<unsigned>(Coord::TranslationX)) };
309         ...
310     };
311     \endcode
312     */
313 #ifndef SWIG
314     /** Utility for a derived Joint to add the Coordinates that correspond to
315     the motion it permits. Derived Joints must construct as many Coordinates as
316     are reflected by the underlying Mobilizer Qs. The index of the corresponding
317     enumeration (enum) is required at construction to ensure the Joint's
318     internal list of Coordinates is consistent with its interface; an exception
319     is thrown if the Coordinates are not constructed in the same order as the
320     enums have been defined. */
321     CoordinateIndex constructCoordinate(Coordinate::MotionType mt,
322                                         unsigned idx);
323 
324     // This is only intended to allow the CustomJoint to set the MotionTypes
325     // of its Coordinates
326     void setMotionType(CoordinateIndex cix, Coordinate::MotionType mt);
327 #endif //SWIG
328 
329     // build Joint transforms from properties
330     void extendFinalizeFromProperties() override;
331     void extendConnectToModel(Model& model) override;
332     void extendAddToSystem(SimTK::MultibodySystem& system) const override;
333     void extendInitStateFromProperties(SimTK::State& s) const override;
334     void extendSetPropertiesFromState(const SimTK::State& state) override;
335 
336     // Methods that allow access for Joint subclasses to data members of
337     // Body and Coordinate , which Joint befriends
338     const SimTK::MobilizedBodyIndex getMobilizedBodyIndex(const Body& body) const;
339 
340     void setChildMobilizedBodyIndex(SimTK::MobilizedBodyIndex index) const;
setCoordinateMobilizedBodyIndex(Coordinate * aCoord,SimTK::MobilizedBodyIndex index)341     void setCoordinateMobilizedBodyIndex(Coordinate *aCoord, SimTK::MobilizedBodyIndex index) const {aCoord->_bodyIndex = index;}
setCoordinateMobilizerQIndex(Coordinate * aCoord,int index)342     void setCoordinateMobilizerQIndex(Coordinate *aCoord, int index) const
343         { aCoord->_mobilizerQIndex = SimTK::MobilizerQIndex(index);}
setCoordinateModel(Coordinate * aCoord,Model * aModel)344     void setCoordinateModel(Coordinate *aCoord, Model *aModel) const {aCoord->_model = aModel;}
345 
346     /** Updating XML formating to latest revision */
347     void updateFromXMLNode(SimTK::Xml::Element& aNode, int versionNumber) override;
348 
349     /** Calculate the equivalent spatial force, FB_G, acting on a mobilized body
350         specified by index acting at its mobilizer frame B, expressed in ground. */
351     SimTK::SpatialVec
352         calcEquivalentSpatialForceForMobilizedBody(const SimTK::State &s,
353             const SimTK::MobilizedBodyIndex mbx,
354             const SimTK::Vector &mobilityForces) const;
355 
356     /** Return the equivalent (internal) SimTK::Rigid::Body for the parent/child
357         OpenSim::Body. Not valid until after extendAddToSystem on the Body has been called.*/
358     const SimTK::Body& getParentInternalRigidBody() const;
359     const SimTK::Body& getChildInternalRigidBody() const;
360 
361 
362     /** Utility method for creating the underlying MobilizedBody of the desired
363         type of the concrete Joint. It is templatized by the MobilizedBody type.
364         Concrete class cannot override this method but can customize extendAddToSystem()
365         instead of using this service. It assumes that the MobilizedBody is
366         associated with the child body, unless the Joint is specified as
367         reversed in which case the parent is the Body that is "mobilized".
368         For more granularity as to which bodies are being interconnected
369         internally, and to specify their joint (mobilizer) transforms use:
370         @see createMobilizedBody(MobilizedBody& inboard,
371                         const SimTK::Transform& inboardTransform,
372                             ...).  following this method. */
373     template <typename T>
createMobilizedBody(SimTK::MultibodySystem & mbs)374     T createMobilizedBody(SimTK::MultibodySystem& mbs) const
375     {
376         SimTK::MobilizedBody inb;
377         const SimTK::Body* outb = &getChildInternalRigidBody();
378         SimTK::Transform inbX = getParentFrame().findTransformInBaseFrame();
379         SimTK::Transform outbX = getChildFrame().findTransformInBaseFrame();
380         const PhysicalFrame* associatedFrame = nullptr;
381         // if the joint is reversed then flip the underlying tree representation
382         // of inboard and outboard bodies, although the joint direction will be
383         // preserved, the inboard must exist first.
384         if (isReversed){
385             inb = getChildFrame().getMobilizedBody();
386             SimTK::Transform swap = inbX;
387             inbX = outbX;
388             outbX = swap;
389 
390             outb = &getParentInternalRigidBody();
391             associatedFrame = _slaveBodyForParent ? _slaveBodyForParent.get()
392                                                   : &getParentFrame();
393         }
394         else{
395             inb = getParentFrame().getMobilizedBody();
396 
397             associatedFrame = _slaveBodyForChild ? _slaveBodyForChild.get()
398                                                  : &getChildFrame();
399         }
400 
401         int startingCoordinateIndex = 0;
402         T simtkBody = createMobilizedBody<T>(inb, inbX,
403                                              *outb, outbX,
404                                              startingCoordinateIndex,
405                                              associatedFrame);
406 
407         return simtkBody;
408     }
409 
410     /** Utility method for creating an underlying MobilizedBody of the desired
411     type. Method is templatized by the MobilizedBody. Unlike the previous method,
412     the inboard and outboard of the mobilized body are not assumed to be parent
413     and child of the joint. This enables Joint component makers to introduce
414     intermediate MobilizedBodies for the purpose of creating more complex Joints.
415     If more than one MobilizedBody is being created, it is up to the caller to
416     supply the corresponding coordinateIndex for the purpose of automatically
417     assigning indices necessary for the Coordinates of this Joint to access
418     the coordinates and speed values from the state of the MultibodySystem.
419     As a convenience the startingCoorinateIndex is updated so
420     that sequential calls will increment correctly based on the number of
421     mobilities the concrete MobilizedBody enables.
422 
423     @param[in] inboard           an existing SimTK::MobilizedBody in the
424                                  multibody tree
425     @param[in] inboardTransform  the transform locating the joint (mobilizer)
426                                  frame on the inboard body
427     @param[in] outboard          a SimTK::Rigid::Body to be added to the
428                                  multibody tree
429     @param[in] outboardTransform  the transform locating the joint (mobilizer)
430                                   frame on the outboard body
431     @param[in,out] startingCoordinateIndex
432                                  the starting index of mobilities
433                                  enabled by the created MobilizedBody and used
434                                  to assign mobility indices to the Joint's
435                                  coordinates. It is incremented by the number of
436                                  mobilities of the MobilizedBody created
437     @param[in] physicalFrame     (optional) the PhysicalFrame associated with
438                                  the MobilizeBody. The MobilizedBody index is
439                                  assigned to the associated Body.
440     */
441     template <typename T>
442     T createMobilizedBody(SimTK::MobilizedBody& inboard,
443                           const SimTK::Transform& inboardTransform,
444                           const SimTK::Body& outboard,
445                           const SimTK::Transform& outboardTransform,
446                           int& startingCoordinateIndex,
447                           const PhysicalFrame* physicalFrame = nullptr) const {
448         // CREATE MOBILIZED BODY
449         SimTK::MobilizedBody::Direction dir =
450             SimTK::MobilizedBody::Direction(isReversed);
451 
452         T simtkBody(inboard, inboardTransform, outboard, outboardTransform, dir);
453 
454         startingCoordinateIndex = assignSystemIndicesToBodyAndCoordinates(simtkBody,
455             physicalFrame,
456             getNumMobilities<T>(simtkBody),
457             startingCoordinateIndex);
458 
459         return simtkBody;
460     }
461 
462     /** Utility method to assign body and coordinate indices from the underlying
463         MultibodySystem from a newly created MobilizedBody. Assign its mobilities
464         to OpenSim::Coordinates that belong to this Joint and the body index
465         to the Body being mobilized by this Joint. If the Body is NULL then we
466         assume that we are constructing intermediate MobilizedBodies and indices
467         are assigned to corresponding coordinates, but not to a Body. If a Body
468         is provided we assume that Joint creator has provided the correct child
469         (or parent, if reversed) body that has been mobilized. This method throws
470         an exception if the mobilized Body is neither the Joint's parent or child.*/
471     int assignSystemIndicesToBodyAndCoordinates(
472         const SimTK::MobilizedBody& mobod,
473         const OpenSim::PhysicalFrame* mobilized,
474         const int& numMobilities,
475         const int& startingCoordinateIndex) const;
476 
477 private:
478     void setNull();
479 
480     /** Construct the infrastructure of the Joint component.
481         Begin with its properties. */
482     void constructProperties();
483 
484     /** Utility method for accessing the number of mobilities provided by
485         an underlying MobilizedBody */
486     template <typename T>
getNumMobilities(const T & mobod)487     int getNumMobilities(const T& mobod) const
488     {
489         return mobod.getDefaultQ().size();
490     }
491 
492     // Only Model::extendConnectToModel() should access private members
493     // of the Joint to set whether the Joint is connected to a slave body.
494     // See Model::createMultibodyTree();
495     friend Model;
496 
setSlaveBodyForParent(Body & slaveForParent)497     void setSlaveBodyForParent(Body& slaveForParent){
498         _slaveBodyForParent = slaveForParent;
499     }
500 
setSlaveBodyForChild(Body & slaveForChild)501     void setSlaveBodyForChild(Body& slaveForChild){
502         _slaveBodyForChild = slaveForChild;
503     }
504 
505     //==========================================================================
506     // DATA
507     //==========================================================================
508 protected:
509     // Specifies the direction of the Joint in the multibody tree: parent->child
510     // (isReversed is false; default) or child->parent (isReversed is true). The
511     // Joint's transform and coordinates maintain a parent->child sense even if
512     // the joint is reversed.
513     bool isReversed = false;
514 
515 private:
516     SimTK::ReferencePtr<Body> _slaveBodyForParent;
517     SimTK::ReferencePtr<Body> _slaveBodyForChild;
518 
519     SimTK::Array_<Coordinate::MotionType> _motionTypes;
520 
521 //==============================================================================
522 };  // END of class Joint
523 //==============================================================================
524 //==============================================================================
525 
526 // Specializations
527 template <>
getNumMobilities(const SimTK::MobilizedBody::Pin & mobod)528 inline int Joint::getNumMobilities(const SimTK::MobilizedBody::Pin& mobod) const
529 {
530     return 1;
531 }
532 
533 template <>
getNumMobilities(const SimTK::MobilizedBody::Slider & mobod)534 inline int Joint::getNumMobilities(const SimTK::MobilizedBody::Slider& mobod) const
535 {
536     return 1;
537 }
538 
539 template <>
getNumMobilities(const SimTK::MobilizedBody::Weld & mobod)540 inline int Joint::getNumMobilities(const SimTK::MobilizedBody::Weld& mobod) const
541 {
542     return 0;
543 }
544 
545 template <>
getNumMobilities(const SimTK::MobilizedBody::Universal & mobod)546 inline int Joint::getNumMobilities(const SimTK::MobilizedBody::Universal& mobod) const
547 {
548     return 2;
549 }
550 
551 template <>
getNumMobilities(const SimTK::MobilizedBody::Ball & mobod)552 inline int Joint::getNumMobilities(const SimTK::MobilizedBody::Ball& mobod) const
553 {
554     return 3;
555 }
556 
557 template <>
getNumMobilities(const SimTK::MobilizedBody::Ellipsoid & mobod)558 inline int Joint::getNumMobilities(const SimTK::MobilizedBody::Ellipsoid& mobod) const
559 {
560     return 3;
561 }
562 
563 template <>
getNumMobilities(const SimTK::MobilizedBody::Free & mobod)564 inline int Joint::getNumMobilities(const SimTK::MobilizedBody::Free& mobod) const
565 {
566     return 6;
567 }
568 
569 class JointHasNoCoordinates : public Exception {
570 public:
JointHasNoCoordinates(const std::string & file,size_t line,const std::string & func)571     JointHasNoCoordinates(const std::string& file,
572                           size_t line,
573                           const std::string& func) :
574         Exception(file, line, func) {
575         std::string mesg = "The Joint has no Coordinates.";
576 
577         addMessage(mesg);
578     }
579 };
580 
581 } // end of namespace OpenSim
582 
583 #endif // OPENSIM_JOINT_H_
584