https://github.com/brownvc/deep-synth
Tip revision: b800e11290b763b58e7d3b30329769a7b77cd12a authored by kwang-ether on 14 June 2019, 23:53:57 UTC
remove csv
remove csv
Tip revision: b800e11
OBB.py
import random
import numpy as np
from pyquaternion import Quaternion
from math_utils import Transform
class OBB(object):
def __init__(self, center, half_widths, rotation_matrix):
self._c = np.squeeze(center)
self._h = np.squeeze(half_widths)
self._R = rotation_matrix
self._recompute_transforms()
def _recompute_transforms(self):
# local-to-world transform: takes [0,1]^3 points in OBB to world space points
self._local_to_world = np.identity(4)
for i in range(3):
self._local_to_world[:3, i] = self._R[:, i] * self._h[i]
self._local_to_world[:3, 3] = self._c
# world-to-local transform: world space points within OBB to [0,1]^3
self._world_to_local = np.identity(4)
for i in range(3):
self._world_to_local[i, :3] = self._R[:, i] * (1.0 / self._h[i])
t_inv = - np.matmul(self._world_to_local[:3, :3], np.transpose(self._c))
self._world_to_local[:3, 3] = np.squeeze(t_inv)
@classmethod
def from_local2world_transform(cls, transform):
xform = Transform.from_mat4(transform)
return cls(center=xform.translation, half_widths=xform.scale, rotation_matrix=xform.rotation.rotation_matrix)
@classmethod
def from_node(cls, node, aligned_dims):
xform = Transform.from_mat4x4_flat_row_major(node.transform)
return cls(center=xform.translation, half_widths=aligned_dims * xform.scale * 0.5,
rotation_matrix=xform.rotation.rotation_matrix)
@property
def half_extents(self):
return self._h
@property
def rotation_matrix(self):
return self._R
@property
def rotation_quat(self):
return Quaternion(matrix=self._R)
@property
def dimensions(self):
return 2.0 * self._h
@property
def half_dimensions(self):
return self._h
@property
def centroid(self):
return self._c
@property
def world2local(self):
return self._world_to_local
@property
def local2world(self):
return self._local_to_world
def __repr__(self):
return 'OBB: {c:' + str(self._c) + ',h:' + str(self._h) + ',R:' + str(self._R.tolist()) + '}'
def transform_point(self, p):
return np.matmul(self._world_to_local, np.append(p, [1], axis=0))[:3]
def transform_direction(self, d):
return np.matmul(np.transpose(self._R), d)
def distance_to_point(self, p):
if self.contains_point(p):
return 0.0
closest = self.closest_point(p)
return np.linalg.norm(p - closest)
def contains_point(self, p):
p_local = np.matmul(self._world_to_local, np.append(p, [1], axis=0))[:3]
bound = 1.0
for i in range(3):
if abs(p_local[i]) > bound:
return False
return True # here only if all three coord within bounds
def closest_point(self, p):
d = p - self._c
closest = np.copy(self._c)
for i in range(3):
closest += np.clip(self._R[:, i] * d, -self._h[i], self._h[i]) * self._R[:, i]
return closest
def sample(self):
p = np.copy(self._c)
for i in range(3):
r = random.random() * 2.0 - 1.0
p += r * self._h[i] * self._R[:, i]
return p
def project_to_axis(self, direction):
"""
Projects this OBB onto the given 1D direction vector and returns projection interval [min, max].
If vector is unnormalized, the output is scaled by the length of the vector
:param direction: the axis on which to project this OBB
:return: out_min - the minimum extent of this OBB along direction, out_max - the maximum extent
"""
x = abs(np.dot(direction, self._R[0]) * self._h[0])
y = abs(np.dot(direction, self._R[1]) * self._h[1])
z = abs(np.dot(direction, self._R[2]) * self._h[2])
p = np.dot(direction, self._c)
out_min = p - x - y - z
out_max = p + x + y + z
return out_min, out_max
def signed_distance_to_plane(self, plane):
p_min, p_max = self.project_to_axis(plane.normal)
p_min -= plane.d
p_max -= plane.d
if p_min * p_max <= 0.0:
return 0.0
return p_min if abs(p_min) < abs(p_max) else p_max
def to_aabb(self):
h_size = abs(self._R[0] * self._h[0]) + abs(self._R[1] * self._h[1]) + abs(self._R[2] * self._h[2])
p_min = self._c - h_size
p_max = self._c + h_size
return p_min, p_max