rod 0.3.1.dev7__tar.gz → 0.3.2.dev4__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 (52) hide show
  1. {rod-0.3.1.dev7 → rod-0.3.2.dev4}/PKG-INFO +1 -1
  2. {rod-0.3.1.dev7 → rod-0.3.2.dev4}/src/rod/builder/primitive_builder.py +6 -7
  3. {rod-0.3.1.dev7 → rod-0.3.2.dev4}/src/rod/kinematics/kinematic_tree.py +13 -13
  4. {rod-0.3.1.dev7 → rod-0.3.2.dev4}/src/rod/pretty_printer.py +3 -3
  5. {rod-0.3.1.dev7 → rod-0.3.2.dev4}/src/rod/sdf/collision.py +1 -2
  6. {rod-0.3.1.dev7 → rod-0.3.2.dev4}/src/rod/sdf/common.py +16 -14
  7. {rod-0.3.1.dev7 → rod-0.3.2.dev4}/src/rod/sdf/element.py +6 -6
  8. {rod-0.3.1.dev7 → rod-0.3.2.dev4}/src/rod/sdf/geometry.py +43 -16
  9. {rod-0.3.1.dev7 → rod-0.3.2.dev4}/src/rod/sdf/joint.py +8 -5
  10. {rod-0.3.1.dev7 → rod-0.3.2.dev4}/src/rod/sdf/link.py +13 -14
  11. {rod-0.3.1.dev7 → rod-0.3.2.dev4}/src/rod/sdf/material.py +6 -7
  12. {rod-0.3.1.dev7 → rod-0.3.2.dev4}/src/rod/sdf/model.py +15 -16
  13. {rod-0.3.1.dev7 → rod-0.3.2.dev4}/src/rod/sdf/physics.py +2 -2
  14. {rod-0.3.1.dev7 → rod-0.3.2.dev4}/src/rod/sdf/scene.py +4 -5
  15. {rod-0.3.1.dev7 → rod-0.3.2.dev4}/src/rod/sdf/sdf.py +10 -11
  16. {rod-0.3.1.dev7 → rod-0.3.2.dev4}/src/rod/sdf/visual.py +1 -1
  17. {rod-0.3.1.dev7 → rod-0.3.2.dev4}/src/rod/sdf/world.py +6 -7
  18. {rod-0.3.1.dev7 → rod-0.3.2.dev4}/src/rod/tree/directed_tree.py +5 -5
  19. {rod-0.3.1.dev7 → rod-0.3.2.dev4}/src/rod/tree/tree_elements.py +15 -17
  20. {rod-0.3.1.dev7 → rod-0.3.2.dev4}/src/rod/urdf/exporter.py +36 -55
  21. {rod-0.3.1.dev7 → rod-0.3.2.dev4}/src/rod/utils/frame_convention.py +0 -2
  22. {rod-0.3.1.dev7 → rod-0.3.2.dev4}/src/rod/utils/gazebo.py +1 -1
  23. {rod-0.3.1.dev7 → rod-0.3.2.dev4}/src/rod/utils/resolve_frames.py +1 -2
  24. {rod-0.3.1.dev7 → rod-0.3.2.dev4}/src/rod/utils/resolve_uris.py +1 -2
  25. {rod-0.3.1.dev7 → rod-0.3.2.dev4}/src/rod.egg-info/PKG-INFO +1 -1
  26. {rod-0.3.1.dev7 → rod-0.3.2.dev4}/.github/workflows/ci_cd.yml +0 -0
  27. {rod-0.3.1.dev7 → rod-0.3.2.dev4}/.github/workflows/style.yml +0 -0
  28. {rod-0.3.1.dev7 → rod-0.3.2.dev4}/.gitignore +0 -0
  29. {rod-0.3.1.dev7 → rod-0.3.2.dev4}/LICENSE +0 -0
  30. {rod-0.3.1.dev7 → rod-0.3.2.dev4}/README.md +0 -0
  31. {rod-0.3.1.dev7 → rod-0.3.2.dev4}/pyproject.toml +0 -0
  32. {rod-0.3.1.dev7 → rod-0.3.2.dev4}/setup.cfg +0 -0
  33. {rod-0.3.1.dev7 → rod-0.3.2.dev4}/setup.py +0 -0
  34. {rod-0.3.1.dev7 → rod-0.3.2.dev4}/src/rod/__init__.py +0 -0
  35. {rod-0.3.1.dev7 → rod-0.3.2.dev4}/src/rod/builder/__init__.py +0 -0
  36. {rod-0.3.1.dev7 → rod-0.3.2.dev4}/src/rod/builder/primitives.py +0 -0
  37. {rod-0.3.1.dev7 → rod-0.3.2.dev4}/src/rod/kinematics/__init__.py +0 -0
  38. {rod-0.3.1.dev7 → rod-0.3.2.dev4}/src/rod/kinematics/tree_transforms.py +0 -0
  39. {rod-0.3.1.dev7 → rod-0.3.2.dev4}/src/rod/logging.py +0 -0
  40. {rod-0.3.1.dev7 → rod-0.3.2.dev4}/src/rod/sdf/__init__.py +0 -0
  41. {rod-0.3.1.dev7 → rod-0.3.2.dev4}/src/rod/tree/__init__.py +0 -0
  42. {rod-0.3.1.dev7 → rod-0.3.2.dev4}/src/rod/urdf/__init__.py +0 -0
  43. {rod-0.3.1.dev7 → rod-0.3.2.dev4}/src/rod/utils/__init__.py +0 -0
  44. {rod-0.3.1.dev7 → rod-0.3.2.dev4}/src/rod.egg-info/SOURCES.txt +0 -0
  45. {rod-0.3.1.dev7 → rod-0.3.2.dev4}/src/rod.egg-info/dependency_links.txt +0 -0
  46. {rod-0.3.1.dev7 → rod-0.3.2.dev4}/src/rod.egg-info/not-zip-safe +0 -0
  47. {rod-0.3.1.dev7 → rod-0.3.2.dev4}/src/rod.egg-info/requires.txt +0 -0
  48. {rod-0.3.1.dev7 → rod-0.3.2.dev4}/src/rod.egg-info/top_level.txt +0 -0
  49. {rod-0.3.1.dev7 → rod-0.3.2.dev4}/tests/test_meshbuilder.py +0 -0
  50. {rod-0.3.1.dev7 → rod-0.3.2.dev4}/tests/test_urdf_exporter.py +0 -0
  51. {rod-0.3.1.dev7 → rod-0.3.2.dev4}/tests/test_urdf_parsing.py +0 -0
  52. {rod-0.3.1.dev7 → rod-0.3.2.dev4}/tests/utils_models.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: rod
