import numpy as np from .. import util from ..constants import tol_path as tol def line_line(origins, directions, plane_normal=None): """ Find the intersection between two lines. Uses terminology from: http://geomalgorithms.com/a05-_intersect-1.html line 1: P(s) = p_0 + sU line 2: Q(t) = q_0 + tV Parameters --------- origins: (2, d) float, points on lines (d in [2,3]) directions: (2, d) float, direction vectors plane_normal: (3, ) float, if not passed computed from cross Returns --------- intersects: boolean, whether the lines intersect. In 2D, false if the lines are parallel In 3D, false if lines are not coplanar intersection: if intersects: (d) length point of intersection else: None """ # check so we can accept 2D or 3D points origins, is_2D = util.stack_3D(origins, return_2D=True) directions, is_2D = util.stack_3D(directions, return_2D=True) # unitize direction vectors directions /= util.row_norm(directions).reshape((-1, 1)) # exit if values are parallel if np.sum(np.abs(np.diff(directions, axis=0))) < tol.zero: return False, None # using notation from docstring q_0, p_0 = origins v, u = directions w = p_0 - q_0 # recompute plane normal if not passed if plane_normal is None: # the normal of the plane given by the two direction vectors plane_normal = np.cross(u, v) plane_normal /= np.linalg.norm(plane_normal) # vectors perpendicular to the two lines v_perp = np.cross(v, plane_normal) v_perp /= np.linalg.norm(v_perp) # if the vector from origin to origin is on the plane given by # the direction vector, the dot product with the plane normal # should be within floating point error of zero coplanar = abs(np.dot(plane_normal, w)) < tol.zero if not coplanar: return False, None # value of parameter s where intersection occurs s_I = (np.dot(-v_perp, w) / np.dot(v_perp, u)) # plug back into the equation of the line to find the point intersection = p_0 + s_I * u return True, intersection[:(3 - is_2D)]