/* -------------------------------------------------------------------------- * * OpenSim: SmoothSegmentedFunctionFactory.cpp * * -------------------------------------------------------------------------- * * The OpenSim API is a toolkit for musculoskeletal modeling and simulation. * * See http://opensim.stanford.edu and the NOTICE file for more information. * * OpenSim is developed at Stanford University and supported by the US * * National Institutes of Health (U54 GM072970, R24 HD065690) and by DARPA * * through the Warrior Web program. * * * * Copyright (c) 2005-2012 Stanford University and the Authors * * Author(s): Matthew Millard * * * * Licensed under the Apache License, Version 2.0 (the "License"); you may * * not use this file except in compliance with the License. You may obtain a * * copy of the License at http://www.apache.org/licenses/LICENSE-2.0. * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * * -------------------------------------------------------------------------- */ /* Update: This is a port of the original code so that it will work with the multibody code RBDL written by Martin Felis. Author: Matthew Millard Date: Nov 2015 */ //============================================================================= // INCLUDES //============================================================================= #include "MuscleFunctionFactory.h" #include #include #include #include #include using namespace std; using namespace RigidBodyDynamics::Addons::Muscle; using namespace RigidBodyDynamics::Addons::Geometry; //============================================================================= // STATICS //============================================================================= //using namespace std; static int NUM_SAMPLE_PTS = 100; //The number of knot points to use to sample //each Bezier corner section static double SMOOTHING = 0; //The amount of smoothing to use when fitting //3rd order splines to the quintic Bezier //functions static bool DEBUG = true; //When this is set to true, each function's debug //routine will be called, which ususally results //in a text file of its output being produced static double UTOL = (double)std::numeric_limits::epsilon()*1e2; static double INTTOL = (double)std::numeric_limits::epsilon()*1e4; static int MAXITER = 20; //============================================================================= // UTILITY FUNCTIONS //============================================================================= //============================================================================= // MUSCLE CURVE FITTING FUNCTIONS //============================================================================= void MuscleFunctionFactory::createFiberActiveForceLengthCurve( double x0, double x1, double x2, double x3, double ylow, double dydx, double curviness, const std::string& curveName, SmoothSegmentedFunction& smoothSegmentedFunctionToUpdate) { //Ensure that the inputs are within a valid range double rootEPS = sqrt(std::numeric_limits::epsilon()); if( (!(x0>=0 && x1>x0+rootEPS && x2>x1+rootEPS && x3>x2+rootEPS) ) ){ cerr << "MuscleFunctionFactory::" << "createFiberActiveForceLengthCurve: " << curveName << ": This must be true: 0 < lce0 < lce1 < lce2 < lce3" << endl; assert(0); abort(); } if( !(ylow >= 0) ){ cerr << "MuscleFunctionFactory::" << "createFiberActiveForceLengthCurve:" << curveName <<": shoulderVal must be greater than, or equal to 0" << endl; assert(0); abort(); } double dydxUpperBound = (1-ylow)/(x2-x1); if( !(dydx >= 0 && dydx < dydxUpperBound) ){ cerr << "MuscleFunctionFactory::" << "createFiberActiveForceLengthCurve:" << curveName << ": plateauSlope must be greater than 0 and less than " << dydxUpperBound << endl; assert(0); abort(); } if( !(curviness >= 0 && curviness <= 1) ){ cerr << "MuscleFunctionFactory::" << "createFiberActiveForceLengthCurve:" << curveName << ": curviness must be between 0 and 1" << endl; assert(0); abort(); } std::string name = curveName; name.append(".createFiberActiveForceLengthCurve"); //Translate the users parameters into Bezier curves double c = SegmentedQuinticBezierToolkit::scaleCurviness(curviness); //The active force length curve is made up of 5 elbow shaped sections. //Compute the locations of the joining point of each elbow section. //Calculate the location of the shoulder double xDelta = 0.05*x2; //half the width of the sarcomere 0.0259, //but TM.Winter's data has a wider shoulder than //this double xs = (x2-xDelta);//x1 + 0.75*(x2-x1); //Calculate the intermediate points located on the ascending limb double y0 = 0; double dydx0 = 0; double y1 = 1 - dydx*(xs-x1); double dydx01= 1.25*(y1-y0)/(x1-x0);//(y1-y0)/(x1-(x0+xDelta)); double x01 = x0 + 0.5*(x1-x0); //x0 + xDelta + 0.5*(x1-(x0+xDelta)); double y01 = y0 + 0.5*(y1-y0); //Calculate the intermediate points of the shallow ascending plateau double x1s = x1 + 0.5*(xs-x1); double y1s = y1 + 0.5*(1-y1); double dydx1s= dydx; //double dydx01c0 = 0.5*(y1s-y01)/(x1s-x01) + 0.5*(y01-y0)/(x01-x0); //double dydx01c1 = 2*( (y1-y0)/(x1-x0)); //double dydx01(1-c)*dydx01c0 + c*dydx01c1; //x2 entered double y2 = 1; double dydx2 = 0; //Descending limb //x3 entered double y3 = 0; double dydx3 = 0; double x23 = (x2+xDelta) + 0.5*(x3-(x2+xDelta)); //x2 + 0.5*(x3-x2); double y23 = y2 + 0.5*(y3-y2); //double dydx23c0 = 0.5*((y23-y2)/(x23-x2)) + 0.5*((y3-y23)/(x3-x23)); //double dydx23c1 = 2*(y3-y2)/(x3-x2); double dydx23 = (y3-y2)/((x3-xDelta)-(x2+xDelta)); //(1-c)*dydx23c0 + c*dydx23c1; //Compute the locations of the control points RigidBodyDynamics::Math::MatrixNd p0 = SegmentedQuinticBezierToolkit:: calcQuinticBezierCornerControlPoints(x0,ylow,dydx0,x01,y01,dydx01,c); RigidBodyDynamics::Math::MatrixNd p1 = SegmentedQuinticBezierToolkit:: calcQuinticBezierCornerControlPoints(x01,y01,dydx01,x1s,y1s,dydx1s,c); RigidBodyDynamics::Math::MatrixNd p2 = SegmentedQuinticBezierToolkit:: calcQuinticBezierCornerControlPoints(x1s,y1s,dydx1s,x2, y2, dydx2,c); RigidBodyDynamics::Math::MatrixNd p3 = SegmentedQuinticBezierToolkit:: calcQuinticBezierCornerControlPoints(x2, y2, dydx2,x23,y23,dydx23,c); RigidBodyDynamics::Math::MatrixNd p4 = SegmentedQuinticBezierToolkit:: calcQuinticBezierCornerControlPoints(x23,y23,dydx23,x3,ylow,dydx3,c); RigidBodyDynamics::Math::MatrixNd mX(6,5), mY(6,5); mX.col(0) = p0.col(0); mX.col(1) = p1.col(0); mX.col(2) = p2.col(0); mX.col(3) = p3.col(0); mX.col(4) = p4.col(0); mY.col(0) = p0.col(1); mY.col(1) = p1.col(1); mY.col(2) = p2.col(1); mY.col(3) = p3.col(1); mY.col(4) = p4.col(1); smoothSegmentedFunctionToUpdate.updSmoothSegmentedFunction( mX,mY,x0,x3,ylow,ylow,0,0,curveName); } void MuscleFunctionFactory::createFiberForceVelocityCurve( double fmaxE, double dydxC, double dydxNearC, double dydxIso, double dydxE, double dydxNearE, double concCurviness, double eccCurviness, const std::string& curveName, SmoothSegmentedFunction& smoothSegmentedFunctionToUpdate) { //Ensure that the inputs are within a valid range if( !(fmaxE > 1.0) ){ cerr << "MuscleFunctionFactory::" << "createFiberForceVelocityCurve: " << curveName <<": fmaxE must be greater than 1" << endl; assert(0); abort(); } if( !(dydxC >= 0.0 && dydxC < 1) ){ cerr << "MuscleFunctionFactory::" << "createFiberForceVelocityCurve: " << curveName << ": dydxC must be greater than or equal to 0 " <<" and less than 1" << endl; assert(0); abort(); } if( !(dydxNearC > dydxC && dydxNearC <= 1) ){ cerr << "MuscleFunctionFactory::" << "createFiberForceVelocityCurve: " << curveName << ": dydxNearC must be greater than or equal to 0 " << "and less than 1" << endl; assert(0); abort(); } if( !(dydxIso > 1) ){ cerr << "MuscleFunctionFactory::" << "createFiberForceVelocityCurve: " << curveName << ": dydxIso must be greater than (fmaxE-1)/1 (" << ((fmaxE-1.0)/1.0) << ")" << endl; assert(0); abort(); } if( !(dydxE >= 0.0 && dydxE < (fmaxE-1)) ){ cerr << "MuscleFunctionFactory::" << "createFiberForceVelocityCurve: " << curveName <<": dydxE must be greater than or equal to 0 " << "and less than fmaxE-1 (" << (fmaxE-1) << ")" << endl; assert(0); abort(); } if(!(dydxNearE >= dydxE && dydxNearE < (fmaxE-1))){ cerr << "MuscleFunctionFactory::" << "createFiberForceVelocityCurve" << curveName << ": dydxNearE must be greater than or equal to dydxE " << "and less than fmaxE-1 (" << (fmaxE-1) << ")" << endl; assert(0); abort(); } if(! (concCurviness <= 1.0 && concCurviness >= 0)){ cerr << "MuscleFunctionFactory::" << "createFiberForceVelocityCurve " << curveName << ": concCurviness must be between 0 and 1" << endl; assert(0); abort(); } if(! (eccCurviness <= 1.0 && eccCurviness >= 0)){ cerr << "MuscleFunctionFactory::" << "createFiberForceVelocityCurve " << curveName << ": eccCurviness must be between 0 and 1" << endl; assert(0); abort(); } std::string name = curveName; name.append(".createFiberForceVelocityCurve"); //Translate the users parameters into Bezier point locations double cC = SegmentedQuinticBezierToolkit::scaleCurviness(concCurviness); double cE = SegmentedQuinticBezierToolkit::scaleCurviness(eccCurviness); //Compute the concentric control point locations double xC = -1; double yC = 0; double xNearC = -0.9; double yNearC = yC + 0.5*dydxNearC*(xNearC-xC) + 0.5*dydxC*(xNearC-xC); double xIso = 0; double yIso = 1; double xE = 1; double yE = fmaxE; double xNearE = 0.9; double yNearE = yE + 0.5*dydxNearE*(xNearE-xE) + 0.5*dydxE*(xNearE-xE); RigidBodyDynamics::Math::MatrixNd concPts1 = SegmentedQuinticBezierToolkit:: calcQuinticBezierCornerControlPoints( xC, yC, dydxC, xNearC, yNearC,dydxNearC,cC); RigidBodyDynamics::Math::MatrixNd concPts2 = SegmentedQuinticBezierToolkit:: calcQuinticBezierCornerControlPoints(xNearC,yNearC,dydxNearC, xIso, yIso, dydxIso, cC); RigidBodyDynamics::Math::MatrixNd eccPts1 = SegmentedQuinticBezierToolkit:: calcQuinticBezierCornerControlPoints( xIso, yIso, dydxIso, xNearE, yNearE, dydxNearE, cE); RigidBodyDynamics::Math::MatrixNd eccPts2 = SegmentedQuinticBezierToolkit:: calcQuinticBezierCornerControlPoints(xNearE, yNearE, dydxNearE, xE, yE, dydxE, cE); RigidBodyDynamics::Math::MatrixNd mX(6,4), mY(6,4); mX.col(0) = concPts1.col(0); mX.col(1) = concPts2.col(0); mX.col(2) = eccPts1.col(0); mX.col(3) = eccPts2.col(0); mY.col(0) = concPts1.col(1); mY.col(1) = concPts2.col(1); mY.col(2) = eccPts1.col(1); mY.col(3) = eccPts2.col(1); smoothSegmentedFunctionToUpdate.updSmoothSegmentedFunction( mX,mY,xC,xE,yC,yE,dydxC,dydxE,curveName); } void MuscleFunctionFactory::createFiberForceVelocityInverseCurve( double fmaxE, double dydxC, double dydxNearC, double dydxIso, double dydxE, double dydxNearE, double concCurviness, double eccCurviness, const std::string& curveName, SmoothSegmentedFunction& smoothSegmentedFunctionToUpdate) { //Ensure that the inputs are within a valid range if(! (fmaxE > 1.0 )){ cerr << "MuscleFunctionFactory::" << "createFiberForceVelocityInverseCurve: " << curveName << ": fmaxE must be greater than 1" << endl; assert(0); abort(); } double SimTKSignificantReal = pow((double)std::numeric_limits::epsilon(), 7.0/8.0); if(! (dydxC > SimTKSignificantReal && dydxC < 1 )){ cerr << "MuscleFunctionFactory::" << "createFiberForceVelocityInverseCurve " << curveName << ": dydxC must be greater than 0" << "and less than 1" << endl; assert(0); abort(); } if(! (dydxNearC > dydxC && dydxNearC < 1 )){ std::stringstream errMsg; cerr << "MuscleFunctionFactory::" << "createFiberForceVelocityInverseCurve " << ": dydxNearC must be greater than 0 " << curveName << " and less than 1" << endl; assert(0); abort(); } if(! (dydxIso > 1)){ cerr << "MuscleFunctionFactory::" << "createFiberForceVelocityInverseCurve " << curveName << ": dydxIso must be greater than or equal to 1" << endl; assert(0); abort(); } //double SimTKSignificantReal = // pow(std::numeric_limits::epsilon(), 7.0/8.0); if(! (dydxE > SimTKSignificantReal && dydxE < (fmaxE-1)) ){ cerr << "MuscleFunctionFactory::" << "createFiberForceVelocityInverseCurve " << curveName << ": dydxE must be greater than or equal to 0" << " and less than fmaxE-1 (" << (fmaxE-1) << ")" << endl; assert(0); abort(); } if(! (dydxNearE >= dydxE && dydxNearE < (fmaxE-1)) ){ cerr << "MuscleFunctionFactory::" << "createFiberForceVelocityInverseCurve " << curveName << ": dydxNearE must be greater than or equal to dydxE" << "and less than fmaxE-1 ("<< (fmaxE-1) << ")" << endl; assert(0); abort(); } if(! (concCurviness <= 1.0 && concCurviness >= 0) ){ cerr << "MuscleFunctionFactory::" << "createFiberForceVelocityInverseCurve " << curveName << ": concCurviness must be between 0 and 1" << endl; assert(0); abort(); } if(! (eccCurviness <= 1.0 && eccCurviness >= 0) ){ cerr << "MuscleFunctionFactory::" << "createFiberForceVelocityInverseCurve " << curveName << ": eccCurviness must be between 0 and 1" << endl; assert(0); abort(); } std::string name = curveName; name.append(".createFiberForceVelocityInverseCurve"); //Translate the users parameters into Bezier point locations double cC = SegmentedQuinticBezierToolkit::scaleCurviness(concCurviness); double cE = SegmentedQuinticBezierToolkit::scaleCurviness(eccCurviness); //Compute the concentric control point locations double xC = -1; double yC = 0; double xNearC = -0.9; double yNearC = yC + 0.5*dydxNearC*(xNearC-xC) + 0.5*dydxC*(xNearC-xC); double xIso = 0; double yIso = 1; double xE = 1; double yE = fmaxE; double xNearE = 0.9; double yNearE = yE + 0.5*dydxNearE*(xNearE-xE) + 0.5*dydxE*(xNearE-xE); RigidBodyDynamics::Math::MatrixNd concPts1 = SegmentedQuinticBezierToolkit:: calcQuinticBezierCornerControlPoints( xC, yC, dydxC, xNearC, yNearC,dydxNearC,cC); RigidBodyDynamics::Math::MatrixNd concPts2 = SegmentedQuinticBezierToolkit:: calcQuinticBezierCornerControlPoints(xNearC,yNearC,dydxNearC, xIso, yIso, dydxIso, cC); RigidBodyDynamics::Math::MatrixNd eccPts1 = SegmentedQuinticBezierToolkit:: calcQuinticBezierCornerControlPoints( xIso, yIso, dydxIso, xNearE, yNearE, dydxNearE, cE); RigidBodyDynamics::Math::MatrixNd eccPts2 = SegmentedQuinticBezierToolkit:: calcQuinticBezierCornerControlPoints(xNearE, yNearE, dydxNearE, xE, yE, dydxE, cE); RigidBodyDynamics::Math::MatrixNd mX(6,4), mY(6,4); mX.col(0) = concPts1.col(0); mX.col(1) = concPts2.col(0); mX.col(2) = eccPts1.col(0); mX.col(3) = eccPts2.col(0); mY.col(0) = concPts1.col(1); mY.col(1) = concPts2.col(1); mY.col(2) = eccPts1.col(1); mY.col(3) = eccPts2.col(1); smoothSegmentedFunctionToUpdate.updSmoothSegmentedFunction( mY,mX,yC,yE,xC,xE,1/dydxC,1/dydxE, curveName); } void MuscleFunctionFactory::createFiberCompressiveForcePennationCurve( double phi0, double k, double curviness, const std::string& curveName, SmoothSegmentedFunction& smoothSegmentedFunctionToUpdate) { //Check the input arguments if( !(phi0>0 && phi0<(M_PI/2.0)) ){ cerr << "MuscleFunctionFactory::" << "createFiberCompressiveForcePennationCurve " << curveName << ": phi0 must be greater than 0, and less than Pi/2" << endl; assert(0); abort(); } if( !(k > (1.0/(M_PI/2.0-phi0))) ){ cerr << "MuscleFunctionFactory::" << "createFiberCompressiveForcePennationCurve " << curveName << ": k must be greater than " << (1.0/(M_PI/2.0-phi0)) << endl; assert(0); abort(); } if( !(curviness>=0 && curviness <= 1) ){ cerr << "MuscleFunctionFactory::" << "createFiberCompressiveForcePennationCurve " << curveName << ": curviness must be between 0.0 and 1.0" << endl; assert(0); abort(); } std::string name=curveName; name.append(".createFiberCompressiveForcePennationCurve"); //Translate the user parameters to quintic Bezier points double c = SegmentedQuinticBezierToolkit::scaleCurviness(curviness); double x0 = phi0; double y0 = 0; double dydx0 = 0; double x1 = M_PI/2.0; double y1 = 1; double dydx1 = k; RigidBodyDynamics::Math::MatrixNd ctrlPts = SegmentedQuinticBezierToolkit:: calcQuinticBezierCornerControlPoints(x0,y0,dydx0,x1,y1,dydx1,c); RigidBodyDynamics::Math::MatrixNd mX(6,1), mY(6,1); mX.col(0) = ctrlPts.col(0); mY.col(0) = ctrlPts.col(1); smoothSegmentedFunctionToUpdate.updSmoothSegmentedFunction( mX,mY,x0,x1,y0,y1,dydx0,dydx1,curveName); } void MuscleFunctionFactory:: createFiberCompressiveForceCosPennationCurve( double cosPhi0, double k, double curviness, const std::string& curveName, SmoothSegmentedFunction& smoothSegmentedFunctionToUpdate) { //Check the input arguments if( !(cosPhi0>0 && cosPhi0 < 1) ){ cerr << "MuscleFunctionFactory::" << "createFiberCompressiveForceCosPennationCurve " << curveName << ": cosPhi0 must be greater than 0, and less than 1" << endl; assert(0); abort(); } if( !(k < 1/cosPhi0) ){ cerr << "MuscleFunctionFactory::" << "createFiberCompressiveForceCosPennationCurve " << curveName << ": k must be less than 0" << endl; assert(0); abort(); } if( !(curviness>=0 && curviness <= 1) ){ cerr << "MuscleFunctionFactory::" << "createFiberCompressiveForceCosPennationCurve" << curveName << ": curviness must be between 0.0 and 1.0" << endl; assert(0); abort(); } std::string name=curveName; name.append(".createFiberCompressiveForceCosPennationCurve"); //Translate the user parameters to quintic Bezier points double c = SegmentedQuinticBezierToolkit::scaleCurviness(curviness); double x0 = 0; double y0 = 1; double dydx0 = k; double x1 = cosPhi0; double y1 = 0; double dydx1 = 0; RigidBodyDynamics::Math::MatrixNd ctrlPts = SegmentedQuinticBezierToolkit:: calcQuinticBezierCornerControlPoints(x0,y0,dydx0,x1,y1,dydx1,c); RigidBodyDynamics::Math::MatrixNd mX(6,1), mY(6,1); mX.col(0) = ctrlPts.col(0); mY.col(0) = ctrlPts.col(1); smoothSegmentedFunctionToUpdate.updSmoothSegmentedFunction( mX,mY,x0,x1,y0,y1,dydx0,dydx1,curveName); } void MuscleFunctionFactory::createFiberCompressiveForceLengthCurve( double lmax, double k, double curviness, const std::string& curveName, SmoothSegmentedFunction& smoothSegmentedFunctionToUpdate) { if( !(lmax>0) ){ cerr << "MuscleFunctionFactory::" << "createFiberCompressiveForceLength " << curveName << ": l0 must be greater than 0" << endl; assert(0); abort(); } if( !(k < -(1.0/lmax)) ){ cerr << "MuscleFunctionFactory::" << "createFiberCompressiveForceLength " << curveName << ": k must be less than " << -(1.0/lmax) << endl; assert(0); abort(); } if( !(curviness>=0 && curviness <= 1) ){ cerr << "MuscleFunctionFactory::" << "createFiberCompressiveForceLength " << curveName << ": curviness must be between 0.0 and 1.0" << endl; assert(0); abort(); } std::string caller = curveName; caller.append(".createFiberCompressiveForceLength"); //Translate the user parameters to quintic Bezier points double c = SegmentedQuinticBezierToolkit::scaleCurviness(curviness); double x0 = 0.0; double y0 = 1; double dydx0 = k; double x1 = lmax; double y1 = 0; double dydx1 = 0; RigidBodyDynamics::Math::MatrixNd ctrlPts = SegmentedQuinticBezierToolkit:: calcQuinticBezierCornerControlPoints(x0,y0,dydx0,x1,y1,dydx1,c); RigidBodyDynamics::Math::MatrixNd mX(6,1), mY(6,1); mX.col(0) = ctrlPts.col(0); mY.col(0) = ctrlPts.col(1); smoothSegmentedFunctionToUpdate.updSmoothSegmentedFunction( mX,mY,x0,x1,y0,y1,dydx0,dydx1,curveName); } void MuscleFunctionFactory::createFiberForceLengthCurve( double eZero, double eIso, double kLow, double kIso, double curviness, const std::string& curveName, SmoothSegmentedFunction& smoothSegmentedFunctionToUpdate) { if( !(eIso > eZero) ){ cerr << "MuscleFunctionFactory::" << "createFiberForceLength " << curveName << ": The following must hold: eIso > eZero" << endl; assert(0); abort(); } if( !(kIso > (1.0/(eIso-eZero))) ){ std::stringstream errMsg; cerr << "MuscleFunctionFactory::" << "createFiberForceLength " << curveName << ": kiso must be greater than 1/(eIso-eZero) (" << (1.0/(eIso-eZero)) << ")" << endl; assert(0); abort(); } if( !(kLow > 0.0 && kLow < 1/(eIso-eZero)) ){ cerr << "MuscleFunctionFactory::" << "createFiberForceLength " << curveName << ": kLow must be greater than 0 and less than" << 1.0/(eIso-eZero) << endl; assert(0); abort(); } if( !(curviness>=0 && curviness <= 1) ){ cerr << "MuscleFunctionFactory::" << "createFiberForceLength " << curveName << ": curviness must be between 0.0 and 1.0" << endl; assert(0); abort(); } std::string callerName = curveName; callerName.append(".createFiberForceLength"); //Translate the user parameters to quintic Bezier points double c = SegmentedQuinticBezierToolkit::scaleCurviness(curviness); double xZero = 1+eZero; double yZero = 0; double xIso = 1 + eIso; double yIso = 1; double deltaX = std::min(0.1*(1.0/kIso), 0.1*(xIso-xZero)); double xLow = xZero + deltaX; double xfoot = xZero + 0.5*(xLow-xZero); double yfoot = 0; double yLow = yfoot + kLow*(xLow-xfoot); //Compute the Quintic Bezier control points RigidBodyDynamics::Math::MatrixNd p0 = SegmentedQuinticBezierToolkit:: calcQuinticBezierCornerControlPoints(xZero, yZero, 0, xLow, yLow, kLow,c); RigidBodyDynamics::Math::MatrixNd p1 = SegmentedQuinticBezierToolkit:: calcQuinticBezierCornerControlPoints(xLow, yLow, kLow, xIso, yIso, kIso, c); RigidBodyDynamics::Math::MatrixNd mX(6,2); RigidBodyDynamics::Math::MatrixNd mY(6,2); mX.col(0) = p0.col(0); mY.col(0) = p0.col(1); mX.col(1) = p1.col(0); mY.col(1) = p1.col(1); smoothSegmentedFunctionToUpdate.updSmoothSegmentedFunction( mX, mY, xZero, xIso, yZero, yIso, 0.0, kIso, curveName); } void MuscleFunctionFactory:: createTendonForceLengthCurve( double eIso, double kIso, double fToe, double curviness, const std::string& curveName, SmoothSegmentedFunction& smoothSegmentedFunctionToUpdate) { if( !(eIso>0) ){ cerr << "MuscleFunctionFactory::" << "createTendonForceLengthCurve " << curveName << ": eIso must be greater than 0, but " << eIso << " was entered" << endl; assert(0); abort(); } if( !(fToe>0 && fToe < 1) ){ cerr << "MuscleFunctionFactory::" << "createTendonForceLengthCurve " << curveName << ": fToe must be greater than 0 and less than 1, but " << fToe << " was entered" << endl; assert(0); abort(); } if( !(kIso > (1/eIso)) ){ cerr << "MuscleFunctionFactory::" << "createTendonForceLengthCurve " << curveName << ": kIso must be greater than 1/eIso, (" << (1/eIso) << "), but kIso (" << kIso << ") was entered" << endl; assert(0); abort(); } if( !(curviness>=0 && curviness <= 1) ){ cerr << "MuscleFunctionFactory::" << "createTendonForceLengthCurve " << curveName << " : curviness must be between 0.0 and 1.0, but " << curviness << " was entered" << endl; assert(0); abort(); } std::string callerName = curveName; callerName.append(".createTendonForceLengthCurve"); //Translate the user parameters to quintic Bezier points double c = SegmentedQuinticBezierToolkit::scaleCurviness(curviness); double x0 = 1.0; double y0 = 0; double dydx0 = 0; double xIso = 1.0 + eIso; double yIso = 1; double dydxIso = kIso; //Location where the curved section becomes linear double yToe = fToe; double xToe = (yToe-1)/kIso + xIso; //To limit the 2nd derivative of the toe region the line it tends to //has to intersect the x axis to the right of the origin double xFoot = 1.0+(xToe-1.0)/10.0; double yFoot = 0; double dydxToe = (yToe-yFoot)/(xToe-xFoot); //Compute the location of the corner formed by the average slope of the //toe and the slope of the linear section double yToeMid = yToe*0.5; double xToeMid = (yToeMid-yIso)/kIso + xIso; double dydxToeMid = (yToeMid-yFoot)/(xToeMid-xFoot); //Compute the location of the control point to the left of the corner double xToeCtrl = xFoot + 0.5*(xToeMid-xFoot); double yToeCtrl = yFoot + dydxToeMid*(xToeCtrl-xFoot); //Compute the Quintic Bezier control points RigidBodyDynamics::Math::MatrixNd p0 = SegmentedQuinticBezierToolkit:: calcQuinticBezierCornerControlPoints(x0,y0,dydx0, xToeCtrl,yToeCtrl,dydxToeMid,c); RigidBodyDynamics::Math::MatrixNd p1 = SegmentedQuinticBezierToolkit:: calcQuinticBezierCornerControlPoints(xToeCtrl, yToeCtrl, dydxToeMid, xToe, yToe, dydxIso, c); RigidBodyDynamics::Math::MatrixNd mX(6,2); RigidBodyDynamics::Math::MatrixNd mY(6,2); mX.col(0) = p0.col(0); mY.col(0) = p0.col(1); mX.col(1) = p1.col(0); mY.col(1) = p1.col(1); smoothSegmentedFunctionToUpdate.updSmoothSegmentedFunction( mX, mY, x0, xToe, y0, yToe, dydx0, dydxIso, curveName); }