@flighthq/scene2d 0.3.0-next.1689.2810c05 → 0.3.0-next.1702.8973fda

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.
@@ -4,4 +4,5 @@ export * from './displayObject';
4
4
  export * from './displayObjectAnimation';
5
5
  export * from './htmlView';
6
6
  export * from './scene2d';
7
+ export * from './sceneKindUsage';
7
8
  //# sourceMappingURL=contract.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,oBAAoB,CAAC;AACnC,cAAc,iBAAiB,CAAC;AAChC,cAAc,0BAA0B,CAAC;AACzC,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC"}
1
+ {"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,oBAAoB,CAAC;AACnC,cAAc,iBAAiB,CAAC;AAChC,cAAc,0BAA0B,CAAC;AACzC,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,kBAAkB,CAAC"}
package/dist/contract.js CHANGED
@@ -4,4 +4,5 @@ export * from './displayObject';
4
4
  export * from './displayObjectAnimation';
5
5
  export * from './htmlView';
6
6
  export * from './scene2d';
7
+ export * from './sceneKindUsage';
7
8
  //# sourceMappingURL=contract.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"contract.js","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,oBAAoB,CAAC;AACnC,cAAc,iBAAiB,CAAC;AAChC,cAAc,0BAA0B,CAAC;AACzC,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC"}
