Source code for optable.solver

import numpy as np
from typing import List, Tuple, Union


[docs] def solve_ray_bboxes_intersections( ray_origin, ray_direction, bboxes: Union[List[Tuple], Tuple] ) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: """for each set of bbox plane, solve the intersection with the ray [t1,t2] then the intersection point should be intersection of [t1x,t2x], [t1y,t2y], [t1z,t2z] return [t1, t2], if no intersection return [0, inf] """ EPS = 1e-12 if isinstance(bboxes, tuple): bboxes = [bboxes] bboxes = np.array(bboxes) # (n_boxes, 6) # # t1, t2 = 0, np.inf t1, t2 = np.full(bboxes.shape[0], 0, dtype=float), np.full( bboxes.shape[0], np.inf, dtype=float ) for axis in range(3): o = ray_origin[axis] d = ray_direction[axis] bmin = bboxes[:, 2 * axis] bmax = bboxes[:, 2 * axis + 1] # if np.isclose(d, 0.0): # parallel outside = (o < bmin) | (o > bmax) # Mark no-hit by forcing t1 > t2 for those t1[outside] = 1 t2[outside] = 0 continue inv_d = 1.0 / d t_axis0 = (bmin - o) * inv_d t_axis1 = (bmax - o) * inv_d t_axis_near = np.minimum(t_axis0, t_axis1) t_axis_far = np.maximum(t_axis0, t_axis1) # Update global t1, t2 using this axis t1 = np.maximum(t1, t_axis_near) t2 = np.minimum(t2, t_axis_far) # Valid hit: intervals overlap and intersection not entirely behind the origin hit = (t2 + EPS >= t1) & (t2 >= 0.0) return t1, t2, hit
[docs] def solve_ray_ray_intersection( ray1_origin, ray1_direction, ray2_origin, ray2_direction ): """ Computes ray-ray intersection/closest point with hard clamping for t > 0. Returns: t1, t2 (float): Parameters for the closest points. P (np.array): intersection_point. n (np.array): The surface normal that reflects beam1 to beam2. """ # 1. Convert to numpy arrays and Normalize Directions # Normalizing is crucial for the reflection normal calculation to be correct. p1 = np.array(ray1_origin, dtype=np.float64) d1 = np.array(ray1_direction, dtype=np.float64) d1 = d1 / np.linalg.norm(d1) p2 = np.array(ray2_origin, dtype=np.float64) d2 = np.array(ray2_direction, dtype=np.float64) d2 = d2 / np.linalg.norm(d2) # 2. Least Squares Solution for Infinite Lines # We solve the linear system for the segment perpendicular to both lines r = p1 - p2 # Dot products a = np.dot(d1, d1) # 1.0 since normalized b = np.dot(d1, d2) c = np.dot(d2, d2) # 1.0 since normalized d = np.dot(d1, r) e = np.dot(d2, r) denom = a * c - b * b # Check for parallel rays if denom < 1e-6: t1 = 0.0 t2 = e / c # Projection onto d2 else: t1 = (b * e - c * d) / denom t2 = (a * e - b * d) / denom # 3. Hard Clamp (as requested) t1 = max(0.0, t1) t2 = max(0.0, t2) # 4. Calculate Closest Points point1 = p1 + t1 * d1 point2 = p2 + t2 * d2 # The intersection is the midpoint of the closest approach P = (point1 + point2) * 0.5 # 5. Calculate Reflection Normal norm = -(d1 + d2) * 0.5 # both rays point towards (-norm) n = norm / np.linalg.norm(norm) return t1, t2, P, n
[docs] def solve_normal_to_normal_rotation(n1, n2) -> Tuple[np.ndarray, float]: """Solve the rotation axis and angle to rotate n1 to n2 Args: n1 (np.ndarray): normal vector 1 n2 (np.ndarray): normal vector 2 Returns: Tuple[np.ndarray,float]: rotation axis, rotation angle in radian """ n1 = n1 / np.linalg.norm(n1) n2 = n2 / np.linalg.norm(n2) # axis = np.cross(n1, n2) axis_norm = np.linalg.norm(axis) if axis_norm < 1e-12: # parallel return np.array([1, 0, 0]), 0.0 axis = axis / axis_norm # cos_theta = np.clip(np.dot(n1, n2), -1.0, 1.0) theta = np.arccos(cos_theta) return axis, theta