longsim-base / backups /dev /metrics /geometry_utils.py
gzzyyxy's picture
Upload folder using huggingface_hub
c1a7f73 verified
import torch
import numpy as np
from torch import Tensor
from typing import Tuple
NUM_VERTICES_IN_BOX = 4
def minkowski_sum_of_box_and_box_points(box1_points: Tensor,
box2_points: Tensor) -> Tensor:
"""Batched Minkowski sum of two boxes (counter-clockwise corners in xy)."""
point_order_1 = torch.tensor([0, 0, 1, 1, 2, 2, 3, 3], dtype=torch.long)
point_order_2 = torch.tensor([0, 1, 1, 2, 2, 3, 3, 0], dtype=torch.long)
box1_start_idx, downmost_box1_edge_direction = _get_downmost_edge_in_box(
box1_points)
box2_start_idx, downmost_box2_edge_direction = _get_downmost_edge_in_box(
box2_points)
condition = (cross_product_2d(downmost_box1_edge_direction, downmost_box2_edge_direction) >= 0.)
condition = condition.repeat(1, 8)
box1_point_order = torch.where(condition, point_order_2, point_order_1)
box1_point_order = (box1_point_order + box1_start_idx) % NUM_VERTICES_IN_BOX
ordered_box1_points = torch.gather(
box1_points, 1, box1_point_order.unsqueeze(-1).expand(-1, -1, 2))
box2_point_order = torch.where(condition, point_order_1, point_order_2)
box2_point_order = (box2_point_order + box2_start_idx) % NUM_VERTICES_IN_BOX
ordered_box2_points = torch.gather(
box2_points, 1, box2_point_order.unsqueeze(-1).expand(-1, -1, 2))
minkowski_sum = ordered_box1_points + ordered_box2_points
return minkowski_sum
def signed_distance_from_point_to_convex_polygon(query_points: Tensor, polygon_points: Tensor) -> Tensor:
"""Finds the signed distances from query points to convex polygons."""
tangent_unit_vectors, normal_unit_vectors, edge_lengths = _get_edge_info(
polygon_points)
query_points = query_points.unsqueeze(1)
vertices_to_query_vectors = query_points - polygon_points
vertices_distances = torch.norm(vertices_to_query_vectors, dim=-1)
edge_signed_perp_distances = torch.sum(-normal_unit_vectors * vertices_to_query_vectors, dim=-1)
is_inside = torch.all(edge_signed_perp_distances <= 0, dim=-1)
projection_along_tangent = torch.sum(tangent_unit_vectors * vertices_to_query_vectors, dim=-1)
projection_along_tangent_proportion = projection_along_tangent / edge_lengths
is_projection_on_edge = (projection_along_tangent_proportion >= 0.) & (
projection_along_tangent_proportion <= 1.)
edge_perp_distances = edge_signed_perp_distances.abs()
edge_distances = torch.where(is_projection_on_edge, edge_perp_distances, torch.tensor(np.inf))
edge_and_vertex_distance = torch.cat([edge_distances, vertices_distances], dim=-1)
min_distance = torch.min(edge_and_vertex_distance, dim=-1)[0]
signed_distances = torch.where(is_inside, -min_distance, min_distance)
return signed_distances
def _get_downmost_edge_in_box(box: Tensor) -> Tuple[Tensor, Tensor]:
"""Finds the downmost (lowest y-coordinate) edge in the box."""
downmost_vertex_idx = torch.argmin(box[..., 1], dim=-1, keepdim=True)
edge_start_vertex = torch.gather(box, 1, downmost_vertex_idx.unsqueeze(-1).expand(-1, -1, 2))
edge_end_idx = (downmost_vertex_idx + 1) % NUM_VERTICES_IN_BOX
edge_end_vertex = torch.gather(box, 1, edge_end_idx.unsqueeze(-1).expand(-1, -1, 2))
downmost_edge = edge_end_vertex - edge_start_vertex
downmost_edge_length = torch.norm(downmost_edge, dim=-1, keepdim=True)
downmost_edge_direction = downmost_edge / downmost_edge_length
return downmost_vertex_idx, downmost_edge_direction
def cross_product_2d(a: Tensor, b: Tensor) -> Tensor:
return a[..., 0] * b[..., 1] - a[..., 1] * b[..., 0]
def dot_product_2d(a: Tensor, b: Tensor) -> Tensor:
return a[..., 0] * b[..., 0] + a[..., 1] * b[..., 1]
def _get_edge_info(polygon_points: Tensor):
"""
Computes properties about the edges of a polygon.
Args:
polygon_points: Tensor containing the vertices of each polygon, with
shape (num_polygons, num_points_per_polygon, 2). Each polygon is assumed
to have an equal number of vertices.
Returns:
tangent_unit_vectors: A unit vector in (x,y) with the same direction as
the tangent to the edge. Shape: (num_polygons, num_points_per_polygon, 2).
normal_unit_vectors: A unit vector in (x,y) with the same direction as
the normal to the edge.
Shape: (num_polygons, num_points_per_polygon, 2).
edge_lengths: Lengths of the edges.
Shape (num_polygons, num_points_per_polygon).
"""
# Shift the polygon points by 1 position to get the edges.
first_point_in_polygon = polygon_points[:, 0:1, :] # Shape: (num_polygons, 1, 2)
shifted_polygon_points = torch.cat([polygon_points[:, 1:, :], first_point_in_polygon], dim=1)
# Shape: (num_polygons, num_points_per_polygon, 2)
edge_vectors = shifted_polygon_points - polygon_points # Shape: (num_polygons, num_points_per_polygon, 2)
edge_lengths = torch.norm(edge_vectors, dim=-1) # Shape: (num_polygons, num_points_per_polygon)
# Avoid division by zero by adding a small epsilon
eps = torch.finfo(edge_lengths.dtype).eps
tangent_unit_vectors = edge_vectors / (edge_lengths[..., None] + eps) # Shape: (num_polygons, num_points_per_polygon, 2)
normal_unit_vectors = torch.stack(
[-tangent_unit_vectors[..., 1], tangent_unit_vectors[..., 0]], dim=-1
) # Shape: (num_polygons, num_points_per_polygon, 2)
return tangent_unit_vectors, normal_unit_vectors, edge_lengths
def rotate_2d_points(xys: Tensor, rotation_yaws: Tensor) -> Tensor:
"""Rotates `xys` counterclockwise using the `rotation_yaws`."""
cos_yaws = torch.cos(rotation_yaws)
sin_yaws = torch.sin(rotation_yaws)
rotated_x = cos_yaws * xys[..., 0] - sin_yaws * xys[..., 1]
rotated_y = sin_yaws * xys[..., 0] + cos_yaws * xys[..., 1]
return torch.stack([rotated_x, rotated_y], axis=-1)