c3d-parser 1.5.4__tar.gz → 1.6.1__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.
Files changed (47) hide show
  1. {c3d_parser-1.5.4/src/c3d_parser.egg-info → c3d_parser-1.6.1}/PKG-INFO +2 -2
  2. {c3d_parser-1.5.4 → c3d_parser-1.6.1}/requirements.txt +1 -1
  3. c3d_parser-1.6.1/src/c3d_parser/__init__.py +2 -0
  4. {c3d_parser-1.5.4 → c3d_parser-1.6.1}/src/c3d_parser/core/c3d_parser.py +55 -10
  5. {c3d_parser-1.5.4 → c3d_parser-1.6.1}/src/c3d_parser/view/main_window.py +25 -36
  6. {c3d_parser-1.5.4 → c3d_parser-1.6.1/src/c3d_parser.egg-info}/PKG-INFO +2 -2
  7. {c3d_parser-1.5.4 → c3d_parser-1.6.1}/src/c3d_parser.egg-info/requires.txt +1 -1
  8. c3d_parser-1.5.4/src/c3d_parser/__init__.py +0 -2
  9. {c3d_parser-1.5.4 → c3d_parser-1.6.1}/LICENSE +0 -0
  10. {c3d_parser-1.5.4 → c3d_parser-1.6.1}/README.md +0 -0
  11. {c3d_parser-1.5.4 → c3d_parser-1.6.1}/pyproject.toml +0 -0
  12. {c3d_parser-1.5.4 → c3d_parser-1.6.1}/setup.cfg +0 -0
  13. {c3d_parser-1.5.4 → c3d_parser-1.6.1}/src/c3d_parser/application.py +0 -0
  14. {c3d_parser-1.5.4 → c3d_parser-1.6.1}/src/c3d_parser/core/c3d_patch.py +0 -0
  15. {c3d_parser-1.5.4 → c3d_parser-1.6.1}/src/c3d_parser/core/osim.py +0 -0
  16. {c3d_parser-1.5.4 → c3d_parser-1.6.1}/src/c3d_parser/core/osim_resources/external_loads_template.xml +0 -0
  17. {c3d_parser-1.5.4 → c3d_parser-1.6.1}/src/c3d_parser/core/osim_resources/ik_task_set.xml +0 -0
  18. {c3d_parser-1.5.4 → c3d_parser-1.6.1}/src/c3d_parser/core/utils.py +0 -0
  19. {c3d_parser-1.5.4 → c3d_parser-1.6.1}/src/c3d_parser/settings/general.py +0 -0
  20. {c3d_parser-1.5.4 → c3d_parser-1.6.1}/src/c3d_parser/settings/logging.py +0 -0
  21. {c3d_parser-1.5.4 → c3d_parser-1.6.1}/src/c3d_parser/settings/marker_maps/AUC.json +0 -0
  22. {c3d_parser-1.5.4 → c3d_parser-1.6.1}/src/c3d_parser/settings/marker_maps/FMC.json +0 -0
  23. {c3d_parser-1.5.4 → c3d_parser-1.6.1}/src/c3d_parser/settings/marker_maps/MH.json +0 -0
  24. {c3d_parser-1.5.4 → c3d_parser-1.6.1}/src/c3d_parser/settings/marker_maps/QCMAS.json +0 -0
  25. {c3d_parser-1.5.4 → c3d_parser-1.6.1}/src/c3d_parser/settings/marker_maps/RBWH.json +0 -0
  26. {c3d_parser-1.5.4 → c3d_parser-1.6.1}/src/c3d_parser/settings/marker_maps/RCH.json +0 -0
  27. {c3d_parser-1.5.4 → c3d_parser-1.6.1}/src/c3d_parser/settings/marker_maps/Sydney.json +0 -0
  28. {c3d_parser-1.5.4 → c3d_parser-1.6.1}/src/c3d_parser/splash_rc.py +0 -0
  29. {c3d_parser-1.5.4 → c3d_parser-1.6.1}/src/c3d_parser/splashscreen.py +0 -0
  30. {c3d_parser-1.5.4 → c3d_parser-1.6.1}/src/c3d_parser/view/dialogs/about_dialog.py +0 -0
  31. {c3d_parser-1.5.4 → c3d_parser-1.6.1}/src/c3d_parser/view/dialogs/delete_marker_set_dialog.py +0 -0
  32. {c3d_parser-1.5.4 → c3d_parser-1.6.1}/src/c3d_parser/view/dialogs/marker_set_dialog.py +0 -0
  33. {c3d_parser-1.5.4 → c3d_parser-1.6.1}/src/c3d_parser/view/dialogs/marker_set_import_dialog.py +0 -0
  34. {c3d_parser-1.5.4 → c3d_parser-1.6.1}/src/c3d_parser/view/dialogs/options_dialog.py +0 -0
  35. {c3d_parser-1.5.4 → c3d_parser-1.6.1}/src/c3d_parser/view/ui/resources_rc.py +0 -0
  36. {c3d_parser-1.5.4 → c3d_parser-1.6.1}/src/c3d_parser/view/ui/ui_delete_marker_set_dialog.py +0 -0
  37. {c3d_parser-1.5.4 → c3d_parser-1.6.1}/src/c3d_parser/view/ui/ui_main_window.py +0 -0
  38. {c3d_parser-1.5.4 → c3d_parser-1.6.1}/src/c3d_parser/view/ui/ui_marker_set_dialog.py +0 -0
  39. {c3d_parser-1.5.4 → c3d_parser-1.6.1}/src/c3d_parser/view/ui/ui_marker_set_import_dialog.py +0 -0
  40. {c3d_parser-1.5.4 → c3d_parser-1.6.1}/src/c3d_parser/view/ui/ui_options_dialog.py +0 -0
  41. {c3d_parser-1.5.4 → c3d_parser-1.6.1}/src/c3d_parser/view/utils.py +0 -0
  42. {c3d_parser-1.5.4 → c3d_parser-1.6.1}/src/c3d_parser/view/widgets.py +0 -0
  43. {c3d_parser-1.5.4 → c3d_parser-1.6.1}/src/c3d_parser.egg-info/SOURCES.txt +0 -0
  44. {c3d_parser-1.5.4 → c3d_parser-1.6.1}/src/c3d_parser.egg-info/dependency_links.txt +0 -0
  45. {c3d_parser-1.5.4 → c3d_parser-1.6.1}/src/c3d_parser.egg-info/entry_points.txt +0 -0
  46. {c3d_parser-1.5.4 → c3d_parser-1.6.1}/src/c3d_parser.egg-info/top_level.txt +0 -0
  47. {c3d_parser-1.5.4 → c3d_parser-1.6.1}/tests/test_parser.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: c3d-parser
