Source code for arlunio.raytrace.object

from __future__ import annotations

import logging

import numpy as np

import arlunio as ar
from .data import Rays
from .data import ScatterPoint
from arlunio.mask import all_
from arlunio.math import dot
from arlunio.math import normalise


[docs]@ar.definition def Sphere( rays: Rays, t_min: float, t_max: float, *, center=None, radius=0.5 ) -> ScatterPoint: """A sphere.""" logger = logging.getLogger(__name__) center = np.array([0, 0, -1]) if center is None else center oc = rays.origin - center a = dot(rays.direction, rays.direction) b = dot(oc, rays.direction) c = dot(oc, oc) - radius * radius logger.debug("a: %s, b: %s, c: %s", a.shape, b.shape, c.shape) # Determine which rays intersect disc = b * b - a * c hits = disc > 0 logger.debug( "disc: %s, hits: %s, #hits: %s", disc.shape, hits.shape, hits[hits == True].shape, ) a, b, root = a[hits], b[hits], np.sqrt(disc[hits]) # Determine the smallest value of t for those intersections t1 = (-b - root) / a t2 = (-b + root) / a # Assume t1 is the correct value to choose t = t1 # But where t1 < t_min, replace it with t2 if applicable mask = all_(t1 < t_min, t2 > t_min) t[mask] = t2[mask] # Now apply the bounds to t to get the actucal hits mask = all_(t > t_min, t < t_max[hits]) hits[hits] = mask t = t[mask] # Determine the points of intersection and surface normals at t p = rays[hits].at(t) normals = normalise(p - center) # Finally determine which rays are hitting the outside surface of the sphere front_face = dot(rays.direction[hits], normals) < 0 normals[front_face != True] = -normals[front_face != True] return ScatterPoint(hits, t, p, normals, front_face)