1 /* -------------------------------------------------------------------------- *
2  *                               Simbody(tm)                                  *
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) 2011-15 Stanford University and the Authors.        *
10  * Authors: Peter Eastman                                                     *
11  * Contributors: Michael Sherman                                              *
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 #include "SimbodyMatterSubsystemRep.h"
25 #include "RigidBodyNode.h"
26 #include "MobilizedBodyImpl.h"
27 #include "RigidBodyNodeSpec_Translation.h"
28 
29 /* This is a specialized class used for MobilizedBody::Translation objects that
30 satisfy all of the following requirements:
31  -  The body has no children.
32  -  The body's parent is ground.
33  -  The inboard and outboard transforms are both identities.
34  -  The body is not reversed.
35 
36  These assumptions allow lots of routines to be implemented simpler and faster.
37  */
38 class RBNodeLoneParticle : public RigidBodyNode {
39 public:
RBNodeLoneParticle(const MassProperties & mProps_B,UIndex & nextUSlot,USquaredIndex & nextUSqSlot,QIndex & nextQSlot)40 RBNodeLoneParticle(const MassProperties& mProps_B,
41                    UIndex&               nextUSlot,
42                    USquaredIndex&        nextUSqSlot,
43                    QIndex&               nextQSlot)
44 :   RigidBodyNode(mProps_B, Vec3(0), Vec3(0),
45                   QDotIsAlwaysTheSameAsU, QuaternionIsNeverUsed, false) {
46     uIndex = nextUSlot;
47     uSqIndex = nextUSqSlot;
48     qIndex = nextQSlot;
49     nextUSlot += 3;
50     nextUSqSlot += 9;
51     nextQSlot += 3;
52 }
53 
type() const54 const char* type() const override {return "loneparticle";}
getDOF() const55 int  getDOF() const override {return 3;}
getMaxNQ() const56 int  getMaxNQ() const override {return 3;}
57 
getNQInUse(const SBModelVars &) const58 int getNQInUse(const SBModelVars&) const override {return 3;}
getNUInUse(const SBModelVars &) const59 int getNUInUse(const SBModelVars&) const override {return 3;}
60 
isUsingQuaternion(const SBStateDigest &,MobilizerQIndex & startOfQuaternion) const61 bool isUsingQuaternion(const SBStateDigest&,
62                        MobilizerQIndex& startOfQuaternion) const override {
63     return false;
64 }
65 
calcQPoolSize(const SBModelVars &) const66 int calcQPoolSize(const SBModelVars&) const override {
67     return 0;
68 }
69 
performQPrecalculations(const SBStateDigest & sbs,const Real * q,int nq,Real * qCache,int nQCache,Real * qErr,int nQErr) const70 void performQPrecalculations(const SBStateDigest& sbs,
71                              const Real* q, int nq,
72                              Real* qCache,  int nQCache,
73                              Real* qErr,    int nQErr) const override {
74     assert(q && nq==3 && nQCache==0 && nQErr==0);
75 }
76 
calcX_FM(const SBStateDigest & sbs,const Real * q,int nq,const Real * qCache,int nQCache,Transform & X_FM) const77 void calcX_FM(const SBStateDigest& sbs,
78               const Real* q,      int nq,
79               const Real* qCache, int nQCache,
80               Transform&  X_FM) const override {
81     assert(q && nq==3 && nQCache==0);
82     X_FM = Transform(Rotation(), Vec3::getAs(&q[0]));
83 }
84 
calcQDot(const SBStateDigest &,const Real * u,Real * qdot) const85 void calcQDot(const SBStateDigest&, const Real* u, Real* qdot) const override {
86     Vec3::updAs(qdot) = Vec3::getAs(u);
87 }
calcQDotDot(const SBStateDigest &,const Real * udot,Real * qdotdot) const88 void calcQDotDot(const SBStateDigest&, const Real* udot,
89                  Real* qdotdot) const override {
90     Vec3::updAs(qdotdot) = Vec3::getAs(udot);
91 }
92 
multiplyByN(const SBStateDigest &,bool matrixOnRight,const Real * in,Real * out) const93 void multiplyByN(const SBStateDigest&, bool matrixOnRight, const Real* in,
94                  Real* out) const override {
95     Vec3::updAs(out) = Vec3::getAs(in);
96 }
multiplyByNInv(const SBStateDigest &,bool matrixOnRight,const Real * in,Real * out) const97 void multiplyByNInv(const SBStateDigest&, bool matrixOnRight, const Real* in,
98                     Real* out) const override {
99     Vec3::updAs(out) = Vec3::getAs(in);
100 }
multiplyByNDot(const SBStateDigest &,bool matrixOnRight,const Real * in,Real * out) const101 void multiplyByNDot(const SBStateDigest&, bool matrixOnRight, const Real* in,
102                     Real* out) const override {
103     Vec3::updAs(out) = 0;
104 }
105 
enforceQuaternionConstraints(const SBStateDigest & sbs,Vector & q,Vector & qErrest) const106 bool enforceQuaternionConstraints(
107     const SBStateDigest& sbs,
108     Vector&            q,
109     Vector&            qErrest) const override {
110     return false;
111 }
112 
convertToEulerAngles(const Vector & inputQ,Vector & outputQ) const113 void convertToEulerAngles(const Vector& inputQ,
114                           Vector& outputQ) const override {
115     Vec3::updAs(&outputQ[qIndex]) = Vec3::getAs(&inputQ[qIndex]);
116 }
117 
convertToQuaternions(const Vector & inputQ,Vector & outputQ) const118 void convertToQuaternions(const Vector& inputQ,
119                           Vector& outputQ) const override {
120     Vec3::updAs(&outputQ[qIndex]) = Vec3::getAs(&inputQ[qIndex]);
121 }
122 
setMobilizerDefaultModelValues(const SBTopologyCache &,SBModelVars &) const123 void setMobilizerDefaultModelValues
124    (const SBTopologyCache&, SBModelVars&)        const override {}
125 
setMobilizerDefaultInstanceValues(const SBModelVars &,SBInstanceVars &) const126 void setMobilizerDefaultInstanceValues
127    (const SBModelVars&,     SBInstanceVars&)     const override {}
setMobilizerDefaultTimeValues(const SBModelVars &,SBTimeVars &) const128 void setMobilizerDefaultTimeValues
129    (const SBModelVars&,     SBTimeVars&)         const override {}
setMobilizerDefaultPositionValues(const SBModelVars &,Vector & q) const130 void setMobilizerDefaultPositionValues
131    (const SBModelVars&,     Vector& q)           const override {q = 0;}
setMobilizerDefaultVelocityValues(const SBModelVars &,Vector & u) const132 void setMobilizerDefaultVelocityValues
133    (const SBModelVars&,     Vector& u)           const override {u = 0;}
setMobilizerDefaultDynamicsValues(const SBModelVars &,SBDynamicsVars &) const134 void setMobilizerDefaultDynamicsValues
135    (const SBModelVars&,     SBDynamicsVars&)     const override {}
setMobilizerDefaultAccelerationValues(const SBModelVars &,SBAccelerationVars &) const136 void setMobilizerDefaultAccelerationValues
137    (const SBModelVars&,     SBAccelerationVars&) const override {}
138 
realizeModel(SBStateDigest & sbs) const139 void realizeModel(SBStateDigest& sbs) const override {
140 }
141 
realizeInstance(const SBStateDigest & sbs) const142 void realizeInstance(const SBStateDigest& sbs) const override {
143     // Initialize cache entries that will never be changed at later stages.
144 
145     SBTreePositionCache& pc = sbs.updTreePositionCache();
146     SBTreeVelocityCache& vc = sbs.updTreeVelocityCache();
147     SBDynamicsCache& dc = sbs.updDynamicsCache();
148     SBTreeAccelerationCache& ac = sbs.updTreeAccelerationCache();
149     Transform& X_FM = toB(pc.bodyJointInParentJointFrame);
150     X_FM.updR().setRotationToIdentityMatrix();
151     updV_FM(vc)[0] = Vec3(0);
152     updV_PB_G(vc)[0] = Vec3(0);
153     updVD_PB_G(vc)[0] = Vec3(0);
154     updV_GB(vc)[0] = Vec3(0);
155     updGyroscopicForce(vc) = SpatialVec(Vec3(0), Vec3(0));
156     updMobilizerCoriolisAcceleration(vc) = SpatialVec(Vec3(0), Vec3(0));
157     updTotalCoriolisAcceleration(vc) = SpatialVec(Vec3(0), Vec3(0));
158     updTotalCentrifugalForces(vc) = SpatialVec(Vec3(0), Vec3(0));
159     updY(dc) = SpatialMat(Mat33(0), Mat33(0), Mat33(0), Mat33(1/getMass()));
160     updA_GB(ac)[0] = Vec3(0);
161 }
162 
realizePosition(const SBStateDigest & sbs) const163 void realizePosition(const SBStateDigest& sbs) const override {
164     SBTreePositionCache& pc = sbs.updTreePositionCache();
165     Transform& X_FM = toB(pc.bodyJointInParentJointFrame);
166     const Vec3& q = Vec3::getAs(&sbs.getQ()[qIndex]);
167     X_FM.updP() = q;
168     updX_PB(pc) = X_FM;
169     updX_GB(pc) = X_FM;
170     updPhi(pc) = PhiMatrix(q);
171     updCOM_G(pc) = q + getCOM_B(); // 3 flops
172     updMk_G(pc) = SpatialInertia(getMass(), getCOM_B(), getUnitInertia_OB_B());
173 }
174 
realizeVelocity(const SBStateDigest & sbs) const175 void realizeVelocity(const SBStateDigest& sbs) const override {
176     SBTreeVelocityCache& vc = sbs.updTreeVelocityCache();
177     const Vector& allU = sbs.getU();
178     const Vec3& u = Vec3::getAs(&allU[uIndex]);
179 
180     Vec3::updAs(&sbs.updQDot()[qIndex]) = Vec3::getAs(&allU[uIndex]);
181     updV_FM(vc)[1] = u;
182     updV_PB_G(vc)[1] = u;
183     updV_GB(vc)[1] = u;
184 }
185 
realizeDynamics(const SBStateDigest &) const186 void realizeDynamics(const SBStateDigest&) const override {
187 }
188 
189 // There is no realizeAcceleration().
190 
realizeReport(const SBStateDigest &) const191 void realizeReport(const SBStateDigest&) const override {
192 }
193 
realizeArticulatedBodyInertiasInward(const SBInstanceCache & ic,const SBTreePositionCache & pc,SBArticulatedBodyInertiaCache & abc) const194 void realizeArticulatedBodyInertiasInward(
195         const SBInstanceCache&          ic,
196         const SBTreePositionCache&      pc,
197         SBArticulatedBodyInertiaCache&  abc) const override {
198     ArticulatedInertia& P     = updP(abc);
199     ArticulatedInertia& PPlus = updPPlus(abc);
200 
201     PPlus = P = ArticulatedInertia(getMk_G(pc));
202 }
203 
realizeYOutward(const SBInstanceCache & ic,const SBTreePositionCache & pc,const SBArticulatedBodyInertiaCache & abc,SBDynamicsCache & dc) const204 void realizeYOutward(
205             const SBInstanceCache&                ic,
206             const SBTreePositionCache&            pc,
207             const SBArticulatedBodyInertiaCache&  abc,
208             SBDynamicsCache&                      dc) const override {
209 }
210 
calcCompositeBodyInertiasInward(const SBTreePositionCache & pc,Array_<SpatialInertia,MobilizedBodyIndex> & R) const211 void calcCompositeBodyInertiasInward
212    (const SBTreePositionCache& pc,
213     Array_<SpatialInertia,MobilizedBodyIndex>& R) const override {
214     toB(R) = getMk_G(pc);
215 }
216 
multiplyBySystemJacobian(const SBTreePositionCache & pc,const Real * v,SpatialVec * Jv) const217 void multiplyBySystemJacobian(
218         const SBTreePositionCache&  pc,
219         const Real*                 v,
220         SpatialVec*                 Jv) const override {
221     const Vec3& in = Vec3::getAs(&v[uIndex]);
222     SpatialVec& out = Jv[nodeNum];
223     out = ~getPhi(pc) * Jv[parent->getNodeNum()];
224     out[1] += in;
225 }
226 
multiplyBySystemJacobianTranspose(const SBTreePositionCache & pc,SpatialVec * zTmp,const SpatialVec * X,Real * JtX) const227 void multiplyBySystemJacobianTranspose(
228         const SBTreePositionCache&  pc,
229         SpatialVec*                 zTmp,
230         const SpatialVec*           X,
231         Real*                       JtX) const override {
232     const SpatialVec& in = X[getNodeNum()];
233     Vec3& out = Vec3::updAs(&JtX[getUIndex()]);
234     SpatialVec& z = zTmp[getNodeNum()];
235     z = in;
236     out = z[1];
237 }
238 
calcEquivalentJointForces(const SBTreePositionCache & pc,const SBTreeVelocityCache & vc,const SpatialVec * bodyForces,SpatialVec * allZ,Real * jointForces) const239 void calcEquivalentJointForces(
240         const SBTreePositionCache&  pc,
241         const SBTreeVelocityCache&  vc,
242         const SpatialVec*           bodyForces,
243         SpatialVec*                 allZ,
244         Real*                       jointForces) const override {
245     const SpatialVec& myBodyForce = bodyForces[nodeNum];
246     SpatialVec& z = allZ[nodeNum];
247     Vec3& eps = Vec3::updAs(&jointForces[uIndex]);
248     z = myBodyForce;
249     eps = z[1];
250 }
251 
calcUDotPass1Inward(const SBInstanceCache & ic,const SBTreePositionCache &,const SBArticulatedBodyInertiaCache &,const SBArticulatedBodyVelocityCache &,const Real * jointForces,const SpatialVec * bodyForces,const Real * allUDot,SpatialVec * allZ,SpatialVec * allZPlus,Real * allEpsilon) const252 void calcUDotPass1Inward(
253         const SBInstanceCache&                  ic,
254         const SBTreePositionCache&,
255         const SBArticulatedBodyInertiaCache&,
256         const SBArticulatedBodyVelocityCache&,
257         const Real*                             jointForces,
258         const SpatialVec*                       bodyForces,
259         const Real*                             allUDot,
260         SpatialVec*                             allZ,
261         SpatialVec*                             allZPlus,
262         Real*                                   allEpsilon) const override
263 {
264     const Vec3&         f       = Vec3::getAs(&jointForces[uIndex]);
265     const SpatialVec&   F       = bodyForces[nodeNum];
266     SpatialVec&         z       = allZ[nodeNum];
267     SpatialVec&         zPlus   = allZPlus[nodeNum];
268     Vec3&               eps     = Vec3::updAs(&allEpsilon[uIndex]);
269 
270     const bool isPrescribed = isUDotKnown(ic);
271 
272     z = -F;
273 
274     if (isPrescribed && !isUDotKnownToBeZero(ic)) {
275         const Vec3& udot = Vec3::getAs(&allUDot[uIndex]);
276         z[1] += getMass()*udot; // == P*H*udot
277     }
278 
279     // Lone particle has no children.
280 
281     eps = f - z[1];
282 
283     zPlus = z;
284     if (!isPrescribed)
285         zPlus[1] += eps;
286 }
287 
calcUDotPass2Outward(const SBInstanceCache & ic,const SBTreePositionCache & pc,const SBArticulatedBodyInertiaCache & abc,const SBTreeVelocityCache & vc,const SBDynamicsCache & dc,const Real * allEpsilon,SpatialVec * allA_GB,Real * allUDot,Real * allTau) const288 void calcUDotPass2Outward(
289         const SBInstanceCache&                  ic,
290         const SBTreePositionCache&              pc,
291         const SBArticulatedBodyInertiaCache&    abc,
292         const SBTreeVelocityCache&              vc,
293         const SBDynamicsCache&                  dc,
294         const Real*                             allEpsilon,
295         SpatialVec*                             allA_GB,
296         Real*                                   allUDot,
297         Real*                                   allTau) const override {
298     const Vec3& eps = Vec3::getAs(&allEpsilon[uIndex]);
299     SpatialVec& A_GB = allA_GB[nodeNum];
300     Vec3& udot = Vec3::updAs(&allUDot[uIndex]);
301 
302     const bool isPrescribed = isUDotKnown(ic);
303 
304     if (isPrescribed) {
305         const PresForcePoolIndex tauIx =
306             ic.getMobodInstanceInfo(nodeNum).firstPresForce;
307         assert(tauIx.isValid());
308         Vec3& tau = Vec3::updAs(&allTau[tauIx]);
309         tau = eps; // our sign convention
310     } else
311         udot = eps/getMass();
312 
313     A_GB = SpatialVec(Vec3(0), udot);
314 }
315 
316 // Note that we're not setting z temporaries here; you can't count on that as
317 // a side effect the M^-1*f kernel.
multiplyByMInvPass1Inward(const SBInstanceCache & ic,const SBTreePositionCache & pc,const SBArticulatedBodyInertiaCache & abc,const Real * jointForces,SpatialVec * allZ,SpatialVec * allZPlus,Real * allEpsilon) const318 void multiplyByMInvPass1Inward(
319         const SBInstanceCache&                  ic,
320         const SBTreePositionCache&              pc,
321         const SBArticulatedBodyInertiaCache&    abc,
322         const Real*                             jointForces,
323         SpatialVec*                             allZ,
324         SpatialVec*                             allZPlus,
325         Real*                                   allEpsilon) const override
326 {
327     if (isUDotKnown(ic)) // prescribed
328         return;
329 
330     // We promised not to look at f if it is part of f_p (prescribed).
331     const Vec3& f     = Vec3::getAs(&jointForces[uIndex]);
332     Vec3&       eps   = Vec3::updAs(&allEpsilon[uIndex]);
333 
334     eps = f;
335 }
336 
337 // Outward pass must set A_GB properly so it can be propagated
338 // to children.
multiplyByMInvPass2Outward(const SBInstanceCache & ic,const SBTreePositionCache & pc,const SBArticulatedBodyInertiaCache & abc,const Real * allEpsilon,SpatialVec * allA_GB,Real * allUDot) const339 void multiplyByMInvPass2Outward(
340         const SBInstanceCache&                  ic,
341         const SBTreePositionCache&              pc,
342         const SBArticulatedBodyInertiaCache&    abc,
343         const Real*                             allEpsilon,
344         SpatialVec*                             allA_GB,
345         Real*                                   allUDot) const override
346 {
347     const bool isPrescribed = isUDotKnown(ic);
348     const Vec3& eps = Vec3::getAs(&allEpsilon[uIndex]);
349     SpatialVec& A_GB = allA_GB[nodeNum];
350     Vec3&       udot = Vec3::updAs(&allUDot[uIndex]);
351 
352     if (isPrescribed) {
353         udot = 0;
354         A_GB = SpatialVec(Vec3(0), Vec3(0));
355     } else {
356         udot = eps/getMass();
357         A_GB = SpatialVec(Vec3(0), udot);
358     }
359 }
360 
361 // Also serves as pass 1 for inverse dynamics.
calcBodyAccelerationsFromUdotOutward(const SBTreePositionCache & pc,const SBTreeVelocityCache & vc,const Real * allUDot,SpatialVec * allA_GB) const362 void calcBodyAccelerationsFromUdotOutward(
363         const SBTreePositionCache&  pc,
364         const SBTreeVelocityCache&  vc,
365         const Real*                 allUDot,
366         SpatialVec*                 allA_GB) const override {
367     const Vec3& udot = Vec3::getAs(&allUDot[uIndex]);
368     SpatialVec& A_GB = allA_GB[nodeNum];
369     A_GB = SpatialVec(Vec3(0), udot);
370 }
calcInverseDynamicsPass2Inward(const SBTreePositionCache & pc,const SBTreeVelocityCache & vc,const SpatialVec * allA_GB,const Real * jointForces,const SpatialVec * bodyForces,SpatialVec * allF,Real * allTau) const371 void calcInverseDynamicsPass2Inward(
372         const SBTreePositionCache&  pc,
373         const SBTreeVelocityCache&  vc,
374         const SpatialVec*           allA_GB,
375         const Real*                 jointForces,
376         const SpatialVec*           bodyForces,
377         SpatialVec*                 allF,
378         Real*                       allTau) const override {
379     const Vec3& myJointForce = Vec3::getAs(&jointForces[uIndex]);
380     const SpatialVec& myBodyForce = bodyForces[nodeNum];
381     const SpatialVec& A_GB = allA_GB[nodeNum];
382     SpatialVec& F = allF[nodeNum];
383     Vec3& tau = Vec3::updAs(&allTau[uIndex]);
384     F = getMk_G(pc)*A_GB - myBodyForce;
385     tau = F[1] - myJointForce;
386 }
387 
multiplyByMPass1Outward(const SBTreePositionCache & pc,const Real * allUDot,SpatialVec * allA_GB) const388 void multiplyByMPass1Outward(
389         const SBTreePositionCache&  pc,
390         const Real*                 allUDot,
391         SpatialVec*                 allA_GB) const override {
392     const Vec3& udot = Vec3::getAs(&allUDot[uIndex]);
393     SpatialVec& A_GB = allA_GB[nodeNum];
394     A_GB = SpatialVec(Vec3(0), udot);
395 }
multiplyByMPass2Inward(const SBTreePositionCache & pc,const SpatialVec * allA_GB,SpatialVec * allF,Real * allTau) const396 void multiplyByMPass2Inward(
397         const SBTreePositionCache&  pc,
398         const SpatialVec*           allA_GB,
399         SpatialVec*                 allF,
400         Real*                       allTau) const override {
401     const SpatialVec& A_GB = allA_GB[nodeNum];
402     SpatialVec& F = allF[nodeNum];
403     Vec3& tau = Vec3::updAs(&allTau[uIndex]);
404     F = getMk_G(pc)*A_GB;
405     tau = F[1];
406 }
407 
getHCol(const SBTreePositionCache & pc,int j) const408 const SpatialVec& getHCol(const SBTreePositionCache& pc,
409                           int j) const override {
410     Mat<2,3,Vec3> H = Mat<2,3,Vec3>::getAs(&pc.storageForH[2*uIndex]);
411     SpatialVec& col = H(j);
412     col = SpatialVec(Vec3(0), Vec3(0));
413     col[1][j] = 1;
414     return col;
415 }
416 
getH_FMCol(const SBTreePositionCache & pc,int j) const417 const SpatialVec& getH_FMCol(const SBTreePositionCache& pc,
418                              int j) const override {
419     Mat<2,3,Vec3> H = Mat<2,3,Vec3>::getAs(&pc.storageForH_FM[2*uIndex]);
420     SpatialVec& col = H(j);
421     col = SpatialVec(Vec3(0), Vec3(0));
422     col[1][j] = 1;
423     return col;
424 }
425 
setQToFitTransformImpl(const SBStateDigest &,const Transform & X_F0M0,Vector & q) const426 void setQToFitTransformImpl(const SBStateDigest&, const Transform& X_F0M0,
427                             Vector& q) const override {
428     Vec3::updAs(&q[qIndex]) = X_F0M0.p();
429 }
setQToFitRotationImpl(const SBStateDigest &,const Rotation & R_F0M0,Vector & q) const430 void setQToFitRotationImpl(const SBStateDigest&, const Rotation& R_F0M0,
431                            Vector& q) const override {
432 }
setQToFitTranslationImpl(const SBStateDigest &,const Vec3 & p_F0M0,Vector & q) const433 void setQToFitTranslationImpl(const SBStateDigest&, const Vec3& p_F0M0,
434                               Vector& q) const override {
435     Vec3::updAs(&q[qIndex]) = p_F0M0;
436 }
437 
setUToFitVelocityImpl(const SBStateDigest &,const Vector & q,const SpatialVec & V_F0M0,Vector & u) const438 void setUToFitVelocityImpl(const SBStateDigest&, const Vector& q,
439                            const SpatialVec& V_F0M0, Vector& u) const override {
440     Vec3::updAs(&u[uIndex]) = V_F0M0[1];
441 }
setUToFitAngularVelocityImpl(const SBStateDigest &,const Vector & q,const Vec3 & w_F0M0,Vector & u) const442 void setUToFitAngularVelocityImpl(const SBStateDigest&, const Vector& q,
443                                   const Vec3& w_F0M0, Vector& u) const override {
444 }
setUToFitLinearVelocityImpl(const SBStateDigest &,const Vector & q,const Vec3 & v_F0M0,Vector & u) const445 void setUToFitLinearVelocityImpl(const SBStateDigest&, const Vector& q,
446                                  const Vec3& v_F0M0, Vector& u) const override {
447     Vec3::updAs(&u[uIndex]) = v_F0M0;
448 }
449 
450 };
451 
createRigidBodyNode(UIndex & nextUSlot,USquaredIndex & nextUSqSlot,QIndex & nextQSlot) const452 RigidBodyNode* MobilizedBody::TranslationImpl::createRigidBodyNode(
453         UIndex&        nextUSlot,
454         USquaredIndex& nextUSqSlot,
455         QIndex&        nextQSlot) const {
456     if (!hasChildren && getMyParentMobilizedBodyIndex() == 0 && !isReversed() &&
457             getDefaultInboardFrame().p() == 0 && getDefaultInboardFrame().R() == Mat33(1) &&
458             getDefaultOutboardFrame().p() == 0 && getDefaultOutboardFrame().R() == Mat33(1)) {
459         // This satisfies all the requirements to use RBNodeLoneParticle.
460 
461         return new RBNodeLoneParticle(getDefaultRigidBodyMassProperties(), nextUSlot,nextUSqSlot,nextQSlot);
462     }
463 
464     // Use RBNodeTranslate for the general case.
465 
466     bool noX_MB = (getDefaultOutboardFrame().p() == 0 && getDefaultOutboardFrame().R() == Mat33(1));
467     bool noR_PF = (getDefaultInboardFrame().R() == Mat33(1));
468     if (noX_MB) {
469         if (noR_PF)
470             return new RBNodeTranslate<true, true> (
471                 getDefaultRigidBodyMassProperties(),
472                 getDefaultInboardFrame(),getDefaultOutboardFrame(),
473                 isReversed(),
474                 nextUSlot,nextUSqSlot,nextQSlot);
475         else
476             return new RBNodeTranslate<true, false> (
477                 getDefaultRigidBodyMassProperties(),
478                 getDefaultInboardFrame(),getDefaultOutboardFrame(),
479                 isReversed(),
480                 nextUSlot,nextUSqSlot,nextQSlot);
481     }
482     else {
483         if (noR_PF)
484             return new RBNodeTranslate<false, true> (
485                 getDefaultRigidBodyMassProperties(),
486                 getDefaultInboardFrame(),getDefaultOutboardFrame(),
487                 isReversed(),
488                 nextUSlot,nextUSqSlot,nextQSlot);
489         else
490             return new RBNodeTranslate<false, false> (
491                 getDefaultRigidBodyMassProperties(),
492                 getDefaultInboardFrame(),getDefaultOutboardFrame(),
493                 isReversed(),
494                 nextUSlot,nextUSqSlot,nextQSlot);
495     }
496 }
497