3
- Version: 1.5.4
3
+ Version: 1.6.1
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
@@ -12,7 +12,7 @@ Requires-Dist: numpy==1.26.4
12
12
  Requires-Dist: pandas
13
13
  Requires-Dist: c3d==0.5.2
14
14
  Requires-Dist: trc-data-reader>=0.4.1
15
- Requires-Dist: opensim-model-creator==0.8.3
15
+ Requires-Dist: opensim-model-creator==0.10.1
16
16
  Requires-Dist: PySide6
17
17
  Requires-Dist: matplotlib
18
18
  Requires-Dist: mplcursors
@@ -3,7 +3,7 @@ numpy==1.26.4
3
3
  pandas
4
4
  c3d==0.5.2
5
5
  trc-data-reader>=0.4.1
6
- opensim-model-creator==0.8.3
6
+ opensim-model-creator==0.10.1
7
7
  PySide6
8
8
  matplotlib
9
9
  mplcursors
@@ -0,0 +1,2 @@
1
+
2
+ __version__ = "1.6.1"
@@ -16,7 +16,6 @@ from decimal import Decimal, ROUND_CEILING, ROUND_FLOOR
16
16
 
17
17
  from trc import TRCData
18
18
  from opensim_model_creator.Create_Model import create_model
19
- from ll_visualiser.utils import get_fit_metrics, load_landmarks, define_measurements
20
19
 
21
20
  from c3d_parser.core.c3d_patch import c3d
22
21
  from c3d_parser.core.utils import clear_directory
