1 #ifndef SimTK_SIMBODY_CONSTRAINT_IMPL_H_
2 #define SimTK_SIMBODY_CONSTRAINT_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-14 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 Private implementation of Constraint and its included subclasses which
29 represent the built-in constraint types. **/
30
31 #include "SimTKmath.h"
32 #include "simbody/internal/common.h"
33 #include "simbody/internal/Constraint.h"
34 #include "simbody/internal/SimbodyMatterSubsystem.h"
35 #include "simbody/internal/SimbodyMatterSubtree.h"
36
37 #include "SimbodyTreeState.h"
38
39 #include <map>
40 #include <utility> // std::pair
41 #include <iostream>
42 using std::cout; using std::endl;
43
44 class SimbodyMatterSubsystemRep;
45
46 namespace SimTK {
47
48 class SimbodyMatterSubsystem;
49 class SimbodyMatterSubtree;
50 class MobilizedBody;
51
52 //==============================================================================
53 // CONSTRAINT IMPL
54 //==============================================================================
55 // This is what a Constraint handle points to.
56 class ConstraintImpl : public PIMPLImplementation<Constraint, ConstraintImpl> {
57 public:
58
ConstraintImpl()59 ConstraintImpl()
60 : myMatterSubsystemRep(0),
61 defaultMp(0), defaultMv(0), defaultMa(0), defaultDisabled(false),
62 constraintIsConditional(false), myAncestorBodyIsNotGround(false)
63 {
64 }
~ConstraintImpl()65 virtual ~ConstraintImpl() { }
66 virtual ConstraintImpl* clone() const = 0;
67
ConstraintImpl(int mp,int mv,int ma)68 ConstraintImpl(int mp, int mv, int ma)
69 : myMatterSubsystemRep(0),
70 defaultMp(mp), defaultMv(mv), defaultMa(ma), defaultDisabled(false),
71 constraintIsConditional(false), myAncestorBodyIsNotGround(false)
72 {
73 }
74
setDefaultNumConstraintEquations(int mp,int mv,int ma)75 void setDefaultNumConstraintEquations(int mp, int mv, int ma) {
76 assert(mp >= 0 && mv >= 0 && ma >= 0);
77 invalidateTopologyCache();
78 defaultMp = mp;
79 defaultMv = mv;
80 defaultMa = ma;
81 }
82
getDefaultNumConstraintEquations(int & mp,int & mv,int & ma)83 void getDefaultNumConstraintEquations(int& mp, int& mv, int& ma) const {
84 mp = defaultMp;
85 mv = defaultMv;
86 ma = defaultMa;
87 }
88
setDisabledByDefault(bool shouldBeDisabled)89 void setDisabledByDefault(bool shouldBeDisabled) {
90 invalidateTopologyCache();
91 defaultDisabled = shouldBeDisabled;
92 }
93
isDisabledByDefault()94 bool isDisabledByDefault() const {
95 return defaultDisabled;
96 }
97
98 void setDisabled(State& s, bool shouldBeDisabled) const ;
99 bool isDisabled(const State& s) const;
100
setIsConditional(bool isConditional)101 void setIsConditional(bool isConditional) {
102 invalidateTopologyCache();
103 constraintIsConditional = isConditional;
104 }
105
isConditional()106 bool isConditional() const {return constraintIsConditional;}
107
108 typedef std::map<MobilizedBodyIndex,ConstrainedBodyIndex>
109 MobilizedBody2ConstrainedBodyMap;
110 typedef std::map<MobilizedBodyIndex,ConstrainedMobilizerIndex>
111 MobilizedBody2ConstrainedMobilizerMap;
112
113 // Call this during construction phase to add a body to the topological
114 // structure of this Constraint. This body's mobilizer's mobilities are *not*
115 // part of the constraint; mobilizers must be added separately. If this
116 // mobilized body has been seen as a constrained body before we'll return the
117 // same index as first assigned to it.
118 ConstrainedBodyIndex addConstrainedBody(const MobilizedBody&);
119
120 // Call this during construction phase to add a mobilizer to the topological
121 // structure of this Constraint. All the coordinates q and mobilities u for
122 // this mobilizer are added as "constrainable". We don't know how many
123 // coordinates and speeds there are until Stage::Model. If this
124 // mobilized body has been seen as a constrained mobilizer before we'll return
125 // the same index as first assigned to it.
126 ConstrainedMobilizerIndex addConstrainedMobilizer(const MobilizedBody&);
127
getMobilizedBodyIndexOfConstrainedBody(ConstrainedBodyIndex c)128 MobilizedBodyIndex getMobilizedBodyIndexOfConstrainedBody
129 (ConstrainedBodyIndex c) const {
130 assert(0 <= c && c < (int)myConstrainedBodies.size());
131 return myConstrainedBodies[c];
132 }
getMobilizedBodyIndexOfConstrainedMobilizer(ConstrainedMobilizerIndex c)133 MobilizedBodyIndex getMobilizedBodyIndexOfConstrainedMobilizer
134 (ConstrainedMobilizerIndex c) const {
135 assert(0 <= c && c < (int)myConstrainedMobilizers.size());
136 return myConstrainedMobilizers[c];
137 }
138
139 //TODO: Constraint-local State allocation
140 //int allocateDiscreteVariable(State& s, Stage g, AbstractValue* v) const;
141 //int allocateCacheEntry(State& s, Stage g, AbstractValue* v) const;
142
143 QIndex getQIndexOfConstrainedQ(const State& s, ConstrainedQIndex cqx) const;
144 UIndex getUIndexOfConstrainedU(const State& s, ConstrainedUIndex cqx) const;
145
146 void convertQForcesToUForces(const State& s,
147 const Array_<Real,ConstrainedQIndex>& qForces,
148 Array_<Real,ConstrainedUIndex>& uForces) const;
149
150 // Given a position-stage state and an array of body velocities for all
151 // bodies (relative to Ground), select the short list of constrained bodies
152 // for this Constraint and ensure that their velocities are relative to
153 // the Ancestor body.
154 void convertBodyVelocityToConstrainedBodyVelocity
155 (const State& state,
156 const Array_<SpatialVec, MobilizedBodyIndex>& V_GB,
157 Array_<SpatialVec, ConstrainedBodyIndex>& V_AB) const;
158
159 // Given a velocity-stage state and an array of body accelerations for all
160 // bodies (relative to Ground), select the short list of constrained bodies
161 // for this Constraint and ensure that their accelerations are relative to
162 // the Ancestor body.
163 void convertBodyAccelToConstrainedBodyAccel
164 (const State& state,
165 const Array_<SpatialVec, MobilizedBodyIndex>& A_GB,
166 Array_<SpatialVec, ConstrainedBodyIndex>& A_AB) const;
167
168 void realizeTopology(State&) const; // eventually calls realizeTopologyVirtual()
169 void realizeModel (State&) const; // eventually calls realizeModelVirtual()
170 void realizeInstance(const State&) const; // eventually calls realizeInstanceVirtual()
171
172 // These are called in loops over all the Constraints from the
173 // SimbodyMatterSubsystem realize() methods, which will have already
174 // deconstructed the State into an SBStateDigest object.
175 void realizeTime (const SBStateDigest&) const; // eventually calls realizeTimeVirtual()
176 void realizePosition(const SBStateDigest&) const; // eventually calls realizePositionVirtual()
177 void realizeVelocity(const SBStateDigest&) const; // eventually calls realizeVelocityVirtual()
178 void realizeDynamics(const SBStateDigest&) const; // eventually calls realizeDynamicsVirtual()
179 void realizeAcceleration
180 (const SBStateDigest&) const; // eventually calls realizeAccelerationVirtual()
181 void realizeReport (const State&) const; // eventually calls realizeReportVirtual()
182
183 // Given a state realized to Position stage, extract the position constraint
184 // errors corresponding to this Constraint. The 'mp' argument is for sanity
185 // checking -- it is an error if that isn't an exact match for the current
186 // number of holonomic constraint equations generated by this Constraint. We
187 // expect that perr points to an array of at least mp elements that we can
188 // write on.
189 void getPositionErrors(const State& s, int mp, Real* perr) const;
190
191 // Given a State realized to Velocity stage, extract the velocity constraint
192 // errors corresponding to this Constraint. This includes velocity constraints
193 // which were produced by differentiation of holonomic (position) constraints,
194 // and nonholonomic constraints which are introduced at the velocity level. The
195 // 'mpv' argument is for sanity checking -- it is an error if that isn't an
196 // exact match for the current number of holonomic+nonholonomic (mp+mv)
197 // constraint equations generated by this Constraint. We expect that pverr
198 // points to an array of at least mp+mv elements that we can write on.
199 void getVelocityErrors(const State& s, int mpv, Real* pverr) const;
200
201 // Given a State realized to Acceleration stage, extract the accleration
202 // constraint errors corresponding to this Constraint. This includes
203 // acceleration constraints which were produced by twice differentiation of
204 // holonomic (position) constraints, and differentiation of nonholonomic
205 // (velocity) constraints, and acceleration-only constraints which are first
206 // introduced at the acceleration level. The 'mpva' argument is for sanity
207 // checking -- it is an error if that isn't an exact match for the current
208 // number of holonomic+nonholonomic+accelerationOnly (mp+mv+ma) constraint
209 // equations generated by this Constraint. We expect that pvaerr points to an
210 // array of at least mp+mv+ma elements that we can write on.
211 void getAccelerationErrors(const State& s, int mpva, Real* pvaerr) const;
212
213 // Given a State realized to Acceleration stage, extract the constraint
214 // multipliers lambda corresponding to this constraint. This includes
215 // multipliers for all the holonomic, nonholonomic, and acceleration-only
216 // constraints (but not quaternion constraints which do not use multipliers).
217 // The 'mpva' argument is for sanity checking -- it is an error if that isn't
218 // an exact match for the current number (mp+mv+ma) of constraint equations
219 // generated by this Constraint. We expect that lambda points to an array of at
220 // least mp+mv+ma elements that we can write on.
221 void getMultipliers(const State& s, int mpva, Real* lambda) const;
222
223 // Return a small, writable array directly referencing the segment of the longer
224 // passed-in array that belongs to this constraint. State must be realized
225 // through Instance stage.
226 ArrayView_<SpatialVec,ConstrainedBodyIndex>
227 updConstrainedBodyForces(const State& state,
228 Array_<SpatialVec>& allConsBodyForces) const;
229 // Same, but for mobility forces.
230 ArrayView_<Real,ConstrainedUIndex>
231 updConstrainedMobilityForces(const State& state,
232 Array_<Real>& allConsMobForces) const;
233
234 // Return a const reference to our segment. Can use above methods efficiently
235 // since ArrayView is derived from ArrayViewConst; this just does a
236 // shallow copy to fill in the ArrayViewConst handle; no heap is involved.
237 ArrayViewConst_<SpatialVec,ConstrainedBodyIndex>
getConstrainedBodyForces(const State & state,const Array_<SpatialVec> & allConsBodyForces)238 getConstrainedBodyForces(const State& state,
239 const Array_<SpatialVec>& allConsBodyForces) const
240 {
241 return updConstrainedBodyForces
242 (state, const_cast<Array_<SpatialVec>&>(allConsBodyForces));
243 }
244 ArrayViewConst_<Real,ConstrainedUIndex>
getConstrainedMobilityForces(const State & state,const Array_<Real> & allConsMobForces)245 getConstrainedMobilityForces(const State& state,
246 const Array_<Real>& allConsMobForces) const
247 {
248 return updConstrainedMobilityForces
249 (state, const_cast<Array_<Real>&>(allConsMobForces));
250 }
251
252 // Same as above but we use the matter subsystem's constrained acceleration
253 // cache as the source of the full-sized constrained body forces array,
254 // where the forces are expressed in Ground. That cache entry must have been
255 // realized which occurs during Acceleration stage computations.
256 ArrayViewConst_<SpatialVec,ConstrainedBodyIndex>
getConstrainedBodyForcesInGFromState(const State & state)257 getConstrainedBodyForcesInGFromState(const State& state) const
258 {
259 const SBConstrainedAccelerationCache& cac =
260 getConstrainedAccelerationCache(state);
261 return getConstrainedBodyForces(state, cac.constrainedBodyForcesInG);
262 }
263
264 ArrayView_<SpatialVec,ConstrainedBodyIndex>
updConstrainedBodyForcesInGFromState(const State & state)265 updConstrainedBodyForcesInGFromState(const State& state) const
266 {
267 SBConstrainedAccelerationCache& cac =
268 updConstrainedAccelerationCache(state);
269 return updConstrainedBodyForces(state, cac.constrainedBodyForcesInG);
270 }
271
272 ArrayViewConst_<Real,ConstrainedUIndex>
getConstrainedMobilityForcesFromState(const State & state)273 getConstrainedMobilityForcesFromState(const State& state) const
274 {
275 const SBConstrainedAccelerationCache& cac =
276 getConstrainedAccelerationCache(state);
277 return getConstrainedMobilityForces(state, cac.constraintMobilityForces);
278 }
279
280 ArrayView_<Real,ConstrainedUIndex>
updConstrainedMobilityForcesFromState(const State & state)281 updConstrainedMobilityForcesFromState(const State& state) const
282 {
283 SBConstrainedAccelerationCache& cac =
284 updConstrainedAccelerationCache(state);
285 return getConstrainedMobilityForces(state, cac.constraintMobilityForces);
286 }
287
288 // Given a State and a set of m multipliers lambda,
289 // calculate in O(m) time the constraint forces (body forces and torques and
290 // u-space mobility forces) which would be generated by those multipliers. You can
291 // restrict this to P,V,A subsets setting mp, mv, or ma to zero; in any case
292 // m = mp+mv+ma and any non-zero segments must match the corresponding number
293 // of constraint equations of that type.
294 //
295 // The State must be realized to at least Position stage, and if you ask for
296 // forces from holonomic (mv>0) or acceleration-only (ma>0) constraints then
297 // the State will have to be realized to Velocity stage if the corresponding
298 // constraint equations are velocity dependent.
calcConstraintForcesFromMultipliers(const State & s,const Array_<Real> & lambdap,const Array_<Real> & lambdav,const Array_<Real> & lambdaa,Array_<SpatialVec,ConstrainedBodyIndex> & bodyForcesInA,Array_<Real,ConstrainedUIndex> & mobilityForces)299 void calcConstraintForcesFromMultipliers
300 (const State& s,
301 const Array_<Real>& lambdap, // 0 or mp of these
302 const Array_<Real>& lambdav, // 0 or mv of these
303 const Array_<Real>& lambdaa, // 0 or ma of these
304 Array_<SpatialVec,ConstrainedBodyIndex>& bodyForcesInA,
305 Array_<Real, ConstrainedUIndex>& mobilityForces) const
306 {
307 int actual_mp,actual_mv,actual_ma;
308 getNumConstraintEquationsInUse(s, actual_mp, actual_mv, actual_ma);
309
310 bodyForcesInA.resize(getNumConstrainedBodies());
311 bodyForcesInA.fill(SpatialVec(Vec3(0), Vec3(0)));
312 mobilityForces.resize(getNumConstrainedU(s));
313 mobilityForces.fill(Real(0));
314
315 if (lambdap.size()) {
316 assert(lambdap.size() == actual_mp);
317 const int ncq = getNumConstrainedQ(s);
318 const int ncu = getNumConstrainedU(s);
319 // State need only be realized to Position stage
320 Array_<Real, ConstrainedQIndex> qForces(ncq, Real(0));
321 Array_<Real, ConstrainedUIndex> uForces(ncu);
322 addInPositionConstraintForces(s, lambdap,
323 bodyForcesInA, qForces);
324 convertQForcesToUForces(s, qForces, uForces);
325 for (ConstrainedUIndex cux(0); cux < ncu; ++cux)
326 mobilityForces[cux] += uForces[cux];
327 }
328 if (lambdav.size()) {
329 assert(lambdav.size() == actual_mv);
330 // State may need to be realized to Velocity stage
331 addInVelocityConstraintForces(s, lambdav,
332 bodyForcesInA, mobilityForces);
333 }
334 if (lambdaa.size()) {
335 assert(lambdaa.size() == actual_ma);
336 // State may need to be realized to Velocity stage
337 addInAccelerationConstraintForces(s, lambdaa,
338 bodyForcesInA, mobilityForces);
339 }
340 }
341
342 // Given a State realized to Position stage, and a set of spatial forces applied
343 // to the constrained bodies and u-space generalized forces applied to the
344 // constrained mobilities, convert these to an equivalent set
345 // of n generalized forces applied at each of the participating mobilities, in
346 // O(n) time.
347 // TODO
convertConstraintForcesToGeneralizedForces(const State & s,const Array_<SpatialVec,ConstrainedBodyIndex> & bodyForcesInA,const Array_<Real,ConstrainedUIndex> & mobilityForces,Vector & generalizedForces)348 void convertConstraintForcesToGeneralizedForces(const State& s,
349 const Array_<SpatialVec,ConstrainedBodyIndex>& bodyForcesInA,
350 const Array_<Real, ConstrainedUIndex>& mobilityForces,
351 Vector& generalizedForces) const
352 {
353 // TODO
354 assert(!"convertConstraintForcesToGeneralizedForces: not implemented yet");
355 }
356
357 // Calculate f = ~G*lambda in O(n+m) time. ~G=[~P ~V ~A] and you can work with
358 // any subblock or combination by setting some of mp,mv,ma to zero. If nonzero
359 // they have to match the actual number of holonomic, nonholonomic, and
360 // acceleration-only constraints. Vector lambda (typically Lagrange multipliers
361 // but not necessarily) is segmented lambda=[mp|mv|ma] where some of the
362 // segments can be empty.
calcGTransposeLambda(const State & s,const Array_<Real> & lambdap,const Array_<Real> & lambdav,const Array_<Real> & lambdaa,Vector & f)363 void calcGTransposeLambda
364 (const State& s,
365 const Array_<Real>& lambdap, // 0 or mp of these
366 const Array_<Real>& lambdav, // 0 or mv of these
367 const Array_<Real>& lambdaa, // 0 or ma of these
368 Vector& f) const
369 {
370 Array_<SpatialVec,ConstrainedBodyIndex> bodyForcesInA;
371 Array_<Real, ConstrainedUIndex> mobilityForces;
372
373 calcConstraintForcesFromMultipliers
374 (s, lambdap, lambdav, lambdaa, bodyForcesInA, mobilityForces);
375 convertConstraintForcesToGeneralizedForces
376 (s, bodyForcesInA, mobilityForces, f);
377 }
378
379 // Find the indicated cache in the passed-in State. The "get" methods require
380 // that the cache entry has already been marked valid.
381
382 const SBModelCache& getModelCache(const State&) const;
383 const SBInstanceCache& getInstanceCache(const State&) const;
384 const SBTreePositionCache& getTreePositionCache(const State&) const;
385 const SBTreeVelocityCache& getTreeVelocityCache(const State&) const;
386 const SBTreeAccelerationCache& getTreeAccelerationCache(const State&) const;
387 const SBConstrainedAccelerationCache&
388 getConstrainedAccelerationCache(const State& s) const;
389 SBConstrainedAccelerationCache&
390 updConstrainedAccelerationCache(const State& s) const;
391
392 // Methods for use with ConstrainedMobilizers.
393
394 Real getOneQFromState
395 (const State&, ConstrainedMobilizerIndex, MobilizerQIndex) const;
396 Real getOneQDotFromState
397 (const State&, ConstrainedMobilizerIndex, MobilizerQIndex) const;
398 Real getOneQDotDotFromState
399 (const State&, ConstrainedMobilizerIndex, MobilizerQIndex) const;
400
401 Real getOneUFromState
402 (const State&, ConstrainedMobilizerIndex, MobilizerUIndex) const;
403 Real getOneUDotFromState
404 (const State&, ConstrainedMobilizerIndex, MobilizerUIndex) const;
405
406 // Analogous methods for use when the generalized coordinate has been provided
407 // as an operand. The state is still necessary for modeling information.
getOneQ(const State & s,const Array_<Real,ConstrainedQIndex> & cq,ConstrainedMobilizerIndex M,MobilizerQIndex whichQ)408 Real getOneQ(const State& s,
409 const Array_<Real,ConstrainedQIndex>& cq,
410 ConstrainedMobilizerIndex M,
411 MobilizerQIndex whichQ) const
412 {
413 assert(cq.size() == getNumConstrainedQ(s));
414 assert(0 <= whichQ && whichQ < getNumConstrainedQ(s, M));
415 return cq[getConstrainedQIndex(s,M,whichQ)];
416 }
getOneQDot(const State & s,const Array_<Real,ConstrainedQIndex> & cqdot,ConstrainedMobilizerIndex M,MobilizerQIndex whichQ)417 Real getOneQDot(const State& s,
418 const Array_<Real,ConstrainedQIndex>& cqdot,
419 ConstrainedMobilizerIndex M,
420 MobilizerQIndex whichQ) const
421 {
422 assert(cqdot.size() == getNumConstrainedQ(s));
423 assert(0 <= whichQ && whichQ < getNumConstrainedQ(s, M));
424 return cqdot[getConstrainedQIndex(s,M,whichQ)];
425 }
getOneQDotDot(const State & s,const Array_<Real,ConstrainedQIndex> & cqdotdot,ConstrainedMobilizerIndex M,MobilizerQIndex whichQ)426 Real getOneQDotDot(const State& s,
427 const Array_<Real,ConstrainedQIndex>& cqdotdot,
428 ConstrainedMobilizerIndex M,
429 MobilizerQIndex whichQ) const
430 {
431 assert(cqdotdot.size() == getNumConstrainedQ(s));
432 assert(0 <= whichQ && whichQ < getNumConstrainedQ(s, M));
433 return cqdotdot[getConstrainedQIndex(s,M,whichQ)];
434 }
435
getOneU(const State & s,const Array_<Real,ConstrainedUIndex> & cu,ConstrainedMobilizerIndex M,MobilizerUIndex whichU)436 Real getOneU(const State& s,
437 const Array_<Real,ConstrainedUIndex>& cu,
438 ConstrainedMobilizerIndex M,
439 MobilizerUIndex whichU) const
440 {
441 assert(cu.size() == getNumConstrainedU(s));
442 assert(0 <= whichU && whichU < getNumConstrainedU(s, M));
443 return cu[getConstrainedUIndex(s,M,whichU)];
444 }
445
getOneUDot(const State & s,const Array_<Real,ConstrainedUIndex> & cudot,ConstrainedMobilizerIndex M,MobilizerUIndex whichU)446 Real getOneUDot(const State& s,
447 const Array_<Real,ConstrainedUIndex>& cudot,
448 ConstrainedMobilizerIndex M,
449 MobilizerUIndex whichU) const
450 {
451 assert(cudot.size() == getNumConstrainedU(s));
452 assert(0 <= whichU && whichU < getNumConstrainedU(s, M));
453 return cudot[getConstrainedUIndex(s,M,whichU)];
454 }
455
456 // Apply a u-space (mobility) generalized force fu to a particular mobility of
457 // the given constrained mobilizer, adding it in to the appropriate slot of the
458 // mobilityForces vector which is of length numConstrainedU for this Constraint.
addInOneMobilityForce(const State & s,ConstrainedMobilizerIndex cmx,MobilizerUIndex whichU,Real fu,Array_<Real,ConstrainedUIndex> & mobilityForces)459 void addInOneMobilityForce
460 (const State& s,
461 ConstrainedMobilizerIndex cmx,
462 MobilizerUIndex whichU,
463 Real fu,
464 Array_<Real,ConstrainedUIndex>& mobilityForces) const
465 {
466 assert(mobilityForces.size() == getNumConstrainedU(s));
467 assert(0 <= whichU && whichU < getNumConstrainedU(s, cmx));
468 mobilityForces[getConstrainedUIndex(s,cmx,whichU)] += fu;
469 }
470
471 // Apply a q-space generalized force fq to a particular generalized coordinate q
472 // of the given constrained mobilizer, adding it in to the appropriate slot of
473 // the qForces vector which is of length numConstrainedQ for this Constraint.
addInOneQForce(const State & s,ConstrainedMobilizerIndex cmx,MobilizerQIndex whichQ,Real fq,Array_<Real,ConstrainedQIndex> & qForces)474 void addInOneQForce
475 (const State& s,
476 ConstrainedMobilizerIndex cmx,
477 MobilizerQIndex whichQ,
478 Real fq,
479 Array_<Real,ConstrainedQIndex>& qForces) const
480 {
481 assert(qForces.size() == getNumConstrainedQ(s));
482 assert(0 <= whichQ && whichQ < getNumConstrainedQ(s, cmx));
483 qForces[getConstrainedQIndex(s,cmx,whichQ)] += fq;
484 }
485
486 // Methods for use with ConstrainedBodies.
487
488 // These are used to retrieve the indicated values from the State cache, with all values
489 // measured and expressed in the Ancestor (A) frame.
490 const Transform& getBodyTransformFromState
491 (const State& s, ConstrainedBodyIndex B) const; // X_AB
492 const SpatialVec& getBodyVelocityFromState
493 (const State& s, ConstrainedBodyIndex B) const; // V_AB
494
495 // Extract just the rotational quantities from the spatial quantities above.
getBodyRotationFromState(const State & s,ConstrainedBodyIndex B)496 const Rotation& getBodyRotationFromState
497 (const State& s, ConstrainedBodyIndex B) const // R_AB
498 {return getBodyTransformFromState(s,B).R();}
getBodyAngularVelocityFromState(const State & s,ConstrainedBodyIndex B)499 const Vec3& getBodyAngularVelocityFromState
500 (const State& s, ConstrainedBodyIndex B) const // w_AB
501 {return getBodyVelocityFromState(s,B)[0];}
502
503 // Extract just the translational (linear) quantities from the spatial quantities above.
getBodyOriginLocationFromState(const State & s,ConstrainedBodyIndex B)504 const Vec3& getBodyOriginLocationFromState
505 (const State& s, ConstrainedBodyIndex B) const // p_AB
506 {return getBodyTransformFromState(s,B).p();}
getBodyOriginVelocityFromState(const State & s,ConstrainedBodyIndex B)507 const Vec3& getBodyOriginVelocityFromState
508 (const State& s, ConstrainedBodyIndex B) const // v_AB
509 {return getBodyVelocityFromState(s,B)[1];}
510
511 // These are analogous methods for when you've been given X_AB, V_AB, or A_AB
512 // explicitly as an operand. These are all inline and trivial.
getBodyTransform(const Array_<Transform,ConstrainedBodyIndex> & allX_AB,ConstrainedBodyIndex B)513 const Transform& getBodyTransform
514 (const Array_<Transform,ConstrainedBodyIndex>& allX_AB,
515 ConstrainedBodyIndex B) const
516 { return allX_AB[B]; }
517
getBodyVelocity(const Array_<SpatialVec,ConstrainedBodyIndex> & allV_AB,ConstrainedBodyIndex B)518 const SpatialVec& getBodyVelocity
519 (const Array_<SpatialVec,ConstrainedBodyIndex>& allV_AB,
520 ConstrainedBodyIndex B) const
521 { return allV_AB[B]; }
522
getBodyAcceleration(const Array_<SpatialVec,ConstrainedBodyIndex> & allA_AB,ConstrainedBodyIndex B)523 const SpatialVec& getBodyAcceleration
524 (const Array_<SpatialVec,ConstrainedBodyIndex>& allA_AB,
525 ConstrainedBodyIndex B) const
526 { return allA_AB[B]; }
527
getBodyRotation(const Array_<Transform,ConstrainedBodyIndex> & allX_AB,ConstrainedBodyIndex B)528 const Rotation& getBodyRotation
529 (const Array_<Transform,ConstrainedBodyIndex>& allX_AB,
530 ConstrainedBodyIndex B) const
531 { return allX_AB[B].R(); }
532
getBodyAngularVelocity(const Array_<SpatialVec,ConstrainedBodyIndex> & allV_AB,ConstrainedBodyIndex B)533 const Vec3& getBodyAngularVelocity
534 (const Array_<SpatialVec,ConstrainedBodyIndex>& allV_AB,
535 ConstrainedBodyIndex B) const
536 { return allV_AB[B][0]; }
537
getBodyAngularAcceleration(const Array_<SpatialVec,ConstrainedBodyIndex> & allA_AB,ConstrainedBodyIndex B)538 const Vec3& getBodyAngularAcceleration
539 (const Array_<SpatialVec,ConstrainedBodyIndex>& allA_AB,
540 ConstrainedBodyIndex B) const
541 { return allA_AB[B][0]; }
542
getBodyOriginLocation(const Array_<Transform,ConstrainedBodyIndex> & allX_AB,ConstrainedBodyIndex B)543 const Vec3& getBodyOriginLocation
544 (const Array_<Transform,ConstrainedBodyIndex>& allX_AB,
545 ConstrainedBodyIndex B) const
546 { return allX_AB[B].p(); }
547
getBodyOriginVelocity(const Array_<SpatialVec,ConstrainedBodyIndex> & allV_AB,ConstrainedBodyIndex B)548 const Vec3& getBodyOriginVelocity
549 (const Array_<SpatialVec,ConstrainedBodyIndex>& allV_AB,
550 ConstrainedBodyIndex B) const
551 { return allV_AB[B][1]; }
552
getBodyOriginAcceleration(const Array_<SpatialVec,ConstrainedBodyIndex> & allA_AB,ConstrainedBodyIndex B)553 const Vec3& getBodyOriginAcceleration
554 (const Array_<SpatialVec,ConstrainedBodyIndex>& allA_AB,
555 ConstrainedBodyIndex B) const
556 { return allA_AB[B][1]; }
557
558
559 // Given a station S on body B, specified by the vector p_BS from Bo to S
560 // expressed in B, find its location p_AS measured from Ao and expressed in the
561 // Ancestor's frame A. 18 flops.
findStationLocationFromState(const State & s,ConstrainedBodyIndex B,const Vec3 & p_BS)562 Vec3 findStationLocationFromState
563 (const State& s, ConstrainedBodyIndex B, const Vec3& p_BS) const {
564 return getBodyTransformFromState(s,B) * p_BS; // re-measure and re-express
565 }
566
567 // Same, but we're given the constrained body poses as an operand (18 flops).
findStationLocation(const Array_<Transform,ConstrainedBodyIndex> & allX_AB,ConstrainedBodyIndex B,const Vec3 & p_BS)568 Vec3 findStationLocation
569 (const Array_<Transform, ConstrainedBodyIndex>& allX_AB,
570 ConstrainedBodyIndex B, const Vec3& p_BS) const {
571 const Transform& X_AB = allX_AB[B];
572 return X_AB * p_BS; // re-measure and re-express (18 flops)
573 }
574
575 // Given a station S on body B, find its velocity measured and expressed in the
576 // Ancestor's frame A. 27 flops.
findStationVelocityFromState(const State & s,ConstrainedBodyIndex B,const Vec3 & p_BS)577 Vec3 findStationVelocityFromState
578 (const State& s, ConstrainedBodyIndex B, const Vec3& p_BS) const {
579 // p_BS rexpressed in A but not shifted to Ao
580 const Vec3 p_BS_A = getBodyRotationFromState(s,B) * p_BS; // 15 flops
581 const SpatialVec& V_AB = getBodyVelocityFromState(s,B);
582 return V_AB[1] + (V_AB[0] % p_BS_A); // 12 flops
583 }
584
585 // Same, but only configuration comes from state; velocities are an operand.
findStationVelocity(const State & s,const Array_<SpatialVec,ConstrainedBodyIndex> & allV_AB,ConstrainedBodyIndex B,const Vec3 & p_BS)586 Vec3 findStationVelocity
587 (const State& s,
588 const Array_<SpatialVec, ConstrainedBodyIndex>& allV_AB,
589 ConstrainedBodyIndex B, const Vec3& p_BS) const
590 {
591 // p_BS rexpressed in A but not shifted to Ao
592 const Vec3 p_BS_A = getBodyRotationFromState(s,B) * p_BS;
593 const SpatialVec& V_AB = allV_AB[B];
594 return V_AB[1] + (V_AB[0] % p_BS_A);
595 }
596
597 // Combo method is cheaper. Location comes from state, velocities from operand.
598 // NOTE: you must provide the p_BS vector expressed (but not measured) in A.
599 // 15 flops.
findStationInALocationVelocity(const Transform & X_AB,const SpatialVec & V_AB,const Vec3 & p_BS_A,Vec3 & p_AS,Vec3 & v_AS)600 void findStationInALocationVelocity
601 (const Transform& X_AB,
602 const SpatialVec& V_AB,
603 const Vec3& p_BS_A,
604 Vec3& p_AS, Vec3& v_AS) const
605 {
606 const Vec3& w_AB = V_AB[0]; const Vec3& v_AB = V_AB[1];
607 const Vec3 pdot_BS_A = w_AB % p_BS_A; // 9 flops
608
609 p_AS = X_AB.p() + p_BS_A; // 3 flops
610 v_AS = v_AB + pdot_BS_A; // 3 flops
611 }
612
613 // Given a station P on body F, and a station Q on body B, report the
614 // relative position p_PQ_A and relative velocity v_PQ_A=v_FQ_A. Note that
615 // the velocity is measured in the F frame, but expressed in the common A
616 // frame. 78 flops
findRelativePositionVelocity(const Transform & X_AF,const SpatialVec & V_AF,const Vec3 & p_FP,const Transform & X_AB,const SpatialVec & V_AB,const Vec3 & p_BQ,Vec3 & p_PQ_A,Vec3 & v_FQ_A)617 void findRelativePositionVelocity
618 (const Transform& X_AF,
619 const SpatialVec& V_AF,
620 const Vec3& p_FP,
621 const Transform& X_AB,
622 const SpatialVec& V_AB,
623 const Vec3& p_BQ,
624 Vec3& p_PQ_A, Vec3& v_FQ_A) const
625 {
626 const Vec3& w_AF = V_AF[0];
627
628 // Express the point vectors in the A frame, but still measuring from the
629 // body origins.
630 const Vec3 p_FP_A = X_AF.R() * p_FP; // 15 flops
631 const Vec3 p_BQ_A = X_AB.R() * p_BQ; // 15 flops
632
633 Vec3 p_AP, v_AP;
634 findStationInALocationVelocity(X_AF, V_AF, p_FP_A, p_AP, v_AP);//15 flops
635
636 Vec3 p_AQ, v_AQ;
637 findStationInALocationVelocity(X_AB, V_AB, p_BQ_A, p_AQ, v_AQ);//15 flops
638
639 p_PQ_A = p_AQ - p_AP; // 3 flops
640 const Vec3 p_PQ_A_dot = v_AQ - v_AP; // derivative in A (3 flops)
641 v_FQ_A = p_PQ_A_dot - w_AF % p_PQ_A; // derivative in F (12 flops)
642 }
643
644 // There is no findStationAccelerationFromState().
645
646 // Only configuration and velocity come from state; accelerations are an
647 // operand (15 flops).
648 // p_BS_A is p_BS rexpressed in A but not shifted to Ao.
649 // wXwX_p_BS_A is w_AB x (w_AB x p_BS_A) (Coriolis acceleration)
findStationInAAcceleration(const State & s,const Array_<SpatialVec,ConstrainedBodyIndex> & allA_AB,ConstrainedBodyIndex B,const Vec3 & p_BS_A,const Vec3 & wXwX_p_BS_A)650 Vec3 findStationInAAcceleration
651 (const State& s,
652 const Array_<SpatialVec,ConstrainedBodyIndex>& allA_AB,
653 ConstrainedBodyIndex B,
654 const Vec3& p_BS_A,
655 const Vec3& wXwX_p_BS_A) const
656 {
657 const SpatialVec& A_AB = allA_AB[B];
658 const Vec3& b_AB = A_AB[0]; const Vec3& a_AB = A_AB[1];
659
660 // Result is a + b X r + w X (w X r).
661 // ("b" is angular acceleration; w is angular velocity) 15 flops.
662 const Vec3 a_AS = a_AB + (b_AB % p_BS_A) + wXwX_p_BS_A;
663 return a_AS;
664 }
665
666 // Same as above but we only know the station in B. (48 flops)
findStationAcceleration(const State & s,const Array_<SpatialVec,ConstrainedBodyIndex> & allA_AB,ConstrainedBodyIndex B,const Vec3 & p_BS)667 Vec3 findStationAcceleration
668 (const State& s,
669 const Array_<SpatialVec,ConstrainedBodyIndex>& allA_AB,
670 ConstrainedBodyIndex B,
671 const Vec3& p_BS) const
672 { // p_BS_A is p_BS rexpressed in A but not shifted to Ao
673 const Rotation& R_AB = getBodyRotationFromState(s,B);
674 const Vec3& w_AB = getBodyAngularVelocityFromState(s,B);
675 const Vec3 p_BS_A = R_AB * p_BS; // 15 flops
676 const Vec3 wXwX_p_BS_A = w_AB % (w_AB % p_BS_A); // 18 flops
677 return findStationInAAcceleration(s,allA_AB,B,p_BS_A,wXwX_p_BS_A);
678 }
679
680
681 // Combo method is cheaper. Location and velocity come from state, accelerations
682 // from operand. NOTE: you must provide the p_BS vector expressed (but not
683 // measured) in A. 39 flops.
findStationInALocationVelocityAcceleration(const Transform & X_AB,const SpatialVec & V_AB,const SpatialVec & A_AB,const Vec3 & p_BS_A,Vec3 & p_AS,Vec3 & v_AS,Vec3 & a_AS)684 void findStationInALocationVelocityAcceleration
685 (const Transform& X_AB,
686 const SpatialVec& V_AB,
687 const SpatialVec& A_AB,
688 const Vec3& p_BS_A,
689 Vec3& p_AS, Vec3& v_AS, Vec3& a_AS) const
690 {
691 const Vec3& w_AB = V_AB[0]; const Vec3& v_AB = V_AB[1];
692 const Vec3& b_AB = A_AB[0]; const Vec3& a_AB = A_AB[1];
693
694 const Vec3 pdot_BS_A = w_AB % p_BS_A; // 9 flops
695
696 p_AS = X_AB.p() + p_BS_A; // 3 flops
697 v_AS = v_AB + pdot_BS_A; // 3 flops
698
699 // Result is a + b X r + w X (w X r).
700 // ("b" is angular acceleration; w is angular velocity) 24 flops.
701 a_AS = a_AB + (b_AB % p_BS_A) + (w_AB % pdot_BS_A);
702 }
703
704 // Apply an Ancestor-frame force to a B-frame station S, adding it to the
705 // appropriate bodyForcesInA entry, where bodyForcesInA is *already* size
706 // numConstrainedBodies for this Constraint. 30 flops.
addInStationForce(const State & s,ConstrainedBodyIndex B,const Vec3 & p_BS,const Vec3 & forceInA,Array_<SpatialVec,ConstrainedBodyIndex> & bodyForcesInA)707 void addInStationForce(const State& s,
708 ConstrainedBodyIndex B, const Vec3& p_BS,
709 const Vec3& forceInA,
710 Array_<SpatialVec, ConstrainedBodyIndex>& bodyForcesInA)
711 const
712 {
713 assert(bodyForcesInA.size() == getNumConstrainedBodies());
714 const Rotation& R_AB = getBodyRotationFromState(s,B);
715 const Vec3 p_BS_A = R_AB * p_BS; // 15 flops
716 bodyForcesInA[B] += SpatialVec(p_BS_A % forceInA, forceInA); // rXf, f
717 }
718
719 // If you already have the p_BS station vector re-expressed in A, use this
720 // faster method (15 flops).
addInStationInAForce(const Vec3 & p_BS_A,const Vec3 & forceInA,SpatialVec & bodyForceOnBInA)721 void addInStationInAForce(const Vec3& p_BS_A, const Vec3& forceInA,
722 SpatialVec& bodyForceOnBInA)
723 const
724 {
725 bodyForceOnBInA += SpatialVec(p_BS_A % forceInA, forceInA); // rXf, f
726 }
727
728 // Same thing but subtract the force; this is just to save having to negate it.
subInStationInAForce(const Vec3 & p_BS_A,const Vec3 & negForceInA,SpatialVec & bodyForceOnBInA)729 void subInStationInAForce(const Vec3& p_BS_A, const Vec3& negForceInA,
730 SpatialVec& bodyForceOnBInA)
731 const
732 {
733 bodyForceOnBInA -= SpatialVec(p_BS_A % negForceInA, negForceInA); //-rXf,-f
734 }
735
736 // Apply an Ancestor-frame torque to body B, updating the appropriate
737 // bodyForcesInA entry, where bodyForcesInA is *already* size
738 // numConstrainedBodies for this Constraint. 3 flops.
addInBodyTorque(const State & s,ConstrainedBodyIndex B,const Vec3 & torqueInA,Array_<SpatialVec,ConstrainedBodyIndex> & bodyForcesInA)739 void addInBodyTorque(const State& s, ConstrainedBodyIndex B,
740 const Vec3& torqueInA,
741 Array_<SpatialVec, ConstrainedBodyIndex>& bodyForcesInA)
742 const
743 {
744 assert(bodyForcesInA.size() == getNumConstrainedBodies());
745 bodyForcesInA[B][0] += torqueInA; // no force
746 }
747
748 // After realizeTopology() we can look at the values of modeling variables in
749 // the State. A Constraint is free to use those in determining how many
750 // constraint equations of each type to generate. The default implementation
751 // doesn't look at the state but instead returns the default numbers of
752 // equations supplied when the Constraint was constructed.
calcNumConstraintEquationsInUse(const State & s,int & mp,int & mv,int & ma)753 void calcNumConstraintEquationsInUse(const State& s, int& mp, int& mv, int& ma) const {
754 calcNumConstraintEquationsInUseVirtual(s,mp,mv,ma);
755 }
756
757 // Abbreviated version of the above; returns number of holonomic constraint
758 // equations in use.
calcNumPositionEquationsInUse(const State & s)759 int calcNumPositionEquationsInUse(const State& s) const {
760 int mp, mv, ma; calcNumConstraintEquationsInUse(s, mp, mv, ma);
761 return mp;
762 }
763 // Abbreviated version of the above; returns number of nonholonomic constraint
764 // equations in use.
calcNumVelocityEquationsInUse(const State & s)765 int calcNumVelocityEquationsInUse(const State& s) const {
766 int mp, mv, ma; calcNumConstraintEquationsInUse(s, mp, mv, ma);
767 return mv;
768 }
769 // Abbreviated version of the above; returns number of acceleration-only
770 // constraint equations in use.
calcNumAccelerationEquationsInUse(const State & s)771 int calcNumAccelerationEquationsInUse(const State& s) const {
772 int mp, mv, ma; calcNumConstraintEquationsInUse(s, mp, mv, ma);
773 return ma;
774 }
775
776 // The next three methods are just interfaces to the constraint
777 // operators like calcPositionErrors(); they extract needed operands
778 // from the supplied state and then call the operator.
779
780 // Calculate the mp position errors that would result from the configuration
781 // present in the supplied state (that is, q's and body transforms). The state
782 // must be realized through Time stage and part way through realization of
783 // Position stage.
784 void calcPositionErrorsFromState(const State& s, Array_<Real>& perr) const;
785
786 // Calculate the mp velocity errors resulting from pdot equations, given a
787 // configuration and velocities in the supplied state which must be realized
788 // through Position stage and part way through realization of Velocity stage.
789 void calcPositionDotErrorsFromState(const State& s, Array_<Real>& pverr) const;
790
791 // Calculate the mv velocity errors resulting from the nonholonomic constraint
792 // equations here, taking the configuration and velocities (u, qdot, body
793 // spatial velocities) from the supplied state, which must be realized through
794 // Position stage and part way through realization of Velocity stage.
795 void calcVelocityErrorsFromState(const State& s, Array_<Real>& verr) const;
796
797 // Calculate position errors given pose of the constrained bodies and the
798 // values of the constrained q's. Pull t from state.
calcPositionErrors(const State & s,const Array_<Transform,ConstrainedBodyIndex> & X_AB,const Array_<Real,ConstrainedQIndex> & constrainedQ,Array_<Real> & perr)799 void calcPositionErrors
800 (const State& s, // Stage::Time
801 const Array_<Transform,ConstrainedBodyIndex>& X_AB,
802 const Array_<Real, ConstrainedQIndex>& constrainedQ,
803 Array_<Real>& perr) // mp of these
804 const
805 {
806 assert(X_AB.size() == getNumConstrainedBodies());
807 assert(constrainedQ.size() == getNumConstrainedQ(s));
808 assert(perr.size() == calcNumPositionEquationsInUse(s));
809
810 calcPositionErrorsVirtual(s,X_AB,constrainedQ,perr);
811 }
812
813 // Calculate pdot errors given spatial velocity of the constrained bodies and
814 // the values of the constrained qdot's. Pull t, X_AB and q from state.
calcPositionDotErrors(const State & s,const Array_<SpatialVec,ConstrainedBodyIndex> & V_AB,const Array_<Real,ConstrainedQIndex> & constrainedQDot,Array_<Real> & pverr)815 void calcPositionDotErrors
816 (const State& s, // Stage::Position
817 const Array_<SpatialVec,ConstrainedBodyIndex>& V_AB,
818 const Array_<Real, ConstrainedQIndex>& constrainedQDot,
819 Array_<Real>& pverr) // mp of these
820 const
821 {
822 assert(V_AB.size() == getNumConstrainedBodies());
823 assert(constrainedQDot.size() == getNumConstrainedQ(s));
824 assert(pverr.size() == calcNumPositionEquationsInUse(s));
825
826 calcPositionDotErrorsVirtual(s,V_AB,constrainedQDot,pverr);
827 }
828
829 // Calculate pdotdot errors given spatial acceleration of the constrained
830 // bodies and the values of the constrained qdotdot's. Pull t, X_AB, q, V_AB,
831 // qdot from state.
calcPositionDotDotErrors(const State & s,const Array_<SpatialVec,ConstrainedBodyIndex> & A_AB,const Array_<Real,ConstrainedQIndex> & constrainedQDotDot,Array_<Real> & paerr)832 void calcPositionDotDotErrors
833 (const State& s, // Stage::Velocity
834 const Array_<SpatialVec,ConstrainedBodyIndex>& A_AB,
835 const Array_<Real, ConstrainedQIndex>& constrainedQDotDot,
836 Array_<Real>& paerr) // mp of these
837 const
838 {
839 assert(A_AB.size() == getNumConstrainedBodies());
840 assert(constrainedQDotDot.size() == getNumConstrainedQ(s));
841 assert(paerr.size() == calcNumPositionEquationsInUse(s));
842
843 calcPositionDotDotErrorsVirtual(s,A_AB,constrainedQDotDot,paerr);
844 }
845
846 // Given mp position constraint multipliers, generate the corresponding
847 // constraint forces acting on the constrained bodies and the generalized
848 // coordinates q of the constrained mobilizers and add them in to the given
849 // arrays. The state must be realized through Position stage.
addInPositionConstraintForces(const State & s,const Array_<Real> & multipliers,Array_<SpatialVec,ConstrainedBodyIndex> & bodyForcesInA,Array_<Real,ConstrainedQIndex> & qForces)850 void addInPositionConstraintForces
851 (const State& s,
852 const Array_<Real>& multipliers, // mp of these
853 Array_<SpatialVec, ConstrainedBodyIndex>& bodyForcesInA,
854 Array_<Real, ConstrainedQIndex>& qForces) const
855 {
856 assert(multipliers.size() == calcNumPositionEquationsInUse(s));
857 assert(bodyForcesInA.size() == getNumConstrainedBodies());
858 assert(qForces.size() == getNumConstrainedQ(s));
859
860 // Note that position constraints act on q (qdot,qdotdot) and for
861 // convenience we allow them to generate generalized forces in qdot-space
862 // rather than u-space. These will have to be mapped into u-space when
863 // they are used, since Simbody only deals in u-space forces normally.
864 // Since qdot=N*u, and we must have power ~f_qdot*qdot==~f_u*u, we have
865 // f_u=~N * f_qdot.
866 addInPositionConstraintForcesVirtual
867 (s,multipliers,bodyForcesInA,qForces);
868 }
869
870 // Calculate velocity errors (errors produced by nonholonomic constraint
871 // equations) given spatial velocity of the constrained bodies and the
872 // values of the constrained u's. Pull t, X_AB, q from state.
calcVelocityErrors(const State & s,const Array_<SpatialVec,ConstrainedBodyIndex> & V_AB,const Array_<Real,ConstrainedUIndex> & constrainedU,Array_<Real> & verr)873 void calcVelocityErrors
874 (const State& s, // Stage::Position
875 const Array_<SpatialVec,ConstrainedBodyIndex>& V_AB,
876 const Array_<Real, ConstrainedUIndex>& constrainedU,
877 Array_<Real>& verr) // mv of these
878 const
879 {
880 assert(V_AB.size() == getNumConstrainedBodies());
881 assert(constrainedU.size() == getNumConstrainedU(s));
882 assert(verr.size() == calcNumVelocityEquationsInUse(s));
883
884 calcVelocityErrorsVirtual(s,V_AB,constrainedU,verr);
885 }
886
887 // Calculate vdot errors (errors produced by nonholonomic constraint
888 // derivatives) given spatial acceleration of the constrained bodies and the
889 // values of the constrained udot's. Pull t, X_AB, q, V_AB, u from state.
calcVelocityDotErrors(const State & s,const Array_<SpatialVec,ConstrainedBodyIndex> & A_AB,const Array_<Real,ConstrainedUIndex> & constrainedUDot,Array_<Real> & vaerr)890 void calcVelocityDotErrors
891 (const State& s, // Stage::Velocity
892 const Array_<SpatialVec,ConstrainedBodyIndex>& A_AB,
893 const Array_<Real, ConstrainedUIndex>& constrainedUDot,
894 Array_<Real>& vaerr) // mv of these
895 const
896 {
897 assert(A_AB.size() == getNumConstrainedBodies());
898 assert(constrainedUDot.size() == getNumConstrainedU(s));
899 assert(vaerr.size() == calcNumVelocityEquationsInUse(s));
900
901 calcVelocityDotErrorsVirtual(s,A_AB,constrainedUDot,vaerr);
902 }
903
904
905 // Given mv velocity constraint multipliers, generate the corresponding
906 // constraint forces acting on the constrained bodies and the mobilities of the
907 // constrained mobilizers, and add them in to the given arrays. The state must
908 // be realized through Velocity stage unless the V matrix is
909 // velocity-independent in which case Position stage is enough.
addInVelocityConstraintForces(const State & s,const Array_<Real> & multipliers,Array_<SpatialVec,ConstrainedBodyIndex> & bodyForcesInA,Array_<Real,ConstrainedUIndex> & mobilityForces)910 void addInVelocityConstraintForces
911 (const State& s,
912 const Array_<Real>& multipliers, // mv of these
913 Array_<SpatialVec, ConstrainedBodyIndex>& bodyForcesInA,
914 Array_<Real, ConstrainedUIndex>& mobilityForces) const
915 {
916 assert(multipliers.size() == calcNumVelocityEquationsInUse(s));
917 assert(bodyForcesInA.size() == getNumConstrainedBodies());
918 assert(mobilityForces.size() == getNumConstrainedU(s));
919
920 addInVelocityConstraintForcesVirtual
921 (s,multipliers,bodyForcesInA,mobilityForces);
922 }
923
924 // Calculate acceleration errors (errors produced by acceleration-only
925 // constraint equations) given spatial acceleration of the constrained bodies
926 // and the values of the constrained udot's. Pull t, X_AB, q, V_AB, u from state.
calcAccelerationErrors(const State & s,const Array_<SpatialVec,ConstrainedBodyIndex> & A_AB,const Array_<Real,ConstrainedUIndex> & constrainedUDot,Array_<Real> & aerr)927 void calcAccelerationErrors
928 (const State& s, // Stage::Velocity
929 const Array_<SpatialVec,ConstrainedBodyIndex>& A_AB,
930 const Array_<Real, ConstrainedUIndex>& constrainedUDot,
931 Array_<Real>& aerr) // ma of these
932 const
933 {
934 assert(A_AB.size() == getNumConstrainedBodies());
935 assert(constrainedUDot.size() == getNumConstrainedU(s));
936 assert(aerr.size() == calcNumAccelerationEquationsInUse(s));
937
938 calcAccelerationErrorsVirtual(s,A_AB,constrainedUDot,aerr);
939 }
940
941
942 // Given ma acceleration constraint multipliers, generate the corresponding
943 // constraint forces acting on the constrained bodies and the mobilities of the
944 // constrained mobilizers, and add them in to the given arrays. The state must
945 // be realized through Velocity stage unless the A matrix is
946 // velocity-independent in which case Position stage is enough.
addInAccelerationConstraintForces(const State & s,const Array_<Real> & multipliers,Array_<SpatialVec,ConstrainedBodyIndex> & bodyForcesInA,Array_<Real,ConstrainedUIndex> & mobilityForces)947 void addInAccelerationConstraintForces
948 (const State& s,
949 const Array_<Real>& multipliers, // ma of these
950 Array_<SpatialVec, ConstrainedBodyIndex>& bodyForcesInA,
951 Array_<Real, ConstrainedUIndex>& mobilityForces) const
952 {
953 assert(multipliers.size() == calcNumAccelerationEquationsInUse(s));
954 assert(bodyForcesInA.size() == getNumConstrainedBodies());
955 assert(mobilityForces.size() == getNumConstrainedU(s));
956
957 addInAccelerationConstraintForcesVirtual
958 (s,multipliers,bodyForcesInA,mobilityForces);
959 }
960
calcDecorativeGeometryAndAppend(const State & s,Stage stage,Array_<DecorativeGeometry> & geom)961 void calcDecorativeGeometryAndAppend
962 (const State& s, Stage stage, Array_<DecorativeGeometry>& geom) const
963 {
964 // Let the individual constraint deal with any complicated stuff.
965 calcDecorativeGeometryAndAppendVirtual(s,stage,geom);
966 }
967
968 // Don't call these virtuals directly; use the provided interface
969 // methods for safety (they evaporate in Release builds anyway).
970
calcNumConstraintEquationsInUseVirtual(const State &,int & mp,int & mv,int & ma)971 virtual void calcNumConstraintEquationsInUseVirtual
972 (const State&, int& mp, int& mv, int& ma) const
973 { mp = defaultMp; mv = defaultMv; ma = defaultMa; }
974
realizeTopologyVirtual(State &)975 virtual void realizeTopologyVirtual (State&) const {}
realizeModelVirtual(State &)976 virtual void realizeModelVirtual (State&) const {}
realizeInstanceVirtual(const State &)977 virtual void realizeInstanceVirtual (const State&) const {}
realizeTimeVirtual(const State &)978 virtual void realizeTimeVirtual (const State&) const {}
realizePositionVirtual(const State &)979 virtual void realizePositionVirtual (const State&) const {}
realizeVelocityVirtual(const State &)980 virtual void realizeVelocityVirtual (const State&) const {}
realizeDynamicsVirtual(const State &)981 virtual void realizeDynamicsVirtual (const State&) const {}
realizeAccelerationVirtual(const State &)982 virtual void realizeAccelerationVirtual (const State&) const {}
realizeReportVirtual(const State &)983 virtual void realizeReportVirtual (const State&) const {}
984
985 // These must be defined if there are any position (holonomic) constraints.
986
987 // Pull t from state.
988 virtual void calcPositionErrorsVirtual
989 (const State& state, // Stage::Time
990 const Array_<Transform,ConstrainedBodyIndex>& X_AB,
991 const Array_<Real, ConstrainedQIndex>& constrainedQ,
992 Array_<Real>& perr) // mp of these
993 const;
994
995 // Pull t, X_AB and q from state.
996 virtual void calcPositionDotErrorsVirtual
997 (const State& state, // Stage::Position
998 const Array_<SpatialVec,ConstrainedBodyIndex>& V_AB,
999 const Array_<Real, ConstrainedQIndex>& constrainedQDot,
1000 Array_<Real>& pverr) // mp of these
1001 const;
1002
1003 // Pull t, X_AB, q, V_AB, qdot from state.
1004 virtual void calcPositionDotDotErrorsVirtual
1005 (const State& state, // Stage::Velocity
1006 const Array_<SpatialVec,ConstrainedBodyIndex>& A_AB,
1007 const Array_<Real, ConstrainedQIndex>& constrainedQDotDot,
1008 Array_<Real>& paerr) // mp of these
1009 const;
1010
1011 // Pull t, X_AB and q from state.
1012 virtual void addInPositionConstraintForcesVirtual
1013 (const State& state, // Stage::Position
1014 const Array_<Real>& multipliers, // mp of these
1015 Array_<SpatialVec,ConstrainedBodyIndex>& bodyForcesInA,
1016 Array_<Real, ConstrainedQIndex>& qForces)
1017 const;
1018
1019 // These must be defined if there are velocity (nonholonomic) constraints.
1020
1021 // Pull t, X_AB, q from state.
1022 virtual void calcVelocityErrorsVirtual
1023 (const State& state, // Stage::Position
1024 const Array_<SpatialVec,ConstrainedBodyIndex>& V_AB,
1025 const Array_<Real, ConstrainedUIndex>& constrainedU,
1026 Array_<Real>& verr) // mv of these
1027 const;
1028
1029 // Pull t, X_AB, q, V_AB, u from state.
1030 virtual void calcVelocityDotErrorsVirtual
1031 (const State& state, // Stage::Velocity
1032 const Array_<SpatialVec,ConstrainedBodyIndex>& A_AB,
1033 const Array_<Real, ConstrainedUIndex>& constrainedUDot,
1034 Array_<Real>& vaerr) // mv of these
1035 const;
1036
1037 // Pull t, X_AB, q, V_AB, u from state.
1038 virtual void addInVelocityConstraintForcesVirtual
1039 (const State& state, // Stage::Velocity
1040 const Array_<Real>& multipliers, // mv of these
1041 Array_<SpatialVec,ConstrainedBodyIndex>& bodyForcesInA,
1042 Array_<Real, ConstrainedUIndex>& mobilityForces)
1043 const;
1044
1045 // These must be defined if there are any acceleration-only constraints.
1046
1047 // Pull t, X_AB, q, V_AB, u from state.
1048 virtual void calcAccelerationErrorsVirtual
1049 (const State& state, // Stage::Velocity
1050 const Array_<SpatialVec,ConstrainedBodyIndex>& A_AB,
1051 const Array_<Real, ConstrainedUIndex>& constrainedUDot,
1052 Array_<Real>& aerr) // ma of these
1053 const;
1054
1055 // Pull t, X_AB, q, V_AB, u from state.
1056 virtual void addInAccelerationConstraintForcesVirtual
1057 (const State& state, // Stage::Velocity
1058 const Array_<Real>& multipliers, // ma of these
1059 Array_<SpatialVec,ConstrainedBodyIndex>& bodyForcesInA,
1060 Array_<Real, ConstrainedUIndex>& mobilityForces)
1061 const;
1062
1063
calcDecorativeGeometryAndAppendVirtual(const State & s,Stage stage,Array_<DecorativeGeometry> & geom)1064 virtual void calcDecorativeGeometryAndAppendVirtual
1065 (const State& s, Stage stage, Array_<DecorativeGeometry>& geom) const {}
1066
1067
1068 void invalidateTopologyCache() const;
1069 bool subsystemTopologyHasBeenRealized() const;
1070
1071 void setMyMatterSubsystem(SimbodyMatterSubsystem& matter,
1072 ConstraintIndex id);
1073
1074 const SimbodyMatterSubsystem& getMyMatterSubsystem() const;
1075
isInSubsystem()1076 bool isInSubsystem() const {
1077 return myMatterSubsystemRep != 0;
1078 }
1079
1080 // Is the supplied body in the same subsystem as this Constraint? (Returns
1081 // false also if either the Constraint or the MobilizedBody is not in a
1082 // subsystem.)
1083 bool isInSameSubsystem(const MobilizedBody& body) const;
1084
getNumConstrainedBodies()1085 int getNumConstrainedBodies() const {
1086 SimTK_ASSERT(subsystemTopologyHasBeenRealized(),
1087 "Number of constrained bodies is not available until Topology stage has been realized.");
1088 return (int)myConstrainedBodies.size();
1089 }
getNumConstrainedMobilizers()1090 int getNumConstrainedMobilizers() const {
1091 SimTK_ASSERT(subsystemTopologyHasBeenRealized(),
1092 "Number of constrained mobilizers is not available until Topology stage has been realized.");
1093 return (int)myConstrainedMobilizers.size();
1094 }
1095
1096 const MobilizedBody&
1097 getMobilizedBodyFromConstrainedMobilizer(ConstrainedMobilizerIndex) const;
1098 const MobilizedBody&
1099 getMobilizedBodyFromConstrainedBody(ConstrainedBodyIndex) const;
1100
1101 // Don't call this unless there is at least one Constrained Body.
1102 const MobilizedBody& getAncestorMobilizedBody() const;
1103
1104 // After realizeTopology() we remember whether this constraint's Ancestor
1105 // body is different from Ground.
isAncestorDifferentFromGround()1106 bool isAncestorDifferentFromGround() const {
1107 SimTK_ASSERT(subsystemTopologyHasBeenRealized(),
1108 "isAncestorDifferentFromGround(): must call realizeTopology() first.");
1109 return myAncestorBodyIsNotGround;
1110 }
1111
1112 // Find out how many holonomic (position), nonholonomic (velocity),
1113 // and acceleration-only constraint equations are generated by this Constraint
1114 // as currently modeled. State must be realized to Stage::Model.
1115 void getNumConstraintEquationsInUse
1116 (const State&, int& mHolo, int& mNonholo, int& mAccOnly) const;
1117
1118 // Return the starting index within the multiplier or udot error arrays
1119 // for each of the acceleration-level constraints produced by this Constraint.
1120 // Let holo0, nonholo0, accOnly0 be the first index of the slots assigned to
1121 // this Constraint's holonomic, nonholonomic, and acceleration-only constraints
1122 // within each block for that category. Then the returns here are:
1123 // px = holo0
1124 // vx = totalNumHolo + nonholo0
1125 // ax = totalNumHolo+totalNumNonholo + accOnly0
1126 // These are returned invalid if there are no constraint equations in that
1127 // category.
1128 void getIndexOfMultipliersInUse(const State& state,
1129 MultiplierIndex& px0,
1130 MultiplierIndex& vx0,
1131 MultiplierIndex& ax0) const;
1132
1133 void setMyPartInConstraintSpaceVector(const State& state,
1134 const Vector& myPart,
1135 Vector& constraintSpace) const;
1136
1137 void getMyPartFromConstraintSpaceVector(const State& state,
1138 const Vector& constraintSpace,
1139 Vector& myPart) const;
1140
1141 int getNumConstrainedQ(const State&) const;
1142 int getNumConstrainedU(const State&) const;
1143 int getNumConstrainedQ(const State&, ConstrainedMobilizerIndex) const;
1144 int getNumConstrainedU(const State&, ConstrainedMobilizerIndex) const;
1145 ConstrainedQIndex getConstrainedQIndex
1146 (const State&, ConstrainedMobilizerIndex, MobilizerQIndex which) const;
1147 ConstrainedUIndex getConstrainedUIndex
1148 (const State&, ConstrainedMobilizerIndex, MobilizerUIndex which) const;
1149
getMyMatterSubsystemRep()1150 const SimbodyMatterSubsystemRep& getMyMatterSubsystemRep() const {
1151 SimTK_ASSERT(myMatterSubsystemRep,
1152 "Operation illegal on a Constraint that is not in a Subsystem.");
1153 return *myMatterSubsystemRep;
1154 }
updMyMatterSubsystemRep()1155 SimbodyMatterSubsystemRep& updMyMatterSubsystemRep() {
1156 SimTK_ASSERT(myMatterSubsystemRep,
1157 "Operation illegal on a Constraint that is not in a Subsystem.");
1158 return *myMatterSubsystemRep;
1159 }
1160
getMyConstraintIndex()1161 ConstraintIndex getMyConstraintIndex() const {
1162 SimTK_ASSERT(myMatterSubsystemRep,
1163 "Operation illegal on a Constraint that is not in a Subsystem.");
1164 return myConstraintIndex;
1165 }
1166
1167
1168 // Calculate the transform X_AB of each ConstrainedBody in its Ancestor frame,
1169 // provided Ancestor!=Ground and A!=B. We expect a TreePositionCache in
1170 // which the mobilizer- and ground-frame position kinematics results have
1171 // already been calculated. We then fill in the missing ancestor-frame
1172 // results back into that same TreePositionCache.
1173 void calcConstrainedBodyTransformInAncestor(const SBInstanceVars&, // in only
1174 SBTreePositionCache& // in/out
1175 ) const;
1176
1177 // Similarly we calculate V_AB during realizeVelocity().
1178 // Here we expect a StateDigest realized through Position stage, and a
1179 // partly-filled-in VelocityCache where we'll put V_AB for the
1180 // ConstrainedBodies.
1181 void calcConstrainedBodyVelocityInAncestor(const SBInstanceVars&, // in only
1182 const SBTreePositionCache&, // "
1183 SBTreeVelocityCache& // in/out
1184 ) const;
1185
1186 // A_AB is not cached.
1187
1188 private:
1189 friend class Constraint;
1190
1191 // TOPOLOGY "STATE"
1192
1193 // These data members are filled in once the Constraint is added to
1194 // a MatterSubsystem.
1195 SimbodyMatterSubsystemRep* myMatterSubsystemRep;
1196 ConstraintIndex myConstraintIndex; // id within the matter subsystem
1197
1198 // We'll keep the constrained bodies and constrained mobilizers each in two
1199 // maps: one maps MobilizedBodyIndex->ConstrainedBody[Mobilizer]Index (O(log n)
1200 // to look up), and the other maps ConstrainedBody[Mobilizer]Index->
1201 // MobilizedBodyIndex (randomly addressable in constant time).
1202 MobilizedBody2ConstrainedBodyMap myMobilizedBody2ConstrainedBodyMap;
1203 MobilizedBody2ConstrainedMobilizerMap myMobilizedBody2ConstrainedMobilizerMap;
1204
1205 Array_<MobilizedBodyIndex> myConstrainedBodies; // [ConstrainedBodyIndex]
1206 Array_<MobilizedBodyIndex> myConstrainedMobilizers; // [ConstrainedMobilizerIndex]
1207
1208
1209 // These are the defaults for the number of position (holonomic) constraint
1210 // equations, the number of velocity (nonholonomic) constraint equations, and
1211 // the number of acceleration-only constraint equations.
1212 int defaultMp, defaultMv, defaultMa;
1213
1214 // This says whether the Model-stage "disabled" flag for this Constraint should
1215 // be initially on or off. Most constraints are enabled by default.
1216 bool defaultDisabled;
1217
1218 // ConditionalConstraints set this flag when they add a constraint to the
1219 // system. It can be referenced by a time stepper to determine whether to
1220 // treat a constraint as unconditional (in which case someone else has to
1221 // figure out whether it is active). This flag doesn't affect the operation of
1222 // the Constraint object itself; it is just stored and reported.
1223 bool constraintIsConditional;
1224
1225 // TOPOLOGY "CACHE"
1226
1227 // When topology is realized we study the constrained bodies to identify the
1228 // subtree of mobilized bodies which may be kinematically involved in
1229 // satisfaction of this Constraint. This requires finding the outmost common
1230 // ancestor of the constrained bodies. All mobilized bodies on the paths inward
1231 // from the constrained bodies to the ancestor are included; nothing outboard
1232 // of the constrained bodies is included; and the ancestor is treated as ground
1233 // so that its mobilities are *not* included. The Ancestor may be one of the
1234 // Constrained Bodies or may be distinct.
1235 mutable SimbodyMatterSubtree mySubtree;
1236
1237 // This is true only when (1) there are Constrained Bodies and (2) their
1238 // Ancestor is some MobilizedBody other than Ground.
1239 mutable bool myAncestorBodyIsNotGround;
1240
1241 // When the Ancestor is not Ground, each of the Constrained Bodies (except
1242 // the Ancestor) needs some additional precalculated data in the State cache
1243 // so is assigned a MatterSubsystem-global AncestorConstrainedBodyPool slot.
1244 // If Ancestor isn't Ground there is an entry here for each ConstrainedBody
1245 // (including Ancestor if it is one), but the index is invalid for Ancestor's
1246 // entry. When Ancestor is Ground we don't allocate this array.
1247 mutable Array_<AncestorConstrainedBodyPoolIndex> myPoolIndex;
1248 // index with ConstrainedBodyIndex
1249 };
1250
1251
1252
1253 //==============================================================================
1254 // POINT IN PLANE IMPL
1255 //==============================================================================
1256 class Constraint::PointInPlaneImpl : public ConstraintImpl {
1257 public:
PointInPlaneImpl()1258 PointInPlaneImpl()
1259 : ConstraintImpl(1,0,0), defaultPlaneNormal(), defaultPlaneHeight(0), defaultFollowerPoint(0),
1260 planeHalfWidth(1), pointRadius(Real(0.05))
1261 { }
clone()1262 PointInPlaneImpl* clone() const override { return new PointInPlaneImpl(*this); }
1263
1264 void calcDecorativeGeometryAndAppendVirtual
1265 (const State& s, Stage stage, Array_<DecorativeGeometry>& geom) const override;
1266
setPlaneDisplayHalfWidth(Real h)1267 void setPlaneDisplayHalfWidth(Real h) {
1268 // h <= 0 means don't display plane
1269 invalidateTopologyCache();
1270 planeHalfWidth = h > 0 ? h : 0;
1271 }
getPlaneDisplayHalfWidth()1272 Real getPlaneDisplayHalfWidth() const {return planeHalfWidth;}
1273
setPointDisplayRadius(Real r)1274 void setPointDisplayRadius(Real r) {
1275 // r <= 0 means don't display point
1276 invalidateTopologyCache();
1277 pointRadius= r > 0 ? r : 0;
1278 }
getPointDisplayRadius()1279 Real getPointDisplayRadius() const {return pointRadius;}
1280
1281 // Implementation of virtuals required for holonomic constraints.
1282
1283 // We have a point-in-plane connection between base body B, on which the plane
1284 // is fixed, and follower body F, on which the follower point S is fixed. All
1285 // forces will be applied at point S and the coincident material point C on B
1286 // which is instantaneously at the same spatial location as S. Then n is the
1287 // plane normal (a constant unit vector in B), h is the plane height measured
1288 // from the B origin along n (a scalar constant).Point C's location in B is
1289 // given by the vector p_BC from B's origin to the current location of S, and
1290 // expressed in B. That vector expressed in A is p_BC_A (= p_AS-p_AB). We will
1291 // express in the A frame but differentiate in the B frame.
1292 //
1293 // Derivation:
1294 // (1) Note that to take a derivative d/dt_B in a moving frame B, we can take
1295 // the derivative d/dt_A and then add in the contribution d_A/dt_B from
1296 // A's rotation in B (which is the angular velocity of A in B,
1297 // w_BA=-w_AB).
1298 // (2) p_CS = p_AS-p_AC = 0 by construction of C, but its derivative in A,
1299 // v_CS_A = d/dt_A p_CS != 0.
1300 //
1301 // perr = p_CS * n + constant
1302 // = constant (because p_CS==0 by construction)
1303 //
1304 // verr = d/dt_B perr = d/dt_A perr + d_A/dt_B perr
1305 // = [v_CS_A*n + p_CS * (w_AB X n)] + [(w_BA X p_CS) * n + p_CS * (w_BA X n)]
1306 // = v_CS_A*n + p_CS * (w_AB X n) (because terms in 2nd [] cancel)
1307 // = v_CS_A * n (because p_CS==0 by construction)
1308 //
1309 // aerr = d/dt_B verr = d/dt_A verr + d_A/dt_B verr
1310 // = [a_CS_A*n + v_CS_A*(w_AB X n) + v_CS_A*(w_AB X n) + p_CS*(2 w_AB X(w_AB X n))]
1311 // + [w_BAXv_CS_A*n + v_CS_A*w_BAXn]
1312 // = (a_CS_A - 2 w_AB X v_CS_A) * n (2nd bracket cancels, and p_CS==0)
1313 //
1314 // (The constant in perr is set so that S starts at the same height h as the
1315 // plane.)
1316 //
1317 // Then, from examination of verr noting that v_CS_A=v_AS-v_AC:
1318 // ~v_AS*n (body F at point S)
1319 // - ~v_AC*n (body B at point C)
1320 // so we apply a forces lambda*n to F at S, -lambda*n to B at C.
1321 //
1322 // --------------------------------
1323 // perr = ~p_BS*n - h
1324 // --------------------------------
calcPositionErrorsVirtual(const State & s,const Array_<Transform,ConstrainedBodyIndex> & allX_AB,const Array_<Real,ConstrainedQIndex> & constrainedQ,Array_<Real> & perr)1325 void calcPositionErrorsVirtual
1326 (const State& s, // Stage::Time
1327 const Array_<Transform,ConstrainedBodyIndex>& allX_AB,
1328 const Array_<Real, ConstrainedQIndex>& constrainedQ,
1329 Array_<Real>& perr) // mp of these
1330 const override
1331 {
1332 assert(allX_AB.size()==2 && constrainedQ.size()==0 && perr.size() == 1);
1333
1334 const Vec3 p_AS = findStationLocation(allX_AB, followerBody,
1335 defaultFollowerPoint);
1336 const Transform& X_AB = getBodyTransform(allX_AB, planeBody);
1337 const Vec3 p_BC = ~X_AB * p_AS; // shift to B origin, reexpress in B;
1338 // C is material pt of B coincident with S
1339
1340 // We'll calculate this scalar using B-frame vectors, but any frame would
1341 // have done.
1342 perr[0] = dot(p_BC, defaultPlaneNormal) - defaultPlaneHeight;
1343 }
1344
1345 // --------------------------------
1346 // verr = ~v_CS_A*n
1347 // --------------------------------
calcPositionDotErrorsVirtual(const State & s,const Array_<SpatialVec,ConstrainedBodyIndex> & V_AB,const Array_<Real,ConstrainedQIndex> & constrainedQDot,Array_<Real> & pverr)1348 void calcPositionDotErrorsVirtual
1349 (const State& s, // Stage::Position
1350 const Array_<SpatialVec,ConstrainedBodyIndex>& V_AB,
1351 const Array_<Real, ConstrainedQIndex>& constrainedQDot,
1352 Array_<Real>& pverr) // mp of these
1353 const override
1354 {
1355 assert(V_AB.size()==2 && constrainedQDot.size()==0 && pverr.size() == 1);
1356 //TODO: should be able to get p info from State
1357
1358 const Vec3 p_AS = findStationLocationFromState(s, followerBody,
1359 defaultFollowerPoint);
1360 const Transform& X_AB = getBodyTransformFromState(s, planeBody);
1361 const Vec3 p_BC = ~X_AB * p_AS; // shift to B origin, reexpress in B;
1362 // C is material point of B coincident with S
1363 const UnitVec3 n_A = X_AB.R() * defaultPlaneNormal;
1364
1365 const Vec3 v_AS = findStationVelocity(s, V_AB, followerBody,
1366 defaultFollowerPoint);
1367 const Vec3 v_AC = findStationVelocity(s, V_AB, planeBody, p_BC);
1368
1369 // Calculate this scalar using A-frame vectors.
1370 pverr[0] = dot( v_AS-v_AC, n_A );
1371 }
1372
1373 // -------------------------------------
1374 // aerr = ~(a_CS_A - 2 w_AB X v_CS_A) * n
1375 // -------------------------------------
calcPositionDotDotErrorsVirtual(const State & s,const Array_<SpatialVec,ConstrainedBodyIndex> & A_AB,const Array_<Real,ConstrainedQIndex> & constrainedQDotDot,Array_<Real> & paerr)1376 void calcPositionDotDotErrorsVirtual
1377 (const State& s, // Stage::Velocity
1378 const Array_<SpatialVec,ConstrainedBodyIndex>& A_AB,
1379 const Array_<Real, ConstrainedQIndex>& constrainedQDotDot,
1380 Array_<Real>& paerr) // mp of these
1381 const override
1382 {
1383 assert(A_AB.size()==2 && constrainedQDotDot.size()==0 && paerr.size() == 1);
1384 //TODO: should be able to get p and v info from State
1385 const Vec3 p_AS = findStationLocationFromState(s, followerBody,
1386 defaultFollowerPoint);
1387 const Transform& X_AB = getBodyTransformFromState(s, planeBody);
1388 const Vec3 p_BC = ~X_AB * p_AS; // shift to B origin, reexpress in B;
1389 // C is material point of B coincident with S
1390 const UnitVec3 n_A = X_AB.R() * defaultPlaneNormal;
1391
1392 const Vec3& w_AB = getBodyAngularVelocityFromState(s, planeBody);
1393 const Vec3 v_AS = findStationVelocityFromState(s, followerBody,
1394 defaultFollowerPoint);
1395 const Vec3 v_AC = findStationVelocityFromState(s, planeBody, p_BC);
1396
1397 const Vec3 a_AS = findStationAcceleration(s, A_AB, followerBody,
1398 defaultFollowerPoint);;
1399 const Vec3 a_AC = findStationAcceleration(s, A_AB, planeBody, p_BC);
1400
1401 paerr[0] = dot( (a_AS-a_AC) - 2.*w_AB % (v_AS-v_AC), n_A );
1402 }
1403
1404 // apply f=lambda*n to the follower point S of body F,
1405 // -f to point C (coincident point) of body B
addInPositionConstraintForcesVirtual(const State & s,const Array_<Real> & multipliers,Array_<SpatialVec,ConstrainedBodyIndex> & bodyForcesInA,Array_<Real,ConstrainedQIndex> & qForces)1406 void addInPositionConstraintForcesVirtual
1407 (const State& s, // Stage::Position
1408 const Array_<Real>& multipliers, // mp of these
1409 Array_<SpatialVec,ConstrainedBodyIndex>& bodyForcesInA,
1410 Array_<Real, ConstrainedQIndex>& qForces)
1411 const override
1412 {
1413 assert(multipliers.size()==1 && bodyForcesInA.size()==2
1414 && qForces.size()==0);
1415 const Real lambda = multipliers[0];
1416
1417 //TODO: should be able to get p info from State
1418 const Vec3& p_FS = defaultFollowerPoint; // measured & expressed in F
1419 const Vec3 p_AS = findStationLocationFromState(s, followerBody,
1420 defaultFollowerPoint);
1421 const Transform& X_AB = getBodyTransformFromState(s, planeBody);
1422 const Vec3 p_BC = ~X_AB * p_AS; // measured & expressed in B
1423 const Vec3 force_A = X_AB.R()*(lambda*defaultPlaneNormal);
1424
1425 addInStationForce(s, followerBody, p_FS, force_A, bodyForcesInA);
1426 addInStationForce(s, planeBody, p_BC, -force_A, bodyForcesInA);
1427 }
1428
1429 SimTK_DOWNCAST(PointInPlaneImpl, ConstraintImpl);
1430 //------------------------------------------------------------------------------
1431 private:
1432 friend class Constraint::PointInPlane;
1433
1434 ConstrainedBodyIndex planeBody; // B1
1435 ConstrainedBodyIndex followerBody; // B2
1436
1437 UnitVec3 defaultPlaneNormal; // on body 1, exp. in B1 frame
1438 Real defaultPlaneHeight;
1439 Vec3 defaultFollowerPoint; // on body 2, exp. in B2 frame
1440
1441 // These are just for visualization
1442 Real planeHalfWidth;
1443 Real pointRadius;
1444 };
1445
1446
1447
1448 //==============================================================================
1449 // POINT ON LINE IMPL
1450 //==============================================================================
1451 class Constraint::PointOnLineImpl : public ConstraintImpl {
1452 public:
PointOnLineImpl()1453 PointOnLineImpl()
1454 : ConstraintImpl(2,0,0),
1455 defaultLineDirection(), defaultPointOnLine(), defaultFollowerPoint(0),
1456 lineHalfLength(1), pointRadius(Real(0.05))
1457 { }
clone()1458 PointOnLineImpl* clone() const override { return new PointOnLineImpl(*this); }
1459
1460 void calcDecorativeGeometryAndAppendVirtual
1461 (const State& s, Stage stage, Array_<DecorativeGeometry>& geom) const override;
1462
setLineDisplayHalfLength(Real h)1463 void setLineDisplayHalfLength(Real h) {
1464 // h <= 0 means don't display line
1465 invalidateTopologyCache();
1466 lineHalfLength = h > 0 ? h : 0;
1467 }
getLineDisplayHalfLength()1468 Real getLineDisplayHalfLength() const {return lineHalfLength;}
1469
setPointDisplayRadius(Real r)1470 void setPointDisplayRadius(Real r) {
1471 // r <= 0 means don't display point
1472 invalidateTopologyCache();
1473 pointRadius= r > 0 ? r : 0;
1474 }
getPointDisplayRadius()1475 Real getPointDisplayRadius() const {return pointRadius;}
1476
1477 // Implementation of ContraintRep virtuals
realizeTopologyVirtual(State & s)1478 void realizeTopologyVirtual(State& s) const override {
1479 x = defaultLineDirection.perp(); // x and y are mutable
1480 y = UnitVec3(defaultLineDirection % x);
1481 }
1482
1483 // Implementation of virtuals required for holonomic constraints.
1484
1485 // We have a point-on-line connection between base body B, on which the line is
1486 // fixed, and follower body F, on which the follower point S is fixed. All
1487 // forces will be applied at point S and the coincident material point C on B
1488 // which is instantaneously at the same spatial location as S. Then z is a unit
1489 // vector in the direction of the line, and P is a point fixed to B that the
1490 // line passes through. We will enforce this using two point-on-plane
1491 // constraints, where the intersection of the two planes is the line. For that
1492 // we need two plane normals perpendicular to z. We'll use an arbitrary
1493 // perpendicular x, then use y=z X x as the other perpendicular. This
1494 // establishes a right handed coordinate system where the line is along the z
1495 // axis, and we'll apply constraint forces in the x-y plane.
1496 //
1497 // See the point-in-plane constraint for details; here we're just picking x and
1498 // y as plane normals.
1499
1500 // --------------------------------
1501 // perr = ~(p_BS-p_BP) * x
1502 // ~(p_BS-p_BP) * y
1503 // --------------------------------
calcPositionErrorsVirtual(const State & s,const Array_<Transform,ConstrainedBodyIndex> & allX_AB,const Array_<Real,ConstrainedQIndex> & constrainedQ,Array_<Real> & perr)1504 void calcPositionErrorsVirtual
1505 (const State& s, // Stage::Time
1506 const Array_<Transform,ConstrainedBodyIndex>& allX_AB,
1507 const Array_<Real, ConstrainedQIndex>& constrainedQ,
1508 Array_<Real>& perr) // mp of these
1509 const override
1510 {
1511 assert(allX_AB.size()==2 && constrainedQ.size()==0 && perr.size() == 2);
1512
1513 const Transform& X_AB = getBodyTransform(allX_AB, lineBody);
1514 const Vec3 p_AS = findStationLocation(allX_AB, followerBody,
1515 defaultFollowerPoint);
1516 const Vec3 p_BC = ~X_AB * p_AS; // shift to B origin, reexpress in B;
1517 // C is material point of B coincident with S
1518 const Vec3 p_PC = p_BC - defaultPointOnLine;
1519
1520 // We'll calculate these two scalars using B-frame vectors, but any frame
1521 // would have done.
1522 perr[0] = ~p_PC * x;
1523 perr[1] = ~p_PC * y;
1524 }
1525
1526 // --------------------------------
1527 // verr = ~v_CS_A*n
1528 // --------------------------------
calcPositionDotErrorsVirtual(const State & s,const Array_<SpatialVec,ConstrainedBodyIndex> & allV_AB,const Array_<Real,ConstrainedQIndex> & constrainedQDot,Array_<Real> & pverr)1529 void calcPositionDotErrorsVirtual
1530 (const State& s, // Stage::Position
1531 const Array_<SpatialVec,ConstrainedBodyIndex>& allV_AB,
1532 const Array_<Real, ConstrainedQIndex>& constrainedQDot,
1533 Array_<Real>& pverr) // mp of these
1534 const override
1535 {
1536 assert(allV_AB.size()==2 && constrainedQDot.size()==0 && pverr.size() == 2);
1537 //TODO: should be able to get p info from State
1538 const Transform& X_AB = getBodyTransformFromState(s, lineBody);
1539 const Vec3 p_AS = findStationLocationFromState(s, followerBody,
1540 defaultFollowerPoint);
1541 const Vec3 p_BC = ~X_AB * p_AS;
1542 const Vec3 p_PC = p_BC - defaultPointOnLine;
1543
1544 const Vec3 v_AS = findStationVelocity(s, allV_AB, followerBody,
1545 defaultFollowerPoint);
1546 const Vec3 v_AC = findStationVelocity(s, allV_AB, lineBody, p_BC);
1547
1548 const Vec3 v_CS_B = ~X_AB.R()*(v_AS-v_AC); // reexpress in B
1549
1550 // Calculate these scalar using B-frame vectors, but any frame would
1551 // have done.
1552 pverr[0] = ~v_CS_B * x;
1553 pverr[1] = ~v_CS_B * y;
1554 }
1555
1556 // -------------------------------------
1557 // aerr = ~(a_CS_A - 2 w_AB X v_CS_A) * n
1558 // -------------------------------------
calcPositionDotDotErrorsVirtual(const State & s,const Array_<SpatialVec,ConstrainedBodyIndex> & allA_AB,const Array_<Real,ConstrainedQIndex> & constrainedQDotDot,Array_<Real> & paerr)1559 void calcPositionDotDotErrorsVirtual
1560 (const State& s, // Stage::Velocity
1561 const Array_<SpatialVec,ConstrainedBodyIndex>& allA_AB,
1562 const Array_<Real, ConstrainedQIndex>& constrainedQDotDot,
1563 Array_<Real>& paerr) // mp of these
1564 const override
1565 {
1566 assert(allA_AB.size()==2 && constrainedQDotDot.size()==0 && paerr.size()==2);
1567 //TODO: should be able to get p and v info from State
1568 const Transform& X_AB = getBodyTransformFromState(s, lineBody);
1569 const Vec3 p_AS = findStationLocationFromState(s, followerBody,
1570 defaultFollowerPoint);
1571 const Vec3 p_BC = ~X_AB * p_AS; // shift to B origin, reexpress in B;
1572 // C is material point of B coincident with S
1573 const Vec3 p_PC = p_BC - defaultPointOnLine;
1574
1575 const Vec3& w_AB = getBodyAngularVelocityFromState(s, lineBody);
1576 const Vec3 v_AS = findStationVelocityFromState(s, followerBody, defaultFollowerPoint);
1577 const Vec3 v_AC = findStationVelocityFromState(s, lineBody, p_BC);
1578
1579 const Vec3 a_AS = findStationAcceleration(s, allA_AB, followerBody,
1580 defaultFollowerPoint);
1581 const Vec3 a_AC = findStationAcceleration(s, allA_AB, lineBody, p_BC);
1582 const Vec3 a_CS_B = ~X_AB.R()*(a_AS-a_AC - 2 * w_AB % (v_AS-v_AC));
1583
1584 // Calculate these scalar using B-frame vectors, but any frame would
1585 // have done.
1586 paerr[0] = ~a_CS_B * x;
1587 paerr[1] = ~a_CS_B * y;
1588 }
1589
1590 // apply f=lambda0*x + lambda1*y to the follower point S of body F,
1591 // -f to point C (coincident point) of body B
addInPositionConstraintForcesVirtual(const State & s,const Array_<Real> & multipliers,Array_<SpatialVec,ConstrainedBodyIndex> & bodyForcesInA,Array_<Real,ConstrainedQIndex> & qForces)1592 void addInPositionConstraintForcesVirtual
1593 (const State& s, // Stage::Position
1594 const Array_<Real>& multipliers, // mp of these
1595 Array_<SpatialVec,ConstrainedBodyIndex>& bodyForcesInA,
1596 Array_<Real, ConstrainedQIndex>& qForces)
1597 const override
1598 {
1599 assert(multipliers.size()==2 && bodyForcesInA.size()==2
1600 && qForces.size()==0);
1601 const Vec2 lambda = Vec2::getAs(&multipliers[0]);
1602
1603 //TODO: should be able to get p info from State
1604 const Transform& X_AB = getBodyTransformFromState(s, lineBody);
1605 const Vec3& p_FS = defaultFollowerPoint; // measured & expressed in F
1606 const Vec3 p_AS = findStationLocationFromState(s, followerBody,
1607 defaultFollowerPoint);
1608 const Vec3 p_BC = ~X_AB * p_AS; // measured & expressed in B
1609
1610 const Vec3 force_B = lambda[0] * x + lambda[1] * y;
1611 const Vec3 force_A = X_AB.R() * force_B;
1612
1613 addInStationForce(s, followerBody, p_FS, force_A, bodyForcesInA);
1614 addInStationForce(s, lineBody, p_BC, -force_A, bodyForcesInA);
1615 }
1616
1617 SimTK_DOWNCAST(PointOnLineImpl, ConstraintImpl);
1618 //------------------------------------------------------------------------------
1619 private:
1620 friend class Constraint::PointOnLine;
1621
1622 ConstrainedBodyIndex lineBody; // B
1623 ConstrainedBodyIndex followerBody; // F
1624
1625 UnitVec3 defaultLineDirection; // z on B, exp. in B frame
1626 Vec3 defaultPointOnLine; // P on B, meas&exp in B frame
1627 Vec3 defaultFollowerPoint; // S on F, meas&exp in F frame
1628
1629 // These are just for visualization
1630 Real lineHalfLength;
1631 Real pointRadius;
1632
1633 // TOPOLOGY CACHE (that is, calculated from construction data)
1634 mutable UnitVec3 x, y;
1635 };
1636
1637
1638
1639 //==============================================================================
1640 // CONSTANT ANGLE IMPL
1641 //==============================================================================
1642 class Constraint::ConstantAngleImpl : public ConstraintImpl {
1643 public:
ConstantAngleImpl()1644 ConstantAngleImpl()
1645 : ConstraintImpl(1,0,0), defaultAxisB(), defaultAxisF(), defaultAngle(Pi/2),
1646 axisLength(1), axisThickness(1), cosineOfDefaultAngle(NaN)
1647 { }
clone()1648 ConstantAngleImpl* clone() const override { return new ConstantAngleImpl(*this); }
1649
1650 void calcDecorativeGeometryAndAppendVirtual
1651 (const State& s, Stage stage, Array_<DecorativeGeometry>& geom) const override;
1652
setAxisLength(Real length)1653 void setAxisLength(Real length) {
1654 // length <= 0 means don't display axis
1655 invalidateTopologyCache();
1656 axisLength = length > 0 ? length : 0;
1657 }
getAxisLength()1658 Real getAxisLength() const {return axisLength;}
1659
setAxisThickness(Real t)1660 void setAxisThickness(Real t) {
1661 // t <= 0 means don't display axis
1662 invalidateTopologyCache();
1663 axisThickness = t > 0 ? t : 0;
1664 }
getAxisThickness()1665 Real getAxisThickness() const {return axisThickness;}
1666
1667 // Implementation of ContraintRep virtuals
realizeTopologyVirtual(State & s)1668 void realizeTopologyVirtual(State& s) const override {
1669 cosineOfDefaultAngle = std::cos(defaultAngle);
1670 }
1671
1672
1673 // Implementation of virtuals required for holonomic constraints.
1674
1675 // Let B=B1 be the "base" body onto which unit vector b is fixed, and F=B2 the
1676 // "follower" body onto which unit vector f is fixed. The angle theta between
1677 // these vectors is given by cos(theta) = dot(b, f) with the axes expressed in
1678 // a common basis. This can range from 1 to -1, corresponding to angles 0 to
1679 // 180 respectively. We would like to enforce the constraint that cos(theta) is
1680 // a constant. This can be done with a single constraint equation as long as
1681 // theta is sufficiently far away from 0 and 180, with the numerically best
1682 // performance at theta=90 degrees where cos(theta)==0.
1683 //
1684 // If you want to enforce that two axes are aligned with one another (that is,
1685 // the angle between them is 0 or 180), that takes *two* constraint equations
1686 // since the only remaining rotation is about the common axis.
1687 //
1688 // We will work in the A frame.
1689 //
1690 // ------------------------------
1691 // perr = ~b_A * f_A - cos(theta)
1692 // ------------------------------
1693 //
1694 // verr = d/dt perr (derivative taken in A)
1695 // = ~b_A * (w_AF % f_A) + ~f_A * (w_AB % b_A)
1696 // = ~w_AF * (f_A % b_A) - ~w_AB * (f_A % b_A) (scalar triple product identity)
1697 // => ------------------------------
1698 // verr = ~(w_AF-w_AB) * (f_A % b_A)
1699 // ---------------------------------
1700 //
1701 // aerr = d/dt verr (derivative taken in A)
1702 // = ~(b_AF-b_AB) * (f_A % b_A)
1703 // + (w_AF-w_AB) * ((w_AF%f_A) % b_A)
1704 // + (w_AF-w_AB) * (f_A % (w_AB%b_A))
1705 // = ~(b_AF-b_AB) * (f_A % b_A)
1706 // + ~(w_AF-w_AB) * ((w_AF%f_A) % b_A) - (w_AB%b_A) % f_A)
1707 // => -----------------------------------------------------------
1708 // aerr = ~(b_AF-b_AB) * (f_A % b_A)
1709 // + ~(w_AF-w_AB) * ((w_AF%f_A) % b_A) - (w_AB%b_A) % f_A)
1710 // --------------------------------------------------------------
1711 //
1712 // Constraint torque can be determined by inspection of verr:
1713 // lambda * (f_A % b_A) applied to body F
1714 // -lambda * (f_A % b_A) applied to body B
1715 //
1716
1717 // ------------------------------
1718 // perr = ~b_A * f_A - cos(theta)
1719 // ------------------------------
calcPositionErrorsVirtual(const State & s,const Array_<Transform,ConstrainedBodyIndex> & allX_AB,const Array_<Real,ConstrainedQIndex> & constrainedQ,Array_<Real> & perr)1720 void calcPositionErrorsVirtual
1721 (const State& s, // Stage::Time
1722 const Array_<Transform,ConstrainedBodyIndex>& allX_AB,
1723 const Array_<Real, ConstrainedQIndex>& constrainedQ,
1724 Array_<Real>& perr) // mp of these
1725 const override
1726 {
1727 assert(allX_AB.size()==2 && constrainedQ.size()==0 && perr.size() == 1);
1728
1729 const Rotation& R_AB = getBodyRotation(allX_AB, B);
1730 const Rotation& R_AF = getBodyRotation(allX_AB, F);
1731 const UnitVec3 b_A = R_AB * defaultAxisB;
1732 const UnitVec3 f_A = R_AF * defaultAxisF;
1733
1734 perr[0] = dot(b_A, f_A) - cosineOfDefaultAngle;
1735 }
1736
1737 // ----------------------------------
1738 // pverr = ~(w_AF-w_AB) * (f_A % b_A)
1739 // ----------------------------------
calcPositionDotErrorsVirtual(const State & s,const Array_<SpatialVec,ConstrainedBodyIndex> & allV_AB,const Array_<Real,ConstrainedQIndex> & constrainedQDot,Array_<Real> & pverr)1740 void calcPositionDotErrorsVirtual
1741 (const State& s, // Stage::Position
1742 const Array_<SpatialVec,ConstrainedBodyIndex>& allV_AB,
1743 const Array_<Real, ConstrainedQIndex>& constrainedQDot,
1744 Array_<Real>& pverr) // mp of these
1745 const override
1746 {
1747 assert(allV_AB.size()==2 && constrainedQDot.size()==0 && pverr.size()==1);
1748 //TODO: should be able to get p info from State
1749 const Rotation& R_AB = getBodyRotationFromState(s, B);
1750 const Rotation& R_AF = getBodyRotationFromState(s, F);
1751 const UnitVec3 b_A = R_AB * defaultAxisB;
1752 const UnitVec3 f_A = R_AF * defaultAxisF;
1753
1754 const Vec3& w_AB = getBodyAngularVelocity(allV_AB, B);
1755 const Vec3& w_AF = getBodyAngularVelocity(allV_AB, F);
1756
1757 pverr[0] = dot( w_AF-w_AB, f_A % b_A );
1758 }
1759
1760 // --------------------------------------------------------------
1761 // paerr = ~(b_AF-b_AB) * (f_A % b_A)
1762 // + ~(w_AF-w_AB) * ((w_AF%f_A) % b_A) - (w_AB%b_A) % f_A)
1763 // --------------------------------------------------------------
calcPositionDotDotErrorsVirtual(const State & s,const Array_<SpatialVec,ConstrainedBodyIndex> & allA_AB,const Array_<Real,ConstrainedQIndex> & constrainedQDotDot,Array_<Real> & paerr)1764 void calcPositionDotDotErrorsVirtual
1765 (const State& s, // Stage::Velocity
1766 const Array_<SpatialVec,ConstrainedBodyIndex>& allA_AB,
1767 const Array_<Real, ConstrainedQIndex>& constrainedQDotDot,
1768 Array_<Real>& paerr) // mp of these
1769 const override
1770 {
1771 assert(allA_AB.size()==2 && constrainedQDotDot.size()==0 && paerr.size()==1);
1772 //TODO: should be able to get p and v info from State
1773 const Rotation& R_AB = getBodyRotationFromState(s, B);
1774 const Rotation& R_AF = getBodyRotationFromState(s, F);
1775 const UnitVec3 b_A = R_AB * defaultAxisB;
1776 const UnitVec3 f_A = R_AF * defaultAxisF;
1777 const Vec3& w_AB = getBodyAngularVelocityFromState(s, B);
1778 const Vec3& w_AF = getBodyAngularVelocityFromState(s, F);
1779
1780 const Vec3& b_AB = getBodyAngularAcceleration(allA_AB, B);
1781 const Vec3& b_AF = getBodyAngularAcceleration(allA_AB, F);
1782
1783 paerr[0] = dot( b_AF-b_AB, f_A % b_A )
1784 + dot( w_AF-w_AB, (w_AF%f_A) % b_A - (w_AB%b_A) % f_A);
1785 }
1786
1787 // lambda * (f_A % b_A) applied to body F
1788 // -lambda * (f_A % b_A) applied to body B
addInPositionConstraintForcesVirtual(const State & s,const Array_<Real> & multipliers,Array_<SpatialVec,ConstrainedBodyIndex> & bodyForcesInA,Array_<Real,ConstrainedQIndex> & qForces)1789 void addInPositionConstraintForcesVirtual
1790 (const State& s, // Stage::Position
1791 const Array_<Real>& multipliers, // mp of these
1792 Array_<SpatialVec,ConstrainedBodyIndex>& bodyForcesInA,
1793 Array_<Real, ConstrainedQIndex>& qForces)
1794 const override
1795 {
1796 assert(multipliers.size()==1 && bodyForcesInA.size()==2
1797 && qForces.size()==0);
1798 const Real lambda = multipliers[0];
1799 //TODO: should be able to get p info from State
1800 const Rotation& R_AB = getBodyRotationFromState(s, B);
1801 const Rotation& R_AF = getBodyRotationFromState(s, F);
1802 const UnitVec3 b_A = R_AB*defaultAxisB;
1803 const UnitVec3 f_A = R_AF*defaultAxisF;
1804 const Vec3 torque_F_A = lambda * (f_A % b_A); // on F, in A frame
1805
1806 addInBodyTorque(s, F, torque_F_A, bodyForcesInA);
1807 addInBodyTorque(s, B, -torque_F_A, bodyForcesInA);
1808 }
1809
1810 SimTK_DOWNCAST(ConstantAngleImpl, ConstraintImpl);
1811 //------------------------------------------------------------------------------
1812 private:
1813 friend class Constraint::ConstantAngle;
1814
1815 ConstrainedBodyIndex B; // B1 is "base" body
1816 ConstrainedBodyIndex F; // B2 is "follower" body
1817
1818 UnitVec3 defaultAxisB; // fixed to B, expressed in B frame
1819 UnitVec3 defaultAxisF; // fixed to F, expressed in F frame
1820 Real defaultAngle; // required angle between axisB and axisF
1821
1822 // These are just for visualization
1823 Real axisLength;
1824 Real axisThickness;
1825
1826 // TOPOLOGY CACHE (that is, calculated from construction data)
1827 mutable Real cosineOfDefaultAngle;
1828 };
1829
1830
1831
1832 //==============================================================================
1833 // BALL IMPL
1834 //==============================================================================
1835 class Constraint::BallImpl : public ConstraintImpl {
1836 public:
BallImpl()1837 BallImpl() : ConstraintImpl(3,0,0), defaultPoint1(0), defaultPoint2(0),
1838 defaultRadius(Real(0.1)) { }
clone()1839 BallImpl* clone() const override { return new BallImpl(*this); }
1840
1841 void calcDecorativeGeometryAndAppendVirtual
1842 (const State& s, Stage stage, Array_<DecorativeGeometry>& geom) const override;
1843
setDefaultRadius(Real r)1844 void setDefaultRadius(Real r) {
1845 // r <= 0 means don't display
1846 invalidateTopologyCache();
1847 defaultRadius = r > 0 ? r : 0;
1848 }
getDefaultRadius()1849 Real getDefaultRadius() const {return defaultRadius;}
1850
1851 // The default body stations may be overridden by setting instance variables
1852 // in the state. We allocate the state resources here.
1853 void realizeTopologyVirtual(State& state) const override;
1854
1855 // Return the pair of constrained station points, with the first expressed
1856 // in the body 1 frame and the second in the body 2 frame. Note that although
1857 // these are used to define the position error, only the station on body 2
1858 // is used to generate constraint forces; the point of body 1 that is
1859 // coincident with the body 2 point receives the equal and opposite force.
1860 const std::pair<Vec3,Vec3>& getBodyStations(const State& state) const;
1861
1862 // Return a writable reference into the Instance-stage state variable
1863 // containing the pair of constrained station points, with the first expressed
1864 // in the body 1 frame and the second in the body 2 frame. Calling this
1865 // method invalidates the Instance stage and above in the given state.
1866 std::pair<Vec3,Vec3>& updBodyStations(State& state) const;
1867
1868 // Implementation of virtuals required for holonomic constraints.
1869
1870 // We have a ball joint between base body B and follower body F, located at a
1871 // point P fixed to B and point S fixed on F. All forces will be applied at
1872 // point S and the coincident material point C on B which is instantaneously at
1873 // the same spatial location as S. We will work in the A frame.
1874 //
1875 // First, find the material point C of B that is coincident
1876 // in space with point S of F: p_BC = p_AS-p_AB. This vector
1877 // is *constant* in the B frame because it is a material point,
1878 // despite the fact that its definition involves a point which
1879 // moves with respect to B. The velocity constraint is then
1880 // very simple: the spatial velocity of point C of B should be
1881 // the same as the spatial velocity of point S of F:
1882 // verr = v_AS - v_AC = v_AS - (v_AB + w_AB X p_BC) = 0
1883 // Integrating to get perr, we get
1884 // perr = p_AS - p_AC + constant = 0
1885 // But p_AC=p_AS by construction, so perr=constant=0.
1886 // The constant is defined by the fact that we want material point
1887 // C of B to be in the same spatial location as point P of B,
1888 // so constant=p_BC-p_BP=0. Writing in the A frame we have:
1889 // perr = p_AS-(p_AB+R_AB*p_BP) = 0 (a constant)
1890 // verr = v_AS - (v_AB + w_AB X R_AB*p_BC)
1891 // aerr = a_AS - (a_AB + b_AB X R_AB*p_BC + w_AB X (w_AB X R_AB*p_BC))
1892 // apply +lambda to S of F, -lambda to C of B.
1893 //
1894 //
1895
calcPositionErrorsVirtual(const State & s,const Array_<Transform,ConstrainedBodyIndex> & allX_AB,const Array_<Real,ConstrainedQIndex> & constrainedQ,Array_<Real> & perr)1896 void calcPositionErrorsVirtual
1897 (const State& s, // Stage::Time
1898 const Array_<Transform,ConstrainedBodyIndex>& allX_AB,
1899 const Array_<Real, ConstrainedQIndex>& constrainedQ,
1900 Array_<Real>& perr) // mp of these
1901 const override
1902 {
1903 assert(allX_AB.size()==2 && constrainedQ.size()==0 && perr.size() == 3);
1904
1905 const std::pair<Vec3,Vec3>& pts = getBodyStations(s);
1906
1907 const Vec3 p_AP = findStationLocation(allX_AB, B1, pts.first);
1908 const Vec3 p_AS = findStationLocation(allX_AB, B2, pts.second);
1909
1910 // See above comments -- this is just the constant of integration; there is a missing (p_AS-p_AC)
1911 // term (always 0) here which is what we differentiate to get the verr equation.
1912 Vec3::updAs(&perr[0]) = p_AS - p_AP;
1913 }
1914
calcPositionDotErrorsVirtual(const State & s,const Array_<SpatialVec,ConstrainedBodyIndex> & allV_AB,const Array_<Real,ConstrainedQIndex> & constrainedQDot,Array_<Real> & pverr)1915 void calcPositionDotErrorsVirtual
1916 (const State& s, // Stage::Position
1917 const Array_<SpatialVec,ConstrainedBodyIndex>& allV_AB,
1918 const Array_<Real, ConstrainedQIndex>& constrainedQDot,
1919 Array_<Real>& pverr) // mp of these
1920 const override
1921 {
1922 assert(allV_AB.size()==2 && constrainedQDot.size()==0 && pverr.size()==3);
1923
1924 // Note that we're not using point1.
1925 const Vec3& point2 = getBodyStations(s).second;
1926
1927 //TODO: should be able to get p info from State
1928 const Transform& X_AB = getBodyTransformFromState(s, B1);
1929 const Vec3 p_AS = findStationLocationFromState(s, B2, point2);
1930 const Vec3 p_BC = ~X_AB*p_AS; // C is a material point of body B
1931
1932 const Vec3 v_AS = findStationVelocity(s, allV_AB, B2,
1933 point2);
1934 const Vec3 v_AC = findStationVelocity(s, allV_AB, B1, p_BC);
1935 Vec3::updAs(&pverr[0]) = v_AS - v_AC;
1936 }
1937
calcPositionDotDotErrorsVirtual(const State & s,const Array_<SpatialVec,ConstrainedBodyIndex> & allA_AB,const Array_<Real,ConstrainedQIndex> & constrainedQDotDot,Array_<Real> & paerr)1938 void calcPositionDotDotErrorsVirtual
1939 (const State& s, // Stage::Velocity
1940 const Array_<SpatialVec,ConstrainedBodyIndex>& allA_AB,
1941 const Array_<Real, ConstrainedQIndex>& constrainedQDotDot,
1942 Array_<Real>& paerr) // mp of these
1943 const override
1944 {
1945 assert(allA_AB.size()==2 && constrainedQDotDot.size()==0 && paerr.size()==3);
1946
1947 // Note that we're not using point1.
1948 const Vec3& point2 = getBodyStations(s).second;
1949
1950 //TODO: should be able to get p and v info from State
1951
1952 const Transform& X_AB = getBodyTransformFromState(s, B1);
1953 const Vec3 p_AS = findStationLocationFromState(s, B2, point2);
1954 const Vec3 p_BC = ~X_AB*p_AS; // C is a material point of body B
1955
1956 const Vec3 a_AS = findStationAcceleration(s, allA_AB, B2,
1957 point2);
1958 const Vec3 a_AC = findStationAcceleration(s, allA_AB, B1, p_BC);
1959 Vec3::updAs(&paerr[0]) = a_AS - a_AC;
1960 }
1961
addInPositionConstraintForcesVirtual(const State & s,const Array_<Real> & multipliers,Array_<SpatialVec,ConstrainedBodyIndex> & bodyForcesInA,Array_<Real,ConstrainedQIndex> & qForces)1962 void addInPositionConstraintForcesVirtual
1963 (const State& s, // Stage::Position
1964 const Array_<Real>& multipliers, // mp of these
1965 Array_<SpatialVec,ConstrainedBodyIndex>& bodyForcesInA,
1966 Array_<Real, ConstrainedQIndex>& qForces)
1967 const override
1968 {
1969 assert(multipliers.size()==3 && bodyForcesInA.size()==2
1970 && qForces.size()==0);
1971
1972 // Note that we're not using point1.
1973 const Vec3& point2 = getBodyStations(s).second;
1974
1975 //TODO: should be able to get p info from State
1976 const Transform& X_AB = getBodyTransformFromState(s,B1);
1977 const Vec3& p_FS = point2;
1978 const Vec3 p_AS = findStationLocationFromState(s, B2, p_FS);
1979 const Vec3 p_BC = ~X_AB * p_AS; // shift to B origin, reexpress in B;
1980 // C is material point of B coincident with S
1981
1982 const Vec3 force_A = Vec3::getAs(&multipliers[0]);
1983
1984 // Multipliers are force to be applied to S on F, but
1985 // apply the -force not to point P of B, but to the point "C" of B
1986 // coincident with S, which won't be exactly the same place
1987 // as P if the position-level constraint isn't met exactly.
1988
1989 addInStationForce(s, B2, p_FS, force_A, bodyForcesInA);
1990 addInStationForce(s, B1, p_BC, -force_A, bodyForcesInA);
1991 }
1992
1993 SimTK_DOWNCAST(BallImpl, ConstraintImpl);
1994 //------------------------------------------------------------------------------
1995 private:
1996 friend class Constraint::Ball;
1997
1998 ConstrainedBodyIndex B1;
1999 ConstrainedBodyIndex B2;
2000
2001 Vec3 defaultPoint1; // on body 1, exp. in B1 frame
2002 Vec3 defaultPoint2; // on body 2, exp. in B2 frame
2003 Real defaultRadius; // used for visualization only
2004
2005 // This Instance-stage variable holds the actual stations on B1 & B2.
2006 mutable DiscreteVariableIndex stationsIx;
2007 };
2008
2009
2010
2011 //==============================================================================
2012 // CONSTANT ORIENTATION IMPL
2013 //==============================================================================
2014 class Constraint::ConstantOrientationImpl : public ConstraintImpl {
2015 public:
ConstantOrientationImpl()2016 ConstantOrientationImpl()
2017 : ConstraintImpl(3,0,0), defaultRB(), defaultRF()
2018 { }
clone()2019 ConstantOrientationImpl* clone() const override { return new ConstantOrientationImpl(*this); }
2020
2021 //TODO: visualization?
2022
2023
2024 // Implementation of virtuals required for holonomic constraints.
2025
2026 // Let B=B1 be the "base" body onto which rotation matrix RB is fixed, and F=B2
2027 // the "follower" body onto which rotation matrix RF is fixed. We would like to
2028 // enforce the constraint that RB==RF when both are expressed in a common basis.
2029 // (Remember that a rotation matrix is just three axis vectors.)
2030 //
2031 // Here the (redundant) assembly constraint is that all the axes are parallel,
2032 // that is RBx==RFx, RBy==RFy, and RBz==RFz. However, aligning two vectors
2033 // takes *two* constraints so that would be a total of 6 constraints, with only
2034 // 3 independent. The independent runtime constraints just enforce
2035 // perpendicularity, but can be satisfied in cases where some of the axes are
2036 // antiparallel so are not suited for the initial assembly. The runtime
2037 // constraints are thus three "constant angle" constraints, where the angle
2038 // is always 90 degrees:
2039 //
2040 // ~RFx * RBy = 0
2041 // ~RFy * RBz = 0
2042 // ~RFz * RBx = 0
2043 //
2044 // We'll work in A. See the "constant angle" constraint for details.
2045 //
2046 // -----------------
2047 // perr = ~RFx * RBy (with all axes expressed in A)
2048 // ~RFy * RBz
2049 // ~RFz * RBx
2050 // -----------------
2051 //
2052 // ---------------------------------
2053 // verr = ~(w_AF-w_AB) * (RFx % RBy)
2054 // = ~(w_AF-w_AB) * (RFy % RBz)
2055 // = ~(w_AF-w_AB) * (RFz % RBx)
2056 // ---------------------------------
2057 //
2058 // -----------------------------------------------------------------------
2059 // aerr = ~(b_AF-b_AB) * (RFx % RBy)
2060 // + ~(w_AF-w_AB) * ((w_AF%RFx) % RBy) - (w_AB%RBy) % RFx)
2061 // ~(b_AF-b_AB) * (RFy % RBz)
2062 // + ~(w_AF-w_AB) * ((w_AF%RFy) % RBz) - (w_AB%RBz) % RFy)
2063 // ~(b_AF-b_AB) * (RFz % RBx)
2064 // + ~(w_AF-w_AB) * ((w_AF%RFz) % RBx) - (w_AB%RBx) % RFz)
2065 // -----------------------------------------------------------------------
2066 //
2067 // Constraint torque can be determined by inspection of verr:
2068 // t_F = lambda_x * (RFx % RBy) (applied to body F)
2069 // + lambda_y * (RFy % RBz)
2070 // + lambda_z * (RFz % RBx)
2071 // t_B = -t_F (applied to body B)
2072 //
2073
2074 // -----------------
2075 // perr = ~RFx * RBy (with all axes expressed in A)
2076 // ~RFy * RBz
2077 // ~RFz * RBx
2078 // -----------------
calcPositionErrorsVirtual(const State & s,const Array_<Transform,ConstrainedBodyIndex> & allX_AB,const Array_<Real,ConstrainedQIndex> & constrainedQ,Array_<Real> & perr)2079 void calcPositionErrorsVirtual
2080 (const State& s, // Stage::Time
2081 const Array_<Transform,ConstrainedBodyIndex>& allX_AB,
2082 const Array_<Real, ConstrainedQIndex>& constrainedQ,
2083 Array_<Real>& perr) // mp of these
2084 const override
2085 {
2086 assert(allX_AB.size()==2 && constrainedQ.size()==0 && perr.size()==3);
2087
2088 const Rotation& R_AB = getBodyRotation(allX_AB, B);
2089 const Rotation& R_AF = getBodyRotation(allX_AB, F);
2090 const Rotation RB = R_AB * defaultRB; // now expressed in A
2091 const Rotation RF = R_AF * defaultRF;
2092
2093 Vec3::updAs(&perr[0]) = Vec3(~RF.x()*RB.y(),
2094 ~RF.y()*RB.z(),
2095 ~RF.z()*RB.x());
2096 }
2097
2098 // ----------------------------------
2099 // verr = ~(w_AF-w_AB) * (RFx % RBy)
2100 // = ~(w_AF-w_AB) * (RFy % RBz)
2101 // = ~(w_AF-w_AB) * (RFz % RBx)
2102 // ----------------------------------
calcPositionDotErrorsVirtual(const State & s,const Array_<SpatialVec,ConstrainedBodyIndex> & allV_AB,const Array_<Real,ConstrainedQIndex> & constrainedQDot,Array_<Real> & pverr)2103 void calcPositionDotErrorsVirtual
2104 (const State& s, // Stage::Position
2105 const Array_<SpatialVec,ConstrainedBodyIndex>& allV_AB,
2106 const Array_<Real, ConstrainedQIndex>& constrainedQDot,
2107 Array_<Real>& pverr) // mp of these
2108 const override
2109 {
2110 assert(allV_AB.size()==2 && constrainedQDot.size()==0 && pverr.size()==3);
2111 //TODO: should be able to get p info from State
2112 const Rotation& R_AB = getBodyRotationFromState(s, B);
2113 const Rotation& R_AF = getBodyRotationFromState(s, F);
2114 const Rotation RB = R_AB * defaultRB; // now expressed in A
2115 const Rotation RF = R_AF * defaultRF;
2116
2117 const Vec3& w_AB = getBodyAngularVelocity(allV_AB, B);
2118 const Vec3& w_AF = getBodyAngularVelocity(allV_AB, F);
2119 const Vec3 w_BF = w_AF-w_AB; // in A
2120
2121 Vec3::updAs(&pverr[0]) = Vec3( ~w_BF * (RF.x() % RB.y()),
2122 ~w_BF * (RF.y() % RB.z()),
2123 ~w_BF * (RF.z() % RB.x()) );
2124 }
2125
2126 //------------------------------------------------------------------------
2127 // aerr = ~(b_AF-b_AB) * (RFx % RBy)
2128 // + ~(w_AF-w_AB) * ((w_AF%RFx) % RBy) - (w_AB%RBy) % RFx)
2129 // ~(b_AF-b_AB) * (RFy % RBz)
2130 // + ~(w_AF-w_AB) * ((w_AF%RFy) % RBz) - (w_AB%RBz) % RFy)
2131 // ~(b_AF-b_AB) * (RFz % RBx)
2132 // + ~(w_AF-w_AB) * ((w_AF%RFz) % RBx) - (w_AB%RBx) % RFz)
2133 //------------------------------------------------------------------------
calcPositionDotDotErrorsVirtual(const State & s,const Array_<SpatialVec,ConstrainedBodyIndex> & allA_AB,const Array_<Real,ConstrainedQIndex> & constrainedQDotDot,Array_<Real> & paerr)2134 void calcPositionDotDotErrorsVirtual
2135 (const State& s, // Stage::Velocity
2136 const Array_<SpatialVec,ConstrainedBodyIndex>& allA_AB,
2137 const Array_<Real, ConstrainedQIndex>& constrainedQDotDot,
2138 Array_<Real>& paerr) // mp of these
2139 const override
2140 {
2141 assert(allA_AB.size()==2 && constrainedQDotDot.size()==0 && paerr.size()==3);
2142 //TODO: should be able to get p and v info from State
2143 const Rotation& R_AB = getBodyRotationFromState(s, B);
2144 const Rotation& R_AF = getBodyRotationFromState(s, F);
2145 const Rotation RB = R_AB * defaultRB; // now expressed in A
2146 const Rotation RF = R_AF * defaultRF;
2147
2148 const Vec3& w_AB = getBodyAngularVelocityFromState(s, B);
2149 const Vec3& w_AF = getBodyAngularVelocityFromState(s, F);
2150 const Vec3 w_BF = w_AF-w_AB; // in A
2151
2152 const Vec3& b_AB = getBodyAngularAcceleration(allA_AB, B);
2153 const Vec3& b_AF = getBodyAngularAcceleration(allA_AB, F);
2154 const Vec3 b_BF = b_AF-b_AB; // in A
2155
2156 Vec3::updAs(&paerr[0]) =
2157 Vec3( dot( b_BF, RF.x() % RB.y() )
2158 + dot( w_BF, (w_AF%RF.x()) % RB.y() - (w_AB%RB.y()) % RF.x()),
2159 dot( b_BF, RF.y() % RB.z() )
2160 + dot( w_BF, (w_AF%RF.y()) % RB.z() - (w_AB%RB.z()) % RF.y()),
2161 dot( b_BF, RF.z() % RB.x() )
2162 + dot( w_BF, (w_AF%RF.z()) % RB.x() - (w_AB%RB.x()) % RF.z()));
2163 }
2164
2165 // t_F = lambda_x * (RFx % RBy) (applied to body F)
2166 // + lambda_y * (RFy % RBz)
2167 // + lambda_z * (RFz % RBx)
2168 // t_B = -t_F (applied to body B)
addInPositionConstraintForcesVirtual(const State & s,const Array_<Real> & multipliers,Array_<SpatialVec,ConstrainedBodyIndex> & bodyForcesInA,Array_<Real,ConstrainedQIndex> & qForces)2169 void addInPositionConstraintForcesVirtual
2170 (const State& s, // Stage::Position
2171 const Array_<Real>& multipliers, // mp of these
2172 Array_<SpatialVec,ConstrainedBodyIndex>& bodyForcesInA,
2173 Array_<Real, ConstrainedQIndex>& qForces)
2174 const override
2175 {
2176 assert(multipliers.size()==3 && bodyForcesInA.size()==2
2177 && qForces.size()==0);
2178 const Vec3& lambda = Vec3::getAs(&multipliers[0]);
2179
2180 //TODO: should be able to get p info from State
2181 const Rotation& R_AB = getBodyRotationFromState(s, B);
2182 const Rotation& R_AF = getBodyRotationFromState(s, F);
2183 const Rotation RB = R_AB * defaultRB; // now expressed in A
2184 const Rotation RF = R_AF * defaultRF;
2185
2186 const Vec3 torque_F_A = lambda[0] * (RF.x() % RB.y())
2187 + lambda[1] * (RF.y() % RB.z())
2188 + lambda[2] * (RF.z() % RB.x());
2189
2190 addInBodyTorque(s, F, torque_F_A, bodyForcesInA);
2191 addInBodyTorque(s, B, -torque_F_A, bodyForcesInA);
2192 }
2193
2194 SimTK_DOWNCAST(ConstantOrientationImpl, ConstraintImpl);
2195 //------------------------------------------------------------------------------
2196 private:
2197 friend class Constraint::ConstantOrientation;
2198
2199 ConstrainedBodyIndex B; // B1 is "base" body
2200 ConstrainedBodyIndex F; // B2 is "follower" body
2201
2202 Rotation defaultRB; // fixed to B, expressed in B frame; RB = R_B_RB
2203 Rotation defaultRF; // fixed to F, expressed in F frame; RF = R_F_RF
2204 };
2205
2206
2207
2208 //==============================================================================
2209 // WELD IMPL
2210 //==============================================================================
2211 class Constraint::WeldImpl : public ConstraintImpl {
getDefaultAxisDisplayLength()2212 static Real getDefaultAxisDisplayLength() {return 1;}
getDefaultFrameColor(int which)2213 static Vec3 getDefaultFrameColor(int which) {
2214 return which==0 ? Blue : Purple;
2215 }
2216 public:
WeldImpl()2217 WeldImpl()
2218 : ConstraintImpl(6,0,0), axisDisplayLength(-1), // means "use default axis length"
2219 frameBColor(-1), frameFColor(-1) // means "use default colors"
2220 { // default Transforms are identity, i.e. body frames
2221 }
clone()2222 WeldImpl* clone() const override { return new WeldImpl(*this); }
2223
2224 // Draw the two frames.
2225 void calcDecorativeGeometryAndAppendVirtual
2226 (const State& s, Stage stage, Array_<DecorativeGeometry>& geom) const override;
2227
setAxisDisplayLength(Real len)2228 void setAxisDisplayLength(Real len) {
2229 // len == 0 means "don't display"
2230 // len < 0 means "use default"
2231 invalidateTopologyCache();
2232 axisDisplayLength = len >= 0 ? len : -1;
2233 }
getAxisDisplayLength()2234 Real getAxisDisplayLength() const {
2235 return axisDisplayLength < 0 ? getDefaultAxisDisplayLength() : axisDisplayLength;
2236 }
2237
setFrameColor(int which,const Vec3 & color)2238 void setFrameColor(int which, const Vec3& color) {
2239 assert(which==0 || which==1);
2240 // color[0] < 0 means "use default color for this frame"
2241 invalidateTopologyCache();
2242 if (which==0) frameBColor = color[0] < 0 ? Vec3(-1) : color;
2243 else frameFColor = color[0] < 0 ? Vec3(-1) : color;
2244 }
getFrameColor(int which)2245 Vec3 getFrameColor(int which) const {
2246 assert(which==0 || which==1);
2247 if (which==0) return frameBColor[0] < 0 ? getDefaultFrameColor(0) : frameBColor;
2248 else return frameFColor[0] < 0 ? getDefaultFrameColor(1) : frameFColor;
2249 }
2250
2251 // Implementation of virtuals required for holonomic constraints.
2252
2253 // For theory, look at the ConstantOrientation (1st 3 equations) and
2254 // Ball (last 3 equations) theory above. Otherwise just lay back and
2255 // enjoy the ride.
2256
calcPositionErrorsVirtual(const State & s,const Array_<Transform,ConstrainedBodyIndex> & allX_AB,const Array_<Real,ConstrainedQIndex> & constrainedQ,Array_<Real> & perr)2257 void calcPositionErrorsVirtual
2258 (const State& s, // Stage::Time
2259 const Array_<Transform,ConstrainedBodyIndex>& allX_AB,
2260 const Array_<Real, ConstrainedQIndex>& constrainedQ,
2261 Array_<Real>& perr) // mp of these
2262 const override
2263 {
2264 assert(allX_AB.size()==2 && constrainedQ.size()==0 && perr.size()==6);
2265
2266 const Rotation& R_AB = getBodyRotation(allX_AB, B);
2267 const Rotation& R_AF = getBodyRotation(allX_AB, F);
2268 const Rotation RB = R_AB * defaultFrameB.R(); // now expressed in A
2269 const Rotation RF = R_AF * defaultFrameF.R();
2270
2271 // Orientation error
2272 Vec3::updAs(&perr[0]) = Vec3(~RF.x()*RB.y(),
2273 ~RF.y()*RB.z(),
2274 ~RF.z()*RB.x());
2275
2276 const Vec3 p_AF1 = findStationLocation(allX_AB, B, defaultFrameB.p());
2277 const Vec3 p_AF2 = findStationLocation(allX_AB, F, defaultFrameF.p());
2278
2279 // position error
2280 Vec3::updAs(&perr[3]) = p_AF2 - p_AF1;
2281 }
2282
calcPositionDotErrorsVirtual(const State & s,const Array_<SpatialVec,ConstrainedBodyIndex> & allV_AB,const Array_<Real,ConstrainedQIndex> & constrainedQDot,Array_<Real> & pverr)2283 void calcPositionDotErrorsVirtual
2284 (const State& s, // Stage::Position
2285 const Array_<SpatialVec,ConstrainedBodyIndex>& allV_AB,
2286 const Array_<Real, ConstrainedQIndex>& constrainedQDot,
2287 Array_<Real>& pverr) // mp of these
2288 const override
2289 {
2290 assert(allV_AB.size()==2 && constrainedQDot.size()==0 && pverr.size()==6);
2291 //TODO: should be able to get p info from State
2292 const Rotation& R_AB = getBodyRotationFromState(s, B);
2293 const Rotation& R_AF = getBodyRotationFromState(s, F);
2294 const Rotation RB = R_AB * defaultFrameB.R(); // now expressed in A
2295 const Rotation RF = R_AF * defaultFrameF.R();
2296
2297 const Vec3& w_AB = getBodyAngularVelocity(allV_AB, B);
2298 const Vec3& w_AF = getBodyAngularVelocity(allV_AB, F);
2299 const Vec3 w_BF = w_AF-w_AB; // in A
2300
2301 // orientation error
2302 Vec3::updAs(&pverr[0]) = Vec3( ~w_BF * (RF.x() % RB.y()),
2303 ~w_BF * (RF.y() % RB.z()),
2304 ~w_BF * (RF.z() % RB.x()) );
2305
2306 //TODO: should be able to get p info from State
2307 const Transform& X_AB = getBodyTransformFromState(s, B);
2308 const Vec3 p_AF2 = findStationLocationFromState(s, F,
2309 defaultFrameF.p());
2310 const Vec3 p_BC = ~X_AB*p_AF2; // C is a material point of body B
2311
2312 const Vec3 v_AF2 = findStationVelocity(s, allV_AB, F,
2313 defaultFrameF.p());
2314 const Vec3 v_AC = findStationVelocity(s, allV_AB, B, p_BC);
2315
2316 // position error
2317 Vec3::updAs(&pverr[3]) = v_AF2 - v_AC;
2318 }
2319
calcPositionDotDotErrorsVirtual(const State & s,const Array_<SpatialVec,ConstrainedBodyIndex> & allA_AB,const Array_<Real,ConstrainedQIndex> & constrainedQDotDot,Array_<Real> & paerr)2320 void calcPositionDotDotErrorsVirtual
2321 (const State& s, // Stage::Velocity
2322 const Array_<SpatialVec,ConstrainedBodyIndex>& allA_AB,
2323 const Array_<Real, ConstrainedQIndex>& constrainedQDotDot,
2324 Array_<Real>& paerr) // mp of these
2325 const override
2326 {
2327 assert(allA_AB.size()==2 && constrainedQDotDot.size()==0 && paerr.size()==6);
2328 //TODO: should be able to get p and v info from State
2329 const Rotation& R_AB = getBodyRotationFromState(s, B);
2330 const Rotation& R_AF = getBodyRotationFromState(s, F);
2331 const Rotation RB = R_AB * defaultFrameB.R(); // now expressed in A
2332 const Rotation RF = R_AF * defaultFrameF.R();
2333
2334 const Vec3& w_AB = getBodyAngularVelocityFromState(s, B);
2335 const Vec3& w_AF = getBodyAngularVelocityFromState(s, F);
2336 const Vec3 w_BF = w_AF-w_AB; // in A
2337
2338 const Vec3& b_AB = getBodyAngularAcceleration(allA_AB, B);
2339 const Vec3& b_AF = getBodyAngularAcceleration(allA_AB, F);
2340 const Vec3 b_BF = b_AF-b_AB; // in A
2341
2342 // orientation error
2343 Vec3::updAs(&paerr[0]) =
2344 Vec3( dot( b_BF, RF.x() % RB.y() )
2345 + dot( w_BF, (w_AF%RF.x()) % RB.y() - (w_AB%RB.y()) % RF.x()),
2346 dot( b_BF, RF.y() % RB.z() )
2347 + dot( w_BF, (w_AF%RF.y()) % RB.z() - (w_AB%RB.z()) % RF.y()),
2348 dot( b_BF, RF.z() % RB.x() )
2349 + dot( w_BF, (w_AF%RF.z()) % RB.x() - (w_AB%RB.x()) % RF.z()));
2350
2351 const Transform& X_AB = getBodyTransformFromState(s, B);
2352 const Vec3 p_AF2 = findStationLocationFromState(s, F,
2353 defaultFrameF.p());
2354 const Vec3 p_BC = ~X_AB*p_AF2; // C is a material point of body B
2355
2356 const Vec3 a_AF2 = findStationAcceleration(s, allA_AB, F,
2357 defaultFrameF.p());
2358 const Vec3 a_AC = findStationAcceleration(s, allA_AB, B, p_BC);
2359
2360 // position error
2361 Vec3::updAs(&paerr[3]) = a_AF2 - a_AC;
2362 }
2363
addInPositionConstraintForcesVirtual(const State & s,const Array_<Real> & multipliers,Array_<SpatialVec,ConstrainedBodyIndex> & bodyForcesInA,Array_<Real,ConstrainedQIndex> & qForces)2364 void addInPositionConstraintForcesVirtual
2365 (const State& s, // Stage::Position
2366 const Array_<Real>& multipliers, // mp of these
2367 Array_<SpatialVec,ConstrainedBodyIndex>& bodyForcesInA,
2368 Array_<Real, ConstrainedQIndex>& qForces)
2369 const override
2370 {
2371 assert(multipliers.size()==6 && bodyForcesInA.size()==2
2372 && qForces.size()==0);
2373
2374 const Vec3& torques = Vec3::getAs(&multipliers[0]);
2375 const Vec3& force_A = Vec3::getAs(&multipliers[3]);
2376
2377 //TODO: should be able to get p info from State
2378 const Rotation& R_AB = getBodyRotationFromState(s, B);
2379 const Rotation& R_AF = getBodyRotationFromState(s, F);
2380 const Rotation RB = R_AB * defaultFrameB.R(); // now expressed in A
2381 const Rotation RF = R_AF * defaultFrameF.R();
2382
2383 const Vec3 torque_F_A = torques[0] * (RF.x() % RB.y())
2384 + torques[1] * (RF.y() % RB.z())
2385 + torques[2] * (RF.z() % RB.x());
2386
2387 addInBodyTorque(s, F, torque_F_A, bodyForcesInA);
2388 addInBodyTorque(s, B, -torque_F_A, bodyForcesInA);
2389
2390 const Transform& X_AB = getBodyTransformFromState(s,B);
2391 const Vec3& p_FF2 = defaultFrameF.p();
2392 const Vec3 p_AF2 = findStationLocationFromState(s, F, p_FF2);
2393 const Vec3 p_BC = ~X_AB * p_AF2;
2394
2395 addInStationForce(s, F, p_FF2, force_A, bodyForcesInA);
2396 addInStationForce(s, B, p_BC, -force_A, bodyForcesInA);
2397 }
2398
2399 SimTK_DOWNCAST(WeldImpl, ConstraintImpl);
2400 //------------------------------------------------------------------------------
2401 private:
2402 friend class Constraint::Weld;
2403
2404 ConstrainedBodyIndex B; // aka "body 1"
2405 ConstrainedBodyIndex F; // aka "body 2"
2406
2407 Transform defaultFrameB; // on body 1, relative to B frame
2408 Transform defaultFrameF; // on body 2, relative to F frame};
2409
2410 // These are for visualization control only.
2411 Real axisDisplayLength; // for all 6 axes; <= 0 means "don't
2412 Vec3 frameBColor; // display"
2413 Vec3 frameFColor;
2414 };
2415
2416
2417
2418 //==============================================================================
2419 // NO SLIP 1D IMPL
2420 //==============================================================================
2421 class Constraint::NoSlip1DImpl : public ConstraintImpl {
2422 public:
NoSlip1DImpl()2423 NoSlip1DImpl()
2424 : ConstraintImpl(0,1,0), defaultContactPoint(0), defaultNoSlipDirection(),
2425 directionLength(1), pointRadius(Real(0.05))
2426 { }
clone()2427 NoSlip1DImpl* clone() const override { return new NoSlip1DImpl(*this); }
2428
2429 // The default contact point and no-slip direction may be overridden by
2430 // setting an instance variable in the state. We allocate the state
2431 // resources here.
2432 void realizeTopologyVirtual(State& state) const override;
2433
2434 // Return the contact point and no-slip direction, both expressed
2435 // in the Case body frame C.
2436 const std::pair<Vec3,UnitVec3>& getContactInfo(const State& state) const;
2437
2438 // Return a writable reference into the Instance-stage state variable
2439 // containing the contact point and no-slip direction, both expressed
2440 // in the Case body frame C. Calling this
2441 // method invalidates the Instance stage and above in the given state.
2442 std::pair<Vec3,UnitVec3>& updContactInfo(State& state) const;
2443
2444 void calcDecorativeGeometryAndAppendVirtual
2445 (const State& s, Stage stage, Array_<DecorativeGeometry>& geom) const override;
2446
setDirectionDisplayLength(Real l)2447 void setDirectionDisplayLength(Real l) {
2448 // l <= 0 means don't display direction line
2449 invalidateTopologyCache();
2450 directionLength = l > 0 ? l : 0;
2451 }
getDirectionDisplayLength()2452 Real getDirectionDisplayLength() const {return directionLength;}
2453
setPointDisplayRadius(Real r)2454 void setPointDisplayRadius(Real r) {
2455 // r <= 0 means don't display point
2456 invalidateTopologyCache();
2457 pointRadius= r > 0 ? r : 0;
2458 }
getPointDisplayRadius()2459 Real getPointDisplayRadius() const {return pointRadius;}
2460
2461 // Implementation of virtuals required for nonholonomic constraints.
2462
2463 // One non-holonomic constraint equation. There is a contact point P and a
2464 // no-slip direction n fixed in a case body C. There are two moving bodies B0
2465 // and B1. The material point P0 of B0 and the material point P1 of B1 which
2466 // are each coincident with the contact point P must have identical velocities
2467 // in C, along the direction n. This can be used to implement simple rolling
2468 // contact between disks, such as occurs in gear trains.
2469 //
2470 // There is no perr equation here since this is a non-holonomic (velocity)
2471 // constraint. In the C frame, the constraint we want is
2472 // verr = ~(v_CP1 - v_CP0) * n_C
2473 // that is, the two contact points have no relative velocity in C along the
2474 // normal. We can calculate this in A instead since the velocities in C of each
2475 // point will differ from their velocities in A by a constant (because they are
2476 // both in the same place in space). So:
2477 // verr = ~(v_AP1 - v_AP0) * n_A
2478 // Differentiating material point velocities in A, we get the acceleration
2479 // error
2480 // aerr = ~(a_AP1 - a_AP0) * n_A + ~(v_AP1 - v_AP0) * (w_AC X n_A)
2481 // = ~(a_AP1 - a_AP0 - w_AC X (v_AP1 - v_AP0)) * n_A
2482 //
calcVelocityErrorsVirtual(const State & s,const Array_<SpatialVec,ConstrainedBodyIndex> & allV_AB,const Array_<Real,ConstrainedUIndex> & constrainedU,Array_<Real> & verr)2483 void calcVelocityErrorsVirtual
2484 (const State& s, // Stage::Position
2485 const Array_<SpatialVec,ConstrainedBodyIndex>& allV_AB,
2486 const Array_<Real, ConstrainedUIndex>& constrainedU,
2487 Array_<Real>& verr) // mv of these
2488 const override
2489 {
2490 // There ought to be at least 2 distinct bodies, up to 3.
2491 assert((allV_AB.size()==2 || allV_AB.size() == 3)
2492 && constrainedU.size()==0 && verr.size()==1);
2493
2494 const std::pair<Vec3,UnitVec3>& info = getContactInfo(s);
2495 const Vec3& P_C = info.first;
2496 const UnitVec3& n_C = info.second;
2497
2498 //TODO: should be able to get p info from State
2499 const Transform& X_AC = getBodyTransformFromState(s, caseBody);
2500 const Transform& X_AB0 = getBodyTransformFromState(s, movingBody0);
2501 const Transform& X_AB1 = getBodyTransformFromState(s, movingBody1);
2502 const Vec3 p_AP = X_AC * P_C; // P's location in A
2503 const Vec3 p_P0 = ~X_AB0 * p_AP; // P0's station in B0
2504 const Vec3 p_P1 = ~X_AB1 * p_AP; // P1's station in B1
2505 const UnitVec3 n_A = X_AC.R() * n_C;
2506
2507 const Vec3 v_AP0 = findStationVelocity(s, allV_AB, movingBody0, p_P0);
2508 const Vec3 v_AP1 = findStationVelocity(s, allV_AB, movingBody1, p_P1);
2509
2510 // Calculate this scalar using A-frame vectors.
2511 verr[0] = ~(v_AP1-v_AP0) * n_A;
2512 }
2513
calcVelocityDotErrorsVirtual(const State & s,const Array_<SpatialVec,ConstrainedBodyIndex> & allA_AB,const Array_<Real,ConstrainedUIndex> & constrainedUDot,Array_<Real> & vaerr)2514 void calcVelocityDotErrorsVirtual
2515 (const State& s, // Stage::Velocity
2516 const Array_<SpatialVec,ConstrainedBodyIndex>& allA_AB,
2517 const Array_<Real, ConstrainedUIndex>& constrainedUDot,
2518 Array_<Real>& vaerr) // mv of these
2519 const override
2520 {
2521 assert((allA_AB.size()==2 || allA_AB.size() == 3)
2522 && constrainedUDot.size()==0 && vaerr.size()==1);
2523
2524 const std::pair<Vec3,UnitVec3>& info = getContactInfo(s);
2525 const Vec3& P_C = info.first;
2526 const UnitVec3& n_C = info.second;
2527
2528 //TODO: should be able to get p and v info from State
2529 const Transform& X_AC = getBodyTransformFromState(s, caseBody);
2530 const Transform& X_AB0 = getBodyTransformFromState(s, movingBody0);
2531 const Transform& X_AB1 = getBodyTransformFromState(s, movingBody1);
2532 const Vec3 p_AP = X_AC * P_C; // P's location in A
2533 const Vec3 p_P0 = ~X_AB0 * p_AP; // P0's station in B0
2534 const Vec3 p_P1 = ~X_AB1 * p_AP; // P1's station in B1
2535 const UnitVec3 n_A = X_AC.R() * n_C;
2536
2537 const Vec3 v_AP0 = findStationVelocityFromState(s, movingBody0, p_P0);
2538 const Vec3 v_AP1 = findStationVelocityFromState(s, movingBody1, p_P1);
2539 const Vec3& w_AC = getBodyAngularVelocityFromState(s, caseBody);
2540
2541 const Vec3 a_AP0 = findStationAcceleration(s, allA_AB, movingBody0, p_P0);
2542 const Vec3 a_AP1 = findStationAcceleration(s, allA_AB, movingBody1, p_P1);
2543
2544 // Calculate this scalar using A-frame vectors.
2545 vaerr[0] = ~(a_AP1-a_AP0 - w_AC % (v_AP1-v_AP0)) * n_A;
2546 }
2547
2548 // apply f=lambda*n to contact point P1 of B1,
2549 // -f to contact point P2 of B2
addInVelocityConstraintForcesVirtual(const State & s,const Array_<Real> & multipliers,Array_<SpatialVec,ConstrainedBodyIndex> & bodyForcesInA,Array_<Real,ConstrainedUIndex> & mobilityForces)2550 void addInVelocityConstraintForcesVirtual
2551 (const State& s, // Stage::Velocity
2552 const Array_<Real>& multipliers, // mv of these
2553 Array_<SpatialVec,ConstrainedBodyIndex>& bodyForcesInA,
2554 Array_<Real, ConstrainedUIndex>& mobilityForces) const override
2555 {
2556 assert(multipliers.size()==1 && mobilityForces.size()==0
2557 && (bodyForcesInA.size()==2 || bodyForcesInA.size()==3));
2558
2559 const std::pair<Vec3,UnitVec3>& info = getContactInfo(s);
2560 const Vec3& P_C = info.first;
2561 const UnitVec3& n_C = info.second;
2562
2563 const Real lambda = multipliers[0];
2564
2565 //TODO: should be able to get p info from State
2566 const Transform& X_AC = getBodyTransformFromState(s, caseBody);
2567 const Transform& X_AB0 = getBodyTransformFromState(s, movingBody0);
2568 const Transform& X_AB1 = getBodyTransformFromState(s, movingBody1);
2569 const Vec3 p_AP = X_AC * P_C; // P's location in A
2570 const Vec3 p_P0 = ~X_AB0 * p_AP; // P0's station in B0
2571 const Vec3 p_P1 = ~X_AB1 * p_AP; // P1's station in B1
2572
2573 const Vec3 force_A = X_AC.R()*(lambda*n_C);
2574
2575 addInStationForce(s, movingBody1, p_P1, force_A, bodyForcesInA);
2576 addInStationForce(s, movingBody0, p_P0, -force_A, bodyForcesInA);
2577 }
2578
2579 SimTK_DOWNCAST(NoSlip1DImpl, ConstraintImpl);
2580 //------------------------------------------------------------------------------
2581 private:
2582 friend class Constraint::NoSlip1D;
2583
2584 ConstrainedBodyIndex caseBody; // C
2585 ConstrainedBodyIndex movingBody0; // B0
2586 ConstrainedBodyIndex movingBody1; // B1
2587
2588 Vec3 defaultContactPoint; // on body C, exp. in C frame
2589 UnitVec3 defaultNoSlipDirection; // on body C, exp. in C frame
2590
2591 // These are just for visualization
2592 Real directionLength;
2593 Real pointRadius;
2594
2595 // This Instance-stage variable holds the actual contact point and no-slip
2596 // direction on the Case body.
2597 mutable DiscreteVariableIndex contactInfoIx;
2598 };
2599
2600
2601 //==============================================================================
2602 // CONSTANT COORDINATE IMPL
2603 //==============================================================================
2604 class Constraint::ConstantCoordinateImpl : public ConstraintImpl {
2605 public:
ConstantCoordinateImpl()2606 ConstantCoordinateImpl()
2607 : ConstraintImpl(1,0,0), theMobilizer(), whichCoordinate(),
2608 defaultPosition(NaN){}
2609
clone()2610 ConstantCoordinateImpl* clone() const override
2611 { return new ConstantCoordinateImpl(*this); }
2612
2613 // Allocate a state variable to hold the desired position.
2614 void realizeTopologyVirtual(State& state) const override;
2615 // Obtain the currently-set desired position from the state.
2616 Real getPosition(const State& state) const;
2617 // Get a reference to the desired position in the state; this
2618 // invalidates Position stage in the supplied state.
2619 Real& updPosition(State& state) const;
2620
2621 // Implementation of virtuals required for holonomic constraints.
2622
2623 // One holonomic constraint equation.
2624 // perr = q - p
2625 // verr = qdot
2626 // aerr = qdotdot
2627 //
calcPositionErrorsVirtual(const State & s,const Array_<Transform,ConstrainedBodyIndex> & X_AB,const Array_<Real,ConstrainedQIndex> & constrainedQ,Array_<Real> & perr)2628 void calcPositionErrorsVirtual
2629 (const State& s, // Stage::Time
2630 const Array_<Transform,ConstrainedBodyIndex>& X_AB,
2631 const Array_<Real, ConstrainedQIndex>& constrainedQ,
2632 Array_<Real>& perr) // mp of these
2633 const override
2634 {
2635 // All the q's for a given constrained mobilizer are considered
2636 // constrainedQ's, but we're just going to grab one of them.
2637 assert(X_AB.size()==0 && constrainedQ.size()>=1 && perr.size()==1);
2638 const Real q = getOneQ(s, constrainedQ, theMobilizer, whichCoordinate);
2639 perr[0] = q - getPosition(s);
2640 }
2641
calcPositionDotErrorsVirtual(const State & s,const Array_<SpatialVec,ConstrainedBodyIndex> & V_AB,const Array_<Real,ConstrainedQIndex> & constrainedQDot,Array_<Real> & pverr)2642 void calcPositionDotErrorsVirtual
2643 (const State& s, // Stage::Position
2644 const Array_<SpatialVec,ConstrainedBodyIndex>& V_AB,
2645 const Array_<Real, ConstrainedQIndex>& constrainedQDot,
2646 Array_<Real>& pverr) // mp of these
2647 const override
2648 {
2649 // All the q's for a given constrained mobilizer are considered
2650 // constrainedQDots's, but we're just going to grab one of them.
2651 assert(V_AB.size()==0 && constrainedQDot.size()>=1 && pverr.size()==1);
2652 const Real qdot = getOneQDot(s, constrainedQDot,
2653 theMobilizer, whichCoordinate);
2654 pverr[0] = qdot;
2655 }
2656
2657 // Pull t, X_AB, q, V_AB, qdot from state.
calcPositionDotDotErrorsVirtual(const State & s,const Array_<SpatialVec,ConstrainedBodyIndex> & A_AB,const Array_<Real,ConstrainedQIndex> & constrainedQDotDot,Array_<Real> & paerr)2658 void calcPositionDotDotErrorsVirtual
2659 (const State& s, // Stage::Velocity
2660 const Array_<SpatialVec,ConstrainedBodyIndex>& A_AB,
2661 const Array_<Real, ConstrainedQIndex>& constrainedQDotDot,
2662 Array_<Real>& paerr) // mp of these
2663 const override
2664 {
2665 // All the q's for a given constrained mobilizer are considered
2666 // constrainedQDotDots's, but we're just going to grab one of them.
2667 assert(A_AB.size()==0 && constrainedQDotDot.size()>=1 && paerr.size()==1);
2668 const Real qdotdot = getOneQDotDot(s, constrainedQDotDot,
2669 theMobilizer, whichCoordinate);
2670 paerr[0] = qdotdot;
2671 }
2672
2673 // apply generalized force lambda to the mobility
addInPositionConstraintForcesVirtual(const State & s,const Array_<Real> & multipliers,Array_<SpatialVec,ConstrainedBodyIndex> & bodyForcesInA,Array_<Real,ConstrainedQIndex> & qForces)2674 void addInPositionConstraintForcesVirtual
2675 (const State& s, // Stage::Position
2676 const Array_<Real>& multipliers, // mp of these
2677 Array_<SpatialVec,ConstrainedBodyIndex>& bodyForcesInA,
2678 Array_<Real, ConstrainedQIndex>& qForces)
2679 const override
2680 {
2681 // All the coordinates for a given constrained mobilizer have slots in
2682 // qForces, but we're just going to update one of them.
2683 assert(multipliers.size()==1 && bodyForcesInA.size()==0
2684 && qForces.size()>=1);
2685
2686 const Real lambda = multipliers[0];
2687 addInOneQForce(s, theMobilizer, whichCoordinate, lambda, qForces);
2688 }
2689
2690 SimTK_DOWNCAST(ConstantCoordinateImpl, ConstraintImpl);
2691 //------------------------------------------------------------------------------
2692 private:
2693 friend class Constraint::ConstantCoordinate;
2694
2695 // TOPOLOGY STATE
2696 ConstrainedMobilizerIndex theMobilizer;
2697 MobilizerQIndex whichCoordinate;
2698 Real defaultPosition;
2699
2700 // TOPOLOGY CACHE
2701 DiscreteVariableIndex positionIx;
2702 };
2703
2704
2705
2706
2707 //==============================================================================
2708 // CONSTANT SPEED IMPL
2709 //==============================================================================
2710 class Constraint::ConstantSpeedImpl : public ConstraintImpl {
2711 public:
ConstantSpeedImpl()2712 ConstantSpeedImpl()
2713 : ConstraintImpl(0,1,0), theMobilizer(), whichMobility(),
2714 defaultSpeed(NaN){}
2715
clone()2716 ConstantSpeedImpl* clone() const override
2717 { return new ConstantSpeedImpl(*this); }
2718
2719 // Allocate a state variable to hold the desired speed.
2720 void realizeTopologyVirtual(State& state) const override;
2721 // Obtain the currently-set desired speed from the state.
2722 Real getSpeed(const State& state) const;
2723 // Get a reference to the desired speed in the state; this
2724 // invalidates Velocity stage in the supplied state.
2725 Real& updSpeed(State& state) const;
2726
2727 // Implementation of virtuals required for nonholonomic constraints.
2728
2729 // One non-holonomic (well, velocity-level) constraint equation.
2730 // verr = u - s
2731 // aerr = udot
2732 //
calcVelocityErrorsVirtual(const State & s,const Array_<SpatialVec,ConstrainedBodyIndex> & allV_AB,const Array_<Real,ConstrainedUIndex> & constrainedU,Array_<Real> & verr)2733 void calcVelocityErrorsVirtual
2734 (const State& s, // Stage::Position
2735 const Array_<SpatialVec,ConstrainedBodyIndex>& allV_AB,
2736 const Array_<Real, ConstrainedUIndex>& constrainedU,
2737 Array_<Real>& verr) // mv of these
2738 const override
2739 {
2740 // All the u's for a given constrained mobilizer are considered
2741 // constrainedU's, but we're just going to grab one of them.
2742 assert(allV_AB.size()==0 && constrainedU.size()>=1 && verr.size()==1);
2743 const Real u = getOneU(s, constrainedU, theMobilizer, whichMobility);
2744 verr[0] = u - getSpeed(s);
2745 }
2746
calcVelocityDotErrorsVirtual(const State & s,const Array_<SpatialVec,ConstrainedBodyIndex> & allA_AB,const Array_<Real,ConstrainedUIndex> & constrainedUDot,Array_<Real> & vaerr)2747 void calcVelocityDotErrorsVirtual
2748 (const State& s, // Stage::Velocity
2749 const Array_<SpatialVec,ConstrainedBodyIndex>& allA_AB,
2750 const Array_<Real, ConstrainedUIndex>& constrainedUDot,
2751 Array_<Real>& vaerr) // mv of these
2752 const override
2753 {
2754 // All the u's for a given constrained mobilizer are considered
2755 // constrainedUDots's, but we're just going to grab one of them.
2756 assert(allA_AB.size()==0 && constrainedUDot.size()>=1 && vaerr.size()==1);
2757 const Real udot = getOneUDot(s, constrainedUDot,
2758 theMobilizer, whichMobility);
2759 vaerr[0] = udot;
2760 }
2761
2762 // apply generalized force lambda to the mobility
addInVelocityConstraintForcesVirtual(const State & s,const Array_<Real> & multipliers,Array_<SpatialVec,ConstrainedBodyIndex> & bodyForcesInA,Array_<Real,ConstrainedUIndex> & mobilityForces)2763 void addInVelocityConstraintForcesVirtual
2764 (const State& s, // Stage::Velocity
2765 const Array_<Real>& multipliers, // mv of these
2766 Array_<SpatialVec,ConstrainedBodyIndex>& bodyForcesInA,
2767 Array_<Real, ConstrainedUIndex>& mobilityForces) const override
2768 {
2769 // All the mobilities for a given constrained mobilizer have slots in
2770 // mobilizedForces, but we're just going to update one of them.
2771 assert(multipliers.size()==1 && bodyForcesInA.size()==0
2772 && mobilityForces.size()>=1);
2773
2774 const Real lambda = multipliers[0];
2775 addInOneMobilityForce(s, theMobilizer, whichMobility, lambda,
2776 mobilityForces);
2777 }
2778
2779 SimTK_DOWNCAST(ConstantSpeedImpl, ConstraintImpl);
2780 //------------------------------------------------------------------------------
2781 private:
2782 friend class Constraint::ConstantSpeed;
2783
2784 // TOPOLOGY STATE
2785 ConstrainedMobilizerIndex theMobilizer;
2786 MobilizerUIndex whichMobility;
2787 Real defaultSpeed;
2788
2789 // TOPOLOGY CACHE
2790 DiscreteVariableIndex speedIx;
2791 };
2792
2793
2794
2795 //==============================================================================
2796 // CONSTANT ACCELERATION IMPL
2797 //==============================================================================
2798 class Constraint::ConstantAccelerationImpl : public ConstraintImpl {
2799 public:
ConstantAccelerationImpl()2800 ConstantAccelerationImpl()
2801 : ConstraintImpl(0,0,1), theMobilizer(), whichMobility(),
2802 defaultAcceleration(NaN) {}
2803
clone()2804 ConstantAccelerationImpl* clone() const override
2805 { return new ConstantAccelerationImpl(*this); }
2806
2807 // Allocate a state variable to hold the desired acceleration.
2808 void realizeTopologyVirtual(State& state) const override;
2809 // Obtain the currently-set desired acceleration from the state.
2810 Real getAcceleration(const State& state) const;
2811 // Get a reference to the desired acceleration in the state; this
2812 // invalidates Acceleration stage in the supplied state.
2813 Real& updAcceleration(State& state) const;
2814
2815 // Implementation of virtuals required for acceleration-only constraints.
2816
2817 // One acceleration-only constraint equation.
2818 // aerr = udot - a
2819 //
calcAccelerationErrorsVirtual(const State & s,const Array_<SpatialVec,ConstrainedBodyIndex> & allA_AB,const Array_<Real,ConstrainedUIndex> & constrainedUDot,Array_<Real> & aerr)2820 void calcAccelerationErrorsVirtual
2821 (const State& s, // Stage::Velocity
2822 const Array_<SpatialVec,ConstrainedBodyIndex>& allA_AB,
2823 const Array_<Real, ConstrainedUIndex>& constrainedUDot,
2824 Array_<Real>& aerr) const override // ma of these
2825 {
2826 // All the u's for a given constrained mobilizer are considered
2827 // constrainedUDots's, but we're just going to grab one of them.
2828 assert(allA_AB.size()==0 && constrainedUDot.size()>=1 && aerr.size()==1);
2829
2830 const Real udot = getOneUDot(s, constrainedUDot,
2831 theMobilizer, whichMobility);
2832 aerr[0] = udot - getAcceleration(s);
2833 }
2834
2835 // apply generalized force lambda to the mobility
addInAccelerationConstraintForcesVirtual(const State & state,const Array_<Real> & multipliers,Array_<SpatialVec,ConstrainedBodyIndex> & bodyForcesInA,Array_<Real,ConstrainedUIndex> & mobilityForces)2836 void addInAccelerationConstraintForcesVirtual
2837 (const State& state, // Stage::Velocity
2838 const Array_<Real>& multipliers, // ma of these
2839 Array_<SpatialVec,ConstrainedBodyIndex>& bodyForcesInA,
2840 Array_<Real, ConstrainedUIndex>& mobilityForces) const override
2841 {
2842 // All the mobilities for a given constrained mobilizer have slots in
2843 // mobilizedForces, but we're just going to update one of them.
2844 assert(multipliers.size()==1 && bodyForcesInA.size()==0
2845 && mobilityForces.size()>=1);
2846
2847 const Real lambda = multipliers[0];
2848 addInOneMobilityForce(state, theMobilizer, whichMobility, lambda,
2849 mobilityForces);
2850 }
2851
2852 SimTK_DOWNCAST(ConstantAccelerationImpl, ConstraintImpl);
2853 //------------------------------------------------------------------------------
2854 private:
2855 friend class Constraint::ConstantAcceleration;
2856
2857 // TOPOLOGY STATE
2858 ConstrainedMobilizerIndex theMobilizer;
2859 MobilizerUIndex whichMobility;
2860 Real defaultAcceleration;
2861
2862 // TOPOLOGY CACHE
2863 DiscreteVariableIndex accelIx;
2864 };
2865
2866
2867
2868 //==============================================================================
2869 // CUSTOM IMPLEMENTATION IMPL
2870 //==============================================================================
2871 // This class exists primarily to allow the Custom::Implementation class to keep
2872 // a pointer to its handle class's CustomImpl class which is derived from
2873 // ConstraintImpl which has all the goodies that are needed for defining a
2874 // Constraint.
2875 //
2876 // At first this class is the owner of the CustomImpl. Then when this is put in
2877 // a Custom handle, that handle takes over ownership of the CustomImpl and the
2878 // CustomImpl takes over ownership of this ImplementationImpl object.
2879 class Constraint::Custom::ImplementationImpl
2880 : public PIMPLImplementation<Implementation, ImplementationImpl>
2881 {
2882 public:
2883 // no default constructor
ImplementationImpl(CustomImpl * customImpl)2884 explicit ImplementationImpl(CustomImpl* customImpl) : isOwner(true), builtInImpl(customImpl) { }
2885 inline ~ImplementationImpl(); // see below -- have to wait for CustomImpl's definition
2886
2887 // Copying one of these just gives us a new one with a NULL CustomImpl pointer.
ImplementationImpl(const ImplementationImpl & src)2888 ImplementationImpl(const ImplementationImpl& src) : isOwner(false), builtInImpl(0) { }
2889
clone()2890 ImplementationImpl* clone() const {return new ImplementationImpl(*this);}
2891
isOwnerOfCustomImpl()2892 bool isOwnerOfCustomImpl() const {return builtInImpl && isOwner;}
removeOwnershipOfCustomImpl()2893 CustomImpl* removeOwnershipOfCustomImpl() {
2894 assert(isOwnerOfCustomImpl());
2895 isOwner=false;
2896 return builtInImpl;
2897 }
2898
setReferenceToCustomImpl(CustomImpl * cimpl)2899 void setReferenceToCustomImpl(CustomImpl* cimpl) {
2900 assert(!builtInImpl); // you can only do this once
2901 isOwner=false;
2902 builtInImpl = cimpl;
2903 }
2904
hasCustomImpl()2905 bool hasCustomImpl() const {return builtInImpl != 0;}
2906
getCustomImpl()2907 const CustomImpl& getCustomImpl() const {
2908 assert(builtInImpl);
2909 return *builtInImpl;
2910 }
updCustomImpl()2911 CustomImpl& updCustomImpl() {
2912 assert(builtInImpl);
2913 return *builtInImpl;
2914 }
2915
2916 //------------------------------------------------------------------------------
2917 private:
2918 bool isOwner;
2919 CustomImpl* builtInImpl; // just a reference; not owned
2920
2921 // suppress assignment
2922 ImplementationImpl& operator=(const ImplementationImpl&);
2923 };
2924
2925
2926
2927 //==============================================================================
2928 // CUSTOM IMPL
2929 //==============================================================================
2930 class Constraint::CustomImpl : public ConstraintImpl {
2931 public:
CustomImpl()2932 CustomImpl() : implementation(0) { }
CustomImpl(int mp,int mv,int ma)2933 CustomImpl(int mp, int mv, int ma) : ConstraintImpl(mp,mv,ma), implementation(0) { }
2934
2935 void takeOwnershipOfImplementation(Custom::Implementation* userImpl);
2936
CustomImpl(Custom::Implementation * userImpl)2937 explicit CustomImpl(Custom::Implementation* userImpl) : implementation(0) {
2938 assert(userImpl);
2939 implementation = userImpl;
2940 implementation->updImpl().setReferenceToCustomImpl(this);
2941 }
2942
2943 // Copy constructor
CustomImpl(const CustomImpl & src)2944 CustomImpl(const CustomImpl& src) : implementation(0) {
2945 if (src.implementation) {
2946 implementation = src.implementation->clone();
2947 implementation->updImpl().setReferenceToCustomImpl(this);
2948 }
2949 }
2950
~CustomImpl()2951 ~CustomImpl() {
2952 delete implementation;
2953 }
2954
clone()2955 CustomImpl* clone() const override { return new CustomImpl(*this); }
2956
getImplementation()2957 const Custom::Implementation& getImplementation() const {
2958 assert(implementation);
2959 return *implementation;
2960 }
2961
updImplementation()2962 Custom::Implementation& updImplementation() {
2963 assert(implementation);
2964 return *implementation;
2965 }
2966
2967 // Forward all the virtuals to the Custom::Implementation virtuals.
realizeTopologyVirtual(State & s)2968 void realizeTopologyVirtual(State& s) const override {getImplementation().realizeTopology(s);}
realizeModelVirtual(State & s)2969 void realizeModelVirtual (State& s) const override {getImplementation().realizeModel(s);}
realizeInstanceVirtual(const State & s)2970 void realizeInstanceVirtual(const State& s) const override {getImplementation().realizeInstance(s);}
realizeTimeVirtual(const State & s)2971 void realizeTimeVirtual (const State& s) const override {getImplementation().realizeTime(s);}
realizePositionVirtual(const State & s)2972 void realizePositionVirtual(const State& s) const override {getImplementation().realizePosition(s);}
realizeVelocityVirtual(const State & s)2973 void realizeVelocityVirtual(const State& s) const override {getImplementation().realizeVelocity(s);}
realizeDynamicsVirtual(const State & s)2974 void realizeDynamicsVirtual(const State& s) const override {getImplementation().realizeDynamics(s);}
realizeAccelerationVirtual(const State & s)2975 void realizeAccelerationVirtual(const State& s) const override {getImplementation().realizeAcceleration(s);}
realizeReportVirtual(const State & s)2976 void realizeReportVirtual (const State& s) const override {getImplementation().realizeReport(s);}
2977
calcPositionErrorsVirtual(const State & state,const Array_<Transform,ConstrainedBodyIndex> & X_AB,const Array_<Real,ConstrainedQIndex> & constrainedQ,Array_<Real> & perr)2978 void calcPositionErrorsVirtual
2979 (const State& state,
2980 const Array_<Transform,ConstrainedBodyIndex>& X_AB,
2981 const Array_<Real, ConstrainedQIndex>& constrainedQ,
2982 Array_<Real>& perr) const override
2983 { getImplementation().calcPositionErrors(state,X_AB,constrainedQ,perr); }
2984
calcPositionDotErrorsVirtual(const State & state,const Array_<SpatialVec,ConstrainedBodyIndex> & V_AB,const Array_<Real,ConstrainedQIndex> & constrainedQDot,Array_<Real> & pverr)2985 void calcPositionDotErrorsVirtual
2986 (const State& state,
2987 const Array_<SpatialVec,ConstrainedBodyIndex>& V_AB,
2988 const Array_<Real, ConstrainedQIndex>& constrainedQDot,
2989 Array_<Real>& pverr) const override
2990 { getImplementation().calcPositionDotErrors
2991 (state,V_AB,constrainedQDot,pverr); }
2992
calcPositionDotDotErrorsVirtual(const State & state,const Array_<SpatialVec,ConstrainedBodyIndex> & A_AB,const Array_<Real,ConstrainedQIndex> & constrainedQDotDot,Array_<Real> & paerr)2993 void calcPositionDotDotErrorsVirtual
2994 (const State& state,
2995 const Array_<SpatialVec,ConstrainedBodyIndex>& A_AB,
2996 const Array_<Real, ConstrainedQIndex>& constrainedQDotDot,
2997 Array_<Real>& paerr) const override
2998 { getImplementation().calcPositionDotDotErrors
2999 (state,A_AB,constrainedQDotDot,paerr); }
3000
addInPositionConstraintForcesVirtual(const State & s,const Array_<Real> & multipliers,Array_<SpatialVec,ConstrainedBodyIndex> & bodyForcesInA,Array_<Real,ConstrainedQIndex> & qForces)3001 void addInPositionConstraintForcesVirtual
3002 (const State& s,
3003 const Array_<Real>& multipliers,
3004 Array_<SpatialVec,ConstrainedBodyIndex>& bodyForcesInA,
3005 Array_<Real,ConstrainedQIndex>& qForces) const override
3006 { getImplementation().addInPositionConstraintForces
3007 (s,multipliers,bodyForcesInA,qForces); }
3008
calcVelocityErrorsVirtual(const State & state,const Array_<SpatialVec,ConstrainedBodyIndex> & V_AB,const Array_<Real,ConstrainedUIndex> & constrainedU,Array_<Real> & verr)3009 void calcVelocityErrorsVirtual
3010 (const State& state,
3011 const Array_<SpatialVec,ConstrainedBodyIndex>& V_AB,
3012 const Array_<Real, ConstrainedUIndex>& constrainedU,
3013 Array_<Real>& verr) const override
3014 { getImplementation().calcVelocityErrors(state,V_AB,constrainedU,verr); }
3015
calcVelocityDotErrorsVirtual(const State & state,const Array_<SpatialVec,ConstrainedBodyIndex> & A_AB,const Array_<Real,ConstrainedUIndex> & constrainedUDot,Array_<Real> & vaerr)3016 void calcVelocityDotErrorsVirtual
3017 (const State& state,
3018 const Array_<SpatialVec,ConstrainedBodyIndex>& A_AB,
3019 const Array_<Real, ConstrainedUIndex>& constrainedUDot,
3020 Array_<Real>& vaerr) const override
3021 { getImplementation().calcVelocityDotErrors
3022 (state,A_AB,constrainedUDot,vaerr); }
3023
addInVelocityConstraintForcesVirtual(const State & s,const Array_<Real> & multipliers,Array_<SpatialVec,ConstrainedBodyIndex> & bodyForcesInA,Array_<Real,ConstrainedUIndex> & mobilityForces)3024 void addInVelocityConstraintForcesVirtual
3025 (const State& s,
3026 const Array_<Real>& multipliers,
3027 Array_<SpatialVec,ConstrainedBodyIndex>& bodyForcesInA,
3028 Array_<Real,ConstrainedUIndex>& mobilityForces) const override
3029 { getImplementation().addInVelocityConstraintForces
3030 (s,multipliers,bodyForcesInA,mobilityForces); }
3031
calcAccelerationErrorsVirtual(const State & state,const Array_<SpatialVec,ConstrainedBodyIndex> & A_AB,const Array_<Real,ConstrainedUIndex> & constrainedUDot,Array_<Real> & aerr)3032 void calcAccelerationErrorsVirtual
3033 (const State& state,
3034 const Array_<SpatialVec,ConstrainedBodyIndex>& A_AB,
3035 const Array_<Real, ConstrainedUIndex>& constrainedUDot,
3036 Array_<Real>& aerr) const override
3037 { getImplementation().calcAccelerationErrors
3038 (state,A_AB,constrainedUDot,aerr); }
3039
addInAccelerationConstraintForcesVirtual(const State & s,const Array_<Real> & multipliers,Array_<SpatialVec,ConstrainedBodyIndex> & bodyForcesInA,Array_<Real,ConstrainedUIndex> & mobilityForces)3040 void addInAccelerationConstraintForcesVirtual
3041 (const State& s,
3042 const Array_<Real>& multipliers,
3043 Array_<SpatialVec,ConstrainedBodyIndex>& bodyForcesInA,
3044 Array_<Real,ConstrainedUIndex>& mobilityForces) const override
3045 { getImplementation().addInAccelerationConstraintForces
3046 (s,multipliers,bodyForcesInA,mobilityForces); }
3047
calcDecorativeGeometryAndAppendVirtual(const State & s,Stage stage,Array_<DecorativeGeometry> & geom)3048 void calcDecorativeGeometryAndAppendVirtual
3049 (const State& s, Stage stage, Array_<DecorativeGeometry>& geom) const override
3050 {getImplementation().calcDecorativeGeometryAndAppend(s,stage,geom);}
3051
3052 SimTK_DOWNCAST(CustomImpl, ConstraintImpl);
3053 //------------------------------------------------------------------------------
3054 private:
3055 friend class Constraint::Custom;
3056
3057 Custom::Implementation* implementation;
3058
3059 CustomImpl& operator=(const CustomImpl&); // suppress assignment
3060 };
3061
3062 // Need definition for CustomImpl here in case we have to delete it.
~ImplementationImpl()3063 inline Constraint::Custom::ImplementationImpl::~ImplementationImpl() {
3064 if (isOwner)
3065 delete builtInImpl;
3066 builtInImpl=0;
3067 }
3068
3069
3070
3071 //==============================================================================
3072 // COORDINATE COUPLER IMPL
3073 //==============================================================================
3074 class Constraint::CoordinateCouplerImpl
3075 : public Constraint::Custom::Implementation {
3076 public:
3077 CoordinateCouplerImpl(SimbodyMatterSubsystem& matter,
3078 const Function* function,
3079 const Array_<MobilizedBodyIndex>& coordBody,
3080 const Array_<MobilizerQIndex>& coordIndex);
3081
~CoordinateCouplerImpl()3082 ~CoordinateCouplerImpl() {
3083 if (--referenceCount[0] == 0) {
3084 delete function;
3085 delete[] referenceCount;
3086 }
3087 }
3088
clone()3089 Implementation* clone() const override {
3090 referenceCount[0]++;
3091 CoordinateCouplerImpl* newCoupler = new CoordinateCouplerImpl(*this);
3092 return newCoupler;
3093 }
3094
3095 void calcPositionErrors
3096 (const State& state,
3097 const Array_<Transform,ConstrainedBodyIndex>& X_AB,
3098 const Array_<Real, ConstrainedQIndex>& constrainedQ,
3099 Array_<Real>& perr) const override;
3100
3101 void calcPositionDotErrors
3102 (const State& state,
3103 const Array_<SpatialVec,ConstrainedBodyIndex>& V_AB,
3104 const Array_<Real, ConstrainedQIndex>& constrainedQDot,
3105 Array_<Real>& pverr) const override;
3106
3107 void calcPositionDotDotErrors
3108 (const State& state,
3109 const Array_<SpatialVec,ConstrainedBodyIndex>& A_AB,
3110 const Array_<Real, ConstrainedQIndex>& constrainedQDotDot,
3111 Array_<Real>& paerr) const override;
3112
3113 void addInPositionConstraintForces
3114 (const State& state,
3115 const Array_<Real>& multipliers,
3116 Array_<SpatialVec,ConstrainedBodyIndex>& bodyForces,
3117 Array_<Real,ConstrainedQIndex>& qForces) const override;
3118
3119 //------------------------------------------------------------------------------
3120 private:
3121 friend class Constraint::CoordinateCoupler;
3122
3123 // TOPOLOGY STATE
3124 const Function* function;
3125 Array_<ConstrainedMobilizerIndex> coordBodies;
3126 Array_<MobilizerQIndex> coordIndices;
3127
3128 // TOPOLOGY CACHE
3129 // None.
3130
3131 // A reusable temporary variable allocated to the correct size
3132 // to hold all the Function arguments.
3133 mutable Vector temp;
3134
3135 // This allows copies to be made of this constraint which share
3136 // the function object.
3137 int* referenceCount;
3138
3139 };
3140
3141
3142
3143 //==============================================================================
3144 // SPEED COUPLER IMPL
3145 //==============================================================================
3146 class Constraint::SpeedCouplerImpl
3147 : public Constraint::Custom::Implementation {
3148 public:
3149 SpeedCouplerImpl(SimbodyMatterSubsystem& matter,
3150 const Function* function,
3151 const Array_<MobilizedBodyIndex>& speedBody,
3152 const Array_<MobilizerUIndex>& speedIndex,
3153 const Array_<MobilizedBodyIndex>& coordBody,
3154 const Array_<MobilizerQIndex>& coordIndex);
3155
~SpeedCouplerImpl()3156 ~SpeedCouplerImpl() {
3157 if (--referenceCount[0] == 0) {
3158 delete function;
3159 delete[] referenceCount;
3160 }
3161 }
3162
clone()3163 Implementation* clone() const override {
3164 referenceCount[0]++;
3165 return new SpeedCouplerImpl(*this);
3166 }
3167
3168 void calcVelocityErrors
3169 (const State& state,
3170 const Array_<SpatialVec,ConstrainedBodyIndex>& V_AB,
3171 const Array_<Real, ConstrainedUIndex>& constrainedU,
3172 Array_<Real>& verr) const override;
3173
3174 void calcVelocityDotErrors
3175 (const State& state,
3176 const Array_<SpatialVec,ConstrainedBodyIndex>& A_AB,
3177 const Array_<Real, ConstrainedUIndex>& constrainedUDot,
3178 Array_<Real>& vaerr) const override;
3179
3180 void addInVelocityConstraintForces
3181 (const State& state,
3182 const Array_<Real>& multipliers,
3183 Array_<SpatialVec,ConstrainedBodyIndex>& bodyForces,
3184 Array_<Real,ConstrainedUIndex>& mobilityForces) const override;
3185
3186 //------------------------------------------------------------------------------
3187 private:
3188
3189 const Function* function;
3190 int* referenceCount;
3191 Array_<ConstrainedMobilizerIndex> speedBodies;
3192 Array_<MobilizedBodyIndex> coordBodies;
3193 Array_<MobilizerUIndex> speedIndices;
3194 Array_<MobilizerQIndex> coordIndices;
3195 mutable Vector temp;
3196 };
3197
3198
3199
3200 //==============================================================================
3201 // PRESCRIBED MOTION IMPL
3202 //==============================================================================
3203 class Constraint::PrescribedMotionImpl
3204 : public Constraint::Custom::Implementation {
3205 public:
3206 PrescribedMotionImpl(SimbodyMatterSubsystem& matter,
3207 const Function* function,
3208 MobilizedBodyIndex coordBody,
3209 MobilizerQIndex coordIndex);
3210
~PrescribedMotionImpl()3211 ~PrescribedMotionImpl() {
3212 if (--referenceCount[0] == 0) {
3213 delete function;
3214 delete[] referenceCount;
3215 }
3216 }
3217
clone()3218 Implementation* clone() const override {
3219 referenceCount[0]++;
3220 return new PrescribedMotionImpl(*this);
3221 }
3222
3223 void calcPositionErrors
3224 (const State& state,
3225 const Array_<Transform,ConstrainedBodyIndex>& X_AB,
3226 const Array_<Real, ConstrainedQIndex>& constrainedQ,
3227 Array_<Real>& perr) const override;
3228
3229 void calcPositionDotErrors
3230 (const State& state,
3231 const Array_<SpatialVec,ConstrainedBodyIndex>& V_AB,
3232 const Array_<Real, ConstrainedQIndex>& constrainedQDot,
3233 Array_<Real>& pverr) const override;
3234
3235 void calcPositionDotDotErrors
3236 (const State& state,
3237 const Array_<SpatialVec,ConstrainedBodyIndex>& A_AB,
3238 const Array_<Real, ConstrainedQIndex>& constrainedQDotDot,
3239 Array_<Real>& paerr) const override;
3240
3241 void addInPositionConstraintForces
3242 (const State& state,
3243 const Array_<Real>& multipliers,
3244 Array_<SpatialVec,ConstrainedBodyIndex>& bodyForces,
3245 Array_<Real,ConstrainedQIndex>& qForces) const override;
3246
3247 //------------------------------------------------------------------------------
3248 private:
3249 const Function* function;
3250 int* referenceCount;
3251 ConstrainedMobilizerIndex coordBody;
3252 MobilizerQIndex coordIndex;
3253 mutable Vector temp;
3254 };
3255
3256
3257 } // namespace SimTK
3258
3259 #endif // SimTK_SIMBODY_CONSTRAINT_IMPL_H_
3260
3261
3262
3263