1
+ {"version":3,"file":"contract.js","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,oBAAoB,CAAC;AACnC,cAAc,iBAAiB,CAAC;AAChC,cAAc,0BAA0B,CAAC;AACzC,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,kBAAkB,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { Scene2D, Scene2DKindUsage } from '@flighthq/types/contract';
2
+ export declare function createScene2DKindUsage(): Scene2DKindUsage;
3
+ export declare function getScene2DKindUsage(out: Scene2DKindUsage, scene: Readonly<Scene2D>): void;
4
+ //# sourceMappingURL=sceneKindUsage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sceneKindUsage.d.ts","sourceRoot":"","sources":["../src/sceneKindUsage.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAU,OAAO,EAAE,gBAAgB,EAAqB,MAAM,0BAA0B,CAAC;AAKrG,wBAAgB,sBAAsB,IAAI,gBAAgB,CAEzD;AASD,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,gBAAgB,EAAE,KAAK,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,IAAI,CAmCzF"}
@@ -0,0 +1,61 @@
1
+ import { forEachNodeDescendant } from '@flighthq/node/contract';
2
+ import { BlendMode } from '@flighthq/types/contract';
3
+ // Allocates an empty usage record. Separate from the walk so a caller can reuse one across scenes or
4
+ // frames without reallocating four arrays.
5
+ export function createScene2DKindUsage() {
6
+ return { blendModes: [], materialKinds: [], nodeKinds: [], shapeCommandKeys: [] };
7
+ }
8
+ // Clears `out`, then fills it with every kind this scene uses. One walk, no registry, no backend, no
9
+ // prose — it reads the fields the scene graph already carries, so it cannot itself be the thing you
10
+ // forgot to wire.
11
+ //
12
+ // This answers only WHAT IS IN THE SCENE. Whether anything is registered to draw it is a question for
13
+ // the package that owns the registry: each backend checks its own renderer, material, and shape-command
14
+ // registries. Keeping those answers out of here is what lets a scene stay ignorant of rendering.
15
+ export function getScene2DKindUsage(out, scene) {
16
+ out.blendModes.length = 0;
17
+ out.materialKinds.length = 0;
18
+ out.nodeKinds.length = 0;
19
+ out.shapeCommandKeys.length = 0;
20
+ const visit = (node) => {
21
+ addScene2DUsedKind(out.nodeKinds, node.kind);
22
+ // An unset node carries null and inherits its parent's mode; Normal is what every backend
23
+ // composites with no registration at all. Reporting either would send a caller looking for a
24
+ // realization it never needs, so only an explicit non-Normal mode counts as used.
25
+ if (node.blendMode !== null && node.blendMode !== BlendMode.Normal) {
26
+ addScene2DUsedKind(out.blendModes, node.blendMode);
27
+ }
28
+ if (node.material !== null)
29
+ addScene2DUsedKind(out.materialKinds, node.material.kind);
30
+ // Structural, like isMesh: any node family whose data carries a command stream is walked, including
31
+ // a custom kind, without a per-kind table that would drift as families are added. Shape, MorphShape
32
+ // and Scale9Shape all record into the same flat [key, argCount, ...args] buffer.
33
+ const commands = node.data?.commands;
34
+ if (commands === undefined)
35
+ return;
36
+ let i = 0;
37
+ while (i < commands.length) {
38
+ addScene2DUsedKind(out.shapeCommandKeys, commands[i]);
39
+ i += commands[i + 1] + 2;
40
+ }
41
+ };
42
+ visit(scene.root);
43
+ // forEachNodeDescendant yields Node<Node2DTraits>; the cast restores the trait fields the walk
44
+ // generic drops, which the visitor reads structurally.
45
+ forEachNodeDescendant(scene.root, (node) => visit(node));
46
+ out.blendModes.sort();
47
+ out.materialKinds.sort();
48
+ out.nodeKinds.sort();
49
+ out.shapeCommandKeys.sort();
50
+ }
51
+ // Linear scan rather than a Set: these lists are the handful of distinct kinds a document uses, where
52
+ // scanning beats allocating four Sets per walk, and it keeps the result a plain array a C port can hold
53
+ // without a hash container.
54
+ function addScene2DUsedKind(kinds, kind) {
55
+ for (let i = 0; i < kinds.length; i++) {
56
+ if (kinds[i] === kind)
57
+ return;
58
+ }
59
+ kinds.push(kind);
60
+ }
61
+ //# sourceMappingURL=sceneKindUsage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sceneKindUsage.js","sourceRoot":"","sources":["../src/sceneKindUsage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAErD,qGAAqG;AACrG,2CAA2C;AAC3C,MAAM,UAAU,sBAAsB;IACpC,OAAO,EAAE,UAAU,EAAE,EAAE,EAAE,aAAa,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,gBAAgB,EAAE,EAAE,EAAE,CAAC;AACpF,CAAC;AAED,qGAAqG;AACrG,oGAAoG;AACpG,kBAAkB;AAClB,EAAE;AACF,sGAAsG;AACtG,wGAAwG;AACxG,iGAAiG;AACjG,MAAM,UAAU,mBAAmB,CAAC,GAAqB,EAAE,KAAwB;IACjF,GAAG,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC;IAC1B,GAAG,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,CAAC;IAC7B,GAAG,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC;IACzB,GAAG,CAAC,gBAAgB,CAAC,MAAM,GAAG,CAAC,CAAC;IAEhC,MAAM,KAAK,GAAG,CAAC,IAAsB,EAAQ,EAAE;QAC7C,kBAAkB,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;QAC7C,0FAA0F;QAC1F,6FAA6F;QAC7F,kFAAkF;QAClF,IAAI,IAAI,CAAC,SAAS,KAAK,IAAI,IAAI,IAAI,CAAC,SAAS,KAAK,SAAS,CAAC,MAAM,EAAE,CAAC;YACnE,kBAAkB,CAAC,GAAG,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;QACrD,CAAC;QACD,IAAI,IAAI,CAAC,QAAQ,KAAK,IAAI;YAAE,kBAAkB,CAAC,GAAG,CAAC,aAAa,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QACtF,oGAAoG;QACpG,oGAAoG;QACpG,iFAAiF;QACjF,MAAM,QAAQ,GAAI,IAAI,CAAC,IAA6E,EAAE,QAAQ,CAAC;QAC/G,IAAI,QAAQ,KAAK,SAAS;YAAE,OAAO;QACnC,IAAI,CAAC,GAAG,CAAC,CAAC;QACV,OAAO,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC;YAC3B,kBAAkB,CAAC,GAAG,CAAC,gBAAgB,EAAE,QAAQ,CAAC,CAAC,CAAW,CAAC,CAAC;YAChE,CAAC,IAAK,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAY,GAAG,CAAC,CAAC;QACvC,CAAC;IACH,CAAC,CAAC;IACF,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAClB,+FAA+F;IAC/F,uDAAuD;IACvD,qBAAqB,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,IAAwB,CAAC,CAAC,CAAC;IAE7E,GAAG,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC;IACtB,GAAG,CAAC,aAAa,CAAC,IAAI,EAAE,CAAC;IACzB,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;IACrB,GAAG,CAAC,gBAAgB,CAAC,IAAI,EAAE,CAAC;AAC9B,CAAC;AAED,sGAAsG;AACtG,wGAAwG;AACxG,4BAA4B;AAC5B,SAAS,kBAAkB,CAAC,KAAe,EAAE,IAAY;IACvD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI;YAAE,OAAO;IAChC,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACnB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flighthq/scene2d",
3
- "version": "0.3.0-next.1689.2810c05",
3
+ "version": "0.3.0-next.1702.8973fda",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/flighthq/flight.git",
@@ -36,15 +36,15 @@
36
36
  "clean:dist": "tsx ../../scripts/clean-package-dist.ts"
37
37
  },