@@ -38,11 +37,15 @@ torso_markers = ["C7", "T2", "T10", "MAN"]
38
37
  required_markers = [{"LASI", "RASI"}, {"LKNE", "RKNE"}, {"LANK", "RANK"}, {"LMED", "RMED"}, {"LHEE", "RHEE"},
39
38
  {"LTOE", "RTOE"}, ({"LPSI", "RPSI"}, {"SACR"}), ({"LKNEM", "RKNEM"}, {"LKAX", "RKAX"})]
40
39
 
40
+ required_measurements = ['Sex', 'Age', 'Height', 'Weight', 'ASIS Width', 'Left Knee Width', 'Right Knee Width',
41
+ 'Left Ankle Width', 'Right Ankle Width']
42
+
41
43
 
42
44
  def parse_session(static_trial, dynamic_trials, input_directory, output_directory, lab, marker_diameter, static_data,
43
45
  left_foot_flat, right_foot_flat, toe_marker_proximal, optimise_knee_axis, filter_trc, filter_grf,
44
46
  ik_task_set, running_gait, progress_tracker):
45
47
 
48
+ validate_static_data(static_data)
46
49
  clear_directory(output_directory)
47
50
 
48
51
  logger.info(f"Processing session {os.path.normpath(input_directory)}.")
@@ -89,7 +92,8 @@ def parse_session(static_trial, dynamic_trials, input_directory, output_director
89
92
  output_directory, left_foot_flat, right_foot_flat, toe_marker_proximal,
90
93
  optimise_knee_axis, progress_tracker)
91
94
 
92
- write_c3d_parser_history(input_directory, output_directory, static_trial, deidentified_file_names, static_data)
95
+ write_c3d_parser_history(input_directory, output_directory, lab, static_trial, deidentified_file_names, static_data,
96
+ left_foot_flat, right_foot_flat, toe_marker_proximal)
93
97
 
94
98
  if not trc_file_paths:
95
99
  raise CancelException("No dynamic trials found.")
@@ -127,16 +131,26 @@ def approximate_anthropometrics(c3d_file, lab, marker_diameter):
127
131
 
128
132
  def calculate_anthropometrics(frame_data, marker_diameter):
129
133
 
130
- def distance(frame, landmark_1, landmark_2):
131
- return round(np.linalg.norm(np.array(frame[landmark_1]) - np.array(frame[landmark_2])), 1)
134
+ def distance(frame, landmark_1, landmark_2, marker_diameter=0):
135
+ missing = [landmark for landmark in (landmark_1, landmark_2) if landmark not in frame]
136
+ if missing:
137
+ logger.warn(f"Unable to approximate distance between {landmark_1} and {landmark_2}. "
138
+ f"Missing marker(s): {', '.join(missing)}.")
139
+ return 0
140
+
141
+ point_1 = np.array(frame[landmark_1])
142
+ point_2 = np.array(frame[landmark_2])
143
+ length = round(np.linalg.norm(point_1 - point_2), 1)
144
+
145
+ return length - marker_diameter
132
146
 