3
- Version: 0.3.1.dev7
3
+ Version: 0.3.2.dev4
4
4
  Summary: The ultimate Python tool for RObot Descriptions processing.
5
5
  Home-page: https://github.com/ami-iit/rod
6
6
  Author: Diego Ferigo
@@ -2,7 +2,6 @@ from __future__ import annotations
2
2
 
3
3
  import abc
4
4
  import dataclasses
5
- from typing import Optional
6
5
 
7
6
  import numpy as np
8
7
  import numpy.typing as npt
@@ -272,7 +271,7 @@ class PrimitiveBuilder(abc.ABC):
272
271
 
273
272
  def _collision(
274
273
  self,
275
- name: Optional[str],
274
+ name: str | None,
276
275
  pose: rod.Pose | None = None,
277
276
  ) -> rod.Collision:
278
277
  name = name if name is not None else f"{self.name}_collision"
@@ -294,11 +293,11 @@ class PrimitiveBuilder(abc.ABC):
294
293
 
295
294
  @staticmethod
296
295
  def build_pose(
297
- pos: npt.NDArray = None,
298
- rpy: npt.NDArray = None,
299
- relative_to: str = None,
300
- degrees: bool = None,
301
- rotation_format: str = None,
296
+ pos: npt.NDArray | None = None,
297
+ rpy: npt.NDArray | None = None,
298
+ relative_to: str | None = None,
299
+ degrees: bool | None = None,
300
+ rotation_format: str | None = None,
302
301
  ) -> rod.Pose | None:
