From 49f82d7700ac5eb78d296e702dc1bc7f09351253 Mon Sep 17 00:00:00 2001 From: Clemens Eppner Date: Fri, 10 Jan 2025 23:35:39 -0800 Subject: [PATCH] Version 1.12.2 --- src/scene_synthesizer/assets.py | 28 ++++++--- src/scene_synthesizer/scene.py | 107 +++++++++++++++++++------------- 2 files changed, 81 insertions(+), 54 deletions(-) diff --git a/src/scene_synthesizer/assets.py b/src/scene_synthesizer/assets.py index c1b5c9c..5956ea9 100644 --- a/src/scene_synthesizer/assets.py +++ b/src/scene_synthesizer/assets.py @@ -1818,51 +1818,59 @@ def __init__(self, mesh, **kwargs): class BoxAsset(TrimeshAsset): - def __init__(self, extents, **kwargs): + def __init__(self, extents, transform=None, **kwargs): """A box primitive. Args: extents (list[float]): 3D extents of the box. + transform (np.ndarray, optional): 4x4 homogeneous transformation matrix for box center. Defaults to None. """ - super().__init__(mesh=trimesh.primitives.Box(extents=extents), **kwargs) + super().__init__(mesh=trimesh.primitives.Box(extents=extents, transform=transform), **kwargs) class BoxMeshAsset(TrimeshAsset): - def __init__(self, extents, **kwargs): + def __init__(self, extents, transform=None, **kwargs): """A triangular mesh in the shape of a box. Args: extents (list[float]): 3D extents of the box. + transform (np.ndarray, optional): 4x4 homogeneous transformation matrix for box center. Defaults to None. """ - super().__init__(mesh=trimesh.creation.box(extents=extents), **kwargs) + super().__init__(mesh=trimesh.creation.box(extents=extents, transform=transform), **kwargs) class CylinderAsset(TrimeshAsset): - def __init__(self, radius, height, **kwargs): + def __init__(self, radius, height, transform=None, sections=32, **kwargs): """A cylinder primitive. Args: radius (float): Radius of cylinder. height (float): Height of cylinder. + transform (np.ndarray, optional): 4x4 homogeneous transformation matrix for cylinder center. Defaults to None. + sections (int, optional): Number of facets in circle. Defaults to 32. """ - super().__init__(mesh=trimesh.primitives.Cylinder(radius=radius, height=height), **kwargs) + super().__init__(mesh=trimesh.primitives.Cylinder(radius=radius, height=height, transform=transform, sections=sections), **kwargs) class SphereAsset(TrimeshAsset): - def __init__(self, radius, **kwargs): + def __init__(self, radius, transform=None, subdivisions=3, **kwargs): """A sphere primitive. Args: radius (float): Radius of sphere. + transform (np.ndarray, optional): 4x4 homogeneous transformation matrix for sphere center. Defaults to None. + subdivisions (int, optional): Number of subdivisions for icosphere. Defaults to 3. """ - super().__init__(mesh=trimesh.primitives.Sphere(radius=radius), **kwargs) + super().__init__(mesh=trimesh.primitives.Sphere(radius=radius, transform=transform, subdivisions=subdivisions), **kwargs) class CapsuleAsset(TrimeshAsset): - def __init__(self, radius, height, **kwargs): + def __init__(self, radius, height, transform=None, sections=32, **kwargs): """A capsule primitive. Args: radius (float): Radius of cylindrical part (and spherical end parts). height (float): Height of cylindrical part. Total height of capsule will be height + 2*radius. + transform (np.ndarray, optional): 4x4 homogeneous transformation matrix for capsule center. Defaults to None. + sections (int, optional): Number of facets in circle. Defaults to 32. """ - super().__init__(mesh=trimesh.primitives.Capsule(radius=radius, height=height), **kwargs) + super().__init__(mesh=trimesh.primitives.Capsule(radius=radius, height=height, transform=transform, sections=sections), **kwargs) class TrimeshSceneAsset(Asset): diff --git a/src/scene_synthesizer/scene.py b/src/scene_synthesizer/scene.py index a98b92d..5f232f1 100644 --- a/src/scene_synthesizer/scene.py +++ b/src/scene_synthesizer/scene.py @@ -1005,64 +1005,86 @@ def add_walls( dimensions, thickness=0.15, overhang=0.0, + offset=0.0, hole_extents=(0, 0), hole_offset=(0, 0), use_primitives=True, object_ids=None, + use_collision_geometry=True, + joint_type=None, ): """Add walls/box assets to any combination of the six faces of the scene volume. Args: dimensions (list[str]): In which dimension to add a wall. Any subset of ['x', '-x', 'y', '-y', 'z', '-z']. thickness (float, optional): Thickness of the wall. Defaults to 0.15. - overhang (float, optional): The amount of overhang of the wall. Can be a single scalar, a 3-dim list or a 6-dim list. Defaults to 0.0. + overhang (float, optional): The amount of overhang of the wall. Can be a single scalar or a len(dimensions)-dim list. Defaults to 0.0. + offset (float, optional): The distance between wall and closest scene geometry. Can be a single scalar or a len(dimensions)-dim list. Defaults to 0.0. hole_extents (list[float], optional): 2-dim extents of the hole in the wall. Defaults to (0, 0). hole_offset (tuple[float], optional): 2-dim position offset of the hole in the wall. Defaults to (0, 0). use_primitives (bool, optional): Whether to create a mesh or use a primitive. Defaults to True. object_ids (list[str], optional): A list of object names used for adding walls, needs to have same length as dimensions. If None, names will be "wall_{dim}". Defaults to None + use_collision_geometry (bool, optional): Defaults to True. + joint_type (str, optional): Defaults to None. Raises: - ValueError: If overhang is not a float, or a list of length 3 or 6. + ValueError: If overhang is not a float, or a list of length len(dimensions). ValueError: If object_ids is not None and len(object_ids) != len(dimensions). """ overhangs = None + offsets = None + dim_to_index = {'x': 0, '-x': 1, 'y': 2, '-y': 3, 'z': 4, '-z': 5} + if isinstance(overhang, float): overhangs = [overhang] * 6 - elif isinstance(overhang, list): - if len(overhang) == 3: - overhangs = [ - overhang[0], - overhang[0], - overhang[1], - overhang[1], - overhang[2], - overhang[2], - ] - elif len(overhang) == 6: - overhangs = overhang + elif isinstance(overhang, list) and len(overhang) == len(dimensions): + overhangs = [0.0] * 6 + for i, d in enumerate(dimensions): + overhangs[dim_to_index[d]] = overhang[i] + if overhangs is None: - raise ValueError("Overhang needs to be a float or a list with 3 or 6 floats.") + raise ValueError("Overhang needs to be a float or a list with len(dimensions) floats.") + + if isinstance(offset, float): + offsets = [offset] * 6 + elif isinstance(offset, list) and len(offset) == len(dimensions): + offsets = [0.0] * 6 + for i, d in enumerate(dimensions): + offsets[dim_to_index[d]] = offset[i] + + if offsets is None: + raise ValueError("Offset needs to be a float or a list with len(dimensions) floats.") + offsets = np.array(offsets) if object_ids is not None and len(object_ids) != len(dimensions): raise ValueError("If object_ids is set it needs to have as many elements as dimensions.") + scene_bounds = self.get_bounds() + scene_extents = self.get_extents() + + scene_bounds[0] -= offsets[1::2] + scene_bounds[1] += offsets[0::2] + + scene_extents += (offsets[1::2] + offsets[0::2]) + extents = { "x": [ thickness, - self._scene.extents[1] + overhangs[2] + overhangs[3], - self._scene.extents[2] + overhangs[4] + overhangs[5], + scene_extents[1] + overhangs[2] + overhangs[3], + scene_extents[2] + overhangs[4] + overhangs[5], ], "y": [ - self._scene.extents[0] + overhangs[0] + overhangs[1], + scene_extents[0] + overhangs[0] + overhangs[1], thickness, - self._scene.extents[2] + overhangs[4] + overhangs[5], + scene_extents[2] + overhangs[4] + overhangs[5], ], "z": [ - self._scene.extents[0] + overhangs[0] + overhangs[1], - self._scene.extents[1] + overhangs[2] + overhangs[3], + scene_extents[0] + overhangs[0] + overhangs[1], + scene_extents[1] + overhangs[2] + overhangs[3], thickness, ], } + if all(hole_extents): # check if hole is not zero hole_extents_per_dim = { "x": [None, hole_extents[0], hole_extents[1]], @@ -1075,23 +1097,6 @@ def add_walls( if all(hole_extents): hole_extents_per_dim[f"-{k}"] = hole_extents_per_dim[k] - parent_achors = { - "x": ("top", "center", "center"), - "-x": ("bottom", "center", "center"), - "y": ("center", "top", "center"), - "-y": ("center", "bottom", "center"), - "z": ("center", "center", "top"), - "-z": ("center", "center", "bottom"), - } - obj_anchors = { - "x": ("bottom", "center", "center"), - "-x": ("top", "center", "center"), - "y": ("center", "bottom", "center"), - "-y": ("center", "top", "center"), - "z": ("center", "center", "bottom"), - "-z": ("center", "center", "top"), - } - if object_ids is None: object_ids = [f"wall_{dim}" for dim in dimensions] @@ -1104,16 +1109,30 @@ def add_walls( use_primitives=use_primitives, ) else: - wall = BoxAsset(extents=extents[dim]) if use_primitives else BoxMeshAsset(extents=extents[dim]) + if use_primitives: + wall = BoxAsset(extents=extents[dim]) + else: + wall = BoxMeshAsset(extents=extents[dim]) + + if dim == 'x': + transform = tra.translation_matrix(scene_bounds[1] * np.array([1.0, 0.0, 0.0]) + [thickness/2.0, 0, 0] + (scene_bounds[1] + scene_bounds[0]) * np.array([0, 0.5, 0.5])) + elif dim == '-x': + transform = tra.translation_matrix(scene_bounds[0] * np.array([1.0, 0.0, 0.0]) - [thickness/2.0, 0, 0] + (scene_bounds[1] + scene_bounds[0]) * np.array([0, 0.5, 0.5])) + elif dim == 'y': + transform = tra.translation_matrix(scene_bounds[1] * np.array([0.0, 1.0, 0.0]) + [0, thickness/2.0, 0] + (scene_bounds[1] + scene_bounds[0]) * np.array([0.5, 0, 0.5])) + elif dim == '-y': + transform = tra.translation_matrix(scene_bounds[0] * np.array([0.0, 1.0, 0.0]) - [0, thickness/2.0, 0] + (scene_bounds[1] + scene_bounds[0]) * np.array([0.5, 0, 0.5])) + elif dim == 'z': + transform = tra.translation_matrix(scene_bounds[1] * np.array([0.0, 0.0, 1.0]) + [0, 0, thickness/2.0] + (scene_bounds[1] + scene_bounds[0]) * np.array([0.5, 0.5, 0])) + elif dim == '-z': + transform = tra.translation_matrix(scene_bounds[0] * np.array([0.0, 0.0, 1.0]) - [0, 0, thickness/2.0] + (scene_bounds[1] + scene_bounds[0]) * np.array([0.5, 0.5, 0])) self.add_object( wall, obj_id, - connect_parent_id=self._scene.graph.base_frame, - connect_parent_anchor=parent_achors[dim], - connect_obj_anchor=obj_anchors[dim], - use_collision_geometry=True, - joint_type=None, + transform=transform, + use_collision_geometry=use_collision_geometry, + joint_type=joint_type, ) def distance_matrix_geometry(self, nodes_geometry=None, return_names=False):