Skip to content

Commit 1ea87f6

Browse files
authored
Merge pull request #71 from Ipuch/opensim_rm
Improves OpenSim dependency handling
2 parents c758a3e + 7389643 commit 1ea87f6

File tree

8 files changed

+138
-69
lines changed

8 files changed

+138
-69
lines changed

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ And also animate biorbd models from the pyomeca organization.
66

77
``` conda install -c conda-forge pyorerun rerun-sdk=0.21```
88

9-
``` conda install opensim-org::opensim # mandatory dependency```
9+
``` conda install opensim-org::opensim # not a mandatory dependency```
1010

1111
# Rerun .c3d - As simple as that
1212

@@ -59,7 +59,9 @@ animation.rerun()
5959

6060
## From source
6161
```conda install -c conda-forge ezc3d rerun-sdk=0.21 trimesh numpy biorbd pyomeca tk imageio imageio-ffmpeg```
62-
```conda install opensim-org::opensim```
62+
63+
if you want to use the OpenSim, you also need to install separately:
64+
```conda install -c opensim-org::opensim```
6365

6466
Then, ensure it is accessible in your Python environment by installing the package:
6567

pyorerun/__init__.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,19 @@
22
from .live_integration import LiveModelIntegration
33
from .model_components.model_display_options import DisplayModelOptions
44
from .model_components.model_updapter import ModelUpdater
5+
6+
try:
7+
from .model_interfaces import (
8+
OsimModel,
9+
OsimModelNoMesh,
10+
)
11+
except ImportError:
12+
# OpenSim n'est pas installé, ces classes ne seront pas disponibles
13+
pass
14+
515
from .model_interfaces import (
616
BiorbdModel,
717
BiorbdModelNoMesh,
8-
OsimModel,
9-
OsimModelNoMesh,
1018
AbstractSegment,
1119
AbstractModel,
1220
AbstractModelNoMesh,

pyorerun/live_animation.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44
import numpy as np
55
import rerun as rr
66

7-
from .model_interfaces.biorbd_model_interface import BiorbdModel
8-
from .model_interfaces.osim_model_interface import OsimModel
97
from .model_components.model_updapter import ModelUpdater
108
from .model_components.model_display_options import DisplayModelOptions
119

pyorerun/model_components/model_updapter.py

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@
1212
from ..abstract.linestrip import LineStripProperties
1313
from ..abstract.markers import MarkerProperties
1414
from ..model_components.ligaments import LigamentsUpdater, MusclesUpdater, LineStripUpdaterFromGlobalTransform
15-
from ..model_interfaces import BiorbdModel, BiorbdModelNoMesh, OsimModel, OsimModelNoMesh, AbstractModel
15+
from ..model_interfaces import AbstractModel, model_from_file
1616

1717

1818
class ModelUpdater(Components):
1919
def __init__(
2020
self,
21-
name: str,
22-
model: BiorbdModelNoMesh | BiorbdModel | OsimModelNoMesh | OsimModel | AbstractModel,
21+
name,
22+
model: AbstractModel,
2323
muscle_colors: np.ndarray = None,
2424
):
2525
self.name = name
@@ -62,14 +62,7 @@ def from_file(cls, model_path: str, options: DisplayModelOptions = None):
6262
>>> rr.log("anything", rr.Anything())
6363
6464
"""
65-
if model_path.endswith(".osim"):
66-
model = OsimModel(model_path, options=options)
67-
no_mesh_instance = OsimModelNoMesh
68-
elif model_path.endswith(".bioMod"):
69-
model = BiorbdModel(model_path, options=options)
70-
no_mesh_instance = BiorbdModelNoMesh
71-
else:
72-
raise ValueError("The model must be in biorbd or opensim format.")
65+
model, no_mesh_instance = model_from_file(model_path, options=options)
7366

7467
if model.has_mesh or model.has_meshlines:
7568
return cls(model.name, model)
Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
11
from .abstract_model_interface import AbstractSegment, AbstractModel, AbstractModelNoMesh
22
from .biorbd_model_interface import BiorbdModelNoMesh, BiorbdModel
3-
from .osim_model_interface import OsimModelNoMesh, OsimModel
3+
4+
try:
5+
from .osim_model_interface import OsimModelNoMesh, OsimModel
6+
except ImportError:
7+
# OpenSim n'est pas installé, ces classes ne seront pas disponibles
8+
pass
9+
10+
from .available_interfaces import model_from_file
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
from .abstract_model_interface import AbstractModel, AbstractModelNoMesh
2+
from .biorbd_model_interface import BiorbdModelNoMesh, BiorbdModel
3+
from ..model_components.model_display_options import DisplayModelOptions
4+
5+
AVAILABLE_INTERFACES = {
6+
"biorbd": (BiorbdModel, BiorbdModelNoMesh),
7+
}
8+
9+
try:
10+
from .osim_model_interface import OsimModelNoMesh, OsimModel
11+
12+
AVAILABLE_INTERFACES["opensim"] = (OsimModel, OsimModelNoMesh)
13+
except ImportError:
14+
# OpenSim n'est pas installé, ces classes ne seront pas disponibles
15+
pass
16+
17+
18+
def model_from_file(model_path: str, options: DisplayModelOptions = None) -> tuple[AbstractModel, AbstractModelNoMesh]:
19+
"""
20+
Create a model interface based on the provided model path.
21+
22+
Parameters
23+
----------
24+
model_path : str
25+
Path to the model file (either .osim or .bioMod).
26+
options : dict, optional
27+
Options for the model interface.
28+
29+
Returns
30+
-------
31+
tuple[AbstractModel, AbstractModelNoMesh]
32+
A tuple containing the model interface and a no-mesh instance of the model.
33+
"""
34+
35+
if model_path.endswith(".osim"):
36+
if "opensim" not in AVAILABLE_INTERFACES:
37+
raise ImportError(
38+
f"OpenSim is not installed. Please install it to use OpenSim models."
39+
f"Use: conda install opensim-org::opensim"
40+
)
41+
model = AVAILABLE_INTERFACES["opensim"][0](model_path, options=options)
42+
no_instance_mesh = AVAILABLE_INTERFACES["opensim"][1]
43+
elif model_path.endswith(".bioMod"):
44+
model = AVAILABLE_INTERFACES["biorbd"][0](model_path, options=options)
45+
no_instance_mesh = AVAILABLE_INTERFACES["biorbd"][1]
46+
else:
47+
raise ValueError("The model must be in biorbd or opensim format.")
48+
49+
return model, no_instance_mesh

tests/test_osim_series.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
from pathlib import Path
2+
3+
import numpy as np
4+
import pytest
5+
6+
from pyorerun import OsimTimeSeries
7+
8+
try:
9+
import opensim
10+
11+
HAS_OPENSIM = True
12+
except ImportError:
13+
HAS_OPENSIM = False
14+
15+
16+
@pytest.mark.skipif(not HAS_OPENSIM, reason="OpenSim is not installed.")
17+
def test_osim_time_series():
18+
19+
MOT_FILE = "../examples/osim/ik.mot"
20+
OSIM_TIME_SERIES = OsimTimeSeries(str(Path(__file__).parent) + "/" + MOT_FILE)
21+
22+
assert isinstance(OSIM_TIME_SERIES, OsimTimeSeries)
23+
assert OSIM_TIME_SERIES.coordinate_names == (
24+
"pelvis_tilt",
25+
"pelvis_list",
26+
"pelvis_rotation",
27+
"pelvis_tx",
28+
"pelvis_ty",
29+
"pelvis_tz",
30+
"hip_flexion_r",
31+
"hip_adduction_r",
32+
"hip_rotation_r",
33+
"knee_angle_r",
34+
"ankle_angle_r",
35+
"subtalar_angle_r",
36+
"mtp_angle_r",
37+
"hip_flexion_l",
38+
"hip_adduction_l",
39+
"hip_rotation_l",
40+
"knee_angle_l",
41+
"ankle_angle_l",
42+
"subtalar_angle_l",
43+
"mtp_angle_l",
44+
"lumbar_extension",
45+
"lumbar_bending",
46+
"lumbar_rotation",
47+
"arm_flex_r",
48+
"arm_add_r",
49+
"arm_rot_r",
50+
"elbow_flex_r",
51+
"pro_sup_r",
52+
"wrist_flex_r",
53+
"wrist_dev_r",
54+
"arm_flex_l",
55+
"arm_add_l",
56+
"arm_rot_l",
57+
"elbow_flex_l",
58+
"pro_sup_l",
59+
"wrist_flex_l",
60+
"wrist_dev_l",
61+
)
62+
assert OSIM_TIME_SERIES.q[0, 50] == np.float64(3.16086172)
63+
assert OSIM_TIME_SERIES.is_degree is True

tests/test_xp_phase.py

Lines changed: 0 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,7 @@
1-
from pathlib import Path
2-
3-
import numpy as np
4-
5-
from pyorerun import OsimTimeSeries
61
from pyorerun.abstract.abstract_class import ExperimentalData
72
from pyorerun.xp_phase import XpRerunPhase
83

94
MOT_FILE = "../examples/osim/ik.mot"
10-
OSIM_TIME_SERIES = OsimTimeSeries(str(Path(__file__).parent) + "/" + MOT_FILE)
115

126

137
class MockExperimentalData(ExperimentalData):
@@ -91,48 +85,3 @@ def test_component_names():
9185
phase.add_data(data2)
9286

9387
assert phase.component_names == ["data1", "data2"]
94-
95-
96-
def test_osim_time_series():
97-
assert isinstance(OSIM_TIME_SERIES, OsimTimeSeries)
98-
assert OSIM_TIME_SERIES.coordinate_names == (
99-
"pelvis_tilt",
100-
"pelvis_list",
101-
"pelvis_rotation",
102-
"pelvis_tx",
103-
"pelvis_ty",
104-
"pelvis_tz",
105-
"hip_flexion_r",
106-
"hip_adduction_r",
107-
"hip_rotation_r",
108-
"knee_angle_r",
109-
"ankle_angle_r",
110-
"subtalar_angle_r",
111-
"mtp_angle_r",
112-
"hip_flexion_l",
113-
"hip_adduction_l",
114-
"hip_rotation_l",
115-
"knee_angle_l",
116-
"ankle_angle_l",
117-
"subtalar_angle_l",
118-
"mtp_angle_l",
119-
"lumbar_extension",
120-
"lumbar_bending",
121-
"lumbar_rotation",
122-
"arm_flex_r",
123-
"arm_add_r",
124-
"arm_rot_r",
125-
"elbow_flex_r",
126-
"pro_sup_r",
127-
"wrist_flex_r",
128-
"wrist_dev_r",
129-
"arm_flex_l",
130-
"arm_add_l",
131-
"arm_rot_l",
132-
"elbow_flex_l",
133-
"pro_sup_l",
134-
"wrist_flex_l",
135-
"wrist_dev_l",
136-
)
137-
assert OSIM_TIME_SERIES.q[0, 50] == np.float64(3.16086172)
138-
assert OSIM_TIME_SERIES.is_degree is True

0 commit comments

Comments
 (0)