38
38
  "dependencies": {
39
- "@flighthq/adjustments": "0.3.0-next.1689.2810c05",
40
- "@flighthq/animation": "0.3.0-next.1689.2810c05",
41
- "@flighthq/entity": "0.3.0-next.1689.2810c05",
42
- "@flighthq/geometry": "0.3.0-next.1689.2810c05",
43
- "@flighthq/materials": "0.3.0-next.1689.2810c05",
44
- "@flighthq/node": "0.3.0-next.1689.2810c05",
45
- "@flighthq/signals": "0.3.0-next.1689.2810c05",
46
- "@flighthq/texture": "0.3.0-next.1689.2810c05",
47
- "@flighthq/types": "0.3.0-next.1689.2810c05"
39
+ "@flighthq/adjustments": "0.3.0-next.1702.8973fda",
40
+ "@flighthq/animation": "0.3.0-next.1702.8973fda",
41
+ "@flighthq/entity": "0.3.0-next.1702.8973fda",
42
+ "@flighthq/geometry": "0.3.0-next.1702.8973fda",
43
+ "@flighthq/materials": "0.3.0-next.1702.8973fda",
44
+ "@flighthq/node": "0.3.0-next.1702.8973fda",
45
+ "@flighthq/signals": "0.3.0-next.1702.8973fda",
46
+ "@flighthq/texture": "0.3.0-next.1702.8973fda",
47
+ "@flighthq/types": "0.3.0-next.1702.8973fda"
48
48
  },
