Skip to content

Commit

Permalink
feat(geom): update intersects(), support more shape types
Browse files Browse the repository at this point in the history
- add IntersectOpts & as optional arg
  - update impl for ray-poly/ray-polyline
- add support for new shape pairings:
  - line-polygon
  - line-polyline
  - ray-line
  - ray-group
  • Loading branch information
postspectacular committed Mar 20, 2024
1 parent cb18d3b commit 21ce0d4
Show file tree
Hide file tree
Showing 2 changed files with 123 additions and 48 deletions.
169 changes: 121 additions & 48 deletions packages/geom/src/intersects.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,40 @@
import type { MultiFn2 } from "@thi.ng/defmulti";
import { isNumber } from "@thi.ng/checks/is-number";
import type { MultiFn2O } from "@thi.ng/defmulti";
import { defmulti } from "@thi.ng/defmulti/defmulti";
import type { IntersectionResult, IShape, PCLike } from "@thi.ng/geom-api";
import type { IShape, IntersectionResult, PCLike } from "@thi.ng/geom-api";
import { IntersectionType } from "@thi.ng/geom-api/isec";
import { NONE } from "@thi.ng/geom-isec/api";
import { intersectCircleCircle } from "@thi.ng/geom-isec/circle-circle";
import { intersectLineLine } from "@thi.ng/geom-isec/line-line";
import { intersectLinePolylineAll } from "@thi.ng/geom-isec/line-poly";
import { intersectPlanePlane } from "@thi.ng/geom-isec/plane-plane";
import { intersectRayCircle } from "@thi.ng/geom-isec/ray-circle";
import { intersectRayPlane } from "@thi.ng/geom-isec/ray-plane";
import { intersectRayPolyline } from "@thi.ng/geom-isec/ray-poly";
import {
intersectRayPolyline,
intersectRayPolylineAll,
} from "@thi.ng/geom-isec/ray-poly";
import { intersectRayAABB, intersectRayRect } from "@thi.ng/geom-isec/ray-rect";
import { testRectCircle } from "@thi.ng/geom-isec/rect-circle";
import { testAabbAabb, testRectRect } from "@thi.ng/geom-isec/rect-rect";
import { dist2, type Vec } from "@thi.ng/vectors";
import type { AABB } from "./api/aabb.js";
import type { Circle } from "./api/circle.js";
import type { Group } from "./api/group.js";
import type { Line } from "./api/line.js";
import type { Plane } from "./api/plane.js";
import type { Ray } from "./api/ray.js";
import type { Rect } from "./api/rect.js";
import type { Sphere } from "./api/sphere.js";
import { __dispatch2 } from "./internal/dispatch.js";

export interface IntersectOpts {
/**
* Force returning all intersections, if possible (and supported).
*/
all: boolean;
}