303
302
  if pos is None and rpy is None:
304
303
  return rod.Pose.from_transform(transform=np.eye(4), relative_to=relative_to)
@@ -3,7 +3,7 @@ from __future__ import annotations
3
3
  import copy
4
4
  import dataclasses
5
5
  import functools
6
- from typing import Dict, List, Sequence, Tuple
6
+ from typing import Sequence
7
7
 
8
8
  import numpy as np
9
9
 
@@ -16,8 +16,8 @@ from rod.tree import DirectedTree, DirectedTreeNode, TreeEdge, TreeFrame
16
16
  class KinematicTree(DirectedTree):
17
17
  model: rod.Model
18
18
 
19
- joints: List[TreeEdge] = dataclasses.field(default_factory=list)
20
- frames: List[TreeFrame] = dataclasses.field(default_factory=list)
19
+ joints: list[TreeEdge] = dataclasses.field(default_factory=list)
20
+ frames: list[TreeFrame] = dataclasses.field(default_factory=list)
21
21
 
22
22
  def __post_init__(self):
23
23
  # Initialize base class
@@ -38,13 +38,13 @@ class KinematicTree(DirectedTree):
38
38
  self.joints.sort(key=lambda j: j.index)
39
39
  self.frames.sort(key=lambda f: f.index)
40
40
 
41
- def link_names(self) -> List[str]:
41
+ def link_names(self) -> list[str]:
42
42
  return [node.name() for node in self]
43
43
 
44
- def frame_names(self) -> List[str]:
44
+ def frame_names(self) -> list[str]:
45
45
  return [frame.name() for frame in self.frames]
46
46
 
47
- def joint_names(self) -> List[str]:
47
+ def joint_names(self) -> list[str]:
48
48
  return [joint.name() for joint in self.joints]
49
49
 
50
50
  @staticmethod
@@ -73,7 +73,7 @@ class KinematicTree(DirectedTree):
73
73
 
74
74
  # In our tree, links are the nodes and joints the edges.
75
75
  # Create a dict mapping link names to tree nodes, for easy retrieval.
