1 /* -------------------------------------------------------------------------- *
2  *                          OpenSim:  Coordinate.cpp                          *
3  * -------------------------------------------------------------------------- *
4  * The OpenSim API is a toolkit for musculoskeletal modeling and simulation.  *
5  * See http://opensim.stanford.edu and the NOTICE file for more information.  *
6  * OpenSim is developed at Stanford University and supported by the US        *
7  * National Institutes of Health (U54 GM072970, R24 HD065690) and by DARPA    *
8  * through the Warrior Web program.                                           *
9  *                                                                            *
10  * Author(s): Ajay Seth, Michael A. Sherman, Ayman Habib                      *
11  * Contributor(s): Frank C. Anderson, Jeffrey A. Reinbolt                     *
12  *                                                                            *
13  * Licensed under the Apache License, Version 2.0 (the "License"); you may    *
14  * not use this file except in compliance with the License. You may obtain a  *
15  * copy of the License at http://www.apache.org/licenses/LICENSE-2.0.         *
16  *                                                                            *
17  * Unless required by applicable law or agreed to in writing, software        *
18  * distributed under the License is distributed on an "AS IS" BASIS,          *
19  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   *
20  * See the License for the specific language governing permissions and        *
21  * limitations under the License.                                             *
22  * -------------------------------------------------------------------------- */
23 
24 //=============================================================================
25 // INCLUDES
26 //=============================================================================
27 #include "Coordinate.h"
28 #include "CoordinateCouplerConstraint.h"
29 #include <OpenSim/Simulation/Model/Model.h>
30 #include <OpenSim/Simulation/SimbodyEngine/Joint.h>
31 #include "simbody/internal/Constraint.h"
32 
33 //=============================================================================
34 // STATICS
35 //=============================================================================
36 using namespace std;
37 using namespace SimTK;
38 using namespace OpenSim;
39 
40 /** @cond **/ // hide from Doxygen
41 /**
42  * A Constant Function whose value is modifiable.
43  */
44 class ModifiableConstant : public SimTK::Function_<SimTK::Real>{
45 public:
ModifiableConstant(const SimTK::Real & value,int argumentSize)46     ModifiableConstant(const SimTK::Real& value, int argumentSize) :
47         argumentSize(argumentSize), value(value) { }
48 
clone() const49     ModifiableConstant* clone() const override {
50         return new ModifiableConstant(this->value, this->argumentSize);
51     }
52 
calcValue(const SimTK::Vector & x) const53     SimTK::Real calcValue(const SimTK::Vector& x) const override {
54         assert(x.size() == argumentSize);
55         return value;
56     }
57 
calcDerivative(const std::vector<int> & derivComponents,const SimTK::Vector & x) const58     SimTK::Real calcDerivative(const std::vector<int>& derivComponents,
59         const SimTK::Vector& x) const {
60         return calcDerivative(SimTK::ArrayViewConst_<int>(derivComponents),x);
61     }
calcDerivative(const SimTK::Array_<int> & derivComponents,const SimTK::Vector & x) const62     SimTK::Real calcDerivative(const SimTK::Array_<int>& derivComponents,
63         const SimTK::Vector& x) const override {
64         return 0;
65     }
66 
getArgumentSize() const67     int getArgumentSize() const override {
68         return argumentSize;
69     }
getMaxDerivativeOrder() const70     int getMaxDerivativeOrder() const override {
71         return std::numeric_limits<int>::max();
72     }
73 
setValue(SimTK::Real newValue)74     void setValue(SimTK::Real newValue){
75         value = newValue;
76     }
77 private:
78     const int argumentSize;
79     SimTK::Real value;
80 };
81 /** @endcond **/
82 
83 //=============================================================================
84 // CONSTRUCTOR(S) AND DESTRUCTOR
85 //=============================================================================
86 //_____________________________________________________________________________
87 /**
88  * Default constructor.
89  */
Coordinate()90 Coordinate::Coordinate()
91 {
92     constructProperties();
93 }
94 
95 //_____________________________________________________________________________
96 /**
97  * Constructor.
98  */
Coordinate(const std::string & aName,MotionType aMotionType,double defaultValue,double aRangeMin,double aRangeMax)99 Coordinate::Coordinate(const std::string &aName, MotionType aMotionType,
100         double defaultValue, double aRangeMin, double aRangeMax) :
101     Coordinate()
102 {
103     setName(aName);
104     //setMotionType(aMotionType);
105     setDefaultValue(defaultValue);
106     setRangeMin(aRangeMin);
107     setRangeMax(aRangeMax);
108 }
109 
110 //_____________________________________________________________________________
111 /**
112  * Connect properties to local pointers.
113  */
constructProperties(void)114 void Coordinate::constructProperties(void)
115 {
116     setAuthors("Ajay Seth, Ayman Habib, Michael Sherman");
117 
118     constructProperty_default_value(0.0);
119     constructProperty_default_speed_value(0.0);
120 
121     Array<double> defaultRange(-10.0, 2); //two values in range
122     defaultRange[1] = 10.0; // second value in range is 10.0
123     constructProperty_range(defaultRange);
124 
125     constructProperty_clamped(false);
126     constructProperty_locked(false);
127 
128     constructProperty_prescribed_function();
129     constructProperty_prescribed(false);
130     constructProperty_is_free_to_satisfy_constraints(false);
131 }
132 
133 
134 //_____________________________________________________________________________
135 /*
136  * Perform some set up functions that happen after the
137  * object has been deserialized or copied.
138  *
139  */
extendFinalizeFromProperties()140 void Coordinate::extendFinalizeFromProperties()
141 {
142     Super::extendFinalizeFromProperties();
143 
144     string prefix = "Coordinate("+getName()+")::extendFinalizeFromProperties:";
145 
146     // Make sure the default value is within the range when clamped
147     if (get_clamped()){
148         // Make sure the range is min to max.
149         SimTK_ERRCHK_ALWAYS(get_range(0) <= get_range(1), prefix.c_str(),
150             "Maximum coordinate range less than minimum.");
151 
152         double dv = get_default_value();
153         SimTK_ERRCHK2_ALWAYS(dv > (get_range(0) - SimTK::SqrtEps), prefix.c_str(),
154             "Default coordinate value is less than range minimum.\n"
155             "Default value = %g  < min = %g.", dv,  get_range(0));
156 
157         SimTK_ERRCHK2_ALWAYS(dv < (get_range(1) + SimTK::SqrtEps), prefix.c_str(),
158             "Default coordinate value is greater than range maximum.\n"
159             "Default value = %g > max = %g.", dv, get_range(1));
160     }
161 
162     _lockedWarningGiven=false;
163 
164     _speedName = getName() + "/speed";
165 }
166 
extendAddToSystem(SimTK::MultibodySystem & system) const167 void Coordinate::extendAddToSystem(SimTK::MultibodySystem& system) const
168 {
169     Super::extendAddToSystem(system);
170 
171     // Make this modifiable temporarily so we can record information needed
172     // to later access our pieces of the SimTK::MultibodySystem. That info is
173     // const after the system has been built.
174     Coordinate* mutableThis = const_cast<Coordinate *>(this);
175 
176     // Define the locked value for the constraint as a function.
177     // The PrescribedMotion will take ownership, but we'll keep a reference
178     // pointer here to allow for later modification.
179     std::unique_ptr<ModifiableConstant>
180         funcOwner(new ModifiableConstant(get_default_value(), 1));
181     mutableThis->_lockFunction = funcOwner.get();
182 
183     // The underlying SimTK constraint
184     SimTK::Constraint::PrescribedMotion
185         lock(system.updMatterSubsystem(),
186              funcOwner.release(),   // give up ownership
187              _bodyIndex,
188              SimTK::MobilizerQIndex(_mobilizerQIndex));
189 
190     // Save the index so we can access the SimTK::Constraint later
191     mutableThis->_lockedConstraintIndex = lock.getConstraintIndex();
192 
193     if(!getProperty_prescribed_function().empty()){
194         //create prescribed motion constraint automatically
195         SimTK::Constraint::PrescribedMotion prescribe(
196                 _model->updMatterSubsystem(),
197                 get_prescribed_function().createSimTKFunction(),
198                 _bodyIndex,
199                 SimTK::MobilizerQIndex(_mobilizerQIndex));
200         mutableThis->_prescribedConstraintIndex = prescribe.getConstraintIndex();
201     }
202     else if(get_prescribed()){
203         // if prescribed is set to true, and there is no prescribed
204         // function defined, then it cannot be prescribed (set to false).
205         mutableThis->upd_prescribed() = false;
206     }
207 
208     //Now add clamping
209     addModelingOption("is_clamped", 1);
210 
211     SimTK::SubsystemIndex sbsix =
212         getModel().getMatterSubsystem().getMySubsystemIndex();
213 
214     //Expose coordinate state variable
215     CoordinateStateVariable* csv
216         = new CoordinateStateVariable("value", *this,
217                                        sbsix, _mobilizerQIndex );
218     addStateVariable(csv);
219 
220     //Expose coordinate's speed state variable
221     SpeedStateVariable* ssv =
222         new SpeedStateVariable("speed", *this, sbsix, _mobilizerQIndex);
223     addStateVariable(ssv);
224 }
225 
extendRealizeInstance(const SimTK::State & state) const226 void Coordinate::extendRealizeInstance(const SimTK::State& state) const
227 {
228     //const MobilizedBody& mb
229     //    = getModel().getMatterSubsystem().getMobilizedBody(_bodyIndex);
230 
231     //int uix = state.getUStart() + mb.getFirstUIndex(state) + _mobilizerQIndex;
232 
233     /* Set the YIndex on the StateVariable */
234 }
235 
extendInitStateFromProperties(State & s) const236 void Coordinate::extendInitStateFromProperties(State& s) const
237 {
238     // Cannot enforce the constraint, since state of constraints may still be undefined
239     const MobilizedBody& mb=_model->getMatterSubsystem().getMobilizedBody(_bodyIndex);
240     int nq=mb.getNumQ(s);
241     if (_mobilizerQIndex>=nq){
242         //Something is wrong/inconsistent with model definition. Abort
243         throw(Exception("Coordinate: "+getName()+" is not consistent with owner Joint. Aborting."));
244     }
245     _model->getMatterSubsystem().getMobilizedBody(_bodyIndex)
246         .setOneQ(s,_mobilizerQIndex,get_default_value());
247     _model->getMatterSubsystem().getMobilizedBody(_bodyIndex)
248         .setOneU(s,_mobilizerQIndex,get_default_speed_value());
249     setIsPrescribed(s, get_prescribed());
250     setClamped(s, get_clamped());
251     // Locking takes precedence if all joint constraints are ON
252     setLocked(s, get_locked());
253 }
254 
extendSetPropertiesFromState(const SimTK::State & state)255 void Coordinate::extendSetPropertiesFromState(const SimTK::State& state)
256 {
257     upd_default_value() = _model->getMatterSubsystem().getMobilizedBody(_bodyIndex).getOneQ(state,_mobilizerQIndex);
258     upd_default_speed_value() = _model->getMatterSubsystem().getMobilizedBody(_bodyIndex).getOneU(state,_mobilizerQIndex);
259     upd_prescribed() = isPrescribed(state);
260     upd_clamped() = getClamped(state);
261     upd_locked() = getLocked(state);
262 }
263 
264 
265 //=============================================================================
266 // GET AND SET
267 //=============================================================================
268 //-----------------------------------------------------------------------------
269 // JOINT
270 //-----------------------------------------------------------------------------
271 //_____________________________________________________________________________
272 /**
273  * Set the joint to which this coordinate belongs.
274  *
275  * @param aowningJoint Joint to which this coordinate belongs.
276  */
setJoint(const Joint & aOwningJoint)277 void Coordinate::setJoint(const Joint& aOwningJoint)
278 {
279     _joint = &aOwningJoint;
280 }
281 //_____________________________________________________________________________
282 /**
283  * Get the joint to which this coordinate belongs.
284  *
285  * @return Joint to which this coordinate belongs.
286  */
getJoint() const287 const Joint& Coordinate::getJoint() const
288 {
289     return(_joint.getRef());
290 }
291 
getMotionType() const292 Coordinate::MotionType Coordinate::getMotionType() const
293 {
294     int ix = getJoint().getProperty_coordinates().findIndexForName(getName());
295     return getJoint().getMotionType(Joint::CoordinateIndex(ix));
296 }
297 
298 
299 //-----------------------------------------------------------------------------
300 // VALUE
301 //-----------------------------------------------------------------------------
302 //done_____________________________________________________________________________
303 /**
304  * Get the value.
305  *
306  * @return The current value of the coordinate.
307  */
getValue(const SimTK::State & s) const308 double Coordinate::getValue(const SimTK::State& s) const
309 {
310     return _model->getMatterSubsystem().getMobilizedBody(_bodyIndex).getOneQ(s,_mobilizerQIndex);
311 }
312 //done_____________________________________________________________________________
313 /**
314  * Set the value.
315  *
316  * @param aValue value to change to.
317  */
setValue(SimTK::State & s,double aValue,bool enforceConstraints) const318 void Coordinate::setValue(SimTK::State& s, double aValue , bool enforceConstraints) const
319 {
320     // If enforceConstraints is true and coordinate is clamped, clamp aValue into range
321     if (enforceConstraints && getClamped(s)) {
322         if (aValue < get_range(0))
323             aValue = get_range(0);
324         else if (aValue > get_range(1))
325             aValue = get_range(1);
326     }
327 
328     // If the coordinate is locked and aValue is not the current value, print an error.
329     // Otherwise, set the value to aValue.
330     if (getLocked(s)) {
331         if (aValue != getValue(s) && !_lockedWarningGiven){
332             cout<<"Coordinate.setValue: WARN- coordinate "<<getName()<<" is locked. Unable to change its value." << endl;
333             _lockedWarningGiven=true;
334         }
335     } else {
336         _model->updMatterSubsystem().getMobilizedBody(_bodyIndex).setOneQ(s,_mobilizerQIndex,aValue);
337     }
338 
339     // The Q that was set might not satisfy constraints, so if enforceConstraints then call model assemble.
340     // You want to do this even if the coordinate is locked and its value hasn't changed, because this may be
341     // the last setValue() call in a string of them (e.g., to set a model pose), in which case you only try to
342     // enforce constraints during the last call.
343     if (enforceConstraints) {
344         if (_model->getConstraintSet().getSize()>0 || isConstrained(s)){
345             // if this coordinate is set up to be dependent on other coordinates
346             // its value should be dictated by the other coordinates and not its present value
347             double weight = isDependent(s) ? 0.0  : 10;
348             // assemble model so that states satisfy ALL constraints
349             _model->assemble(s, this, weight);
350         }
351         else
352             _model->getMultibodySystem().realize(s, Stage::Position );
353     }
354 }
355 
getSpeedValue(const SimTK::State & s) const356 double Coordinate::getSpeedValue(const SimTK::State& s) const
357 {
358     return _model->getMatterSubsystem().getMobilizedBody(_bodyIndex).getOneU(s,_mobilizerQIndex);
359 }
360 
361 
setSpeedValue(SimTK::State & s,double aValue) const362 void Coordinate::setSpeedValue(SimTK::State& s, double aValue) const
363 {
364     _model->updMatterSubsystem().getMobilizedBody(_bodyIndex).setOneU(s,_mobilizerQIndex,aValue);
365 }
366 
getSpeedName() const367 const std::string&  Coordinate::getSpeedName() const
368 {
369     return _speedName;
370 }
371 
getAccelerationValue(const SimTK::State & s) const372 double Coordinate::getAccelerationValue(const SimTK::State& s) const
373 {
374     return getModel().getMatterSubsystem().getMobilizedBody(_bodyIndex).getOneUDot(s, _mobilizerQIndex);
375 }
376 
377 
378 //_____________________________________________________________________________
379 /**
380  * Set the range min and max.
381  *
382  * @param aRange range min and man to change to.
383  * @return Whether or not the range was changed.
384  */
setRange(double aRange[2])385 void Coordinate::setRange(double aRange[2])
386 {
387     if (aRange[1] >= aRange[0]) {
388         upd_range(0) = aRange[0];
389         upd_range(1) = aRange[1];
390     }
391     else
392         throw Exception("Coordinate::setRange, range is invalid, "
393             "min range value exceeds max.");
394 }
395 
396 //_____________________________________________________________________________
397 /**
398  * Set the range min.
399  *
400  * @param aRange range min to change to.
401  * @return Whether or not the range min was changed.
402  */
setRangeMin(double aMin)403 void Coordinate::setRangeMin(double aMin)
404 {
405     upd_range(0) = aMin;
406 }
407 
408 //_____________________________________________________________________________
409 /**
410  * Set the range max.
411  *
412  * @param aRange range max to change to.
413  * @return Whether or not the range max was changed.
414  */
setRangeMax(double aMax)415 void Coordinate::setRangeMax(double aMax)
416 {
417     upd_range(1) = aMax;
418 }
419 
420 //_____________________________________________________________________________
421 /**
422  * Set the default value.
423  *
424  * @param aDefaultValue new default value to change to.
425  */
setDefaultValue(double aDefaultValue)426 void Coordinate::setDefaultValue(double aDefaultValue)
427 {
428     upd_default_value() = aDefaultValue;
429 }
430 
431 //_____________________________________________________________________________
432 /**
433  * Get the prescribed motion function.
434  *
435  * @return const reference to the prescribed motion function.
436  */
getPrescribedFunction() const437 const OpenSim::Function& Coordinate::getPrescribedFunction() const
438 {
439     return get_prescribed_function();
440 }
441 
442 //_____________________________________________________________________________
443 /**
444  * Set the prescribed motion function.
445  */
setPrescribedFunction(const OpenSim::Function & function)446 void Coordinate::setPrescribedFunction(const OpenSim::Function& function)
447 {
448     //Optional property so clear out previous function value if any
449     updProperty_prescribed_function().clear();
450     updProperty_prescribed_function().adoptAndAppendValue(function.clone());
451 }
452 
453 //_____________________________________________________________________________
454 /**
455  * Determine if the coordinate is dependent on other coordinates or not,
456  * by checking to see whether there is a CoordinateCouplerConstraint relating
457  * it to another coordinate. If so return true, false otherwise.
458  * TODO: note that this will fail to detect any other kind of constraint that
459  * might couple coordinates together.
460  */
isDependent(const SimTK::State & s) const461  bool Coordinate::isDependent(const SimTK::State& s) const
462 {
463     if(get_is_free_to_satisfy_constraints())
464         return true;
465 
466     for(int i=0; i<_model->getConstraintSet().getSize(); i++){
467         Constraint& constraint = _model->getConstraintSet().get(i);
468         CoordinateCouplerConstraint* couplerp =
469             dynamic_cast<CoordinateCouplerConstraint*>(&constraint);
470         if(couplerp) {
471             if (couplerp->getDependentCoordinateName() == getName())
472                 return couplerp->isEnforced(s);
473         }
474     }
475     return false;
476 }
477 //_____________________________________________________________________________
478 /**
479  *  Determine if the coordinate is constrained or not.
480  *  Specifically, is locked, prescribed, or completely dependent on other coordinates?
481  *  If so return true, false otherwise.
482  */
isConstrained(const SimTK::State & s) const483 bool Coordinate::isConstrained(const SimTK::State& s) const
484 {
485     return (getLocked(s) || isPrescribed(s) || isDependent(s));
486 }
487 
488 
489 //-----------------------------------------------------------------------------
490 // LOCK
491 //-----------------------------------------------------------------------------
492 //_____________________________________________________________________________
493 /**
494  * Set whether or not this coordinate is locked.
495  * A prescribed constraint is used to lock the joint at the dynamics level.
496  * If lock is applied after clamping or prescribed motion it takes precedence.
497  *
498  * @param aLocked If true the joint is locked; if false the joint is unlocked.
499  */
setLocked(SimTK::State & s,bool aLocked) const500 void Coordinate::setLocked(SimTK::State& s, bool aLocked) const
501 {
502     // Do nothing if the same, but make sure _locked is also up-to-date
503     if(aLocked == getLocked(s)){
504         return;
505     }
506 
507     _lockedWarningGiven=false;  // reset flag in case needed later
508     SimTK::Constraint *lock = NULL;
509 
510     // Get constraint
511     if(_lockedConstraintIndex.isValid()){
512         lock = &_model->updMultibodySystem().updMatterSubsystem().updConstraint(_lockedConstraintIndex);
513     }
514     else{
515         string msg = "Lock constraint for coordinate could not be found.";
516         throw Exception(msg,__FILE__,__LINE__);
517     }
518 
519     // Now enable if locked otherwise disable
520     if(aLocked){
521         // Update the locked value of the constraint before locking
522         _lockFunction->setValue(getValue(s));
523         lock->enable(s);
524         //Cannot be locked and have prescribed motion and/or clamping
525         setIsPrescribed(s, false);
526     }
527     else {
528         lock->disable(s);
529     }
530 }
531 
532 /**
533  * Get whether or not this coordinate is locked.
534  * Calls the underlying constraint at the dynamics level.
535  *
536  * @return true if the coordinate is locked and false if unlocked.
537  */
getLocked(const SimTK::State & s) const538 bool Coordinate::getLocked(const SimTK::State& s) const
539 {
540     if(_lockedConstraintIndex.isValid()){
541         bool disabled = _model->updMultibodySystem().updMatterSubsystem().getConstraint(_lockedConstraintIndex).isDisabled(s);
542         return !disabled;
543     }
544     else{
545         return get_locked();
546     }
547 }
548 
549 //-----------------------------------------------------------------------------
550 // PRESCRIBED MOTION
551 //-----------------------------------------------------------------------------
552 //_____________________________________________________________________________
553 /**
554  * Set whether or not this coordinate is being prescribed.
555  * A prescribed constraint is used specify motion at the dynamics level.
556  * If isPrescribed is set after clamping or locking it takes precedence-
557  * and clamped and locked are false.
558  *
559  * @param isPrescribed If true the coordinate is prescribed; if false not prescribed.
560  */
setIsPrescribed(SimTK::State & s,bool isPrescribed) const561 void Coordinate::setIsPrescribed(SimTK::State& s, bool isPrescribed) const
562 {
563     // Do nothing if the same
564     if(isPrescribed == this->isPrescribed(s) ) return;
565 
566     // The underlying SimTK constraint
567     SimTK::Constraint *prescribe = NULL;
568 
569     // Get constraint
570     if(_prescribedConstraintIndex.isValid()){
571         //get constraint
572         prescribe = &_model->updMultibodySystem().updMatterSubsystem().updConstraint(_prescribedConstraintIndex);
573     }
574     else{
575         string msg = "Prescribed motion for coordinate not found.";
576         throw Exception(msg,__FILE__,__LINE__);
577     }
578 
579     // Now enable if prescribed motion constraint otherwise disable
580     if(isPrescribed){
581         prescribe->enable(s);
582         setLocked(s, false);
583     }
584     else
585         prescribe->disable(s);
586 }
587 
isPrescribed(const SimTK::State & s) const588 bool Coordinate::isPrescribed(const SimTK::State& s) const
589 {
590     if(_prescribedConstraintIndex.isValid()){
591         bool disabled = _model->updMultibodySystem().updMatterSubsystem().getConstraint(_prescribedConstraintIndex).isDisabled(s);
592         return !disabled;
593     }
594     else{
595         return get_prescribed();
596     }
597 
598 }
599 
600 //-----------------------------------------------------------------------------
601 // CLAMP
602 //-----------------------------------------------------------------------------
603 //_____________________________________________________________________________
getClamped(const SimTK::State & s) const604 bool Coordinate::getClamped(const SimTK::State& s) const
605 {
606     return getModelingOption(s, "is_clamped") > 0;
607 }
608 
setClamped(SimTK::State & s,bool aLocked) const609 void Coordinate::setClamped(SimTK::State& s, bool aLocked) const
610 {
611     setModelingOption(s, "is_clamped", (int)aLocked);
612 }
613 
614 //-----------------------------------------------------------------------------
615 // Coordinate::CoordinateStateVariable
616 //-----------------------------------------------------------------------------
617 double Coordinate::CoordinateStateVariable::
getValue(const SimTK::State & state) const618     getValue(const SimTK::State& state) const
619 {
620     return ((Coordinate *)&getOwner())->getValue(state);
621 }
622 
623 void Coordinate::CoordinateStateVariable::
setValue(SimTK::State & state,double value) const624     setValue(SimTK::State& state, double value) const
625 {
626     ((Coordinate *)&getOwner())->setValue(state, value, false);
627 }
628 
629 double Coordinate::CoordinateStateVariable::
getDerivative(const SimTK::State & state) const630     getDerivative(const SimTK::State& state) const
631 {
632     //TODO: update to get qdot value from the mobilized body
633     return ((Coordinate *)&getOwner())->getSpeedValue(state);
634 }
635 
636 
637 void Coordinate::CoordinateStateVariable::
setDerivative(const SimTK::State & state,double deriv) const638     setDerivative(const SimTK::State& state, double deriv) const
639 {
640     string msg = "CoordinateStateVariable::setDerivative() - ERROR \n";
641     msg +=  "Coordinate derivative (qdot) is computed by the Multibody system.";
642     throw Exception(msg);
643 }
644 
645 
646 //-----------------------------------------------------------------------------
647 // Coordinate::SpeedStateVariable
648 //-----------------------------------------------------------------------------
649 double Coordinate::SpeedStateVariable::
getValue(const SimTK::State & state) const650     getValue(const SimTK::State& state) const
651 {
652     //TODO: update to get qdot value from the mobilized body
653     return ((Coordinate *)&getOwner())->getSpeedValue(state);
654 }
655 
656 void Coordinate::SpeedStateVariable::
setValue(SimTK::State & state,double deriv) const657     setValue(SimTK::State& state, double deriv) const
658 {
659     //TODO: update to set qdot value from the mobilized body
660     ((Coordinate *)&getOwner())->setSpeedValue(state, deriv);
661 }
662 
663 double Coordinate::SpeedStateVariable::
getDerivative(const SimTK::State & state) const664     getDerivative(const SimTK::State& state) const
665 {
666     const Coordinate& owner = *((Coordinate *)&getOwner());
667     const MobilizedBody& mb = owner.getModel().getMatterSubsystem()
668                                 .getMobilizedBody(owner.getBodyIndex());
669 
670     return mb.getUDotAsVector(state)[owner.getMobilizerQIndex()];
671 }
672 
673 void Coordinate::SpeedStateVariable::
setDerivative(const SimTK::State & state,double deriv) const674     setDerivative(const SimTK::State& state, double deriv) const
675 {
676     string msg = "SpeedStateVariable::setDerivative() - ERROR \n";
677     msg +=  "Generalized speed derivative (udot) can only be set by the Multibody system.";
678     throw Exception(msg);
679 }
680 
681 //=============================================================================
682 // XML Deserialization
683 //=============================================================================
updateFromXMLNode(SimTK::Xml::Element & aNode,int versionNumber)684 void Coordinate::updateFromXMLNode(SimTK::Xml::Element& aNode,
685     int versionNumber)
686 {
687     if (versionNumber < XMLDocument::getLatestVersion()) {
688         if (versionNumber < 30514) {
689             SimTK::Xml::element_iterator iter = aNode.element_begin("motion_type");
690             if (iter != aNode.element_end()) {
691                 SimTK::Xml::Element motionTypeElm =
692                     SimTK::Xml::Element::getAs(aNode.removeNode(iter));
693                 std::string typeName = motionTypeElm.getValue();
694 
695                 if ((IO::Lowercase(typeName) == "rotational"))
696                     _userSpecifiedMotionTypePriorTo40 = Rotational;
697                 else if (IO::Lowercase(typeName) == "translational")
698                     _userSpecifiedMotionTypePriorTo40 = Translational;
699                 else if (IO::Lowercase(typeName) == "coupled")
700                     _userSpecifiedMotionTypePriorTo40 = Coupled;
701                 else
702                     _userSpecifiedMotionTypePriorTo40 = Undefined;
703             }
704         }
705     }
706     Super::updateFromXMLNode(aNode, versionNumber);
707 }
708 
getUserSpecifiedMotionTypePriorTo40() const709 const Coordinate::MotionType& Coordinate::getUserSpecifiedMotionTypePriorTo40() const
710 {
711     return _userSpecifiedMotionTypePriorTo40;
712 }
713