1 #ifndef SimTK_SIMBODY_MOBILIZED_BODY_IMPL_H_
2 #define SimTK_SIMBODY_MOBILIZED_BODY_IMPL_H_
3
4 /* -------------------------------------------------------------------------- *
5 * Simbody(tm) *
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) 2007-12 Stanford University and the Authors. *
13 * Authors: Michael Sherman *
14 * Contributors: *
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 *
29 * Private implementation of Body and MobilizedBody, and their included
30 * subclasses which represent the built-in body and mobilizer types.
31 */
32
33 #include "SimTKcommon.h"
34 #include "simbody/internal/common.h"
35 #include "simbody/internal/Body.h"
36 #include "simbody/internal/MobilizedBody.h"
37 #include "simbody/internal/MobilizedBody_BuiltIns.h"
38
39 #include "SimbodyMatterSubsystemRep.h"
40 #include "BodyRep.h"
41 #include "MotionImpl.h"
42
43 class RigidBodyNode;
44
45 namespace SimTK {
46
47 /////////////////////////
48 // MOBILIZED BODY REPS //
49 /////////////////////////
50
51 // This is what a MobilizedBody handle points to.
52 class MobilizedBodyImpl : public PIMPLImplementation<MobilizedBody,MobilizedBodyImpl> {
53 public:
MobilizedBodyImpl(MobilizedBody::Direction d)54 explicit MobilizedBodyImpl(MobilizedBody::Direction d)
55 : defaultLockLevel(Motion::NoLevel),
56 reversed(d==MobilizedBody::Reverse),
57 myMatterSubsystemRep(0), myLevel(-1), myRBnode(0), hasChildren(false)
58 {
59 }
60
setDirection(MobilizedBody::Direction d)61 void setDirection(MobilizedBody::Direction d) {
62 const bool wantReversed = (d==MobilizedBody::Reverse);
63 if (wantReversed != reversed) {
64 invalidateTopologyCache();
65 reversed = wantReversed;
66 }
67 }
68
MobilizedBodyImpl(const MobilizedBodyImpl & src)69 MobilizedBodyImpl(const MobilizedBodyImpl& src) {
70 *this = src;
71 myRBnode = 0;
72 }
73
74
75 void lock(State& state, Motion::Level level) const;
76 void lockAt(State& state, int n, const Real* value, Motion::Level level) const;
77 void unlock(State& state) const;
78 Motion::Level getLockLevel(const State& state) const;
79 Vector getLockValueAsVector(const State& state) const;
lockByDefault(Motion::Level level)80 void lockByDefault(Motion::Level level)
81 { invalidateTopologyCache(); defaultLockLevel=level; }
getLockByDefaultLevel()82 Motion::Level getLockByDefaultLevel() const
83 { return defaultLockLevel; }
84
85 // The matter subsystem must issue these MobilizedBody realize() calls in base-to-tip
86 // order, because these methods are allowed to assume that their parent (and
87 // ancestors) have already been realized.
88
89 // eventually calls realizeTopologyVirtual()
90 const RigidBodyNode& realizeTopology(State& s, UIndex& nxtU, USquaredIndex& nxtUSq, QIndex& nxtQ) const;
91
92 void realizeModel (State&) const; // eventually calls realizeModelVirtual()
93 void realizeInstance(const SBStateDigest&) const; // eventually calls realizeInstanceVirtual()
94 void realizeTime (const SBStateDigest&) const; // eventually calls realizeTimeVirtual()
95 void realizePosition(const SBStateDigest&) const; // eventually calls realizePositionVirtual()
96 void realizeVelocity(const SBStateDigest&) const; // eventually calls realizeVelocityVirtual()
97 void realizeDynamics(const SBStateDigest&) const; // eventually calls realizeDynamicsVirtual()
98 void realizeAcceleration
99 (const SBStateDigest&) const; // eventually calls realizeAccelerationVirtual()
100 void realizeReport (const SBStateDigest&) const; // eventually calls realizeReportVirtual()
101
~MobilizedBodyImpl()102 virtual ~MobilizedBodyImpl() {
103 delete myRBnode;
104 }
105 virtual MobilizedBodyImpl* clone() const = 0;
106
107 // This creates a rigid body node using the appropriate mobilizer type.
108 // Called during realizeTopology().
109 virtual RigidBodyNode* createRigidBodyNode(
110 UIndex& nextUSlot,
111 USquaredIndex& nextUSqSlot,
112 QIndex& nextQSlot) const = 0;
113
realizeTopologyVirtual(State &)114 virtual void realizeTopologyVirtual (State&) const {}
realizeModelVirtual(State &)115 virtual void realizeModelVirtual (State&) const {}
realizeInstanceVirtual(const State &)116 virtual void realizeInstanceVirtual (const State&) const {}
realizeTimeVirtual(const State &)117 virtual void realizeTimeVirtual (const State&) const {}
realizePositionVirtual(const State &)118 virtual void realizePositionVirtual (const State&) const {}
realizeVelocityVirtual(const State &)119 virtual void realizeVelocityVirtual (const State&) const {}
realizeDynamicsVirtual(const State &)120 virtual void realizeDynamicsVirtual (const State&) const {}
realizeAccelerationVirtual(const State &)121 virtual void realizeAccelerationVirtual (const State&) const {}
realizeReportVirtual(const State &)122 virtual void realizeReportVirtual (const State&) const {}
123
124 // Copy out nq default values for q, beginning at the indicated address.
125 // The concrete class should assert if nq is not a reasonable
126 // number for the kind of mobilizer; there is a bug somewhere in that case.
127 // This routine shouldn't be called directly -- call copyOutDefaultQ() below
128 // instead which has a nicer interface and does some error checking.
129 virtual void copyOutDefaultQImpl(int nq, Real* q) const = 0;
130
calcDecorativeGeometryAndAppendImpl(const State & s,Stage stage,Array_<DecorativeGeometry> & geom)131 virtual void calcDecorativeGeometryAndAppendImpl
132 (const State& s, Stage stage, Array_<DecorativeGeometry>& geom) const
133 {
134 if (stage != Stage::Instance || !getMyMatterSubsystemRep().getShowDefaultGeometry())
135 return;
136 const Real scale = 1;
137 DecorativeFrame axes(scale/2);
138 axes.setLineThickness(2);
139 axes.setBodyId(myMobilizedBodyIndex);
140 geom.push_back(axes); // the body frame
141
142 // Display the inboard joint frame (at half size), unless it is the
143 // same as the body frame. Then find the corresponding frame on the
144 // parent and display that in this body's color.
145 if (myMobilizedBodyIndex != 0) {
146 const Real pscale = 1;
147 const Transform& X_BM = getDefaultOutboardFrame(); // TODO: get from state
148 if (X_BM.p() != Vec3(0) || X_BM.R() != Mat33(1)) {
149 DecorativeFrame frameOnChild(scale/4);
150 frameOnChild.setBodyId(myMobilizedBodyIndex);
151 frameOnChild.setColor(Red);
152 frameOnChild.setTransform(X_BM);
153 geom.push_back(frameOnChild);
154 if (X_BM.p() != Vec3(0))
155 geom.push_back(DecorativeLine(Vec3(0), X_BM.p()).setBodyId(myMobilizedBodyIndex));
156 }
157 const Transform& X_PF = getDefaultInboardFrame(); // TODO: from state
158 DecorativeFrame frameOnParent(pscale*Real(0.3)); // slightly larger than child
159 frameOnParent.setBodyId(myParentIndex);
160 frameOnParent.setColor(Blue);
161 frameOnParent.setTransform(X_PF);
162 geom.push_back(frameOnParent);
163 if (X_PF.p() != Vec3(0))
164 geom.push_back(DecorativeLine(Vec3(0),X_PF.p()).setBodyId(myParentIndex));
165 }
166
167 // Put a little purple wireframe sphere at the COM, and add a line from
168 // body origin to the com.
169
170 DecorativeSphere com(scale*Real(.05));
171 com.setBodyId(myMobilizedBodyIndex);
172 com.setColor(Purple).setRepresentation(DecorativeGeometry::DrawPoints);
173 const Vec3& comPos_B = theBody.getDefaultRigidBodyMassProperties().getMassCenter(); // TODO: from state
174 com.setTransform(comPos_B);
175 geom.push_back(com);
176 if (comPos_B != Vec3(0))
177 geom.push_back(DecorativeLine(Vec3(0), comPos_B).setBodyId(myMobilizedBodyIndex));
178 }
179
calcDecorativeGeometryAndAppend(const State & s,Stage stage,Array_<DecorativeGeometry> & geom)180 void calcDecorativeGeometryAndAppend
181 (const State& s, Stage stage, Array_<DecorativeGeometry>& geom) const
182 {
183 // We know how to deal with the topological (construction) geometry
184 // here. For bodies we can just draw it at topology stage. For mobilizers,
185 // we might not know the placement of the mobilizer frames on the parent
186 // and child bodies until Instance stage, at which point we can transform
187 // and then draw the topological geometry.
188 if (stage == Stage::Topology) {
189 appendTopologicalBodyGeometry(geom);
190 } else if (stage == Stage::Instance) {
191 const SimbodyMatterSubsystemRep& matterRep = getMyMatterSubsystemRep();
192 const Transform& X_PF = getInboardFrame(s);
193 const Transform& X_BM = getOutboardFrame(s);
194 appendTopologicalMobilizerGeometry(X_BM, X_PF, geom);
195 }
196
197 // Let the individual mobilizer deal with any complicated stuff.
198 calcDecorativeGeometryAndAppendImpl(s,stage,geom);
199 }
200
addOutboardDecoration(const Transform & X_MD,const DecorativeGeometry & g)201 int addOutboardDecoration(const Transform& X_MD, const DecorativeGeometry& g) {
202 const int nxt = (int)outboardGeometry.size();
203 outboardGeometry.push_back(g); // make a new copy
204 // Combine the placement frame and the transform already in the geometry
205 // so we end up with geometry expressed directly in the M frame.
206 outboardGeometry.back().setTransform(X_MD*g.getTransform());
207 // Record the assigned ordinal (not in the same ordinal space as
208 // body decorations).
209 outboardGeometry.back().setIndexOnBody(nxt);
210 return nxt;
211 }
addInboardDecoration(const Transform & X_FD,const DecorativeGeometry & g)212 int addInboardDecoration(const Transform& X_FD, const DecorativeGeometry& g) {
213 const int nxt = (int)inboardGeometry.size();
214 inboardGeometry.push_back(g); // make a new copy
215 // Combine the placement frame and the transform already in the geometry
216 // so we end up with geometry expressed directly in the F frame.
217 inboardGeometry.back().setTransform(X_FD*g.getTransform());
218 // Record the assigned ordinal (not in the same ordinal space as
219 // body decorations).
220 inboardGeometry.back().setIndexOnBody(nxt);
221 return nxt;
222 }
223
224
225 void findMobilizerQs(const State&, QIndex& qStart, int& nq) const;
226 void findMobilizerUs(const State&, UIndex& uStart, int& nu) const;
227
228
229 Motion::Method getQMotionMethod(const State&) const;
230 Motion::Method getUMotionMethod(const State&) const;
231 Motion::Method getUDotMotionMethod(const State&) const;
232
233 // Given the Model stage state variables (if any are relevant), each
234 // concrete mobilizer should return the number of q's and u's it expects
235 // to be allotted.
236 //virtual void getStateNeeds(const State& s, int& nq, int& nu) const = 0;
237
238 // Given the Model stage variable values in the passed-in State (if
239 // any of them are relevant) copy out the appropriate default values
240 // to the appropriate slot in qDefault.
241 void copyOutDefaultQ(const State& s, Vector& qDefault) const;
242
243 // Generic State-access routines (that is, those which can be handled in
244 // the MobilizedBody base class).
245
246
247 // Invalidate Stage::Position.
248 void setQToFitTransform(State& s, const Transform& X_FM) const;
249 void setQToFitRotation(State& s, const Rotation& R_FM) const;
250 void setQToFitTranslation(State& s, const Vec3& p_FM) const;
251
252 // Invalidate Stage::Velocity.
253 void setUToFitVelocity(State& s, const SpatialVec& V_FM) const;
254 void setUToFitAngularVelocity(State& s, const Vec3& w_FM) const;
255 void setUToFitLinearVelocity(State& s, const Vec3& v_FM) const;
256
getBodyMassProperties(const State & s)257 const MassProperties& getBodyMassProperties(const State& s) const {
258 // TODO: these should come from the state if the body has variable mass props
259 const SBInstanceVars& iv = getMyMatterSubsystemRep().getInstanceVars(s);
260 return getMyRigidBodyNode().getMassProperties_OB_B();
261 }
262
getBodySpatialInertiaInGround(const State & s)263 const SpatialInertia& getBodySpatialInertiaInGround(const State& s) const {
264 const SBTreePositionCache& tpc = getMyMatterSubsystemRep().getTreePositionCache(s);
265 return getMyRigidBodyNode().getMk_G(tpc);
266 }
267
getInboardFrame(const State & s)268 const Transform& getInboardFrame (const State& s) const {
269 // TODO: these should come from the state if the mobilizer has variable frames
270 const SBInstanceVars& iv = getMyMatterSubsystemRep().getInstanceVars(s);
271 return getMyRigidBodyNode().getX_PF();
272 }
getOutboardFrame(const State & s)273 const Transform& getOutboardFrame(const State& s) const {
274 // TODO: these should come from the state if the mobilizer has variable frames
275 const SBInstanceVars& iv = getMyMatterSubsystemRep().getInstanceVars(s);
276 return getMyRigidBodyNode().getX_BM();
277 }
278
setInboardFrame(State & s,const Transform & X_PF)279 void setInboardFrame (State& s, const Transform& X_PF) const {
280 assert(!"setInboardFrame(s) not implemented yet");
281 }
setOutboardFrame(State & s,const Transform & X_BM)282 void setOutboardFrame(State& s, const Transform& X_BM) const {
283 assert(!"setOutboardFrame(s) not implemented yet");
284 }
285
getBodyTransform(const State & s)286 const Transform& getBodyTransform(const State& s) const {
287 const SBTreePositionCache& pc = getMyMatterSubsystemRep().getTreePositionCache(s);
288 return getMyRigidBodyNode().getX_GB(pc);
289 }
getBodyVelocity(const State & s)290 const SpatialVec& getBodyVelocity(const State& s) const {
291 const SBTreeVelocityCache& vc = getMyMatterSubsystemRep().getTreeVelocityCache(s);
292 return getMyRigidBodyNode().getV_GB(vc);
293 }
getBodyAcceleration(const State & s)294 const SpatialVec& getBodyAcceleration(const State& s) const {
295 const SBTreeAccelerationCache& ac = getMyMatterSubsystemRep().getTreeAccelerationCache(s);
296 return getMyRigidBodyNode().getA_GB(ac);
297 }
298
getMobilizerTransform(const State & s)299 const Transform& getMobilizerTransform(const State& s) const {
300 const SBTreePositionCache& pc = getMyMatterSubsystemRep().getTreePositionCache(s);
301 return getMyRigidBodyNode().getX_FM(pc);
302 }
getMobilizerVelocity(const State & s)303 const SpatialVec& getMobilizerVelocity(const State& s) const {
304 const SBTreeVelocityCache& vc = getMyMatterSubsystemRep().getTreeVelocityCache(s);
305 return getMyRigidBodyNode().getV_FM(vc);
306 }
307
getHCol(const State & s,MobilizerUIndex ux)308 SpatialVec getHCol(const State& s, MobilizerUIndex ux) const {
309 const SBTreePositionCache& pc = getMyMatterSubsystemRep().getTreePositionCache(s);
310 return getMyRigidBodyNode().getHCol(pc,ux);
311 }
312
getH_FMCol(const State & s,MobilizerUIndex ux)313 SpatialVec getH_FMCol(const State& s, MobilizerUIndex ux) const {
314 const SBTreePositionCache& pc = getMyMatterSubsystemRep().getTreePositionCache(s);
315 return getMyRigidBodyNode().getH_FMCol(pc,ux);
316 }
317
invalidateTopologyCache()318 void invalidateTopologyCache() const {
319 delete myRBnode; myRBnode=0;
320 if (myMatterSubsystemRep)
321 myMatterSubsystemRep->invalidateSubsystemTopologyCache();
322 }
323
324 // This might get called *during* realizeTopology() so just make sure there is
325 // a node here without checking whether we're done with realizeTopology().
getMyRigidBodyNode()326 const RigidBodyNode& getMyRigidBodyNode() const {
327 SimTK_ASSERT(myRBnode && myMatterSubsystemRep,
328 "An operation on a MobilizedBody was illegal because realizeTopology() has "
329 "not been performed on the containing Subsystem since the last topological change."
330 );
331 return *myRBnode;
332 }
333
getBody()334 const Body& getBody() const {return theBody;}
getDefaultInboardFrame()335 const Transform& getDefaultInboardFrame() const {return defaultInboardFrame;}
getDefaultOutboardFrame()336 const Transform& getDefaultOutboardFrame() const {return defaultOutboardFrame;}
getDefaultRigidBodyMassProperties()337 const MassProperties& getDefaultRigidBodyMassProperties() const {
338 return theBody.getDefaultRigidBodyMassProperties();
339 }
340
isReversed()341 bool isReversed() const {return reversed;}
342
adoptMotion(Motion & ownerHandle)343 void adoptMotion(Motion& ownerHandle) {
344 SimTK_ERRCHK(!hasMotion(), "MobilizedBody::adoptMotion()",
345 "This MobilizedBody already has a Motion object associated with it.\n"
346 "Use clearMotion() first to replace an existing Motion object.");
347 ownerHandle.disown(motion); // transfer ownership to handle "motion"
348 invalidateTopologyCache();
349
350 // Tell the Motion that it belongs to this MobilizedBody.
351 motion.updImpl().setMobilizedBodyImpl(this);
352 }
353
clearMotion()354 void clearMotion() {
355 motion.clearHandle();
356 invalidateTopologyCache();
357 }
358
hasMotion()359 bool hasMotion() const {return !motion.isEmptyHandle();}
360
getMotion()361 const Motion& getMotion() const {
362 SimTK_ERRCHK(!motion.isEmptyHandle(),
363 "MobilizedBody::getMotion()",
364 "There is no Motion object associated with this MobilizedBody.");
365 return motion;
366 }
367
getMySimbodyMatterSubsystem()368 const SimbodyMatterSubsystem& getMySimbodyMatterSubsystem() const {
369 return getMyMatterSubsystemRep().getMySimbodyMatterSubsystemHandle();
370 }
371
getMyMatterSubsystemRep()372 const SimbodyMatterSubsystemRep& getMyMatterSubsystemRep() const {
373 SimTK_ASSERT(myMatterSubsystemRep,
374 "An operation was illegal because a MobilizedBody was not in a Subsystem.");
375 return *myMatterSubsystemRep;
376 }
updMyMatterSubsystemRep()377 SimbodyMatterSubsystemRep& updMyMatterSubsystemRep() {
378 SimTK_ASSERT(myMatterSubsystemRep,
379 "An operation was illegal because a MobilizedBody was not in a Subsystem.");
380 return *myMatterSubsystemRep;
381 }
382
getMyModelInfo(const State & s)383 const SBModelPerMobodInfo& getMyModelInfo(const State& s) const {
384 const SimbodyMatterSubsystemRep& matterRep = getMyMatterSubsystemRep();
385 const SBModelCache& mc = matterRep.getModelCache(s);
386 const SBModelPerMobodInfo& info =
387 mc.getMobodModelInfo(myMobilizedBodyIndex);
388 return info;
389 }
390
getMyInstanceInfo(const State & s)391 const SBInstancePerMobodInfo& getMyInstanceInfo(const State& s) const {
392 const SimbodyMatterSubsystemRep& matterRep = getMyMatterSubsystemRep();
393 const SBInstanceCache& ic = matterRep.getInstanceCache(s);
394 const SBInstancePerMobodInfo& info =
395 ic.getMobodInstanceInfo(myMobilizedBodyIndex);
396 return info;
397 }
398
getMyHandle()399 const MobilizedBody& getMyHandle() const {
400 const MobilizedBody& mobod =
401 getMyMatterSubsystemRep().getMobilizedBody(getMyMobilizedBodyIndex());
402 SimTK_ASSERT(&mobod.getImpl() == this,
403 "A MobilizedBodyImpl's handle didn't refer back to the same Impl!");
404 return mobod;
405 }
406
updMyHandle()407 MobilizedBody& updMyHandle() {
408 MobilizedBody& mobod =
409 updMyMatterSubsystemRep().updMobilizedBody(getMyMobilizedBodyIndex());
410 SimTK_ASSERT(&mobod.getImpl() == this,
411 "A MobilizedBodyImpl's handle didn't refer back to the same Impl!");
412 return mobod;
413 }
414
getMyMobilizedBodyIndex()415 MobilizedBodyIndex getMyMobilizedBodyIndex() const {
416 assert(myMobilizedBodyIndex.isValid());
417 return myMobilizedBodyIndex;
418 }
419
getMyParentMobilizedBodyIndex()420 MobilizedBodyIndex getMyParentMobilizedBodyIndex() const {
421 assert(myParentIndex.isValid());
422 return myParentIndex;
423 }
424
getMyBaseBodyMobilizedBodyIndex()425 MobilizedBodyIndex getMyBaseBodyMobilizedBodyIndex() const {
426 assert(myBaseBodyIndex.isValid());
427 return myBaseBodyIndex;
428 }
429
getMyLevel()430 int getMyLevel() const {
431 assert(myLevel >= 0);
432 return myLevel;
433 }
434
isInSubsystem()435 bool isInSubsystem() const {return myMatterSubsystemRep != 0;}
436
setMyMatterSubsystem(SimbodyMatterSubsystem & matter,MobilizedBodyIndex parentIndex,MobilizedBodyIndex index)437 void setMyMatterSubsystem(SimbodyMatterSubsystem& matter,
438 MobilizedBodyIndex parentIndex,
439 MobilizedBodyIndex index)
440 {
441 // If the subsystem is already set it must be the same one.
442 assert(!myMatterSubsystemRep || myMatterSubsystemRep==&matter.getRep());
443 myMatterSubsystemRep = &matter.updRep();
444
445 assert(index.isValid());
446 assert(parentIndex.isValid() || index==GroundIndex);
447
448 myParentIndex = parentIndex; // invalid if this is Ground
449 myMobilizedBodyIndex = index;
450
451 if (index != GroundIndex) {
452 MobilizedBody& parent = matter.updMobilizedBody(parentIndex);
453 myLevel = parent.getLevelInMultibodyTree() + 1;
454 myBaseBodyIndex = (myLevel == 1 ? myMobilizedBodyIndex
455 : parent.getBaseMobilizedBody().getMobilizedBodyIndex());
456 parent.updImpl().hasChildren = true;
457 } else {
458 myLevel = 0;
459 myBaseBodyIndex = GroundIndex;
460 }
461 }
462
463 private:
464 // Body topological geometry is defined with respect to the body frame so we
465 // can draw it right away.
appendTopologicalBodyGeometry(Array_<DecorativeGeometry> & geom)466 void appendTopologicalBodyGeometry(Array_<DecorativeGeometry>& geom) const {
467 getBody().getRep().appendDecorativeGeometry(getMyMobilizedBodyIndex(), geom);
468 }
469
470 // Mobilizer topological geometry is defined with respect to the M (outboard, child)
471 // frame and the F (inboard, parent) frame. The placement of those frames with
472 // respect to the body frame can be Instance variables, so we can't draw this geometry
473 // until Instance stage. At that point we can find M and F, so they are passed in
474 // here.
appendTopologicalMobilizerGeometry(const Transform & X_BM,const Transform & X_PF,Array_<DecorativeGeometry> & geom)475 void appendTopologicalMobilizerGeometry(const Transform& X_BM, const Transform& X_PF,
476 Array_<DecorativeGeometry>& geom) const
477 {
478 for (int i=0; i<(int)outboardGeometry.size(); ++i) {
479 geom.push_back(outboardGeometry[i]);
480 geom.back().setBodyId(getMyMobilizedBodyIndex())
481 .setTransform(X_BM*outboardGeometry[i].getTransform());
482 }
483 for (int i=0; i<(int)inboardGeometry.size(); ++i) {
484 geom.push_back(inboardGeometry[i]);
485 geom.back().setBodyId(getMyParentMobilizedBodyIndex())
486 .setTransform(X_PF*inboardGeometry[i].getTransform());
487 }
488 }
489
490 private:
491 friend class MobilizedBody;
492
493 // TOPOLOGY "STATE"
494
495 // Base class topological properties. Derived MobilizedBodies may
496 // have further topological properties. Whenever these change, be
497 // sure to set topologyRealized=false!
498
499 // Body represents the mass structure, mass properties, and possibly some
500 // decorative geometry for the body associated with this (body,mobilizer)
501 // pair.
502 Body theBody;
503 Transform defaultInboardFrame; // default for F (in Parent frame)
504 Transform defaultOutboardFrame; // default for M (in Body frame)
505 Motion::Level defaultLockLevel;
506
507 // A Motion object, if present, defines how this mobilizer's motion is
508 // to be calculated. Otherwise, the motion is determined dynamically
509 // as a result of forces and constraints. A Motion always prescribes
510 // \e all of the mobilizer's generalized accelerations udot (index 1);
511 // may also some of the prescribed generalized speeds u (index 2); and
512 // for speeds that are prescribed it may also prescribe the corresponding
513 // generalized coordinates q (index 3).
514 Motion motion;
515
516 bool reversed; // is the mobilizer defined from M to F?
517
518 Array_<DecorativeGeometry> outboardGeometry;
519 Array_<DecorativeGeometry> inboardGeometry;
520
521 // These data members are filled in once the MobilizedBody is added to
522 // a MatterSubsystem. Note that this pointer is just a reference to
523 // the Subsystem which owns this MobilizedBody -- we don't own the
524 // heap space here so don't delete it!
525 SimbodyMatterSubsystemRep* myMatterSubsystemRep;
526 MobilizedBodyIndex myMobilizedBodyIndex; // index within the subsystem
527 MobilizedBodyIndex myParentIndex; // Invalid if this body is Ground, otherwise the parent's index
528 MobilizedBodyIndex myBaseBodyIndex; // GroundIndex if this is ground, otherwise a level-1 BodyIndex
529 int myLevel; // Distance from ground in multibody graph (0 if this is ground,
530 // 1 if a base body, 2 if parent is a base body, etc.)
531
532 // TOPOLOGY "CACHE"
533
534 // A RigidBodyNode is created for each MobilizedBody during realizeTopology().
535 // Think of it as the *computational* form of the MobilizedBody; the MobilizedBody
536 // itself exists to make a nice API for the programmer. In fact is is possible
537 // for several different MobilizedBody classes to use the same underlying
538 // computational form while providing a different API.
539 //
540 // This is a pointer to an abstract object whose heap space is *owned* by
541 // this MobilizedBody. Be sure to delete it upon destruction and whenever
542 // topology is re-realized.
543 mutable RigidBodyNode* myRBnode;
544
545 protected:
546 // Keep track of whether this body has any children.
547 bool hasChildren;
548 };
549
550 class MobilizedBody::PinImpl : public MobilizedBodyImpl {
551 public:
PinImpl(Direction d)552 explicit PinImpl(Direction d) : MobilizedBodyImpl(d), defaultQ(0) { }
clone()553 PinImpl* clone() const override { return new PinImpl(*this); }
554
555 RigidBodyNode* createRigidBodyNode(
556 UIndex& nextUSlot,
557 USquaredIndex& nextUSqSlot,
558 QIndex& nextQSlot) const override;
559
copyOutDefaultQImpl(int nq,Real * q)560 void copyOutDefaultQImpl(int nq, Real* q) const override {
561 SimTK_ASSERT(nq==1,
562 "MobilizedBody::PinImpl::copyOutDefaultQImpl(): wrong number of q's");
563 *q = defaultQ;
564 }
565
566 SimTK_DOWNCAST(PinImpl, MobilizedBodyImpl);
567 private:
568 friend class MobilizedBody::Pin;
569 Real defaultQ; // the angle in radians
570 };
571
572
573 class MobilizedBody::SliderImpl : public MobilizedBodyImpl {
574 public:
SliderImpl(Direction d)575 explicit SliderImpl(Direction d) : MobilizedBodyImpl(d), defaultQ(0) { }
clone()576 SliderImpl* clone() const override { return new SliderImpl(*this); }
577
578 RigidBodyNode* createRigidBodyNode(
579 UIndex& nextUSlot,
580 USquaredIndex& nextUSqSlot,
581 QIndex& nextQSlot) const override;
582
copyOutDefaultQImpl(int nq,Real * q)583 void copyOutDefaultQImpl(int nq, Real* q) const override {
584 SimTK_ASSERT(nq==1,
585 "MobilizedBody::SliderImpl::copyOutDefaultQImpl(): wrong number of q's");
586 *q = defaultQ;
587 }
588
589 SimTK_DOWNCAST(SliderImpl, MobilizedBodyImpl);
590 private:
591 friend class MobilizedBody::Slider;
592 Real defaultQ; // the displacement
593 };
594
595 class MobilizedBody::UniversalImpl : public MobilizedBodyImpl {
596 public:
UniversalImpl(Direction d)597 explicit UniversalImpl(Direction d) : MobilizedBodyImpl(d), defaultQ(0) { }
clone()598 UniversalImpl* clone() const override { return new UniversalImpl(*this); }
599
600 RigidBodyNode* createRigidBodyNode(
601 UIndex& nextUSlot,
602 USquaredIndex& nextUSqSlot,
603 QIndex& nextQSlot) const override;
604
copyOutDefaultQImpl(int nq,Real * q)605 void copyOutDefaultQImpl(int nq, Real* q) const override {
606 SimTK_ASSERT(nq==2,
607 "MobilizedBody::UniversalImpl::copyOutDefaultQImpl(): wrong number of q's");
608 Vec2::updAs(q) = defaultQ;
609 }
610
611 SimTK_DOWNCAST(UniversalImpl, MobilizedBodyImpl);
612 private:
613 friend class MobilizedBody::Universal;
614 Vec2 defaultQ; // the two angles
615 };
616
617 class MobilizedBody::CylinderImpl : public MobilizedBodyImpl {
618 public:
CylinderImpl(Direction d)619 explicit CylinderImpl(Direction d) : MobilizedBodyImpl(d), defaultQ(0) { }
clone()620 CylinderImpl* clone() const override { return new CylinderImpl(*this); }
621
622 RigidBodyNode* createRigidBodyNode(
623 UIndex& nextUSlot,
624 USquaredIndex& nextUSqSlot,
625 QIndex& nextQSlot) const override;
626
copyOutDefaultQImpl(int nq,Real * q)627 void copyOutDefaultQImpl(int nq, Real* q) const override {
628 SimTK_ASSERT(nq==2,
629 "MobilizedBody::CylinderImpl::copyOutDefaultQImpl(): wrong number of q's");
630 Vec2::updAs(q) = defaultQ;
631 }
632
633 SimTK_DOWNCAST(CylinderImpl, MobilizedBodyImpl);
634 private:
635 friend class MobilizedBody::Cylinder;
636 Vec2 defaultQ; // the angle in radians followed by the displacement
637 };
638
639 class MobilizedBody::BendStretchImpl : public MobilizedBodyImpl {
640 public:
BendStretchImpl(Direction d)641 explicit BendStretchImpl(Direction d) : MobilizedBodyImpl(d), defaultQ(0) { }
clone()642 BendStretchImpl* clone() const override { return new BendStretchImpl(*this); }
643
644 RigidBodyNode* createRigidBodyNode(
645 UIndex& nextUSlot,
646 USquaredIndex& nextUSqSlot,
647 QIndex& nextQSlot) const override;
648
copyOutDefaultQImpl(int nq,Real * q)649 void copyOutDefaultQImpl(int nq, Real* q) const override {
650 SimTK_ASSERT(nq==2,
651 "MobilizedBody::BendStretchImpl::copyOutDefaultQImpl(): wrong number of q's");
652 Vec2::updAs(q) = defaultQ;
653 }
654
655 SimTK_DOWNCAST(BendStretchImpl, MobilizedBodyImpl);
656 private:
657 friend class MobilizedBody::BendStretch;
658 Vec2 defaultQ; // the angle in radians followed by the displacement
659 };
660
661 class MobilizedBody::PlanarImpl : public MobilizedBodyImpl {
662 public:
PlanarImpl(Direction d)663 explicit PlanarImpl(Direction d) : MobilizedBodyImpl(d), defaultQ(0) { }
clone()664 PlanarImpl* clone() const override { return new PlanarImpl(*this); }
665
666 RigidBodyNode* createRigidBodyNode(
667 UIndex& nextUSlot,
668 USquaredIndex& nextUSqSlot,
669 QIndex& nextQSlot) const override;
670
copyOutDefaultQImpl(int nq,Real * q)671 void copyOutDefaultQImpl(int nq, Real* q) const override {
672 SimTK_ASSERT(nq==3,
673 "MobilizedBody::PlanarImpl::copyOutDefaultQImpl(): wrong number of q's");
674 Vec3::updAs(q) = defaultQ;
675 }
676
677 SimTK_DOWNCAST(PlanarImpl, MobilizedBodyImpl);
678 private:
679 friend class MobilizedBody::Planar;
680 Vec3 defaultQ; // the angle in radians followed by the two displacements
681 };
682
683 class MobilizedBody::SphericalCoordsImpl : public MobilizedBodyImpl {
684 public:
SphericalCoordsImpl(Direction d)685 explicit SphericalCoordsImpl(Direction d)
686 : MobilizedBodyImpl(d), az0(0), ze0(0), axisT(ZAxis),
687 negAz(false), negZe(false), negT(false),
688 defaultQ(0) {}
689
SphericalCoordsImpl(Real az0,bool negAz,Real ze0,bool negZe,CoordinateAxis axisT,bool negT,Direction d)690 SphericalCoordsImpl(Real az0, bool negAz,
691 Real ze0, bool negZe,
692 CoordinateAxis axisT, bool negT,
693 Direction d)
694 : MobilizedBodyImpl(d), az0(az0), ze0(ze0), axisT(axisT),
695 negAz(negAz), negZe(negZe), negT(negT),
696 defaultQ(0) {}
697
clone()698 SphericalCoordsImpl* clone() const override { return new SphericalCoordsImpl(*this); }
699
700 RigidBodyNode* createRigidBodyNode(
701 UIndex& nextUSlot,
702 USquaredIndex& nextUSqSlot,
703 QIndex& nextQSlot) const override;
704
copyOutDefaultQImpl(int nq,Real * q)705 void copyOutDefaultQImpl(int nq, Real* q) const override {
706 SimTK_ASSERT(nq==3,
707 "MobilizedBody::SphericalCoordsImpl::copyOutDefaultQImpl(): wrong number of q's");
708 Vec3::updAs(q) = defaultQ;
709 }
710
711 SimTK_DOWNCAST(SphericalCoordsImpl, MobilizedBodyImpl);
712 private:
713 friend class MobilizedBody::SphericalCoords;
714
715 Real az0, ze0; // angle offsets
716 CoordinateAxis axisT; // translation axis (X or Z)
717 bool negAz, negZe, negT; // true if we should negate the corresponding coordinate
718
719 Vec3 defaultQ; // two angles in radians followed by a displacement
720 };
721
722 class MobilizedBody::GimbalImpl : public MobilizedBodyImpl {
723 public:
GimbalImpl(Direction d)724 explicit GimbalImpl(Direction d)
725 : MobilizedBodyImpl(d), defaultRadius(Real(0.1)), defaultQ(0) { }
clone()726 GimbalImpl* clone() const override { return new GimbalImpl(*this); }
727
728 RigidBodyNode* createRigidBodyNode(
729 UIndex& nextUSlot,
730 USquaredIndex& nextUSqSlot,
731 QIndex& nextQSlot) const override;
732
copyOutDefaultQImpl(int nq,Real * q)733 void copyOutDefaultQImpl(int nq, Real* q) const override {
734 SimTK_ASSERT(nq==3,
735 "MobilizedBody::GimbalImpl::copyOutDefaultQImpl(): wrong number of q's");
736 Vec3::updAs(q) = defaultQ;
737 }
738
739 void calcDecorativeGeometryAndAppendImpl
740 (const State& s, Stage stage, Array_<DecorativeGeometry>& geom) const override;
741
setDefaultRadius(Real r)742 void setDefaultRadius(Real r) {
743 assert(r>0);
744 invalidateTopologyCache();
745 defaultRadius=r;
746 }
getDefaultRadius()747 Real getDefaultRadius() const {return defaultRadius;}
748
749 SimTK_DOWNCAST(GimbalImpl, MobilizedBodyImpl);
750 private:
751 friend class MobilizedBody::Gimbal;
752 Real defaultRadius; // used for visualization only
753 Vec3 defaultQ; // the three angles in radians
754 };
755
756 class MobilizedBody::BushingImpl : public MobilizedBodyImpl {
757 public:
BushingImpl(Direction d)758 explicit BushingImpl(Direction d)
759 : MobilizedBodyImpl(d), defaultQ(0) { }
clone()760 BushingImpl* clone() const override { return new BushingImpl(*this); }
761
762 RigidBodyNode* createRigidBodyNode(
763 UIndex& nextUSlot,
764 USquaredIndex& nextUSqSlot,
765 QIndex& nextQSlot) const override;
766
copyOutDefaultQImpl(int nq,Real * q)767 void copyOutDefaultQImpl(int nq, Real* q) const override {
768 SimTK_ASSERT(nq==6,
769 "MobilizedBody::BushingImpl::copyOutDefaultQImpl(): wrong number of q's");
770 Vec6::updAs(q) = defaultQ;
771 }
772
773 SimTK_DOWNCAST(BushingImpl, MobilizedBodyImpl);
774 private:
775 friend class MobilizedBody::Bushing;
776 Vec6 defaultQ; // 3 angles in radians, then p_FM
777 };
778
779 class MobilizedBody::BallImpl : public MobilizedBodyImpl {
780 public:
BallImpl(Direction d)781 explicit BallImpl(Direction d)
782 : MobilizedBodyImpl(d), defaultRadius(Real(0.1)), defaultQ() {} // (1,0,0,0), the identity rotation
clone()783 BallImpl* clone() const override { return new BallImpl(*this); }
784
785 RigidBodyNode* createRigidBodyNode(
786 UIndex& nextUSlot,
787 USquaredIndex& nextUSqSlot,
788 QIndex& nextQSlot) const override;
789
copyOutDefaultQImpl(int nq,Real * q)790 void copyOutDefaultQImpl(int nq, Real* q) const override {
791 SimTK_ASSERT(nq==4||nq==3,
792 "MobilizedBody::BallImpl::copyOutDefaultQImpl(): wrong number of q's");
793 if (nq==4)
794 Vec4::updAs(q) = defaultQ.asVec4();
795 else
796 Vec3::updAs(q) = Rotation(defaultQ).convertRotationToBodyFixedXYZ();
797 }
798
799 void calcDecorativeGeometryAndAppendImpl
800 (const State& s, Stage stage, Array_<DecorativeGeometry>& geom) const override;
801
setDefaultRadius(Real r)802 void setDefaultRadius(Real r) {
803 assert(r>0);
804 invalidateTopologyCache();
805 defaultRadius=r;
806 }
getDefaultRadius()807 Real getDefaultRadius() const {return defaultRadius;}
808
809 SimTK_DOWNCAST(BallImpl, MobilizedBodyImpl);
810 private:
811 friend class MobilizedBody::Ball;
812 Real defaultRadius; // used for visualization only
813 Quaternion defaultQ; // the default orientation
814 };
815
816 class MobilizedBody::EllipsoidImpl : public MobilizedBodyImpl {
817 public:
EllipsoidImpl(Direction d)818 explicit EllipsoidImpl(Direction d)
819 : MobilizedBodyImpl(d), defaultRadii(Real(0.5),Real(1./3.),Real(0.25)),
820 defaultQ() { } // default is (1,0,0,0), the identity rotation
clone()821 EllipsoidImpl* clone() const override { return new EllipsoidImpl(*this); }
822
823 RigidBodyNode* createRigidBodyNode(
824 UIndex& nextUSlot,
825 USquaredIndex& nextUSqSlot,
826 QIndex& nextQSlot) const override;
827
copyOutDefaultQImpl(int nq,Real * q)828 void copyOutDefaultQImpl(int nq, Real* q) const override {
829 SimTK_ASSERT(nq==4||nq==3,
830 "MobilizedBody::EllipsoidImpl::copyOutDefaultQImpl(): wrong number of q's");
831 if (nq==4)
832 Vec4::updAs(q) = defaultQ.asVec4();
833 else
834 Vec3::updAs(q) = Rotation(defaultQ).convertRotationToBodyFixedXYZ();
835 }
836
837 void calcDecorativeGeometryAndAppendImpl
838 (const State& s, Stage stage, Array_<DecorativeGeometry>& geom) const override;
839
setDefaultRadii(const Vec3 & r)840 void setDefaultRadii(const Vec3& r) {
841 assert(r[0]>0 && r[1]>0 && r[2]>0);
842 invalidateTopologyCache();
843 defaultRadii=r;
844 }
getDefaultRadii()845 const Vec3& getDefaultRadii() const {return defaultRadii;}
846
847 SimTK_DOWNCAST(EllipsoidImpl, MobilizedBodyImpl);
848 private:
849 friend class MobilizedBody::Ellipsoid;
850 Vec3 defaultRadii; // used for visualization only
851 Quaternion defaultQ; // the default orientation
852 };
853
854 class MobilizedBody::TranslationImpl : public MobilizedBodyImpl {
855 public:
TranslationImpl(Direction d)856 explicit TranslationImpl(Direction d) : MobilizedBodyImpl(d), defaultQ(0) { }
clone()857 TranslationImpl* clone() const override { return new TranslationImpl(*this); }
858
859 RigidBodyNode* createRigidBodyNode(
860 UIndex& nextUSlot,
861 USquaredIndex& nextUSqSlot,
862 QIndex& nextQSlot) const override;
863
copyOutDefaultQImpl(int nq,Real * q)864 void copyOutDefaultQImpl(int nq, Real* q) const override {
865 SimTK_ASSERT(nq==3,
866 "MobilizedBody::TranslationImpl::copyOutDefaultQImpl(): wrong number of q's");
867 Vec3::updAs(q) = defaultQ;
868 }
869
870 SimTK_DOWNCAST(TranslationImpl, MobilizedBodyImpl);
871 private:
872 friend class MobilizedBody::Translation;
873 Vec3 defaultQ; // the three displacements
874 };
875
876 class MobilizedBody::FreeImpl : public MobilizedBodyImpl {
877 public:
FreeImpl(Direction d)878 explicit FreeImpl(Direction d) : MobilizedBodyImpl(d), defaultQOrientation(), defaultQTranslation(0) { }
clone()879 FreeImpl* clone() const override { return new FreeImpl(*this); }
880
881 RigidBodyNode* createRigidBodyNode(
882 UIndex& nextUSlot,
883 USquaredIndex& nextUSqSlot,
884 QIndex& nextQSlot) const override;
885
copyOutDefaultQImpl(int nq,Real * q)886 void copyOutDefaultQImpl(int nq, Real* q) const override {
887 SimTK_ASSERT(nq==7||nq==6,
888 "MobilizedBody::FreeImpl::copyOutDefaultQImpl(): wrong number of q's");
889 if (nq==7) {
890 Vec4::updAs(q) = defaultQOrientation.asVec4();
891 Vec3::updAs(q+4) = defaultQTranslation;
892 } else {
893 Vec3::updAs(q) = Rotation(defaultQOrientation).convertRotationToBodyFixedXYZ();
894 Vec3::updAs(q+3) = defaultQTranslation;
895 }
896 }
897
898 SimTK_DOWNCAST(FreeImpl, MobilizedBodyImpl);
899 private:
900 friend class MobilizedBody::Free;
901 Quaternion defaultQOrientation;
902 Vec3 defaultQTranslation;
903 };
904
905 class MobilizedBody::LineOrientationImpl : public MobilizedBodyImpl {
906 public:
LineOrientationImpl(Direction d)907 explicit LineOrientationImpl(Direction d) : MobilizedBodyImpl(d), defaultQ() { } // 1,0,0,0
clone()908 LineOrientationImpl* clone() const override { return new LineOrientationImpl(*this); }
909
910 RigidBodyNode* createRigidBodyNode(
911 UIndex& nextUSlot,
912 USquaredIndex& nextUSqSlot,
913 QIndex& nextQSlot) const override;
914
copyOutDefaultQImpl(int nq,Real * q)915 void copyOutDefaultQImpl(int nq, Real* q) const override {
916 SimTK_ASSERT(nq==4||nq==3,
917 "MobilizedBody::LineOrientationImpl::copyOutDefaultQImpl(): wrong number of q's");
918 if (nq==4)
919 Vec4::updAs(q) = defaultQ.asVec4();
920 else
921 Vec3::updAs(q) = Rotation(defaultQ).convertRotationToBodyFixedXYZ();
922 }
923
924 SimTK_DOWNCAST(LineOrientationImpl, MobilizedBodyImpl);
925 private:
926 friend class MobilizedBody::LineOrientation;
927 Quaternion defaultQ;
928 };
929
930 class MobilizedBody::FreeLineImpl : public MobilizedBodyImpl {
931 public:
FreeLineImpl(Direction d)932 explicit FreeLineImpl(Direction d) : MobilizedBodyImpl(d), defaultQOrientation(), defaultQTranslation(0) { }
clone()933 FreeLineImpl* clone() const override { return new FreeLineImpl(*this); }
934
935 RigidBodyNode* createRigidBodyNode(
936 UIndex& nextUSlot,
937 USquaredIndex& nextUSqSlot,
938 QIndex& nextQSlot) const override;
939
copyOutDefaultQImpl(int nq,Real * q)940 void copyOutDefaultQImpl(int nq, Real* q) const override {
941 SimTK_ASSERT(nq==7||nq==6,
942 "MobilizedBody::FreeLineImpl::copyOutDefaultQImpl(): wrong number of q's");
943 if (nq==7) {
944 Vec4::updAs(q) = defaultQOrientation.asVec4();
945 Vec3::updAs(q+4) = defaultQTranslation;
946 } else {
947 Vec3::updAs(q) = Rotation(defaultQOrientation).convertRotationToBodyFixedXYZ();
948 Vec3::updAs(q+3) = defaultQTranslation;
949 }
950 }
951
952 SimTK_DOWNCAST(FreeLineImpl, MobilizedBodyImpl);
953 private:
954 friend class MobilizedBody::FreeLine;
955 Quaternion defaultQOrientation;
956 Vec3 defaultQTranslation;
957 };
958
959 class MobilizedBody::WeldImpl : public MobilizedBodyImpl {
960 public:
WeldImpl(Direction d)961 explicit WeldImpl(Direction d) : MobilizedBodyImpl(d) { }
clone()962 WeldImpl* clone() const override { return new WeldImpl(*this); }
963
964 RigidBodyNode* createRigidBodyNode(
965 UIndex& nextUSlot,
966 USquaredIndex& nextUSqSlot,
967 QIndex& nextQSlot) const override;
968
copyOutDefaultQImpl(int nq,Real * q)969 void copyOutDefaultQImpl(int nq, Real* q) const override {
970 SimTK_ASSERT(nq==0,
971 "MobilizedBody::WeldImpl::copyOutDefaultQImpl(): wrong number of q's");
972 }
973
974 SimTK_DOWNCAST(WeldImpl, MobilizedBodyImpl);
975 private:
976 friend class MobilizedBody::Weld;
977 // no q's
978 };
979
980 class MobilizedBody::GroundImpl : public MobilizedBodyImpl {
981 public:
GroundImpl()982 GroundImpl() : MobilizedBodyImpl(MobilizedBody::Forward) { }
clone()983 GroundImpl* clone() const override { return new GroundImpl(*this); }
984
985 RigidBodyNode* createRigidBodyNode(
986 UIndex& nextUSlot,
987 USquaredIndex& nextUSqSlot,
988 QIndex& nextQSlot) const override;
989
copyOutDefaultQImpl(int nq,Real * q)990 void copyOutDefaultQImpl(int nq, Real* q) const override {
991 SimTK_ASSERT(nq==0,
992 "MobilizedBody::GroundImpl::copyOutDefaultQImpl(): wrong number of q's");
993 }
994
995 SimTK_DOWNCAST(GroundImpl, MobilizedBodyImpl);
996 private:
997 friend class MobilizedBody::Ground;
998 // no q's
999 };
1000
1001 class MobilizedBody::ScrewImpl : public MobilizedBodyImpl {
1002 public:
ScrewImpl(Real p,Direction d)1003 ScrewImpl(Real p, Direction d) : MobilizedBodyImpl(d), defaultPitch(p), defaultQ(0) { }
1004
getDefaultPitch()1005 Real getDefaultPitch() const {return defaultPitch;}
setDefaultPitch(Real p)1006 void setDefaultPitch(Real p) {
1007 invalidateTopologyCache();
1008 defaultPitch=p;
1009 }
1010
clone()1011 ScrewImpl* clone() const override { return new ScrewImpl(*this); }
1012
1013 RigidBodyNode* createRigidBodyNode(
1014 UIndex& nextUSlot,
1015 USquaredIndex& nextUSqSlot,
1016 QIndex& nextQSlot) const override;
1017
copyOutDefaultQImpl(int nq,Real * q)1018 void copyOutDefaultQImpl(int nq, Real* q) const override {
1019 SimTK_ASSERT(nq==1,
1020 "MobilizedBody::ScrewImpl::copyOutDefaultQImpl(): wrong number of q's");
1021 *q = defaultQ;
1022 }
1023
1024 SimTK_DOWNCAST(ScrewImpl, MobilizedBodyImpl);
1025 private:
1026 friend class MobilizedBody::Screw;
1027 Real defaultPitch;
1028 Real defaultQ; // the angle in radians
1029 };
1030
1031 /////////////////////////////////////////////////
1032 // MOBILIZED BODY::CUSTOM::IMPLEMENTATION IMPL //
1033 /////////////////////////////////////////////////
1034
1035 // This class exists primarily to allow the Custom::Implementation class to keep
1036 // a pointer to its handle class's CustomImpl class which is derived from MobilizedBodyImpl
1037 // which has all the goodies that are needed for defining a MobilizedBody.
1038 //
1039 // At first this class is the owner of the CustomImpl. Then when this is put in a
1040 // Custom handle, that handle takes over ownership of the CustomImpl and the
1041 // CustomImpl takes over ownership of this ImplementationImpl object.
1042 class MobilizedBody::Custom::ImplementationImpl : public PIMPLImplementation<Implementation, ImplementationImpl> {
1043 public:
1044 // no default constructor
ImplementationImpl(CustomImpl * customImpl,int nu,int nq,int nAngles)1045 explicit ImplementationImpl(CustomImpl* customImpl, int nu, int nq, int nAngles) : isOwner(true), builtInImpl(customImpl), nu(nu), nq(nq), nAngles(nAngles) { }
1046 inline ~ImplementationImpl(); // see below -- have to wait for CustomImpl's definition
1047
1048 // Copying one of these just gives us a new one with a NULL CustomImpl pointer.
ImplementationImpl(const ImplementationImpl & src)1049 ImplementationImpl(const ImplementationImpl& src) : isOwner(false), builtInImpl(0), nu(src.nu), nq(src.nq), nAngles(src.nAngles) { }
1050
clone()1051 ImplementationImpl* clone() const {return new ImplementationImpl(*this);}
1052
isOwnerOfCustomImpl()1053 bool isOwnerOfCustomImpl() const {return builtInImpl && isOwner;}
removeOwnershipOfCustomImpl()1054 CustomImpl* removeOwnershipOfCustomImpl() {
1055 assert(isOwnerOfCustomImpl());
1056 isOwner=false;
1057 return builtInImpl;
1058 }
1059
setReferenceToCustomImpl(CustomImpl * cimpl)1060 void setReferenceToCustomImpl(CustomImpl* cimpl) {
1061 assert(!builtInImpl); // you can only do this once
1062 isOwner=false;
1063 builtInImpl = cimpl;
1064 }
1065
hasCustomImpl()1066 bool hasCustomImpl() const {return builtInImpl != 0;}
1067
getCustomImpl()1068 const CustomImpl& getCustomImpl() const {
1069 assert(builtInImpl);
1070 return *builtInImpl;
1071 }
updCustomImpl()1072 CustomImpl& updCustomImpl() {
1073 assert(builtInImpl);
1074 return *builtInImpl;
1075 }
1076
getNU()1077 int getNU() const {
1078 return nu;
1079 }
1080
getNQ()1081 int getNQ() const {
1082 return nq;
1083 }
1084
getNumAngles()1085 int getNumAngles() const {
1086 return nAngles;
1087 }
1088
1089 private:
1090 bool isOwner;
1091 CustomImpl* builtInImpl; // just a reference; not owned
1092 int nu, nq, nAngles;
1093
1094 // suppress assignment
1095 ImplementationImpl& operator=(const ImplementationImpl&);
1096 };
1097
1098 /////////////////////////////////
1099 // MOBILIZED BODY::CUSTOM IMPL //
1100 /////////////////////////////////
1101
1102 class MobilizedBody::CustomImpl : public MobilizedBodyImpl {
1103 public:
CustomImpl(Direction d)1104 explicit CustomImpl(Direction d) : MobilizedBodyImpl(d), implementation(0) { }
1105
1106 void takeOwnershipOfImplementation(Custom::Implementation* userImpl);
1107
CustomImpl(Custom::Implementation * userImpl,Direction d)1108 explicit CustomImpl(Custom::Implementation* userImpl, Direction d)
1109 : MobilizedBodyImpl(d), implementation(0) {
1110 assert(userImpl);
1111 implementation = userImpl;
1112 implementation->updImpl().setReferenceToCustomImpl(this);
1113 }
1114
1115 // Copy constructor
CustomImpl(const CustomImpl & src)1116 CustomImpl(const CustomImpl& src)
1117 : MobilizedBodyImpl(src), implementation(0) {
1118 if (src.implementation) {
1119 implementation = src.implementation->clone();
1120 implementation->updImpl().setReferenceToCustomImpl(this);
1121 }
1122 }
1123
~CustomImpl()1124 ~CustomImpl() {
1125 if (implementation)
1126 delete implementation;
1127 }
1128
clone()1129 CustomImpl* clone() const override { return new CustomImpl(*this); }
1130
getImplementation()1131 const Custom::Implementation& getImplementation() const {
1132 assert(implementation);
1133 return *implementation;
1134 }
1135
updImplementation()1136 Custom::Implementation& updImplementation() {
1137 assert(implementation);
1138 return *implementation;
1139 }
1140
1141 RigidBodyNode* createRigidBodyNode(
1142 UIndex& nextUSlot,
1143 USquaredIndex& nextUSqSlot,
1144 QIndex& nextQSlot) const override;
1145
copyOutDefaultQImpl(int nq,Real * q)1146 void copyOutDefaultQImpl(int nq, Real* q) const override {
1147 SimTK_ASSERT(nq==getImplementation().getImpl().getNQ() || nq==getImplementation().getImpl().getNQ()-1,
1148 "MobilizedBody::CustomImpl::copyOutDefaultQImpl(): wrong number of q's");
1149 for (int i = 0; i < nq; ++i)
1150 q[i] = 0.0;
1151 if (implementation->getImpl().getNumAngles() == 4)
1152 q[0] = 1.0;
1153 }
1154
1155 // Forward all the virtuals to the Custom::Implementation virtuals.
realizeTopologyVirtual(State & s)1156 void realizeTopologyVirtual(State& s) const override {getImplementation().realizeTopology(s);}
realizeModelVirtual(State & s)1157 void realizeModelVirtual (State& s) const override {getImplementation().realizeModel(s);}
realizeInstanceVirtual(const State & s)1158 void realizeInstanceVirtual(const State& s) const override {getImplementation().realizeInstance(s);}
realizeTimeVirtual(const State & s)1159 void realizeTimeVirtual (const State& s) const override {getImplementation().realizeTime(s);}
realizePositionVirtual(const State & s)1160 void realizePositionVirtual(const State& s) const override {getImplementation().realizePosition(s);}
realizeVelocityVirtual(const State & s)1161 void realizeVelocityVirtual(const State& s) const override {getImplementation().realizeVelocity(s);}
realizeDynamicsVirtual(const State & s)1162 void realizeDynamicsVirtual(const State& s) const override {getImplementation().realizeDynamics(s);}
realizeAccelerationVirtual(const State & s)1163 void realizeAccelerationVirtual
1164 (const State& s) const override {getImplementation().realizeAcceleration(s);}
realizeReportVirtual(const State & s)1165 void realizeReportVirtual (const State& s) const override {getImplementation().realizeReport(s);}
1166
calcDecorativeGeometryAndAppend(const State & s,Stage stage,Array_<DecorativeGeometry> & geom)1167 void calcDecorativeGeometryAndAppend(const State& s, Stage stage, Array_<DecorativeGeometry>& geom) const
1168 {getImplementation().calcDecorativeGeometryAndAppend(s,stage,geom);}
1169
1170 SimTK_DOWNCAST(CustomImpl, MobilizedBodyImpl);
1171 private:
1172 friend class MobilizedBody::Custom;
1173
1174 Custom::Implementation* implementation;
1175
1176 CustomImpl& operator=(const CustomImpl&); // suppress assignment
1177 };
1178
1179 // Need definition for CustomImpl here in case we have to delete it.
~ImplementationImpl()1180 inline MobilizedBody::Custom::ImplementationImpl::~ImplementationImpl() {
1181 if (isOwner)
1182 delete builtInImpl;
1183 builtInImpl=0;
1184 }
1185
1186 ////////////////////////////////////////
1187 // MOBILIZED BODY::FUNCTIONBASED IMPL //
1188 ////////////////////////////////////////
1189
1190
1191 class MobilizedBody::FunctionBasedImpl : public MobilizedBody::Custom::Implementation {
1192 public:
1193 //Constructor that uses default axes
FunctionBasedImpl(SimbodyMatterSubsystem & matter,int nmobilities,const Array_<const Function * > & functions,const Array_<Array_<int>> & coordIndices)1194 FunctionBasedImpl(SimbodyMatterSubsystem& matter, int nmobilities, const Array_<const Function*>& functions, const Array_<Array_<int> >& coordIndices)
1195 : Implementation(matter, nmobilities, nmobilities, 0), subsystem(matter.getMySubsystemIndex()), nu(nmobilities), cacheIndex(0), functions(functions), coordIndices(coordIndices), referenceCount(new int[1]) {
1196 assert(functions.size() == 6);
1197 assert(coordIndices.size() == 6);
1198 referenceCount[0] = 1;
1199 for (int i = 0; i < (int)functions.size(); ++i) {
1200 assert(functions[i]->getArgumentSize() == coordIndices[i].size());
1201 assert(functions[i]->getMaxDerivativeOrder() >= 2);
1202 }
1203 Arot = Mat33(1);
1204 Atrans = Mat33(1);
1205 }
FunctionBasedImpl(SimbodyMatterSubsystem & matter,int nmobilities,const Array_<const Function * > & functions,const Array_<Array_<int>> & coordIndices,const Array_<Vec3> & axes)1206 FunctionBasedImpl(SimbodyMatterSubsystem& matter, int nmobilities, const Array_<const Function*>& functions, const Array_<Array_<int> >& coordIndices, const Array_<Vec3>& axes)
1207 : Implementation(matter, nmobilities, nmobilities, 0), subsystem(matter.getMySubsystemIndex()), nu(nmobilities), cacheIndex(0), functions(functions), coordIndices(coordIndices), referenceCount(new int[1]) {
1208 assert(functions.size() == 6);
1209 assert(coordIndices.size() == 6);
1210 assert(axes.size() == 6);
1211 referenceCount[0] = 1;
1212 for (int i = 0; i < (int)functions.size(); ++i) {
1213 assert(functions[i]->getArgumentSize() == coordIndices[i].size());
1214 assert(functions[i]->getMaxDerivativeOrder() >= 2);
1215 }
1216 double tol = 1e-5;
1217 // Verify that none of the rotation axes are colinear
1218 assert((axes[0]%axes[1]).norm() > tol);
1219 assert((axes[0]%axes[2]).norm() > tol);
1220 assert((axes[1]%axes[2]).norm() > tol);
1221 // Verify that none of the translational axes are colinear
1222 assert((axes[3]%axes[4]).norm() > tol);
1223 assert((axes[3]%axes[5]).norm() > tol);
1224 assert((axes[4]%axes[5]).norm() > tol);
1225
1226 Arot = Mat33(axes[0].normalize(), axes[1].normalize(), axes[2].normalize());
1227 Atrans = Mat33(axes[3].normalize(), axes[4].normalize(), axes[5].normalize());
1228 }
1229
~FunctionBasedImpl()1230 ~FunctionBasedImpl() {
1231 if (--referenceCount[0] == 0) {
1232 for (int i = 0; i < (int) functions.size(); i++)
1233 delete functions[i];
1234 delete[] referenceCount;
1235 }
1236 }
1237
clone()1238 MobilizedBody::Custom::Implementation* clone() const override {
1239 referenceCount[0]++;
1240 return new FunctionBasedImpl(*this);
1241 }
1242
calcMobilizerTransformFromQ(const State & s,int nq,const Real * q)1243 Transform calcMobilizerTransformFromQ(const State& s, int nq, const Real* q) const override {
1244 // Initialize the tranformation to be returned
1245 Transform X(Vec3(0));
1246 Vec6 spatialCoords;
1247
1248 // Get the spatial cooridinates as a function of the q's
1249 for(int i=0; i < 6; i++){
1250 //Coordinates for this function
1251 int nc = coordIndices[i].size();
1252 Vector fcoords(nc);
1253
1254 for(int j=0; j < nc; j++)
1255 fcoords(j) = q[coordIndices[i][j]];
1256
1257 //default behavior of constant function should take a Vector of length 0
1258 spatialCoords(i) = functions[i]->calcValue(fcoords);
1259 }
1260
1261 /*
1262 UnitVec3 axis1;
1263 axis1.getAs(&Arot(0,0));
1264 UnitVec3 axis2;
1265 axis2.getAs(&Arot(0,1));
1266 UnitVec3 axis3;
1267 axis3.getAs(&Arot(0,2));
1268 */
1269
1270 //X.updR().setRotationToBodyFixedXYZ(spatialCoords.getSubVec<3>(0));
1271 X.updR().setRotationFromMat33TrustMe(Rotation(spatialCoords(0), UnitVec3::getAs(&Arot(0,0)))*
1272 Rotation(spatialCoords(1), UnitVec3::getAs(&Arot(0,1)))*Rotation(spatialCoords(2), UnitVec3::getAs(&Arot(0,2))));
1273 X.updP() = spatialCoords(3)*UnitVec3::getAs(&Atrans(0,0))+spatialCoords(4)*UnitVec3::getAs(&Atrans(0,1))
1274 +spatialCoords(5)*UnitVec3::getAs(&Atrans(0,2));
1275
1276 return X;
1277 }
1278
multiplyByHMatrix(const State & s,int nu,const Real * u)1279 SpatialVec multiplyByHMatrix(const State& s, int nu, const Real* u) const override {
1280
1281 switch (nu) {
1282 case 1: {
1283 // Check that the H matrices in the cache are valid
1284 if (!(Value<CacheInfo<1>>::updDowncast(s.updCacheEntry(subsystem, cacheIndex)).upd().isValidH))
1285 updateH(s);
1286
1287 Mat<2,1,Vec3> h = Value<CacheInfo<1> >::downcast(s.getCacheEntry(subsystem, cacheIndex)).get().h;
1288 return h*Vec1::getAs(u);
1289 }
1290 case 2: {
1291 // Check that the H matrices in the cache are valid
1292 if (!(Value<CacheInfo<2> >::updDowncast(s.updCacheEntry(subsystem, cacheIndex)).upd().isValidH))
1293 updateH(s);
1294
1295 Mat<2,2,Vec3> h = Value<CacheInfo<2> >::downcast(s.getCacheEntry(subsystem, cacheIndex)).get().h;
1296 return h*Vec2::getAs(u);
1297 }
1298 case 3: {
1299 // Check that the H matrices in the cache are valid
1300 if (!(Value<CacheInfo<3> >::updDowncast(s.updCacheEntry(subsystem, cacheIndex)).upd().isValidH))
1301 updateH(s);
1302
1303 Mat<2,3,Vec3> h = Value<CacheInfo<3> >::downcast(s.getCacheEntry(subsystem, cacheIndex)).get().h;
1304 return h*Vec3::getAs(u);
1305 }
1306 case 4: {
1307 // Check that the H matrices in the cache are valid
1308 if (!(Value<CacheInfo<4> >::updDowncast(s.updCacheEntry(subsystem, cacheIndex)).upd().isValidH))
1309 updateH(s);
1310
1311 Mat<2,4,Vec3> h = Value<CacheInfo<4> >::downcast(s.getCacheEntry(subsystem, cacheIndex)).get().h;
1312 return h*Vec4::getAs(u);
1313 }
1314 case 5: {
1315 // Check that the H matrices in the cache are valid
1316 if (!(Value<CacheInfo<5> >::updDowncast(s.updCacheEntry(subsystem, cacheIndex)).upd().isValidH))
1317 updateH(s);
1318
1319 Mat<2,5,Vec3> h = Value<CacheInfo<5> >::downcast(s.getCacheEntry(subsystem, cacheIndex)).get().h;
1320 return h*Vec5::getAs(u);
1321 }
1322 case 6: {
1323 // Check that the H matrices in the cache are valid
1324 if (!(Value<CacheInfo<6> >::updDowncast(s.updCacheEntry(subsystem, cacheIndex)).upd().isValidH))
1325 updateH(s);
1326
1327 Mat<2,6,Vec3> h = Value<CacheInfo<6> >::downcast(s.getCacheEntry(subsystem, cacheIndex)).get().h;
1328 return h*Vec6::getAs(u);
1329 }
1330 }
1331 SimTK_THROW5(SimTK::Exception::ValueOutOfRange, "nu", 1, nu, 6, "MobilizedBody::FunctionBasedImpl::multiplyByHMatrix");
1332 }
1333
multiplyByHTranspose(const State & s,const SpatialVec & F,int nu,Real * f)1334 void multiplyByHTranspose(const State& s, const SpatialVec& F, int nu, Real* f) const override {
1335
1336 switch (nu) {
1337 case 1: {
1338 // Check that the H and Hdot matrices in the cache are valid
1339 if (!(Value<CacheInfo<1> >::updDowncast(s.updCacheEntry(subsystem, cacheIndex)).upd().isValidH))
1340 updateH(s);
1341
1342 Mat<2,1,Vec3> h = Value<CacheInfo<1> >::downcast(s.getCacheEntry(subsystem, cacheIndex)).get().h;
1343 Vec1::updAs(f) = ~h*F;
1344 return;
1345 }
1346 case 2: {
1347 // Check that the H and Hdot matrices in the cache are valid
1348 if (!(Value<CacheInfo<2> >::updDowncast(s.updCacheEntry(subsystem, cacheIndex)).upd().isValidH))
1349 updateH(s);
1350
1351 Mat<2,2,Vec3> h = Value<CacheInfo<2> >::downcast(s.getCacheEntry(subsystem, cacheIndex)).get().h;
1352 Vec2::updAs(f) = ~h*F;
1353 return;
1354 }
1355 case 3: {
1356 // Check that the H and Hdot matrices in the cache are valid
1357 if (!(Value<CacheInfo<3> >::updDowncast(s.updCacheEntry(subsystem, cacheIndex)).upd().isValidH))
1358 updateH(s);
1359
1360 Mat<2,3,Vec3> h = Value<CacheInfo<3> >::downcast(s.getCacheEntry(subsystem, cacheIndex)).get().h;
1361 Vec3::updAs(f) = ~h*F;
1362 return;
1363 }
1364 case 4: {
1365 // Check that the H and Hdot matrices in the cache are valid
1366 if (!(Value<CacheInfo<4> >::updDowncast(s.updCacheEntry(subsystem, cacheIndex)).upd().isValidH))
1367 updateH(s);
1368
1369 Mat<2,4,Vec3> h = Value<CacheInfo<4> >::downcast(s.getCacheEntry(subsystem, cacheIndex)).get().h;
1370 Vec4::updAs(f) = ~h*F;
1371 return;
1372 }
1373 case 5: {
1374 // Check that the H and Hdot matrices in the cache are valid
1375 if (!(Value<CacheInfo<5> >::updDowncast(s.updCacheEntry(subsystem, cacheIndex)).upd().isValidH))
1376 updateH(s);
1377
1378 Mat<2,5,Vec3> h = Value<CacheInfo<5> >::downcast(s.getCacheEntry(subsystem, cacheIndex)).get().h;
1379 Vec5::updAs(f) = ~h*F;
1380 return;
1381 }
1382 case 6: {
1383 // Check that the H and Hdot matrices in the cache are valid
1384 if (!(Value<CacheInfo<6> >::updDowncast(s.updCacheEntry(subsystem, cacheIndex)).upd().isValidH))
1385 updateH(s);
1386
1387 Mat<2,6,Vec3> h = Value<CacheInfo<6> >::downcast(s.getCacheEntry(subsystem, cacheIndex)).get().h;
1388 Vec6::updAs(f) = ~h*F;
1389 return;
1390 }
1391 }
1392 SimTK_THROW5(SimTK::Exception::ValueOutOfRange, "nu", 1, nu, 6, "MobilizedBody::FunctionBasedImpl::multiplyByHTranspose");
1393 }
1394
multiplyByHDotMatrix(const State & s,int nu,const Real * u)1395 SpatialVec multiplyByHDotMatrix(const State& s, int nu, const Real* u) const override {
1396
1397 switch (nu) {
1398 case 1: {
1399 // Check that the H and Hdot matrices in the cache are valid
1400 if (!(Value<CacheInfo<1> >::updDowncast(s.updCacheEntry(subsystem, cacheIndex)).upd().isValidHdot))
1401 updateHdot(s);
1402
1403 Mat<2,1,Vec3> hdot = Value<CacheInfo<1> >::downcast(s.getCacheEntry(subsystem, cacheIndex)).get().hdot;
1404 return hdot*Vec1::getAs(u);
1405 }
1406 case 2: {
1407 // Check that the H and Hdot matrices in the cache are valid
1408 if (!(Value<CacheInfo<2> >::updDowncast(s.updCacheEntry(subsystem, cacheIndex)).upd().isValidHdot))
1409 updateHdot(s);
1410
1411 Mat<2,2,Vec3> hdot = Value<CacheInfo<2> >::downcast(s.getCacheEntry(subsystem, cacheIndex)).get().hdot;
1412 return hdot*Vec2::getAs(u);
1413 }
1414 case 3: {
1415 // Check that the H and Hdot matrices in the cache are valid
1416 if (!(Value<CacheInfo<3> >::updDowncast(s.updCacheEntry(subsystem, cacheIndex)).upd().isValidHdot))
1417 updateHdot(s);
1418
1419 Mat<2,3,Vec3> hdot = Value<CacheInfo<3> >::downcast(s.getCacheEntry(subsystem, cacheIndex)).get().hdot;
1420 return hdot*Vec3::getAs(u);
1421 }
1422 case 4: {
1423 // Check that the H and Hdot matrices in the cache are valid
1424 if (!(Value<CacheInfo<4> >::updDowncast(s.updCacheEntry(subsystem, cacheIndex)).upd().isValidHdot))
1425 updateHdot(s);
1426
1427 Mat<2,4,Vec3> hdot = Value<CacheInfo<4> >::downcast(s.getCacheEntry(subsystem, cacheIndex)).get().hdot;
1428 return hdot*Vec4::getAs(u);
1429 }
1430 case 5: {
1431 // Check that the H and Hdot matrices in the cache are valid
1432 if (!(Value<CacheInfo<5> >::updDowncast(s.updCacheEntry(subsystem, cacheIndex)).upd().isValidHdot))
1433 updateHdot(s);
1434
1435 Mat<2,5,Vec3> hdot = Value<CacheInfo<5> >::downcast(s.getCacheEntry(subsystem, cacheIndex)).get().hdot;
1436 return hdot*Vec5::getAs(u);
1437 }
1438 case 6: {
1439 // Check that the H and Hdot matrices in the cache are valid
1440 if (!(Value<CacheInfo<6> >::updDowncast(s.updCacheEntry(subsystem, cacheIndex)).upd().isValidHdot))
1441 updateHdot(s);
1442
1443 Mat<2,6,Vec3> hdot = Value<CacheInfo<6> >::downcast(s.getCacheEntry(subsystem, cacheIndex)).get().hdot;
1444 return hdot*Vec6::getAs(u);
1445 }
1446 }
1447 SimTK_THROW5(SimTK::Exception::ValueOutOfRange, "nu", 1, nu, 6, "MobilizedBody::FunctionBasedImpl::multiplyByHDotMatrix");
1448 }
1449
multiplyByHDotTranspose(const State & s,const SpatialVec & F,int nu,Real * f)1450 void multiplyByHDotTranspose(const State& s, const SpatialVec& F, int nu, Real* f) const override {
1451
1452 switch (nu) {
1453 case 1: {
1454 // Check that the H and Hdot matrices in the cache are valid
1455 if (!(Value<CacheInfo<1> >::updDowncast(s.updCacheEntry(subsystem, cacheIndex)).upd().isValidHdot))
1456 updateHdot(s);
1457
1458 Mat<2,1,Vec3> hdot = Value<CacheInfo<1> >::downcast(s.getCacheEntry(subsystem, cacheIndex)).get().hdot;
1459 Vec1::updAs(f) = ~hdot*F;
1460 return;
1461 }
1462 case 2: {
1463 // Check that the H and Hdot matrices in the cache are valid
1464 if (!(Value<CacheInfo<2> >::updDowncast(s.updCacheEntry(subsystem, cacheIndex)).upd().isValidHdot))
1465 updateHdot(s);
1466
1467 Mat<2,2,Vec3> hdot = Value<CacheInfo<2> >::downcast(s.getCacheEntry(subsystem, cacheIndex)).get().hdot;
1468 Vec2::updAs(f) = ~hdot*F;
1469 return;
1470 }
1471 case 3: {
1472 // Check that the H and Hdot matrices in the cache are valid
1473 if (!(Value<CacheInfo<3> >::updDowncast(s.updCacheEntry(subsystem, cacheIndex)).upd().isValidHdot))
1474 updateHdot(s);
1475
1476 Mat<2,3,Vec3> hdot = Value<CacheInfo<3> >::downcast(s.getCacheEntry(subsystem, cacheIndex)).get().hdot;
1477 Vec3::updAs(f) = ~hdot*F;
1478 return;
1479 }
1480 case 4: {
1481 // Check that the H and Hdot matrices in the cache are valid
1482 if (!(Value<CacheInfo<4> >::updDowncast(s.updCacheEntry(subsystem, cacheIndex)).upd().isValidHdot))
1483 updateHdot(s);
1484
1485 Mat<2,4,Vec3> hdot = Value<CacheInfo<4> >::downcast(s.getCacheEntry(subsystem, cacheIndex)).get().hdot;
1486 Vec4::updAs(f) = ~hdot*F;
1487 return;
1488 }
1489 case 5: {
1490 // Check that the H and Hdot matrices in the cache are valid
1491 if (!(Value<CacheInfo<5> >::updDowncast(s.updCacheEntry(subsystem, cacheIndex)).upd().isValidHdot))
1492 updateHdot(s);
1493
1494 Mat<2,5,Vec3> hdot = Value<CacheInfo<5> >::downcast(s.getCacheEntry(subsystem, cacheIndex)).get().hdot;
1495 Vec5::updAs(f) = ~hdot*F;
1496 return;
1497 }
1498 case 6: {
1499 // Check that the H and Hdot matrices in the cache are valid
1500 if (!(Value<CacheInfo<6> >::updDowncast(s.updCacheEntry(subsystem, cacheIndex)).upd().isValidHdot))
1501 updateHdot(s);
1502
1503 Mat<2,6,Vec3> hdot = Value<CacheInfo<6> >::downcast(s.getCacheEntry(subsystem, cacheIndex)).get().hdot;
1504 Vec6::updAs(f) = ~hdot*F;
1505 return;
1506 }
1507 }
1508 SimTK_THROW5(SimTK::Exception::ValueOutOfRange, "nu", 1, nu, 6, "MobilizedBody::FunctionBasedImpl::multiplyByHDotTranspose");
1509 }
1510
realizeTopology(State & s)1511 void realizeTopology(State& s) const override {
1512 switch (nu) {
1513 case 1:
1514 cacheIndex = s.allocateCacheEntry(subsystem, Stage::Topology, new Value<CacheInfo<1> >());
1515 break;
1516 case 2:
1517 cacheIndex = s.allocateCacheEntry(subsystem, Stage::Topology, new Value<CacheInfo<2> >());
1518 break;
1519 case 3:
1520 cacheIndex = s.allocateCacheEntry(subsystem, Stage::Topology, new Value<CacheInfo<3> >());
1521 break;
1522 case 4:
1523 cacheIndex = s.allocateCacheEntry(subsystem, Stage::Topology, new Value<CacheInfo<4> >());
1524 break;
1525 case 5:
1526 cacheIndex = s.allocateCacheEntry(subsystem, Stage::Topology, new Value<CacheInfo<5> >());
1527 break;
1528 case 6:
1529 cacheIndex = s.allocateCacheEntry(subsystem, Stage::Topology, new Value<CacheInfo<6> >());
1530 break;
1531 }
1532 }
1533
realizePosition(const State & s)1534 void realizePosition(const State& s) const override {
1535 switch (nu) {
1536 case 1: {
1537 // invalidate H and Hdot matrices
1538 Value<CacheInfo<1> >::updDowncast(s.updCacheEntry(subsystem, cacheIndex)).upd().isValidH = false;
1539 break;
1540 }
1541 case 2: {
1542 // invalidate H and Hdot matrices
1543 Value<CacheInfo<2> >::updDowncast(s.updCacheEntry(subsystem, cacheIndex)).upd().isValidH = false;
1544 break;
1545 }
1546 case 3: {
1547 // invalidate H and Hdot matrices
1548 Value<CacheInfo<3> >::updDowncast(s.updCacheEntry(subsystem, cacheIndex)).upd().isValidH = false;
1549 break;
1550 }
1551 case 4: {
1552 // invalidate H and Hdot matrices
1553 Value<CacheInfo<4> >::updDowncast(s.updCacheEntry(subsystem, cacheIndex)).upd().isValidH = false;
1554 break;
1555 }
1556 case 5: {
1557 // invalidate H and Hdot matrices
1558 Value<CacheInfo<5> >::updDowncast(s.updCacheEntry(subsystem, cacheIndex)).upd().isValidH = false;
1559 break;
1560 }
1561 case 6: {
1562 // invalidate H and Hdot matrices
1563 Value<CacheInfo<6> >::updDowncast(s.updCacheEntry(subsystem, cacheIndex)).upd().isValidH = false;
1564 break;
1565 }
1566 }
1567 }
1568
realizeVelocity(const State & s)1569 void realizeVelocity(const State& s) const override {
1570 switch (nu) {
1571 case 1: {
1572 // invalidate H and Hdot matrices
1573 Value<CacheInfo<1> >::updDowncast(s.updCacheEntry(subsystem, cacheIndex)).upd().isValidHdot = false;
1574 break;
1575 }
1576 case 2: {
1577 // invalidate H and Hdot matrices
1578 Value<CacheInfo<2> >::updDowncast(s.updCacheEntry(subsystem, cacheIndex)).upd().isValidHdot = false;
1579 break;
1580 }
1581 case 3: {
1582 // invalidate H and Hdot matrices
1583 Value<CacheInfo<3> >::updDowncast(s.updCacheEntry(subsystem, cacheIndex)).upd().isValidHdot = false;
1584 break;
1585 }
1586 case 4: {
1587 // invalidate H and Hdot matrices
1588 Value<CacheInfo<4> >::updDowncast(s.updCacheEntry(subsystem, cacheIndex)).upd().isValidHdot = false;
1589 break;
1590 }
1591 case 5: {
1592 // invalidate H and Hdot matrices
1593 Value<CacheInfo<5> >::updDowncast(s.updCacheEntry(subsystem, cacheIndex)).upd().isValidHdot = false;
1594 break;
1595 }
1596 case 6: {
1597 // invalidate H and Hdot matrices
1598 Value<CacheInfo<6> >::updDowncast(s.updCacheEntry(subsystem, cacheIndex)).upd().isValidHdot = false;
1599 break;
1600 }
1601 }
1602 }
1603
updateH(const State & s)1604 void updateH(const State& s) const {
1605 // Get mobilizer kinematics
1606 Vector q = getQ(s);
1607 Vector u = getU(s);
1608 switch (nu) {
1609 case 1: {
1610 CacheInfo<1>& cache = Value<CacheInfo<1> >::updDowncast(s.updCacheEntry(subsystem, cacheIndex)).upd();
1611 cache.buildH(q, u, getMobilizerTransform(s), functions, coordIndices, Arot, Atrans);
1612 // H matrix is now valid
1613 cache.isValidH = true;
1614 break;
1615 }
1616 case 2: {
1617 CacheInfo<2>& cache = Value<CacheInfo<2> >::updDowncast(s.updCacheEntry(subsystem, cacheIndex)).upd();
1618 cache.buildH(q, u, getMobilizerTransform(s), functions, coordIndices, Arot, Atrans);
1619 // H matrix is now valid
1620 cache.isValidH = true;
1621 break;
1622 }
1623 case 3: {
1624 CacheInfo<3>& cache = Value<CacheInfo<3> >::updDowncast(s.updCacheEntry(subsystem, cacheIndex)).upd();
1625 cache.buildH(q, u, getMobilizerTransform(s), functions, coordIndices, Arot, Atrans);
1626 // H matrix is now valid
1627 cache.isValidH = true;
1628 break;
1629 }
1630 case 4: {
1631 CacheInfo<4>& cache = Value<CacheInfo<4> >::updDowncast(s.updCacheEntry(subsystem, cacheIndex)).upd();
1632 cache.buildH(q, u, getMobilizerTransform(s), functions, coordIndices, Arot, Atrans);
1633 // H matrix is now valid
1634 cache.isValidH = true;
1635 break;
1636 }
1637 case 5: {
1638 CacheInfo<5>& cache = Value<CacheInfo<5> >::updDowncast(s.updCacheEntry(subsystem, cacheIndex)).upd();
1639 cache.buildH(q, u, getMobilizerTransform(s), functions, coordIndices, Arot, Atrans);
1640 // H matrix is now valid
1641 cache.isValidH = true;
1642 break;
1643 }
1644 case 6: {
1645 CacheInfo<6>& cache = Value<CacheInfo<6> >::updDowncast(s.updCacheEntry(subsystem, cacheIndex)).upd();
1646 cache.buildH(q, u, getMobilizerTransform(s), functions, coordIndices, Arot, Atrans);
1647 // H matrix is now valid
1648 cache.isValidH = true;
1649 break;
1650 }
1651 }
1652 }
1653
updateHdot(const State & s)1654 void updateHdot(const State& s) const {
1655 // Get mobilizer kinematics
1656 Vector q = getQ(s);
1657 Vector u = getU(s);
1658 switch (nu) {
1659 case 1: {
1660 CacheInfo<1>& cache = Value<CacheInfo<1> >::updDowncast(s.updCacheEntry(subsystem, cacheIndex)).upd();
1661 cache.buildHdot(q, u, getMobilizerTransform(s), functions, coordIndices, Arot, Atrans);
1662 // Hdot matrix is now valid
1663 cache.isValidHdot = true;
1664 break;
1665 }
1666 case 2: {
1667 CacheInfo<2>& cache = Value<CacheInfo<2> >::updDowncast(s.updCacheEntry(subsystem, cacheIndex)).upd();
1668 cache.buildHdot(q, u, getMobilizerTransform(s), functions, coordIndices, Arot, Atrans);
1669 // Hdot matrix is now valid
1670 cache.isValidHdot = true;
1671 break;
1672 }
1673 case 3: {
1674 CacheInfo<3>& cache = Value<CacheInfo<3> >::updDowncast(s.updCacheEntry(subsystem, cacheIndex)).upd();
1675 cache.buildHdot(q, u, getMobilizerTransform(s), functions, coordIndices, Arot, Atrans);
1676 // Hdot matrix is now valid
1677 cache.isValidHdot = true;
1678 break;
1679 }
1680 case 4: {
1681 CacheInfo<4>& cache = Value<CacheInfo<4> >::updDowncast(s.updCacheEntry(subsystem, cacheIndex)).upd();
1682 cache.buildHdot(q, u, getMobilizerTransform(s), functions, coordIndices, Arot, Atrans);
1683 // Hdot matrix is now valid
1684 cache.isValidHdot = true;
1685 break;
1686 }
1687 case 5: {
1688 CacheInfo<5>& cache = Value<CacheInfo<5> >::updDowncast(s.updCacheEntry(subsystem, cacheIndex)).upd();
1689 cache.buildHdot(q, u, getMobilizerTransform(s), functions, coordIndices, Arot, Atrans);
1690 // Hdot matrix is now valid
1691 cache.isValidHdot = true;
1692 break;
1693 }
1694 case 6: {
1695 CacheInfo<6>& cache = Value<CacheInfo<6> >::updDowncast(s.updCacheEntry(subsystem, cacheIndex)).upd();
1696 cache.buildHdot(q, u, getMobilizerTransform(s), functions, coordIndices, Arot, Atrans);
1697 // Hdot matrix is now valid
1698 cache.isValidHdot = true;
1699 break;
1700 }
1701 }
1702 }
1703
1704 private:
1705 const SubsystemIndex subsystem;
1706 const int nu;
1707 mutable CacheEntryIndex cacheIndex;
1708 const Array_<const Function*> functions;
1709 const Array_<Array_<int> > coordIndices;
1710 int* referenceCount;
1711 //const Array_<Vec3> axes;
1712 Mat33 Arot, Atrans;
1713 template <int N> class CacheInfo {
1714 public:
CacheInfo()1715 CacheInfo() : isValidH(false), isValidHdot(false) { }
1716
buildH(Vector & q,Vector & u,const Transform & X_FM,const Array_<const Function * > & functions,const Array_<Array_<int>> & coordIndices,const Mat33 Arot,const Mat33 Atrans)1717 void buildH(Vector& q, Vector& u, const Transform& X_FM, const Array_<const Function*>& functions, const Array_<Array_<int> >& coordIndices, const Mat33 Arot, const Mat33 Atrans)
1718 {
1719 // Build the Fq and Fqq matrices of partials of the spatial functions with respect to the gen coordinates, q
1720 // Cycle through each row (function describing spatial coordinate)
1721 Fq = Mat<6,N>(0);
1722 Vec6 spatialCoords(0);
1723 Array_<int> deriv(1);
1724 Vector fcoords(coordIndices[0].size());
1725
1726 for(int i=0; i < 6; i++){
1727 // Determine the number of coordinates for this function
1728 int nc = coordIndices[i].size();
1729 if (fcoords.size() != nc)
1730 fcoords.resize(nc);
1731
1732 if (nc > 0) {
1733 // Get coordinate values to evaluate the function
1734 for(int k = 0; k < nc; k++)
1735 fcoords(k) = q(coordIndices[i][k]);
1736
1737 for (int j = 0; j < nc; j++) {
1738 deriv[0] = j;
1739 Fq(i, coordIndices[i][j]) = functions[i]->calcDerivative(deriv, fcoords);
1740 }
1741 //default behavior of constant function should take a Vector of length 0
1742 spatialCoords(i) = functions[i]->calcValue(fcoords);
1743 }
1744
1745 }
1746 // Note rotations are body fixed sequences, and so taking the derivative yields a body-fixed angular velocity
1747 // and therefore, must be transformed to the parent frame.
1748
1749 // omega = [a1, R_F1*a2, R_F2*a3]*{Theta_dot(q)}, where Theta_dot(q) is described by the first three functions
1750 // = [W*[Fq_theta]*qdot
1751 // vel = [A]*{X_dot(q)}, where X_dot(q) is described by the last three functions
1752 // = [A]*[Fq_x]*qdot
1753
1754 Rotation R_F1 = Rotation(spatialCoords(0), UnitVec3::getAs(&Arot(0,0)));
1755 Rotation R_F2 = R_F1*Rotation(spatialCoords(1), UnitVec3::getAs(&Arot(0,1)));
1756
1757 Mat31 temp;
1758 Mat33 W(UnitVec3::getAs(&Arot(0,0)), R_F1*(UnitVec3::getAs(&Arot(0,1))), R_F2*(UnitVec3::getAs(&Arot(0,2))));
1759
1760 for(int i=0; i < N; i++){
1761 temp = W*(Fq.template getSubMat<3,1>(0,i));
1762 h(0,i) = Vec3::getAs(&temp(0,0));
1763 temp = Atrans*(Fq.template getSubMat<3,1>(3,i));
1764 h(1,i) = Vec3::getAs(&temp(0,0));
1765 }
1766 }
1767
buildHdot(Vector & q,Vector & u,const Transform & X_FM,const Array_<const Function * > & functions,const Array_<Array_<int>> & coordIndices,const Mat33 Arot,const Mat33 Atrans)1768 void buildHdot(Vector& q, Vector& u, const Transform& X_FM, const Array_<const Function*>& functions, const Array_<Array_<int> >& coordIndices, const Mat33 Arot, const Mat33 Atrans)
1769 {
1770 Mat<6,N> Fqdot(0);
1771 Vec6 spatialCoords;
1772 Array_<int> derivs(2);
1773 Vector fcoords(coordIndices[0].size());
1774
1775 for(int i=0; i < 6; i++){
1776 // Determine the number of coordinates for this function
1777 int nc = coordIndices[i].size();
1778 if (fcoords.size() != nc)
1779 fcoords.resize(nc);
1780
1781 if (nc > 0) {
1782 // Get coordinate values to evaluate the function
1783 for(int k = 0; k < nc; k++)
1784 fcoords(k) = q(coordIndices[i][k]);
1785
1786 // function is dependent on a mobility if its index is in the list of function coordIndices
1787 // cycle through the mobilities
1788 for (int j = 0; j < nc; j++) {
1789 derivs[0] = j;
1790 for (int k = 0; k < nc; k++) {
1791 derivs[1] = k;
1792 Fqdot(i, coordIndices[i][j]) += functions[i]->calcDerivative(derivs, fcoords)*u[coordIndices[i][k]];
1793 }
1794 }
1795 }
1796 //default behavior of constant function should take a Vector of length 0
1797 spatialCoords(i) = functions[i]->calcValue(fcoords);
1798 }
1799
1800 Rotation R_F1 = Rotation(spatialCoords(0), UnitVec3::getAs(&Arot(0,0)));
1801 Rotation R_F2 = R_F1*Rotation(spatialCoords(1), UnitVec3::getAs(&Arot(0,1)));
1802
1803 Mat33 W(UnitVec3::getAs(&Arot(0,0)), R_F1*(UnitVec3::getAs(&Arot(0,1))), R_F2*(UnitVec3::getAs(&Arot(0,2))));
1804
1805 // Hdot_theta = [Wdot]*[Fq] + [W][Fqdot]
1806 // Hdot_x = [A]*[Fqdot]
1807 Real *up = &u[0];
1808 Vec<N> uv = Vec<N>::getAs(up);
1809 Vec<N> uv1 = uv;
1810 Vec<N> uv2 = uv;
1811
1812 for(int i=1; i < N; i++){
1813 uv1(i) = 0;
1814 }
1815 if(N > 2)
1816 uv2(2) = 0;
1817
1818 // Spatial velocity
1819 SpatialVec V = h*uv;
1820 SpatialVec V1 = h*uv1;
1821 SpatialVec V2 = h*uv2;
1822 // First V[0] is angular velocity, omega
1823 //Mat33 Wdot(Vec3(0), Vec3(V[0](0),0,0)%(Vec3::getAs(&W(0,1))), Vec3(V[0](0),V[0](1),0)%(Vec3::getAs(&W(0,2))));
1824 Mat33 Wdot(Vec3(0), V1[0]%(Vec3::getAs(&W(0,1))), V2[0]%(Vec3::getAs(&W(0,2))));
1825
1826 //Sanity check Omega == V[0]
1827 Mat31 Omega = W*(Fq.template getSubMat<3,N>(0,0))*(Mat<N,1>::getAs(up));
1828
1829 Mat31 temp;
1830 for(int i=0; i < N; i++){
1831 temp = Wdot*(Fq.template getSubMat<3,1>(0,i))+W*(Fqdot.template getSubMat<3,1>(0,i));
1832 hdot(0,i) = Vec3::getAs(&temp(0,0));
1833 temp = Atrans*(Fqdot.template getSubMat<3,1>(3,i));
1834 hdot(1,i) = Vec3::getAs(&temp(0,0));
1835 }
1836 }
1837
1838 Mat<2,N,Vec3> h, hdot;
1839 Mat<6,N> Fq;
1840 bool isValidH;
1841 bool isValidHdot;
1842 };
1843
1844 };
1845
1846 } // namespace SimTK
1847
1848 #endif // SimTK_SIMBODY_MOBILIZED_BODY_IMPL_H_
1849