76
- nodes_links_dict: Dict[str, DirectedTreeNode] = {
76
+ nodes_links_dict: dict[str, DirectedTreeNode] = {
77
77
  # Add one node for each link of the model
78
78
  **{link.name: DirectedTreeNode(_source=link) for link in model.links()},
79
79
  # Add special world node, that will become a frame later
@@ -101,7 +101,7 @@ class KinematicTree(DirectedTree):
101
101
  # Furthermore, existing frames are extra elements that could be optionally
102
102
  # attached to the kinematic tree (but by default they're not part of it).
103
103
  # Create a dict mapping frame names to frame nodes, for easy retrieval.
104
- nodes_frames_dict: Dict[str, TreeFrame] = {
104
+ nodes_frames_dict: dict[str, TreeFrame] = {
105
105
  # Add a frame node for each frame in the model
106
106
  **{frame.name: TreeFrame(_source=frame) for frame in model.frames()},
107
107
  # Add implicit frames used in the SDF specification (__model__).
@@ -249,7 +249,7 @@ class KinematicTree(DirectedTree):
249
249
  @staticmethod
250
250
  def remove_edge(
251
251
  edge: TreeEdge, keep_parent: bool = True
252
- ) -> Tuple[DirectedTreeNode, Sequence[TreeFrame]]:
252
+ ) -> tuple[DirectedTreeNode, Sequence[TreeFrame]]:
253
253
  # Removed node: the node to remove.
254
254
  # Replaced node: the node removed and replaced with the new node.
255
255
  # New node: the new node that combines the removed and replaced nodes.
@@ -306,17 +306,17 @@ class KinematicTree(DirectedTree):
306
306
  raise NotImplementedError("Inertial parameters lumping")
307
307
 
308
308
  @functools.cached_property
309
- def links_dict(self) -> Dict[str, DirectedTreeNode]:
309
+ def links_dict(self) -> dict[str, DirectedTreeNode]:
310
310
  return self.nodes_dict
311
311
 
312
312
  @functools.cached_property
313
- def frames_dict(self) -> Dict[str, TreeFrame]:
313
+ def frames_dict(self) -> dict[str, TreeFrame]:
314
314
  return {frame.name(): frame for frame in self.frames}
315
315
 
316
316
  @functools.cached_property
317
- def joints_dict(self) -> Dict[str, TreeEdge]:
317
+ def joints_dict(self) -> dict[str, TreeEdge]:
318
318
  return {joint.name(): joint for joint in self.joints}
319
319
 
320
320
  @functools.cached_property
321
- def joints_connection_dict(self) -> Dict[Tuple[str, str], TreeEdge]:
321
+ def joints_connection_dict(self) -> dict[tuple[str, str], TreeEdge]:
322
322
  return {(j.parent.name(), j.child.name()): j for j in self.joints}
@@ -1,7 +1,7 @@
1
1
  import abc
2
2
  import dataclasses
3
3
  import numbers
4
- from typing import Any, List, Tuple
4
+ from typing import Any
5
5
 
6
6
 
7
7
  class DataclassPrettyPrinter(abc.ABC):
@@ -9,7 +9,7 @@ class DataclassPrettyPrinter(abc.ABC):
9
9
  return DataclassPrettyPrinter.dataclass_to_str(obj=self, level=1)
10
10
 
11
11
  @staticmethod
12
- def list_to_string(obj: List[Any], level: int = 1) -> str:
12
+ def list_to_string(obj: list[Any], level: int = 1) -> str:
13
13
  if not isinstance(obj, list):
14
14
  raise TypeError(obj, type(obj))
15
15
 
@@ -40,7 +40,7 @@ class DataclassPrettyPrinter(abc.ABC):
40
40
  if not dataclasses.is_dataclass(obj):
41
41
  raise TypeError(obj, type(obj))
42
42
 
43
- serialization: List[Tuple[str, str]] = []
43
+ serialization: list[tuple[str, str]] = []
44
44
 
45
45
  for field in dataclasses.fields(obj):
46
46
  attr = getattr(obj, field.name)
@@ -1,5 +1,4 @@
1
1
  import dataclasses
2
- from typing import Optional
3
2
 
4
3
  import mashumaro
5
4
 
@@ -13,4 +12,4 @@ class Collision(Element):
13
12
  geometry: Geometry
14
13
  name: str = dataclasses.field(metadata=mashumaro.field_options(alias="@name"))
15
14
 
16
- pose: Optional[Pose] = dataclasses.field(default=None)
15
+ pose: Pose | None = dataclasses.field(default=None)
@@ -1,5 +1,7 @@
1
+ from __future__ import annotations
2
+
1
3
  import dataclasses
2
- from typing import Any, Dict, List, Optional
4
+ from typing import Any
3
5
 
4
6
  import mashumaro
5
7
  import numpy.typing as npt
@@ -9,7 +11,7 @@ from .element import Element
9
11
 
10
12
  @dataclasses.dataclass
11
13
  class Xyz(Element):
12
- xyz: List[float] = dataclasses.field(
14
+ xyz: list[float] = dataclasses.field(
13
15
  default=None,
14
16
  metadata=mashumaro.field_options(
15
17
  alias="#text",
@@ -18,12 +20,12 @@ class Xyz(Element):
18
20
  ),
19
21
  )
20
22
 
21
- expressed_in: Optional[str] = dataclasses.field(
23
+ expressed_in: str | None = dataclasses.field(
22
24
  default=None, metadata=mashumaro.field_options(alias="@expressed_in")
23
25
  )
24
26
 
25
27
  @classmethod
26
- def __pre_deserialize__(cls, d: Dict[Any, Any]) -> Dict[Any, Any]:
28
+ def __pre_deserialize__(cls, d: dict[Any, Any]) -> dict[Any, Any]:
27
29
  if isinstance(d, str):
28
30
  d = {"#text": d, "@expressed_in": ""}
29
31
 
@@ -32,7 +34,7 @@ class Xyz(Element):
32
34
 
33
35
  @dataclasses.dataclass
34
36
  class Pose(Element):
35
- pose: List[float] = dataclasses.field(
37
+ pose: list[float] = dataclasses.field(
36
38
  default=None,
37
39
  metadata=mashumaro.field_options(
38
40
  alias="#text",
@@ -41,31 +43,31 @@ class Pose(Element):
41
43
  ),
42
44
  )
43
45
 
44
- relative_to: Optional[str] = dataclasses.field(
46
+ relative_to: str | None = dataclasses.field(
45
47
  default=None, metadata=mashumaro.field_options(alias="@relative_to")
46
48
  )
47
49
 
48
- degrees: Optional[bool] = dataclasses.field(
50
+ degrees: bool | None = dataclasses.field(
49
51
  default=None, metadata=mashumaro.field_options(alias="@degrees")
50
52
  )
51
53
 
52
- rotation_format: Optional[str] = dataclasses.field(
54
+ rotation_format: str | None = dataclasses.field(
53
55
  default=None, metadata=mashumaro.field_options(alias="@rotation_format")
54
56
  )
55
57
 
56
58
  @classmethod
57
- def __pre_deserialize__(cls, d: Dict[Any, Any]) -> Dict[Any, Any]:
59
+ def __pre_deserialize__(cls, d: dict[Any, Any]) -> dict[Any, Any]:
58
60
  if isinstance(d, str):
59
61
  d = {"#text": d, "@relative_to": ""}
60
62
 
61
63
  return d
62
64
 
63
65
  @property
64
- def xyz(self) -> List[float]:
66
+ def xyz(self) -> list[float]:
65
67
  return self.pose[0:3]
66
68
 
67
69
  @property
68
- def rpy(self) -> List[float]:
70
+ def rpy(self) -> list[float]:
69
71
  return self.pose[3:6]
70
72
 
71
73
  def transform(self) -> npt.NDArray:
@@ -89,7 +91,7 @@ class Pose(Element):
89
91
  )
90
92
 
91
93
  @staticmethod
92
- def from_transform(transform: npt.NDArray, relative_to: str = None) -> "Pose":
94
+ def from_transform(transform: npt.NDArray, relative_to: str | None = None) -> Pose:
93
95
  if transform.shape != (4, 4):
94
96
  raise ValueError(transform.shape)
95
97
 
@@ -105,8 +107,8 @@ class Pose(Element):
105
107
  class Frame(Element):
106
108
  name: str = dataclasses.field(metadata=mashumaro.field_options(alias="@name"))
107
109
 
108
- attached_to: Optional[str] = dataclasses.field(
110
+ attached_to: str | None = dataclasses.field(
109
111
  default=None, metadata=mashumaro.field_options(alias="@attached_to")
110
112
  )
111
113
 
112
- pose: Optional[Pose] = dataclasses.field(default=None)
114
+ pose: Pose | None = dataclasses.field(default=None)
@@ -1,5 +1,5 @@
1
1
  import dataclasses
2
- from typing import Any, Dict, List
2
+ from typing import Any
3
3
 
4
4
  import mashumaro.config
5
5
  import mashumaro.mixins.dict
@@ -13,7 +13,7 @@ class Element(mashumaro.mixins.dict.DataClassDictMixin, DataclassPrettyPrinter):
13
13
  class Config(mashumaro.config.BaseConfig):
14
14
  serialize_by_alias = True
15
15
 
16
- def __post_serialize__(self, d: Dict[Any, Any]) -> Dict[Any, Any]:
16
+ def __post_serialize__(self, d: dict[Any, Any]) -> dict[Any, Any]:
17
17
  out = d.copy()
18
18
 
19
19
  for key, value in d.items():
@@ -45,16 +45,16 @@ class Element(mashumaro.mixins.dict.DataClassDictMixin, DataclassPrettyPrinter):
45
45
  return str(data)
46
46
 
47
47
  @staticmethod
48
- def serialize_list(data: List[float]) -> str:
48
+ def serialize_list(data: list[float]) -> str:
49
49
  assert isinstance(data, list)
50
- return " ".join(np.array(data, dtype=str))
50
+ return " ".join(map(lambda element: str(float(element)), data))
51
51
 
52
52
  @staticmethod
53
- def deserialize_list(data: str, length: int | None = None) -> List[float]:
53
+ def deserialize_list(data: str, length: int | None = None) -> list[float]:
54
54
  assert isinstance(data, str)
55
55
  array = np.atleast_1d(np.array(data.split(sep=" "), dtype=float).squeeze())
56
56
 
57
57
  if length is not None:
58
58
  assert array.size == length
59
59
 
60
- return list(array)
60
+ return array.tolist()
@@ -1,5 +1,8 @@
1
+ from __future__ import annotations
2
+
1
3
  import dataclasses
2
- from typing import List, Optional
4
+ import types
5
+ from typing import ClassVar, Union
3
6
 
4
7
  import mashumaro
5
8
 
@@ -8,7 +11,7 @@ from .element import Element
8
11
 
9
12
  @dataclasses.dataclass
10
13
  class Box(Element):
11
- size: List[float] = dataclasses.field(
14
+ size: list[float] = dataclasses.field(
12
15
  default=None,
13
16
  metadata=mashumaro.field_options(
14
17
  serialize=Element.serialize_list,
@@ -41,7 +44,7 @@ class Cylinder(Element):
41
44
 
42
45
  @dataclasses.dataclass
43
46
  class Ellipsoid(Element):
44
- radii: List[float] = dataclasses.field(
47
+ radii: list[float] = dataclasses.field(
45
48
  default=None,
46
49
  metadata=mashumaro.field_options(
47
50
  serialize=Element.serialize_list,
@@ -54,7 +57,7 @@ class Ellipsoid(Element):
54
57
  class Heightmap(Element):
55
58
  uri: str
56
59
 
57
- size: List[float] = dataclasses.field(
60
+ size: list[float] = dataclasses.field(
58
61
  default=None,
59
62
  metadata=mashumaro.field_options(
60
63
  alias="#text",
@@ -63,7 +66,7 @@ class Heightmap(Element):
63
66
  ),
64
67
  )
65
68
 
66
- pos: List[float] = dataclasses.field(
69
+ pos: list[float] = dataclasses.field(
67
70
  default=None,
68
71
  metadata=mashumaro.field_options(
69
72
  alias="#text",
@@ -77,7 +80,7 @@ class Heightmap(Element):
77
80
  class Mesh(Element):
78
81
  uri: str
79
82
 
80
- scale: Optional[List[float]] = dataclasses.field(
83
+ scale: list[float] | None = dataclasses.field(
81
84
  default=None,
82
85
  metadata=mashumaro.field_options(
83
86
  serialize=Element.serialize_list,
@@ -88,14 +91,14 @@ class Mesh(Element):
88
91
 
89
92
  @dataclasses.dataclass
90
93
  class Plane(Element):
91
- normal: List[float] = dataclasses.field(
94
+ normal: list[float] = dataclasses.field(
92
95
  metadata=mashumaro.field_options(
93
96
  serialize=Element.serialize_list,
94
97
  deserialize=lambda l: Element.deserialize_list(data=l, length=3),
95
98
  ),
96
99
  )
97
100
 
98
- size: Optional[List[float]] = dataclasses.field(
101
+ size: list[float] | None = dataclasses.field(
99
102
  default=None,
100
103
  metadata=mashumaro.field_options(
101
104
  serialize=Element.serialize_list,
@@ -113,11 +116,35 @@ class Sphere(Element):
113
116
 
114
117
  @dataclasses.dataclass
115
118
  class Geometry(Element):
116
- box: Optional[Box] = dataclasses.field(default=None)
117
- capsule: Optional[Capsule] = dataclasses.field(default=None)
118
- cylinder: Optional[Capsule] = dataclasses.field(default=None)
119
- ellipsoid: Optional[Capsule] = dataclasses.field(default=None)
120
- heightmap: Optional[Heightmap] = dataclasses.field(default=None)
121
- mesh: Optional[Mesh] = dataclasses.field(default=None)
122
- plane: Optional[Mesh] = dataclasses.field(default=None)
123
- sphere: Optional[Sphere] = dataclasses.field(default=None)
119
+
120
+ GeometryType: ClassVar[types.UnionType] = (
121
+ Box | Capsule | Cylinder | Ellipsoid | Heightmap | Mesh | Plane | Sphere
122
+ )
123
+
124
+ box: Box | None = dataclasses.field(default=None)
125
+ capsule: Capsule | None = dataclasses.field(default=None)
126
+ cylinder: Cylinder | None = dataclasses.field(default=None)
127
+ ellipsoid: Ellipsoid | None = dataclasses.field(default=None)
128
+ heightmap: Heightmap | None = dataclasses.field(default=None)
129
+ mesh: Mesh | None = dataclasses.field(default=None)
130
+ plane: Plane | None = dataclasses.field(default=None)
131
+ sphere: Sphere | None = dataclasses.field(default=None)
132
+
133
+ def geometries(self) -> list[Geometry.GeometryType]:
134
+
135
+ return [
136
+ self.__getattribute__(field.name)
137
+ for field in dataclasses.fields(self)
138
+ if self.__getattribute__(field.name) is not None
139
+ ]
140
+
141
+ def geometry(self) -> Geometry.GeometryType | None:
142
+ """Return the actual geometry stored in the object"""
143
+
144
+ geometries = self.geometries()
145
+
146
+ if len(geometries) > 1:
147
+ msg = "More than one geometry found, returning the first one"
148
+ logging.warning(msg)
149
+
150
+ return geometries[0] if len(geometries) > 0 else None
@@ -9,12 +9,15 @@ from .element import Element
9
9
 
10
10
  @dataclasses.dataclass
11
11
  class Limit(Element):
12
- lower: float = dataclasses.field(
13
- metadata=mashumaro.field_options(serialize=Element.serialize_float)
12
+
13
+ lower: Optional[float] = dataclasses.field(
14
+ default=None,
15
+ metadata=mashumaro.field_options(serialize=Element.serialize_float),
14
16
  )
15
17
 
16
- upper: float = dataclasses.field(
17
- metadata=mashumaro.field_options(serialize=Element.serialize_float)
18
+ upper: Optional[float] = dataclasses.field(
19
+ default=None,
20
+ metadata=mashumaro.field_options(serialize=Element.serialize_float),
18
21
  )
19
22
 
20
23
  effort: Optional[float] = dataclasses.field(
@@ -74,5 +77,5 @@ class Joint(Element):
74
77
  parent: str
75
78
  child: str
76
79
 
77
- pose: Optional[Pose] = dataclasses.field(default=None)
80
+ pose: Pose | None = dataclasses.field(default=None)
78
81
  axis: Optional[Axis] = dataclasses.field(default=None)
@@ -1,5 +1,4 @@
1
1
  import dataclasses
2
- from typing import List, Optional
3
2
 
4
3
  import mashumaro
5
4
  import numpy as np
@@ -61,58 +60,58 @@ class Inertial(Element):
61
60
 
62
61
  inertia: Inertia
63
62
 
64
- name: Optional[str] = dataclasses.field(default=None)
65
- pose: Optional[Pose] = dataclasses.field(default=None)
63
+ name: str | None = dataclasses.field(default=None)
64
+ pose: Pose | None = dataclasses.field(default=None)
66
65
 
67
66
 
68
67
  @dataclasses.dataclass
69
68
  class Link(Element):
70
69
  name: str = dataclasses.field(metadata=mashumaro.field_options(alias="@name"))
71
70
 
72
- pose: Optional[Pose] = dataclasses.field(default=None)
71
+ pose: Pose | None = dataclasses.field(default=None)
73
72
 
74
- inertial: Optional[Inertial] = dataclasses.field(default=None)
73
+ inertial: Inertial | None = dataclasses.field(default=None)
75
74
 
76
- visual: Optional[Visual | List[Visual]] = dataclasses.field(default=None)
75
+ visual: Visual | list[Visual] | None = dataclasses.field(default=None)
77
76
 
78
- collision: Optional[Collision | List[Collision]] = dataclasses.field(default=None)
77
+ collision: Collision | list[Collision] | None = dataclasses.field(default=None)
79
78
 
80
- gravity: Optional[bool] = dataclasses.field(
79
+ gravity: bool | None = dataclasses.field(
81
80
  default=None,
82
81
  metadata=mashumaro.field_options(
83
82
  serialize=Element.serialize_bool, deserialize=Element.deserialize_bool
84
83
  ),
85
84
  )
86
85
 
87
- enable_wind: Optional[bool] = dataclasses.field(
86
+ enable_wind: bool | None = dataclasses.field(
88
87
  default=None,
89
88
  metadata=mashumaro.field_options(
90
89
  serialize=Element.serialize_bool, deserialize=Element.deserialize_bool
91
90
  ),
92
91
  )
93
92
 
94
- self_collide: Optional[bool] = dataclasses.field(
93
+ self_collide: bool | None = dataclasses.field(
95
94
  default=None,
96
95
  metadata=mashumaro.field_options(
97
96
  serialize=Element.serialize_bool, deserialize=Element.deserialize_bool
98
97
  ),
99
98
  )
100
99
 
101
- kinematic: Optional[bool] = dataclasses.field(
100
+ kinematic: bool | None = dataclasses.field(
102
101
  default=None,
103
102
  metadata=mashumaro.field_options(
104
103
  serialize=Element.serialize_bool, deserialize=Element.deserialize_bool
105
104
  ),
106
105
  )
107
106
 
108
- must_be_base_link: Optional[bool] = dataclasses.field(
107
+ must_be_base_link: bool | None = dataclasses.field(
109
108
  default=None,
110
109
  metadata=mashumaro.field_options(
111
110
  serialize=Element.serialize_bool, deserialize=Element.deserialize_bool
112
111
  ),
113
112
  )
114
113
 
115
- def visuals(self) -> List[Visual]:
114
+ def visuals(self) -> list[Visual]:
116
115
  if self.visual is None:
117
116
  return []
118
117
 
@@ -122,7 +121,7 @@ class Link(Element):
122
121
  assert isinstance(self.visual, list), type(self.visual)
123
122
  return self.visual
124
123
 
125
- def collisions(self) -> List[Collision]:
124
+ def collisions(self) -> list[Collision]:
126
125
  if self.collision is None:
127
126
  return []
128
127
 
@@ -1,5 +1,4 @@
1
1
  import dataclasses
2
- from typing import List, Optional
3
2
 
4
3
  import mashumaro
5
4
 
@@ -14,16 +13,16 @@ class Script(Element):
14
13
 
15
14
  @dataclasses.dataclass
16
15
  class Material(Element):
17
- script: Optional[Script] = dataclasses.field(default=None)
16
+ script: Script | None = dataclasses.field(default=None)
18
17
 
19
- lightning: Optional[bool] = dataclasses.field(
18
+ lightning: bool | None = dataclasses.field(
20
19
  default=None,
21
20
  metadata=mashumaro.field_options(
22
21
  serialize=Element.serialize_bool, deserialize=Element.deserialize_bool
23
22
  ),
24
23
  )
25
24
 
26
- ambient: Optional[List[float]] = dataclasses.field(
25
+ ambient: list[float] | None = dataclasses.field(
27
26
  default=None,
28
27
  metadata=mashumaro.field_options(
29
28
  serialize=Element.serialize_list,
@@ -31,7 +30,7 @@ class Material(Element):
31
30
  ),
32
31
  )
33
32
 
34
- diffuse: Optional[List[float]] = dataclasses.field(
33
+ diffuse: list[float] | None = dataclasses.field(
35
34
  default=None,
36
35
  metadata=mashumaro.field_options(
37
36
  serialize=Element.serialize_list,
@@ -39,7 +38,7 @@ class Material(Element):
39
38
  ),
40
39
  )
41
40
 
42
- specular: Optional[List[float]] = dataclasses.field(
41
+ specular: list[float] | None = dataclasses.field(
43
42
  default=None,
44
43
  metadata=mashumaro.field_options(
45
44
  serialize=Element.serialize_list,
@@ -47,7 +46,7 @@ class Material(Element):
47
46
  ),
48
47
  )
49
48
 
50
- emissive: Optional[List[float]] = dataclasses.field(
49
+ emissive: list[float] | None = dataclasses.field(
51
50
  default=None,
52
51
  metadata=mashumaro.field_options(
53
52
  serialize=Element.serialize_list,