49
49
  "devDependencies": {
50
50
  "typescript": "^5.3.0"
@@ -0,0 +1,119 @@
1
+ import { createUnlitMaterial } from '@flighthq/materials/contract';
2
+ import { addNodeChild } from '@flighthq/node/contract';
3
+ import type { Node2D, Node2DData, ShapeCommandToken } from '@flighthq/types/contract';
4
+ import { BlendMode, DisplayObjectKind, HtmlViewKind } from '@flighthq/types/contract';
5
+ import { describe, expect, it } from 'vitest';
6
+
7
+ import { createDisplayObject } from './displayObject';
8
+ import { createHtmlView } from './htmlView';
9
+ import { createScene2D } from './scene2d';
10
+ import { createScene2DKindUsage, getScene2DKindUsage } from './sceneKindUsage';
11
+
12
+ // The walk detects a command stream structurally — any node whose `data` carries `commands` — rather
13
+ // than by node kind, so the tests record streams directly. That also keeps @flighthq/shape out of this
14
+ // package's dependencies, which it could not take anyway since shape depends on scene2d.
15
+ function shapeLikeNode(...commands: ShapeCommandToken[]): Node2D {
16
+ const node = createDisplayObject();
17
+ node.data = { commands } as unknown as Node2DData;
18
+ return node;
19
+ }
20
+
21
+ function usageOf(build: (root: Node2D) => void) {
22
+ const scene = createScene2D();
23
+ build(scene.root);
24
+ const usage = createScene2DKindUsage();
25
+ getScene2DKindUsage(usage, scene);
26
+ return usage;
27
+ }
28
+
29
+ describe('createScene2DKindUsage', () => {
30
+ it('starts empty so a caller can reuse one record across scenes', () => {
31
+ expect(createScene2DKindUsage()).toEqual({
32
+ blendModes: [],
33
+ materialKinds: [],
34
+ nodeKinds: [],
35
+ shapeCommandKeys: [],
36
+ });
37
+ });
38
+ });
39
+
40
+ describe('getScene2DKindUsage', () => {
41
+ it('collects node kinds across the tree, deduped and sorted', () => {
42
+ const usage = usageOf((root) => {
43
+ addNodeChild(root, createHtmlView());
44
+ addNodeChild(root, createHtmlView());
45
+ addNodeChild(root, createDisplayObject());
46
+ });
47
+ expect(usage.nodeKinds).toEqual([DisplayObjectKind, HtmlViewKind]);
48
+ });
49
+
50
+ it('walks a nested child, not just the root generation', () => {
51
+ const usage = usageOf((root) => {
52
+ const group = createDisplayObject();
53
+ addNodeChild(group, createHtmlView());
54
+ addNodeChild(root, group);
55
+ });
56
+ expect(usage.nodeKinds).toContain(HtmlViewKind);
57
+ });
58
+
59
+ it('reads the command keys out of a recorded stream', () => {
60
+ const usage = usageOf((root) => {
61
+ addNodeChild(root, shapeLikeNode('beginFill', 2, 0xff0000, 1, 'drawRectangle', 4, 0, 0, 10, 10, 'endFill', 0));
62
+ });
63
+ expect(usage.shapeCommandKeys).toEqual(['beginFill', 'drawRectangle', 'endFill']);
64
+ });
65
+
66
+ it('dedupes a command key repeated within one stream and across nodes', () => {
67
+ const usage = usageOf((root) => {
68
+ addNodeChild(root, shapeLikeNode('drawRectangle', 4, 0, 0, 10, 10, 'drawRectangle', 4, 20, 0, 10, 10));
69
+ addNodeChild(root, shapeLikeNode('drawRectangle', 4, 0, 0, 5, 5));
70
+ });
71
+ expect(usage.shapeCommandKeys).toEqual(['drawRectangle']);
72
+ });
73
+
74
+ it('advances by the declared argument count, so arguments are never read as command keys', () => {
75
+ // The stream is [key, argCount, ...args]. Misreading the stride would report an argument value —
76
+ // here the string 'round' — as if it were a command key. That is the failure this pins.
77
+ const usage = usageOf((root) => {
78
+ addNodeChild(root, shapeLikeNode('lineStyle', 3, 2, 0x000000, 'round', 'moveTo', 2, 0, 0));
79
+ });
80
+ expect(usage.shapeCommandKeys).toEqual(['lineStyle', 'moveTo']);
81
+ });
82
+
83
+ it('ignores a node whose data carries no command stream', () => {
84
+ expect(usageOf((root) => addNodeChild(root, createHtmlView())).shapeCommandKeys).toEqual([]);
85
+ });
86
+
87
+ it('reports a non-default blend mode and omits Normal, which needs no realization', () => {
88
+ const usage = usageOf((root) => {
89
+ const plain = createDisplayObject();
90
+ const lit = createDisplayObject();
91
+ lit.blendMode = BlendMode.Multiply;
92
+ addNodeChild(root, plain);
93
+ addNodeChild(root, lit);
94
+ });
95
+ expect(usage.blendModes).toEqual([BlendMode.Multiply]);
96
+ });
97
+
98
+ it('reports the kind of a material a node carries', () => {
99
+ const usage = usageOf((root) => {
100
+ const node = createDisplayObject();
101
+ node.material = createUnlitMaterial();
102
+ addNodeChild(root, node);
103
+ });
104
+ expect(usage.materialKinds).toEqual(['UnlitMaterial']);
105
+ });
106
+
107
+ it('leaves material kinds empty when no node carries one', () => {
108
+ expect(usageOf((root) => addNodeChild(root, createDisplayObject())).materialKinds).toEqual([]);
109
+ });
110
+
111
+ it('clears every list, so a reused record does not accumulate', () => {
112
+ const scene = createScene2D();
113
+ addNodeChild(scene.root, createHtmlView());
114
+ const usage = createScene2DKindUsage();
115
+ getScene2DKindUsage(usage, scene);
116
+ getScene2DKindUsage(usage, scene);
117
+ expect(usage.nodeKinds).toEqual([DisplayObjectKind, HtmlViewKind]);
118
+ });
119
+ });