Processing Data in Visual3D
All of the work in this tutorial can be done using a pipeline in Visual3D (V3D). The final pipeline is quite complicated, so it is broken up into a variety of steps, but everything should be run in one pipeline at the very end.
Converting Between .json and .cmz
1. Open up the Pipeline Workshop
2. Use 'Set_Pipeline_Parameter_To_Folder_Path' by clicking on the '»' in the workshop. This command is there to select the right folder on the computer.
Set_Pipeline_Parameter_To_Folder_Path /PARAMETER_NAME=FOLDER /PARAMETER_VALUE=C:\...\Introductory_Project_August_2025\SPL-Open-Data-main\basketball\freethrow\data\P0001\ ! /FROM_MOTION_FILE_IN_WORKSPACE= ! /PARAMETER_VALUE_SEARCH_FOR= ! /PARAMETER_VALUE_REPLACE_WITH= ! /PARAMETER_VALUE_APPEND= ! /SET_PROMPT=Select a directory ! /ALPHABETIZE=TRUE ;
3. Use 'Set_Pipeline_Parameter_To_List_Of_Files' by clicking on the '»' in the workshop. This command selects all the files in this folder which have the file type .json. In this case, that is every file in the folder.
Set_Pipeline_Parameter_To_List_Of_Files /PARAMETER_NAME= FILES /FOLDER= ::FOLDER ! /SEARCH_SUBFOLDERS=FALSE /FILE_MASK=*.json ! /ALPHABETIZE=TRUE ! /RETURN_FOLDER_NAMES=FALSE ! /RETURN_RELATIVE_FILENAMES=FALSE ;
4. With all the files loaded into the pipeline, the next step is to run the for-loop that converts all the .json to .cmz files. This requires 5 commands: For_Each, File_New, File_Open, File_Save_As, End_For_Each.
For_Each /ITERATION_PARAMETER_NAME= INDEX /ITERATION_PARAMETER_COUNT_NAME=COUNT /ITEMS= ::FILES ; File_New ; File_Open /FILE_NAME= ::INDEX ! /FILE_PATH= ! /SEARCH_SUBFOLDERS=FALSE ! /SUFFIX= ! /SET_PROMPT=File_Open ! /ON_FILE_NOT_FOUND=PROMPT ! /FILE_TYPES_ON_PROMPT= ; File_Save_As /FILE_NAME=TRIAL&::COUNT /FOLDER=C:\HAS_Motion\Introductory_Project_August_2025\SPL-Open-Data-main\basketball\freethrow\data\CMZ_Trials\ ! /SET_PROMPT=Save CMZ file as ! /SAVE_EMBEDDED_GRAPHICS=FALSE ! /CREATE_FOLDER_PATH=FALSE ; End_For_Each /ITERATION_PARAMETER_NAME= INDEX ;
This pipeline itself will convert all the .json files to workspaces(.cmz) and can be run individually. But for consistency, the following commands will all be added to the for-loop, after 'File_Open' and before 'File_Save_As'
Assigning Tags
In this dataset, the variables that are analyzed more in depth are whether or not the subject made or missed the shot and during which quarter of the experiment did the free throw trial take place. Tags are placed on each subject that will allow each trial to be easier identified when analyzed in Sift.
Many of the Tag commands are run multiple times in this pipeline, so each different command will be shown here once and the order of the pipeline will be provided so that it can be built manually. If you are having difficulty recreating the pipeline, the complete pipeline can be downloaded at the end of this V3D section.
1. 'Select_Active_File' command. This command is run first to select the file to be tagged.
Select_Active_File /FILE_NAME=ALL_FILES ! /QUERY= ! /SUBJECT_TAGS=NO_SUBJECT ;
2. To assign a tag the 'Assign_Tags_To_Files' command is used.
Assign_Tags_To_Files /MOTION_FILE_NAMES =ALL_FILES /QUERY= PARAMETERS::MLSE::RESULTS="missed" ! change this to "made" to collect the made shots /TAGS= missed ! change the tag to 'made' as well when collecting the made shots ;
3. Now, this subject performed 125 trials. Since the main research question being proposed is about fatigue, the 125 trials are divided into 4 sections (quarters) so that each quarter can be analyzed separately and compared. To create the proper tags for this, conditional statements are used and depending on which iteration of the for-loop the pipeline is on, different tags to mark which quarter the trial took place are assigned. The quarters are: Q1 (Trial 1-31), Q2 (Trial 32-63), Q3 (Trial 64-94), Q4 (Trial 95-125).
a. For Q1 and Q4, the conditional statements are relatively simple and the expression is true if the iteration count is greater or less than the quarter number.
Conditional_Statement /ITERATION_PARAMETER_NAME= QuarterOne /EXPRESSION= &::COUNT& <= 31 ! /AS_INTEGER=TRUE ; Assign_Tags_To_Files /MOTION_FILE_NAMES = ALL_FILES !/QUERY= /TAGS= Q1 ; Conditional_Statement_End /ITERATION_PARAMETER_NAME= QuarterOne ;
b. For Q2 and Q3, nested conditional statements must be used to satisfy the lower and upper bounds.
Conditional_Statement /ITERATION_PARAMETER_NAME= QuarterTwo /EXPRESSION= &::COUNT& >= 32 ! /AS_INTEGER=TRUE ; Conditional_Statement /ITERATION_PARAMETER_NAME= QuarterTwoIn /EXPRESSION= &::COUNT& <= 63 ! /AS_INTEGER=TRUE ; Assign_Tags_To_Files /MOTION_FILE_NAMES= ALL_FILES ! /QUERY= /TAGS= Q2 ; Conditional_Statement_End /ITERATION_PARAMETER_NAME= QuarterTwoIn ; Conditional_Statement_End /ITERATION_PARAMETER_NAME= QuarterTwo ;
4. The order of the pipeline in its current state at this point would be:
- Set_Pipeline_Parameter_To_Folder_Path
- Set_Pipeline_Parameter_To_List_Of_Files
- For_Each
- File_New
- File_Open
- Select_Active_File
- Assign_Tags_To_Files
- Assign_Tags_To_Files
- Conditional_Statement
- Assign_Tags_To_Files
- Conditional_Statement_End
- Conditional_Statement
- Conditional_Statement
- Assign_Tags_To_Files
- Conditional_Statement_End
- Conditional_Statement_End
- Conditional_Statement
- Conditional_Statement
- Assign_Tags_To_Files
- Conditional_Statement_End
- Conditional_Statement_End
- Conditional_Statement
- Assign_Tags_To_Files
- Conditional_Statement_End
- File_Save_As
- End_For_Each
Event Tags
In biomechanical data analysis, it is necessary to have defined events such as 'start', 'stop' where the movements are consistent and the data can be normalized. In this free throw dataset, a large portion of the time-series data is spent with the subject catching the pass and walking to the free throw line. This is not important data to answer the research question so it is cut out in this section. After some trial and error, the right hand was used as the event indicator. When the right hand was at its lowest was the start of the free throw and when the right hand was at its highest was the end of the follow through, a repeatable motion. See the gif below for a visual and graph of the start and end of the repeatable free throw motion.
Now that it is understand where the events are, they can be added to the pipeline.
1. Add 'Event_Global_Minimum' which will get the lowest point on the graph corresponding to the lowest point of the right hand.
Event_Global_Minimum ! name the event /RESULT_EVENT_NAME=START /SIGNAL_TYPES=KINETIC_KINEMATIC ! right hand /SIGNAL_FOLDER=P0001:RHA ! center of gravity /SIGNAL_NAMES=CGPOS ! z-component /SIGNAL_COMPONENTS=Z ! /FRAME_OFFSET=0 ! /TIME_OFFSET= ! /EVENT_SEQUENCE= ! /EXCLUDE_EVENTS= ! /EVENT_SEQUENCE_INSTANCE=0 ! /EVENT_SUBSEQUENCE= ! /SUBSEQUENCE_EXCLUDE_EVENTS= ! /EVENT_SUBSEQUENCE_INSTANCE=0 ! /THRESHOLD= ;
2. Add 'Event_Global_Maximum' which will get the highest point on the graph corresponding to the highest point of the right hand.
Event_Global_Maximum /RESULT_EVENT_NAME=END /SIGNAL_TYPES=KINETIC_KINEMATIC /SIGNAL_FOLDER=P0001:RHA /SIGNAL_NAMES=CGPOS /SIGNAL_COMPONENTS=Z ! /FRAME_OFFSET=0 ! /TIME_OFFSET= ! /EVENT_SEQUENCE= ! /EXCLUDE_EVENTS= ! /EVENT_SEQUENCE_INSTANCE=0 ! /EVENT_SUBSEQUENCE= ! /SUBSEQUENCE_EXCLUDE_EVENTS= ! /EVENT_SUBSEQUENCE_INSTANCE=0 ! /THRESHOLD= ;
3. This step isn't required, but for interest, an event between can be added that will put an event at the midpoint between the start and end events.
Event_Between /NEW_EVENT_NAME=MIDSHOT ! /RANGE_INSTANCE=0 !includes all events /EVENT_SEQUENCE=START+END ! /EXCLUDE_EVENTS= ! /FRAME_OFFSET=0 ! /TIME_OFFSET= ! /PERCENT_OFFSET= ;
4. All 3 of these event commands can be added in order after the last 'Conditional_Statement_End' and before the 'File_Save_As' in the pipeline.
Computing Joint Angles
Now that all the tags and events are created, the final step is to calculate kinematic variables and specifically in this case, joint angles. The joint angles for the lower limb model will be calculated (ankle, knee, and hip joint).
1. Navigate to the Compute Model Based Data window.