/**
* Performs intersection tests on given 2 shapes and returns
* [`IntersectionResult`](https://docs.thi.ng/umbrella/geom-api/interfaces/IntersectionResult.html).
Expand All @@ -29,9 +44,12 @@ import { __dispatch2 } from "./internal/dispatch.js";
*
* - {@link Circle} / {@link Circle}
* - {@link Line} / {@link Line}
* - {@link Line} / {@link Polygon}
* - {@link Line} / {@link Polyline}
* - {@link Plane} / {@link Plane}
* - {@link Ray} / {@link AABB}
* - {@link Ray} / {@link Circle}
* - {@link Ray} / {@link Line}
* - {@link Ray} / {@link Plane}
* - {@link Ray} / {@link Polygon}
* - {@link Ray} / {@link Polyline}
Expand All @@ -42,62 +60,117 @@ import { __dispatch2 } from "./internal/dispatch.js";
* - {@link Rect} / {@link Rect}
* - {@link Sphere} / {@link Sphere}
*
* If {@link IntersectOpts.all} is enabled (default: false) and if the
* intersection pair supports it, all possible intersections will be returned
* (for some implementations this always the case anyway). Currently, this is
* option is only implemented for the following pairings:
*
* - {@link Ray} / {@link Polygon}
* - {@link Ray} / {@link Polyline}
*
* @param a
* @param b
* @param opts
*/
export const intersects: MultiFn2<IShape, IShape, IntersectionResult> =
defmulti<any, any, IntersectionResult>(
__dispatch2,
{
"ray-sphere": "ray-circle",
"ray-quad": "ray-poly",
"ray-tri": "ray-poly",
"sphere-sphere": "circle-circle",
},
{
"aabb-aabb": (a: AABB, b: AABB) => ({
type: testAabbAabb(a.pos, a.size, b.pos, b.size)
? IntersectionType.INTERSECT
: IntersectionType.NONE,
}),
export const intersects: MultiFn2O<
IShape,
IShape,
Partial<IntersectOpts>,
IntersectionResult
> = defmulti<any, any, any, IntersectionResult>(
__dispatch2,
{
"ray-line": "ray-polyline",
"ray-sphere": "ray-circle",
"ray-quad": "ray-poly",
"ray-tri": "ray-poly",
"sphere-sphere": "circle-circle",
},
{
"aabb-aabb": (a: AABB, b: AABB) => ({
type: testAabbAabb(a.pos, a.size, b.pos, b.size)
? IntersectionType.INTERSECT
: IntersectionType.NONE,
}),

"circle-circle": (a: Circle, b: Circle) =>
intersectCircleCircle(a.pos, b.pos, a.r, b.r),

"line-line": ({ points: a }: Line, { points: b }: Line) =>
intersectLineLine(a[0], a[1], b[0], b[1]),

"circle-circle": (a: Circle, b: Circle) =>
intersectCircleCircle(a.pos, b.pos, a.r, b.r),
"line-poly": ({ points: a }: Line, poly: PCLike) =>
intersectLinePolylineAll(a[0], a[1], poly.points, true),

"line-line": ({ points: a }: Line, { points: b }: Line) =>
intersectLineLine(a[0], a[1], b[0], b[1]),
"line-polyline": ({ points: a }: Line, poly: PCLike) =>
intersectLinePolylineAll(a[0], a[1], poly.points, false),

"plane-plane": (a: Plane, b: Plane) =>
intersectPlanePlane(a.normal, a.w, b.normal, b.w),
"plane-plane": (a: Plane, b: Plane) =>
intersectPlanePlane(a.normal, a.w, b.normal, b.w),

"ray-aabb": (ray: Ray, box: AABB) =>
intersectRayAABB(ray.pos, ray.dir, box.pos, box.max()),
"ray-aabb": (ray: Ray, box: AABB) =>
intersectRayAABB(ray.pos, ray.dir, box.pos, box.max()),

"ray-circle": (ray: Ray, sphere: Sphere) =>
intersectRayCircle(ray.pos, ray.dir, sphere.pos, sphere.r),
"ray-circle": (ray: Ray, sphere: Sphere) =>
intersectRayCircle(ray.pos, ray.dir, sphere.pos, sphere.r),

"ray-group": (ray: Ray, { children }: Group, opts?: IntersectOpts) => {
let minD = Infinity;
const points: Vec[] = [];
const all = opts?.all;
for (let child of children) {
let $res = intersects(ray, child, opts);
if ($res.type !== IntersectionType.INTERSECT) continue;
const single = isNumber($res.isec![0]);
const first = single ? <Vec>$res.isec : (<Vec[]>$res.isec)[0];
const alpha =
$res.alpha !== undefined
? $res.alpha
: dist2(ray.pos, first);
if (all) {
if (single) points.push(first);
else points.push(...(<Vec[]>$res.isec));
minD = Math.min(minD, alpha);
} else if (alpha < minD) {
minD = alpha;
points[0] = first;
}
}
return minD < Infinity
? {
type: IntersectionType.INTERSECT,
isec: points,
alpha: minD,
}
: NONE;
},

"ray-plane": (ray: Ray, plane: Plane) =>
intersectRayPlane(ray.pos, ray.dir, plane.normal, plane.w),
"ray-plane": (ray: Ray, plane: Plane) =>
intersectRayPlane(ray.pos, ray.dir, plane.normal, plane.w),

"ray-poly": (ray: Ray, poly: PCLike) =>
intersectRayPolyline(ray.pos, ray.dir, poly.points, true),
"ray-poly": (ray: Ray, poly: PCLike, opts?: IntersectOpts) =>
opts?.all
? intersectRayPolylineAll(ray.pos, ray.dir, poly.points, true)
: intersectRayPolyline(ray.pos, ray.dir, poly.points, true),

"ray-polyline": (ray: Ray, poly: PCLike) =>
intersectRayPolyline(ray.pos, ray.dir, poly.points, false),
"ray-polyline": (ray: Ray, poly: PCLike, opts?: IntersectOpts) =>
opts?.all
? intersectRayPolylineAll(ray.pos, ray.dir, poly.points, false)
: intersectRayPolyline(ray.pos, ray.dir, poly.points, false),

"ray-rect": (ray: Ray, rect: Rect) =>
intersectRayRect(ray.pos, ray.dir, rect.pos, rect.max()),
"ray-rect": (ray: Ray, rect: Rect) =>
intersectRayRect(ray.pos, ray.dir, rect.pos, rect.max()),

"rect-circle": (rect: Rect, circle: Circle) => ({
type: testRectCircle(rect.pos, rect.size, circle.pos, circle.r)
? IntersectionType.INTERSECT
: IntersectionType.NONE,
}),
"rect-circle": (rect: Rect, circle: Circle) => ({
type: testRectCircle(rect.pos, rect.size, circle.pos, circle.r)
? IntersectionType.INTERSECT
: IntersectionType.NONE,
}),

"rect-rect": (a: Rect, b: Rect) => ({
type: testRectRect(a.pos, a.size, b.pos, b.size)
? IntersectionType.INTERSECT
: IntersectionType.NONE,
}),
}
);
"rect-rect": (a: Rect, b: Rect) => ({
type: testRectRect(a.pos, a.size, b.pos, b.size)
? IntersectionType.INTERSECT
: IntersectionType.NONE,
}),
}
);
2 changes: 2 additions & 0 deletions packages/geom/src/point-at.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ import { vertices } from "./vertices.js";
* - {@link Rect}
* - {@link Triangle}
*
* Note: For {@link Ray}, the `t` is interpreted as distance from the ray origin.
*
* @param shape
* @param t
*/
Expand Down

0 comments on commit 21ce0d4

Please sign in to comment.