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