Skip to content

Commit b71a888

Browse files
committed
Marker occlusion tester
1 parent 41892ce commit b71a888

8 files changed

Lines changed: 924 additions & 0 deletions

File tree

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/**
2+
* Returns an element's visible CSS-pixel size.
3+
*
4+
* Pointer events that callers convert with `clientX/Y - rect.left/top` are in
5+
* `getBoundingClientRect()` coordinates. Use the same dimensions when turning
6+
* those coordinates into NDC or projecting world points back into overlay
7+
* space. Fall back to layout metrics for hidden/jsdom elements whose rect has
8+
* not been populated.
9+
*
10+
* @internal
11+
*/
12+
export function getElementCssSize(element: HTMLElement): { width: number; height: number } {
13+
const rect = element.getBoundingClientRect();
14+
const width = finitePositive(rect.width)
15+
? rect.width
16+
: fallbackDimension(element.clientWidth, element.offsetWidth);
17+
const height = finitePositive(rect.height)
18+
? rect.height
19+
: fallbackDimension(element.clientHeight, element.offsetHeight);
20+
return {width, height};
21+
}
22+
23+
function finitePositive(value: number): boolean {
24+
return Number.isFinite(value) && value > 0;
25+
}
26+
27+
function fallbackDimension(client: number, offset: number): number {
28+
if (finitePositive(client)) return client;
29+
if (finitePositive(offset)) return offset;
30+
return 1;
31+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import {getElementCssSize} from "../getElementCssSize";
2+
3+
describe("getElementCssSize", () => {
4+
test("prefers bounding-client-rect dimensions", () => {
5+
const element = {
6+
getBoundingClientRect: () => ({width: 123.5, height: 45.25}),
7+
clientWidth: 100,
8+
clientHeight: 40,
9+
offsetWidth: 90,
10+
offsetHeight: 30,
11+
} as HTMLElement;
12+
13+
expect(getElementCssSize(element)).toEqual({width: 123.5, height: 45.25});
14+
});
15+
16+
test("falls back to layout dimensions when the rect is empty", () => {
17+
const element = {
18+
getBoundingClientRect: () => ({width: 0, height: 0}),
19+
clientWidth: 100,
20+
clientHeight: 40,
21+
offsetWidth: 90,
22+
offsetHeight: 30,
23+
} as HTMLElement;
24+
25+
expect(getElementCssSize(element)).toEqual({width: 100, height: 40});
26+
});
27+
});
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import type {Vec2, Vec3} from "../../base/math/vector";
2+
import type {MarkerOcclusionMarker, MarkerOcclusionMode} from "./MarkerOcclusionTesterParams";
3+
4+
/**
5+
* Visibility result for a marker after one occlusion update.
6+
*/
7+
export interface MarkerOcclusionResult {
8+
/** Stable ID from {@link MarkerOcclusionMarker.id}. */
9+
markerId: string;
10+
11+
/** Marker input that produced this result. */
12+
marker: MarkerOcclusionMarker;
13+
14+
/** Backend used for this result. */
15+
mode: Exclude<MarkerOcclusionMode, "auto">;
16+
17+
/** `true` when the marker should be shown after frustum and hysteresis rules. */
18+
visible: boolean;
19+
20+
/** Raw occlusion result for this update before hysteresis is applied. */
21+
occluded: boolean;
22+
23+
/** `true` when the marker projects inside the view frustum. */
24+
inFrustum: boolean;
25+
26+
/** Canvas-space marker position in CSS pixels, or `null` when not projectable. */
27+
canvasPos: Vec2 | null;
28+
29+
/** World-space ray origin used by the occlusion test, or `null` when skipped. */
30+
rayOrigin: Vec3 | null;
31+
32+
/** World-space ray direction used by the occlusion test, or `null` when skipped. */
33+
rayDir: Vec3 | null;
34+
35+
/** Distance from ray origin to marker along {@link rayDir}, or `null` when skipped. */
36+
distanceToMarker: number | null;
37+
38+
/** Object ID of the occluder, if any. */
39+
occluderObjectId: string | null;
40+
41+
/** Mesh ID of the occluder, if any. */
42+
occluderMeshId: string | null;
43+
}

0 commit comments

Comments
 (0)