vgpu-render 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +555 -2
  3. package/dist/assets/fira-mono-medium-subset.d.ts +4 -0
  4. package/dist/assets/fira-mono-medium-subset.js +889 -0
  5. package/dist/assets/fira-mono-msdf-atlas.d.ts +13 -0
  6. package/dist/assets/fira-mono-msdf-atlas.js +6222 -0
  7. package/dist/camera-2d.d.ts +43 -0
  8. package/dist/camera-2d.js +75 -0
  9. package/dist/color-space.d.ts +32 -0
  10. package/dist/color-space.js +45 -0
  11. package/dist/curve.d.ts +16 -0
  12. package/dist/curve.js +46 -0
  13. package/dist/debug-draw-3d.d.ts +14 -0
  14. package/dist/debug-draw-3d.js +36 -0
  15. package/dist/frame-recorder-2d.d.ts +177 -0
  16. package/dist/frame-recorder-2d.js +378 -0
  17. package/dist/frame-recorder.d.ts +361 -0
  18. package/dist/frame-recorder.js +515 -0
  19. package/dist/geometry-generators.d.ts +101 -0
  20. package/dist/geometry-generators.js +481 -0
  21. package/dist/gpu-debug-draw-2d.d.ts +48 -0
  22. package/dist/gpu-debug-draw-2d.js +450 -0
  23. package/dist/gpu-debug-draw-3d.d.ts +52 -0
  24. package/dist/gpu-debug-draw-3d.js +890 -0
  25. package/dist/gpu-instancing.d.ts +34 -0
  26. package/dist/gpu-instancing.js +93 -0
  27. package/dist/gpu-text-3d.d.ts +98 -0
  28. package/dist/gpu-text-3d.js +467 -0
  29. package/dist/index.d.ts +21 -0
  30. package/dist/index.js +21 -0
  31. package/dist/infinite-grid-2d.d.ts +73 -0
  32. package/dist/infinite-grid-2d.js +253 -0
  33. package/dist/infinite-grid-3d.d.ts +134 -0
  34. package/dist/infinite-grid-3d.js +407 -0
  35. package/dist/layout-3d.d.ts +227 -0
  36. package/dist/layout-3d.js +226 -0
  37. package/dist/mesh-batch-3d.d.ts +10 -0
  38. package/dist/mesh-batch-3d.js +67 -0
  39. package/dist/scene-examples.d.ts +39 -0
  40. package/dist/scene-examples.js +200 -0
  41. package/dist/scenes-2d.d.ts +18 -0
  42. package/dist/scenes-2d.js +160 -0
  43. package/dist/scenes-3d.d.ts +46 -0
  44. package/dist/scenes-3d.js +1994 -0
  45. package/dist/shape2d-shader.d.ts +14 -0
  46. package/dist/shape2d-shader.js +287 -0
  47. package/dist/text-atlas.d.ts +174 -0
  48. package/dist/text-atlas.js +495 -0
  49. package/dist/text-font.d.ts +149 -0
  50. package/dist/text-font.js +209 -0
  51. package/dist/text-layout.d.ts +116 -0
  52. package/dist/text-layout.js +340 -0
  53. package/dist/text-msdf-baked.d.ts +36 -0
  54. package/dist/text-msdf-baked.js +112 -0
  55. package/dist/text-msdf-font.d.ts +79 -0
  56. package/dist/text-msdf-font.js +153 -0
  57. package/dist/text-msdf.d.ts +70 -0
  58. package/dist/text-msdf.js +242 -0
  59. package/dist/text-raster.d.ts +40 -0
  60. package/dist/text-raster.js +60 -0
  61. package/dist/text-scenes.d.ts +85 -0
  62. package/dist/text-scenes.js +338 -0
  63. package/dist/text-sdf.d.ts +28 -0
  64. package/dist/text-sdf.js +119 -0
  65. package/package.json +80 -4
