-
Notifications
You must be signed in to change notification settings - Fork 42
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Mesh and object #206
base: dev
Are you sure you want to change the base?
Mesh and object #206
Changes from 7 commits
75c16a0
b2564c9
4fa042a
f4f8fbe
fbd2134
1093253
0db8e9d
41ad375
b6bb696
cf019f2
dfbdc8c
d74f7d6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,10 +4,11 @@ | |
from copy import deepcopy, copy | ||
from dataclasses import dataclass | ||
|
||
from typing_extensions import List, Optional, Tuple, Callable, Dict, Any, Union, TYPE_CHECKING | ||
import numpy as np | ||
from typing_extensions import List, Optional, Tuple, Callable, Dict, Any, Union, TYPE_CHECKING, Sequence | ||
|
||
from .enums import JointType, Shape, VirtualMobileBaseJointName | ||
from .pose import Pose, Point | ||
from .pose import Pose, Point, Transform | ||
from ..validation.error_checkers import calculate_joint_position_error, is_error_acceptable | ||
|
||
if TYPE_CHECKING: | ||
|
@@ -88,7 +89,7 @@ def get_rgb(self) -> List[float]: | |
|
||
|
||
@dataclass | ||
class AxisAlignedBoundingBox: | ||
class BoundingBox: | ||
""" | ||
Dataclass for storing an axis-aligned bounding box. | ||
""" | ||
|
@@ -99,15 +100,24 @@ class AxisAlignedBoundingBox: | |
max_y: float | ||
max_z: float | ||
|
||
@classmethod | ||
def from_min_max(cls, min_point: List[float], max_point: List[float]): | ||
def get_points_list(self) -> List[List[float]]: | ||
""" | ||
Set the axis-aligned bounding box from a minimum and maximum point. | ||
:return: The points of the bounding box as a list of lists of floats. | ||
""" | ||
return [[point.x, point.y, point.z] for point in self.get_points()] | ||
|
||
:param min_point: The minimum point | ||
:param max_point: The maximum point | ||
def get_points(self) -> List[Point]: | ||
""" | ||
return cls(min_point[0], min_point[1], min_point[2], max_point[0], max_point[1], max_point[2]) | ||
:return: The points of the bounding box as a list of Point instances. | ||
""" | ||
return [Point(self.min_x, self.min_y, self.min_z), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this intended like this? and why are there so many points? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes these are the 8 corners of the bounding box. |
||
Point(self.min_x, self.min_y, self.max_z), | ||
Point(self.min_x, self.max_y, self.min_z), | ||
Point(self.min_x, self.max_y, self.max_z), | ||
Point(self.max_x, self.min_y, self.min_z), | ||
Point(self.max_x, self.min_y, self.max_z), | ||
Point(self.max_x, self.max_y, self.min_z), | ||
Point(self.max_x, self.max_y, self.max_z)] | ||
|
||
def get_min_max_points(self) -> Tuple[Point, Point]: | ||
""" | ||
|
@@ -146,6 +156,96 @@ def get_max(self) -> List[float]: | |
return [self.max_x, self.max_y, self.max_z] | ||
|
||
|
||
@dataclass | ||
class AxisAlignedBoundingBox(BoundingBox): | ||
|
||
def get_transformed_box(self, transform: Transform) -> 'AxisAlignedBoundingBox': | ||
""" | ||
Apply a transformation to the axis-aligned bounding box and return the transformed axis-aligned bounding box. | ||
|
||
:param transform: The transformation to apply | ||
:return: The transformed axis-aligned bounding box | ||
""" | ||
transformed_points = transform.apply_transform_to_array_of_points(np.array(self.get_min_max())) | ||
min_p = [min(transformed_points[:, i]) for i in range(3)] | ||
max_p = [max(transformed_points[:, i]) for i in range(3)] | ||
return AxisAlignedBoundingBox.from_min_max(min_p, max_p) | ||
|
||
@classmethod | ||
def from_min_max(cls, min_point: Sequence[float], max_point: Sequence[float]): | ||
""" | ||
Set the axis-aligned bounding box from a minimum and maximum point. | ||
|
||
:param min_point: The minimum point | ||
:param max_point: The maximum point | ||
""" | ||
return cls(min_point[0], min_point[1], min_point[2], max_point[0], max_point[1], max_point[2]) | ||
|
||
|
||
@dataclass | ||
class RotatedBoundingBox(BoundingBox): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why dont use a polytope here There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can add the convex hull version which is available directly from trimesh, but this does not remove the need for a rotated shape, because the rotation here is meant to rotate the shape to the current pose of the link, this rotation will also be needed in a polytope or a convex hull as we only get the points relative to the object origin and are aligned with world frame not with object current frame. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have added a method to get convex hull |
||
""" | ||
Dataclass for storing a rotated bounding box. | ||
""" | ||
|
||
def __init__(self, min_x: float, min_y: float, min_z: float, max_x: float, max_y: float, max_z: float, | ||
transform: Transform, points: Optional[List[Point]] = None): | ||
self.min_x, self.min_y, self.min_z = min_x, min_y, min_z | ||
self.max_x, self.max_y, self.max_z = max_x, max_y, max_z | ||
self.transform: Transform = transform | ||
self._points: Optional[List[Point]] = points | ||
|
||
@classmethod | ||
def from_min_max(cls, min_point: Sequence[float], max_point: Sequence[float], transform: Transform): | ||
""" | ||
Set the rotated bounding box from a minimum, maximum point, and a transformation. | ||
|
||
:param min_point: The minimum point | ||
:param max_point: The maximum point | ||
:param transform: The transformation | ||
""" | ||
return cls(min_point[0], min_point[1], min_point[2], max_point[0], max_point[1], max_point[2], transform) | ||
|
||
@classmethod | ||
def from_axis_aligned_bounding_box(cls, axis_aligned_bounding_box: AxisAlignedBoundingBox, | ||
transform: Transform) -> 'RotatedBoundingBox': | ||
""" | ||
Set the rotated bounding box from an axis-aligned bounding box and a transformation. | ||
|
||
:param axis_aligned_bounding_box: The axis-aligned bounding box. | ||
:param transform: The transformation. | ||
""" | ||
return cls(axis_aligned_bounding_box.min_x, axis_aligned_bounding_box.min_y, axis_aligned_bounding_box.min_z, | ||
axis_aligned_bounding_box.max_x, axis_aligned_bounding_box.max_y, axis_aligned_bounding_box.max_z, | ||
transform) | ||
|
||
def get_points_list(self) -> List[List[float]]: | ||
""" | ||
:return: The points of the rotated bounding box as a list of lists of floats. | ||
""" | ||
return [[point.x, point.y, point.z] for point in self.get_points()] | ||
|
||
def get_points(self, transform: Optional[Transform] = None) -> List[Point]: | ||
""" | ||
:param transform: The transformation to apply to the points, if None the stored transformation is used. | ||
:return: The points of the rotated bounding box. | ||
""" | ||
if (self._points is None) or (transform is not None): | ||
if transform is not None: | ||
self.transform = transform | ||
points_array = np.array([[self.min_x, self.min_y, self.min_z], | ||
[self.min_x, self.min_y, self.max_z], | ||
[self.min_x, self.max_y, self.min_z], | ||
[self.min_x, self.max_y, self.max_z], | ||
[self.max_x, self.min_y, self.min_z], | ||
[self.max_x, self.min_y, self.max_z], | ||
[self.max_x, self.max_y, self.min_z], | ||
[self.max_x, self.max_y, self.max_z]]) | ||
transformed_points = self.transform.apply_transform_to_array_of_points(points_array).tolist() | ||
self._points = [Point(*point) for point in transformed_points] | ||
return self._points | ||
|
||
|
||
@dataclass | ||
class CollisionCallbacks: | ||
""" | ||
|
@@ -196,6 +296,13 @@ def visual_geometry_type(self) -> Shape: | |
""" | ||
pass | ||
|
||
@property | ||
def axis_aligned_bounding_box(self) -> AxisAlignedBoundingBox: | ||
""" | ||
:return: The axis-aligned bounding box of the visual shape. | ||
""" | ||
raise NotImplementedError | ||
|
||
|
||
@dataclass | ||
class BoxVisualShape(VisualShape): | ||
|
@@ -215,6 +322,14 @@ def visual_geometry_type(self) -> Shape: | |
def size(self) -> List[float]: | ||
return self.half_extents | ||
|
||
@property | ||
def axis_aligned_bounding_box(self) -> AxisAlignedBoundingBox: | ||
""" | ||
:return: The axis-aligned bounding box of the box visual shape. | ||
""" | ||
return AxisAlignedBoundingBox(-self.half_extents[0], -self.half_extents[1], -self.half_extents[2], | ||
self.half_extents[0], self.half_extents[1], self.half_extents[2]) | ||
|
||
|
||
@dataclass | ||
class SphereVisualShape(VisualShape): | ||
|
@@ -230,6 +345,13 @@ def shape_data(self) -> Dict[str, float]: | |
def visual_geometry_type(self) -> Shape: | ||
return Shape.SPHERE | ||
|
||
@property | ||
def axis_aligned_bounding_box(self) -> AxisAlignedBoundingBox: | ||
""" | ||
:return: The axis-aligned bounding box of the sphere visual shape. | ||
""" | ||
return AxisAlignedBoundingBox(-self.radius, -self.radius, -self.radius, self.radius, self.radius, self.radius) | ||
|
||
|
||
@dataclass | ||
class CapsuleVisualShape(VisualShape): | ||
|
@@ -246,6 +368,14 @@ def shape_data(self) -> Dict[str, float]: | |
def visual_geometry_type(self) -> Shape: | ||
return Shape.CAPSULE | ||
|
||
@property | ||
def axis_aligned_bounding_box(self) -> AxisAlignedBoundingBox: | ||
""" | ||
:return: The axis-aligned bounding box of the capsule visual shape. | ||
""" | ||
return AxisAlignedBoundingBox(-self.radius, -self.radius, -self.length / 2, | ||
self.radius, self.radius, self.length / 2) | ||
|
||
|
||
@dataclass | ||
class CylinderVisualShape(CapsuleVisualShape): | ||
|
@@ -490,6 +620,7 @@ class ContactPointsList(list): | |
""" | ||
A list of contact points. | ||
""" | ||
|
||
def get_links_that_got_removed(self, previous_points: 'ContactPointsList') -> List[Link]: | ||
""" | ||
Return the links that are not in the current points list but were in the initial points list. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don't need to put Transform in quotation marks if you add the import:
from future import annotations
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh that's nicee thanks