Switch to side-by-side view

--- a
+++ b/Tools/AnyMocap/OptimizeBVH_Origin.any
@@ -0,0 +1,518 @@
+/* Class Template OptimizeBVH_Origin
+
+This class template can be useful when BVH recordings consist
+of interaction of the subject with environment objects. In such
+trials, it can be hard to locate the environment object relative
+to the subject. An example could be a subject lifting a box, 
+where the position of the box is known but the BVH recordings of 
+different trials results in the subject being located inconsistently
+with respect to the box.
+
+This class template optimizes the origin of the BVH model. 
+Normally, the origin of the BVH recording is coincident with the
+Global Ref of the model in AnyMoCap models. The BVH stick figure
+and consequently, the movement of the human subject is reconstructed
+from the origin of the BVH recording. Whereas environment objects
+are normally located with respect to the Global Ref of the model.
+
+The origin of the BVH model (recording) can be set to another position 
+and orientation with respect to the Global Ref. This class template
+can optimize the origin of the BVH model such that a target segment
+of the human model (L/R Foot/Hand) is positioned and oriented in a
+known way for a known time interval while following the recorded
+motion. For example, the class template can set the origin of the BVH 
+recording such that the right hand is positioned and oriented in a 
+known way (such as on a box) in a known and definite time interval 
+of the recording (such as the time interval in which the subject 
+grasps the box).
+
+The class works by defining three ref nodes on the BVH stick figure
+and three corresponding nodes at the target. To help visualization, 
+a drawing of the selected human segment is generated at the target.
+The corresponding nodes are driven to be coincident by using 
+AnyKinMotion driver. These drivers are active only during the active 
+time interval. This is implemented by a square wave function that is 
+set to 1 during the active period and zero outside the active period. 
+Finally, the origin of the BVH recording is set using a design 
+variable that is optimized through the default parameter optimization 
+study of the AnyMoCap model.
+
+Class arguments:
+---------------
+HUMAN_SEG: <Optional. Default ="RFOOT">
+Is the human segment that must be positioned and oriented in the 
+target postion and orientation. 
+
+REPEAT_USE: <Optional. Default = 0>
+Switch to identify if the class template is being used more than one
+time. For multiple use of the of the class template in the same model, 
+the switch must be set to 0 only one time and to 1 for other times. 
+This switch prevents error due to the class template defining the same
+objects multiple times.
+
+OPT_LIN_X: <Optional. Default = 1>
+Switch to enable optimization of BVH origin in translation in X axis.
+
+OPT_LIN_Y: <Optional. Default = 0>
+Switch to enable optimization of BVH origin in translation in Y axis.
+
+OPT_LIN_Z: <Optional. Default = 1>
+Switch to enable optimization of BVH origin in translation in Z axis.
+
+OPT_ROT_X: <Optional. Default = 0>
+Switch to enable optimization of BVH origin in rotation in X axis.
+
+OPT_ROT_Y: <Optional. Default = 1>
+Switch to enable optimization of BVH origin in rotation in Y axis.
+
+OPT_ROT_Z: <Optional. Default = 0>
+Switch to enable optimization of BVH origin in rotation in Z axis.
+
+NAME_PARA_OPT: <Optional. Default = BVH_Origin>
+Is the name of the parameter that will be included in the parameter
+optimization study with suffix for direction (eg: NAME_PARA_OPT_LinX)
+
+REF_FRAME_FOR_TARGET: <Optional. Default = Main.EnvironmentModel.GlobalRef>
+Is the reference frame for defining the target position and orientation.
+
+BVH_FILE_DATA: <Optional. Default = Main.ModelSetup.BVHFileData>
+Is the pointer to the AnyInputBVH object in the model. This is the class
+that reads the BVH file.
+
+HUMAN_MODEL: <Optional. Default = Main.HumanModel>
+Is the pointer to the human model in the model.
+
+{
+  Active_Time_Start: <Obligatory>
+  Is the start of the time interval where the selected human segment
+  must be in the target position and orientation. AnyVector 
+
+  Active_Time_End: <Obligatory>
+  Is the end of the time interval where the selected human segment
+  must be in the target position and orientation. AnyVector
+
+  Target_Position: <Optional. Default = {0.0, 0.0, 0.0};>
+  Is the position vector of the Target referred in REF_FRAME_FOR_TARGET.
+
+  Target_Orientation: <Optional. Default = {{1,0,0},{0,1,0},{0,0,1}};>
+  Is the rotational transformation matrix of the Target referred in
+  REF_FRAME_FOR_TARGET.
+  
+  Optional initialization:
+  -----------------------
+  Settings: 
+    InitialGuess    : Initial guess for the BVH Origin in the order: {lin_x,
+                      lin_y, lin_z, rot_x, rot_y, rot_z}. in m or radians.
+    Weight          : Weight of the AnyKinMotion drivers in the active time interval
+    dt_Ratio        : Start and end transition time of the weight function 
+                      expressed as fraction of the active time interval. For more info,
+                      please see reference for AnyFunSquareWave
+
+}
+
+Please note:
+------------
+* When you implement this class in a copy of the BVH Xsens model from the AMMR, it is possible
+  that the TrialSpecificData.any file defines LoadParametersFrom = {""};. If you want to save the
+  optimized origin and load it again in the future, please comment the definition of 
+  LoadParametersFrom so that the AnyMoCap model can use the default definition of the filename.
+  The class template will provide a warning if LoadParametersFrom = {""};
+
+Initialization example:
+----------------------
+OptimizeBVH_Origin <Object_name> (
+    HUMAN_SEG = "<*RFOOT|LFOOT|RHAND|LHAND>"
+    
+    // Optional Initialization showing default values
+    
+    REPEAT_USE = 0,
+    OPT_LIN_X = 1, OPT_LIN_Y = 0, OPT_LIN_Z = 1,
+    OPT_ROT_X = 0, OPT_ROT_Y = 1, OPT_ROT_Z = 0,
+    NAME_PARA_OPT = BVH_Origin,
+    PARAMETER_OPT_STUDY = Main.Studies.ParameterIdentification,
+    REF_FRAME_FOR_TARGET = Main.EnvironmentModel.GlobalRef,
+    BVH_FILE_DATA = Main.ModelSetup.BVHFileData,
+    HUMAN_MODEL = Main.HumanModel
+    ) = 
+      {
+        Active_Time_Start = 0.1; // Obligatory
+        Active_Time_End = 0.15; // Obligatory
+        Target_Position = {0.0, 0.0, 0.0};
+        Target_Orientation = {{1,0,0},{0,1,0},{0,0,1}};
+        Settings = {
+          InitialGuess = {0.0,0.0,0.0,0.0,0.0,0.0};
+          Weight = 1;
+          dt_Ratio = {0.1,0.1};
+        };
+      };
+
+*/
+
+#class_template OptimizeBVH_Origin (
+HUMAN_SEG = "RFOOT",
+REPEAT_USE = 0,
+OPT_LIN_X = 1, OPT_LIN_Y = 0, OPT_LIN_Z = 1,
+OPT_ROT_X = 0, OPT_ROT_Y = 1, OPT_ROT_Z = 0,
+NAME_PARA_OPT = BVH_Origin,
+PARAMETER_OPT_STUDY = Main.Studies.ParameterIdentification,
+REF_FRAME_FOR_TARGET = Main.EnvironmentModel.GlobalRef,
+BVH_FILE_DATA = Main.ModelSetup.BVHFileData,
+HUMAN_MODEL = Main.HumanModel
+)
+{
+  /// The start of the time interval where the selected human segment must be in the target position and orientation.
+  #var AnyVar Active_Time_Start;
+  /// The end of the time interval where the selected human segment must be in the target position and orientation.  
+  #var AnyVar Active_Time_End;
+  /// A reference to REF_FRAME_FOR_TARGET
+  AnyFolder &Ref_Frame_For_Target = REF_FRAME_FOR_TARGET;
+  /// The position vector of the Target referred in REF_FRAME_FOR_TARGET.
+  #var AnyVec3 Target_Position = {0.0, 0.0, 0.0};
+  /// The rotational transformation matrix of the Target referred in REF_FRAME_FOR_TARGET.
+  #var AnyMat33 Target_Orientation = {{1,0,0},{0,1,0},{0,0,1}};
+  AnyFolder Settings = {
+    /// Initial Guess of the BVH Origin in the order: {lin_x, lin_y, lin_z, rot_x, rot_y, rot_z}. in m or radians.                      
+    #var AnyFloat InitialGuess = {0.0,0.0,0.0,0.0,0.0,0.0};
+    /// Weight of the AnyKinMotion drivers during the active time interval
+    #var AnyVar Weight = 1;
+    /// Start and end transition time of the weight function expressed as fraction of the active time interval. 
+    /// For more info, please see reference for AnyFunSquareWave
+    #var AnyFloat dt_Ratio = {0.1,0.1};
+  };
+  
+  // Implement code only if HUMAN_SEG is correctly defined, else give appropriate error. 
+  // Error is defined at the end of the class template.
+  #if  (HUMAN_SEG =="LFOOT")|(HUMAN_SEG =="RFOOT")|(HUMAN_SEG =="LHAND")|(HUMAN_SEG =="RHAND")  
+  
+  AnyFolder InputArgs = {
+    // warning for LoadParametersFrom value. BVH models normally don't have any parameters to be optimized. Thus, the anyset file with 
+    // parameters won't exist and LoadParametersFrom value is defined as {""}; in TrialSpecificData.any to avoid issues with 
+    // RunAnalysis.LoadParameters Operation. The definition of LoadParametersFrom in TrialSpecificData.any must be commented out 
+    // so that AnyBody can define the default value for LoadParametersFrom value. Alternatively, provide the name of the file from 
+    // which parameters should be loaded.
+    AnyInt Warning_LoadParams = warn(neqfun(Main.ModelSetup.TrialSpecificData.LoadParametersFrom,""), strformat("\n" +
+                                "------------------------------------------------------------------------------------------------------\n"+
+                                "Main.ModelSetup.TrialSpecificData.LoadParametersFrom is defined as empty. Loading parameters operation will not work!\n" + 
+                                "Please comment its definition in TrialSpecificData.any to let AnyBody define this value automatically \n" +
+                                "or, provide the name of the file from which parameters should be loaded.\n" +
+                                "------------------------------------------------------------------------------------------------------"));
+    
+    // Error for ActiveTimeInterval. 
+    AnyInt Error_ActiveTimeInterval = assert(gtfun(.Active_Time_End,.Active_Time_Start), "Please ensure Active_Time_End is greater than Active_Time_Start");
+        
+    #if HUMAN_SEG == "LFOOT"
+    // create nodes on the BVH model
+    AnyRefFrame &BVHSeg = BVH_FILE_DATA.Model.Hips.LeftHip.LeftKnee.LeftAnkle.Seg;
+    BVHSeg = {
+      AnyRefNode NAME_PARA_OPT##_Node0 = {
+        sRel = {0,.LeftToe.sRel[1],0};
+        AnyDrawRefFrame drw ={};  
+        AnyRefNode Node1 = {
+          sRel = {-0.02,0,..LeftToe.sRel[2]};
+          AnyDrawNode drw ={};
+        };
+        AnyRefNode Node2 = {
+          sRel = {0.05,0,..LeftToe.sRel[2]};
+          AnyDrawNode drw ={};
+        };
+      }; // Heel Node      
+    }; // BVHSeg  
+    
+    // Define values for drawing the HUMAN_SEG at the target point and orientation.
+    TargetFrameRef.NAME_PARA_OPT##_Node0 = {
+      AnyFileVar STLFilename = HUMAN_MODEL.BodyModel.Left.Leg.Seg.STL.FilenameFoot;
+      AnyFloat STLPos = -..BVHSeg.NAME_PARA_OPT##_Node0.sRel + {0,HUMAN_MODEL.BodyModel.Left.Leg.Seg.Talus.SubTalarJoint.sRel[1],0};
+      AnyMat33 STLOrientation = RotMat(-pi/2,y);
+      AnyFloat STLScale = {1,1,-1};
+      AnyFunTransform3D &STLTransform = HUMAN_MODEL.BodyModel.Left.Leg.Seg.Foot.Scale;      
+    };    
+    #endif
+    
+    #if HUMAN_SEG == "RFOOT"
+    // create nodes on the BVH model
+    AnyRefFrame &BVHSeg = BVH_FILE_DATA.Model.Hips.RightHip.RightKnee.RightAnkle.Seg;
+    BVHSeg = {
+      AnyRefNode NAME_PARA_OPT##_Node0 = {
+        sRel = {0,.RightToe.sRel[1],0};
+        AnyDrawRefFrame drw ={};  
+        AnyRefNode Node1 = {
+          sRel = {0.02,0,..RightToe.sRel[2]};
+          AnyDrawNode drw ={};
+        };
+        AnyRefNode Node2 = {
+          sRel = {-0.05,0,..RightToe.sRel[2]};
+          AnyDrawNode drw ={};
+        };
+      }; // Heel Node      
+    }; // BVHSeg  
+    
+    // Define values for drawing the HUMAN_SEG at the target point and orientation.
+    TargetFrameRef.NAME_PARA_OPT##_Node0 = {
+      AnyFolder &HumRef = HUMAN_MODEL.BodyModel.Right.Leg.Seg;
+      AnyFileVar STLFilename = HUMAN_MODEL.BodyModel.Right.Leg.Seg.STL.FilenameFoot;
+      AnyFloat STLPos = -..BVHSeg.NAME_PARA_OPT##_Node0.sRel + {0,HUMAN_MODEL.BodyModel.Right.Leg.Seg.Talus.SubTalarJoint.sRel[1],0};
+      AnyMat33 STLOrientation = RotMat(-pi/2,y);
+      AnyFloat STLScale = {1,1,1};
+      AnyFunTransform3D &STLTransform = HUMAN_MODEL.BodyModel.Right.Leg.Seg.Foot.Scale;      
+    };
+    #endif
+    
+    #if HUMAN_SEG == "LHAND"
+    // create nodes on the BVH model
+    AnyRefFrame &BVHSeg = BVH_FILE_DATA.Model.Hips.Chest.Chest2.Chest3.Chest4.LeftCollar.LeftShoulder.LeftElbow.LeftWrist.Seg;
+    BVHSeg = {
+      AnyRefNode NAME_PARA_OPT##_Node0 = {
+        sRel = {0,0,0};
+        AnyDrawRefFrame drw ={};  
+        AnyRefNode Node1 = {
+          sRel = ..LHT1.sRel; 
+          AnyDrawNode drw ={};
+        };
+        AnyRefNode Node2 = {
+          sRel = ..LHT2.sRel;
+          AnyDrawNode drw ={};
+        };
+      }; // Wrist Node      
+    }; // BVHSeg  
+    
+    // Define values for drawing the HUMAN_SEG at the target point and orientation.
+    TargetFrameRef.NAME_PARA_OPT##_Node0 = {
+      AnyFileVar STLFilename = HUMAN_MODEL.BodyModel.Left.ShoulderArm.STL.FileNameHand;
+      AnyFloat STLPos = -..BVHSeg.NAME_PARA_OPT##_Node0.sRel+HUMAN_MODEL.BodyModel.Left.ShoulderArm.Seg.Hand.Ref.wj.sRel;
+      AnyMat33 STLOrientation = RotMat(-pi/2,x)*RotMat(-pi,y);
+      AnyFloat STLScale = {1,1,-1};
+      AnyFunTransform3D &STLTransform = HUMAN_MODEL.Scaling.GeometricalScaling.Left.Hand.ScaleFunction;
+    };    
+    #endif
+    
+    #if HUMAN_SEG == "RHAND"
+    // create nodes on the BVH model
+    AnyRefFrame &BVHSeg = BVH_FILE_DATA.Model.Hips.Chest.Chest2.Chest3.Chest4.RightCollar.RightShoulder.RightElbow.RightWrist.Seg;
+    BVHSeg = {
+      AnyRefNode NAME_PARA_OPT##_Node0 = {
+        sRel = {0,0,0};
+        AnyDrawRefFrame drw ={};  
+        AnyRefNode Node1 = {
+          sRel = ..RHT1.sRel; 
+          AnyDrawNode drw ={};
+        };
+        AnyRefNode Node2 = {
+          sRel = ..RHT2.sRel;
+          AnyDrawNode drw ={};
+        };
+      }; // Wrist Node      
+    }; // BVHSeg  
+    
+    // Define values for drawing the HUMAN_SEG at the target point and orientation.
+    TargetFrameRef.NAME_PARA_OPT##_Node0 = {
+      AnyFileVar STLFilename = HUMAN_MODEL.BodyModel.Right.ShoulderArm.STL.FileNameHand;
+      AnyFloat STLPos = -..BVHSeg.NAME_PARA_OPT##_Node0.sRel-HUMAN_MODEL.BodyModel.Right.ShoulderArm.Seg.Hand.Ref.wj.sRel;
+      AnyMat33 STLOrientation = RotMat(-pi/2,x);
+      AnyFloat STLScale = {1,1,1};
+      AnyFunTransform3D &STLTransform = HUMAN_MODEL.Scaling.GeometricalScaling.Right.Hand.ScaleFunction;
+    };    
+    #endif
+    
+    // Create Target nodes corresponding to the nodes defined on the BVH model
+    AnyRefFrame &TargetFrameRef = REF_FRAME_FOR_TARGET;
+    TargetFrameRef = {
+      AnyRefNode NAME_PARA_OPT##_Node0 = {
+        sRel = ...Target_Position;
+        ARel = ...Target_Orientation;
+        AnyDrawRefFrame drw ={ScaleXYZ = 0.15*{1,1,1}; RGB = {0.65,0.65,0.65};};
+        // Visualize target human segment
+        AnyDrawSurf drwSurf = {
+          FileName = .STLFilename;
+          RelPos = .STLPos;
+          RelRotMat = .STLOrientation;
+          ScaleXYZ = .STLScale;
+          Opacity = 0.3 + ....Functions.WeightFun(.t)[0]*0.5;
+//          RGB ={....Functions.WeightFun(.t)[0]*0.5,(....Settings.Weight-....Functions.WeightFun(.t)[0])*0.7,0.7*....Settings.Weight}/....Settings.Weight;
+          RGB = iffun(gtfun(....Functions.WeightFun(.t)[0],0),{0.2,0.8,0.2},{0.65,0.65,0.65});
+          AnyFunTransform3D &Scale = .STLTransform;
+        };
+        
+      };
+      AnyRefNode NAME_PARA_OPT##_Node1 = {
+        sRel = ..BVHSeg.NAME_PARA_OPT##_Node0.Node1.sRel*...Target_Orientation'+...Target_Position;
+        AnyDrawNode drw ={ScaleXYZ = 0.015*{1,1,1}; RGB = {0.65,0.65,0.65};Opacity = 0.5;};
+      };
+      AnyRefNode NAME_PARA_OPT##_Node2 = {
+        sRel = ..BVHSeg.NAME_PARA_OPT##_Node0.Node2.sRel*...Target_Orientation'+...Target_Position;
+        AnyDrawNode drw ={ScaleXYZ = 0.015*{1,1,1}; RGB = {0.65,0.65,0.65};Opacity = 0.5;};
+      };
+    }; // TargetFrameRef    
+    
+  }; // InputArgs
+  
+  // Define functions needed for the parameter optimization.
+  AnyFolder Functions = {
+    // WeightFun makes a square wave function to consider the active time interval.
+    // WeightFun is equal to the weight value in the active time interval and zero
+    // outside the active time interval
+    AnyFunSquareWave WeightFun = 
+    {
+      InitialValues = {0.0} + 0*..InputArgs.Error_ActiveTimeInterval; // Enforce Active Time Interval error
+      Ts = {..Active_Time_Start,..Active_Time_End};
+      Values = {{..Settings.Weight,0}};
+      dT = ..Settings.dt_Ratio*(Ts[1]-Ts[0]);
+    };
+    
+    // Create constant interpol functions that will be used to drive the linear measure between
+    // BVH model nodes and target nodes to zero.
+    AnyFunInterpol DriverPosNode0 = {
+      Type = ConstantValue;
+      T = BVH_FILE_DATA.Data.Abscissa.Sample*BVH_FILE_DATA.Header.FrameTime;
+      Data = {repmat(SizesOf(T)[0], 0),
+              repmat(SizesOf(T)[0], 0),
+              repmat(SizesOf(T)[0], 0)};
+    };
+    AnyFunInterpol DriverPosNode1 = {
+      Type = ConstantValue;
+      T = BVH_FILE_DATA.Data.Abscissa.Sample*BVH_FILE_DATA.Header.FrameTime;
+      Data = {repmat(SizesOf(T)[0], 0),
+              repmat(SizesOf(T)[0], 0),
+              repmat(SizesOf(T)[0], 0)};
+    };  
+    AnyFunInterpol DriverPosNode2 = {
+      Type = ConstantValue;
+      T = BVH_FILE_DATA.Data.Abscissa.Sample*BVH_FILE_DATA.Header.FrameTime;
+      Data = {repmat(SizesOf(T)[0], 0),
+              repmat(SizesOf(T)[0], 0),
+              repmat(SizesOf(T)[0], 0)};
+    };
+    
+  };// Functions
+  
+  // Define a variable that will be used to set the Origin and Axes value of the BVH origin
+  AnyFolder BVHOrigin = {
+    AnyFloat NAME_PARA_OPT = .Settings.InitialGuess;
+    AnyFixedRefFrame &BVHOriginRef = BVH_FILE_DATA.Model.GlobalRef;
+    BVHOriginRef = {
+      // Set origin and axes only for the primary use of class template.
+      #if REPEAT_USE == 0
+      Origin = {.NAME_PARA_OPT[0], .NAME_PARA_OPT[1], .NAME_PARA_OPT[2]};
+      Axes = RotMat(.NAME_PARA_OPT[3],x)
+            *RotMat(.NAME_PARA_OPT[4],y)
+            *RotMat(.NAME_PARA_OPT[5],z);
+      #else
+      #endif
+    };    
+  }; // BVH Origin
+  
+  // Insert conditionally the parameter used to set the BVH origin in the existing parameter optimization study
+  // and create drivers between BVH model nodes and target nodes.  
+  AnyFolder &ParaOptStudy = PARAMETER_OPT_STUDY;
+  
+  ParaOptStudy = {    
+    // Insert design variables only in case of primary use of the class template.
+    #if REPEAT_USE == 0
+    // and only if they are required to be optimized.
+    #if OPT_LIN_X == 1
+    AnyDesVar NAME_PARA_OPT##_LinX = {
+      Val = ..BVHOrigin.NAME_PARA_OPT[0];
+    };
+    #endif
+    #if OPT_LIN_Y == 1
+    AnyDesVar NAME_PARA_OPT##_LinY = {
+      Val = ..BVHOrigin.NAME_PARA_OPT[1];
+    };
+    #endif
+    #if OPT_LIN_Z == 1
+    AnyDesVar NAME_PARA_OPT##_LinZ = {
+      Val = ..BVHOrigin.NAME_PARA_OPT[2];
+    };
+    #endif
+    #if OPT_ROT_X == 1
+    AnyDesVar NAME_PARA_OPT##_RotX = {
+      Val = ..BVHOrigin.NAME_PARA_OPT[3];
+    };
+    #endif
+    #if OPT_ROT_Y == 1
+    AnyDesVar NAME_PARA_OPT##_RotY = {
+      Val = ..BVHOrigin.NAME_PARA_OPT[4];
+    };
+    #endif
+    #if OPT_ROT_Z == 1
+    AnyDesVar NAME_PARA_OPT##_RotZ = {
+      Val = ..BVHOrigin.NAME_PARA_OPT[5];
+    };
+    #endif
+    
+    #else
+    #endif
+    KinematicStudyForParameterIdentification = {
+      
+      #if REPEAT_USE == 0
+      // include BVH model in the optimization study (only for primary use)
+      AnyFolder &mocapmodel = BVH_FILE_DATA;
+      #else
+      #endif
+      
+      // Create Drivers between BVH model nodes and target nodes. The drivers are
+      // created inside a folder that can be uniquely named using NAME_PARA_OPT
+      AnyFolder NAME_PARA_OPT##_OptDrivers = {
+                
+        AnyKinMotion Node0 = {
+          viewKinMeasure.Size = 0.2;
+          WeightFun = {&....Functions.WeightFun,
+                       &....Functions.WeightFun,
+                       &....Functions.WeightFun};
+          AnyKinLinear Lin = {
+            AnyRefFrame &BVHPoint = .....InputArgs.BVHSeg.NAME_PARA_OPT##_Node0;
+            AnyRefFrame &TargetPoint = .....InputArgs.TargetFrameRef.NAME_PARA_OPT##_Node0;
+          };
+          AnyParamFun &target = ....Functions.DriverPosNode0;
+        };
+        
+        AnyKinMotion Node1 = {
+          viewKinMeasure.Size = 0.2;
+          WeightFun = {&....Functions.WeightFun,
+                       &....Functions.WeightFun,
+                       &....Functions.WeightFun};
+          AnyKinLinear Lin = {
+            AnyRefFrame &BVHPoint = .....InputArgs.BVHSeg.NAME_PARA_OPT##_Node0.Node1;
+            AnyRefFrame &TargetPoint = .....InputArgs.TargetFrameRef.NAME_PARA_OPT##_Node1;
+          };
+          AnyParamFun &target = ....Functions.DriverPosNode1;
+        };
+        
+        AnyKinMotion Node2 = {
+          viewKinMeasure.Size = 0.2;
+          WeightFun = {&....Functions.WeightFun,
+                       &....Functions.WeightFun,
+                       &....Functions.WeightFun};
+          AnyKinLinear Lin = {
+            AnyRefFrame &BVHPoint = .....InputArgs.BVHSeg.NAME_PARA_OPT##_Node0.Node2;
+            AnyRefFrame &TargetPoint = .....InputArgs.TargetFrameRef.NAME_PARA_OPT##_Node2;
+          };
+          AnyParamFun &target = ....Functions.DriverPosNode2;
+        }; 
+         
+      }; // OptDriver
+    };//KinStudyForParaIden
+    
+  }; // ParaOptStudy
+  
+  // Define a sequence of operations to run the the parameter study, update and save values.
+  #if REPEAT_USE == 0
+  Main = { 
+    AnyOperationSequence RunBVH_Origin_Optimization = 
+    {
+      AnyOperation& ParameterOptimization = PARAMETER_OPT_STUDY.ParameterOptimization;
+      AnyOperationMacro UpdateValues = {
+        MacroStr = {"classoperation Main " + strquote("Update Values")};
+      };
+      AnyOperation& SaveParameters = Main.ModelSetup.Macros.Save_parameters;      
+    };
+  };
+  #else
+  #endif
+
+  // Error message for wrong choice of HUMAN_SEG
+  #else
+  AnyInt Error_HumanSeg = assert(0,strformat("Please check choice of HUMAN_SEG. Only one of \x22LFOOT\x22, \x22RFOOT\x22, \x22LHAND\x22, or \x22RHAND\x22 is allowed."));   
+  #endif
+  
+}; // class template
+