Skip to documentation
SLOP

tiny.geometry.intersect

Reference tiny.geometry intersect

Defined in tiny.geometry.

Ray casts and overlap tests between shapes.

API (16)

Actions

Public operations.

Types and contracts

Public types and contracts.

No direct callersNo direct callstiny.geometryintersect
Static calls · unresolved targets: unknown · external targets: unknown.

Source

Called byCallsNo direct callstest sourcelib.geometry.src.intersecttest: overlaps hold at contact and fa...intersectaabbAabb
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallstest sourcelib.geometry.src.intersecttest: overlaps hold at contact and fa...closestsegmentSegmentintersectcapsuleCapsule
Static calls · unresolved targets: 0 · external targets: 2.
Called byCallsBvhoverlapCapsuletest sourcelib.geometry.src.intersecttest: overlaps hold at contact and fa...MeshoverlapCapsuleclosestsegmentTriangleintersectcapsuleTriangle
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallsNo direct callsintersectraySphereprivate sourcelib.geometry.src.sweepsphereFeaturesintersectlineSphereRoots
Static calls · unresolved targets: 0 · external targets: 4.
Called byCallsintersectrayObbtest sourcelib.geometry.src.intersecttest: a ray enters a box through the ...intersectraySlabintersectrayAabb
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallstest sourcelib.geometry.src.intersecttest: a ray enters a turned box in it...intersectrayAabbintersectrayObb
Static calls · unresolved targets: 0 · external targets: 3.
Called byCallstest sourcelib.geometry.src.intersecttest: a ray meets a plane ahead of ittoleranceisParallelintersectrayPlane
Static calls · unresolved targets: 0 · external targets: 3.
Called byCallsNo direct callsBvhraycastBvhraycastAnyprivate sourcelib.geometry.src.bvh.BvhsweepWithintersectrayAabbtest sourcelib.geometry.src.intersecttest: a ray enters a box through the ...intersectraySlab
Static calls · unresolved targets: 0 · external targets: 4.
Called byCallstest sourcelib.geometry.src.intersecttest: a ray enters a sphere, starts i...intersectlineSphereRootsintersectraySphere
Static calls · unresolved targets: 0 · external targets: 2.
Called byCallsBvhraycastBvhraycastAnyclosestsegmentTriangletest sourcelib.geometry.src.intersecttest: a ray meets a triangle from eit...MeshraycastMeshraycastAnytoleranceisParallelintersectrayTriangle
Static calls · unresolved targets: 0 · external targets: 12.
Called byCallsBvhoverlapSpheretest sourcelib.geometry.src.intersecttest: overlaps hold at contact and fa...closestpointAabbintersectsphereAabb
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallstest sourcelib.geometry.src.intersecttest: overlaps hold at contact and fa...closestpointSegmentintersectsphereCapsule
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallstest sourcelib.geometry.src.intersecttest: overlaps hold at contact and fa...closestpointObbintersectsphereObb
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callstest sourcelib.geometry.src.intersecttest: overlaps hold at contact and fa...intersectsphereSphere
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallsBvhoverlapSpheretest sourcelib.geometry.src.intersecttest: overlaps hold at contact and fa...MeshoverlapSphereclosestpointTriangleintersectsphereTriangle
Static calls · unresolved targets: 0 · external targets: 1.

Source: lib/geometry/src/intersect.zig

