|
5 | 5 | import itertools
|
6 | 6 | import random
|
7 | 7 |
|
| 8 | +from compas import IPY |
8 | 9 | from compas.colors import Color
|
9 | 10 | from compas.data import Data
|
10 | 11 | from compas.datastructures import Mesh
|
|
34 | 35 | from .link import Link
|
35 | 36 | from .link import Visual
|
36 | 37 |
|
| 38 | +if not IPY: |
| 39 | + from typing import TYPE_CHECKING |
| 40 | + |
| 41 | + if TYPE_CHECKING: |
| 42 | + from typing import List # noqa: F401 |
| 43 | + from typing import Optional # noqa: F401 |
| 44 | + |
37 | 45 |
|
38 | 46 | class RobotModel(Data):
|
39 | 47 | """RobotModel is the root element of the model.
|
@@ -822,6 +830,10 @@ def scale(self, factor, link=None):
|
822 | 830 |
|
823 | 831 | self._scale_factor = factor
|
824 | 832 |
|
| 833 | + # -------------------------------------------------------------------------- |
| 834 | + # Methods for computing frames and axes from the Robot Model |
| 835 | + # -------------------------------------------------------------------------- |
| 836 | + |
825 | 837 | def compute_transformations(self, joint_state, link=None, parent_transformation=None):
|
826 | 838 | """Recursive function to calculate the transformations of each joint.
|
827 | 839 |
|
@@ -874,7 +886,9 @@ def compute_transformations(self, joint_state, link=None, parent_transformation=
|
874 | 886 | return transformations
|
875 | 887 |
|
876 | 888 | def transformed_frames(self, joint_state):
|
877 |
| - """Returns the transformed frames based on the joint_state. |
| 889 | + """Returns the transformed Joint frames (relative to the Robot Coordinate Frame) based on the joint_state (:class:`~compas_robots.Configuration`). |
| 890 | +
|
| 891 | + The order of the frames is the same as the order returned by :meth:`iter_joints`. |
878 | 892 |
|
879 | 893 | Parameters
|
880 | 894 | ----------
|
@@ -990,6 +1004,180 @@ def _check_link_name(self, name):
|
990 | 1004 | if name in all_link_names:
|
991 | 1005 | raise ValueError("Link name '%s' already used in chain." % name)
|
992 | 1006 |
|
| 1007 | + # -------------------------------------------------------------------------- |
| 1008 | + # Methods for accessing the visual and collision geometry |
| 1009 | + # -------------------------------------------------------------------------- |
| 1010 | + |
| 1011 | + def _extract_link_meshes(self, link_elements): |
| 1012 | + # type: (List[Visual] | List[Collision]) -> List[Mesh] |
| 1013 | + """Extracts the list of compas meshes from a link's Visual or Collision elements. |
| 1014 | +
|
| 1015 | + Note that each link may have multiple visual or collision nodes, each can have |
| 1016 | + a different origin frame. Therefore, the returned meshes are transformed |
| 1017 | + according to the ``.origin`` frame of each visual or collision element. |
| 1018 | +
|
| 1019 | + This transformation is time consuming for large meshes and should be done only once. |
| 1020 | + This transformation is automatically skipped if the origin is None or the identity frame. |
| 1021 | +
|
| 1022 | + Parameters |
| 1023 | + ---------- |
| 1024 | + link_elements : list of :class:`~compas_robots.model.Visual` or :class:`~compas_robots.model.Collision` |
| 1025 | + The list of Visual or Collision elements of a link. |
| 1026 | +
|
| 1027 | + Returns |
| 1028 | + ------- |
| 1029 | + list of :class:`~compas.datastructures.Mesh` |
| 1030 | + A list of meshes belonging to the link elements. |
| 1031 | + If there are no meshes, an empty list is returned. |
| 1032 | +
|
| 1033 | + """ |
| 1034 | + meshes = [] |
| 1035 | + # Note: Each Link can have multiple visual nodes |
| 1036 | + for element in link_elements: |
| 1037 | + # Some elements may have a non-identity origin frame |
| 1038 | + t_origin = None |
| 1039 | + if element.origin: |
| 1040 | + origin = element.origin if isinstance(element.origin, Frame) else element.origin._proxied_object |
| 1041 | + if Frame.worldXY() != origin: |
| 1042 | + t_origin = Transformation.from_frame(element.origin) |
| 1043 | + |
| 1044 | + # Note: the MeshDescriptor.meshes object supports a list of compas meshes. |
| 1045 | + # There can be multiple mesh in a single MeshDescriptor |
| 1046 | + for mesh in LinkGeometry._get_item_meshes(element): |
| 1047 | + # Transform the mesh |
| 1048 | + if t_origin: |
| 1049 | + meshes.append(mesh.transformed(t_origin)) |
| 1050 | + else: |
| 1051 | + meshes.append(mesh) |
| 1052 | + |
| 1053 | + return meshes |
| 1054 | + |
| 1055 | + def get_link_visual_meshes(self, link): |
| 1056 | + # type: (Link) -> List[Mesh] |
| 1057 | + """Get a list of visual meshes from a Link. |
| 1058 | +
|
| 1059 | + The origin of the visual meshes are transformed according to the `element.origin` |
| 1060 | + of the Visual nodes. This means that the returned mesh will match with the link's origin frame. |
| 1061 | +
|
| 1062 | + Parameters |
| 1063 | + ---------- |
| 1064 | + link : :class:`~compas_robots.model.Link` |
| 1065 | + The link to extract the visual meshes from. |
| 1066 | +
|
| 1067 | + Returns |
| 1068 | + ------- |
| 1069 | + list of :class:`~compas.datastructures.Mesh` |
| 1070 | + A list of visual meshes belonging to the link |
| 1071 | + The list is empty if no visual meshes are found. |
| 1072 | +
|
| 1073 | + Notes |
| 1074 | + ----- |
| 1075 | + Only MeshDescriptor in `element.geometry.shape` is supported. Other shapes are ignored. |
| 1076 | + """ |
| 1077 | + visual_meshes = self._extract_link_meshes(link.visual, True) |
| 1078 | + return visual_meshes |
| 1079 | + |
| 1080 | + def get_link_visual_meshes_joined(self, link, weld=False, weld_precision=None): |
| 1081 | + # type: (Link, Optional[bool], Optional[int]) -> Mesh | None |
| 1082 | + """Get the visual meshes of a Link joined into a single mesh. |
| 1083 | +
|
| 1084 | + The origin of the visual meshes are transformed according to the `element.origin` |
| 1085 | + of the Visual nodes. This means that the returned mesh will match with the link's origin frame. |
| 1086 | +
|
| 1087 | + Parameters |
| 1088 | + ---------- |
| 1089 | + link : :class:`~compas_robots.model.Link` |
| 1090 | + The link to extract the visual meshes from. |
| 1091 | + weld : bool, optional |
| 1092 | + If True, weld close vertices after joining. Defaults to False. |
| 1093 | + weld_precision : int, optional |
| 1094 | + The precision used for welding the mesh. |
| 1095 | + Default is :attr:`TOL.precision`. |
| 1096 | +
|
| 1097 | + Returns |
| 1098 | + ------- |
| 1099 | + :class:`~compas.datastructures.Mesh` | None |
| 1100 | + A single mesh representing the visual meshes of the link. |
| 1101 | + None if no visual meshes are found. |
| 1102 | +
|
| 1103 | + Notes |
| 1104 | + ----- |
| 1105 | + Only MeshDescriptor in `element.geometry.shape` is supported. Other shapes are ignored. |
| 1106 | + """ |
| 1107 | + visual_meshes = self._extract_link_meshes(link.visual, True) |
| 1108 | + if not visual_meshes: |
| 1109 | + return None |
| 1110 | + |
| 1111 | + joined_mesh = Mesh() |
| 1112 | + for mesh in visual_meshes: |
| 1113 | + joined_mesh.join(mesh, weld, weld_precision) |
| 1114 | + return joined_mesh |
| 1115 | + |
| 1116 | + def get_link_collision_meshes(self, link): |
| 1117 | + # type: (Link) -> List[Mesh] |
| 1118 | + """Get the list of collision meshes of a link. |
| 1119 | +
|
| 1120 | + The origin of the visual meshes are transformed according to the `element.origin` |
| 1121 | + of the Visual nodes. This means that the returned mesh will match with the link's origin frame. |
| 1122 | +
|
| 1123 | + Parameters |
| 1124 | + ---------- |
| 1125 | + link : :class:`~compas_robots.model.Link` |
| 1126 | + The link to extract the collision meshes from. |
| 1127 | +
|
| 1128 | + Returns |
| 1129 | + ------- |
| 1130 | + list of :class:`~compas.datastructures.Mesh` |
| 1131 | + A list of collision meshes belonging to the link |
| 1132 | + The list is empty if no collision meshes are found. |
| 1133 | +
|
| 1134 | + Notes |
| 1135 | + ----- |
| 1136 | + Only MeshDescriptor in `element.geometry.shape` is supported. Other shapes are ignored. |
| 1137 | + """ |
| 1138 | + collision_meshes = self._extract_link_meshes(link.collision, True) |
| 1139 | + return collision_meshes |
| 1140 | + |
| 1141 | + def get_link_collision_meshes_joined(self, link, weld=False, weld_precision=None): |
| 1142 | + # type: (Link, Optional[bool], Optional[int]) -> Mesh | None |
| 1143 | + """Get the collision meshes of a Link joined into a single mesh. |
| 1144 | +
|
| 1145 | + The origin of the visual meshes are transformed according to the `element.origin` |
| 1146 | + of the Visual nodes. This means that the returned mesh will match with the link's origin frame. |
| 1147 | +
|
| 1148 | + Parameters |
| 1149 | + ---------- |
| 1150 | + link : :class:`~compas_robots.model.Link` |
| 1151 | + The link to extract the collision meshes from. |
| 1152 | + weld : bool, optional |
| 1153 | + If True, weld close vertices after joining. Defaults to False. |
| 1154 | + weld_precision : int, optional |
| 1155 | + The precision used for welding the mesh. |
| 1156 | + Default is :attr:`TOL.precision`. |
| 1157 | +
|
| 1158 | + Returns |
| 1159 | + ------- |
| 1160 | + :class:`~compas.datastructures.Mesh` | None |
| 1161 | + A single mesh representing the collision meshes of the link. |
| 1162 | + None if no collision meshes are found. |
| 1163 | +
|
| 1164 | + Notes |
| 1165 | + ----- |
| 1166 | + Only MeshDescriptor in `element.geometry.shape` is supported. Other shapes are ignored. |
| 1167 | + """ |
| 1168 | + collision_meshes = self._extract_link_meshes(link.collision, True) |
| 1169 | + if not collision_meshes: |
| 1170 | + return None |
| 1171 | + |
| 1172 | + joined_mesh = Mesh() |
| 1173 | + for mesh in collision_meshes: |
| 1174 | + joined_mesh.join(mesh, weld, weld_precision) |
| 1175 | + return joined_mesh |
| 1176 | + |
| 1177 | + # -------------------------------------------------------------------------- |
| 1178 | + # Methods for modifying the Robot Model structure |
| 1179 | + # -------------------------------------------------------------------------- |
| 1180 | + |
993 | 1181 | def add_link(self, name, visual_meshes=None, visual_color=None, collision_meshes=None, **kwargs):
|
994 | 1182 | """Adds a link to the robot model.
|
995 | 1183 |
|
|
0 commit comments