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