@neta-art/cohub 8.24.0 → 9.0.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/dist/board/animation.d.ts +57 -181
- package/dist/board/animation.js +374 -220
- package/dist/board/core/arrow-geometry.d.ts +33 -18
- package/dist/board/core/arrow-geometry.js +283 -52
- package/dist/board/core/draw-geometry.d.ts +22 -36
- package/dist/board/core/draw-geometry.js +148 -85
- package/dist/board/core/export-assets.d.ts +3 -6
- package/dist/board/core/export-assets.js +0 -2
- package/dist/board/core/export-plan.d.ts +12 -63
- package/dist/board/core/export-plan.js +55 -94
- package/dist/board/core/palette.d.ts +4 -21
- package/dist/board/core/palette.js +8 -13
- package/dist/board/core/scene.d.ts +33 -0
- package/dist/board/core/scene.js +302 -0
- package/dist/board/core/shape-definition.d.ts +12 -24
- package/dist/board/core/shape-definition.js +0 -19
- package/dist/board/core/shape-types.d.ts +6 -120
- package/dist/board/core/shape-types.js +5 -5
- package/dist/board/core/text-metrics.d.ts +8 -11
- package/dist/board/core/text-metrics.js +5 -34
- package/dist/board/core/tool-styles.d.ts +6 -16
- package/dist/board/core/tool-styles.js +17 -25
- package/dist/board/export/index.d.ts +10 -17
- package/dist/board/export/index.js +22 -21
- package/dist/board/export/scene.d.ts +10 -16
- package/dist/board/export/scene.js +17 -30
- package/dist/board/geometry.d.ts +27 -56
- package/dist/board/geometry.js +7 -67
- package/dist/board/headless/index.d.ts +6 -27
- package/dist/board/headless/index.js +4 -19
- package/dist/board/headless/sketch.d.ts +19 -0
- package/dist/board/headless/sketch.js +146 -0
- package/dist/board/image-key.d.ts +2 -12
- package/dist/board/image-key.js +3 -13
- package/dist/board/index.d.ts +18 -20
- package/dist/board/index.js +17 -17
- package/dist/board/media-playback.d.ts +3 -6
- package/dist/board/media-playback.js +6 -6
- package/dist/board/presets.d.ts +150 -0
- package/dist/board/presets.js +226 -0
- package/dist/board/render/board-background.d.ts +5 -3
- package/dist/board/render/board-background.js +8 -7
- package/dist/board/render/clip.d.ts +14 -0
- package/dist/board/render/clip.js +47 -0
- package/dist/board/render/index.d.ts +8 -5
- package/dist/board/render/index.js +7 -4
- package/dist/board/render/media-interaction.d.ts +4 -5
- package/dist/board/render/media-interaction.js +1 -2
- package/dist/board/render/palette.d.ts +6 -2
- package/dist/board/render/palette.js +7 -1
- package/dist/board/render/renderers/arrow-card-renderer.js +69 -71
- package/dist/board/render/renderers/audio-card-renderer.js +3 -3
- package/dist/board/render/renderers/base-card-renderer.d.ts +6 -17
- package/dist/board/render/renderers/base-card-renderer.js +1 -24
- package/dist/board/render/renderers/board-renderer-registry.d.ts +20 -50
- package/dist/board/render/renderers/board-renderer-registry.js +6 -7
- package/dist/board/render/renderers/draw-card-renderer.js +89 -56
- package/dist/board/render/renderers/effect-card-renderer.d.ts +6 -0
- package/dist/board/render/renderers/effect-card-renderer.js +197 -0
- package/dist/board/render/renderers/far-plate.d.ts +4 -21
- package/dist/board/render/renderers/far-plate.js +15 -34
- package/dist/board/render/renderers/file-card-renderer.d.ts +4 -25
- package/dist/board/render/renderers/file-card-renderer.js +8 -58
- package/dist/board/render/renderers/frame-card-renderer.js +28 -28
- package/dist/board/render/renderers/image-card-renderer.d.ts +4 -3
- package/dist/board/render/renderers/image-card-renderer.js +0 -9
- package/dist/board/render/renderers/shape-card-renderer.d.ts +9 -0
- package/dist/board/render/renderers/shape-card-renderer.js +240 -0
- package/dist/board/render/renderers/sketch-card-renderer.d.ts +5 -0
- package/dist/board/render/renderers/sketch-card-renderer.js +98 -0
- package/dist/board/render/renderers/task-card-renderer.d.ts +0 -1
- package/dist/board/render/renderers/task-card-renderer.js +14 -16
- package/dist/board/render/renderers/text-card-renderer.d.ts +1 -3
- package/dist/board/render/renderers/text-card-renderer.js +56 -46
- package/dist/board/render/renderers/unknown-card-renderer.js +4 -10
- package/dist/board/render/renderers/video-card-renderer.d.ts +4 -2
- package/dist/board/render/renderers/video-card-renderer.js +2 -1
- package/dist/board/render/sketch-host.d.ts +14 -0
- package/dist/board/render/sketch-host.js +216 -0
- package/dist/board/render/stroke.d.ts +12 -0
- package/dist/board/render/stroke.js +90 -0
- package/dist/board/render/text-measurement.d.ts +6 -26
- package/dist/board/render/text-measurement.js +11 -30
- package/dist/board/replay.d.ts +6 -31
- package/dist/board/replay.js +45 -203
- package/dist/board/runtime/board-player.d.ts +24 -0
- package/dist/board/runtime/board-player.js +161 -0
- package/dist/board/task.d.ts +2 -10
- package/dist/board/task.js +1 -10
- package/dist/chunks/environment.d.ts +2291 -3864
- package/dist/chunks/host-bridge.d.ts +253 -0
- package/dist/chunks/http.d.ts +233 -58
- package/dist/chunks/http.js +1046 -1028
- package/dist/chunks/transport.js +113 -59
- package/dist/chunks/websocket.d.ts +1 -1
- package/dist/chunks/websocket.js +1 -1
- package/dist/host-bridge.d.ts +54 -0
- package/dist/host-bridge.js +322 -0
- package/dist/http.d.ts +3 -3
- package/dist/index.d.ts +74 -191
- package/dist/index.js +2032 -1186
- package/dist/protocol/dist/board-constants.d.ts +1 -29
- package/dist/protocol/dist/board-constants.js +2 -115
- package/dist/protocol/dist/board-layout.d.ts +59 -0
- package/dist/protocol/dist/board-layout.js +482 -0
- package/dist/protocol/dist/board-model.d.ts +2052 -0
- package/dist/protocol/dist/board-model.js +592 -0
- package/dist/protocol/dist/board-patch.d.ts +86 -0
- package/dist/protocol/dist/board-patch.js +734 -0
- package/dist/protocol/dist/board-schema.d.ts +33 -0
- package/dist/protocol/dist/board-schema.js +59 -0
- package/dist/protocol/dist/board-upgrade.d.ts +1 -0
- package/dist/protocol/dist/board-url.d.ts +1 -3
- package/dist/protocol/dist/board-url.js +2 -2
- package/dist/protocol/dist/board.d.ts +37 -232
- package/dist/protocol/dist/board.js +62 -160
- package/dist/protocol/dist/display/actions.d.ts +1 -0
- package/dist/protocol/dist/display/index.d.ts +2 -0
- package/dist/protocol/dist/index.d.ts +11 -12
- package/dist/protocol/dist/model/metrics.d.ts +2 -0
- package/dist/protocol/dist/model/session.d.ts +2 -1
- package/dist/protocol/dist/realtime/types.d.ts +2 -2
- package/dist/protocol/dist/runtime/index.d.ts +1 -0
- package/dist/space-picker.d.ts +1 -0
- package/dist/space-picker.js +1 -0
- package/dist/types.d.ts +2 -5
- package/package.json +9 -1
- package/dist/board/core/connections.d.ts +0 -108
- package/dist/board/core/connections.js +0 -455
- package/dist/board/mutation.d.ts +0 -6
- package/dist/board/mutation.js +0 -14
- package/dist/board/render/connection-layer.d.ts +0 -53
- package/dist/board/render/connection-layer.js +0 -375
- package/dist/board/render/renderers/geo-card-renderer.d.ts +0 -5
- package/dist/board/render/renderers/geo-card-renderer.js +0 -127
- package/dist/board/semantic-document.d.ts +0 -37
- package/dist/board/semantic-document.js +0 -288
- package/dist/board/semantic-mutation.d.ts +0 -10
- package/dist/board/semantic-mutation.js +0 -209
- package/dist/protocol/dist/board-animation.js +0 -34
- package/dist/protocol/dist/board-authoring.d.ts +0 -1326
- package/dist/protocol/dist/board-authoring.js +0 -302
- package/dist/protocol/dist/board-capability-registry.d.ts +0 -2
- package/dist/protocol/dist/board-capability-registry.js +0 -123
- package/dist/protocol/dist/board-codec.d.ts +0 -3
- package/dist/protocol/dist/board-codec.js +0 -383
- package/dist/protocol/dist/board-composition.d.ts +0 -383
- package/dist/protocol/dist/board-composition.js +0 -310
- package/dist/protocol/dist/board-connection.d.ts +0 -317
- package/dist/protocol/dist/board-connection.js +0 -219
- package/dist/protocol/dist/board-document.d.ts +0 -1566
- package/dist/protocol/dist/board-document.js +0 -456
- package/dist/protocol/dist/board-effect.d.ts +0 -96
- package/dist/protocol/dist/board-effect.js +0 -54
- package/dist/protocol/dist/board-geometry.d.ts +0 -15
- package/dist/protocol/dist/board-geometry.js +0 -137
- package/dist/protocol/dist/board-node.d.ts +0 -8
- package/dist/protocol/dist/board-node.js +0 -252
- /package/dist/protocol/dist/{board-animation.d.ts → host-bridge.d.ts} +0 -0
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { BoardSettings, BoardSketchItem } from "../../../protocol/dist/board-model.js";
|
|
2
|
+
import "../../../protocol/dist/index.js";
|
|
3
|
+
import { BoardScene, BoardSceneItem, SceneItem } from "../../core/scene.js";
|
|
2
4
|
import { BoardShapeColors } from "../../core/palette.js";
|
|
3
5
|
import { Container, Graphics, Texture } from "pixi.js";
|
|
4
6
|
//#region src/board/render/renderers/board-renderer-registry.d.ts
|
|
@@ -14,74 +16,42 @@ type BoardRenderPalette = {
|
|
|
14
16
|
epic: number;
|
|
15
17
|
legendary: number;
|
|
16
18
|
};
|
|
19
|
+
type BoardSketchHost = {
|
|
20
|
+
frame: (item: SceneItem<BoardSketchItem>, time: number, resolution: number) => Texture | null;
|
|
21
|
+
error: (id: string) => string | null;
|
|
22
|
+
release: (id: string) => void;
|
|
23
|
+
};
|
|
17
24
|
type BoardRenderContext = {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
* O(1) item lookup by id. Renderers that need to resolve references (arrow
|
|
21
|
-
* bindings) must use this rather than scanning `document.items`, which would
|
|
22
|
-
* be O(items) per card per frame.
|
|
23
|
-
*/
|
|
24
|
-
getItem: (id: string) => BoardItem | null;
|
|
25
|
+
settings: BoardSettings;
|
|
26
|
+
scene: BoardScene;
|
|
25
27
|
selectedIds: Set<string>;
|
|
26
28
|
hoveredId: string | null;
|
|
27
|
-
/** Nodes currently receiving a live resize preview. */
|
|
28
29
|
resizingIds: Set<string>;
|
|
29
30
|
palette: BoardRenderPalette;
|
|
30
|
-
/**
|
|
31
|
-
* Live shape colors resolved from CSS tokens (theme + space theme.css).
|
|
32
|
-
* Prefer this over hard-coded light/dark tables at render time.
|
|
33
|
-
*/
|
|
34
31
|
colors: BoardShapeColors;
|
|
35
|
-
/** Resolved color mode, for fallback mapping when colors are unavailable. */
|
|
36
32
|
colorScheme: "dark" | "light";
|
|
37
|
-
/** Rendering backend selected by the host; Canvas uses compatibility paths. */
|
|
38
33
|
rendererType: "gpu" | "canvas";
|
|
39
|
-
/** Current camera zoom — used for text re-rasterisation buckets. */
|
|
40
34
|
zoom: number;
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
35
|
+
time: number;
|
|
36
|
+
sketches?: BoardSketchHost;
|
|
37
|
+
assetKey: (item: BoardSceneItem) => string | null;
|
|
44
38
|
getTexture: (key: string) => Texture | null;
|
|
45
|
-
/** Whether the preview for a key failed to load (for a failure placeholder). */
|
|
46
39
|
hasError: (key: string) => boolean;
|
|
47
|
-
/**
|
|
48
|
-
* Whether a referenced workspace file could not be read, and why. Transient,
|
|
49
|
-
* client-local state — never part of the document — so one client's outage is
|
|
50
|
-
* not shown to everyone.
|
|
51
|
-
*/
|
|
52
40
|
fileState: (path: string) => "ok" | "missing" | "unavailable";
|
|
53
|
-
/** Reference-counted texture acquisition and release. */
|
|
54
41
|
acquireTexture: (key: string) => void;
|
|
55
42
|
releaseTexture: (key: string) => void;
|
|
56
43
|
};
|
|
57
44
|
type BoardCardRenderer = {
|
|
58
45
|
id: string;
|
|
59
|
-
canRender: (item:
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
update: (container: Container, item:
|
|
63
|
-
|
|
64
|
-
* Draw the item as flat batched geometry into a shared `Graphics`, for the
|
|
65
|
-
* far LOD used when too many cards are on screen to afford a container each.
|
|
66
|
-
*
|
|
67
|
-
* Implementing this opts the shape into the far layer: past the scene's
|
|
68
|
-
* visible-card threshold it is drawn here instead of being materialised, so
|
|
69
|
-
* cost stops scaling with node count. Omit it for shapes whose whole point is
|
|
70
|
-
* their vector detail (strokes, arrows, frames) — those keep real containers.
|
|
71
|
-
*
|
|
72
|
-
* Must draw in world space and must not allocate per call beyond the path
|
|
73
|
-
* itself; this runs once per far-capable item on every far-layer rebuild.
|
|
74
|
-
*/
|
|
75
|
-
renderFar?: (graphics: Graphics, item: BoardItem, context: BoardRenderContext) => void;
|
|
46
|
+
canRender: (item: BoardSceneItem, context: BoardRenderContext) => boolean;
|
|
47
|
+
animated?: boolean;
|
|
48
|
+
create: (item: BoardSceneItem, context: BoardRenderContext) => Container;
|
|
49
|
+
update: (container: Container, item: BoardSceneItem, context: BoardRenderContext) => void;
|
|
50
|
+
renderFar?: (graphics: Graphics, item: BoardSceneItem, context: BoardRenderContext) => void;
|
|
76
51
|
destroy?: (container: Container, context: BoardRenderContext) => void;
|
|
77
52
|
};
|
|
78
|
-
/**
|
|
79
|
-
* Every registered renderer, for invariants that must hold across all of them —
|
|
80
|
-
* notably that each can draw itself at far LOD, which is what keeps the far
|
|
81
|
-
* layer's z-order correct (see board-scene).
|
|
82
|
-
*/
|
|
83
53
|
declare function boardCardRenderersForTest(): readonly BoardCardRenderer[];
|
|
84
|
-
declare function getBoardCardRenderer(item:
|
|
54
|
+
declare function getBoardCardRenderer(item: BoardSceneItem, context: BoardRenderContext): BoardCardRenderer;
|
|
85
55
|
declare function registerBoardCardRenderer(renderer: BoardCardRenderer): void;
|
|
86
56
|
//#endregion
|
|
87
|
-
export { BoardCardRenderer, BoardRenderContext, BoardRenderPalette, boardCardRenderersForTest, getBoardCardRenderer, registerBoardCardRenderer };
|
|
57
|
+
export { BoardCardRenderer, BoardRenderContext, BoardRenderPalette, BoardSketchHost, boardCardRenderersForTest, getBoardCardRenderer, registerBoardCardRenderer };
|
|
@@ -4,8 +4,10 @@ import { ensureBoardTextMeasurement } from "../text-measurement.js";
|
|
|
4
4
|
import { arrowCardRenderer } from "./arrow-card-renderer.js";
|
|
5
5
|
import { audioCardRenderer } from "./audio-card-renderer.js";
|
|
6
6
|
import { drawCardRenderer } from "./draw-card-renderer.js";
|
|
7
|
+
import { effectCardRenderer } from "./effect-card-renderer.js";
|
|
7
8
|
import { frameCardRenderer } from "./frame-card-renderer.js";
|
|
8
|
-
import {
|
|
9
|
+
import { shapeCardRenderer } from "./shape-card-renderer.js";
|
|
10
|
+
import { sketchCardRenderer } from "./sketch-card-renderer.js";
|
|
9
11
|
import { imageCardRenderer } from "./image-card-renderer.js";
|
|
10
12
|
import { textCardRenderer } from "./text-card-renderer.js";
|
|
11
13
|
import { unknownCardRenderer } from "./unknown-card-renderer.js";
|
|
@@ -18,17 +20,14 @@ const boardCardRenderers = [
|
|
|
18
20
|
audioCardRenderer,
|
|
19
21
|
fileCardRenderer,
|
|
20
22
|
taskCardRenderer,
|
|
21
|
-
|
|
23
|
+
shapeCardRenderer,
|
|
22
24
|
drawCardRenderer,
|
|
23
25
|
arrowCardRenderer,
|
|
24
26
|
frameCardRenderer,
|
|
27
|
+
effectCardRenderer,
|
|
28
|
+
sketchCardRenderer,
|
|
25
29
|
unknownCardRenderer
|
|
26
30
|
];
|
|
27
|
-
/**
|
|
28
|
-
* Every registered renderer, for invariants that must hold across all of them —
|
|
29
|
-
* notably that each can draw itself at far LOD, which is what keeps the far
|
|
30
|
-
* layer's z-order correct (see board-scene).
|
|
31
|
-
*/
|
|
32
31
|
function boardCardRenderersForTest() {
|
|
33
32
|
return boardCardRenderers;
|
|
34
33
|
}
|
|
@@ -1,79 +1,110 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { applyMatrix } from "../../../protocol/dist/board-layout.js";
|
|
2
|
+
import { computeDrawBounds, createStrokeRibbonBuilder, trimDrawPoints } from "../../core/draw-geometry.js";
|
|
3
3
|
import { positionShell } from "./base-card-renderer.js";
|
|
4
|
-
import { drawFarStroke } from "./far-plate.js";
|
|
4
|
+
import { drawFarStroke, farStrokeSamples } from "./far-plate.js";
|
|
5
|
+
import { itemColor } from "../palette.js";
|
|
5
6
|
import { Container, Graphics, Mesh, MeshGeometry, Texture } from "pixi.js";
|
|
6
7
|
//#region src/board/render/renderers/draw-card-renderer.ts
|
|
7
8
|
const partsByContainer = /* @__PURE__ */ new WeakMap();
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const graphics = new Graphics();
|
|
9
|
+
function fillRibbon(graphics, ribbon, color) {
|
|
10
|
+
const { positions, indices } = ribbon;
|
|
11
11
|
for (let offset = 0; offset < indices.length; offset += 3) {
|
|
12
12
|
const a = (indices[offset] ?? 0) * 2;
|
|
13
13
|
const b = (indices[offset + 1] ?? 0) * 2;
|
|
14
14
|
const c = (indices[offset + 2] ?? 0) * 2;
|
|
15
15
|
graphics.moveTo(positions[a] ?? 0, positions[a + 1] ?? 0).lineTo(positions[b] ?? 0, positions[b + 1] ?? 0).lineTo(positions[c] ?? 0, positions[c + 1] ?? 0).closePath();
|
|
16
16
|
}
|
|
17
|
-
return graphics.fill({
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
return graphics.fill({ color });
|
|
18
|
+
}
|
|
19
|
+
function replaceStroke(parts, next) {
|
|
20
|
+
if (parts.stroke) {
|
|
21
|
+
parts.root.removeChild(parts.stroke);
|
|
22
|
+
parts.stroke.destroy();
|
|
23
|
+
}
|
|
24
|
+
parts.stroke = next;
|
|
25
|
+
parts.root.addChild(next);
|
|
26
|
+
return next;
|
|
27
|
+
}
|
|
28
|
+
function tessellate(parts, points, size, paint) {
|
|
29
|
+
parts.ribbon ??= createStrokeRibbonBuilder(size);
|
|
30
|
+
const ribbon = parts.ribbon.build(points);
|
|
31
|
+
if (paint !== null) {
|
|
32
|
+
fillRibbon(parts.stroke instanceof Graphics ? parts.stroke.clear() : replaceStroke(parts, new Graphics()), ribbon, paint);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
if (ribbon.indices.length === 0) {
|
|
36
|
+
if (parts.stroke) parts.stroke.visible = false;
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
if (parts.uvs.length < ribbon.positions.length) parts.uvs = new Float32Array(ribbon.positions.length * 2);
|
|
40
|
+
const { positions, indices } = ribbon;
|
|
41
|
+
const uvs = parts.uvs.subarray(0, positions.length);
|
|
42
|
+
if (parts.stroke instanceof Mesh) {
|
|
43
|
+
const geometry = parts.stroke.geometry;
|
|
44
|
+
geometry.positions = positions;
|
|
45
|
+
geometry.uvs = uvs;
|
|
46
|
+
geometry.indices = indices;
|
|
47
|
+
parts.stroke.visible = true;
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
replaceStroke(parts, new Mesh({
|
|
51
|
+
geometry: new MeshGeometry({
|
|
52
|
+
positions,
|
|
53
|
+
uvs,
|
|
54
|
+
indices
|
|
55
|
+
}),
|
|
56
|
+
texture: Texture.WHITE
|
|
57
|
+
}));
|
|
21
58
|
}
|
|
22
59
|
function sync(container, item, context) {
|
|
23
60
|
const parts = partsByContainer.get(container);
|
|
24
61
|
if (!parts) return;
|
|
25
62
|
positionShell(parts.root, item);
|
|
26
|
-
const
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
parts
|
|
40
|
-
parts.points = item.points;
|
|
41
|
-
parts.baseWidth = computeDrawBounds(item.points, item.size).width;
|
|
42
|
-
const ribbon = buildStrokeRibbonGeometry(item.points, item.size);
|
|
43
|
-
const alpha = selected || hovered ? 1 : .92;
|
|
44
|
-
const nextStroke = context.rendererType !== "canvas" ? new Mesh({
|
|
45
|
-
geometry: new MeshGeometry({
|
|
46
|
-
positions: ribbon.positions,
|
|
47
|
-
indices: ribbon.indices
|
|
48
|
-
}),
|
|
49
|
-
texture: Texture.WHITE
|
|
50
|
-
}) : createCanvasStroke(ribbon.positions, ribbon.indices, color.stroke, alpha);
|
|
51
|
-
if (nextStroke instanceof Mesh) {
|
|
52
|
-
nextStroke.tint = color.stroke;
|
|
53
|
-
nextStroke.alpha = alpha;
|
|
54
|
-
}
|
|
55
|
-
const previous = parts.stroke;
|
|
56
|
-
parts.stroke = nextStroke;
|
|
57
|
-
parts.root.removeChild(previous);
|
|
58
|
-
previous.destroy();
|
|
59
|
-
parts.root.addChild(nextStroke);
|
|
63
|
+
const { points } = item.props;
|
|
64
|
+
const size = item.style.strokeWidth ?? 4;
|
|
65
|
+
const trim = item.style.trim;
|
|
66
|
+
const color = itemColor(context, item.style.stroke, "brand");
|
|
67
|
+
const paint = context.rendererType === "canvas" ? color : null;
|
|
68
|
+
if (points !== parts.points || points.length !== parts.count || size !== parts.size || trim !== parts.trim || paint !== parts.paint) {
|
|
69
|
+
if (size !== parts.size) parts.ribbon = null;
|
|
70
|
+
parts.points = points;
|
|
71
|
+
parts.count = points.length;
|
|
72
|
+
parts.size = size;
|
|
73
|
+
parts.trim = trim;
|
|
74
|
+
parts.paint = paint;
|
|
75
|
+
parts.box = computeDrawBounds(points, size);
|
|
76
|
+
tessellate(parts, trimDrawPoints(points, trim), size, paint);
|
|
60
77
|
}
|
|
61
|
-
const
|
|
62
|
-
|
|
78
|
+
const stroke = parts.stroke;
|
|
79
|
+
if (!stroke) return;
|
|
80
|
+
if (stroke instanceof Mesh) stroke.tint = color;
|
|
81
|
+
const { box } = parts;
|
|
82
|
+
const sx = item.frame.width / box.width;
|
|
83
|
+
const sy = item.frame.height / box.height;
|
|
84
|
+
stroke.scale.set(sx, sy);
|
|
85
|
+
stroke.position.set(-box.x * sx, -box.y * sy);
|
|
63
86
|
}
|
|
64
87
|
const drawCardRenderer = {
|
|
65
88
|
id: "draw-card",
|
|
66
89
|
canRender: (item) => item.type === "draw",
|
|
67
90
|
create: (item, context) => {
|
|
68
91
|
const root = new Container();
|
|
69
|
-
const stroke = new Graphics();
|
|
70
|
-
root.addChild(stroke);
|
|
71
92
|
partsByContainer.set(root, {
|
|
72
93
|
root,
|
|
73
|
-
stroke,
|
|
74
|
-
|
|
94
|
+
stroke: null,
|
|
95
|
+
ribbon: null,
|
|
96
|
+
uvs: /* @__PURE__ */ new Float32Array(0),
|
|
75
97
|
points: null,
|
|
76
|
-
|
|
98
|
+
count: 0,
|
|
99
|
+
size: 0,
|
|
100
|
+
trim: void 0,
|
|
101
|
+
paint: null,
|
|
102
|
+
box: {
|
|
103
|
+
x: 0,
|
|
104
|
+
y: 0,
|
|
105
|
+
width: 1,
|
|
106
|
+
height: 1
|
|
107
|
+
}
|
|
77
108
|
});
|
|
78
109
|
if (item.type === "draw") sync(root, item, context);
|
|
79
110
|
return root;
|
|
@@ -83,11 +114,13 @@ const drawCardRenderer = {
|
|
|
83
114
|
},
|
|
84
115
|
renderFar: (graphics, item, context) => {
|
|
85
116
|
if (item.type !== "draw") return;
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
117
|
+
const draw = item;
|
|
118
|
+
const matrix = context.scene.layout.matrix(draw.id);
|
|
119
|
+
const samples = farStrokeSamples(trimDrawPoints(draw.props.points, draw.style.trim));
|
|
120
|
+
drawFarStroke(graphics, samples.map((point) => applyMatrix(matrix, point)), {
|
|
121
|
+
color: itemColor(context, draw.style.stroke, "brand"),
|
|
122
|
+
width: draw.style.strokeWidth ?? 4,
|
|
123
|
+
alpha: 1
|
|
91
124
|
});
|
|
92
125
|
},
|
|
93
126
|
destroy: (container) => {
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { BoardCardRenderer } from "./board-renderer-registry.js";
|
|
2
|
+
//#region src/board/render/renderers/effect-card-renderer.d.ts
|
|
3
|
+
declare const BOARD_EFFECT_MAX_PARTICLES = 4000;
|
|
4
|
+
declare const effectCardRenderer: BoardCardRenderer;
|
|
5
|
+
//#endregion
|
|
6
|
+
export { BOARD_EFFECT_MAX_PARTICLES, effectCardRenderer };
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { positionShell } from "./base-card-renderer.js";
|
|
2
|
+
import { drawFarPlate } from "./far-plate.js";
|
|
3
|
+
import { itemColor } from "../palette.js";
|
|
4
|
+
import { Container, Graphics } from "pixi.js";
|
|
5
|
+
//#region src/board/render/renderers/effect-card-renderer.ts
|
|
6
|
+
const BOARD_EFFECT_MAX_PARTICLES = 4e3;
|
|
7
|
+
const TRAIL_SAMPLES = 64;
|
|
8
|
+
const partsByContainer = /* @__PURE__ */ new WeakMap();
|
|
9
|
+
function hashString(value) {
|
|
10
|
+
let hash = 2166136261;
|
|
11
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
12
|
+
hash ^= value.charCodeAt(index);
|
|
13
|
+
hash = Math.imul(hash, 16777619);
|
|
14
|
+
}
|
|
15
|
+
return hash >>> 0;
|
|
16
|
+
}
|
|
17
|
+
function random(seed, index, channel) {
|
|
18
|
+
let x = (seed ^ Math.imul(index + 1, 2654435761) ^ Math.imul(channel + 1, 2246822519)) >>> 0;
|
|
19
|
+
x ^= x >>> 16;
|
|
20
|
+
x = Math.imul(x, 2146121005);
|
|
21
|
+
x ^= x >>> 15;
|
|
22
|
+
x = Math.imul(x, 2221713035);
|
|
23
|
+
x ^= x >>> 16;
|
|
24
|
+
return (x >>> 0) / 4294967296;
|
|
25
|
+
}
|
|
26
|
+
function velocity(props, seed, index) {
|
|
27
|
+
const angle = (props.direction + (random(seed, index, 1) - .5) * props.spread) * Math.PI / 180;
|
|
28
|
+
const speed = props.speed * (.7 + random(seed, index, 2) * .6);
|
|
29
|
+
return {
|
|
30
|
+
vx: Math.cos(angle) * speed,
|
|
31
|
+
vy: Math.sin(angle) * speed
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function drawParticles(graphics, props, width, height, time, seed, color) {
|
|
35
|
+
const rate = props.rate * props.intensity;
|
|
36
|
+
if (rate <= 0) return;
|
|
37
|
+
const life = props.life;
|
|
38
|
+
const interval = 1e3 / rate;
|
|
39
|
+
const newest = Math.floor(time / interval);
|
|
40
|
+
const count = Math.min(BOARD_EFFECT_MAX_PARTICLES, Math.ceil(life / interval));
|
|
41
|
+
for (let index = Math.max(0, newest - count + 1); index <= newest; index += 1) {
|
|
42
|
+
const age = (time - index * interval) / 1e3;
|
|
43
|
+
if (age < 0 || age * 1e3 > life) continue;
|
|
44
|
+
const progress = age * 1e3 / life;
|
|
45
|
+
const { vx, vy } = velocity(props, seed, index);
|
|
46
|
+
const x = random(seed, index, 3) * width + vx * age;
|
|
47
|
+
const y = random(seed, index, 4) * height + vy * age + .5 * props.gravity * age * age;
|
|
48
|
+
const size = props.particleSize * (.6 + random(seed, index, 5) * .8) * (1 - progress * .5);
|
|
49
|
+
graphics.circle(x, y, size / 2).fill({
|
|
50
|
+
color,
|
|
51
|
+
alpha: (1 - progress) * Math.min(1, props.intensity)
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function drawImpact(graphics, props, width, height, time, seed, color) {
|
|
56
|
+
const life = props.life;
|
|
57
|
+
const cycle = Math.floor(time / life);
|
|
58
|
+
const age = (time - cycle * life) / 1e3;
|
|
59
|
+
const progress = age * 1e3 / life;
|
|
60
|
+
const count = Math.min(BOARD_EFFECT_MAX_PARTICLES, Math.round(props.rate * props.intensity));
|
|
61
|
+
const cx = width / 2;
|
|
62
|
+
const cy = height / 2;
|
|
63
|
+
const alpha = (1 - progress) ** 1.5 * Math.min(1, props.intensity);
|
|
64
|
+
const radius = Math.min(width, height) * .5 * Math.sqrt(progress);
|
|
65
|
+
graphics.circle(cx, cy, radius).stroke({
|
|
66
|
+
color,
|
|
67
|
+
width: Math.max(1, props.particleSize * (1 - progress)),
|
|
68
|
+
alpha: alpha * .8
|
|
69
|
+
});
|
|
70
|
+
const burst = hashString(`${seed}:${cycle}`);
|
|
71
|
+
for (let index = 0; index < count; index += 1) {
|
|
72
|
+
const { vx, vy } = velocity({
|
|
73
|
+
...props,
|
|
74
|
+
spread: props.spread
|
|
75
|
+
}, burst, index);
|
|
76
|
+
const x = cx + vx * age;
|
|
77
|
+
const y = cy + vy * age + .5 * props.gravity * age * age;
|
|
78
|
+
graphics.circle(x, y, props.particleSize / 2 * (1 - progress * .6)).fill({
|
|
79
|
+
color,
|
|
80
|
+
alpha
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
function drawFlash(graphics, props, width, height, time, color) {
|
|
85
|
+
const alpha = (1 - time % props.life / props.life) ** 2 * Math.min(1, props.intensity);
|
|
86
|
+
if (alpha <= .001) return;
|
|
87
|
+
graphics.rect(0, 0, width, height).fill({
|
|
88
|
+
color,
|
|
89
|
+
alpha
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
function drawGlow(graphics, props, width, height, time, color) {
|
|
93
|
+
const pulse = .75 + .25 * Math.sin(time / props.life * Math.PI * 2);
|
|
94
|
+
const rings = 12;
|
|
95
|
+
const cx = width / 2;
|
|
96
|
+
const cy = height / 2;
|
|
97
|
+
for (let ring = rings; ring >= 1; ring -= 1) {
|
|
98
|
+
const fraction = ring / rings;
|
|
99
|
+
graphics.ellipse(cx, cy, width / 2 * fraction, height / 2 * fraction).fill({
|
|
100
|
+
color,
|
|
101
|
+
alpha: Math.min(1, props.intensity) * pulse * (1 - fraction) * 2 / rings
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
function drawTrail(graphics, parts, item, context, color) {
|
|
106
|
+
const target = item.props.follow ? context.scene.get(item.props.follow) : void 0;
|
|
107
|
+
const time = context.time;
|
|
108
|
+
if (!target) {
|
|
109
|
+
parts.trail.length = 0;
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
const head = {
|
|
113
|
+
x: target.frame.x + target.frame.width / 2 - item.frame.x,
|
|
114
|
+
y: target.frame.y + target.frame.height / 2 - item.frame.y,
|
|
115
|
+
t: time
|
|
116
|
+
};
|
|
117
|
+
const last = parts.trail.at(-1);
|
|
118
|
+
if (last && (time < last.t || time - last.t > item.props.life)) parts.trail.length = 0;
|
|
119
|
+
if (!last || Math.hypot(head.x - last.x, head.y - last.y) > .5) parts.trail.push(head);
|
|
120
|
+
while (parts.trail.length > TRAIL_SAMPLES || parts.trail[0] && time - parts.trail[0].t > item.props.life) parts.trail.shift();
|
|
121
|
+
for (let index = 1; index < parts.trail.length; index += 1) {
|
|
122
|
+
const a = parts.trail[index - 1];
|
|
123
|
+
const b = parts.trail[index];
|
|
124
|
+
const fade = 1 - (time - b.t) / item.props.life;
|
|
125
|
+
graphics.moveTo(a.x, a.y).lineTo(b.x, b.y).stroke({
|
|
126
|
+
color,
|
|
127
|
+
width: item.props.particleSize * fade,
|
|
128
|
+
alpha: fade * Math.min(1, item.props.intensity),
|
|
129
|
+
cap: "round"
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
function sync(container, item, context) {
|
|
134
|
+
const parts = partsByContainer.get(container);
|
|
135
|
+
if (!parts) return;
|
|
136
|
+
positionShell(parts.root, item);
|
|
137
|
+
const { width, height } = item.frame;
|
|
138
|
+
const { props } = item;
|
|
139
|
+
const color = itemColor(context, item.style.fill ?? item.style.stroke, "brand", "fill");
|
|
140
|
+
const seed = hashString(props.seed ?? item.id);
|
|
141
|
+
const time = Math.max(0, context.time);
|
|
142
|
+
const graphics = parts.graphics;
|
|
143
|
+
graphics.clear();
|
|
144
|
+
switch (props.kind) {
|
|
145
|
+
case "particles":
|
|
146
|
+
drawParticles(graphics, props, width, height, time, seed, color);
|
|
147
|
+
break;
|
|
148
|
+
case "impact":
|
|
149
|
+
drawImpact(graphics, props, width, height, time, seed, color);
|
|
150
|
+
break;
|
|
151
|
+
case "flash":
|
|
152
|
+
drawFlash(graphics, props, width, height, time, color);
|
|
153
|
+
break;
|
|
154
|
+
case "glow":
|
|
155
|
+
drawGlow(graphics, props, width, height, time, color);
|
|
156
|
+
break;
|
|
157
|
+
case "trail": drawTrail(graphics, parts, item, context, color);
|
|
158
|
+
}
|
|
159
|
+
if (context.selectedIds.has(item.id) || context.hoveredId === item.id) graphics.rect(0, 0, width, height).stroke({
|
|
160
|
+
color: context.palette.muted,
|
|
161
|
+
width: 1,
|
|
162
|
+
alpha: .6
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
const effectCardRenderer = {
|
|
166
|
+
id: "effect-card",
|
|
167
|
+
canRender: (item) => item.type === "effect",
|
|
168
|
+
animated: true,
|
|
169
|
+
create: (item, context) => {
|
|
170
|
+
const root = new Container();
|
|
171
|
+
const graphics = new Graphics();
|
|
172
|
+
root.addChild(graphics);
|
|
173
|
+
partsByContainer.set(root, {
|
|
174
|
+
root,
|
|
175
|
+
graphics,
|
|
176
|
+
trail: []
|
|
177
|
+
});
|
|
178
|
+
if (item.type === "effect") sync(root, item, context);
|
|
179
|
+
return root;
|
|
180
|
+
},
|
|
181
|
+
update: (container, item, context) => {
|
|
182
|
+
if (item.type === "effect") sync(container, item, context);
|
|
183
|
+
},
|
|
184
|
+
renderFar: (graphics, item, context) => {
|
|
185
|
+
if (item.type !== "effect") return;
|
|
186
|
+
drawFarPlate(graphics, item.frame, {
|
|
187
|
+
fill: itemColor(context, item.style.fill, "brand", "fill"),
|
|
188
|
+
fillAlpha: .08
|
|
189
|
+
});
|
|
190
|
+
},
|
|
191
|
+
destroy: (container) => {
|
|
192
|
+
partsByContainer.get(container)?.root.destroy({ children: true });
|
|
193
|
+
partsByContainer.delete(container);
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
//#endregion
|
|
197
|
+
export { BOARD_EFFECT_MAX_PARTICLES, effectCardRenderer };
|
|
@@ -1,32 +1,15 @@
|
|
|
1
|
-
import { BoardFrame } from "../../../protocol/dist/board-
|
|
1
|
+
import { BoardFrame } from "../../../protocol/dist/board-layout.js";
|
|
2
|
+
import "../../../protocol/dist/index.js";
|
|
2
3
|
import { Graphics } from "pixi.js";
|
|
3
4
|
//#region src/board/render/renderers/far-plate.d.ts
|
|
4
5
|
type FarPlateStyle = {
|
|
5
|
-
/** Plate body fill. */
|
|
6
6
|
fill: number;
|
|
7
7
|
fillAlpha?: number;
|
|
8
|
-
/** Optional accent band along the top edge, identifying the content kind. */
|
|
9
8
|
accent?: number;
|
|
10
9
|
accentAlpha?: number;
|
|
11
10
|
};
|
|
12
|
-
/**
|
|
13
|
-
* Draw one card as a far-LOD plate: a filled body plus an optional top accent
|
|
14
|
-
* band. Selection and hover are intentionally not drawn here — the interaction
|
|
15
|
-
* overlay already renders selection in world space, above this layer.
|
|
16
|
-
*/
|
|
17
11
|
declare function drawFarPlate(graphics: Graphics, frame: BoardFrame, style: FarPlateStyle): void;
|
|
18
|
-
|
|
19
|
-
* Draw one card as a far-LOD polyline.
|
|
20
|
-
*
|
|
21
|
-
* The counterpart to `drawFarPlate` for shapes whose meaning is a path rather than
|
|
22
|
-
* a region — strokes and arrows. It stays in the shared `Graphics`, so these keep
|
|
23
|
-
* their place in the batch and therefore in document order: an unbatched shape
|
|
24
|
-
* would be a live container, drawn above every plate no matter where the document
|
|
25
|
-
* puts it.
|
|
26
|
-
*
|
|
27
|
-
* Points are decimated to a bound, because a freehand stroke can hold thousands of
|
|
28
|
-
* samples that are sub-pixel apart at this zoom.
|
|
29
|
-
*/
|
|
12
|
+
declare function farStrokeSamples<T>(points: readonly T[]): readonly T[];
|
|
30
13
|
declare function drawFarStroke(graphics: Graphics, points: ReadonlyArray<{
|
|
31
14
|
x: number;
|
|
32
15
|
y: number;
|
|
@@ -36,4 +19,4 @@ declare function drawFarStroke(graphics: Graphics, points: ReadonlyArray<{
|
|
|
36
19
|
alpha?: number;
|
|
37
20
|
}): void;
|
|
38
21
|
//#endregion
|
|
39
|
-
export { FarPlateStyle, drawFarPlate, drawFarStroke };
|
|
22
|
+
export { FarPlateStyle, drawFarPlate, drawFarStroke, farStrokeSamples };
|
|
@@ -1,16 +1,9 @@
|
|
|
1
1
|
import { degToRad } from "../../geometry.js";
|
|
2
2
|
//#region src/board/render/renderers/far-plate.ts
|
|
3
|
-
/** Upper bound on points kept when a stroke is drawn at far LOD. */
|
|
4
3
|
const FAR_STROKE_MAX_POINTS = 24;
|
|
5
|
-
/** Accent band height as a fraction of the plate, clamped to sane world units. */
|
|
6
4
|
const ACCENT_RATIO = .14;
|
|
7
5
|
const ACCENT_MIN = 2;
|
|
8
6
|
const ACCENT_MAX = 10;
|
|
9
|
-
/**
|
|
10
|
-
* Trace a frame's rectangle, honouring rotation. Unrotated frames take the
|
|
11
|
-
* fast `rect` path; rotated ones emit four explicit corners (still one path,
|
|
12
|
-
* no arcs) so the far layer never needs a per-card transform.
|
|
13
|
-
*/
|
|
14
7
|
function traceFrame(graphics, frame, inset = 0, heightOverride) {
|
|
15
8
|
const x = frame.x + inset;
|
|
16
9
|
const y = frame.y + inset;
|
|
@@ -39,11 +32,6 @@ function traceFrame(graphics, frame, inset = 0, heightOverride) {
|
|
|
39
32
|
const d = corner(x, y + height);
|
|
40
33
|
graphics.moveTo(a.x, a.y).lineTo(b.x, b.y).lineTo(c.x, c.y).lineTo(d.x, d.y).closePath();
|
|
41
34
|
}
|
|
42
|
-
/**
|
|
43
|
-
* Draw one card as a far-LOD plate: a filled body plus an optional top accent
|
|
44
|
-
* band. Selection and hover are intentionally not drawn here — the interaction
|
|
45
|
-
* overlay already renders selection in world space, above this layer.
|
|
46
|
-
*/
|
|
47
35
|
function drawFarPlate(graphics, frame, style) {
|
|
48
36
|
if (frame.width <= 0 || frame.height <= 0) return;
|
|
49
37
|
traceFrame(graphics, frame);
|
|
@@ -58,30 +46,23 @@ function drawFarPlate(graphics, frame, style) {
|
|
|
58
46
|
alpha: style.accentAlpha ?? .9
|
|
59
47
|
});
|
|
60
48
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
*
|
|
70
|
-
* Points are decimated to a bound, because a freehand stroke can hold thousands of
|
|
71
|
-
* samples that are sub-pixel apart at this zoom.
|
|
72
|
-
*/
|
|
49
|
+
function farStrokeSamples(points) {
|
|
50
|
+
if (points.length <= FAR_STROKE_MAX_POINTS) return points;
|
|
51
|
+
const step = Math.ceil(points.length / FAR_STROKE_MAX_POINTS);
|
|
52
|
+
const out = [];
|
|
53
|
+
for (let i = 0; i < points.length - 1; i += step) out.push(points[i]);
|
|
54
|
+
out.push(points[points.length - 1]);
|
|
55
|
+
return out;
|
|
56
|
+
}
|
|
73
57
|
function drawFarStroke(graphics, points, style) {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
const first =
|
|
77
|
-
if (!first) return;
|
|
58
|
+
const samples = farStrokeSamples(points);
|
|
59
|
+
if (samples.length < 2) return;
|
|
60
|
+
const first = samples[0];
|
|
78
61
|
graphics.moveTo(first.x, first.y);
|
|
79
|
-
for (let i =
|
|
80
|
-
const point =
|
|
81
|
-
|
|
62
|
+
for (let i = 1; i < samples.length; i += 1) {
|
|
63
|
+
const point = samples[i];
|
|
64
|
+
graphics.lineTo(point.x, point.y);
|
|
82
65
|
}
|
|
83
|
-
const last = points[points.length - 1];
|
|
84
|
-
if (last) graphics.lineTo(last.x, last.y);
|
|
85
66
|
graphics.stroke({
|
|
86
67
|
color: style.color,
|
|
87
68
|
width: style.width,
|
|
@@ -89,4 +70,4 @@ function drawFarStroke(graphics, points, style) {
|
|
|
89
70
|
});
|
|
90
71
|
}
|
|
91
72
|
//#endregion
|
|
92
|
-
export { drawFarPlate, drawFarStroke };
|
|
73
|
+
export { drawFarPlate, drawFarStroke, farStrokeSamples };
|