1 #ifndef OPENSIM_OPENSIM_CONTEXT_H_ 2 #define OPENSIM_OPENSIM_CONTEXT_H_ 3 /* -------------------------------------------------------------------------- * 4 * OpenSim: OpenSimContext.h * 5 * -------------------------------------------------------------------------- * 6 * The OpenSim API is a toolkit for musculoskeletal modeling and simulation. * 7 * See http://opensim.stanford.edu and the NOTICE file for more information. * 8 * OpenSim is developed at Stanford University and supported by the US * 9 * National Institutes of Health (U54 GM072970, R24 HD065690) and by DARPA * 10 * through the Warrior Web program. * 11 * * 12 * Copyright (c) 2005-2017 Stanford University and the Authors * 13 * Author(s): Jack Middleton, Ayman Habib * 14 * * 15 * Licensed under the Apache License, Version 2.0 (the "License"); you may * 16 * not use this file except in compliance with the License. You may obtain a * 17 * copy of the License at http://www.apache.org/licenses/LICENSE-2.0. * 18 * * 19 * Unless required by applicable law or agreed to in writing, software * 20 * distributed under the License is distributed on an "AS IS" BASIS, * 21 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * 22 * See the License for the specific language governing permissions and * 23 * limitations under the License. * 24 * -------------------------------------------------------------------------- */ 25 26 #include <OpenSim/Common/Object.h> 27 #include <OpenSim/Common/PropertyTransform.h> 28 #include <OpenSim/Simulation/osimSimulationDLL.h> 29 #include <OpenSim/Simulation/Model/Model.h> 30 #include <OpenSim/Simulation/Model/Force.h> 31 #include <OpenSim/Common/Array.h> 32 #include <OpenSim/Tools/InverseKinematicsTool.h> 33 34 #include "Simbody.h" 35 36 namespace OpenSim { 37 38 class Body; 39 class Coordinate; 40 class TransformAxis; 41 class Function; 42 class Marker; 43 class MarkerSet; 44 class Model; 45 class MovingPathPoint; 46 class Muscle; 47 class GeometryPath; 48 class AbstractPathPoint; 49 class PathWrap; 50 class ConditionalPathPoint; 51 class WrapObject; 52 class Analysis; 53 class AnalyzeTool; 54 class ModelScaler; 55 class MarkerPlacer; 56 class MarkerData; 57 class Measurement; 58 59 // Flag to indicate whether calls to the API are made from within try/catch block 60 // so that exceptions due to misuse, typos etc. are handled gracefully in scripts 61 // Set to true by default. 62 static bool mapCxxExceptionsToJava = true; 63 64 //============================================================================== 65 // OpenSimContext 66 //============================================================================== 67 /** Class intended to keep the SimTK::State under an OpenSim model to make it possible 68 to get/set values in the SimTK::State without exposing the SimTK::State class itself. 69 70 The class provides convenient methods to get/set various state entries and query the 71 state for cache values. The main function this class provides is an adaptor of various 72 data types from Java and scripting supported primitive, wrapped and array types to the 73 corresponding possibly templatized or SimTK native data types. 74 75 Most methods of this class are implementated by delegating the call to the SimTK::State 76 under the object, for example: 77 Context::isDisabled(const Force& force) -> force.isDisabled(state) 78 79 The class also provides convenient services to recreateSystem and realize to various stages. 80 81 @author Ayman Habib & Jack Middleton 82 **/ 83 84 class OpenSimContext : public Object { 85 OpenSim_DECLARE_CONCRETE_OBJECT(OpenSimContext, Object); 86 87 88 public: 89 OpenSimContext(SimTK::State* s, Model* model); 90 setState(SimTK::State * s)91 void setState( SimTK::State* s) { _configState.reset(s); } setModel(Model * m)92 void setModel( Model* m) { _model.reset(m); } 93 94 /** Get reference to the single instance of SimTK::State maintained by the Context object **/ getCurrentStateRef()95 const SimTK::State& getCurrentStateRef() const { return (*_configState); }; 96 /** Return a "clone" of the single instance of SimTK::State maintained by the Context object **/ getCurrentStateCopy()97 SimTK::State getCurrentStateCopy() const { return SimTK::State(*_configState); }; 98 void recreateSystemAfterSystemExistsKeepStage(); 99 void recreateSystemAfterSystemExists(); resetStateToDefault()100 void resetStateToDefault() { 101 SimTK::Stage stageBeforeRecreatingSystem = _configState->getSystemStage(); 102 SimTK::State* newState = &_model->initSystem(); 103 setState( newState ); 104 _model->getMultibodySystem().realize( *_configState, stageBeforeRecreatingSystem ); 105 } 106 // Transforms 107 void transformPosition(const PhysicalFrame& body, double offset[], double gOffset[]); 108 SimTK::Transform getTransform(const PhysicalFrame& body); 109 void transform(const PhysicalFrame& ground, double d[], PhysicalFrame& body, double dragVectorBody[]); 110 // Coordinates 111 double getValue(const Coordinate& coord); 112 bool getLocked(const Coordinate& coord); 113 void setValue(const Coordinate& coord, double d, bool enforceConstraints=true); 114 void setClamped(Coordinate& coord, bool newValue); 115 bool getClamped(const Coordinate& coord); 116 void setLocked(Coordinate& coord, bool newValue); 117 bool isPrescribed(const Coordinate& coord) const; 118 bool isConstrained(const Coordinate& coord) const; 119 // Constraints isEnforced(const Constraint & constraint)120 bool isEnforced(const Constraint& constraint) const { 121 return constraint.isEnforced(*_configState); 122 } setIsEnforced(Constraint & constraint,bool isEnforced)123 void setIsEnforced(Constraint& constraint, bool isEnforced) { 124 constraint.setIsEnforced(*_configState, isEnforced); 125 _model->assemble(*_configState); 126 } 127 // Forces appliesForce(const Force & force)128 bool appliesForce(const Force& force) const { 129 return force.appliesForce(*_configState); 130 } setAppliesForce(Force & force,bool applyForce)131 void setAppliesForce(Force& force, bool applyForce) const { 132 force.setAppliesForce(*_configState, applyForce); 133 _model->getMultibodySystem().realize(*_configState, 134 SimTK::Stage::Position); 135 } 136 // Muscles 137 double getActivation(Muscle& act); 138 double getMuscleLength(Muscle& act); 139 const Array<AbstractPathPoint*>& getCurrentPath(Muscle& act); 140 void copyMuscle(Muscle& from, Muscle& to); 141 void replacePropertyFunction(OpenSim::Object& obj, OpenSim::Function* aOldFunction, OpenSim::Function* aNewFunction); 142 143 // Muscle Points 144 void setXFunction(MovingPathPoint& mmp, Function& newFunction); 145 void setYFunction(MovingPathPoint& mmp, Function& newFunction); 146 void setZFunction(MovingPathPoint& mmp, Function& newFunction); 147 void setXCoordinate(MovingPathPoint& mmp, Coordinate& newCoord); 148 void setYCoordinate(MovingPathPoint& mmp, Coordinate& newCoord); 149 void setZCoordinate(MovingPathPoint& mmp, Coordinate& newCoord); 150 void setBody(AbstractPathPoint& pathPoint, PhysicalFrame& newBody); 151 void setCoordinate(ConditionalPathPoint& via, Coordinate& newCoord); 152 void setRangeMin(ConditionalPathPoint& via, double d); 153 void setRangeMax(ConditionalPathPoint& via, double d); 154 bool replacePathPoint(GeometryPath& p, AbstractPathPoint& mp, AbstractPathPoint& newPoint); 155 void setLocation(PathPoint& mp, int i, double d); 156 157 void setLocation(PathPoint& mp, const SimTK::Vec3& newLocation); 158 void setEndPoint(PathWrap& mw, int newEndPt); 159 void addPathPoint(GeometryPath& p, int menuChoice, PhysicalFrame& body); 160 bool deletePathPoint(GeometryPath& p, int menuChoice); 161 bool isActivePathPoint(AbstractPathPoint& mp) ; 162 // Muscle Wrapping 163 void setStartPoint(PathWrap& mw, int newStartPt); 164 void addPathWrap(GeometryPath& p, WrapObject& awo); 165 void moveUpPathWrap(GeometryPath& p, int num); 166 void moveDownPathWrap(GeometryPath& p, int num); 167 void deletePathWrap(GeometryPath& p, int num); 168 // Markers 169 void setBody(Marker& currentMarker, PhysicalFrame& newBody, bool b); 170 void updateMarkerSet(Model& model, MarkerSet& aMarkerSet); 171 getCenterOfMassInGround(double com[3])172 void getCenterOfMassInGround(double com[3]) const { 173 SimTK::Vec3 comV = _model->getMatterSubsystem().calcSystemMassCenterLocationInGround(*_configState); 174 for(int i=0; i<3; i++) com[i] = comV[i]; 175 } 176 // Analyses 177 int step(Analysis& analysis); 178 // Tools 179 bool solveInverseKinematics( InverseKinematicsTool& ikTool); 180 void setStatesFromMotion(AnalyzeTool& analyzeTool, const Storage &aMotion, bool aInDegrees); 181 void loadStatesFromFile(AnalyzeTool& analyzeTool); 182 bool processModelScale(ModelScaler& modelScaler, 183 Model* aModel, const std::string& aPathToSubject="", double aFinalMass = -1.0); 184 bool processModelMarkerPlacer( MarkerPlacer& markerPlacer, 185 Model* aModel, const std::string& aPathToSubject=""); 186 double computeMeasurementScaleFactor(ModelScaler& modelScaler, 187 const Model& aModel, const MarkerData& aMarkerData, const Measurement& aMeasurement) const; 188 void replaceTransformAxisFunction(TransformAxis& aDof, OpenSim::Function& aFunction); 189 190 // Utilities isNaN(double v)191 static bool isNaN( double v ) { return (SimTK::isNaN(v)); } 192 getTime()193 double getTime() { 194 assert(_configState); 195 return (_configState->getTime()); 196 } 197 // Convert SimTK::Transform into a double[] array of 16 doubles getTransformAsDouble16(const SimTK::Transform & aTransform,double flattened[])198 static void getTransformAsDouble16(const SimTK::Transform& aTransform, double flattened[]){ 199 double* matStart = &aTransform.toMat44()[0][0]; 200 for (int i=0; i<16; i++) flattened[i]=matStart[i]; 201 } 202 // Sets the property values in the model from the current state if there 203 // are state variables that correspond to properties. setPropertiesFromState()204 void setPropertiesFromState() { 205 _model->setPropertiesFromState(*_configState); 206 } 207 /** 208 * Create a new System under the model then realize it to the same stage it had 209 */ recreateSystemKeepStage()210 void recreateSystemKeepStage() { 211 SimTK::Stage stageBeforeRecreatingSystem = _configState->getSystemStage(); 212 SimTK::Vector y1 = _configState->getY(); 213 SimTK::State* newState = &_model->initSystem(); 214 newState->updY() = y1; 215 setState( newState ); 216 _model->getMultibodySystem().realize( *_configState, stageBeforeRecreatingSystem ); 217 } 218 // Force re-realization 219 void realizePosition(); 220 void realizeVelocity(); 221 222 void cacheModelAndState(); 223 void restoreStateFromCachedModel() SWIG_DECLARE_EXCEPTION; 224 void setSocketConnecteePath(AbstractSocket& socket, 225 const std::string& newValue) SWIG_DECLARE_EXCEPTION; 226 //============================================================================= 227 // DATA 228 //============================================================================= 229 230 private: 231 // SimTK::State supporting the OpenSim::Model 232 SimTK::ReferencePtr<SimTK::State> _configState; 233 // The OpenSim::model 234 SimTK::ReferencePtr<Model> _model; 235 236 SimTK::ResetOnCopy<std::unique_ptr<Model> > clonedModel; 237 SimTK::State clonedState; 238 }; // class OpenSimContext 239 240 //============================================================================== 241 // OpenSimJavaObject 242 //============================================================================== 243 /** 244 In some cases, the GUI ad/or scripting language needs to create objects that derive from OpenSim::Object 245 The class OpenSim::Object however is not a concrete class, so we introduce OpenSimJavaObject 246 for this purpose 247 **/ 248 class OpenSimJavaObject : public Object { 249 OpenSim_DECLARE_CONCRETE_OBJECT(OpenSimJavaObject, Object); 250 }; 251 252 class AdhocModelComponent : public ModelComponent { 253 OpenSim_DECLARE_CONCRETE_OBJECT(AdhocModelComponent, ModelComponent); 254 }; 255 //============================================================================== 256 // AnalysisWrapper 257 //============================================================================== 258 /** 259 Class used as base class for Java classes deriving from Analysis (used to be callback) 260 It lives on the C++ side so that it gets access to SimTK::State, but it returns quantities 261 in Java data types 262 **/ 263 264 class AnalysisWrapper : public Analysis { 265 OpenSim_DECLARE_CONCRETE_OBJECT(AnalysisWrapper, Analysis); 266 public: 267 AnalysisWrapper(Model *aModel=0): Analysis(aModel)268 Analysis(aModel){ 269 } ~AnalysisWrapper()270 virtual ~AnalysisWrapper() {} 271 }; // Class AnalysisWrapper 272 273 274 //============================================================================== 275 // InterruptCallback 276 //============================================================================== 277 /** 278 Class used to handle interrupts (synchronously). Works by adding it as an analysis 279 And when the client (GUI in most cases) decides to interrupt the simulation/analysis, 280 it calls the interrupt() method. When the step method is invoked later, an exception 281 is thrown. 282 **/ 283 // Class to handle interrupts 284 class InterruptCallback : public AnalysisWrapper { 285 bool _throwException; 286 public: 287 InterruptCallback(Model *aModel=0): AnalysisWrapper(aModel)288 AnalysisWrapper(aModel), 289 _throwException(false){}; 290 interrupt()291 void interrupt() { 292 _throwException=true; 293 } step(const SimTK::State & s,int stepNumber)294 virtual int step( const SimTK::State& s, int stepNumber) { 295 if (_throwException) 296 throw Exception("Operation Aborted"); 297 return 0; 298 } 299 300 }; 301 302 //============================================================================== 303 // PropertyHelper 304 //============================================================================== 305 /** 306 This class allows access to property values using template-free 307 methods. Note that this will work regardless of whether the given 308 AbstractProperty is the deprecated kind or the new one. 309 310 An AbstractProperty represents a (name, list-of-values) pair, possibly 311 with restrictions on the minimum and maximum list length. Basic container 312 methods size(), resize(), clear(), and empty() are available; use resize() 313 before assigning a value to an indexed element. 314 315 For properties that contain objects, you can obtain the values directly 316 from the base class via non-templatized methods. 317 **/ 318 class PropertyHelper { 319 public: 320 //=================Boolean Properties================== 321 // Recover boolean value from an AbstractProperty that was assumed to contain a boolean 322 // Will throw exception if the assumption was wrong/invalid. Use index only if the 323 // property contains an array of booleans. 324 static bool getValueBool(const AbstractProperty& p, int index=-1) 325 { return p.getValue<bool>(index); } 326 // Set boolean value in an AbstractProperty that was assumed to hold a boolean 327 // Will throw exception if the assumption was wrong/invalid. Use index only if the 328 // property contains an array of booleans. 329 static void setValueBool(bool v, AbstractProperty& p, int index=-1) 330 { p.updValue<bool>(index) = v; } 331 // Append a new boolean value to an AbstractProperty that was assumed to hold a variable size 332 // array of booleans. Will throw exception if the assumption was wrong/invalid. appendValueBool(bool v,AbstractProperty & p)333 static void appendValueBool(bool v, AbstractProperty& p) 334 { p.appendValue<bool>(v); } 335 //=================Int Properties, see Boolean Properties for details ================== 336 static int getValueInt(const AbstractProperty& p, int index=-1) 337 { return p.getValue<int>(index); } 338 static void setValueInt(int v, AbstractProperty& p, int index=-1) 339 { p.updValue<int>(index) = v; } appendValueInt(int v,AbstractProperty & p)340 static void appendValueInt(int v, AbstractProperty& p) 341 { p.appendValue<int>(v); } 342 //=================Double Properties, see Boolean Properties for details ================== 343 static double getValueDouble(const AbstractProperty& p, int index=-1) 344 { return p.getValue<double>(index); } 345 static void setValueDouble(double v, AbstractProperty& p, int index=-1) 346 { p.updValue<double>(index) = v; } appendValueDouble(double v,AbstractProperty & p)347 static void appendValueDouble(double v, AbstractProperty& p) 348 { p.appendValue<double>(v); } 349 //=================String Properties, see Boolean Properties for details ================== 350 static std::string getValueString(const AbstractProperty& p, int index=-1) 351 { return p.getValue<std::string>(index); } 352 static void setValueString(const std::string& v, 353 AbstractProperty& p, int index=-1) 354 { p.updValue<std::string>(index) = v; } appendValueString(const std::string & v,AbstractProperty & p)355 static void appendValueString(const std::string& v, AbstractProperty& p) 356 { p.appendValue<std::string>(v); } 357 //=================Transform Properties, treated as six Doubles ================== getValueTransform(const AbstractProperty & p,int index)358 static double getValueTransform(const AbstractProperty& p, int index) 359 { 360 const PropertyTransform& pd = dynamic_cast<const PropertyTransform&>(p); 361 double array6[] = {0., 0., 0., 0., 0., 0.}; 362 pd.getRotationsAndTranslationsAsArray6(array6); 363 return array6[index]; 364 } setValueTransform(double v,AbstractProperty & p,int index)365 static void setValueTransform(double v, AbstractProperty& p, int index) 366 { 367 PropertyTransform& pd = dynamic_cast<PropertyTransform&>(p); 368 double array6[] = {0., 0., 0., 0., 0., 0.}; 369 pd.getRotationsAndTranslationsAsArray6(array6); 370 array6[index] = v; 371 pd.setValue(6, array6); 372 } 373 //=================Vec3 Properties, treated as three Doubles ================== getValueVec3(const AbstractProperty & p,int index)374 static double getValueVec3(const AbstractProperty& p, int index) 375 { 376 const Property<SimTK::Vec3>& pd = dynamic_cast<const Property<SimTK::Vec3>&>(p); 377 const SimTK::Vec3& vec3 = pd.getValue(); 378 return vec3[index]; 379 } setValueVec3(double v,AbstractProperty & p,int index)380 static void setValueVec3(double v, AbstractProperty& p, int index) 381 { 382 Property<SimTK::Vec3>& pd = dynamic_cast<Property<SimTK::Vec3>&>(p); 383 pd.updValue()[index] = v; 384 } getValueVec6(const AbstractProperty & p,int index)385 static double getValueVec6(const AbstractProperty& p, int index) 386 { 387 const Property<SimTK::Vec6>& pd = dynamic_cast<const Property<SimTK::Vec6>&>(p); 388 const SimTK::Vec6& vec6 = pd.getValue(); 389 return vec6[index]; 390 } setValueVec6(double v,AbstractProperty & p,int index)391 static void setValueVec6(double v, AbstractProperty& p, int index) 392 { 393 Property<SimTK::Vec6>& pd = dynamic_cast<Property<SimTK::Vec6>&>(p); 394 pd.updValue()[index] = v; 395 } 396 // ================ String arrays =================================================== getValueStringArray(const AbstractProperty & p)397 static OpenSim::Array<std::string> getValueStringArray(const AbstractProperty& p) 398 { 399 OpenSim::Array<std::string> val = OpenSim::Array<std::string>(); 400 for (int i=0; i< p.size(); i++) 401 val.append(p.getValue<std::string>(i)); 402 return val; 403 } setValueStringArray(AbstractProperty & p,OpenSim::Array<std::string> & aStringArray)404 static void setValueStringArray(AbstractProperty& p, OpenSim::Array<std::string>& aStringArray) 405 { 406 p.clear(); 407 for (int i=0; i< aStringArray.getSize(); i++) 408 try { 409 p.appendValue<std::string>(aStringArray.get(i)); 410 } catch (OpenSim::Exception e) { 411 OpenSim::Exception ex("ERROR- Invalid input (invalid character/spaces in input string)"); 412 throw ex; 413 } 414 } 415 removeItem(AbstractProperty & p,int index)416 static void removeItem(AbstractProperty& p, int index) 417 { 418 if (p.size()>index){ 419 AbstractProperty* cloneP = p.clone(); 420 p.clear(); 421 for(int i=0; i<cloneP->size();i++){ 422 if (i!= index){ 423 if (p.getTypeName()=="string") 424 p.appendValue(cloneP->getValue<std::string>(i)); 425 else if (p.getTypeName()=="int") 426 p.appendValue(cloneP->getValue<int>(i)); 427 else if (p.getTypeName()=="double") 428 p.appendValue(cloneP->getValue<double>(i)); 429 else if (p.getTypeName()=="bool") 430 p.appendValue(cloneP->getValue<bool>(i)); 431 } 432 } 433 } 434 } 435 436 }; 437 438 } // namespace OpenSim 439 440 #endif // OPENSIM_OPENSIM_CONTEXT_H_ 441 442