All files / tools/src/utilities/spatial intersectPlanes.ts

4.54% Statements 1/22
0% Branches 0/8
0% Functions 0/2
4.54% Lines 1/22

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65              128x                                                                                                                  
import { vec3 } from 'gl-matrix';
import type { Plane, WorldLine } from './types';
 
/**
 * Two planes whose normals have a cross product shorter than this are treated
 * as parallel (numerically unstable to intersect).
 */
const PARALLEL_EPSILON = 1e-6;
 
/**
 * Computes the intersection line of two planes.
 *
 * Returns null when the planes are parallel (or numerically too close to
 * parallel to produce a stable line). Otherwise returns a stable point on the
 * line and the normalized line direction. The returned point is deterministic
 * for a given pair of planes and does not depend on any external "center"
 * point.
 */
export default function intersectPlanes(
  planeA: Plane,
  planeB: Plane
): WorldLine | null {
  if (!planeA || !planeB) {
    return null;
  }
 
  const normalA = vec3.normalize(vec3.create(), planeA.normal);
  const normalB = vec3.normalize(vec3.create(), planeB.normal);
 
  const direction = vec3.cross(vec3.create(), normalA, normalB);
  const directionLengthSquared = vec3.squaredLength(direction);
 
  if (directionLengthSquared < PARALLEL_EPSILON * PARALLEL_EPSILON) {
    return null;
  }
 
  // Plane offsets in Hessian normal form: dot(n, x) = d
  const dA = vec3.dot(normalA, planeA.point);
  const dB = vec3.dot(normalB, planeB.point);
 
  // Closed-form point on the intersection line:
  // p = (dA * (nB x dir) + dB * (dir x nA)) / |dir|^2
  // which satisfies dot(nA, p) = dA and dot(nB, p) = dB.
  const nBCrossDir = vec3.cross(vec3.create(), normalB, direction);
  const dirCrossNA = vec3.cross(vec3.create(), direction, normalA);
 
  const point = vec3.create();
  vec3.scaleAndAdd(point, point, nBCrossDir, dA);
  vec3.scaleAndAdd(point, point, dirCrossNA, dB);
  vec3.scale(point, point, 1 / directionLengthSquared);
 
  vec3.normalize(direction, direction);
 
  if (
    ![...point, ...direction].every((component) => Number.isFinite(component))
  ) {
    return null;
  }
 
  return {
    point: [point[0], point[1], point[2]],
    direction: [direction[0], direction[1], direction[2]],
  };
}