133
147
  mid_frame = frame_data.iloc[len(frame_data) // 2].copy()
134
148
 
135
149
  anthropometrics = {
136
- 'left_knee_width': distance(mid_frame, 'LKNE', 'LKNEM') - marker_diameter,
137
- 'right_knee_width': distance(mid_frame, 'RKNE', 'RKNEM') - marker_diameter,
138
- 'left_ankle_width': distance(mid_frame, 'LANK', 'LMED') - marker_diameter,
139
- 'right_ankle_width': distance(mid_frame, 'RANK', 'RMED') - marker_diameter,
150
+ 'left_knee_width': distance(mid_frame, 'LKNE', 'LKNEM', marker_diameter),
151
+ 'right_knee_width': distance(mid_frame, 'RKNE', 'RKNEM', marker_diameter),
152
+ 'left_ankle_width': distance(mid_frame, 'LANK', 'LMED', marker_diameter),
153
+ 'right_ankle_width': distance(mid_frame, 'RANK', 'RMED', marker_diameter),
140
154
  'left_leg_length': distance(mid_frame, 'LASI', 'LMED'),
141
155
  'right_leg_length': distance(mid_frame, 'RASI', 'RMED'),
142
156
  'inter_asis_distance': distance(mid_frame, 'LASI', 'RASI'),
@@ -238,15 +252,18 @@ def parse_dynamic_trial(c3d_file, lab, output_directory, trial_index, marker_dat
238
252
  return analog_data, events, s_t_data, trc_file_path, grf_file_path
239
253
 
240
254
 
241
- def write_c3d_parser_history(input_directory, output_directory, static_trial, deidentified_file_names, static_data):
255
+ def write_c3d_parser_history(input_directory, output_directory, lab, static_trial, deidentified_file_names, static_data,
256
+ left_foot_flat, right_foot_flat, toe_marker_proximal):
242
257
  log_path = os.path.join(input_directory, "c3d_parser_history.log")
243
258
  timestamp = datetime.now().strftime("%Y-%m-%d %H:%M")
244
259
 
245
260
  try:
246
261
  with open(log_path, "w", encoding="utf-8") as f:
247
262
  f.write("C3D-Parser History\n")
248
- f.write(f"Last Run: {timestamp}\n\n")
263
+ f.write(f"Last Run: {timestamp}\n")
264
+ f.write(f"C3D-Parser Version: {VERSION}\n\n")
249
265
 
266
+ f.write(f"Lab: {lab}\n")
250
267
  f.write(f"Static trial: {static_trial}\n\n")
251
268
 
252
269
  if deidentified_file_names:
@@ -273,6 +290,12 @@ def write_c3d_parser_history(input_directory, output_directory, static_trial, de
273
290
  unit = f" {units[key]}" if key in units else ""
274
291
  f.write(f"{label}: {value}{unit}\n")
275
292
 
293
+ f.write("\n")
294
+ f.write("Foot Settings:\n")
295
+ f.write(f"Toe Marker: {'Proximal' if toe_marker_proximal else '2nd/3rd Met'}\n")
296
+ f.write(f"Left Foot Flat: {left_foot_flat}\n")
297
+ f.write(f"Right Foot Flat: {right_foot_flat}\n")
298
+
276
299
  metrics, measurements = define_metrics_and_measurements(output_directory)
277
300
  if metrics:
278
301
  f.write("\n")
@@ -290,6 +313,8 @@ def write_c3d_parser_history(input_directory, output_directory, static_trial, de
290
313
 
291
314
 
292
315
  def define_metrics_and_measurements(output_directory):
316
+ from ll_visualiser.utils import get_fit_metrics, load_landmarks, define_measurements
317
+
293
318
  model_directory = os.path.join(output_directory, "Models", "Meshes")
294
319
  left_predicted_landmark_file = os.path.join(model_directory, 'predicted_lms_left.txt')
295
320
  right_predicted_landmark_file = os.path.join(model_directory, 'predicted_lms_right.txt')
@@ -1700,3 +1725,23 @@ def get_subject_info(static_data):
1700
1725
  'right_malleolar_width'
1701
1726
  ])
1702
1727
  return info
1728
+
1729
+
1730
+ def height_to_mm(height):
1731
+ if 0 < height < 300:
1732
+ logger.info(f"Subject height ({height}) assumed to be in cm. Converting to mm.")
1733
+ return height * 10
1734
+
1735
+ return height
1736
+
1737
+
1738
+ def validate_static_data(static_data):
1739
+ missing = [key for key in required_measurements if not static_data.get(key)]
1740
+ if missing:
1741
+ raise ParserError("Subject measurements missing:\n- " + "\n- ".join(missing))
1742
+
1743
+ static_data['Height'] = height_to_mm(static_data['Height'])
1744
+
1745
+ # Cap age at 18.
1746
+ # Current shape model is paediatric only, ages over 18 can give strange results.
1747
+ static_data['Age'] = min(static_data['Age'], 18)
@@ -17,7 +17,7 @@ from ll_visualiser.visualiser import visualise_model
17
17
 
18
18
  from c3d_parser.core.c3d_parser import (parse_session, extract_static_data, extract_marker_names, is_dynamic,
19
19
  CancelException, write_normalised_grfs, write_normalised_kinematics, write_normalised_kinetics,
20
- write_spatiotemporal_data, approximate_anthropometrics)
20
+ write_spatiotemporal_data, approximate_anthropometrics, height_to_mm)
21
21
  from c3d_parser.settings.general import (APPLICATION_NAME, VERSION, DEFAULT_STYLE_SHEET, INVALID_STYLE_SHEET,
22
22
  get_marker_maps_dir)
23
23
  from c3d_parser.view.ui.ui_main_window import Ui_MainWindow
@@ -124,7 +124,6 @@ class MainWindow(QMainWindow):
124
124
  self._kinetic_curves.signals.cycles_included.connect(self._update_cycle_count)
125
125
 
126
126
  def _setup_progress_bar(self):
127
- self._progress_text = ""
128
127
  self._progress_value = 0
129
128
  self._ui.progressBar.setVisible(False)
130
129
 
@@ -160,7 +159,6 @@ class MainWindow(QMainWindow):
160
159
  self._plotter.clear_actors()
161
160
 
162
161
  def _clear_progress_bar(self):
163
- self._progress_text = ""
164
162
  self._ui.labelProgress.setText("")
165
163
  self._progress_value = 0
166
164
  self._ui.progressBar.setValue(0)
@@ -460,33 +458,32 @@ class MainWindow(QMainWindow):
460
458
  self._set_subject_info(static_trial)
461
459
  self._static_trial = static_trial
462
460
 
463
- # Fill in any anthropometric gaps.
464
- if self._approximate_anthropometrics:
465
- input_directory = self._ui.lineEditInputDirectory.text()
466
- lab = self._ui.comboBoxLab.currentText()
467
- marker_diameter = self._ui.doubleSpinBoxMarkerDiameter.value()
468
- c3d_file = os.path.normpath(os.path.join(input_directory, static_trial))
469
- anthropometrics = approximate_anthropometrics(c3d_file, lab, marker_diameter)
470
-
471
- if not self._ui.doubleSpinBoxASISWidth.value():
472
- self._ui.doubleSpinBoxASISWidth.setValue(anthropometrics['inter_asis_distance'])
473
- if not self._ui.doubleSpinBoxLeftLegLength.value():
474
- self._ui.doubleSpinBoxLeftLegLength.setValue(anthropometrics['left_leg_length'])
475
- if not self._ui.doubleSpinBoxLeftKneeWidth.value():
476
- self._ui.doubleSpinBoxLeftKneeWidth.setValue(anthropometrics['left_knee_width'])
477
- if not self._ui.doubleSpinBoxLeftAnkleWidth.value():
478
- self._ui.doubleSpinBoxLeftAnkleWidth.setValue(anthropometrics['left_ankle_width'])
479
- if not self._ui.doubleSpinBoxRightLegLength.value():
480
- self._ui.doubleSpinBoxRightLegLength.setValue(anthropometrics['right_leg_length'])
481
- if not self._ui.doubleSpinBoxRightKneeWidth.value():
482
- self._ui.doubleSpinBoxRightKneeWidth.setValue(anthropometrics['right_knee_width'])
483
- if not self._ui.doubleSpinBoxRightAnkleWidth.value():
484
- self._ui.doubleSpinBoxRightAnkleWidth.setValue(anthropometrics['right_ankle_width'])
461
+ # Fill in any anthropometric gaps.
462
+ if self._approximate_anthropometrics:
463
+ input_directory = self._ui.lineEditInputDirectory.text()
464
+ lab = self._ui.comboBoxLab.currentText()
465
+ marker_diameter = self._ui.doubleSpinBoxMarkerDiameter.value()
466
+ c3d_file = os.path.normpath(os.path.join(input_directory, static_trial))
467
+ anthropometrics = approximate_anthropometrics(c3d_file, lab, marker_diameter)
468
+
469
+ if not self._ui.doubleSpinBoxASISWidth.value():
470
+ self._ui.doubleSpinBoxASISWidth.setValue(anthropometrics['inter_asis_distance'])
471
+ if not self._ui.doubleSpinBoxLeftLegLength.value():
472
+ self._ui.doubleSpinBoxLeftLegLength.setValue(anthropometrics['left_leg_length'])
473
+ if not self._ui.doubleSpinBoxLeftKneeWidth.value():
474
+ self._ui.doubleSpinBoxLeftKneeWidth.setValue(anthropometrics['left_knee_width'])
475
+ if not self._ui.doubleSpinBoxLeftAnkleWidth.value():
476
+ self._ui.doubleSpinBoxLeftAnkleWidth.setValue(anthropometrics['left_ankle_width'])
477
+ if not self._ui.doubleSpinBoxRightLegLength.value():
478
+ self._ui.doubleSpinBoxRightLegLength.setValue(anthropometrics['right_leg_length'])
479
+ if not self._ui.doubleSpinBoxRightKneeWidth.value():
480
+ self._ui.doubleSpinBoxRightKneeWidth.setValue(anthropometrics['right_knee_width'])
481
+ if not self._ui.doubleSpinBoxRightAnkleWidth.value():
482
+ self._ui.doubleSpinBoxRightAnkleWidth.setValue(anthropometrics['right_ankle_width'])
485
483
 
486
484
  # Check height units.
487
485
  height = self._ui.doubleSpinBoxHeight.value()
488
- if height < 250:
489
- self._ui.doubleSpinBoxHeight.setValue(height * 10)
486
+ self._ui.doubleSpinBoxHeight.setValue(height_to_mm(height))
490
487
 
491
488
 
492
489
  def _clear_subject_info(self):
@@ -586,10 +583,6 @@ class MainWindow(QMainWindow):
586
583
  'Right Leg Length': self._ui.doubleSpinBoxRightLegLength.value(),
587
584
  }
588
585
 
589
- # Cap age at 18.
590
- # Current shape model is paediatric only, ages over 18 can give strange results.
591
- static_data['Age'] = min(static_data['Age'], 18)
592
-
593
586
  missing = [key for key, value in static_data.items() if not value]
594
587
  if 'Left Leg Length' in missing or 'Right Leg Length' in missing:
595
588
  reply = QMessageBox.warning(self, "Warning", "Leg length measurements missing. You can continue "
@@ -604,10 +597,6 @@ class MainWindow(QMainWindow):
604
597
  QMessageBox.warning(self, "Warning", "Subject measurements missing:\n- " + "\n- ".join(missing))
605
598
  return
606
599
 
607
- if static_data['Height'] < 300:
608
- QMessageBox.warning(self, "Warning", "Please ensure subject height is in mm.")
609
- return
610
-
611
600
  optimise_knee_axis = self._optimise_knee_axis
612
601
  if not dynamic_trials:
613
602
  reply = QMessageBox.information(self, "No Dynamic Trials",
@@ -685,7 +674,6 @@ class MainWindow(QMainWindow):
685
674
 
686
675
  def _update_progress(self, message, color):
687
676
  self._ui.labelProgress.setText(message)
688
- self._progress_text = message
689
677
  self._ui.labelProgress.setStyleSheet(f"color: {color};")
690
678
 
691
679
  self._progress_value = min(self._progress_value + 20, 100)
@@ -1010,6 +998,7 @@ class MainWindow(QMainWindow):
1010
998
  dlg.exec()
1011
999
  self._reset_lab_combo_box()
1012
1000
 
1001
+ @handle_runtime_error
1013
1002
  def _handle_error(self, e):
1014
1003
  raise e
1015
1004
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: c3d-parser
3
- Version: 1.5.4
3
+ Version: 1.6.1
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
@@ -12,7 +12,7 @@ Requires-Dist: numpy==1.26.4
12
12
  Requires-Dist: pandas
13
13
  Requires-Dist: c3d==0.5.2
14
14
  Requires-Dist: trc-data-reader>=0.4.1
15
- Requires-Dist: opensim-model-creator==0.8.3
15
+ Requires-Dist: opensim-model-creator==0.10.1
16
16
  Requires-Dist: PySide6
17
17
  Requires-Dist: matplotlib
18
18
  Requires-Dist: mplcursors
@@ -3,7 +3,7 @@ numpy==1.26.4
3
3
  pandas
4
4
  c3d==0.5.2
5
5
  trc-data-reader>=0.4.1
6
- opensim-model-creator==0.8.3
6
+ opensim-model-creator==0.10.1
7
7
  PySide6
8
8
  matplotlib
9
9
  mplcursors
@@ -1,2 +0,0 @@
1
-
2
- __version__ = "1.5.4"
File without changes
File without changes
File without changes
File without changes