Source code for ladybug_geometry.intersection3d

# coding=utf-8
"""Utility functions for computing intersections between geometry in 3D space.

Taken mostly from the euclid package available at
https://pypi.org/project/euclid/
"""
from __future__ import division

from .geometry3d.pointvector import Point3D

import math


[docs]def intersect_line3d_plane(line_ray, plane): """Get the intersection between a Ray3D/LineSegment3D and a Plane. Args: line_ray: A LineSegment3D or Ray3D object. plane: A Plane object to intersect. Returns: Point3D of intersection if it exists. None if no intersection exists. """ d = plane.n.dot(line_ray.v) if not d: # parallel return None u = (plane.k - plane.n.dot(line_ray.p)) / d if not line_ray._u_in(u): # line or ray does not have its domain in the plane return None return Point3D(line_ray.p.x + u * line_ray.v.x, line_ray.p.y + u * line_ray.v.y, line_ray.p.z + u * line_ray.v.z)
[docs]def intersect_line3d_plane_infinite(line_ray, plane): """Get the intersection between a Plane and Ray2D/LineSegment2D extended infinitely. Args: line_ray: ALineSegment2D or Ray2D that will be extended infinitely for intersection. plane: A Plane object to intersect. Returns: Point3D of intersection if it exists. None if no intersection exists. """ d = plane.n.dot(line_ray.v) if not d: # parallel return None u = (plane.k - plane.n.dot(line_ray.p)) / d return Point3D(line_ray.p.x + u * line_ray.v.x, line_ray.p.y + u * line_ray.v.y, line_ray.p.z + u * line_ray.v.z)
[docs]def intersect_plane_plane(plane_a, plane_b): """Get the intersection between two Plane objects. Args: plane_a: A Plane object. plane_b: Another Plane object to intersect. Returns: Two objects that define the intersection between two planes 1) A Point3D that lies along the intersection of the two planes. 2) A Vector3D that describes the direction of the intersection. Will be None if no intersection exists (planes are parallel). """ n1_m = plane_a.n.magnitude_squared n2_m = plane_b.n.magnitude_squared n1d2 = plane_a.n.dot(plane_b.n) det = n1_m * n2_m - n1d2 ** 2 if det == 0: # parallel return None c1 = (plane_a.k * n2_m - plane_b.k * n1d2) / det c2 = (plane_b.k * n1_m - plane_a.k * n1d2) / det return Point3D(c1 * plane_a.n.x + c2 * plane_b.n.x, c1 * plane_a.n.y + c2 * plane_b.n.y, c1 * plane_a.n.z + c2 * plane_b.n.z), plane_a.n.cross(plane_b.n)
[docs]def closest_point3d_on_line3d(point, line_ray): """Get the closest Point3D on a LineSegment3D or Ray3D to the input point. Args: point: A Point3D object. line_ray: A LineSegment3D or Ray3D object along which the closest point will be determined. Returns: Point3D for the closest point on line_ray to point. """ d = line_ray.v.magnitude_squared if d == 0: # zero-length segment; just return the end point return line_ray.p u = ((point.x - line_ray.p.x) * line_ray.v.x + (point.y - line_ray.p.y) * line_ray.v.y + (point.z - line_ray.p.z) * line_ray.v.z) / d if not line_ray._u_in(u): u = max(min(u, 1.0), 0.0) return Point3D(line_ray.p.x + u * line_ray.v.x, line_ray.p.y + u * line_ray.v.y, line_ray.p.z + u * line_ray.v.z)
[docs]def closest_point3d_on_line3d_infinite(point, line_ray): """Get the closest Point3D on an infinite extension of a LineSegment3D or Ray3D. Args: point: A Point3D object. line_ray: A LineSegment3D or Ray3D object along which the closest point will be determined. Returns: Point3D for the closest point on the line_ray to the point. """ d = line_ray.v.magnitude_squared if d == 0: # zero-length segment; just return the end point return line_ray.p u = ((point.x - line_ray.p.x) * line_ray.v.x + (point.y - line_ray.p.y) * line_ray.v.y + (point.z - line_ray.p.z) * line_ray.v.z) / d return Point3D(line_ray.p.x + u * line_ray.v.x, line_ray.p.y + u * line_ray.v.y, line_ray.p.z + u * line_ray.v.z)
[docs]def closest_point3d_on_plane(point, plane): """Get the closest Point3D on a Plane to the input point. Args: point: A Point3D object. plane: A Plane object in which the closest point will be determined. Returns: Point3D for the closest point on the plane to point. """ n = plane.n d = point.dot(plane.n) - plane.k return Point3D(point.x - n.x * d, point.y - n.y * d, point.z - n.z * d)
[docs]def closest_point3d_between_line3d_plane(line_ray, plane): """Get the two closest Point3D between a LineSegment3D/Ray3D and a Plane. Args: line_ray: A LineSegment3D or Ray3D object along which the closest point will be determined. plane: A Plane object on which a closest point will be determined. Returns: Two Point3D objects representing 1) The point on the line_ray that is closest to the plane. 2) The point on the plane that is closest to the line_ray. Will be None if there is an intersection between line_ray and the plane """ d = plane.n.dot(line_ray.v) if not d: # parallel, choose an endpoint return line_ray.p, closest_point3d_on_plane(line_ray.p, plane) u = (plane.k - plane.n.dot(line_ray.p)) / d if not line_ray._u_in(u): # intersects out of range of L, choose nearest endpoint u = max(min(u, 1.0), 0.0) close_pt = Point3D(line_ray.p.x + u * line_ray.v.x, line_ray.p.y + u * line_ray.v.y, line_ray.p.z + u * line_ray.v.z) return close_pt, closest_point3d_on_plane(close_pt, plane) return None # intersection
[docs]def intersect_line3d_sphere(line_ray, sphere): """Get the intersection between this Sphere object and a Ray2D/LineSegment2D. Args: line_ray: A LineSegment3D or Ray3D for intersection. sphere: A Sphere to intersect. Returns: Two Point3D objects if a full intersection exists. A Point3D if a point of tangency exists. Will be None if no intersection exists. """ L = line_ray S = sphere a = L.v.magnitude_squared b = 2 * (L.v.x * (L.p.x - S.center.x) + L.v.y * (L.p.y - S.center.y) + L.v.z * (L.p.z - S.center.z)) c = S.center.magnitude_squared + \ L.p.magnitude_squared - \ 2 * S.center.dot(L.p) - \ S.radius ** 2 det = b ** 2 - 4 * a * c if det < 0: return None sq = math.sqrt(det) u1 = (-b + sq) / (2 * a) u2 = (-b - sq) / (2 * a) if not L._u_in(u1): u1 = max(min(u1, 1.0), 0.0) if not L._u_in(u2): u2 = max(min(u2, 1.0), 0.0) p1 = Point3D(L.p.x + u1 * L.v.x, L.p.y + u1 * L.v.y, L.p.z + u1 * L.v.z) if u1 == u2: return p1 else: p2 = Point3D(L.p.x + u2 * L.v.x, L.p.y + u2 * L.v.y, L.p.z + u2 * L.v.z) return p1, p2
[docs]def intersect_plane_sphere(plane, sphere): """Get the intersection of a plane with this Sphere object Args: plane: A Plane object. sphere: A Sphere to intersect. Returns: If a full intersection exists 1) A Point3D that represents the center of the intersection circle. 2) A Vector3D that represents the normal of the intersection circle. 3) A number that represents the radius of the intersection circle. A Point3D Object if a point of tangency exists. None if no intersection exists. """ r = sphere.radius pt_c = sphere.center pt_o = plane.o v_n = plane.n.normalize() # Resulting circle radius. Radius² = r² - [(c-p).n]² d = (pt_o - pt_c).dot(v_n) if abs(r) < abs(d): # No intersection if (r ** 2 - d ** 2) negative return None cut_r = math.sqrt(r ** 2 - d ** 2) # Intersection circle center point. Center_point = p - [(c-p).n]n cut_center = pt_c + (d * v_n) return (cut_center, v_n, cut_r) if cut_r != 0 else cut_center