[770c98]: / Tools / AnyMocap / ForcePlates / ForcePlateAutoDetection.any

Download this file

697 lines (576 with data), 24.9 kB

#ifndef _ANYMOCAP_FORCE_PLATE_AUTO_DETECTION_ANY_
#define _ANYMOCAP_FORCE_PLATE_AUTO_DETECTION_ANY_
/*
---
group: MoCap
topic: Force plates
descr: Create Force plates based on data in C3D file.
---

See below for more details

*/

#include "ForcePlateTypes.any"


// Class template to create a force plates from measured data (in c3d files).
// 
// The class automatically links the force to the foot closest and stationary 
// on the force plate. 
// 
// The class can also be used for other limbs. 
// 
// :::{tip} If you have a model with only one leg, or want to ensure that only one leg
// contacts the plate then set LIMB1 and LIMB2 to the same segment
// :::
//
// :::{rubric} Example
// :::
// 
// ```{code-block} AnyScriptDoc
//
// ForcePlateAutoDetection Plate1(
//   PLATE_NO=1,
//   LIMB1 = Main.HumanModel.BodyModel.Right.Leg.Seg.Foot,
//   LIMB2 = Main.HumanModel.BodyModel.Left.Leg.Seg.Foot,
//   HeightTolerance = 0.07,
//   VelThreshold = 2.2,
//   FORCEPLATE_TYPE = 4,
//   ALLOW_MULTI_LIMB_CONTACT = OFF
// ) = 
// {
//   SurfaceOffset = {0, 0.1, 0};
// };
//
//```
//
#class_template ForcePlateAutoDetection (
    PLATE_NO,
    FORCEPLATE_TYPE,
    LIMB1 = Main.HumanModel.BodyModel.Right.Leg.Seg.Foot,
    LIMB2 = Main.HumanModel.BodyModel.Left.Leg.Seg.Foot,
    VerticalDirection = "Automatic",
    HeightTolerance = 0.07,
    VelThreshold = 2.2, 
    ALLOW_MULTI_LIMB_CONTACT = 1,
    CONTACT_NODE1 = HeelContactNodeLow,
    CONTACT_NODE2 = ToeLateralContactNode,
    CREATE_KIN_GRF = 0,
    KIN_GRF_LIMB1_FUN = GlobalCopFun,
    KIN_GRF_LIMB2_FUN = GlobalCopFun,
    DRAW_RAW_FORCES = 1,
    C3D_OBJECT = Main.ModelSetup.C3DFileData,
)
{

  // Class template arguments:
  // -------------------------
  // ForcePlateAutoDetection#PLATE_NO
  //   The plate number in the C3D file (1..N)
  // ForcePlateAutoDetection#FORCEPLATE_TYPE
  //   The type of force plate (from the C3D spec.) (1..4)
  // ForcePlateAutoDetection#LIMB1
  //   Location of the limb 1 (defaults to: Main.HumanModel.BodyModel.Right.Leg.Seg.Foot)
  // ForcePlateAutoDetection#LIMB2
  //   Location of the limb 2 (defaults to: Main.HumanModel.BodyModel.Left.Leg.Seg.Foot)
  // ForcePlateAutoDetection#VerticalDirection
  //   The vertical direction of the force plate. 
  //   Possible values are "X", "Y", "Z", "Automatic".
  //   The default is "Automatic" which will use the direction of the force plate
  //   normal vector.
  // ForcePlateAutoDetection#HeightTolerance
  //   The height tolerance for contact detection with the foot. (Defaults to 0.07 m).
  // ForcePlateAutoDetection#VelThreshold
  //   The velocity threshold for contact detection with the foot. (Defaults to 2.2 m/s).
  // ForcePlateAutoDetection#ALLOW_MULTI_LIMB_CONTACT
  //  Switch to specify if both LIMB1 and LIMB2 can be in contact with the plate simultaneously.
  //  If set to OFF only the limb with lowest velocity will be assumed in contact. 
  //  Set this to OFF to prevent false contact detection in the swing phase from 
  //  the collateral limb. (Defaults to ON) 
  // ForcePlateAutoDetection#CONTACT_NODE1
  //   The node on LIMB1/2 used for contact detection. Change this if you are connecting
  //   to something different than the feet. (Defaults to: HeelContactNodeLow)
  // ForcePlateAutoDetection#CONTACT_NODE2
  //   The node on LIMB1/2 used for contact detection. Change this if you are connecting
  //   to something different than the feet. (Defaults to: ToeLateralContactNode)
  // ForcePlateAutoDetection#CREATE_KIN_GRF
  //   Create a kinematic representation of the ground reaction force vector. This is experimental 
  //   but could be used to include the GRF vector in the parameter optimization. (Defaults to OFF)
  // ForcePlateAutoDetection#KIN_GRF_LIMB1_FUN
  //   Switch to customize the kinematic GRF vector representation. Experimental. (Defaults to GlobalCopFun)
  // ForcePlateAutoDetection#KIN_GRF_LIMB2_FUN
  //   Switch to customize the kinematic GRF vector representation. Experimental. (Defaults to GlobalCopFun)
  // ForcePlateAutoDetection#DRAW_RAW_FORCES
  //   Switch to draw the raw forces from the C3D file. (Defaults to ON)
  // ForcePlateAutoDetection#C3D_OBJECT
  //   The c3d object in the model tree


  AnyComponentDefinition obj = {};  
  AnyFolder ForcePlateInfo = 
  {
    #var AnyInt USED = reshape(C3D_OBJECT.Groups.FORCE_PLATFORM.USED.Data, repmat(0,0));
    #var AnyInt TYPE = reshape(C3D_OBJECT.Groups.FORCE_PLATFORM.TYPE.Data, {USED});
    #var AnyFloat CORNERS = reshape(C3D_OBJECT.Groups.FORCE_PLATFORM.CORNERS.Data, {USED, 4, 3});
    #var AnyFloat ORIGIN = reshape(C3D_OBJECT.Groups.FORCE_PLATFORM.ORIGIN.Data, {USED, 3});
    #var AnyIntVar MaxChannels = iffun(sum(eqfun(3, TYPE)), 8, 6);
    #var AnyInt CHANNEL = reshape(C3D_OBJECT.Groups.FORCE_PLATFORM.CHANNEL.Data, {USED, MaxChannels});  
  };
  
  AnyInt ForcePlateType = ForcePlateInfo.TYPE[PLATE_NO-1];
  
  
  #var AnyVar ColorScale = max({min({InContactOnOff *ForcePlate.OnOff*(1* ForcePlate.LoadRatio),1}),0});
  
      
  /// Checks that the FORCEPLATE_TYPE match the specification in the C3D file.   
  AnyMessage PlateTypeCheck  = {
      AnyInt test = max(neqfun(.ForcePlateType, FORCEPLATE_TYPE));
      TriggerPreProcess = test;
      Type = MSG_ErrorFatal ;
      Message = strformat("AnyMOCAP: Wrong force plate type:\n"+
	            "C3D file reports force plate as beeing type " +
				strval(.ForcePlateInfo.TYPE[PLATE_NO-1]) + 
				" not type: " + strval(FORCEPLATE_TYPE));
   };  
   
    
// Include code specific for the different force plates. 
// The classes provide the following:
   // ForcePlate.LoadRatio: A ratio with which the force plate is loaded.
   // ForcePlate.OnOff: A threshold variable that specifies when the there is force on the force plate
   // ForcePlate.NetEffectMeasure: A moment measure that sums up forces on the force plate
   // ForcePlate.ForcePlateSeg:  A segment with all measured forces applied to it.  
  
  #if FORCEPLATE_TYPE == 1
    type_1_class ForcePlate(
       C3D_OBJECT=C3D_OBJECT,
       PLATEIDX=PLATE_NO-1,
       DRAW_RAW_FORCES=DRAW_RAW_FORCES) = 
    {
      Time = .Time;
    };
  #endif    
  
  #if FORCEPLATE_TYPE == 4 | FORCEPLATE_TYPE == 2
    type_2_4_class ForcePlate(
      C3D_OBJECT=C3D_OBJECT,
      PLATEIDX=PLATE_NO-1,
      FORCEPLATE_TYPE=FORCEPLATE_TYPE,
      DRAW_RAW_FORCES=DRAW_RAW_FORCES) = 
    {
      Time = .Time;
    };
  #endif
  
  #if FORCEPLATE_TYPE == 3 
    type_3_class ForcePlate(
       C3D_OBJECT=C3D_OBJECT,
       PLATEIDX=PLATE_NO-1,
       DRAW_RAW_FORCES=DRAW_RAW_FORCES) = 
    {
      Time = .Time;
    };
  #endif
  
  
  AnyRefFrame &Limb1 = LIMB1;
  AnyRefFrame &Limb2 = LIMB2;
  
  
  #var AnySwitch Switch_DrawForceVectorFromCOP = On;
  
  // ForcePlateAutoDetection 
  /// Specifies the velocity of the ground frame.
  /// Modify the value if the subject is for example walking on a treadmill 
  #var AnyVec3 GroundVelocity = {0, 0, 0};

  // ForcePlateAutoDetection 
  /// Offset of the plate surface. Useful if stairs or other equipment
  /// is placed on the force plate and center of pressure should be 
  /// calculated and visualized on this offset surface.
  #var AnyVec3 SurfaceOffset = {0,0,0};
  
  #var AnyVec3 SurfaceCorner_c01 = Corners.Origin + Corners.c01.sRel * Corners.Axes' + SurfaceOffset;
  #var AnyVec3 SurfaceCorner_c02 = Corners.Origin + Corners.c02.sRel * Corners.Axes' + SurfaceOffset;
  #var AnyVec3 SurfaceCorner_c03 = Corners.Origin + Corners.c03.sRel * Corners.Axes' + SurfaceOffset;
  #var AnyVec3 SurfaceCorner_c04 = Corners.Origin + Corners.c04.sRel * Corners.Axes' + SurfaceOffset;
  
  AnyFixedRefFrame Corners ={
    /// Used by AnyMoCap to check that gravity is consistant with the plate
    AnyVec3 DownDirection = RotMat(
       0.25*(c01.cpoint+ c02.cpoint+ c03.cpoint+ c04.cpoint),
       0.5*(c01.cpoint+c04.cpoint),
       0.5*(c01.cpoint+c02.cpoint))'[2];
    AnyVar CoordinateSystemSize= 0.1;
    #var AnyInt plate_idx=PLATE_NO-1;
    AnyRefNode c01={
      #var AnyInt i=0;
      AnyVec3 cpoint = C3D_OBJECT.PointsScaleFactor*{
          ..ForcePlateInfo.CORNERS[.plate_idx][i][0],
          ..ForcePlateInfo.CORNERS[.plate_idx][i][1],
          ..ForcePlateInfo.CORNERS[.plate_idx][i][2]
      };
      #var sRel = cpoint;
      AnyDrawNode drw={ScaleXYZ=0.01*{1,1,1};RGB={1,0,0};};
      AnyDrawVector  DrawName = {
        Vec = {0.0,0,0};  //use zero length
        Line.Thickness = 0.025; //arbitrary value
        Text = "1"; //make reference to name
        Line.RGB={1,0,0};//make reference to color
        #var Visible = Off;
      };
    };
    
    AnyRefNode c02={
      #var AnyInt i=1;
      AnyVec3 cpoint = C3D_OBJECT.PointsScaleFactor*{
          ..ForcePlateInfo.CORNERS[.plate_idx][i][0],
          ..ForcePlateInfo.CORNERS[.plate_idx][i][1],
          ..ForcePlateInfo.CORNERS[.plate_idx][i][2]
      };
      #var sRel=cpoint;
      AnyDrawNode drw={ScaleXYZ=0.01*{1,1,1};RGB={1,0,0};};
      AnyDrawVector  DrawName = {
        Vec = {0.0,0,0};  //use zero length
        Line.Thickness = 0.025; //arbitrary value
        Text = "2"; //make reference to name
        Line.RGB={1,0,0};//make reference to color
        #var Visible = Off;
      };
    };
    
    AnyRefNode c03={
      #var AnyInt i=2;
      AnyVec3 cpoint = C3D_OBJECT.PointsScaleFactor*{
          ..ForcePlateInfo.CORNERS[.plate_idx][i][0],
          ..ForcePlateInfo.CORNERS[.plate_idx][i][1],
          ..ForcePlateInfo.CORNERS[.plate_idx][i][2]
      };
      #var sRel = cpoint;
      AnyDrawNode drw={ScaleXYZ=0.01*{1,1,1};RGB={1,0,0};};
      
      AnyDrawVector  DrawName = {
        Vec = {0.0,0,0};  //use zero length
        Line.Thickness = 0.025; //arbitrary value
        Text = "3"; //make reference to name
        Line.RGB={1,0,0};//make reference to color 
        #var Visible = Off;
      };
    };
    
    AnyRefNode c04={
      #var AnyInt i=3;
      AnyVec3 cpoint = C3D_OBJECT.PointsScaleFactor*{
          ..ForcePlateInfo.CORNERS[.plate_idx][i][0],
          ..ForcePlateInfo.CORNERS[.plate_idx][i][1],
          ..ForcePlateInfo.CORNERS[.plate_idx][i][2]
      };
      #var sRel = cpoint;
      AnyDrawNode drw={ScaleXYZ=0.01*{1,1,1};RGB={1,0,0};};
      AnyDrawVector  DrawName = {
        Vec = {0.0,0,0};  //use zero length
        Line.Thickness = 0.025; //arbitrary value
        Text = "4"; //make reference to name
        Line.RGB={1,0,0};//make reference to color
        #var Visible = Off;
      };
    };
    
    AnyRefNode PlateCenter={
      sRel=0.25*(.c01.sRel+.c02.sRel+.c03.sRel+.c04.sRel);
      AnyVec3 p1=sRel;
      AnyVec3 p2=0.5*(.c01.sRel+.c04.sRel);
      AnyVec3 p3=0.5*(.c01.sRel+.c02.sRel);
      ARel =RotMat(p1,p2,p3);
      //AnyRefNode PlateNumber = 
      //{        
      //  sRel = {-0.08 ,0.1,-0.001};
      //  AnyDrawVector  DrawName =
      //  {  
      //    TextFont.FontName ="Arial";
      //    GlobalCoord = Off;
      //    TextFont.RGB = {0.9,0.9,0.9};
      //    Vec = {0.0, 0.0, 0.0};  //use zero length
      //    Line.Thickness = 1; //arbitrary value
      //    Text = #PLATE_NO; //make reference to name
      //    Line.RGB={1,0,0};//make reference to color
      //    #var Visible = On;
      //    TextRelPos = 0;
      //    TextFont.ModelSized = On;
      //    TextFont.BillBoardView = Off;
      //    TextFont.ModelScaleHeight = 0.2;
      //    TextFont.ModelScaleWidth = 0.2;
      //   DrawCoord =On;
      //  };
      //};
    };
  };
  

  ForcePlate.ForcePlateSeg = {
    AnyRefNode PlateSurface = {
      sRel = ( 0.25*(
               ...SurfaceCorner_c01 +
               ...SurfaceCorner_c02 +
               ...SurfaceCorner_c03 +
               ...SurfaceCorner_c04 ) - .r0)*.Axes0;
        
        
      AnyRefNode SurfaceGraphics = {
        #var AnyVar VisualPlateHeight = max({0.00001, C3D_OBJECT.PointsScaleFactor*abs(....ForcePlateInfo.ORIGIN[PLATE_NO-1][2])});
        sRel={0,0,VisualPlateHeight/2};
        AnyVec3 Size={
          vnorm( ....SurfaceCorner_c01 - ....SurfaceCorner_c02 ,2),
          vnorm( ....SurfaceCorner_c02 - ....SurfaceCorner_c03 ,2),
          VisualPlateHeight
        };
        
        AnyDrawSurf DrwSurfaceBox = 
        {
          AnyStyleDrawMaterial1 material = 
          {
            EnableCreasing = Off;
            EnableSmoothing = Off;
            Opacity = 0.8;//;
            EmissionRGB = {0.1,0.1,0.1};
            SpecularRGB = {0.00,0.00,0.00};
            Shininess = 0.1;
            RGB = {1, 1, 1}*(1 - 0.7*......ColorScale);
          };
          PickableZOrdering = -1;
          FileName = "box";
          ScaleXYZ= .Size/0.30;
          Face=-1;
          Visible = On;
        };    
      };
    };  
  
  };
  

  
  AnyKinEqSimpleDriver ForcePlateDriver ={
    AnyKinLinear ForcePlateLin={
      AnyRefNode &ref1=..Corners.PlateCenter;
      AnySeg &ref2=..ForcePlate.ForcePlateSeg;
      Ref=0;
    };
    
    AnyKinRotational ForcePlateRot={
      AnyRefNode &ref1=..Corners.PlateCenter;
      AnySeg &ref2=..ForcePlate.ForcePlateSeg;
      Type=RotAxesAngles;
    };
    DriverPos={0,0,0,0,0,0}; 
    DriverVel={0,0,0,0,0,0};
    Reaction.Type = {Off,Off,Off,Off,Off,Off};
  };
  
  AnyFloatVar tStart = C3D_OBJECT.Header.FirstFrameNo/C3D_OBJECT.Header.VideoFrameRate;
  AnyFloatVar tEnd =  (C3D_OBJECT.Header.LastFrameNo+1)/C3D_OBJECT.Header.VideoFrameRate;
  AnyInt NoAnalogData= (C3D_OBJECT.Header.LastFrameNo - C3D_OBJECT.Header.FirstFrameNo +1)*C3D_OBJECT.Header.NoAnalogSamplesPer3DFrame;
  AnyFloat Time= farr( tStart, (tEnd-tStart)/( NoAnalogData - 1) ,  NoAnalogData);
  
  
  AnyVar InContactOnOff = iffun( eqfun(0, ContactDetectionLimb2.NodeWithInBox.WithinBoxAndVelBelowThreshold +
                                          ContactDetectionLimb1.NodeWithInBox.WithinBoxAndVelBelowThreshold)
                                   , 0, 1);


  
  AnyFolder CenterOfPressure =
  {
     AnyForceMomentMeasure2& NetEffectMeasure = .ForcePlate.NetEffectMeasure;
     
     AnyVar fx = NetEffectMeasure.Flocal[0]; 
     AnyVar fy = NetEffectMeasure.Flocal[1]; 
     AnyVar fz = NetEffectMeasure.Flocal[2];
     AnyVar mx = NetEffectMeasure.Mlocal[0]; 
     AnyVar my = NetEffectMeasure.Mlocal[1]; 
     AnyVar mz = NetEffectMeasure.Mlocal[2]; 

     AnyVar fzz =iffun(gtfun(abs(fz),0),fz,fz+1000000);

     AnyVar Vx= -my/fzz;
     AnyVar Vy= mx/fzz;
     AnyVar Vz= 0;     

     AnyVar OnOff=.ForcePlate.OnOff*.InContactOnOff ;

     AnyRefFrame& ref_ForcePlate = .ForcePlate.ForcePlateSeg.PlateSurface;
     ref_ForcePlate  = 
     {
        AnyDrawSphere COP_ball = 
        {
            RGB = {0,1,0};
            ScaleXYZ = 0.015 *{1,1,1};
            //Opacity = iffun(gtfun(..fz, -10.0), 0.0, 1.0);
            Opacity = ..OnOff;
            Position = {..Vx, ..Vy, ..Vz};
        };
     };
     
     AnyDrawLine Line = 
     {
       p0 = {.Vx, .Vy, .Vz};
       p1 = p0+0.004*.OnOff*{.fx, .fy, .fz};
       Visible = ..Switch_DrawForceVectorFromCOP ;
       AnyRefFrame &ref = ..ForcePlate.ForcePlateSeg.PlateSurface;

       Line.RGB ={0,0,1};
       Line.Thickness = 0.01;
       Line.End.Thickness = 2*0.01;
       Line.End.Length = 4*0.01;
       GlobalCoord=Off;
     };
     
  };  
   
  #if VerticalDirection=="X"
  AnyIntArray Index={1,2,0};
  #endif
  
  #if VerticalDirection=="Y" 
  AnyIntArray Index={0,2,1};
  #endif
  
  #if VerticalDirection=="Z" 
  AnyIntArray Index={0,1,2};
  #endif
  
  #if (VerticalDirection!="X") & (VerticalDirection!="Y") & (VerticalDirection!="Z") 
  AnyIntArray Index = {{1,2,0},{0,2,1},{0,1,2}}[argmax(round(abs(ForcePlate.ForcePlateSeg.Axes0'[2])))];
  #endif
  
  
  AnyFolder ContactDetectionLimb1 ={
    AnyComponentDefinition obj = {
      SubGroupRegexSearch = "([_[:alnum:]]+?)\.([_[:alnum:]]+?)";
      SubGroupRegexReplace = "$1";
    };  
    AnyVec3 P1= .Limb1.CONTACT_NODE1.r;
    AnyVar P1Vel= vnorm(.Limb1.CONTACT_NODE1.rDot-.GroundVelocity,2);
    
    AnyVec3 P2= .Limb1.CONTACT_NODE2.r;
    AnyVar P2Vel= vnorm(.Limb1.CONTACT_NODE2.rDot-.GroundVelocity,2);
    
    #include "ContactDetection.any"
    
    /// If the ALLOW_MULTI_LIMB_CONTACT option is OFF this ensures that two limbs can't
    /// have contact with the plate at the same time
    /// UseOppositeLimb is 1 if the opposite limb has contact and a lower velocity that limb1
    AnyVar UseOppositeLimb = not(ALLOW_MULTI_LIMB_CONTACT)*
          andfun(.ContactDetectionLimb2.NodeWithInBox.WithinBoxAndVelBelowThreshold,
                 ltfun(.ContactDetectionLimb2.P1Vel, P1Vel));
   
    AnyVar InContactStrength = NodeWithInBox.WithinBoxAndVelBelowThreshold *10000*not(UseOppositeLimb);
    
    AnyVar Limb2NoContact=iffun(eqfun(.ContactDetectionLimb2.NodeWithInBox.WithinBoxAndVelBelowThreshold ,1.0),0.0,1.0); //equal 0 if limb2 in contact
    AnyVar NoContactStrength =Limb2NoContact*NodeWithInBox.OutsideBoxOrVelHigherThanThreshold*10000;
    
    
    AnyFolder PlateFootReactions = {
      AnyRefFrame& ref1=..ForcePlate.ForcePlateSeg;
      AnyRefFrame&ref2=..Limb1;
      AnyVar Strength =.InContactStrength;
      #include "<ANYBODY_PATH_TOOLBOX>/Mocap/ArtificialMuscleConnection.any"
    };
    
    AnyFolder PlateGroundReactions={
      AnyRefFrame& ref1=..Corners.PlateCenter;
      AnyRefFrame& ref2=..ForcePlate.ForcePlateSeg;
      AnyVar Strength =.NoContactStrength;
      #include "<ANYBODY_PATH_TOOLBOX>/Mocap/ArtificialMuscleConnection.any"
    };
  };
 
  AnyFolder ContactDetectionLimb2 = {
    AnyComponentDefinition obj = {
      SubGroupRegexSearch = "([_[:alnum:]]+?)\.([_[:alnum:]]+?)";
      SubGroupRegexReplace = "$1";
    };  
    AnyVec3 P1= .Limb2.CONTACT_NODE1.r;
    AnyVar P1Vel= vnorm(.Limb2.CONTACT_NODE1.rDot-.GroundVelocity,2);
    
    AnyVec3 P2= .Limb2.CONTACT_NODE2.r;
    AnyVar P2Vel= vnorm(.Limb2.CONTACT_NODE2.rDot-.GroundVelocity,2);
    
    #include "ContactDetection.any"
    
    /// If the ALLOW_MULTI_LIMB_CONTACT option is OFF this ensures that two limbs can't
    /// have contact with the plate at the same time
    /// UseOppositeLimb is 1 if the opposite limb has contact and a lower velocity that limb2
    AnyVar UseOppositeLimb = not(ALLOW_MULTI_LIMB_CONTACT)*
            andfun(.ContactDetectionLimb1.NodeWithInBox.WithinBoxAndVelBelowThreshold,
                   ltfun(.ContactDetectionLimb1.P1Vel, P1Vel));
     
    AnyVar InContactStrength = NodeWithInBox.WithinBoxAndVelBelowThreshold *10000*not(UseOppositeLimb);

    
    AnyVar Limb1NoContact=iffun(eqfun(.ContactDetectionLimb1.NodeWithInBox.WithinBoxAndVelBelowThreshold ,1.0),0.0,1.0); //equal 0 if limb1 in contact
    AnyVar NoContactStrength =Limb1NoContact*NodeWithInBox.OutsideBoxOrVelHigherThanThreshold*10000;
    
    
    AnyFolder PlateFootReactions={
      AnyRefFrame& ref1=..ForcePlate.ForcePlateSeg;
      AnyRefFrame& ref2=..Limb2;
      AnyVar Strength=.InContactStrength;
      #include "<ANYBODY_PATH_TOOLBOX>/Mocap/ArtificialMuscleConnection.any"
    };
    
    
    AnyFolder PlateGroundReactions={
      AnyRefFrame &ref1=..Corners.PlateCenter;
      AnyRefFrame& ref2=..ForcePlate.ForcePlateSeg;
      AnyVar Strength =.NoContactStrength;
      #include "<ANYBODY_PATH_TOOLBOX>/Mocap/ArtificialMuscleConnection.any"
    };
  };
  
#if (CREATE_KIN_GRF == 1) & ((FORCEPLATE_TYPE == 4) | (FORCEPLATE_TYPE == 2))
  
  ForcePlate.ForcePlateSeg =
  {
    AnyRefNode KinGRF_rotnode = { ARel = RotMat(pi,y); };
  };
  
  AnyFolder Kinematic_GRF = 
  {
    
    AnyFloat weightdata = sqrt(Calculations.norm);
    AnyVar max_val = max({ max(weightdata),0.000001});
    AnyVar Th = 0.05*max_val;
    
    // Threshold the weight data
    AnyVector ThresHolded = iffun(gtfun(weightdata, Th ),
                             (weightdata-Th )/(max_val-Th),
                             0*farr(0.0,1.0,NumElemOf(weightdata)));

    AnyFunInterpol GlobalCopFun = 
    {
      Type = Bspline;
      T = ..Time;
      Data = ((1+0*farr(0.0,1.0,..NoAnalogData))'*{..ForcePlate.ForcePlateSeg.r0}+ (..ForcePlate.ForcePlateSeg.Axes0*.Calculations.GRF_start.Data)')';
    };    

    
    
    // Alternative Time array which starts from tStart and ends at tEnd  
    AnyInt NoAnalogData2= max({C3D_OBJECT.Header.NoAnalogSamplesPer3DFrame,
                               (C3D_OBJECT.Header.LastFrameNo - C3D_OBJECT.Header.FirstFrameNo)*C3D_OBJECT.Header.NoAnalogSamplesPer3DFrame});
    AnyFloat tStart2 = .tStart+1e-7;
    AnyFloat tEnd2 = max({tStart2+1/C3D_OBJECT.Header.VideoFrameRate,.tEnd-1/C3D_OBJECT.Header.VideoFrameRate})-1e-7;
    AnyFloat Time2 = farr( tStart2, (tEnd2-tStart2)/( max({1,NoAnalogData2 - 1})) ,  NoAnalogData2);

    AnyFloat limb1_COP_dist = vnorm(KIN_GRF_LIMB1_FUN(Time2) - 
                                    GlobalCopFun(Time2));
    AnyFloat limb2_COP_dist = vnorm(KIN_GRF_LIMB2_FUN(Time2)- 
                                    GlobalCopFun(Time2));

    AnyFunInterpol Weight_limb1 = 
    {
       Type = Bspline;
       T = .Time2;
       Data = {iffun(lteqfun(.limb1_COP_dist,.limb2_COP_dist ), .ThresHolded, 0.0*.ThresHolded)};
    };

    AnyFunInterpol Weight_limb2 = 
    {
       Type = Bspline;
       T = .Time2;
       Data = {iffun(lteqfun(.limb2_COP_dist,.limb1_COP_dist ), .ThresHolded, 0.0*.ThresHolded)};
    };


    
    
    AnySeg Frame = 
    {
      Mass = 0.0;
      Jii = {0.0, 0.0, 0.0};
      AnyDrawVector arrow = {Vec ={0,0,..Calculations.GRF_normalized(.t)[0]};GlobalCoord = Off;Line.Thickness = 0.01;};
      AnyDrawRefFrame drw = { ScaleXYZ = 0.2*{1,1,1};};
      AnyRefNode vector= {sRel = {0,0,1};};
    };
    
    AnyKinEq lock_z_rot= {
      AnyKinMeasureOrg zrot = {
        AnyKinRotational rot = {
          Type= RotVector;
          AnyRefFrame &ref0 = ....ForcePlate.ForcePlateSeg.KinGRF_rotnode;
          AnyRefFrame &ref1 = ...Frame;
        };
        MeasureOrganizer = {2};
      };
    };
    
    AnyFolder Calculations = {
      
      AnyVector T =  ..Time;
      AnyVector fx = ..ForcePlate.load.Data[0]; 
      AnyVector fy = ..ForcePlate.load.Data[1]; 
      AnyVector fz = ..ForcePlate.load.Data[2];
      AnyVector mx =  (1/1000)*..ForcePlate.load.Data[3]; 
      AnyVector my =  (1/1000)*..ForcePlate.load.Data[4]; 
      AnyVector mz =  (1/1000)*..ForcePlate.load.Data[5]; 
      
      AnyVector norm = vnorm({fx,fy,fz}');
      
      // Values used when dividing. To avoid dividing by zero. 
      AnyVar force_thresshold = 0.05*max(abs(fz));
      AnyVector _fz = iffun(gtfun(abs(fz),force_thresshold ),fz, fz+10^10);
      AnyVector _norm = iffun(gtfun(abs(norm),force_thresshold ),norm, norm+10^10) ;

      
      AnyVar originz = -1*..ForcePlate.ForcePlateSeg.TransducerLocation.zdist;
      
      
      AnyVector Vx= (originz*fx-my)/( _fz ) ;
      AnyVector Vy= (mx+originz*fy)/( _fz );
      AnyVector Vz= 0*Vy;
      
      AnyFunInterpol GRF_normalized = 
      {
        Type = PiecewiseLinear;
        T = .T;
        Data = {.norm/( iffun(gtfun(max(.norm),0),max(.norm), 10^10) )};
      };
      
      AnyFunInterpol GRF_start = 
      { 
        Type = PiecewiseLinear;
        T = .T;
        Data = {.Vx, .Vy, .Vz};
      };    
      AnyFunInterpol GRF_end = 
      { 
        Type = PiecewiseLinear;
        T = .T;
        
        Data = {.Vx + .fx/(._norm),
          .Vy + .fy/(._norm)
          //.COP_cal.Vz + .COP_cal.fz/ (.COP_cal.norm)
        };      
      };
      
      
    };//KinGRF_Calculations 
    
    
    AnyKinDriver COPDriver = {
      AnyKinLinear start_lin = {
        AnyRefFrame &ref0 = ...ForcePlate.ForcePlateSeg.PlateSurface;
        AnyRefFrame &ref1 = ..Frame;
        Ref = 0;
      };
      AnyParamFun &fun = .Calculations.GRF_start;
      CType = {Hard,Hard,Hard};
    };
    
    AnyKinDriver DirectionDriver = {
      AnyKinMeasureOrg end_lin_xy = {
        AnyKinLinear lin = 
        {
          AnyRefFrame &ref0 = ....ForcePlate.ForcePlateSeg.PlateSurface;
          AnyRefFrame &ref1 = ...Frame.vector;
          Ref = 0;
        };
        MeasureOrganizer = {0,1};
      };
      AnyParamFun &fun = .Calculations.GRF_end ;
      CType = {Hard, Hard};
    };  
  } ;
  #endif  
  
  
  
  
  
  
};

#endif