1 #ifndef SimTK_SIMMATH_MULTIBODY_GRAPH_MAKER_H_
2 #define SimTK_SIMMATH_MULTIBODY_GRAPH_MAKER_H_
3 
4 /* -------------------------------------------------------------------------- *
5  *                     Simbody(tm): Multibody Graph Maker                     *
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) 2013-4 Stanford University and the Authors.         *
13  * Authors: Michael Sherman                                                   *
14  * Contributors: Kevin He                                                     *
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 Declares the SimTK::MultibodyGraphMaker class for use in constructing a
29 spanning-tree-plus-constraints representation of a multibody system from a
30 list of its bodies and joints. **/
31 
32 #include "SimTKcommon.h"
33 #include "simmath/internal/common.h"
34 
35 #include <utility>
36 #include <string>
37 #include <vector>
38 #include <map>
39 #include <iosfwd>
40 
41 namespace SimTK {
42 
43 //==============================================================================
44 //                          MULTIBODY GRAPH MAKER
45 //==============================================================================
46 /** Construct a reasonably good spanning-tree-plus-constraints structure for
47 modeling a given set of bodies and joints with a generalized coordinate
48 multibody system like Simbody. Each body has a unique name; each joint
49 connects two distinct bodies with one designated as the "parent" body and
50 the other the "child". We will output a spanning tree with Ground as the root,
51 containing a mobilizer for every body. Each mobilizer corresponds to one of
52 the joints from the input and has an "inboard" (topologically closer to Ground)
53 and "outboard" (further from Ground) body, chosen from the parent and child
54 bodies of the joint but possibly reordered in which case the mobilizer is
55 marked as "reversed". Additional "free" mobilizers are added as needed between
56 bodies and Ground so that there is a path from every body in the inboard
57 direction all the way to Ground.
58 
59 <h3>Results</h3>
60 The output is
61   -# A sequence of mobilizers (tree joints) in ascending order from
62 Ground to a set of terminal bodies. Every input body will be mobilized by one
63 of these, and there may also be some additional mobilized "slave" bodies.
64   -# A set of constraints, representing either welds to attach slave bodies
65 to their masters, or loop joint constraints that correspond to particular input
66 joints.
67   -# A correspondence between input bodies and mobilized bodies, with some
68 mobilized bodies designated as slaves to particular master input bodies. That
69 is, more than one mobilized body may correspond to the same input body.
70   -# A correspondence between mobilizers and input joints, with some extra
71 mobilizers having been added to connect base bodies to Ground.
72   -# Statistics and diagnostics.
73 
74 Then to build a multibody model
75   -# Run through the mobilizers in order, adding
76 one mobilized body to the tree each time, setting the "reversed" flag if
77 appropriate (that just affects the interpretation of the generalized
78 coordinates). Mass properties and joint information are obtained from the
79 original bodies and joints; they are not stored here.
80   -# Run through the list of constraints adding master-slave welds or loop
81 joint constraints as indicated.
82 
83 <h3>Inputs</h3>
84   - bodies: name, mass, mustBeBaseBody flag
85   - joints: name, type, parent body, child body, mustBeLoopJoint flag
86   - joint type: name, # dofs, haveGoodLoopJoint flag
87   - names for the Ground body and important joint types weld and free
88 
89 The first body you add is assumed to be Ground and its name is used for
90 recognizing Ground connections later in joints. The default names for weld
91 and free joints are, not surprisingly, "weld" and "free" but you can change
92 them.
93 
94 <h3>Loop joints</h3>
95 Normally every joint produces a corresponding mobilizer. A joint that would
96 form a loop is marked as such, and then its child body is split to form a new
97 "slave" body that can be mobilized by the joint. We expect that in the
98 constructed multibody model, a master body and its slaves will be reconnected
99 via weld constraints. This provides for uniform treatment of all joints, such
100 as providing pin joint coordinates that can wrap. However, for some joint types
101 that doesn't matter because the multibody system has equally good "loop joint"
102 constraints. For example, Simbody's Ball mobilizer uses quaternions for
103 orientation and hence has no wrapping; it can be replaced with a Ball constraint
104 which removes the 3-dof mobilizer and 6 weld constraint equations, leaving just
105 3 translational constraints and indistinguishable behavior. Similarly a loop-
106 closing Weld joint can just be replaced by a Weld constraint, and a loop-
107 closing Free joint can simply be ignored.
108 
109 The algorithm normally decides which joints are the loop-breakers, however you
110 can specify in the input that certain joints must be loop joints.
111 
112 <h3>Massless bodies</h3>
113 Massless bodies can be very useful in defining compound joints, by composing
114 pin and slider joints, for example. This is fine in an internal coordinate code
115 as long as no massless (or inertialess) body is a terminal (most outboard) body
116 in the spanning tree. (Terminal massless bodies are OK if their inboard joint
117 has no dofs, i.e. is a weld mobilizer.) Bodies can have a mass provided; if a
118 movable body has zero mass the algorithm will try to avoid ending any branch of
119 the spanning tree there. If it fails, the resulting spanning tree is no good and
120 an exception will be thrown. If no mass is provided we assume it is 1.
121 
122 <h3>Base bodies</h3>
123 A body in the spanning tree that is directly connected to Ground is called a
124 "base" body. If its mobilizer is a free joint then it can be given an arbitrary
125 pose with respect to Ground and everything outboard of it moves together. This
126 is a nice property and you can influence this choice by providing an explicit
127 joint to Ground or designating some bodies as base bodies. Note that although
128 you will then have a set of generalized coordinates permitting you to place
129 these bodies arbitrarily, there is no guarantee that such placement will satisfy
130 constraints. If you don't designate base bodies, the algorithm will pick
131 them heuristically, which often leads to a good choice. The heuristic is to
132 pick the body that has the most children but does not itself appear as a child.
133 Failing that, pick the body that has the most children. In case of tie, pick
134 the first body among the tied ones.
135 
136 <h3>Ground body</h3>
137 The first body you supply in the input will be interpreted as the Ground body.
138 Its name will be used to recognize joints that connect other bodies to Ground.
139 Typical names are "ground" or "world".
140 
141 <h3>Levels</h3>
142 Each body in the spanning tree will be assigned a level, an integer that
143 measures how many edges must be traversed to get from the body to Ground.
144 Ground is at level 0, bodies that have a direct connection to Ground (base
145 bodies) are at level 1, bodies whose mobilizer connects them to base bodies
146 are at level 2, and so on. Multibody models need to be constructed in order
147 of increasing level, so that the inboard body is already in the tree when the
148 outboard body is added. We consider the level of a mobilizer to be
149 the same as the level of its outboard body. Loop joints are not mobilizers and
150 do not have a level.
151 **/
152 class SimTK_SIMMATH_EXPORT MultibodyGraphMaker {
153 public:
154     // Local classes.
155     class Body;
156     class Joint;
157     class JointType;
158     class Mobilizer;
159     class LoopConstraint;
160 
161     /** Construct an empty %MultibodyGraphMaker object and set the default
162     names for weld and free joints to "weld" and "free". **/
163     MultibodyGraphMaker();
164 
165 
166     /** Specify relevant properties of a joint type. Type name must be unique.
167     Weld and free types are predefined and their names are reserved (though you
168     can change the names to be used).
169     @returns Small integer joint type number used for referencing. **/
170     int addJointType(const std::string& name,
171                      int                numMobilities,
172                      bool               haveGoodLoopJointAvailable = false,
173                      void*              userRef                    = 0);
174 
175 
176     /** Add a new body (link) to the set of input bodies.
177     @param[in]      name
178         A unique string identifying this body. There are no other restrictions
179         on the contents of \a name. The first body you add is considered to be
180         Ground, and its name is remembered and recognized when used in joints.
181     @param[in]      mass
182         The mass here is used as a graph-building hint. If the body is massless,
183         be sure to set mass=0 so that the algorithm can avoid making this a
184         terminal body. The algorithm might also use \a mass to preferentially
185         choose heavier bodies as terminal to improve conditioning.
186     @param[in]      mustBeBaseBody
187         If you feel strongly that this body should be able to move freely with
188         respect to Ground, set this flag so that the algorithm will connect it
189         to Ground by a free joint before attempting to build the rest of the
190         tree. Alternatively, provide a joint that connects this body directly
191         to Ground, in which case you should \e not set this flag.
192     @param[in]      userRef
193         This is a generic user reference pointer that is kept with the body
194         and can be used by the caller to map back to his or her own data
195         structure containing body information.
196     @see deleteBody() **/
197     void addBody(const std::string&  name,
198                  double              mass,
199                  bool                mustBeBaseBody,
200                  void*               userRef = 0);
201 
202     /** Delete a body (link) from the set of input bodies. All the joints that
203     reference this body will be deleted too.
204     @param[in]      name
205         A unique string identifying this body. There are no other restrictions
206         on the contents of \a name. Don't delete the Ground body.
207     @returns \c true if the body is successfully deleted, \c false if it
208         didn't exist. **/
209     bool deleteBody(const std::string&  name);
210 
211     /** Add a new joint to the set of input joints.
212     @param[in]      name
213         A string uniquely identifying this joint. There are no other
214         restrictions on the contents of \a name.
215     @param[in]      type
216         A string designating the type of this joint, such as "revolute" or
217         "ball". This must be chosen from the set of joint types previously
218         specified.
219     @param[in]      parentBodyName
220         This must be the name of a body that was already specified in an earlier
221         addBody() call, or it must be the designated name for the Ground body.
222         If possible, this will be used as the inboard body for the corresponding
223         mobilizer.
224     @param[in]      childBodyName
225         This must be the name of a body that was already specified in an earlier
226         addBody() call, or it must be the designated name for the Ground body.
227         It must be distinct from \a parentBodyName. If possible, this will be
228         used as the outboard body for the corresponding mobilizer.
229     @param[in]      mustBeLoopJoint
230         If you feel strongly that this joint should be chosen as a loop joint,
231         set this flag. In that case the joint will not appear in the list of
232         joints that are candidates for mobilizers (tree joints). Only after the
233         tree has been successfully built will this joint be added, either using
234         a loop joint equivalent if one is available, or by splitting the child
235         body into master and slave otherwise. In the latter case this joint
236         \e will be made into a mobilizer but a loop weld joint will be added
237         to attach the child slave body to its master.
238     @param[in]      userRef
239         This is a generic user reference pointer that is kept with the joint
240         and can be used by the caller to map back to his or her own data
241         structure containing joint information.
242     @see deleteJoint() **/
243     void addJoint(const std::string& name,
244                   const std::string& type,
245                   const std::string& parentBodyName,
246                   const std::string& childBodyName,
247                   bool               mustBeLoopJoint,
248                   void*              userRef = 0);
249 
250     /** Delete an existing joint from the set of input joints. The bodies(links)
251     referenced by the joint are expected to exist and their references to this
252     joint will be removed as well.
253     @param[in]      name
254         A string uniquely identifying this joint. There are no other
255         restrictions on the contents of \a name.
256     @returns \c true if the joint is successfully deleted, \c false if it
257         didn't exist. **/
258     bool deleteJoint(const std::string& name);
259 
260     /** Generate a new multibody graph from the input data. Throws an std::exception
261     if it fails, with a message in the what() string. **/
262     void generateGraph();
263     /** Throw away the multibody graph produced by generateGraph(). **/
264     void clearGraph();
265 
266     /** Output a text representation of the multibody graph for debugging. **/
267     void dumpGraph(std::ostream& out) const;
268 
269     /** Returns the number of mobilizers (tree joints) in the spanning tree.
270     These are numbered in order of level. The 0th mobilizer has level 0 and
271     is just a placeholder for Ground's immobile connection to the universe.
272     After that come the base mobilizers at level 1, then mobilizers that
273     connect children to base bodies at level 2, and so on. This is also the
274     number of mobilized bodies, including Ground and slave bodies. **/
getNumMobilizers()275     int getNumMobilizers() const {return (int)mobilizers.size();}
276     /** Get a Mobilizer object by its mobilizer number, ordered outwards by
277     topological distance from Ground. **/
getMobilizer(int mobilizerNum)278     const Mobilizer& getMobilizer(int mobilizerNum) const
279     {   return mobilizers[mobilizerNum]; }
280 
281     /** Return the number of loop joint constraints that were used to close
282     loops in the graph topology. These do not include loops that were broken
283     by cutting a body to make a slave body, just those where the joint itself
284     was implemented using a constraint rather than a mobilizer plus a slave.
285     The latter occurs only if were told there is a perfectly good loop joint
286     constraint available; typically that applies for ball joints and not much
287     else. **/
getNumLoopConstraints()288     int getNumLoopConstraints() const {return (int)constraints.size();}
289     /** Get a loop constraint by its assigned number. These are assigned in
290     an arbitrary order. **/
getLoopConstraint(int loopConstraintNum)291     const LoopConstraint& getLoopConstraint(int loopConstraintNum) const
292     {   return constraints[loopConstraintNum]; }
293 
294     /** Return the number of bodies, including all input bodies, a ground body,
295     and any slave bodies. **/
getNumBodies()296     int getNumBodies() const {return (int)bodies.size();}
297     /** Get a Body object by its assigned number. These are assigned first to
298     input bodies, then we add one for Ground, then we add slave bodies created
299     by body splitting after that. **/
getBody(int bodyNum)300     const Body& getBody(int bodyNum) const {return bodies[bodyNum];}
301     /** Return the body number assigned to the input body with the given name.
302     Returns -1 if the body name is not recognized. You can't look up by name
303     slave bodies that were added by the graph-making algorithm. **/
getBodyNum(const std::string & bodyName)304     int getBodyNum(const std::string& bodyName) const {
305         std::map<std::string,int>::const_iterator p =
306             bodyName2Num.find(bodyName);
307         return p==bodyName2Num.end() ? -1 : p->second;
308     }
309 
310     /** Return the number of joints, including all input joints, and all joints
311     added to connect otherwise disconnected bodies to Ground. Don't confuse
312     these with mobilizers, which are an ordered subset of the joints that are
313     chosen to form a spanning tree connecting all the bodies. **/
getNumJoints()314     int getNumJoints() const {return (int)joints.size();}
315     /** Get a Joint object by its assigned number. These are assigned first to
316     input joints, then we add additional joints to Ground as needed. **/
getJoint(int jointNum)317     const Joint& getJoint(int jointNum) const {return joints[jointNum];}
318     /** Return the joint number assigned to the input joint with the given name.
319     Returns -1 if the joint name is not recognized. You can't look up by name
320     extra joints that were added by the graph-making algorithm. **/
getJointNum(const std::string & jointName)321     int getJointNum(const std::string& jointName) const {
322         std::map<std::string,int>::const_iterator p =
323             jointName2Num.find(jointName);
324         return p==jointName2Num.end() ? -1 : p->second;
325     }
326 
327     /** Return the number of registered joint types. **/
getNumJointTypes()328     int getNumJointTypes() const {return (int)jointTypes.size();}
329     /** Get a JointType object by its assigned number. **/
getJointType(int jointTypeNum)330     const JointType& getJointType(int jointTypeNum) const
331     {   return jointTypes[jointTypeNum]; }
332     /** Get the assigned number for a joint type from the type name. **/
getJointTypeNum(const std::string & jointTypeName)333     int getJointTypeNum(const std::string& jointTypeName) const {
334         std::map<std::string,int>::const_iterator p =
335             jointTypeName2Num.find(jointTypeName);
336         return p==jointTypeName2Num.end() ? -1 : p->second;
337     }
338 
339     /** Change the name to be used to identify the weld joint type (0 dof) and
340     weld loop constraint type (6 constraints). The default is "weld".  Changing
341     this name clears and reinitializes this %MultibodyGraphMaker object. **/
setWeldJointTypeName(const std::string & name)342     void setWeldJointTypeName(const std::string& name)
343     {   weldTypeName=name; initialize(); }
344     /** Return the name currently being used to identify the weld joint type
345     and weld loop constraint type. **/
getWeldJointTypeName()346     const std::string& getWeldJointTypeName() const {return weldTypeName;}
347 
348     /** Change the name to be used to identify the free (6 dof) joint type and
349     free (0 constraints) loop constraint type. The default is "free". Changing
350     this name clears and reinitializes this %MultibodyGraphMaker object. **/
setFreeJointTypeName(const std::string & name)351     void setFreeJointTypeName(const std::string& name)
352     {   freeTypeName=name; initialize(); }
353     /** Return the name currently being used to identify the free joint type
354     and free loop constraint type. **/
getFreeJointTypeName()355     const std::string& getFreeJointTypeName() const {return freeTypeName;}
356 
357     /** Return the name we recognize as the Ground (or World) body. This is
358     the name that was provided in the first addBody() call. **/
359     const std::string& getGroundBodyName() const;
360 private:
361     // Get writable access to bodies and joints.
updBody(int bodyNum)362     Body& updBody(int bodyNum) {return bodies[bodyNum];}
updJoint(int jointNum)363     Joint& updJoint(int jointNum) {return joints[jointNum];}
updJoint(const std::string & name)364     Joint& updJoint(const std::string& name) {return joints[jointName2Num[name]];}
365 
366     void initialize();
367     int splitBody(int bodyNum);
368     int chooseNewBaseBody() const;
369     void connectBodyToGround(int bodyNum);
370     int addMobilizerForJoint(int jointNum);
371     int findHeaviestUnassignedForwardJoint(int inboardBody) const;
372     int findHeaviestUnassignedReverseJoint(int inboardBody) const;
373     void growTree();
374     void breakLoops();
375     bool bodiesAreConnected(int b1, int b2) const;
376 
377     // Clear everything except for default names.
clear()378     void clear() {
379         bodies.clear(); joints.clear(); jointTypes.clear();
380         bodyName2Num.clear(); jointName2Num.clear(); jointTypeName2Num.clear();
381         mobilizers.clear(); constraints.clear();
382     }
383 
384     std::string                 weldTypeName, freeTypeName;
385     std::vector<Body>           bodies; // ground + input bodies + slaves
386     std::vector<Joint>          joints; // input joints + added joints
387     std::vector<JointType>      jointTypes;
388     std::map<std::string,int>   bodyName2Num;
389     std::map<std::string,int>   jointName2Num;
390     std::map<std::string,int>   jointTypeName2Num;
391 
392     // Calculated by generateGraph()
393     std::vector<Mobilizer>      mobilizers; // mobilized bodies
394     std::vector<LoopConstraint> constraints;
395 };
396 
397 //------------------------------------------------------------------------------
398 //                      MULTIBODY GRAPH MAKER :: BODY
399 //------------------------------------------------------------------------------
400 /** Local class that collects information about bodies. **/
401 class MultibodyGraphMaker::Body {
402 public:
Body(const std::string & name,double mass,bool mustBeBaseBody,void * userRef)403     explicit Body(const std::string&    name,
404                     double              mass,
405                     bool                mustBeBaseBody,
406                     void*               userRef)
407     :   name(name), mass(mass), mustBeBaseBody(mustBeBaseBody),
408         userRef(userRef), level(-1), mobilizer(-1), master(-1) {}
409 
410     void forgetGraph(MultibodyGraphMaker& graph);
getNumFragments()411     int getNumFragments() const {return 1 + getNumSlaves();}
getNumSlaves()412     int getNumSlaves() const {return (int)slaves.size();}
getNumJoints()413     int getNumJoints() const
414     {   return int(jointsAsChild.size() + jointsAsParent.size()); }
isSlave()415     bool isSlave() const {return master >= 0;}
isMaster()416     bool isMaster() const {return getNumSlaves()>0;}
isInTree()417     bool isInTree() const {return level>=0;}
418 
419     // Inputs
420     std::string name;
421     double      mass;
422     bool        mustBeBaseBody;
423     void*       userRef;
424 
425     // How this body appears in joints (input and added).
426     std::vector<int>    jointsAsChild;  // where this body is the child
427     std::vector<int>    jointsAsParent; // where this body is the parent
428 
429     // Disposition of this body in the spanning tree.
430 
431     int level; // Ground=0, connected to Ground=1, contact to that=2, etc.
432     int mobilizer; // the unique mobilizer where this is the outboard body
433 
434     int                 master; // >=0 if this is a slave
435     std::vector<int>    slaves; // slave links, if this is a master
436 };
437 
438 //------------------------------------------------------------------------------
439 //                      MULTIBODY GRAPH MAKER :: JOINT
440 //------------------------------------------------------------------------------
441 /** Local class that collects information about joints. **/
442 class MultibodyGraphMaker::Joint {
443 public:
Joint(const std::string & name,int jointTypeNum,int parentBodyNum,int childBodyNum,bool mustBeLoopJoint,void * userRef)444     Joint(const std::string& name, int jointTypeNum,
445           int parentBodyNum, int childBodyNum,
446           bool mustBeLoopJoint, void* userRef)
447     :   name(name),
448         mustBeLoopJoint(mustBeLoopJoint),
449         userRef(userRef),
450         parentBodyNum(parentBodyNum),
451         childBodyNum(childBodyNum),
452         jointTypeNum(jointTypeNum),
453         isAddedBaseJoint(false),
454         mobilizer(-1),
455         loopConstraint(-1) {}
456 
457     /** Return true if the joint is deleted as a result of restoring it
458         to the state prior to generateGraph(). **/
459     bool forgetGraph(MultibodyGraphMaker& graph);
460 
461     // Only one of these will be true -- we don't consider it a LoopConstraint
462     // if we split a body and weld it back.
hasMobilizer()463     bool hasMobilizer() const {return mobilizer>=0;}
hasLoopConstraint()464     bool hasLoopConstraint() const {return loopConstraint>=0;}
465 
466     // Inputs
467     std::string name;
468     bool        mustBeLoopJoint;
469     void*       userRef;
470 
471     // Mapping of strings to indices for fast lookup.
472     int parentBodyNum, childBodyNum;
473     int jointTypeNum;
474 
475     bool isAddedBaseJoint; // true if this wasn't one of the input joints
476 
477     // Disposition of this joint in the multibody graph.
478     int mobilizer;      // if this joint is part of the spanning tree, else -1
479     int loopConstraint; // if this joint used a loop constraint, else -1
480 };
481 
482 //------------------------------------------------------------------------------
483 //                   MULTIBODY GRAPH MAKER :: JOINT TYPE
484 //------------------------------------------------------------------------------
485 /** Local class that defines the properties of a known joint type. **/
486 class MultibodyGraphMaker::JointType {
487 public:
JointType(const std::string & name,int numMobilities,bool haveGoodLoopJointAvailable,void * userRef)488     JointType(const std::string& name, int numMobilities,
489               bool haveGoodLoopJointAvailable, void* userRef)
490     :   name(name), numMobilities(numMobilities),
491         haveGoodLoopJointAvailable(haveGoodLoopJointAvailable),
492         userRef(userRef) {}
493     std::string name;
494     int         numMobilities;
495     bool        haveGoodLoopJointAvailable;
496     void*       userRef;
497 };
498 
499 //------------------------------------------------------------------------------
500 //                   MULTIBODY GRAPH MAKER :: MOBILIZER
501 //------------------------------------------------------------------------------
502 /** Local class that represents one of the mobilizers (tree joints) in the
503 generated spanning tree. There is always a corresponding joint, although that
504 joint might be a ground-to-body free joint that was added automatically. **/
505 class MultibodyGraphMaker::Mobilizer {
506 public:
Mobilizer()507     Mobilizer()
508     :   joint(-1), level(-1), inboardBody(-1), outboardBody(-1),
509         isReversed(false), mgm(0) {}
Mobilizer(int jointNum,int level,int inboardBodyNum,int outboardBodyNum,bool isReversed,MultibodyGraphMaker * graphMaker)510     Mobilizer(int jointNum, int level, int inboardBodyNum, int outboardBodyNum,
511               bool isReversed, MultibodyGraphMaker* graphMaker)
512     :   joint(jointNum), level(level), inboardBody(inboardBodyNum),
513         outboardBody(outboardBodyNum), isReversed(isReversed),
514         mgm(graphMaker) {}
515 
516     /** Return true if this mobilizer does not represent one of the input
517     joints, but is instead a joint we added connecting a base body to ground.
518     If this returns true then there will be no user reference pointer returned
519     from getJointRef(). Also, the inboard body is always ground. When you
520     create this mobilizer, the joint frames should be identity, that is, the
521     joint should connect the ground frame to the outboard body frame. **/
isAddedBaseMobilizer()522     bool isAddedBaseMobilizer() const
523     {   return mgm->getJoint(joint).isAddedBaseJoint; }
524     /** Get the user reference pointer for the joint associated with this
525     mobilizer, if there is such a joint. If this mobilizer doesn't correspond
526     to one of the input joints then a null pointer is returned. **/
getJointRef()527     void* getJointRef() const
528     {   return mgm->getJoint(joint).userRef; }
529     /** Get the user reference pointer for the inboard body of this mobilizer.
530     The inboard body is always one of the input bodies so this will not be
531     returned null unless no reference pointer was supplied in the addBody()
532     call that defined this body. **/
getInboardBodyRef()533     void* getInboardBodyRef() const
534     {   return mgm->getBody(inboardBody).userRef; }
535     /** Get the user reference pointer for the outboard body of this mobilizer.
536     The outboard body may be one of the input bodies, but could also be a
537     slave body, in which case a null pointer will be returned. You can use
538     getOutboardMasterBodyRef() instead to ensure that you will get a reference
539     to one of the input bodies. **/
getOutboardBodyRef()540     void* getOutboardBodyRef() const
541     {   return mgm->getBody(outboardBody).userRef; }
542     /** Get the user reference pointer for the outboard body of this mobilizer,
543     if it is one of the input bodes, or to the master body for the outboard
544     body if the outboard body is a slave body. This ensures that you will get a
545     reference to one of the input bodies. **/
getOutboardMasterBodyRef()546     void* getOutboardMasterBodyRef() const
547     {   return mgm->getBody(getOutboardMasterBodyNum()).userRef; }
548     /** Get the joint type name of the joint that this mobilizer represents. **/
getJointTypeName()549     const std::string& getJointTypeName() const
550     {   return mgm->getJointType(mgm->getJoint(joint).jointTypeNum).name; }
551     /** Get the reference pointer (if any) that was provided when this
552     mobilizer's joint type was defined in an addJointType() call. **/
getJointTypeRef()553     void* getJointTypeRef() const
554     {   return mgm->getJointType(mgm->getJoint(joint).jointTypeNum).userRef; }
555     /** Return true if the outboard body of this mobilizer is a slave we
556     created in order to cut a loop, rather than one of the input bodies. **/
isSlaveMobilizer()557     bool isSlaveMobilizer() const
558     {   return mgm->getBody(outboardBody).isSlave(); }
559     /** Return the number of fragments into which we chopped the outboard body
560     of this mobilizer. There is one fragment for the master body plus however
561     many slaves of that body were created. Thus you should divide the master
562     body's mass by this number to obtain the mass to be assigned to each of
563     the body fragments. **/
getNumFragments()564     int getNumFragments() const
565     {   return mgm->getBody(getOutboardMasterBodyNum()).getNumFragments(); }
566     /** Return true if this mobilizer represents one of the input joints but
567     the sense of inboard->outboard is reversed from the parent->child sense
568     defined in the input joint. In that case you should use a reverse joint
569     when you build the system. **/
isReversedFromJoint()570     bool isReversedFromJoint() const {return isReversed;}
571     /** Return the level of the outboard body (Ground is level 0) **/
getLevel()572     int getLevel() const {return level;}
573 
574 private:
575 friend class MultibodyGraphMaker;
576 
getOutboardMasterBodyNum()577     int getOutboardMasterBodyNum() const
578     {   const Body& outb = mgm->getBody(outboardBody);
579         return outb.isSlave() ? outb.master : outboardBody; }
580 
581     int  joint;         ///< corresponding joint (not necessarily from input)
582     int  level;         ///< level of the outboard body; distance from ground
583     int  inboardBody;   ///< might be ground
584     int  outboardBody;  ///< might be a slave body; can't be ground
585     bool isReversed;    ///< if so, inboard=child, outboard=parent
586 
587     MultibodyGraphMaker*    mgm; // just a reference to container
588 };
589 
590 
591 //------------------------------------------------------------------------------
592 //                  MULTIBODY GRAPH MAKER :: LOOP CONSTRAINT
593 //------------------------------------------------------------------------------
594 /** Local class that represents one of the constraints that were added to close
595 topological loops that were cut to form the spanning tree. **/
596 class MultibodyGraphMaker::LoopConstraint {
597 public:
LoopConstraint()598     LoopConstraint() : joint(-1), parentBody(-1), childBody(-1), mgm(0) {}
LoopConstraint(const std::string & type,int jointNum,int parentBodyNum,int childBodyNum,MultibodyGraphMaker * graphMaker)599     LoopConstraint(const std::string& type, int jointNum,
600                    int parentBodyNum, int childBodyNum,
601                    MultibodyGraphMaker* graphMaker)
602     :   type(type), joint(jointNum),
603         parentBody(parentBodyNum), childBody(childBodyNum),
604         mgm(graphMaker) {}
605 
606     /** Get the user reference pointer for the joint associated with this
607     loop constraint. **/
getJointRef()608     void* getJointRef() const
609     {   return mgm->getJoint(joint).userRef; }
610     /** Get the loop constraint type name of the constraint that should be
611     used here. **/
getJointTypeName()612     const std::string& getJointTypeName() const
613     {   return type; }
614     /** Get the user reference pointer for the parent body defined by the
615     joint associated with this loop constraint. **/
getParentBodyRef()616     void* getParentBodyRef() const
617     {   return mgm->getBody(parentBody).userRef; }
618     /** Get the user reference pointer for the child body defined by the
619     joint associated with this loop constraint. **/
getChildBodyRef()620     void* getChildBodyRef() const
621     {   return mgm->getBody(childBody).userRef; }
622 
623 private:
624 friend class MultibodyGraphMaker;
625 
626     std::string type;        // e.g., ball
627     int         joint;       // always one of the input joints
628     int         parentBody;  // parent from the joint
629     int         childBody;   // child from the joint
630 
631     MultibodyGraphMaker*    mgm; // just a reference to container
632 };
633 
634 
635 } // namespace SimTK
636 
637 #endif // SimTK_SIMMATH_MULTIBODY_GRAPH_MAKER_H_
638 
639