vgpu-render 0.1.0 → 0.2.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.
- package/README.md +76 -26
- package/dist/curve.d.ts +1 -1
- package/dist/debug-draw-3d.d.ts +3 -3
- package/dist/debug-draw-3d.js +10 -10
- package/dist/{frame-recorder-2d.d.ts → frame-draw-list-2d.d.ts} +11 -11
- package/dist/{frame-recorder-2d.js → frame-draw-list-2d.js} +6 -6
- package/dist/{frame-recorder.d.ts → frame-draw-list.d.ts} +60 -8
- package/dist/{frame-recorder.js → frame-draw-list.js} +51 -10
- package/dist/geometry-generators.d.ts +2 -2
- package/dist/geometry-generators.js +1 -1
- package/dist/gpu-debug-draw-2d.d.ts +1 -1
- package/dist/gpu-debug-draw-3d.d.ts +1 -1
- package/dist/gpu-debug-draw-3d.js +4 -2
- package/dist/gpu-text-3d.d.ts +1 -1
- package/dist/gpu-text-3d.js +49 -2
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -3
- package/dist/mesh-batch-3d.d.ts +1 -1
- package/dist/shape2d-shader.js +3 -2
- package/dist/text-atlas.d.ts +6 -4
- package/dist/text-atlas.js +9 -6
- package/dist/text-font.d.ts +48 -7
- package/dist/text-font.js +63 -11
- package/dist/text-layout.d.ts +63 -4
- package/dist/text-layout.js +58 -8
- package/dist/text-msdf-baked.d.ts +9 -4
- package/dist/text-msdf-baked.js +9 -4
- package/dist/text-msdf-font.d.ts +10 -4
- package/dist/text-msdf-font.js +9 -3
- package/dist/vector-math.d.ts +19 -0
- package/dist/vector-math.js +37 -0
- package/package.json +1 -1
- package/dist/layout-3d.d.ts +0 -227
- package/dist/layout-3d.js +0 -226
- package/dist/scene-examples.d.ts +0 -39
- package/dist/scene-examples.js +0 -200
- package/dist/scenes-2d.d.ts +0 -18
- package/dist/scenes-2d.js +0 -160
- package/dist/scenes-3d.d.ts +0 -46
- package/dist/scenes-3d.js +0 -1994
- package/dist/text-scenes.d.ts +0 -85
- package/dist/text-scenes.js +0 -338
package/README.md
CHANGED
|
@@ -30,23 +30,23 @@ dependencies of this package.
|
|
|
30
30
|
|
|
31
31
|
## Usage
|
|
32
32
|
|
|
33
|
-
The public API uses an explicit frame
|
|
33
|
+
The public API uses an explicit frame draw list:
|
|
34
34
|
|
|
35
35
|
```ts
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
36
|
+
const drawList = debug.beginFrame();
|
|
37
|
+
drawList.point([0, 0, 0], { color: [1, 0, 0, 1], size: 8 });
|
|
38
|
+
drawList.line([0, 0, 0], [1, 0, 0], { color: [0, 1, 0, 1], width: 2 });
|
|
39
|
+
drawList.arrow([0, 0, 0], [1, 1, 0], { color: [0, 0.5, 1, 1] });
|
|
40
|
+
drawList.lineStrip([[0, 0, 0], [1, 0, 1], [2, 0, 0]], { width: 4, joint: "round" }, false);
|
|
41
41
|
// Per-vertex normals select the reference `*_n` shading: each segment uses its start
|
|
42
42
|
// vertex's normal and the joints between them blend the two normals.
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const commands = debug.finishFrame(
|
|
43
|
+
drawList.lineStrip([[0, 0, 0], [1, 0, 0], [1, 1, 0]], { width: 4, joint: "round", normals: [[0, 1, 0], [0, 1, 0], [0, 1, 0]] }, false);
|
|
44
|
+
drawList.grid([0, 0, 0], [4, 4], [0.5, 0.5], { color: [0.4, 0.4, 0.5, 1] });
|
|
45
|
+
drawList.curve(cubicBezier, { color: [1, 1, 1, 1], tolerance: 0.08 });
|
|
46
|
+
drawList.mesh(sphere(0.5), { solid: true, color: [0.2, 0.6, 1, 0.8] });
|
|
47
|
+
drawList.text([0, 1, 0], "world label", { fontSize: 32, pixelSize: 0.01, color: [1, 0.8, 0.2, 1] });
|
|
48
|
+
drawList.text([24, 24, 0], "screen HUD", { space: "screen", anchor: "top-left", fontSize: 16 });
|
|
49
|
+
const commands = debug.finishFrame(drawList);
|
|
50
50
|
|
|
51
51
|
// `view` is the world-to-view matrix; normal-aware strokes read it like the reference view matrix.
|
|
52
52
|
renderer.prepare(commands, { viewProjection, view });
|
|
@@ -108,7 +108,7 @@ alphas, which the addon does not expose.
|
|
|
108
108
|
## Pipeline
|
|
109
109
|
|
|
110
110
|
```text
|
|
111
|
-
|
|
111
|
+
FrameDrawList -> FrameCommand[] -> GpuDebugDraw3D
|
|
112
112
|
points -> instanced billboards
|
|
113
113
|
lines -> screen-space or world-space quads, dash + normal aware
|
|
114
114
|
triangles -> transient solid meshes
|
|
@@ -146,7 +146,9 @@ Two coordinate spaces share one pixel-based pipeline:
|
|
|
146
146
|
| `screen` | top-left corner | down | one canvas pixel | no |
|
|
147
147
|
|
|
148
148
|
`world2d` mirrors the reference 2D camera: the origin is the centre of the target, `+y` points up and
|
|
149
|
-
`zoom` is canvas pixels per world unit.
|
|
149
|
+
`zoom` is canvas pixels per world unit. Both spaces are in **target pixels**, like the text `screen`
|
|
150
|
+
space, so a HiDPI caller scales `screen` content itself (`gallery/screen-scale.ts` does that for the
|
|
151
|
+
gallery); `world2d` needs no such step because its projection already reads the target size. `2d_shapes`' `X_EXTENT = 1000` is a pixel-scale world
|
|
150
152
|
coordinate in that sense, not a top-left one — the top-left, +y-down space is the reference UI space,
|
|
151
153
|
and here it is `screen`, the same one `TextSpace "screen"` already uses.
|
|
152
154
|
|
|
@@ -156,7 +158,7 @@ dashes with the same phase accumulation as the 3D paths, and round/bevel/miter j
|
|
|
156
158
|
`triangle`, `rhombus`, `sector`, `segment`. Each is one instanced quad evaluated as a signed
|
|
157
159
|
distance field in the fragment stage, so nothing is triangulated on the CPU and an outline
|
|
158
160
|
width stays a pixel count at any zoom. `fill: false` plus `strokeWidth` turns any of them into
|
|
159
|
-
an outline.
|
|
161
|
+
an outline; an `arc`'s outline is clipped to its `startAngle`/`sweep` wedge.
|
|
160
162
|
- The layer is an overlay: it draws after every 3D primitive, pins itself to the near plane and
|
|
161
163
|
does not write depth. Screen-space text remains the last thing drawn.
|
|
162
164
|
- `InfiniteGrid2D` renders the reference's 2D grid as a full-viewport shader quad: decimal LOD levels,
|
|
@@ -164,11 +166,15 @@ does not write depth. Screen-space text remains the last thing drawn.
|
|
|
164
166
|
`cellSize`, `lineWidthPixels`, `minPixelsBetweenCells`, the LOD range and both line colors from
|
|
165
167
|
the gallery's grid panel, and draws as the 2D layer's background.
|
|
166
168
|
- The 2D camera is independent of the 3D one (`mode: "3d" | "2d"` in the gallery config); the
|
|
167
|
-
`2d-*` pages fit their declared `view2d` extent, drag to pan and scroll to zoom.
|
|
169
|
+
`2d-*` pages fit their declared `view2d` extent, drag or hold WASD to pan, and scroll to zoom. The gallery
|
|
170
|
+
classifies the 2D layer like the 3D scenes: `2d-gizmos` and `2d-shapes` are overviews, while
|
|
171
|
+
`2d-lines`, `2d-curves`, `2d-markers`, `2d-outlines`, `2d-fill`, `2d-spaces` and `2d-grid` are
|
|
172
|
+
column-by-row matrices whose column titles and sample labels are `world2d` text, so they pan
|
|
173
|
+
and zoom with the geometry they annotate.
|
|
168
174
|
|
|
169
175
|
## Text
|
|
170
176
|
|
|
171
|
-
`
|
|
177
|
+
`drawList.text(position, text, options)` draws one block per call. `text` is a string or a list
|
|
172
178
|
of spans, the equivalent of a text root entity plus its child span entities.
|
|
173
179
|
|
|
174
180
|
Three placement spaces share one pipeline and are selected per block:
|
|
@@ -179,6 +185,13 @@ Three placement spaces share one pipeline and are selected per block:
|
|
|
179
185
|
| `world-billboard` | world units, block rebuilt from the camera's axes | `fontSize × pixelSize` |
|
|
180
186
|
| `screen` | **top-left origin pixels** (`position.z` ignored) | `fontSize` pixels |
|
|
181
187
|
|
|
188
|
+
The `screen` space is in **target pixels** — the same units as the pass's viewport — because the
|
|
189
|
+
library has no DOM access and must not guess a display density. A caller on a HiDPI display owns the
|
|
190
|
+
conversion: the gallery authors its pages in CSS pixels and multiplies every screen-space command by
|
|
191
|
+
`devicePixelRatio` before handing the frame over (`gallery/screen-scale.ts`). A block placed at
|
|
192
|
+
`[24, 24]` with `fontSize: 18` in CSS pixels then draws at `[48, 48]` and 36 target pixels on a 2×
|
|
193
|
+
display, which is what keeps a label the same physical size on every screen.
|
|
194
|
+
|
|
182
195
|
`fontSize` is in **font pixels** and is the layout unit; `pixelSize` is world units per font pixel.
|
|
183
196
|
That pair is the reference `font_size` / `pixel_size`, and it lets a block keep a fixed world
|
|
184
197
|
size while its glyphs are rasterised at any resolution. Which resolution that is belongs to the glyph
|
|
@@ -187,6 +200,12 @@ source: one that can rasterise on demand is asked for the size the block covers
|
|
|
187
200
|
`fontSize × rasterScale`. Layout options follow the reference layout model: `justify`, `lineBreak`,
|
|
188
201
|
`bounds` (`TextBounds`, in font pixels), `anchor`, `lineHeight` and `letterSpacing`.
|
|
189
202
|
|
|
203
|
+
`rotation` turns a `world-plane` block away from the XY plane, with the reference `Label3D.rotation`
|
|
204
|
+
Euler order (Y, then X, then Z) in radians. `[-Math.PI / 2, 0, 0]` lays a block flat on the XZ plane
|
|
205
|
+
so it reads from above, which is what the reference example labels do; the two camera-relative
|
|
206
|
+
spaces ignore it. The basis travels in the instance world matrix, so the text pipeline stays inside
|
|
207
|
+
the device's vertex attribute budget.
|
|
208
|
+
|
|
190
209
|
`lineHeight` is the absolute pixel form when you pass one, and the default is a `1.2` multiple of the
|
|
191
210
|
font size otherwise — which means a line's box is that multiple of **the line's own
|
|
192
211
|
largest span**, so a line holding a bigger span is taller. `lineHeightScale` names that multiple
|
|
@@ -198,6 +217,34 @@ shaping pass adds it to each cluster's advance.
|
|
|
198
217
|
World text is depth-tested but never depth-written, so geometry in front of it
|
|
199
218
|
occludes it; `screen` text pins itself to the near plane and always wins.
|
|
200
219
|
|
|
220
|
+
### Font face
|
|
221
|
+
|
|
222
|
+
`weight` (1–1000, default `400`), `style` (`normal` or `italic`) and `width` (the nine
|
|
223
|
+
`font-stretch` keywords) select the face a block is rasterised in, mirroring bevy's `TextFont`.
|
|
224
|
+
They are block defaults, and a span overrides one axis of them at a time rather than replacing the
|
|
225
|
+
whole face. The glyph atlas keys on all three, so two weights of one character cannot share texels.
|
|
226
|
+
|
|
227
|
+
How much of that a browser can honour depends on the loaded face, and the limits are real:
|
|
228
|
+
|
|
229
|
+
| option | what Canvas2D does |
|
|
230
|
+
| --- | --- |
|
|
231
|
+
| `weight` | A weight the loaded family does not carry is **synthesised** in one step (the threshold sits between 500 and 600), so `700` and `900` then look identical and the advance never changes. A variable font's real `wght` axis is not reachable — Canvas2D has no `font-variation-settings` — but the browser **does** pick a face by the weight it was registered with, so registering the same family once per weight gives real weights. |
|
|
232
|
+
| `style` | `italic` is the only slant the font shorthand keeps; `oblique <angle>` reads back as plain `italic`, which is why bevy's `FontStyle::Oblique(a)` is not exposed. A face without an italic is slanted by the browser. |
|
|
233
|
+
| `width` | The keyword only reaches the font when the face has a `wdth` axis. The bundled static subset has none, so every stop draws at normal width there. A percentage is rejected by the shorthand, so only the nine named stops exist here. |
|
|
234
|
+
|
|
235
|
+
The bundled Fira Mono subset is a static Medium face, so out of the box `weight` only ever produces
|
|
236
|
+
one synthesised step. `loadFontFaces(family, faces)` is the way out: it registers each entry as a face
|
|
237
|
+
of `family` with its own `weight` descriptor, so `weight: 200` through `900` select genuinely different
|
|
238
|
+
faces. The gallery does exactly that for the `text-font-query` page, which draws with eight instances
|
|
239
|
+
of a variable font built by `bun scripts/build-font-faces.ts` (see
|
|
240
|
+
`gallery/assets/`). That is gallery-only payload: the library's own entry point does not
|
|
241
|
+
carry it.
|
|
242
|
+
|
|
243
|
+
A custom glyph source receives the resolved face — `{ family, weight, style, width }` — instead of a
|
|
244
|
+
family string, because those are the fields the atlas keys on. A source that holds one fixed face (a
|
|
245
|
+
prebaked page, one parsed font buffer) sets `faceKey` to the part it can honour, so a weight it cannot
|
|
246
|
+
rasterise does not multiply identical atlas entries.
|
|
247
|
+
|
|
201
248
|
### Outline
|
|
202
249
|
|
|
203
250
|
`outlineWidth` (in font pixels) rings every glyph in the block in `outlineColor`, which defaults to
|
|
@@ -381,8 +428,8 @@ const renderer = new GpuDebugDraw3D(gpu, {
|
|
|
381
428
|
},
|
|
382
429
|
});
|
|
383
430
|
|
|
384
|
-
|
|
385
|
-
|
|
431
|
+
drawList.text([0, 0, 0], "read me", { source: "sharp" });
|
|
432
|
+
drawList.text([0, -1, 0], "and me", { source: "cheap" });
|
|
386
433
|
```
|
|
387
434
|
|
|
388
435
|
Blocks that name nothing use the first source registered, and `defaultTextStyle` can set one for the
|
|
@@ -391,7 +438,10 @@ actually names it, so registering sources you never draw costs nothing.
|
|
|
391
438
|
|
|
392
439
|
Seven scenes exercise text end to end, and none of them is a port — the reference addon has no text
|
|
393
440
|
API; the layout they run follows the reference layout model, and `docs/text-reference.md` records how
|
|
394
|
-
that was verified.
|
|
441
|
+
that was verified. The geometry examples also draw their own labels, because the reference scenes
|
|
442
|
+
own `Label3D` captions for every column and row; those go through the same pipeline, lying flat on
|
|
443
|
+
the ground with `rotation: [-Math.PI / 2, 0, 0]`. A `GpuDebugDraw3D` built without a `text` option
|
|
444
|
+
skips those blocks instead of failing, so geometry-only callers keep working.
|
|
395
445
|
|
|
396
446
|
`text-compare` is the parity matrix, in the shape of the line/circle examples: rows are glyph sources,
|
|
397
447
|
columns are font sizes, transposed relative to those because text grows sideways. The six
|
|
@@ -457,7 +507,7 @@ gallery to a single glyph source (`auto`, `bitmap`, `bitmap4x`, `sdf`, `sdf4x`,
|
|
|
457
507
|
- `InfiniteGrid3D` shader-driven ground grid ported from the reference grid addon
|
|
458
508
|
(decimal LOD layers, screen-space width, edge/grazing/center/far fades).
|
|
459
509
|
- Orbit/pan/zoom camera controls in the gallery, tuned to the reference
|
|
460
|
-
`entities/free_camera.gd` and layered on a velocity model (`
|
|
510
|
+
`entities/free_camera.gd` and layered on a velocity model (`gallery/demo-camera.ts`): a drag is
|
|
461
511
|
an angular velocity (0.25°/px) or a linear velocity (`distance`/FOV in perspective,
|
|
462
512
|
`orthographicHeight` in orthographic), a wheel notch is a scalar velocity in log₂ space
|
|
463
513
|
(1.08× per notch), and a released drag keeps its velocity and glides — the exact integral of
|
|
@@ -488,7 +538,7 @@ const debug = new DebugDraw3D({
|
|
|
488
538
|
});
|
|
489
539
|
```
|
|
490
540
|
|
|
491
|
-
`
|
|
541
|
+
`gallery/demo-config.ts` contains independently validated camera/view configuration
|
|
492
542
|
and injected storage helpers. Gallery controls support grid, opacity, animation,
|
|
493
543
|
width multiplier, projection, background, camera preset and reset; camera
|
|
494
544
|
interaction is persisted. The sidebar groups them into three collapsible
|
|
@@ -507,11 +557,11 @@ deterministic preset without localStorage: `/?compare=1&scene=circle`. Orthograp
|
|
|
507
557
|
changes the orthographic extent.
|
|
508
558
|
|
|
509
559
|
The gallery UI is built with **Base UI + shadcn (base-lyra preset) + Tailwind v4**
|
|
510
|
-
in `
|
|
560
|
+
in `gallery/components/`, and the imperative WebGPU side lives in `gallery/demo-engine.ts`.
|
|
511
561
|
React only renders chrome; the render loop, camera, and persistence stay outside
|
|
512
562
|
React and are read through `useSyncExternalStore`
|
|
513
|
-
(`
|
|
514
|
-
theme tokens, and `
|
|
563
|
+
(`gallery/demo-hooks.ts`). `gallery/demo.tsx` mounts the shell, `gallery/index.css` holds the base-lyra
|
|
564
|
+
theme tokens, and `gallery/demo.css` keeps the three-column layout, compare mode
|
|
515
565
|
and code highlighting.
|
|
516
566
|
|
|
517
567
|
Runtime comparison viewers (see `docs/visual-comparison.md` and `docs/text-reference.md`):
|
package/dist/curve.d.ts
CHANGED
package/dist/debug-draw-3d.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { FrameDrawList, type FrameCommand, type LayerMask, type LineDefaults, type PointDefaults } from "./frame-draw-list.js";
|
|
2
2
|
export type DebugDrawOptions = {
|
|
3
3
|
defaultVisibleLayers?: LayerMask;
|
|
4
4
|
/** Style applied to every point that does not override it. */
|
|
@@ -9,6 +9,6 @@ export type DebugDrawOptions = {
|
|
|
9
9
|
export declare class DebugDraw3D {
|
|
10
10
|
#private;
|
|
11
11
|
constructor(options?: DebugDrawOptions);
|
|
12
|
-
beginFrame():
|
|
13
|
-
finishFrame(
|
|
12
|
+
beginFrame(): FrameDrawList;
|
|
13
|
+
finishFrame(drawList: FrameDrawList): readonly FrameCommand[];
|
|
14
14
|
}
|
package/dist/debug-draw-3d.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { FrameDrawList, resolveFrameDrawListDefaults, } from "./frame-draw-list.js";
|
|
2
2
|
export class DebugDraw3D {
|
|
3
3
|
#visibleLayers;
|
|
4
4
|
#defaults;
|
|
@@ -14,22 +14,22 @@ export class DebugDraw3D {
|
|
|
14
14
|
...(options.defaultLineStyle === undefined ? {} : { line: { ...options.defaultLineStyle } }),
|
|
15
15
|
};
|
|
16
16
|
// Validate eagerly so invalid defaults fail at construction, not on the first frame.
|
|
17
|
-
|
|
17
|
+
resolveFrameDrawListDefaults(this.#defaults);
|
|
18
18
|
}
|
|
19
19
|
beginFrame() {
|
|
20
20
|
if (this.#active !== undefined) {
|
|
21
21
|
throw new Error("a debug draw frame is already active");
|
|
22
22
|
}
|
|
23
|
-
const
|
|
24
|
-
this.#active =
|
|
25
|
-
return
|
|
23
|
+
const drawList = new FrameDrawList(this.#defaults);
|
|
24
|
+
this.#active = drawList;
|
|
25
|
+
return drawList;
|
|
26
26
|
}
|
|
27
|
-
finishFrame(
|
|
28
|
-
if (this.#active !==
|
|
29
|
-
throw new Error("
|
|
27
|
+
finishFrame(drawList) {
|
|
28
|
+
if (this.#active !== drawList) {
|
|
29
|
+
throw new Error("drawList does not belong to this debug draw instance");
|
|
30
30
|
}
|
|
31
|
-
const commands =
|
|
32
|
-
|
|
31
|
+
const commands = drawList.commands(this.#visibleLayers);
|
|
32
|
+
drawList.seal();
|
|
33
33
|
this.#active = undefined;
|
|
34
34
|
return commands;
|
|
35
35
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* 2D debug-draw commands and their
|
|
2
|
+
* 2D debug-draw commands and their drawList.
|
|
3
3
|
*
|
|
4
|
-
* The 2D layer is the same data flow as the 3D one — `
|
|
4
|
+
* The 2D layer is the same data flow as the 3D one — `FrameDrawList -> FrameCommand[] -> GPU` —
|
|
5
5
|
* reached through `frame.d2` instead of a second entry point, so a frame has one lifecycle, one
|
|
6
6
|
* layer mask and one set of defaults. The commands are separate types rather than 3D commands
|
|
7
7
|
* with a `space` flag because a 2D point is a `Vec2` and 2D strokes carry no surface normal;
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* See `camera-2d.ts` for what `world2d` and `screen` mean.
|
|
11
11
|
*/
|
|
12
12
|
import type { Space2D, Vec2 } from "./camera-2d.js";
|
|
13
|
-
import type { Color, LayerMask, LineJoint, RenderedJoint, ResolvedLineDefaults, ResolvedPointDefaults } from "./frame-
|
|
13
|
+
import type { Color, LayerMask, LineJoint, RenderedJoint, ResolvedLineDefaults, ResolvedPointDefaults } from "./frame-draw-list.js";
|
|
14
14
|
export type Line2DOptions = {
|
|
15
15
|
color?: Color;
|
|
16
16
|
colorEnd?: Color;
|
|
@@ -115,31 +115,31 @@ export type Joint2DCommand = {
|
|
|
115
115
|
readonly space: Space2D;
|
|
116
116
|
readonly layer: LayerMask;
|
|
117
117
|
};
|
|
118
|
-
/** Everything a `
|
|
118
|
+
/** Everything a `FrameDrawList2D` can emit. */
|
|
119
119
|
export type FrameCommand2D = Point2DCommand | Line2DCommand | Joint2DCommand | Shape2DCommand;
|
|
120
|
-
/** The 3D
|
|
121
|
-
export type
|
|
120
|
+
/** The 3D drawList's resolved point/line styles, reused so 2D and 3D defaults cannot drift. */
|
|
121
|
+
export type FrameDrawList2DStyleDefaults = {
|
|
122
122
|
readonly point: ResolvedPointDefaults;
|
|
123
123
|
readonly line: ResolvedLineDefaults;
|
|
124
124
|
};
|
|
125
125
|
/**
|
|
126
|
-
* Records 2D primitives for one frame. Obtained through `
|
|
126
|
+
* Records 2D primitives for one frame. Obtained through `FrameDrawList.d2`; it seals with its
|
|
127
127
|
* parent, so a caller cannot keep drawing into a finished frame.
|
|
128
128
|
*/
|
|
129
|
-
export declare class
|
|
129
|
+
export declare class FrameDrawList2D {
|
|
130
130
|
#private;
|
|
131
|
-
constructor(defaults:
|
|
131
|
+
constructor(defaults: FrameDrawList2DStyleDefaults, assertOpen: () => void);
|
|
132
132
|
point(position: Vec2, options?: Point2DOptions): this;
|
|
133
133
|
line(start: Vec2, end: Vec2, options?: Line2DOptions): this;
|
|
134
134
|
ray(origin: Vec2, direction: Vec2, options?: Line2DOptions): this;
|
|
135
135
|
/**
|
|
136
|
-
* Path of segments, the 2D twin of `
|
|
136
|
+
* Path of segments, the 2D twin of `FrameDrawList.lineStrip`. Dash distance accumulates over
|
|
137
137
|
* the whole path and an open strip joins only its interior vertices, matching the 3D layer.
|
|
138
138
|
*/
|
|
139
139
|
lineStrip(points: readonly Vec2[], options?: Line2DOptions, closed?: boolean): this;
|
|
140
140
|
polyline(points: readonly Vec2[], options?: Line2DOptions, closed?: boolean): this;
|
|
141
141
|
/**
|
|
142
|
-
* Axis-aligned grid of lines, the 2D twin of `
|
|
142
|
+
* Axis-aligned grid of lines, the 2D twin of `FrameDrawList.grid` and reference `grid_2d`: `cells`
|
|
143
143
|
* are the cells per axis, so the drawn line count is one more on each side.
|
|
144
144
|
*/
|
|
145
145
|
grid(center: Vec2, cells: readonly [number, number], spacing: Vec2, options?: Line2DOptions): this;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* 2D debug-draw commands and their
|
|
2
|
+
* 2D debug-draw commands and their drawList.
|
|
3
3
|
*
|
|
4
|
-
* The 2D layer is the same data flow as the 3D one — `
|
|
4
|
+
* The 2D layer is the same data flow as the 3D one — `FrameDrawList -> FrameCommand[] -> GPU` —
|
|
5
5
|
* reached through `frame.d2` instead of a second entry point, so a frame has one lifecycle, one
|
|
6
6
|
* layer mask and one set of defaults. The commands are separate types rather than 3D commands
|
|
7
7
|
* with a `space` flag because a 2D point is a `Vec2` and 2D strokes carry no surface normal;
|
|
@@ -96,10 +96,10 @@ function spaceValue(value) {
|
|
|
96
96
|
return value;
|
|
97
97
|
}
|
|
98
98
|
/**
|
|
99
|
-
* Records 2D primitives for one frame. Obtained through `
|
|
99
|
+
* Records 2D primitives for one frame. Obtained through `FrameDrawList.d2`; it seals with its
|
|
100
100
|
* parent, so a caller cannot keep drawing into a finished frame.
|
|
101
101
|
*/
|
|
102
|
-
export class
|
|
102
|
+
export class FrameDrawList2D {
|
|
103
103
|
#points = [];
|
|
104
104
|
#lines = [];
|
|
105
105
|
#joints = [];
|
|
@@ -158,7 +158,7 @@ export class FrameRecorder2D {
|
|
|
158
158
|
return this.line(origin, [origin[0] + direction[0], origin[1] + direction[1]], options);
|
|
159
159
|
}
|
|
160
160
|
/**
|
|
161
|
-
* Path of segments, the 2D twin of `
|
|
161
|
+
* Path of segments, the 2D twin of `FrameDrawList.lineStrip`. Dash distance accumulates over
|
|
162
162
|
* the whole path and an open strip joins only its interior vertices, matching the 3D layer.
|
|
163
163
|
*/
|
|
164
164
|
lineStrip(points, options = {}, closed = false) {
|
|
@@ -193,7 +193,7 @@ export class FrameRecorder2D {
|
|
|
193
193
|
return this.lineStrip(points, options, closed);
|
|
194
194
|
}
|
|
195
195
|
/**
|
|
196
|
-
* Axis-aligned grid of lines, the 2D twin of `
|
|
196
|
+
* Axis-aligned grid of lines, the 2D twin of `FrameDrawList.grid` and reference `grid_2d`: `cells`
|
|
197
197
|
* are the cells per axis, so the drawn line count is one more on each side.
|
|
198
198
|
*/
|
|
199
199
|
grid(center, cells, spacing, options = {}) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type Curve3D, type CurveTransform } from "./curve.js";
|
|
2
|
-
import {
|
|
3
|
-
import { type TextAnchor, type TextJustify, type TextLineBreak } from "./text-layout.js";
|
|
2
|
+
import { FrameDrawList2D, type FrameCommand2D } from "./frame-draw-list-2d.js";
|
|
3
|
+
import { type TextAnchor, type TextFontStyle, type TextFontWidth, type TextJustify, type TextLineBreak } from "./text-layout.js";
|
|
4
4
|
export type Vec3 = readonly [number, number, number];
|
|
5
5
|
export type Color = readonly [number, number, number, number];
|
|
6
6
|
export type LayerMask = number;
|
|
@@ -114,6 +114,12 @@ export type TextSpanOptions = {
|
|
|
114
114
|
readonly color?: Color;
|
|
115
115
|
readonly fontSize?: number;
|
|
116
116
|
readonly fontFamily?: string;
|
|
117
|
+
/** reference `TextFont::weight`, 1 to 1000. Defaults to the block's, then to 400. */
|
|
118
|
+
readonly weight?: number;
|
|
119
|
+
/** reference `TextFont::style`, minus `Oblique`: Canvas2D drops the slant angle. */
|
|
120
|
+
readonly style?: TextFontStyle;
|
|
121
|
+
/** reference `TextFont::width`, as a `font-stretch` keyword. Inert unless the face has a `wdth` axis. */
|
|
122
|
+
readonly width?: TextFontWidth;
|
|
117
123
|
};
|
|
118
124
|
export type TextOptions = {
|
|
119
125
|
color?: Color;
|
|
@@ -143,6 +149,21 @@ export type TextOptions = {
|
|
|
143
149
|
*/
|
|
144
150
|
pixelSize?: number;
|
|
145
151
|
fontFamily?: string;
|
|
152
|
+
/**
|
|
153
|
+
* reference `TextFont::weight`, 1 to 1000, default `400`. A face that does not carry the weight is
|
|
154
|
+
* emboldened by the browser in one synthesised step, so the advance does not change.
|
|
155
|
+
*/
|
|
156
|
+
weight?: number;
|
|
157
|
+
/**
|
|
158
|
+
* reference `TextFont::style`. `italic` is the only slant Canvas2D honours: its shorthand drops the
|
|
159
|
+
* angle of `Oblique(a)`, so a synthesised slant is all an oblique could mean here.
|
|
160
|
+
*/
|
|
161
|
+
style?: TextFontStyle;
|
|
162
|
+
/**
|
|
163
|
+
* reference `TextFont::width`, spelled as a `font-stretch` keyword. Canvas2D keeps the keyword only when
|
|
164
|
+
* the face has a `wdth` axis; the bundled static subset has none, so every stop draws identically.
|
|
165
|
+
*/
|
|
166
|
+
width?: TextFontWidth;
|
|
146
167
|
/**
|
|
147
168
|
* Absolute line height in pixels, reference `LineHeight::Px`. Exclusive with `lineHeightScale`;
|
|
148
169
|
* omitting both leaves the line height at reference default, `RelativeToFont(1.2)`.
|
|
@@ -164,6 +185,12 @@ export type TextOptions = {
|
|
|
164
185
|
bounds?: readonly [number, number];
|
|
165
186
|
anchor?: TextAnchor;
|
|
166
187
|
space?: TextSpace;
|
|
188
|
+
/**
|
|
189
|
+
* Local rotation of a `world-plane` block, in radians, as reference `Label3D.rotation` Euler angles
|
|
190
|
+
* (applied Y, then X, then Z). The reference `Label3D` labels use `[-PI/2, 0, 0]` to lay the block
|
|
191
|
+
* flat on the XZ plane, readable from above; the two camera-relative spaces ignore it.
|
|
192
|
+
*/
|
|
193
|
+
rotation?: readonly [number, number, number];
|
|
167
194
|
/**
|
|
168
195
|
* Which registered glyph source draws this block: its encoding and raster scale. Blocks that name
|
|
169
196
|
* nothing use the first source, so a frame can mix a sharp pass and a cheap pass.
|
|
@@ -179,6 +206,9 @@ export type ResolvedTextSpan = {
|
|
|
179
206
|
readonly color: Color;
|
|
180
207
|
readonly fontSize: number;
|
|
181
208
|
readonly fontFamily: string;
|
|
209
|
+
readonly weight: number;
|
|
210
|
+
readonly style: TextFontStyle;
|
|
211
|
+
readonly width: TextFontWidth;
|
|
182
212
|
};
|
|
183
213
|
export type TextCommand = {
|
|
184
214
|
readonly kind: "text";
|
|
@@ -190,6 +220,9 @@ export type TextCommand = {
|
|
|
190
220
|
readonly fontSize: number;
|
|
191
221
|
readonly pixelSize: number;
|
|
192
222
|
readonly fontFamily: string;
|
|
223
|
+
readonly weight: number;
|
|
224
|
+
readonly style: TextFontStyle;
|
|
225
|
+
readonly width: TextFontWidth;
|
|
193
226
|
/**
|
|
194
227
|
* Resolved line height in pixels: the value a `LineHeight::Px` block asked for, or
|
|
195
228
|
* `1.2 × fontSize` for the default, which `lineHeightScale` then marks as resolved per line.
|
|
@@ -207,6 +240,7 @@ export type TextCommand = {
|
|
|
207
240
|
readonly bounds?: readonly [number, number];
|
|
208
241
|
readonly anchor: TextAnchor;
|
|
209
242
|
readonly space: TextSpace;
|
|
243
|
+
readonly rotation: readonly [number, number, number];
|
|
210
244
|
readonly source?: string;
|
|
211
245
|
readonly depthTest: boolean;
|
|
212
246
|
readonly layer: LayerMask;
|
|
@@ -248,6 +282,12 @@ export type TextDefaults = {
|
|
|
248
282
|
fontSize?: number;
|
|
249
283
|
pixelSize?: number;
|
|
250
284
|
fontFamily?: string;
|
|
285
|
+
/** Default reference `TextFont::weight`, 1 to 1000. */
|
|
286
|
+
weight?: number;
|
|
287
|
+
/** Default reference `TextFont::style`. */
|
|
288
|
+
style?: TextFontStyle;
|
|
289
|
+
/** Default reference `TextFont::width`, as a `font-stretch` keyword. */
|
|
290
|
+
width?: TextFontWidth;
|
|
251
291
|
/**
|
|
252
292
|
* Absolute line height in pixels, reference `LineHeight::Px`. Omitted means reference default,
|
|
253
293
|
* `RelativeToFont(1.2)`: every line is 1.2 times its own largest span, so a line holding a
|
|
@@ -261,11 +301,12 @@ export type TextDefaults = {
|
|
|
261
301
|
lineBreak?: TextLineBreak;
|
|
262
302
|
anchor?: TextAnchor;
|
|
263
303
|
space?: TextSpace;
|
|
304
|
+
rotation?: readonly [number, number, number];
|
|
264
305
|
source?: string;
|
|
265
306
|
depthTest?: boolean;
|
|
266
307
|
layer?: LayerMask;
|
|
267
308
|
};
|
|
268
|
-
export type
|
|
309
|
+
export type FrameDrawListDefaults = {
|
|
269
310
|
point?: PointDefaults;
|
|
270
311
|
line?: LineDefaults;
|
|
271
312
|
text?: TextDefaults;
|
|
@@ -276,6 +317,8 @@ export declare const DEFAULT_LAYER = 1;
|
|
|
276
317
|
export declare const DEFAULT_TEXT_SIZE = 16;
|
|
277
318
|
/** Default world units per font pixel, i.e. a 0.32 world-unit glyph at the default size. */
|
|
278
319
|
export declare const DEFAULT_TEXT_PIXEL_SIZE = 0.02;
|
|
320
|
+
/** `world-plane` blocks face +Z until a rotation lays them down, like a reference `Label3D`. */
|
|
321
|
+
export declare const DEFAULT_TEXT_ROTATION: readonly [number, number, number];
|
|
279
322
|
/** reference `LineHeight` default is `RelativeToFont(1.2)`. */
|
|
280
323
|
export declare const DEFAULT_LINE_HEIGHT_RATIO = 1.2;
|
|
281
324
|
export type ResolvedPointDefaults = {
|
|
@@ -301,6 +344,9 @@ export type ResolvedTextDefaults = {
|
|
|
301
344
|
fontSize: number | undefined;
|
|
302
345
|
pixelSize: number | undefined;
|
|
303
346
|
fontFamily: string | undefined;
|
|
347
|
+
weight: number | undefined;
|
|
348
|
+
style: TextFontStyle | undefined;
|
|
349
|
+
width: TextFontWidth | undefined;
|
|
304
350
|
lineHeight: number | undefined;
|
|
305
351
|
lineHeightScale: number | undefined;
|
|
306
352
|
letterSpacing: number | undefined;
|
|
@@ -308,25 +354,31 @@ export type ResolvedTextDefaults = {
|
|
|
308
354
|
lineBreak: TextLineBreak | undefined;
|
|
309
355
|
anchor: TextAnchor | undefined;
|
|
310
356
|
space: TextSpace | undefined;
|
|
357
|
+
rotation: readonly [number, number, number] | undefined;
|
|
311
358
|
source: string | undefined;
|
|
312
359
|
depthTest: boolean | undefined;
|
|
313
360
|
layer: LayerMask | undefined;
|
|
314
361
|
};
|
|
315
|
-
export type
|
|
362
|
+
export type ResolvedFrameDrawListDefaults = {
|
|
316
363
|
point: ResolvedPointDefaults;
|
|
317
364
|
line: ResolvedLineDefaults;
|
|
318
365
|
text: ResolvedTextDefaults;
|
|
319
366
|
};
|
|
320
367
|
/** Validates and resolves style defaults so invalid values fail at construction time. */
|
|
321
|
-
export declare function
|
|
322
|
-
|
|
368
|
+
export declare function resolveFrameDrawListDefaults(defaults?: FrameDrawListDefaults): ResolvedFrameDrawListDefaults;
|
|
369
|
+
/**
|
|
370
|
+
* One frame's draw list: records primitives, then hands them to `GpuDebugDraw3D` as
|
|
371
|
+
* `FrameCommand[]`. This is the `ImDrawList` / `GizmoBuffer` layer; GPU command encoding
|
|
372
|
+
* happens later in the renderer.
|
|
373
|
+
*/
|
|
374
|
+
export declare class FrameDrawList {
|
|
323
375
|
#private;
|
|
324
|
-
constructor(defaults?:
|
|
376
|
+
constructor(defaults?: FrameDrawListDefaults);
|
|
325
377
|
/**
|
|
326
378
|
* The 2D layer: `Vec2` primitives on the reference-style `Camera2d` spaces described in
|
|
327
379
|
* `camera-2d.ts`. Drawing here records into the same frame and layer mask as the 3D methods.
|
|
328
380
|
*/
|
|
329
|
-
get d2():
|
|
381
|
+
get d2(): FrameDrawList2D;
|
|
330
382
|
point(position: Vec3, options?: PointOptions): this;
|
|
331
383
|
line(start: Vec3, end: Vec3, options?: LineOptions): this;
|
|
332
384
|
ray(origin: Vec3, direction: Vec3, options?: LineOptions): this;
|