@@ -0,0 +1,43 @@
1
+ /**
2
+ * 2D projection math for the debug-draw layer.
3
+ *
4
+ * Two spaces share one pixel-based pipeline:
5
+ *
6
+ * - `world2d` mirrors reference `Camera2d`: the origin sits at the canvas centre, +y points up,
7
+ * and at `zoom = 1` one world unit covers one canvas pixel. `pan` translates the view in
8
+ * world units, `zoom` magnifies it. `2d_shapes`' `X_EXTENT = 1000` is such a pixel-scale
9
+ * world coordinate, not a top-left origin one.
10
+ * - `screen` is the top-left origin, +y down pixel space the text renderer already uses for
11
+ * `TextSpace "screen"`, unaffected by pan or zoom.
12
+ *
13
+ * Both end up as a clip-space matrix, which is what lets the 2D primitives reuse the 3D line,
14
+ * point and joint shaders unchanged: those already compute their screen-space geometry after
15
+ * the projection, so they only need `viewProjection * position` to land the primitive. The
16
+ * matrices below produce `z = 0, w = 1`, i.e. the near plane, which is also where a screen-space
17
+ * text block pins itself.
18
+ */
19
+ /** A point or direction in one of the 2D spaces. */
20
+ export type Vec2 = readonly [number, number];
21
+ /** Which 2D space a command is drawn in. See the module comment for the difference. */
22
+ export type Space2D = "world2d" | "screen";
23
+ export type Camera2D = {
24
+ /** View translation in `world2d` units. Ignored by `screen` space. */
25
+ readonly pan: Vec2;
26
+ /** Canvas pixels per `world2d` unit. Larger magnifies; must be greater than zero. */
27
+ readonly zoom: number;
28
+ };
29
+ export declare const DEFAULT_CAMERA_2D: Camera2D;
30
+ /**
31
+ * Clip matrix for `world2d`, column-major like the view matrices in `demo-camera.ts`.
32
+ *
33
+ * `x_clip = 2 · zoom · (x - pan.x) / width`, and `y_clip` keeps +y up: a point above the pan
34
+ * lands nearer the top of the target.
35
+ */
36
+ export declare function world2dProjection(camera: Camera2D, viewportSize: Vec2): Float32Array;
37
+ /** Clip matrix for the top-left origin `screen` space: `x_clip = 2x/width - 1`, `y` flipped. */
38
+ export declare function screenProjection(viewportSize: Vec2): Float32Array;
39
+ export declare function projection2D(space: Space2D, camera: Camera2D, viewportSize: Vec2): Float32Array;
40
+ /** Converts a `world2d` point to top-left origin canvas pixels, the inverse of {@link screenToWorld2d}. */
41
+ export declare function world2dToScreen(point: Vec2, camera: Camera2D, viewportSize: Vec2): Vec2;
42
+ /** Converts top-left origin canvas pixels to a `world2d` point, the inverse of {@link world2dToScreen}. */
43
+ export declare function screenToWorld2d(point: Vec2, camera: Camera2D, viewportSize: Vec2): Vec2;
@@ -0,0 +1,75 @@
1
+ /**
2
+ * 2D projection math for the debug-draw layer.
3
+ *
4
+ * Two spaces share one pixel-based pipeline:
5
+ *
6
+ * - `world2d` mirrors reference `Camera2d`: the origin sits at the canvas centre, +y points up,
7
+ * and at `zoom = 1` one world unit covers one canvas pixel. `pan` translates the view in
8
+ * world units, `zoom` magnifies it. `2d_shapes`' `X_EXTENT = 1000` is such a pixel-scale
9
+ * world coordinate, not a top-left origin one.
10
+ * - `screen` is the top-left origin, +y down pixel space the text renderer already uses for
11
+ * `TextSpace "screen"`, unaffected by pan or zoom.
12
+ *
13
+ * Both end up as a clip-space matrix, which is what lets the 2D primitives reuse the 3D line,
14
+ * point and joint shaders unchanged: those already compute their screen-space geometry after
15
+ * the projection, so they only need `viewProjection * position` to land the primitive. The
16
+ * matrices below produce `z = 0, w = 1`, i.e. the near plane, which is also where a screen-space
17
+ * text block pins itself.
18
+ */
19
+ export const DEFAULT_CAMERA_2D = { pan: [0, 0], zoom: 1 };
20
+ /**
21
+ * Floors a target size at one pixel. `pass.target.size` is `[0, 0]` until the first resize,
22
+ * and a degenerate matrix would make the whole layer disappear rather than merely misfit.
23
+ */
24
+ function extent(viewportSize) {
25
+ return [Math.max(viewportSize[0], 1), Math.max(viewportSize[1], 1)];
26
+ }
27
+ /**
28
+ * Clip matrix for `world2d`, column-major like the view matrices in `demo-camera.ts`.
29
+ *
30
+ * `x_clip = 2 · zoom · (x - pan.x) / width`, and `y_clip` keeps +y up: a point above the pan
31
+ * lands nearer the top of the target.
32
+ */
33
+ export function world2dProjection(camera, viewportSize) {
34
+ if (!(camera.zoom > 0)) {
35
+ throw new RangeError("camera2d.zoom must be greater than zero");
36
+ }
37
+ const [width, height] = extent(viewportSize);
38
+ const scaleX = (2 * camera.zoom) / width;
39
+ const scaleY = (2 * camera.zoom) / height;
40
+ return new Float32Array([
41
+ scaleX, 0, 0, 0,
42
+ 0, scaleY, 0, 0,
43
+ 0, 0, 0, 0,
44
+ -scaleX * camera.pan[0], -scaleY * camera.pan[1], 0, 1,
45
+ ]);
46
+ }
47
+ /** Clip matrix for the top-left origin `screen` space: `x_clip = 2x/width - 1`, `y` flipped. */
48
+ export function screenProjection(viewportSize) {
49
+ const [width, height] = extent(viewportSize);
50
+ return new Float32Array([
51
+ 2 / width, 0, 0, 0,
52
+ 0, -2 / height, 0, 0,
53
+ 0, 0, 0, 0,
54
+ -1, 1, 0, 1,
55
+ ]);
56
+ }
57
+ export function projection2D(space, camera, viewportSize) {
58
+ return space === "screen" ? screenProjection(viewportSize) : world2dProjection(camera, viewportSize);
59
+ }
60
+ /** Converts a `world2d` point to top-left origin canvas pixels, the inverse of {@link screenToWorld2d}. */
61
+ export function world2dToScreen(point, camera, viewportSize) {
62
+ const [width, height] = extent(viewportSize);
63
+ return [
64
+ (point[0] - camera.pan[0]) * camera.zoom + width / 2,
65
+ height / 2 - (point[1] - camera.pan[1]) * camera.zoom,
66
+ ];
67
+ }
68
+ /** Converts top-left origin canvas pixels to a `world2d` point, the inverse of {@link world2dToScreen}. */
69
+ export function screenToWorld2d(point, camera, viewportSize) {
70
+ const [width, height] = extent(viewportSize);
71
+ return [
72
+ (point[0] - width / 2) / camera.zoom + camera.pan[0],
73
+ (height / 2 - point[1]) / camera.zoom + camera.pan[1],
74
+ ];
75
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Color-space helpers for the debug-draw port.
3
+ *
4
+ * Both reference implementations blend in a linear working space and encode only on
5
+ * output: reference renders with `blend_mix` and encodes into its sRGB framebuffer, and reference
6
+ * blends on a linear target and presents the swapchain through an sRGB view
7
+ * (`add_srgb_suffix`). This port therefore renders into a linear `rgba16float` target and
8
+ * re-encodes in the present pass.
9
+ *
10
+ * Which colors need an sRGB decode depends on how the reference declares them:
11
+ *
12
+ * - **Primitive colors are passed through as authored.** reference debug shaders do
13
+ * `ALBEDO = COLOR.rgb`, and `COLOR` carries no `source_color` qualifier, so
14
+ * `Color(1, 0.85, 0.1)` reaches the linear shading math unchanged. Decoding here would
15
+ * darken every debug primitive.
16
+ * - **Grid colors are decoded.** The infinite-grid uniforms are declared
17
+ * `uniform vec4 thin_line_color : source_color`, so reference converts them from sRGB to
18
+ * linear before the shader sees them. Skipping the decode renders the grid far too
19
+ * bright.
20
+ * - **The clear color is decoded.** reference `default_clear_color` is an sRGB-authored
21
+ * project setting that the engine converts before clearing.
22
+ */
23
+ /**
24
+ * Decodes an sRGB-authored clear color into the linear value the scene pass clears to.
25
+ * Returns a new tuple so the caller cannot alias the config array.
26
+ */
27
+ export declare function linearClearColor(color: readonly [number, number, number, number]): [number, number, number, number];
28
+ /**
29
+ * Decodes an sRGB-authored grid color to linear, preserving a caller-supplied alpha.
30
+ * The grid scales alpha per frame, so alpha is passed in separately.
31
+ */
32
+ export declare function linearGridColor(color: readonly [number, number, number, number], alpha: number): [number, number, number, number];
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Color-space helpers for the debug-draw port.
3
+ *
4
+ * Both reference implementations blend in a linear working space and encode only on
5
+ * output: reference renders with `blend_mix` and encodes into its sRGB framebuffer, and reference
6
+ * blends on a linear target and presents the swapchain through an sRGB view
7
+ * (`add_srgb_suffix`). This port therefore renders into a linear `rgba16float` target and
8
+ * re-encodes in the present pass.
9
+ *
10
+ * Which colors need an sRGB decode depends on how the reference declares them:
11
+ *
12
+ * - **Primitive colors are passed through as authored.** reference debug shaders do
13
+ * `ALBEDO = COLOR.rgb`, and `COLOR` carries no `source_color` qualifier, so
14
+ * `Color(1, 0.85, 0.1)` reaches the linear shading math unchanged. Decoding here would
15
+ * darken every debug primitive.
16
+ * - **Grid colors are decoded.** The infinite-grid uniforms are declared
17
+ * `uniform vec4 thin_line_color : source_color`, so reference converts them from sRGB to
18
+ * linear before the shader sees them. Skipping the decode renders the grid far too
19
+ * bright.
20
+ * - **The clear color is decoded.** reference `default_clear_color` is an sRGB-authored
21
+ * project setting that the engine converts before clearing.
22
+ */
23
+ /** sRGB EOTF: decodes one sRGB-encoded component to linear. */
24
+ function decodeComponent(value) {
25
+ // `!(value > 0)` also catches NaN, which would otherwise escape as NaN.
26
+ if (!(value > 0))
27
+ return 0;
28
+ if (value >= 1)
29
+ return 1;
30
+ return value <= 0.04045 ? value / 12.92 : Math.pow((value + 0.055) / 1.055, 2.4);
31
+ }
32
+ /**
33
+ * Decodes an sRGB-authored clear color into the linear value the scene pass clears to.
34
+ * Returns a new tuple so the caller cannot alias the config array.
35
+ */
36
+ export function linearClearColor(color) {
37
+ return [decodeComponent(color[0]), decodeComponent(color[1]), decodeComponent(color[2]), color[3]];
38
+ }
39
+ /**
40
+ * Decodes an sRGB-authored grid color to linear, preserving a caller-supplied alpha.
41
+ * The grid scales alpha per frame, so alpha is passed in separately.
42
+ */
43
+ export function linearGridColor(color, alpha) {
44
+ return [decodeComponent(color[0]), decodeComponent(color[1]), decodeComponent(color[2]), alpha];
45
+ }
@@ -0,0 +1,16 @@
1
+ import type { Vec3 } from "./frame-recorder.js";
2
+ export type CurvePoint = {
3
+ readonly position: Vec3;
4
+ readonly in?: Vec3;
5
+ readonly out?: Vec3;
6
+ };
7
+ export type Curve3D = {
8
+ readonly points: readonly CurvePoint[];
9
+ readonly closed?: boolean;
10
+ };
11
+ export type CurveTransform = {
12
+ readonly center?: Vec3;
13
+ readonly rotation?: readonly [number, number, number];
14
+ };
15
+ export declare function rotatePoint(p: Vec3, rotation: readonly [number, number, number]): Vec3;
16
+ export declare function sampleCurve(curve: Curve3D, tolerance?: number, transform?: CurveTransform): Vec3[];
package/dist/curve.js ADDED
@@ -0,0 +1,46 @@
1
+ const add = (a, b) => [a[0] + b[0], a[1] + b[1], a[2] + b[2]];
2
+ export function rotatePoint(p, rotation) {
3
+ const [sx, cx] = [Math.sin(rotation[0]), Math.cos(rotation[0])];
4
+ const [sy, cy] = [Math.sin(rotation[1]), Math.cos(rotation[1])];
5
+ const [sz, cz] = [Math.sin(rotation[2]), Math.cos(rotation[2])];
6
+ const x1 = p[0] * cy + p[2] * sy;
7
+ const z1 = -p[0] * sy + p[2] * cy;
8
+ const y1 = p[1] * cx - z1 * sx;
9
+ const z2 = p[1] * sx + z1 * cx;
10
+ return [x1 * cz - y1 * sz, x1 * sz + y1 * cz, z2];
11
+ }
12
+ function cubic(p0, p1, p2, p3, t) {
13
+ const u = 1 - t;
14
+ const a = u * u * u;
15
+ const b = 3 * u * u * t;
16
+ const c = 3 * u * t * t;
17
+ const d = t * t * t;
18
+ return [
19
+ a * p0[0] + b * p1[0] + c * p2[0] + d * p3[0],
20
+ a * p0[1] + b * p1[1] + c * p2[1] + d * p3[1],
21
+ a * p0[2] + b * p1[2] + c * p2[2] + d * p3[2],
22
+ ];
23
+ }
24
+ export function sampleCurve(curve, tolerance = 0.08, transform = {}) {
25
+ const points = curve.points;
26
+ if (points.length < 2)
27
+ return points.map((point) => add(rotatePoint(point.position, transform.rotation ?? [0, 0, 0]), transform.center ?? [0, 0, 0]));
28
+ const rotation = transform.rotation ?? [0, 0, 0];
29
+ const center = transform.center ?? [0, 0, 0];
30
+ const place = (p) => add(rotatePoint(p, rotation), center);
31
+ const steps = Math.max(2, Math.min(64, Math.ceil(0.5 / Math.max(tolerance, 0.001))));
32
+ const out = [];
33
+ const count = curve.closed ? points.length : points.length - 1;
34
+ for (let i = 0; i < count; i++) {
35
+ const a = points[i];
36
+ const b = points[(i + 1) % points.length];
37
+ const p0 = a.position;
38
+ const p1 = add(a.position, a.out ?? [0, 0, 0]);
39
+ const p2 = add(b.position, b.in ?? [0, 0, 0]);
40
+ const p3 = b.position;
41
+ for (let s = 0; s < steps; s++)
42
+ out.push(place(cubic(p0, p1, p2, p3, s / steps)));
43
+ }
44
+ out.push(place(curve.closed ? points[0].position : points[points.length - 1].position));
45
+ return out;
46
+ }
@@ -0,0 +1,14 @@
1
+ import { FrameRecorder, type FrameCommand, type LayerMask, type LineDefaults, type PointDefaults } from "./frame-recorder.js";
2
+ export type DebugDrawOptions = {
3
+ defaultVisibleLayers?: LayerMask;
4
+ /** Style applied to every point that does not override it. */
5
+ defaultPointStyle?: PointDefaults;
6
+ /** Style applied to every line-like primitive that does not override it. */
7
+ defaultLineStyle?: LineDefaults;
8
+ };
9
+ export declare class DebugDraw3D {
10
+ #private;
11
+ constructor(options?: DebugDrawOptions);
12
+ beginFrame(): FrameRecorder;
13
+ finishFrame(recorder: FrameRecorder): readonly FrameCommand[];
14
+ }
@@ -0,0 +1,36 @@
1
+ import { FrameRecorder, resolveFrameRecorderDefaults, } from "./frame-recorder.js";
2
+ export class DebugDraw3D {
3
+ #visibleLayers;
4
+ #defaults;
5
+ #active;
6
+ constructor(options = {}) {
7
+ const visibleLayers = options.defaultVisibleLayers ?? 0xffff_ffff;
8
+ if (!Number.isInteger(visibleLayers) || visibleLayers < 0) {
9
+ throw new RangeError("defaultVisibleLayers must be a non-negative integer bitmask");
10
+ }
11
+ this.#visibleLayers = visibleLayers;
12
+ this.#defaults = {
13
+ ...(options.defaultPointStyle === undefined ? {} : { point: { ...options.defaultPointStyle } }),
14
+ ...(options.defaultLineStyle === undefined ? {} : { line: { ...options.defaultLineStyle } }),
15
+ };
16
+ // Validate eagerly so invalid defaults fail at construction, not on the first frame.
17
+ resolveFrameRecorderDefaults(this.#defaults);
18
+ }
19
+ beginFrame() {
20
+ if (this.#active !== undefined) {
21
+ throw new Error("a debug draw frame is already active");
22
+ }
23
+ const recorder = new FrameRecorder(this.#defaults);
24
+ this.#active = recorder;
25
+ return recorder;
26
+ }
27
+ finishFrame(recorder) {
28
+ if (this.#active !== recorder) {
29
+ throw new Error("recorder does not belong to this debug draw instance");
30
+ }
31
+ const commands = recorder.commands(this.#visibleLayers);
32
+ recorder.seal();
33
+ this.#active = undefined;
34
+ return commands;
35
+ }
36
+ }
@@ -0,0 +1,177 @@
1
+ /**
2
+ * 2D debug-draw commands and their recorder.
3
+ *
4
+ * The 2D layer is the same data flow as the 3D one — `FrameRecorder -> FrameCommand[] -> GPU` —
5
+ * reached through `frame.d2` instead of a second entry point, so a frame has one lifecycle, one
6
+ * layer mask and one set of defaults. The commands are separate types rather than 3D commands
7
+ * with a `space` flag because a 2D point is a `Vec2` and 2D strokes carry no surface normal;
8
+ * keeping them apart stops a 3D-only field from silently meaning something different here.
9
+ *
10
+ * See `camera-2d.ts` for what `world2d` and `screen` mean.
11
+ */
12
+ import type { Space2D, Vec2 } from "./camera-2d.js";
13
+ import type { Color, LayerMask, LineJoint, RenderedJoint, ResolvedLineDefaults, ResolvedPointDefaults } from "./frame-recorder.js";
14
+ export type Line2DOptions = {
15
+ color?: Color;
16
+ colorEnd?: Color;
17
+ /** Stroke width in canvas pixels (`widthMode: "screen"`) or world2d units. */
18
+ width?: number;
19
+ widthMode?: "screen" | "world";
20
+ dash?: readonly [number, number, number];
21
+ joint?: LineJoint;
22
+ /** Defaults to `world2d`; see `camera-2d.ts`. */
23
+ space?: Space2D;
24
+ layer?: LayerMask;
25
+ };
26
+ export type Point2DOptions = {
27
+ color?: Color;
28
+ size?: number;
29
+ sizeMode?: "screen" | "world";
30
+ round?: boolean;
31
+ space?: Space2D;
32
+ layer?: LayerMask;
33
+ };
34
+ /** Every filled/stroked 2D shape the layer can draw. */
35
+ export type Shape2DKind = "rect" | "rounded-rect" | "circle" | "ellipse" | "arc" | "annulus" | "capsule" | "polygon" | "triangle" | "rhombus" | "sector" | "segment";
36
+ export type Shape2DOptions = {
37
+ color?: Color;
38
+ /** Rotation in radians, anticlockwise in `world2d` and screen-clockwise in `screen`. */
39
+ rotation?: number;
40
+ /** Filled by default; `false` strokes the outline with `strokeWidth`. */
41
+ fill?: boolean;
42
+ /** Outline width in canvas pixels; only read when `fill` is false. */
43
+ strokeWidth?: number;
44
+ /** Start angle of `arc`/`sector`/`segment` in radians. Defaults to `0`, the +x axis. */
45
+ startAngle?: number;
46
+ /** Sweep of `arc`/`sector`/`segment` in radians. Defaults to a full turn; negative turns the other way. */
47
+ sweep?: number;
48
+ space?: Space2D;
49
+ layer?: LayerMask;
50
+ };
51
+ export type Shape2DCommand = {
52
+ readonly kind: "shape2d";
53
+ readonly shape: Shape2DKind;
54
+ readonly center: Vec2;
55
+ /**
56
+ * Primary half-size, meaning depends on `shape`: box half extents, ellipse radii, circumradius,
57
+ * capsule `[radius, halfLength]`, annulus `[outer, inner]`, rhombus half diagonals, or a
58
+ * triangle's first vertex offset from the centroid.
59
+ */
60
+ readonly size: Vec2;
61
+ /** Second/third triangle vertices relative to the centroid; zero for every other shape. */
62
+ readonly vertexB: Vec2;
63
+ readonly vertexC: Vec2;
64
+ readonly cornerRadius: number;
65
+ /** Outline width in canvas pixels. */
66
+ readonly thickness: number;
67
+ readonly filled: boolean;
68
+ readonly rotation: number;
69
+ readonly startAngle: number;
70
+ readonly sweep: number;
71
+ /** Vertex count of `polygon`; zero for every other shape. */
72
+ readonly sides: number;
73
+ readonly color: Color;
74
+ readonly space: Space2D;
75
+ readonly layer: LayerMask;
76
+ };
77
+ export type Point2DCommand = {
78
+ readonly kind: "point2d";
79
+ readonly position: Vec2;
80
+ readonly color: Color;
81
+ readonly size: number;
82
+ readonly sizeMode: "screen" | "world";
83
+ readonly round?: boolean;
84
+ readonly space: Space2D;
85
+ readonly layer: LayerMask;
86
+ };
87
+ export type Line2DCommand = {
88
+ readonly kind: "line2d";
89
+ readonly start: Vec2;
90
+ readonly end: Vec2;
91
+ readonly color: Color;
92
+ readonly colorEnd: Color;
93
+ readonly width: number;
94
+ readonly widthMode: "screen" | "world";
95
+ readonly dash?: readonly [number, number, number];
96
+ readonly joint?: LineJoint;
97
+ readonly space: Space2D;
98
+ readonly layer: LayerMask;
99
+ };
100
+ /**
101
+ * One path vertex of a `lineStrip`/`polyline`, the 2D twin of `JointCommand`. The renderer
102
+ * rebuilds the round/bevel/miter wedge from `previous`, `center` and `next`, exactly as the 3D
103
+ * joint pipeline does.
104
+ */
105
+ export type Joint2DCommand = {
106
+ readonly kind: "joint2d";
107
+ readonly previous: Vec2;
108
+ readonly center: Vec2;
109
+ readonly next: Vec2;
110
+ readonly color: Color;
111
+ readonly width: number;
112
+ readonly widthMode: "screen" | "world";
113
+ readonly dash?: readonly [number, number, number];
114
+ readonly joint: RenderedJoint;
115
+ readonly space: Space2D;
116
+ readonly layer: LayerMask;
117
+ };
118
+ /** Everything a `FrameRecorder2D` can emit. */
119
+ export type FrameCommand2D = Point2DCommand | Line2DCommand | Joint2DCommand | Shape2DCommand;
120
+ /** The 3D recorder's resolved point/line styles, reused so 2D and 3D defaults cannot drift. */
121
+ export type FrameRecorder2DStyleDefaults = {
122
+ readonly point: ResolvedPointDefaults;
123
+ readonly line: ResolvedLineDefaults;
124
+ };
125
+ /**
126
+ * Records 2D primitives for one frame. Obtained through `FrameRecorder.d2`; it seals with its
127
+ * parent, so a caller cannot keep drawing into a finished frame.
128
+ */
129
+ export declare class FrameRecorder2D {
130
+ #private;
131
+ constructor(defaults: FrameRecorder2DStyleDefaults, assertOpen: () => void);
132
+ point(position: Vec2, options?: Point2DOptions): this;
133
+ line(start: Vec2, end: Vec2, options?: Line2DOptions): this;
134
+ ray(origin: Vec2, direction: Vec2, options?: Line2DOptions): this;
135
+ /**
136
+ * Path of segments, the 2D twin of `FrameRecorder.lineStrip`. Dash distance accumulates over
137
+ * the whole path and an open strip joins only its interior vertices, matching the 3D layer.
138
+ */
139
+ lineStrip(points: readonly Vec2[], options?: Line2DOptions, closed?: boolean): this;
140
+ polyline(points: readonly Vec2[], options?: Line2DOptions, closed?: boolean): this;
141
+ /**
142
+ * Axis-aligned grid of lines, the 2D twin of `FrameRecorder.grid` and reference `grid_2d`: `cells`
143
+ * are the cells per axis, so the drawn line count is one more on each side.
144
+ */
145
+ grid(center: Vec2, cells: readonly [number, number], spacing: Vec2, options?: Line2DOptions): this;
146
+ /** Rectangle with the given full width and height (reference `Rectangle::new`). */
147
+ rect(center: Vec2, size: Vec2, options?: Shape2DOptions): this;
148
+ /** Rectangle with rounded corners; the shader clamps the radius to half the shorter side. */
149
+ roundedRect(center: Vec2, size: Vec2, cornerRadius: number, options?: Shape2DOptions): this;
150
+ /** Filled disc with the given radius (reference `Circle::new`). */
151
+ circle(center: Vec2, radius: number, options?: Shape2DOptions): this;
152
+ /** Ellipse with independent x and y radii (reference `Ellipse::new(x_radius, y_radius)`). */
153
+ ellipse(center: Vec2, radii: Vec2, options?: Shape2DOptions): this;
154
+ /**
155
+ * Circular arc from `startAngle` through `sweep`. It is a curve without an interior, so an
156
+ * unfilled arc (the default) strokes it with `strokeWidth` while `fill: true` leaves a hairline.
157
+ */
158
+ arc(center: Vec2, radius: number, options?: Shape2DOptions): this;
159
+ /** Ring band between two radii (reference `Annulus::new(inner_radius, outer_radius)`). */
160
+ annulus(center: Vec2, innerRadius: number, outerRadius: number, options?: Shape2DOptions): this;
161
+ /** Capsule whose straight section is `length` long along y (reference `Capsule2d::new(radius, length)`). */
162
+ capsule(center: Vec2, radius: number, length: number, options?: Shape2DOptions): this;
163
+ /** Regular polygon with a vertex on the +x axis (reference `RegularPolygon::new(circumradius, sides)`). */
164
+ polygon(center: Vec2, circumradius: number, sides: number, options?: Shape2DOptions): this;
165
+ /** Arbitrary triangle (reference `Triangle2d::new(a, b, c)`); `rotation` turns about its centroid. */
166
+ triangle(a: Vec2, b: Vec2, c: Vec2, options?: Shape2DOptions): this;
167
+ /** Diamond with the given full diagonals (reference `Rhombus::new(horizontal, vertical)`). */
168
+ rhombus(center: Vec2, diagonals: Vec2, options?: Shape2DOptions): this;
169
+ /** Disc sector from `startAngle` through `sweep` (reference `CircularSector`). */
170
+ sector(center: Vec2, radius: number, options?: Shape2DOptions): this;
171
+ /** Disc region between a chord and its arc (reference `CircularSegment`). */
172
+ segment(center: Vec2, radius: number, options?: Shape2DOptions): this;
173
+ get points(): readonly Point2DCommand[];
174
+ get lines(): readonly Line2DCommand[];
175
+ get joints(): readonly Joint2DCommand[];
176
+ get shapes(): readonly Shape2DCommand[];
177
+ }