c3d-parser 1.1.2__tar.gz → 1.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {c3d_parser-1.1.2/src/c3d_parser.egg-info → c3d_parser-1.2.0}/PKG-INFO +3 -3
- {c3d_parser-1.1.2 → c3d_parser-1.2.0}/requirements.txt +2 -2
- c3d_parser-1.2.0/src/c3d_parser/__init__.py +2 -0
- {c3d_parser-1.1.2 → c3d_parser-1.2.0}/src/c3d_parser/core/c3d_parser.py +14 -60
- {c3d_parser-1.1.2 → c3d_parser-1.2.0}/src/c3d_parser/core/osim.py +60 -0
- {c3d_parser-1.1.2 → c3d_parser-1.2.0}/src/c3d_parser/view/main_window.py +0 -19
- {c3d_parser-1.1.2 → c3d_parser-1.2.0/src/c3d_parser.egg-info}/PKG-INFO +3 -3
- {c3d_parser-1.1.2 → c3d_parser-1.2.0}/src/c3d_parser.egg-info/requires.txt +2 -2
- c3d_parser-1.1.2/src/c3d_parser/__init__.py +0 -2
- {c3d_parser-1.1.2 → c3d_parser-1.2.0}/LICENSE +0 -0
- {c3d_parser-1.1.2 → c3d_parser-1.2.0}/README.md +0 -0
- {c3d_parser-1.1.2 → c3d_parser-1.2.0}/pyproject.toml +0 -0
- {c3d_parser-1.1.2 → c3d_parser-1.2.0}/setup.cfg +0 -0
- {c3d_parser-1.1.2 → c3d_parser-1.2.0}/src/c3d_parser/application.py +0 -0
- {c3d_parser-1.1.2 → c3d_parser-1.2.0}/src/c3d_parser/core/c3d_patch.py +0 -0
- {c3d_parser-1.1.2 → c3d_parser-1.2.0}/src/c3d_parser/core/osim_resources/external_loads_template.xml +0 -0
- {c3d_parser-1.1.2 → c3d_parser-1.2.0}/src/c3d_parser/core/osim_resources/ik_task_set.xml +0 -0
- {c3d_parser-1.1.2 → c3d_parser-1.2.0}/src/c3d_parser/core/utils.py +0 -0
- {c3d_parser-1.1.2 → c3d_parser-1.2.0}/src/c3d_parser/settings/general.py +0 -0
- {c3d_parser-1.1.2 → c3d_parser-1.2.0}/src/c3d_parser/settings/logging.py +0 -0
- {c3d_parser-1.1.2 → c3d_parser-1.2.0}/src/c3d_parser/settings/marker_maps/AUC.json +0 -0
- {c3d_parser-1.1.2 → c3d_parser-1.2.0}/src/c3d_parser/settings/marker_maps/FMC.json +0 -0
- {c3d_parser-1.1.2 → c3d_parser-1.2.0}/src/c3d_parser/settings/marker_maps/MH.json +0 -0
- {c3d_parser-1.1.2 → c3d_parser-1.2.0}/src/c3d_parser/settings/marker_maps/QCMAS.json +0 -0
- {c3d_parser-1.1.2 → c3d_parser-1.2.0}/src/c3d_parser/settings/marker_maps/RBWH.json +0 -0
- {c3d_parser-1.1.2 → c3d_parser-1.2.0}/src/c3d_parser/settings/marker_maps/RCH.json +0 -0
- {c3d_parser-1.1.2 → c3d_parser-1.2.0}/src/c3d_parser/settings/marker_maps/Sydney.json +0 -0
- {c3d_parser-1.1.2 → c3d_parser-1.2.0}/src/c3d_parser/splash_rc.py +0 -0
- {c3d_parser-1.1.2 → c3d_parser-1.2.0}/src/c3d_parser/splashscreen.py +0 -0
- {c3d_parser-1.1.2 → c3d_parser-1.2.0}/src/c3d_parser/view/dialogs/about_dialog.py +0 -0
- {c3d_parser-1.1.2 → c3d_parser-1.2.0}/src/c3d_parser/view/dialogs/delete_marker_set_dialog.py +0 -0
- {c3d_parser-1.1.2 → c3d_parser-1.2.0}/src/c3d_parser/view/dialogs/marker_set_dialog.py +0 -0
- {c3d_parser-1.1.2 → c3d_parser-1.2.0}/src/c3d_parser/view/dialogs/marker_set_import_dialog.py +0 -0
- {c3d_parser-1.1.2 → c3d_parser-1.2.0}/src/c3d_parser/view/dialogs/options_dialog.py +0 -0
- {c3d_parser-1.1.2 → c3d_parser-1.2.0}/src/c3d_parser/view/ui/resources_rc.py +0 -0
- {c3d_parser-1.1.2 → c3d_parser-1.2.0}/src/c3d_parser/view/ui/ui_delete_marker_set_dialog.py +0 -0
- {c3d_parser-1.1.2 → c3d_parser-1.2.0}/src/c3d_parser/view/ui/ui_main_window.py +0 -0
- {c3d_parser-1.1.2 → c3d_parser-1.2.0}/src/c3d_parser/view/ui/ui_marker_set_dialog.py +0 -0
- {c3d_parser-1.1.2 → c3d_parser-1.2.0}/src/c3d_parser/view/ui/ui_marker_set_import_dialog.py +0 -0
- {c3d_parser-1.1.2 → c3d_parser-1.2.0}/src/c3d_parser/view/ui/ui_options_dialog.py +0 -0
- {c3d_parser-1.1.2 → c3d_parser-1.2.0}/src/c3d_parser/view/utils.py +0 -0
- {c3d_parser-1.1.2 → c3d_parser-1.2.0}/src/c3d_parser/view/widgets.py +0 -0
- {c3d_parser-1.1.2 → c3d_parser-1.2.0}/src/c3d_parser.egg-info/SOURCES.txt +0 -0
- {c3d_parser-1.1.2 → c3d_parser-1.2.0}/src/c3d_parser.egg-info/dependency_links.txt +0 -0
- {c3d_parser-1.1.2 → c3d_parser-1.2.0}/src/c3d_parser.egg-info/entry_points.txt +0 -0
- {c3d_parser-1.1.2 → c3d_parser-1.2.0}/src/c3d_parser.egg-info/top_level.txt +0 -0
- {c3d_parser-1.1.2 → c3d_parser-1.2.0}/tests/test_parser.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: c3d-parser
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2.0
|
|
4
4
|
Summary: C3D parser for gait data harmonisation.
|
|
5
5
|
Author-email: Timothy Salemink <tim.nicolas@outlook.com>, Sally Jack <sallyjaack@gmail.com>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -11,8 +11,8 @@ Requires-Dist: scipy
|
|
|
11
11
|
Requires-Dist: numpy==1.26.4
|
|
12
12
|
Requires-Dist: pandas
|
|
13
13
|
Requires-Dist: c3d==0.5.2
|
|
14
|
-
Requires-Dist: trc-data-reader
|
|
15
|
-
Requires-Dist: opensim-model-creator==0.
|
|
14
|
+
Requires-Dist: trc-data-reader>=0.4.1
|
|
15
|
+
Requires-Dist: opensim-model-creator==0.7.0
|
|
16
16
|
Requires-Dist: PySide6
|
|
17
17
|
Requires-Dist: matplotlib
|
|
18
18
|
Requires-Dist: mplcursors
|
|
@@ -18,7 +18,7 @@ from opensim_model_creator.Create_Model import create_model
|
|
|
18
18
|
|
|
19
19
|
from c3d_parser.core.c3d_patch import c3d
|
|
20
20
|
from c3d_parser.core.utils import clear_directory
|
|
21
|
-
from c3d_parser.core.osim import perform_ik, perform_id
|
|
21
|
+
from c3d_parser.core.osim import perform_ik, perform_id, calculate_foot_progression_angles
|
|
22
22
|
from c3d_parser.settings.general import get_marker_maps_dir
|
|
23
23
|
from c3d_parser.settings.logging import logger
|
|
24
24
|
from c3d_parser.settings.general import VERSION
|
|
@@ -53,7 +53,6 @@ def parse_session(static_trial, dynamic_trials, input_directory, output_director
|
|
|
53
53
|
grf_data = {}
|
|
54
54
|
event_data = {}
|
|
55
55
|
spatiotemporal_data = {}
|
|
56
|
-
foot_progression_data = {}
|
|
57
56
|
kinematic_data = {}
|
|
58
57
|
kinetic_data = {}
|
|
59
58
|
|
|
@@ -66,7 +65,7 @@ def parse_session(static_trial, dynamic_trials, input_directory, output_director
|
|
|
66
65
|
for trial_index, trial in enumerate(dynamic_trials, start=1):
|
|
67
66
|
file_path = os.path.normpath(os.path.join(input_directory, trial))
|
|
68
67
|
try:
|
|
69
|
-
analog_data, events,
|
|
68
|
+
analog_data, events, s_t_data, trc_file_path, grf_file_path = \
|
|
70
69
|
parse_dynamic_trial(file_path, lab, output_directory, trial_index, marker_data_rate, static_data,
|
|
71
70
|
filter_trc, filter_grf)
|
|
72
71
|
except ParserError as e:
|
|
@@ -76,7 +75,6 @@ def parse_session(static_trial, dynamic_trials, input_directory, output_director
|
|
|
76
75
|
grf_data[trial] = analog_data
|
|
77
76
|
event_data[trial] = events
|
|
78
77
|
spatiotemporal_data[trial] = s_t_data
|
|
79
|
-
foot_progression_data[trial] = foot_progression
|
|
80
78
|
|
|
81
79
|
trc_file_paths[trial] = trc_file_path
|
|
82
80
|
grf_file_paths[trial] = grf_file_path
|
|
@@ -96,9 +94,12 @@ def parse_session(static_trial, dynamic_trials, input_directory, output_director
|
|
|
96
94
|
|
|
97
95
|
for trial in trc_file_paths.keys():
|
|
98
96
|
logger.info(f"Running IK and ID for {trial}.")
|
|
99
|
-
ik_data, ik_output = run_ik(osim_model, trc_file_paths[trial], output_directory,
|
|
100
|
-
|
|
101
|
-
|
|
97
|
+
ik_data, ik_output = run_ik(osim_model, trc_file_paths[trial], output_directory, ik_task_set)
|
|
98
|
+
foot_progression = calculate_foot_progression_angles(osim_model, ik_output)
|
|
99
|
+
ik_data = pd.concat([ik_data, foot_progression], axis=1)
|
|
100
|
+
filter_data(ik_data, marker_data_rate)
|
|
101
|
+
|
|
102
|
+
id_data = run_id(osim_model, ik_data, ik_output, grf_file_paths[trial], output_directory, event_data[trial], weight)
|
|
102
103
|
|
|
103
104
|
kinematic_data[trial] = ik_data
|
|
104
105
|
kinetic_data[trial] = id_data
|
|
@@ -220,12 +221,9 @@ def parse_dynamic_trial(c3d_file, lab, output_directory, trial_index, marker_dat
|
|
|
220
221
|
set_marker_data(trc_data, frame_data, rate=marker_data_rate)
|
|
221
222
|
trc_file_path = write_trc_data(trc_data, output_file_name, output_directory)
|
|
222
223
|
|
|
223
|
-
foot_progression = calculate_foot_progression_angles(frame_data)
|
|
224
|
-
resample_data(foot_progression, trc_data['DataRate'], marker_data_rate)
|
|
225
|
-
|
|
226
224
|
s_t_data = calculate_spatiotemporal_data(frame_data, events, static_data)
|
|
227
225
|
|
|
228
|
-
return analog_data, events,
|
|
226
|
+
return analog_data, events, s_t_data, trc_file_path, grf_file_path
|
|
229
227
|
|
|
230
228
|
|
|
231
229
|
def write_c3d_parser_history(input_directory, static_trial, deidentified_file_names):
|
|
@@ -248,7 +246,7 @@ def write_c3d_parser_history(input_directory, static_trial, deidentified_file_na
|
|
|
248
246
|
raise ParserError("Could not write c3d_parser_history.log")
|
|
249
247
|
|
|
250
248
|
|
|
251
|
-
def run_ik(osim_model, trc_file_path, output_directory,
|
|
249
|
+
def run_ik(osim_model, trc_file_path, output_directory, ik_task_set):
|
|
252
250
|
# Perform inverse kinematics.
|
|
253
251
|
file_name = os.path.splitext(os.path.basename(trc_file_path))[0]
|
|
254
252
|
ik_directory = os.path.join(output_directory, 'ik')
|
|
@@ -257,12 +255,11 @@ def run_ik(osim_model, trc_file_path, output_directory, marker_data_rate, ik_tas
|
|
|
257
255
|
ik_output = os.path.join(ik_directory, f"{file_name}_IK.mot")
|
|
258
256
|
perform_ik(osim_model, trc_file_path, ik_output, ik_task_set)
|
|
259
257
|
ik_data = read_data(ik_output)
|
|
260
|
-
filter_data(ik_data, marker_data_rate)
|
|
261
258
|
|
|
262
259
|
return ik_data, ik_output
|
|
263
260
|
|
|
264
261
|
|
|
265
|
-
def run_id(osim_model, ik_data, ik_output, grf_file_path, output_directory,
|
|
262
|
+
def run_id(osim_model, ik_data, ik_output, grf_file_path, output_directory, events, subject_mass):
|
|
266
263
|
# Perform inverse dynamics.
|
|
267
264
|
file_name = os.path.basename(grf_file_path).replace("_grf.mot", "")
|
|
268
265
|
id_directory = os.path.join(output_directory, 'id')
|
|
@@ -384,21 +381,18 @@ def convert_old_marker_map(map_file, marker_mapping):
|
|
|
384
381
|
return marker_set
|
|
385
382
|
|
|
386
383
|
|
|
387
|
-
# TODO: The marker set should not be so sensitive to case...!
|
|
388
384
|
def harmonise_markers(frame_data, lab, required_markers):
|
|
389
385
|
marker_set = get_marker_map(lab)
|
|
390
386
|
|
|
391
387
|
# Harmonise marker labels.
|
|
392
|
-
reversed_mapping = {value: key for key, value in marker_set.items() if value is not None}
|
|
393
|
-
header_mapping = {header: reversed_mapping.get(header, None) for header in frame_data.columns[1:]}
|
|
388
|
+
reversed_mapping = {value.upper(): key for key, value in marker_set.items() if value is not None}
|
|
389
|
+
header_mapping = {header: reversed_mapping.get(header.upper(), None) for header in frame_data.columns[1:]}
|
|
394
390
|
frame_data.rename(columns=header_mapping, inplace=True)
|
|
395
391
|
|
|
396
392
|
# Filter out non-harmonised data points.
|
|
397
393
|
if None in frame_data.columns:
|
|
398
394
|
frame_data.drop(columns=frame_data.columns[frame_data.columns.isna()], inplace=True)
|
|
399
395
|
|
|
400
|
-
# TODO: This error window does not indicate which trial has the issue.
|
|
401
|
-
# Update this.
|
|
402
396
|
# Ensure required markers are present.
|
|
403
397
|
available = set(frame_data.columns)
|
|
404
398
|
for item in required_markers:
|
|
@@ -454,10 +448,8 @@ def trim_frames(frame_data):
|
|
|
454
448
|
if not drop_frames.empty:
|
|
455
449
|
frame_data.drop(drop_frames, inplace=True)
|
|
456
450
|
|
|
457
|
-
# TODO: If we convert this to a dictionary we can keep the markers that are missing and check if they are required.
|
|
458
451
|
remaining_frames = [frame for frame in incomplete_frames.keys() if trim_start <= frame <= trim_end]
|
|
459
452
|
if remaining_frames:
|
|
460
|
-
# TODO: We should raise a ParserError (skip this trial) if the missing markers are required...?
|
|
461
453
|
logger.warn(f"Frames {remaining_frames} are incomplete.")
|
|
462
454
|
|
|
463
455
|
return trim_start, trim_end
|
|
@@ -633,11 +625,6 @@ def extract_data(file_path, start_frame, end_frame):
|
|
|
633
625
|
analog_data[label].extend(analog[j - 1])
|
|
634
626
|
analog_data = pd.DataFrame(analog_data)
|
|
635
627
|
|
|
636
|
-
# TODO: ACTUALLY, this would also be a good place to just apply the channel order to the analog columns...
|
|
637
|
-
# ???
|
|
638
|
-
# We will need this if we want to support the files with the deleted plates...!
|
|
639
|
-
# And their new force plate setup...!
|
|
640
|
-
|
|
641
628
|
# Extract event information.
|
|
642
629
|
event_group = get_metadata(reader, 'EVENT')
|
|
643
630
|
event_count = get_metadata(event_group, 'USED').int8_value
|
|
@@ -664,6 +651,7 @@ def extract_data(file_path, start_frame, end_frame):
|
|
|
664
651
|
stride_numbers = {"Left": 0, "Right": 0}
|
|
665
652
|
for foot, event in events.items():
|
|
666
653
|
for event_time, event_type in event.items():
|
|
654
|
+
event_type = event_type.title()
|
|
667
655
|
if event_type == "Foot Strike":
|
|
668
656
|
stride_numbers[foot] += 1
|
|
669
657
|
stride_number = stride_numbers[foot]
|
|
@@ -1019,8 +1007,6 @@ def scale_grf_data(analog_data):
|
|
|
1019
1007
|
analog_data[columns] = analog_data[columns] / 1000
|
|
1020
1008
|
|
|
1021
1009
|
|
|
1022
|
-
# TODO: This incorrectly classifies Adelaide's static trials as dynamic.
|
|
1023
|
-
# Use `calculate_distance_covered`...?
|
|
1024
1010
|
def is_dynamic(file_path):
|
|
1025
1011
|
with open(file_path, 'rb') as handle:
|
|
1026
1012
|
try:
|
|
@@ -1187,11 +1173,6 @@ def normalise_kinetics(kinetic_data, events):
|
|
|
1187
1173
|
normalised_data[foot][file_name] = {}
|
|
1188
1174
|
data_segment = data.iloc[start:frame, 1:]
|
|
1189
1175
|
|
|
1190
|
-
# TODO: If the model +tve and -tve are wrong, then we can't just flip these.
|
|
1191
|
-
# Because the non-GRF parts of the moments would also be flipped.
|
|
1192
|
-
# Just visually check the results against some clinical kinetics.
|
|
1193
|
-
# ...
|
|
1194
|
-
# Looks pretty good so far.
|
|
1195
1176
|
# Perform sign transformations.
|
|
1196
1177
|
data_segment[f"hip_flexion_{side}_moment"] = -data_segment[f"hip_flexion_{side}_moment"]
|
|
1197
1178
|
data_segment[f"hip_adduction_{side}_moment"] = -data_segment[f"hip_adduction_{side}_moment"]
|
|
@@ -1425,33 +1406,6 @@ def calculate_walking_direction(frame_data):
|
|
|
1425
1406
|
return walking_direction
|
|
1426
1407
|
|
|
1427
1408
|
|
|
1428
|
-
# TODO: First, we need to calculate the static flexion angle between the foot vector (heel to toe) and the floor.
|
|
1429
|
-
# (before this function)
|
|
1430
|
-
# Then, when we do this, we should adjust the heel position using a rotation matrix defined by that flexion angle.
|
|
1431
|
-
# (before projecting on to the floor)
|
|
1432
|
-
def calculate_foot_progression_angles(frame_data):
|
|
1433
|
-
walking_direction = calculate_walking_direction(frame_data)
|
|
1434
|
-
walking_angle = np.arctan2(walking_direction[1], walking_direction[0])
|
|
1435
|
-
|
|
1436
|
-
foot_progression = pd.DataFrame()
|
|
1437
|
-
for foot in ['Left', 'Right']:
|
|
1438
|
-
side = foot[0]
|
|
1439
|
-
heel_xz = np.stack(frame_data[f"{side}HEE"].values)[:, [0, 2]]
|
|
1440
|
-
toe_xz = np.stack(frame_data[f"{side}TOE"].values)[:, [0, 2]]
|
|
1441
|
-
|
|
1442
|
-
foot_vectors = toe_xz - heel_xz
|
|
1443
|
-
foot_unit_vectors = foot_vectors / np.linalg.norm(foot_vectors, axis=1, keepdims=True)
|
|
1444
|
-
foot_angles = np.arctan2(foot_unit_vectors[:, 1], foot_unit_vectors[:, 0])
|
|
1445
|
-
angles = np.degrees(foot_angles - walking_angle)
|
|
1446
|
-
|
|
1447
|
-
if foot == 'Right':
|
|
1448
|
-
angles = -angles
|
|
1449
|
-
|
|
1450
|
-
foot_progression[f'foot_progression_{side.lower()}'] = angles
|
|
1451
|
-
|
|
1452
|
-
return foot_progression
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
1409
|
def normalise_angle(angle):
|
|
1456
1410
|
if angle < -180:
|
|
1457
1411
|
angle += 360
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
|
|
2
2
|
import os
|
|
3
3
|
import copy
|
|
4
|
+
import numpy as np
|
|
5
|
+
import pandas as pd
|
|
4
6
|
import opensim as osim
|
|
5
7
|
import xml.etree.ElementTree as ET
|
|
6
8
|
|
|
9
|
+
from scipy.spatial.transform import Rotation
|
|
10
|
+
|
|
7
11
|
from c3d_parser.settings.logging import logger
|
|
8
12
|
|
|
9
13
|
|
|
@@ -95,3 +99,59 @@ def setup_external_loads(output_directory, grf_file):
|
|
|
95
99
|
tree.write(external_loads_file)
|
|
96
100
|
|
|
97
101
|
return external_loads_file
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def calculate_foot_progression_angles(model_file, ik_file):
|
|
105
|
+
|
|
106
|
+
# Load model information.
|
|
107
|
+
model = osim.Model(model_file)
|
|
108
|
+
model.initSystem()
|
|
109
|
+
coordinate_set = model.getCoordinateSet()
|
|
110
|
+
n_coordinates = coordinate_set.getSize()
|
|
111
|
+
|
|
112
|
+
# Load IK solution.
|
|
113
|
+
storage = osim.Storage(ik_file)
|
|
114
|
+
labels = storage.getColumnLabels()
|
|
115
|
+
n_frames = storage.getSize()
|
|
116
|
+
|
|
117
|
+
foot_progression = {"l": [], "r": []}
|
|
118
|
+
for i in range(n_frames):
|
|
119
|
+
|
|
120
|
+
# Build a state vector from the IK solution at this frame.
|
|
121
|
+
time_stamp = storage.getStateVector(i).getTime()
|
|
122
|
+
state = model.initSystem()
|
|
123
|
+
state.setTime(time_stamp)
|
|
124
|
+
ik_row = storage.getStateVector(i).getData()
|
|
125
|
+
|
|
126
|
+
# Set body coordinates from the IK solution.
|
|
127
|
+
for j in range(n_coordinates):
|
|
128
|
+
coordinate = coordinate_set.get(j)
|
|
129
|
+
coordinate_name = coordinate.getName()
|
|
130
|
+
column_index = labels.findIndex(coordinate_name)
|
|
131
|
+
if column_index >= 0:
|
|
132
|
+
value = ik_row.get(column_index - 1)
|
|
133
|
+
|
|
134
|
+
if coordinate.getMotionType() == osim.Coordinate.Rotational:
|
|
135
|
+
value = np.deg2rad(value)
|
|
136
|
+
coordinate.setValue(state, value)
|
|
137
|
+
model.realizePosition(state)
|
|
138
|
+
|
|
139
|
+
# Extract foot segment rotation.
|
|
140
|
+
for side in foot_progression.keys():
|
|
141
|
+
foot_body = model.getBodySet().get(f"calcn_{side}")
|
|
142
|
+
transform = foot_body.getTransformInGround(state)
|
|
143
|
+
|
|
144
|
+
# Decompose rotation matrix into ZXY Euler angles.
|
|
145
|
+
rotation_matrix = np.array([[transform.R().get(row, col) for col in range(3)] for row in range(3)])
|
|
146
|
+
angles = Rotation.from_matrix(rotation_matrix).as_euler('ZXY')
|
|
147
|
+
foot_progression_angle = np.rad2deg(angles[2])
|
|
148
|
+
|
|
149
|
+
if side == "l":
|
|
150
|
+
foot_progression_angle = -foot_progression_angle
|
|
151
|
+
foot_progression[side].append(foot_progression_angle)
|
|
152
|
+
|
|
153
|
+
data_frame = pd.DataFrame({
|
|
154
|
+
"foot_progression_l": foot_progression["l"],
|
|
155
|
+
"foot_progression_r": foot_progression["r"],
|
|
156
|
+
})
|
|
157
|
+
return data_frame
|
|
@@ -57,13 +57,6 @@ class ProgressTracker(QObject):
|
|
|
57
57
|
progress = Signal(str, str)
|
|
58
58
|
|
|
59
59
|
|
|
60
|
-
# TODO: Add the deidentified name to the column in the file details.
|
|
61
|
-
# Instead of just "Dynamic", "Dynamic_1" or whatever.
|
|
62
|
-
# ...
|
|
63
|
-
# Nah just create local mapping file directly in input directory.
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
# TODO: CGM2 event detector on GitHub...?
|
|
67
60
|
class MainWindow(QMainWindow):
|
|
68
61
|
|
|
69
62
|
def __init__(self):
|
|
@@ -251,12 +244,6 @@ class MainWindow(QMainWindow):
|
|
|
251
244
|
table.deleteLater()
|
|
252
245
|
self._spatiotemporal_tables = []
|
|
253
246
|
|
|
254
|
-
# TODO: The legend is not correctly displayed if the ST tab is open while processing...
|
|
255
|
-
# Repositioning and updating the legend do nothing.
|
|
256
|
-
|
|
257
|
-
# TODO: The border is still having issues.
|
|
258
|
-
# Seems to be related to the area being scrollable.
|
|
259
|
-
# Only happens on some PCs/monitors...
|
|
260
247
|
def _visualise_spatiotemporal_data(self):
|
|
261
248
|
layout = self._ui.scrollAreaSpatiotemporal.layout()
|
|
262
249
|
self._clear_spatiotemporal_data()
|
|
@@ -882,7 +869,6 @@ class MainWindow(QMainWindow):
|
|
|
882
869
|
self._update_s_t_colours()
|
|
883
870
|
self._update_legend()
|
|
884
871
|
|
|
885
|
-
# TODO: For some reason the legend doesn't get created at all if we are visualising the ST tab while processing...
|
|
886
872
|
def _update_legend(self):
|
|
887
873
|
if self._boxes:
|
|
888
874
|
for side, boxes in self._boxes.items():
|
|
@@ -970,8 +956,6 @@ class MainWindow(QMainWindow):
|
|
|
970
956
|
settings.beginGroup('MainWindow')
|
|
971
957
|
settings.setValue('size', self.size())
|
|
972
958
|
settings.setValue('pos', self.pos())
|
|
973
|
-
# TODO: The taskbar icon fails if the application opens in full-screen...
|
|
974
|
-
# DON'T open in full screen...?
|
|
975
959
|
settings.setValue('is_maximized', self.isMaximized())
|
|
976
960
|
settings.setValue('lab', self._ui.comboBoxLab.currentText())
|
|
977
961
|
settings.setValue('marker_diameter', self._ui.doubleSpinBoxMarkerDiameter.value())
|
|
@@ -1158,9 +1142,6 @@ class GaitCurves(defaultdict):
|
|
|
1158
1142
|
line.set_color(colour)
|
|
1159
1143
|
line.set_zorder(z_order)
|
|
1160
1144
|
|
|
1161
|
-
# TODO: Replace all these with `draw_idle`?
|
|
1162
|
-
# Just the ones that handle many curves...?
|
|
1163
|
-
# Probably all...?
|
|
1164
1145
|
self._canvas.draw()
|
|
1165
1146
|
|
|
1166
1147
|
def show_curve_menu(self, event):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: c3d-parser
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2.0
|
|
4
4
|
Summary: C3D parser for gait data harmonisation.
|
|
5
5
|
Author-email: Timothy Salemink <tim.nicolas@outlook.com>, Sally Jack <sallyjaack@gmail.com>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -11,8 +11,8 @@ Requires-Dist: scipy
|
|
|
11
11
|
Requires-Dist: numpy==1.26.4
|
|
12
12
|
Requires-Dist: pandas
|
|
13
13
|
Requires-Dist: c3d==0.5.2
|
|
14
|
-
Requires-Dist: trc-data-reader
|
|
15
|
-
Requires-Dist: opensim-model-creator==0.
|
|
14
|
+
Requires-Dist: trc-data-reader>=0.4.1
|
|
15
|
+
Requires-Dist: opensim-model-creator==0.7.0
|
|
16
16
|
Requires-Dist: PySide6
|
|
17
17
|
Requires-Dist: matplotlib
|
|
18
18
|
Requires-Dist: mplcursors
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{c3d_parser-1.1.2 → c3d_parser-1.2.0}/src/c3d_parser/core/osim_resources/external_loads_template.xml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{c3d_parser-1.1.2 → c3d_parser-1.2.0}/src/c3d_parser/view/dialogs/delete_marker_set_dialog.py
RENAMED
|
File without changes
|
|
File without changes
|
{c3d_parser-1.1.2 → c3d_parser-1.2.0}/src/c3d_parser/view/dialogs/marker_set_import_dialog.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|