zig
//! Ray casts and overlap tests between shapes.//!//! Ray queries report the parameter `t` of the first point on or inside the//! shape, in units of the ray direction, and ignore the part of the ray behind//! its origin. A ray starting inside a solid reports `t = 0`. Triangles are//! two-sided.const std = @import("std");const linear = @import("linear");const closest = @import("closest.zig");const primitive = @import("primitive.zig");const tolerance = @import("tolerance.zig");const Aabb = primitive.Aabb;const Capsule = primitive.Capsule;const Obb = primitive.Obb;const Plane = primitive.Plane;const Ray = primitive.Ray;const Sphere = primitive.Sphere;const Triangle = primitive.Triangle;const Vec3 = linear.Vec3;/// Where a ray meets a triangle: the ray parameter and the barycentric/// weights of the second and third corners.pub const TriangleHit = struct {    t: f32,    u: f32,    v: f32,};/// Möller–Trumbore over the whole line, from both sides. The caller filters/// `t` to its span. A ray that grazes the triangle plane, or meets a/// degenerate triangle, misses, because its determinant is parallel noise.pub fn rayTriangle(ray: Ray, triangle: Triangle) ?TriangleHit {    const edge1 = triangle.b.sub(triangle.a);    const edge2 = triangle.c.sub(triangle.a);    const pvec = ray.direction.cross(edge2);    const determinant = edge1.dot(pvec);    const factors_sq = edge1.lengthSq() * edge2.lengthSq() * ray.direction.lengthSq();    if (tolerance.isParallel(determinant * determinant, factors_sq)) return null;    const inverse = 1.0 / determinant;    const tvec = ray.origin.sub(triangle.a);    const u = tvec.dot(pvec) * inverse;    if (u < 0.0 or u > 1.0) return null;    const qvec = tvec.cross(edge1);    const v = ray.direction.dot(qvec) * inverse;    if (v < 0.0 or u + v > 1.0) return null;    return .{ .t = edge2.dot(qvec) * inverse, .u = u, .v = v };}/// The two roots of `|origin + direction t - center|^2 = radius^2`, or null/// when the line stays outside the sphere or the direction has no length.pub fn lineSphereRoots(origin: Vec3, direction: Vec3, center: Vec3, radius: f32) ?[2]f32 {    const m = origin.sub(center);    const a = direction.lengthSq();    if (a <= tolerance.closing_sq_floor) return null;    const b = m.dot(direction);    const c = m.lengthSq() - radius * radius;    const discriminant = b * b - a * c;    if (discriminant < 0) return null;    const root = @sqrt(discriminant);    return .{ (-b - root) / a, (-b + root) / a };}pub fn raySphere(ray: Ray, sphere: Sphere) ?f32 {    const m = ray.origin.sub(sphere.center);    if (m.lengthSq() <= sphere.radius * sphere.radius) return 0;    const roots = lineSphereRoots(ray.origin, ray.direction, sphere.center, sphere.radius) orelse return null;    if (roots[1] < 0) return null;    return @max(roots[0], 0);}/// The span of `t` over which a ray lies inside a box, clipped to `[0, t_max]`,/// or null when the ray misses within that span.pub fn raySlab(ray: Ray, box: Aabb, t_max: f32) ?[2]f32 {    const origin = ray.origin.toArray();    const direction = ray.direction.toArray();    const low = box.min.toArray();    const high = box.max.toArray();    var near: f32 = 0;    var far: f32 = t_max;    for (0..3) |axis| {        if (@abs(direction[axis]) <= tolerance.direction_component_floor) {            if (origin[axis] < low[axis] or origin[axis] > high[axis]) return null;            continue;        }        const inverse = 1.0 / direction[axis];        const first = (low[axis] - origin[axis]) * inverse;        const second = (high[axis] - origin[axis]) * inverse;        near = @max(near, @min(first, second));        far = @min(far, @max(first, second));        if (near > far) return null;    }    return .{ near, far };}pub fn rayAabb(ray: Ray, box: Aabb) ?f32 {    const span = raySlab(ray, box, std.math.inf(f32)) orelse return null;    return span[0];}pub fn rayObb(ray: Ray, box: Obb) ?f32 {    const local = Ray{        .origin = box.toLocal(ray.origin),        .direction = .{            .x = ray.direction.dot(box.axes.cols[0]),            .y = ray.direction.dot(box.axes.cols[1]),            .z = ray.direction.dot(box.axes.cols[2]),        },    };    return rayAabb(local, .{ .min = box.half_extents.negate(), .max = box.half_extents });}pub fn rayPlane(ray: Ray, plane: Plane) ?f32 {    const closing = plane.normal.dot(ray.direction);    if (tolerance.isParallel(closing * closing, ray.direction.lengthSq())) return null;    const t = -plane.signedDistance(ray.origin) / closing;    if (t < 0) return null;    return t;}pub fn sphereSphere(a: Sphere, b: Sphere) bool {    const reach = a.radius + b.radius;    return a.center.distanceSq(b.center) <= reach * reach;}pub fn sphereAabb(sphere: Sphere, box: Aabb) bool {    return closest.pointAabb(sphere.center, box).distanceSq(sphere.center) <= sphere.radius * sphere.radius;}pub fn sphereObb(sphere: Sphere, box: Obb) bool {    return closest.pointObb(sphere.center, box).distanceSq(sphere.center) <= sphere.radius * sphere.radius;}pub fn sphereTriangle(sphere: Sphere, triangle: Triangle) bool {    const nearest = closest.pointTriangle(sphere.center, triangle);    return nearest.distanceSq(sphere.center) <= sphere.radius * sphere.radius;}pub fn sphereCapsule(sphere: Sphere, capsule: Capsule) bool {    const reach = sphere.radius + capsule.radius;    return closest.pointSegment(sphere.center, capsule.axis()).distanceSq(sphere.center) <= reach * reach;}pub fn capsuleCapsule(a: Capsule, b: Capsule) bool {    const reach = a.radius + b.radius;    return closest.segmentSegment(a.axis(), b.axis()).distanceSq() <= reach * reach;}pub fn capsuleTriangle(capsule: Capsule, triangle: Triangle) bool {    return closest.segmentTriangle(capsule.axis(), triangle).distanceSq() <= capsule.radius * capsule.radius;}pub fn aabbAabb(a: Aabb, b: Aabb) bool {    return a.overlaps(b);}const testing = std.testing;const unit_triangle = Triangle{ .a = .{}, .b = Vec3.init(1, 0, 0), .c = Vec3.init(0, 1, 0) };const down = Vec3.init(0, 0, -1);test "a ray meets a triangle from either side with barycentric weights" {    const hit = rayTriangle(.{ .origin = Vec3.init(0.25, 0.5, 3), .direction = down }, unit_triangle).?;    try testing.expectEqual(@as(f32, 3), hit.t);    try testing.expectEqual(@as(f32, 0.25), hit.u);    try testing.expectEqual(@as(f32, 0.5), hit.v);    const below = rayTriangle(.{ .origin = Vec3.init(0.25, 0.25, -2), .direction = down.negate() }, unit_triangle).?;    try testing.expectEqual(@as(f32, 2), below.t);    try testing.expectEqual(@as(?TriangleHit, null), rayTriangle(.{ .origin = Vec3.init(0.9, 0.9, 1), .direction = down }, unit_triangle));    const grazing = Ray{ .origin = Vec3.init(-1, 0.25, 0), .direction = Vec3.init(1, 0, 0) };    try testing.expectEqual(@as(?TriangleHit, null), rayTriangle(grazing, unit_triangle));}test "a ray enters a sphere, starts inside it, or misses" {    const sphere = Sphere{ .center = Vec3.init(0, 0, -5), .radius = 1 };    try testing.expectEqual(@as(?f32, 4), raySphere(.{ .origin = .{}, .direction = down }, sphere));    try testing.expectEqual(@as(?f32, 0), raySphere(.{ .origin = Vec3.init(0, 0, -5), .direction = down }, sphere));    try testing.expectEqual(@as(?f32, null), raySphere(.{ .origin = .{}, .direction = down.negate() }, sphere));    try testing.expectEqual(@as(?f32, null), raySphere(.{ .origin = Vec3.init(3, 0, 0), .direction = down }, sphere));}test "a ray enters a box through the nearest slab" {    const box = Aabb{ .min = Vec3.init(-1, -1, -1), .max = Vec3.init(1, 1, 1) };    try testing.expectEqual(@as(?f32, 4), rayAabb(.{ .origin = Vec3.init(0, 0, 5), .direction = down }, box));    try testing.expectEqual(@as(?f32, 0), rayAabb(.{ .origin = .{}, .direction = down }, box));    try testing.expectEqual(@as(?f32, null), rayAabb(.{ .origin = Vec3.init(2, 0, 5), .direction = down }, box));    const span = raySlab(.{ .origin = Vec3.init(0, 0, 5), .direction = down }, box, 5).?;    try testing.expectEqual([2]f32{ 4, 5 }, span);    try testing.expectEqual(@as(?[2]f32, null), raySlab(.{ .origin = Vec3.init(0, 0, 5), .direction = down }, box, 3));}test "a ray enters a turned box in its own frame" {    const turned = Obb{        .center = Vec3.init(0, 0, -5),        .axes = linear.Mat3.fromCols(Vec3.init(0, 1, 0), Vec3.init(-1, 0, 0), Vec3.init(0, 0, 1)),        .half_extents = Vec3.init(3, 0.5, 1),    };    try testing.expectEqual(@as(?f32, 4), rayObb(.{ .origin = Vec3.init(0, 2, 0), .direction = down }, turned));    try testing.expectEqual(@as(?f32, null), rayObb(.{ .origin = Vec3.init(2, 0, 0), .direction = down }, turned));}test "a ray meets a plane ahead of it" {    const floor = Plane.fromPointNormal(.{}, Vec3.init(0, 0, 1));    try testing.expectEqual(@as(?f32, 2), rayPlane(.{ .origin = Vec3.init(1, 1, 2), .direction = down }, floor));    try testing.expectEqual(@as(?f32, null), rayPlane(.{ .origin = Vec3.init(1, 1, 2), .direction = down.negate() }, floor));    try testing.expectEqual(@as(?f32, null), rayPlane(.{ .origin = Vec3.init(1, 1, 2), .direction = Vec3.init(1, 0, 0) }, floor));}test "overlaps hold at contact and fail past it" {    const ball = Sphere{ .center = Vec3.init(0.25, 0.25, 1), .radius = 1 };    try testing.expect(sphereTriangle(ball, unit_triangle));    try testing.expect(!sphereTriangle(.{ .center = ball.center, .radius = 0.99 }, unit_triangle));    try testing.expect(sphereSphere(ball, .{ .center = Vec3.init(0.25, 0.25, 3), .radius = 1 }));    try testing.expect(!sphereSphere(ball, .{ .center = Vec3.init(0.25, 0.25, 3.5), .radius = 1 }));    const box = Aabb{ .min = Vec3.init(2, 0, 0), .max = Vec3.init(3, 1, 1) };    try testing.expect(sphereAabb(.{ .center = Vec3.init(1, 0.5, 0.5), .radius = 1 }, box));    try testing.expect(!sphereAabb(.{ .center = Vec3.init(0.5, 0.5, 0.5), .radius = 1 }, box));    try testing.expect(aabbAabb(box, .{ .min = Vec3.init(3, 1, 1), .max = Vec3.init(4, 2, 2) }));    try testing.expect(!aabbAabb(box, .{ .min = Vec3.init(3.5, 0, 0), .max = Vec3.init(4, 1, 1) }));    const upright = Capsule{ .a = Vec3.init(0.25, 0.25, 0.5), .b = Vec3.init(0.25, 0.25, 2), .radius = 0.5 };    try testing.expect(capsuleTriangle(upright, unit_triangle));    try testing.expect(!capsuleTriangle(.{ .a = upright.a, .b = upright.b, .radius = 0.25 }, unit_triangle));    const other = Capsule{ .a = Vec3.init(-1, 0, 3), .b = Vec3.init(1, 0, 3), .radius = 0.5 };    const lying = Capsule{ .a = Vec3.init(0, -1, 2), .b = Vec3.init(0, 1, 2), .radius = 0.5 };    try testing.expect(capsuleCapsule(other, lying));    try testing.expect(!capsuleCapsule(other, .{ .a = lying.a, .b = lying.b, .radius = 0.25 }));    try testing.expect(sphereCapsule(.{ .center = Vec3.init(0, 0, 1), .radius = 0.5 }, lying));    try testing.expect(!sphereCapsule(.{ .center = Vec3.init(0, 0, 0.5), .radius = 0.5 }, lying));    const turned = Obb{ .center = .{}, .half_extents = Vec3.init(1, 2, 3) };    try testing.expect(sphereObb(.{ .center = Vec3.init(0, 0, 3.5), .radius = 0.5 }, turned));    try testing.expect(!sphereObb(.{ .center = Vec3.init(1.5, 2.5, 0), .radius = 0.5 }, turned));}

Source: lib/geometry/src/root.zig:9

zig
pub const intersect = @import("intersect.zig");

Audit

Definitions17
Public names17
Members3
Version26.7.0
Revisiondaab053ee433