1 /* -------------------------------------------------------------------------- *
2  *                     Simbody(tm): Multibody Graph Maker                     *
3  * -------------------------------------------------------------------------- *
4  * This is part of the SimTK biosimulation toolkit originating from           *
5  * Simbios, the NIH National Center for Physics-Based Simulation of           *
6  * Biological Structures at Stanford, funded under the NIH Roadmap for        *
7  * Medical Research, grant U54 GM072970. See https://simtk.org/home/simbody.  *
8  *                                                                            *
9  * Portions copyright (c) 2013-4 Stanford University and the Authors.         *
10  * Authors: Michael Sherman                                                   *
11  * Contributors: Kevin He                                                     *
12  *                                                                            *
13  * Licensed under the Apache License, Version 2.0 (the "License"); you may    *
14  * not use this file except in compliance with the License. You may obtain a  *
15  * copy of the License at http://www.apache.org/licenses/LICENSE-2.0.         *
16  *                                                                            *
17  * Unless required by applicable law or agreed to in writing, software        *
18  * distributed under the License is distributed on an "AS IS" BASIS,          *
19  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   *
20  * See the License for the specific language governing permissions and        *
21  * limitations under the License.                                             *
22  * -------------------------------------------------------------------------- */
23 
24 /* This is the implementation of MultibodyGraphMaker and its auxiliary classes.
25 */
26 
27 #include "SimTKcommon.h"
28 #include "simmath/internal/common.h"
29 #include "simmath/MultibodyGraphMaker.h"
30 
31 #include <exception>
32 #include <set>
33 #include <stdexcept>
34 #include <string>
35 #include <iostream>
36 #include <cstdio>
37 
38 using std::cout; using std::endl;
39 
40 #ifdef _MSC_VER
41 #pragma warning(disable:4996) // don't warn about strcat, sprintf, etc.
42 #endif
43 
44 namespace SimTK {
45 
46 //------------------------------------------------------------------------------
47 //                              CONSTRUCTOR
48 //------------------------------------------------------------------------------
MultibodyGraphMaker()49 MultibodyGraphMaker::MultibodyGraphMaker()
50 :   weldTypeName("weld"), freeTypeName("free")
51 {   initialize(); }
52 
53 
54 //------------------------------------------------------------------------------
55 //                          GET GROUND BODY NAME
56 //------------------------------------------------------------------------------
getGroundBodyName() const57 const std::string& MultibodyGraphMaker::getGroundBodyName() const {
58     if (bodies.empty())
59         throw std::logic_error(
60             "getGroundBodyName(): you can't call this until you have called "
61             "addBody() at least once -- the first body is Ground.");
62     return bodies[0].name;
63 }
64 
65 
66 //------------------------------------------------------------------------------
67 //                             ADD JOINT TYPE
68 //------------------------------------------------------------------------------
69 int MultibodyGraphMaker::
addJointType(const std::string & name,int numMobilities,bool haveGoodLoopJointAvailable,void * userRef)70 addJointType(const std::string&     name,
71              int                    numMobilities,
72              bool                   haveGoodLoopJointAvailable,
73              void*                  userRef)
74 {
75     if (!(0 <= numMobilities && numMobilities <= 6)) throw std::runtime_error
76        ("addJointType(): Illegal number of mobilities for joint type '"
77         + name + "'");
78 
79     // Reject duplicate type name, and reserved type names.
80     if (name==getWeldJointTypeName() || name==getFreeJointTypeName())
81         throw std::runtime_error("addJointType(): Joint type '" + name
82             + "' is reserved (you can change the reserved names).");
83     std::map<std::string,int>::const_iterator p = jointTypeName2Num.find(name);
84     if (p != jointTypeName2Num.end()) throw std::runtime_error
85        ("addJointType(): Duplicate joint type '" + name + "'");
86 
87     const int jointTypeNum = (int)jointTypes.size(); // next available
88     jointTypeName2Num[name] = jointTypeNum; // provide fast name lookup
89 
90     jointTypes.push_back(JointType(name, numMobilities,
91                                    haveGoodLoopJointAvailable, userRef));
92 
93     return jointTypeNum;
94 }
95 
96 
97 //------------------------------------------------------------------------------
98 //                                 ADD BODY
99 //------------------------------------------------------------------------------
addBody(const std::string & name,double mass,bool mustBeBaseBody,void * userRef)100 void MultibodyGraphMaker::addBody(const std::string& name,
101                                   double             mass,
102                                   bool               mustBeBaseBody,
103                                   void*              userRef)
104 {
105     if (mass < 0) throw std::invalid_argument
106         ("addBody(): Body '" + name + "' specified negative mass");
107 
108     // Reject duplicate body name.
109     std::map<std::string,int>::const_iterator p = bodyName2Num.find(name);
110     if (p != bodyName2Num.end()) throw std::runtime_error
111        ("addBody(): Duplicate body name '" + name + "'");
112 
113     const int bodyNum = (int)bodies.size(); // next available slot
114     bodyName2Num[name] = bodyNum; // provide fast name lookup
115 
116     if (bodyNum == 0) { // First body is Ground; use only the name and ref.
117         Body ground(name, Infinity, false, userRef);
118         ground.level = 0; // already in tree
119         bodies.push_back(ground);
120     } else { // This is a real body.
121         bodies.push_back(Body(name, mass, mustBeBaseBody, userRef));
122     }
123 }
124 
125 //------------------------------------------------------------------------------
126 //                                 DELETE BODY
127 //------------------------------------------------------------------------------
deleteBody(const std::string & name)128 bool MultibodyGraphMaker::deleteBody(const std::string& name)
129 {
130     // Reject non-existing body name.
131     std::map<std::string,int>::iterator p = bodyName2Num.find(name);
132     if (p == bodyName2Num.end())
133         return false;
134 
135     const int bodyNum = p->second;
136 
137     std::vector<int>& jointAsParent = updBody(bodyNum).jointsAsParent;
138     while (jointAsParent.size() > 0)
139         deleteJoint(joints[jointAsParent[0]].name);
140 
141     std::vector<int>& jointAsChild = updBody(bodyNum).jointsAsChild;
142     while (jointAsChild.size() > 0)
143         deleteJoint(joints[jointAsChild[0]].name);
144 
145     bodies.erase(bodies.begin() + bodyNum);
146     bodyName2Num.erase(p);
147 
148     // Update body indices due to the deletion of this body
149     for (unsigned int i = 0; i < joints.size(); ++i) {
150         if (joints[i].parentBodyNum > bodyNum)
151             --(updJoint(i).parentBodyNum);
152         if (joints[i].childBodyNum > bodyNum)
153             --(updJoint(i).childBodyNum);
154     }
155 
156     for (unsigned int i = bodyNum; i < bodies.size(); ++i)
157         bodyName2Num[bodies[i].name] = i;
158 
159     return true;
160 }
161 
162 //------------------------------------------------------------------------------
163 //                                ADD JOINT
164 //------------------------------------------------------------------------------
addJoint(const std::string & name,const std::string & type,const std::string & parentBodyName,const std::string & childBodyName,bool mustBeLoopJoint,void * userRef)165 void MultibodyGraphMaker::addJoint(const std::string&  name,
166                                    const std::string&  type,
167                                    const std::string&  parentBodyName,
168                                    const std::string&  childBodyName,
169                                    bool                mustBeLoopJoint,
170                                    void*               userRef)
171 {
172     // Reject duplicate joint name, unrecognized type or body names.
173     std::map<std::string,int>::const_iterator p = jointName2Num.find(name);
174     if (p != jointName2Num.end()) throw std::runtime_error
175        ("addJoint(): Duplicate joint name '" + name + "'");
176 
177     const int typeNum = getJointTypeNum(type);
178     if (typeNum < 0) throw std::runtime_error
179        ("addJoint(): Joint " + name + " had unrecognized joint type '"
180         + type + "'");
181 
182     const int parentBodyNum = getBodyNum(parentBodyName);
183     if (parentBodyNum < 0) throw std::runtime_error
184        ("addJoint(): Joint " + name + " had unrecognized parent body '"
185         + parentBodyName + "'");
186 
187     const int childBodyNum = getBodyNum(childBodyName);
188     if (childBodyNum < 0) throw std::runtime_error
189        ("addJoint(): Joint " + name + " had unrecognized child body '"
190         + childBodyName + "'");
191 
192     const int jointNum = (int)joints.size(); // next available slot
193     jointName2Num[name] = jointNum; // provide fast name lookup
194 
195     joints.push_back(Joint(name, typeNum, parentBodyNum, childBodyNum,
196                            mustBeLoopJoint, userRef));
197 
198     updBody(parentBodyNum).jointsAsParent.push_back(jointNum);
199     updBody(childBodyNum).jointsAsChild.push_back(jointNum);
200 }
201 
202 //------------------------------------------------------------------------------
203 //                                DELETE JOINT
204 //------------------------------------------------------------------------------
deleteJoint(const std::string & name)205 bool MultibodyGraphMaker::deleteJoint(const std::string&  name)
206 {
207     // Reject duplicate joint name, unrecognized type or body names.
208     std::map<std::string,int>::iterator p = jointName2Num.find(name);
209     if (p == jointName2Num.end())
210         return false;
211 
212     const int jointNum = p->second;
213     jointName2Num.erase(p);
214 
215     std::vector<int>& jointsAsParent =
216         updBody(joints[jointNum].parentBodyNum).jointsAsParent;
217     std::vector<int>::iterator it =
218         std::find(jointsAsParent.begin(), jointsAsParent.end(), jointNum);
219     if (it == jointsAsParent.end()) throw std::runtime_error
220         ("deleteJoint(): Joint " + name +
221          " doesn't exist in jointsAsParent of parent body ");
222     jointsAsParent.erase(it);
223 
224     std::vector<int>& jointsAsChild =
225         updBody(joints[jointNum].childBodyNum).jointsAsChild;
226     it = std::find(jointsAsChild.begin(), jointsAsChild.end(), jointNum);
227     if (it == jointsAsChild.end()) throw std::runtime_error
228         ("deleteJoint(): Joint " + name +
229          " doesn't exist in jointsAsChild of child body ");
230     jointsAsChild.erase(it);
231 
232     joints.erase(joints.begin() + jointNum);
233 
234     // Update indices due to the deletion of this joint
235     for (unsigned int i = 0; i < bodies.size(); ++i) {
236 
237         for (it = updBody(i).jointsAsParent.begin(); it != updBody(i).jointsAsParent.end(); ++it)
238             if (*it > jointNum) {
239                 --(*it);
240                 jointName2Num[joints[*it].name] = *it;
241             }
242 
243         for (it = updBody(i).jointsAsChild.begin(); it != updBody(i).jointsAsChild.end(); ++it)
244             if (*it > jointNum) {
245                 --(*it);
246                 // no need to adjust jointName2Num here since the first loops has done it already
247             }
248 
249     }
250     return true;
251 }
252 
253 //------------------------------------------------------------------------------
254 //                              GENERATE GRAPH
255 //------------------------------------------------------------------------------
generateGraph()256 void MultibodyGraphMaker::generateGraph() {
257     // Body and Joint inputs have been supplied. Body 0 is Ground.
258 
259     // 1. Add free joints to Ground for any body that didn't appear
260     // in any joint, and for any bodies that said they must be base bodies and
261     // don't already have a connection to Ground.
262     //
263     // While we're at it, look for dangling massless bodies -- they are only
264     // allowed if they were originally connected to at least two other bodies
265     // by given tree-eligible joints, or if they are connected by a weld
266     // joint and thus have no mobility.
267     // TODO: "must be loop joint" joints shouldn't count but we're not
268     // checking.
269     for (int bn=1; bn < getNumBodies(); ++bn) { // skip Ground
270         const Body& body = getBody(bn);
271         const int nJoints = body.getNumJoints();
272         if (body.mass == 0) {
273             if (nJoints == 0) {
274                 throw std::runtime_error(
275                     "generateGraph(): body " + body.name +
276                     " is massless but free (no joint). Must be internal or"
277                     " welded to a massful body.");
278             }
279             if (nJoints == 1) {
280                 const int jnum = body.jointsAsChild.empty()
281                                     ? body.jointsAsParent[0]
282                                     : body.jointsAsChild[0];
283                 const Joint& joint = getJoint(jnum);
284                 const JointType& jtype = getJointType(joint.jointTypeNum);
285                 if (jtype.numMobilities > 0) {
286                     throw std::runtime_error(
287                         "generateGraph(): body " + body.name +
288                         " is massless but not internal and not welded to"
289                         " a massful body.");
290                 }
291             }
292         }
293         if (nJoints==0 || (body.mustBeBaseBody && !bodiesAreConnected(bn, 0)))
294             connectBodyToGround(bn);
295     }
296 
297     // 2. Repeat until all bodies are in the tree:
298     //   - try to build the tree from Ground outwards
299     //   - if incomplete, add one missing connection to Ground
300     // This terminates because we add at least one body to the tree each time.
301     while (true) {
302         growTree();
303         const int newBaseBody = chooseNewBaseBody();
304         if (newBaseBody < 0)
305             break; // all bodies are in the tree
306         // Add joint to Ground.
307         connectBodyToGround(newBaseBody);
308     }
309 
310    // 3. Split the loops
311    // This requires adding new "slave" bodies to replace the child bodies in
312    // the loop-forming joints. Then each slave is welded back to its master
313    // body (the original child).
314    breakLoops();
315 }
316 
317 
318 //------------------------------------------------------------------------------
319 //                              CLEAR GRAPH
320 //------------------------------------------------------------------------------
clearGraph()321 void MultibodyGraphMaker::clearGraph() {
322     for (int bn=1; bn < getNumBodies(); ++bn) {  // skip Ground
323         if (bodies[bn].isSlave()) {
324             // Assumption: all slave bodies are clustered at the end of the body array
325             bodies.erase(bodies.begin() + bn, bodies.begin() + bodies.size());
326             break;
327         }
328         updBody(bn).forgetGraph(*this);
329     }
330 
331     for (int jn=0; jn < getNumJoints(); ++jn)
332         if (updJoint(jn).forgetGraph(*this))
333             --jn;
334 
335     mobilizers.clear();
336     constraints.clear();
337 }
338 
339 //------------------------------------------------------------------------------
340 //                               DUMP GRAPH
341 //------------------------------------------------------------------------------
dumpGraph(std::ostream & o) const342 void MultibodyGraphMaker::dumpGraph(std::ostream& o) const {
343     char buf[1024];
344     o << "\nMULTIBODY GRAPH\n";
345     o <<   "---------------\n";
346     o << "\n" << getNumBodies() << " BODIES:\n";
347     for (int i=0; i < getNumBodies(); ++i) {
348         const MultibodyGraphMaker::Body& body  = getBody(i);
349         sprintf(buf, "%2d %2d: %s mass=%g mob=%d master=%d %s\n",
350             i, body.level,
351             body.name.c_str(), body.mass, body.mobilizer, body.master,
352             body.mustBeBaseBody?"MUST BE BASE BODY":"");
353         o << buf;
354         o << "  jointsAsParent=[";
355         for (unsigned j=0; j<body.jointsAsParent.size(); ++j)
356             o << " " << body.jointsAsParent[j];
357         o << "]\t  jointsAsChild=[";
358         for (unsigned j=0; j<body.jointsAsChild.size(); ++j)
359             o << " " << body.jointsAsChild[j];
360         o << "]\t  slaves=[";
361         for (unsigned j=0; j<body.slaves.size(); ++j)
362             o << " " << body.slaves[j];
363         o << "]\n";
364     }
365 
366     o << "\n" << getNumJoints() << " JOINTS:\n";
367     for (int i=0; i < getNumJoints(); ++i) {
368         const MultibodyGraphMaker::Joint& joint = getJoint(i);
369         sprintf(buf, "%2d %2d: %20s %20s->%-20s %10s loopc=%2d %s %s\n",
370             i, joint.mobilizer, joint.name.c_str(),
371             getBody(joint.parentBodyNum).name.c_str(),
372             getBody(joint.childBodyNum).name.c_str(),
373             getJointType(joint.jointTypeNum).name.c_str(),
374             joint.loopConstraint,
375             joint.mustBeLoopJoint?"MUST BE LOOP":"",
376             joint.isAddedBaseJoint?"ADDED BASE JOINT":"");
377         o << buf;
378     }
379 
380     o << "\n" << getNumMobilizers() << " MOBILIZERS:\n";
381     for (int i=0; i < getNumMobilizers(); ++i) {
382         const MultibodyGraphMaker::Mobilizer& mo    = getMobilizer(i);
383         const MultibodyGraphMaker::Joint&     joint = getJoint(mo.joint);
384         const MultibodyGraphMaker::Body&      inb   = getBody(mo.inboardBody);
385         const MultibodyGraphMaker::Body&      outb  = getBody(mo.outboardBody);
386         sprintf(buf, "%2d %2d: %20s %20s->%-20s %10s %2d %3s\n",
387             i, mo.level, joint.name.c_str(),
388             inb.name.c_str(), outb.name.c_str(),
389             mo.getJointTypeName().c_str(),
390             mo.joint, mo.isReversed?"REV":"");
391         o << buf;
392     }
393 
394     o << "\n" << getNumLoopConstraints() << " LOOP CONSTRAINTS:\n";
395     for (int i=0; i < getNumLoopConstraints(); ++i) {
396         const MultibodyGraphMaker::LoopConstraint& lc = getLoopConstraint(i);
397         const MultibodyGraphMaker::Body parent = getBody(lc.parentBody);
398         const MultibodyGraphMaker::Body child  = getBody(lc.childBody);
399         sprintf(buf, "%d: %s parent=%s child=%s jointNum=%d\n",
400             i, lc.type.c_str(), parent.name.c_str(), child.name.c_str(),
401             lc.joint);
402         o << buf;
403     }
404     o << "\n----- END OF MULTIBODY GRAPH.\n\n";
405 }
406 
407 
408 //------------------------------------------------------------------------------
409 //                               INITIALIZE
410 //------------------------------------------------------------------------------
411 // Clear all data and prime with free and weld joint types.
initialize()412 void MultibodyGraphMaker::initialize() {
413     clear();
414     jointTypes.push_back(JointType(weldTypeName, 0, true, NULL));
415     jointTypes.push_back(JointType(freeTypeName, 6, true, NULL));
416     jointTypeName2Num[weldTypeName] = 0;
417     jointTypeName2Num[freeTypeName] = 1;
418 }
419 
420 
421 //------------------------------------------------------------------------------
422 //                                SPLIT BODY
423 //------------------------------------------------------------------------------
424 // Create a new slave body for the given master, and add it to the list of
425 // bodies. Does not create the related loop constraint. The body
426 // number assigned to the slave is returned.
splitBody(int masterBodyNum)427 int MultibodyGraphMaker::splitBody(int masterBodyNum) {
428     const int slaveBodyNum = (int)bodies.size(); // next available
429     Body& master = updBody(masterBodyNum);
430     // First slave is number 1, slave 0 is the master.
431     std::stringstream ss;
432     ss << "#" << master.name << "_slave_" << master.getNumSlaves()+1;
433     Body slave(ss.str(), NaN, false, NULL);
434     slave.master = masterBodyNum;
435     master.slaves.push_back(slaveBodyNum);
436     bodies.push_back(slave);
437     // No name lookup for slave bodies.
438     return slaveBodyNum;
439 }
440 
441 
442 //------------------------------------------------------------------------------
443 //                          CHOOSE NEW BASE BODY
444 //------------------------------------------------------------------------------
445 // We've tried to build the tree but might not have succeeded in using
446 // all the bodies. That means we'll have to connect one of them to Ground.
447 // Select the best unattached body to use as a base body, or -1 if all bodies
448 // are already included in the spanning tree. We hope to find a body that is
449 // serving as a parent but has never been listed as a child; that is crying out
450 // to be connected directly to Ground. Failing that, we'll pick a body that
451 // has been used as a parent often.
chooseNewBaseBody() const452 int MultibodyGraphMaker::chooseNewBaseBody() const {
453     bool parentOnlyBodySeen = false;
454     int bestBody = -1; int nChildren=-1;
455     // Skip the Ground body.
456     for (int bx=1; bx < getNumBodies(); ++bx) {
457         const Body& body = bodies[bx];
458         if (body.isInTree())
459             continue; // unavailable
460         if (parentOnlyBodySeen && !body.jointsAsChild.empty())
461             continue; // already seen parent-only bodies; no need for this one
462         if (!parentOnlyBodySeen && body.jointsAsChild.empty()) {
463             // This is our first parent-only body; it is automatically the
464             // best candidate now.
465             parentOnlyBodySeen = true;
466             bestBody = bx; nChildren = (int)body.jointsAsParent.size();
467         } else { // Keep the body that has the most children.
468             if ((int)body.jointsAsParent.size() > nChildren) {
469                 bestBody  = bx;
470                 nChildren = (int)body.jointsAsParent.size();
471             }
472         }
473     }
474     return bestBody;
475 }
476 
477 
478 //------------------------------------------------------------------------------
479 //                          CONNECT BODY TO GROUND
480 //------------------------------------------------------------------------------
481 // Connect the given body to Ground by a free joint with parent Ground and
482 // child the given body. This makes the body a base body (level 1 body)
483 // for some subtree of the multibody graph.
connectBodyToGround(int bodyNum)484 void MultibodyGraphMaker::connectBodyToGround(int bodyNum) {
485     const Body& body = getBody(bodyNum);
486     assert(!body.isInTree());
487     const std::string jointName = "#" + getGroundBodyName() + "_" + body.name;
488     addJoint(jointName, getFreeJointTypeName(),
489                     getGroundBodyName(), body.name, false, NULL);
490     updJoint(jointName).isAddedBaseJoint = true;
491 }
492 
493 
494 //------------------------------------------------------------------------------
495 //                          ADD MOBILIZER FOR JOINT
496 //------------------------------------------------------------------------------
497 // We've already determined this joint is eligible to become a mobilizer; now
498 // make it so. Given a joint for which either the parent or child is in the
499 // tree, but not both, create a mobilizer implementing this joint and attaching
500 // the unattached body (which will be the mobilizer's outboard body) to the
501 // tree. The mobilizer number is returned and also recorded in the joint
502 // and outboard body.
addMobilizerForJoint(int jointNum)503 int MultibodyGraphMaker::addMobilizerForJoint(int jointNum) {
504     Joint& joint = updJoint(jointNum);
505     assert(!joint.mustBeLoopJoint);   // can't be a tree joint
506     assert(!joint.hasMobilizer());      // already done
507     const int pNum = joint.parentBodyNum, cNum = joint.childBodyNum;
508     Body& parent = updBody(pNum);
509     Body& child  = updBody(cNum);
510     // Exactly one of these must already be in the tree.
511     assert(parent.isInTree() ^ child.isInTree());
512 
513     const int mobNum = (int)mobilizers.size(); // next available
514     if (parent.isInTree()) {
515         // Child is outboard body (forward joint)
516         child.level = parent.level + 1;
517         mobilizers.push_back(Mobilizer(jointNum,child.level,
518                                        pNum,cNum,false,this));
519         child.mobilizer = mobNum;
520     } else if (child.isInTree()) {
521         // Parent is outboard body (reverse joint)
522         parent.level = child.level + 1;
523         mobilizers.push_back(Mobilizer(jointNum,parent.level,
524                                        cNum,pNum,true,this));
525         parent.mobilizer = mobNum;
526     }
527     joint.mobilizer = mobNum;
528     return mobNum;
529 }
530 
531 
532 //------------------------------------------------------------------------------
533 //                   FIND HEAVIEST UNASSIGNED FORWARD JOINT
534 //------------------------------------------------------------------------------
535 // Starting with a given body that is in the tree already, look at its
536 // unassigned children (meaning bodies connected by joints that aren't
537 // mobilizers yet) and return the joint connecting it to the child with the
538 // largest mass. (The idea is that this would be a good direction to extend the
539 // chain.) Return -1 if this body has no children.
540 int MultibodyGraphMaker::
findHeaviestUnassignedForwardJoint(int inboardBody) const541 findHeaviestUnassignedForwardJoint(int inboardBody) const {
542     const Body& inb = getBody(inboardBody);
543     int jointNum = -1;
544     double maxMass=0;
545     // Search joints for which this is the parent body.
546     for (unsigned i=0; i < inb.jointsAsParent.size(); ++i) {
547         const int jfwd = inb.jointsAsParent[i];
548         const Joint& joint = joints[jfwd];
549         if (joint.hasMobilizer()) continue; // already in the tree
550         if (joint.mustBeLoopJoint) continue; // can't be a tree joint
551         const Body& child = getBody(joint.childBodyNum);
552         if (child.isInTree()) continue; // this is a loop joint
553         if (child.mass > maxMass) {
554             jointNum = jfwd;
555             maxMass = child.mass;
556         }
557     }
558     return jointNum;
559 }
560 
561 
562 //------------------------------------------------------------------------------
563 //                   FIND HEAVIEST UNASSIGNED REVERSE JOINT
564 //------------------------------------------------------------------------------
565 // Same as previous method, but now we are looking for unassigned joints where
566 // the given body serves as the child so we would have to reverse the joint to
567 // add it to the tree. (This is less desirable so is a fallback.)
568 int MultibodyGraphMaker::
findHeaviestUnassignedReverseJoint(int inboardBody) const569 findHeaviestUnassignedReverseJoint(int inboardBody) const {
570     const Body& inb = getBody(inboardBody);
571     int jointNum = -1;
572     double maxMass=0;
573     // Search joints for which this is the child body.
574     for (unsigned i=0; i < inb.jointsAsChild.size(); ++i) {
575         const int jrev = inb.jointsAsChild[i];
576         const Joint& joint = joints[jrev];
577         if (joint.hasMobilizer()) continue; // already in the tree
578         if (joint.mustBeLoopJoint) continue; // can't be a tree joint
579         const Body& parent = getBody(joint.parentBodyNum);
580         if (parent.isInTree()) continue; // this is a loop joint
581         if (parent.mass > maxMass) {
582             jointNum = jrev;
583             maxMass = parent.mass;
584         }
585     }
586     return jointNum;
587 }
588 
589 
590 //------------------------------------------------------------------------------
591 //                                 GROW TREE
592 //------------------------------------------------------------------------------
593 // Process unused joints for which one body is in the tree (at level h) and the
594 // other is not. Add the other body to the tree at level h+1, marking the joint
595 // as forward (other body is child) or reverse (other body is parent). Repeat
596 // until no changes are made. Does not assign loop joints or any bodies that
597 // don't have a path to Ground. Extend the multibody tree starting with the
598 // lowest-level eligible joints and moving outwards. We're doing this
599 // breadth-first so that we get roughly even-length chains and we'll stop at
600 // the first level at which we fail to find any joint. If we fail to consume
601 // all the bodies, the caller will have to add a level 1 joint to attach a
602 // previously-disconnected base body to Ground and then call this again.
603 //
604 // We violate the breadth-first heuristic to avoid ending a branch with a
605 // massless body, unless it is welded to its parent. If we add a mobile massless
606 // body, we'll keep going out along its branch until we hit a massful body. It
607 // is a disaster if we fail to find a massful body because a tree that
608 // terminates in a mobile massless body will generate a singular mass matrix.
609 // We'll throw an exception in that case, but note that this may just be a
610 // failure of the heuristic; there may be some tree that could have avoided the
611 // terminal massless body but we failed to discover it.
612 //
613 // TODO: keep a list of unprocessed joints so we don't have to go through
614 // all of them again at each level.
growTree()615 void MultibodyGraphMaker::growTree() {
616     // Record the joints for which we added mobilizers during this subtree
617     // sweep. That way if we jumped levels ahead due to massless bodies we
618     // can take credit and keep going rather than quit.
619     std::set<int> jointsAdded;
620     for (int level=1; ;++level) { // level of outboard (mobilized) body
621         bool anyMobilizerAdded = false;
622         for (int jNum=0; jNum<getNumJoints(); ++jNum) {
623             // See if this joint is at the right level (meaning its inboard
624             // body is at level-1) and is available to become a mobilizer.
625             const Joint& joint = getJoint(jNum);
626             if (joint.hasMobilizer()) {
627                 // Already done -- one of ours?
628                 if (jointsAdded.count(jNum)) {
629                     // We added it during this growTree() call -- is it at
630                     // the current level?
631                     const Mobilizer& mob = mobilizers[joint.mobilizer];
632                     if (mob.getLevel() == level)
633                         anyMobilizerAdded = true; // Added one at level.
634                 }
635                 continue;
636             }
637             if (joint.mustBeLoopJoint) continue; // can't be a tree joint
638             const Body& parent = getBody(joint.parentBodyNum);
639             const Body& child  = getBody(joint.childBodyNum);
640             // Exactly one of parent or child must already be in the tree.
641             if (!(parent.isInTree() ^ child.isInTree())) continue;
642             if (parent.isInTree()) {
643                 if (parent.level != level-1) continue; // not time yet
644             } else { // child is in tree
645                 if (child.level != level-1) continue; // not time yet
646             }
647             addMobilizerForJoint(jNum);
648             jointsAdded.insert(jNum);
649             anyMobilizerAdded = true;
650 
651             // We just made joint jNum a mobilizer. If its outboard body
652             // is massless and the mobilizer was not a weld, we need to keep
653             // extending this branch of the tree until we can end it with a
654             // massful body.
655             const Body& outboard = getBody(mobilizers.back().outboardBody);
656             const JointType& jtype = getJointType(joint.jointTypeNum);
657             if (jtype.numMobilities == 0 || outboard.mass > 0)
658                 continue;
659 
660             // Pick a further-outboard body with mass and extend branch to it.
661             // Prefer forward-direction joints if possible.
662             // If only massless outboard bodies are available, add one and
663             // keep going.
664             while (true) {
665                 const int bNum = mobilizers.back().outboardBody;
666                 const int jfwd = findHeaviestUnassignedForwardJoint(bNum);
667                 if (jfwd>=0 && getBody(getJoint(jfwd).childBodyNum).mass > 0) {
668                     addMobilizerForJoint(jfwd);
669                     jointsAdded.insert(jfwd);
670                     break;
671                 }
672                 const int jrev = findHeaviestUnassignedReverseJoint(bNum);
673                 if (jrev>=0 && getBody(getJoint(jrev).parentBodyNum).mass > 0) {
674                     addMobilizerForJoint(jrev);
675                     jointsAdded.insert(jrev);
676                     break;
677                 }
678 
679                 // Couldn't find a massful body to add. Add another massless
680                 // body (if there is one) and keep trying.
681                 if (jfwd >= 0) {
682                     addMobilizerForJoint(jfwd);
683                     jointsAdded.insert(jfwd);
684                     continue;
685                 }
686                 if (jrev >= 0) {
687                     addMobilizerForJoint(jrev);
688                     jointsAdded.insert(jrev);
689                     continue;
690                 }
691 
692                 // Uh oh. Nothing outboard of the massless body we just added.
693                 // We're ending this chain with a massless body; not good.
694                 throw std::runtime_error("growTree(): algorithm produced an"
695                     " invalid tree containing a terminal massless body ("
696                     + getBody(bNum).name + "). This may be due to an invalid"
697                     " model or failure of heuristics. In the latter case you"
698                     " may be able to get a valid tree by forcing a different"
699                     " loop break or changing parent->child ordering.");
700             }
701         }
702         if (!anyMobilizerAdded)
703             break;
704     }
705 }
706 
707 
708 //------------------------------------------------------------------------------
709 //                                 BREAK LOOPS
710 //------------------------------------------------------------------------------
711 // Find any remaining unused joints, which will have both parent and
712 // child bodies already in the tree. This includes joints that the user
713 // told us must be loop joints, and ones picked by the algorithm.
714 // For each of those, implement the joint with a loop constraint if one
715 // is provided, otherwise implement it with a mobilizer and split off a
716 // slave body from the child body, use that slave as the outboard body
717 // for the mobilizer, and add a weld constraint to reconnect the slave to
718 // its master (the original child body).
breakLoops()719 void MultibodyGraphMaker::breakLoops() {
720     for (int jx=0; jx < getNumJoints(); ++jx) {
721         Joint& jinfo = joints[jx];
722         if (jinfo.hasMobilizer()) continue; // already done
723         const int px = jinfo.parentBodyNum, cx = jinfo.childBodyNum;
724         assert(getBody(px).isInTree() && getBody(cx).isInTree());
725 
726         const JointType& jtype = getJointType(jinfo.jointTypeNum);
727         if (jtype.haveGoodLoopJointAvailable) {
728             const int loopNum = (int)constraints.size();
729             constraints.push_back(LoopConstraint(jtype.name,jx,px,cx,this));
730             jinfo.loopConstraint = loopNum;
731             continue;
732         }
733 
734         // No usable loop constraint for this type of joint. Add a new slave
735         // body so we can use a mobilizer. (Body references are invalidated here
736         // since we're adding a new body -- don't reuse them.)
737         const int sx = splitBody(cx);
738         Body& parent = updBody(px);
739         Body& child  = updBody(cx);
740         Body& slave  = updBody(sx);
741 
742         // Mobilize the slave body.
743         const int mobNum = (int)mobilizers.size(); // next available
744         const int level = parent.level+1;
745         jinfo.mobilizer = slave.mobilizer = mobNum;
746         slave.jointsAsChild.push_back(jx);
747         mobilizers.push_back(Mobilizer(jx,level,px,sx,false,this));
748         slave.level = level;
749     }
750 }
751 
752 
753 //------------------------------------------------------------------------------
754 //                         BODIES ARE CONNECTED
755 //------------------------------------------------------------------------------
756 // Return true if there is a joint between two bodies given by body number,
757 // regardless of parent/child ordering.
bodiesAreConnected(int b1,int b2) const758 bool MultibodyGraphMaker::bodiesAreConnected(int b1, int b2) const {
759     const Body& body1 = getBody(b1);
760     for (unsigned i=0; i < body1.jointsAsParent.size(); ++i)
761         if (getJoint(body1.jointsAsParent[i]).childBodyNum == b2)
762             return true;
763     for (unsigned i=0; i < body1.jointsAsChild.size(); ++i)
764         if (getJoint(body1.jointsAsChild[i]).parentBodyNum == b2)
765             return true;
766     return false;
767 }
768 
769 //------------------------------------------------------------------------------
770 //                         FORGET GRAPH
771 //------------------------------------------------------------------------------
772 // Restore the Body to its state prior to generateGraph()
forgetGraph(MultibodyGraphMaker & graph)773 void MultibodyGraphMaker::Body::forgetGraph(MultibodyGraphMaker& graph)
774 {
775     level = -1;
776     mobilizer = -1;
777     master = -1;
778     slaves.clear();
779 }
780 
781 //------------------------------------------------------------------------------
782 //                         FORGET GRAPH
783 //------------------------------------------------------------------------------
784 // Restore the Joint to its state prior to generateGraph()
forgetGraph(MultibodyGraphMaker & graph)785 bool MultibodyGraphMaker::Joint::forgetGraph(MultibodyGraphMaker& graph)
786 {
787     if (isAddedBaseJoint) {
788         graph.deleteJoint(name);
789         return true;
790     }
791     mobilizer = -1;
792     loopConstraint = -1;
793     return false;
794 }
795 } // namespace SimTK
796 
797