@flighthq/scene2d 0.3.0-next.1692.f1a7a9a → 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.
- package/dist/contract.d.ts +1 -0
- package/dist/contract.d.ts.map +1 -1
- package/dist/contract.js +1 -0
- package/dist/contract.js.map +1 -1
- package/dist/sceneKindUsage.d.ts +4 -0
- package/dist/sceneKindUsage.d.ts.map +1 -0
- package/dist/sceneKindUsage.js +61 -0
- package/dist/sceneKindUsage.js.map +1 -0
- package/package.json +10 -10
- package/src/sceneKindUsage.test.ts +119 -0
package/dist/contract.d.ts
CHANGED
package/dist/contract.d.ts.map
CHANGED
|
@@ -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
package/dist/contract.js.map
CHANGED
|
@@ -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.
|
|
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.
|
|
40
|
-
"@flighthq/animation": "0.3.0-next.
|
|
41
|
-
"@flighthq/entity": "0.3.0-next.
|
|
42
|
-
"@flighthq/geometry": "0.3.0-next.
|
|
43
|
-
"@flighthq/materials": "0.3.0-next.
|
|
44
|
-
"@flighthq/node": "0.3.0-next.
|
|
45
|
-
"@flighthq/signals": "0.3.0-next.
|
|
46
|
-
"@flighthq/texture": "0.3.0-next.
|
|
47
|
-
"@flighthq/types": "0.3.0-next.
|
|
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
|
+
});
|