@flighthq/node 0.3.0-next.906.07cea63 → 0.3.1-next.1041.35b950c
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/boundsRectangle.d.ts +11 -0
- package/dist/boundsRectangle.d.ts.map +1 -1
- package/dist/boundsRectangle.js +110 -12
- package/dist/boundsRectangle.js.map +1 -1
- package/dist/contract.d.ts +2 -0
- package/dist/contract.d.ts.map +1 -1
- package/dist/contract.js +2 -0
- package/dist/contract.js.map +1 -1
- package/dist/enableNodeGuards.d.ts +4 -0
- package/dist/enableNodeGuards.d.ts.map +1 -0
- package/dist/enableNodeGuards.js +29 -0
- package/dist/enableNodeGuards.js.map +1 -0
- package/dist/hasAppearance.d.ts +2 -1
- package/dist/hasAppearance.d.ts.map +1 -1
- package/dist/hasAppearance.js +10 -0
- package/dist/hasAppearance.js.map +1 -1
- package/dist/hasBoundsRectangle.d.ts +1 -1
- package/dist/hasBoundsRectangle.d.ts.map +1 -1
- package/dist/hasBoundsRectangle.js +5 -0
- package/dist/hasBoundsRectangle.js.map +1 -1
- package/dist/hasTransform3d.d.ts.map +1 -1
- package/dist/hasTransform3d.js +11 -4
- package/dist/hasTransform3d.js.map +1 -1
- package/dist/hierarchy.d.ts +15 -7
- package/dist/hierarchy.d.ts.map +1 -1
- package/dist/hierarchy.js +65 -28
- package/dist/hierarchy.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/node.d.ts.map +1 -1
- package/dist/node.js +2 -0
- package/dist/node.js.map +1 -1
- package/dist/nodeColorAdjustment.d.ts.map +1 -1
- package/dist/nodeColorAdjustment.js +1 -7
- package/dist/nodeColorAdjustment.js.map +1 -1
- package/dist/nodeOrderList.d.ts +88 -0
- package/dist/nodeOrderList.d.ts.map +1 -0
- package/dist/nodeOrderList.js +257 -0
- package/dist/nodeOrderList.js.map +1 -0
- package/dist/nodeTransform2d.d.ts.map +1 -1
- package/dist/nodeTransform2d.js +1 -1
- package/dist/nodeTransform2d.js.map +1 -1
- package/dist/revision.d.ts +5 -2
- package/dist/revision.d.ts.map +1 -1
- package/dist/revision.js +17 -5
- package/dist/revision.js.map +1 -1
- package/dist/stageFit.d.ts.map +1 -1
- package/dist/stageFit.js +8 -4
- package/dist/stageFit.js.map +1 -1
- package/dist/traversal.d.ts +1 -1
- package/dist/traversal.d.ts.map +1 -1
- package/dist/traversal.js +1 -1
- package/dist/traversal.js.map +1 -1
- package/package.json +11 -7
- package/src/boundsRectangle.test.ts +378 -7
- package/src/enableNodeGuards.test.ts +78 -0
- package/src/hasAppearance.test.ts +33 -3
- package/src/hasBoundsRectangle.test.ts +8 -0
- package/src/hasTransform2d.test.ts +6 -0
- package/src/hasTransform3d.test.ts +40 -8
- package/src/hierarchy.test.ts +298 -4
- package/src/node.test.ts +11 -1
- package/src/nodeColorAdjustment.test.ts +11 -0
- package/src/nodeOrderList.test.ts +683 -0
- package/src/nodeTransform2d.test.ts +45 -0
- package/src/revision.test.ts +51 -0
- package/src/stageFit.test.ts +56 -1
- package/src/traversal.test.ts +32 -3
|
@@ -166,6 +166,28 @@ describe('ensureNodeLocalMatrix', () => {
|
|
|
166
166
|
ensureNodeLocalMatrix(node);
|
|
167
167
|
expect(equalsMatrix(runtime.localMatrix, cache)).toBe(true);
|
|
168
168
|
});
|
|
169
|
+
|
|
170
|
+
it('normalizes rotations above 180 degrees', () => {
|
|
171
|
+
const runtime = getEntityRuntime(node) as HasTransform2DRuntime;
|
|
172
|
+
node.rotation = 270;
|
|
173
|
+
invalidateNodeLocalTransform(node);
|
|
174
|
+
|
|
175
|
+
ensureNodeLocalMatrix(node);
|
|
176
|
+
|
|
177
|
+
expect(runtime.rotationAngle).toBe(-90);
|
|
178
|
+
expect(runtime.localMatrix?.b).toBeCloseTo(-1);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it('normalizes rotations below -180 degrees', () => {
|
|
182
|
+
const runtime = getEntityRuntime(node) as HasTransform2DRuntime;
|
|
183
|
+
node.rotation = -270;
|
|
184
|
+
invalidateNodeLocalTransform(node);
|
|
185
|
+
|
|
186
|
+
ensureNodeLocalMatrix(node);
|
|
187
|
+
|
|
188
|
+
expect(runtime.rotationAngle).toBe(90);
|
|
189
|
+
expect(runtime.localMatrix?.b).toBeCloseTo(1);
|
|
190
|
+
});
|
|
169
191
|
});
|
|
170
192
|
|
|
171
193
|
describe('ensureNodeWorldMatrix', () => {
|
|
@@ -282,6 +304,29 @@ describe('setNodeLocalMatrix', () => {
|
|
|
282
304
|
expect(m.ty).toBeCloseTo(20);
|
|
283
305
|
});
|
|
284
306
|
|
|
307
|
+
// Pins the losslessness this function's contract promises for a reflected (negative determinant)
|
|
308
|
+
// matrix. Positive-determinant cases round-tripped even while reflected ones silently lost the
|
|
309
|
+
// reflection, so a fixture that is not reflected cannot speak to this.
|
|
310
|
+
it('round-trips a reflected matrix, keeping the mirror', () => {
|
|
311
|
+
setNodeLocalMatrix(node, createMatrix(1, 0, 0, -1, 0, 0));
|
|
312
|
+
const m = getNodeLocalMatrix(node);
|
|
313
|
+
expect(m.a).toBeCloseTo(1);
|
|
314
|
+
expect(m.b).toBeCloseTo(0);
|
|
315
|
+
expect(m.c).toBeCloseTo(0);
|
|
316
|
+
expect(m.d).toBeCloseTo(-1);
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
it('round-trips a rotated reflection through all four matrix cells', () => {
|
|
320
|
+
setNodeLocalMatrix(node, createMatrix(0.5, 0.8, 0.8, -0.5, 3, 4));
|
|
321
|
+
const m = getNodeLocalMatrix(node);
|
|
322
|
+
expect(m.a).toBeCloseTo(0.5);
|
|
323
|
+
expect(m.b).toBeCloseTo(0.8);
|
|
324
|
+
expect(m.c).toBeCloseTo(0.8);
|
|
325
|
+
expect(m.d).toBeCloseTo(-0.5);
|
|
326
|
+
expect(m.tx).toBeCloseTo(3);
|
|
327
|
+
expect(m.ty).toBeCloseTo(4);
|
|
328
|
+
});
|
|
329
|
+
|
|
285
330
|
it('resets pivot and absorbs its offset into translation', () => {
|
|
286
331
|
node.pivotX = 5;
|
|
287
332
|
node.pivotY = 5;
|
package/src/revision.test.ts
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import { createNode, getNodeRuntime } from '@flighthq/node/contract';
|
|
2
2
|
import type { Node, NodeRuntime } from '@flighthq/types/contract';
|
|
3
3
|
|
|
4
|
+
import { addNodeChild } from './hierarchy';
|
|
4
5
|
import {
|
|
5
6
|
computeNodeWorldTransformRevision,
|
|
6
7
|
getNodeAppearanceRevision,
|
|
8
|
+
getNodeChildrenRevision,
|
|
7
9
|
getNodeLocalBoundsRevision,
|
|
8
10
|
getNodeLocalContentRevision,
|
|
9
11
|
getNodeLocalTransformRevision,
|
|
12
|
+
getNodeParentReferenceRevision,
|
|
10
13
|
getNodeWorldTransformRevision,
|
|
11
14
|
invalidateContent,
|
|
12
15
|
invalidateNode,
|
|
@@ -39,6 +42,7 @@ describe('computeNodeWorldTransformRevision', () => {
|
|
|
39
42
|
computeNodeWorldTransformRevision(runtime);
|
|
40
43
|
expect(runtime.worldTransformUsingLocalTransformId).toBe(3);
|
|
41
44
|
expect(runtime.worldTransformUsingParentTransformId).toBe(0);
|
|
45
|
+
expect(runtime.worldTransformId).not.toBe(0);
|
|
42
46
|
});
|
|
43
47
|
|
|
44
48
|
it('incorporates parent worldTransformId when provided', () => {
|
|
@@ -49,6 +53,17 @@ describe('computeNodeWorldTransformRevision', () => {
|
|
|
49
53
|
computeNodeWorldTransformRevision(runtime, parentRuntime);
|
|
50
54
|
expect(runtime.worldTransformUsingParentTransformId).toBe(7);
|
|
51
55
|
});
|
|
56
|
+
|
|
57
|
+
it('assigns the next monotonic world revision on every recompute', () => {
|
|
58
|
+
const runtime = getEntityRuntime(node);
|
|
59
|
+
computeNodeWorldTransformRevision(runtime);
|
|
60
|
+
const first = runtime.worldTransformId;
|
|
61
|
+
|
|
62
|
+
computeNodeWorldTransformRevision(runtime);
|
|
63
|
+
|
|
64
|
+
const next = (first + 1) >>> 0;
|
|
65
|
+
expect(runtime.worldTransformId).toBe(next === 0 ? 1 : next);
|
|
66
|
+
});
|
|
52
67
|
});
|
|
53
68
|
|
|
54
69
|
describe('getNodeAppearanceRevision', () => {
|
|
@@ -59,6 +74,14 @@ describe('getNodeAppearanceRevision', () => {
|
|
|
59
74
|
});
|
|
60
75
|
});
|
|
61
76
|
|
|
77
|
+
describe('getNodeChildrenRevision', () => {
|
|
78
|
+
it('returns childrenId', () => {
|
|
79
|
+
const runtime = getEntityRuntime(node);
|
|
80
|
+
runtime.childrenId = 100;
|
|
81
|
+
expect(getNodeChildrenRevision(node)).toStrictEqual(runtime.childrenId);
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
|
|
62
85
|
describe('getNodeLocalBoundsRevision', () => {
|
|
63
86
|
it('returns localBoundsId', () => {
|
|
64
87
|
const runtime = getEntityRuntime(node);
|
|
@@ -83,6 +106,14 @@ describe('getNodeLocalTransformRevision', () => {
|
|
|
83
106
|
});
|
|
84
107
|
});
|
|
85
108
|
|
|
109
|
+
describe('getNodeParentReferenceRevision', () => {
|
|
110
|
+
it('returns parentReferenceId', () => {
|
|
111
|
+
const runtime = getEntityRuntime(node);
|
|
112
|
+
runtime.parentReferenceId = 100;
|
|
113
|
+
expect(getNodeParentReferenceRevision(node)).toStrictEqual(runtime.parentReferenceId);
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
|
|
86
117
|
describe('getNodeWorldTransformRevision', () => {
|
|
87
118
|
it('returns worldTransformId', () => {
|
|
88
119
|
const runtime = getEntityRuntime(node);
|
|
@@ -193,6 +224,13 @@ describe('invalidateNodeLocalTransform', () => {
|
|
|
193
224
|
});
|
|
194
225
|
|
|
195
226
|
describe('invalidateNodeParentReference', () => {
|
|
227
|
+
it('increments parentReferenceId', () => {
|
|
228
|
+
const runtime = getEntityRuntime(node);
|
|
229
|
+
const parentReferenceId = runtime.parentReferenceId;
|
|
230
|
+
invalidateNodeParentReference(node);
|
|
231
|
+
expect(runtime.parentReferenceId).toBe(parentReferenceId + 1);
|
|
232
|
+
});
|
|
233
|
+
|
|
196
234
|
it('invalidates the world transform parent transform cached ID', () => {
|
|
197
235
|
const runtime = getEntityRuntime(node);
|
|
198
236
|
runtime.worldTransformUsingParentTransformId = 1;
|
|
@@ -221,6 +259,19 @@ describe('invalidateNodeWorldBounds', () => {
|
|
|
221
259
|
expect(runtime.worldBoundsUsingWorldTransformId).toBe(-1);
|
|
222
260
|
expect(runtime.worldBoundsUsingLocalBoundsId).toBe(-1);
|
|
223
261
|
});
|
|
262
|
+
|
|
263
|
+
it('invalidates every ancestor whose aggregate includes the target', () => {
|
|
264
|
+
const parent = createTestNode();
|
|
265
|
+
addNodeChild(parent, node);
|
|
266
|
+
const parentRuntime = getEntityRuntime(parent);
|
|
267
|
+
parentRuntime.worldBoundsUsingWorldTransformId = 1;
|
|
268
|
+
parentRuntime.worldBoundsUsingLocalBoundsId = 1;
|
|
269
|
+
|
|
270
|
+
invalidateNodeWorldBounds(node);
|
|
271
|
+
|
|
272
|
+
expect(parentRuntime.worldBoundsUsingWorldTransformId).toBe(-1);
|
|
273
|
+
expect(parentRuntime.worldBoundsUsingLocalBoundsId).toBe(-1);
|
|
274
|
+
});
|
|
224
275
|
});
|
|
225
276
|
|
|
226
277
|
type TestNode = Node;
|
package/src/stageFit.test.ts
CHANGED
|
@@ -18,10 +18,12 @@ import {
|
|
|
18
18
|
|
|
19
19
|
const TestNodeKind = 'TestNode';
|
|
20
20
|
|
|
21
|
-
function makeNodeWithBounds(width: number, height: number) {
|
|
21
|
+
function makeNodeWithBounds(width: number, height: number, x = 0, y = 0) {
|
|
22
22
|
const node = createNode(TestNodeKind);
|
|
23
23
|
const runtime = getNodeRuntime(node) as unknown as HasBoundsRectangleRuntime;
|
|
24
24
|
runtime.computeLocalBoundsRectangle = (out) => {
|
|
25
|
+
out.x = x;
|
|
26
|
+
out.y = y;
|
|
25
27
|
out.width = width;
|
|
26
28
|
out.height = height;
|
|
27
29
|
};
|
|
@@ -137,6 +139,18 @@ describe('computeScene2DFitTransform', () => {
|
|
|
137
139
|
expect(m.d).toBe(1);
|
|
138
140
|
});
|
|
139
141
|
|
|
142
|
+
it('sets identity when only the content width is zero', () => {
|
|
143
|
+
const m = createMatrix(2, 3, 4, 5, 6, 7);
|
|
144
|
+
computeScene2DFitTransform(m, fit({ root: makeNodeWithBounds(0, 50), scaleMode: 'exactfit' }), 800, 600);
|
|
145
|
+
expect(m).toMatchObject({ a: 1, b: 0, c: 0, d: 1, tx: 0, ty: 0 });
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('sets identity when only the content height is zero', () => {
|
|
149
|
+
const m = createMatrix(2, 3, 4, 5, 6, 7);
|
|
150
|
+
computeScene2DFitTransform(m, fit({ root: makeNodeWithBounds(100, 0), scaleMode: 'exactfit' }), 800, 600);
|
|
151
|
+
expect(m).toMatchObject({ a: 1, b: 0, c: 0, d: 1, tx: 0, ty: 0 });
|
|
152
|
+
});
|
|
153
|
+
|
|
140
154
|
it('noscale with topleft: identity scale at origin', () => {
|
|
141
155
|
const m = createMatrix();
|
|
142
156
|
computeScene2DFitTransform(
|
|
@@ -226,4 +240,45 @@ describe('computeScene2DFitTransform', () => {
|
|
|
226
240
|
expect(m.b).toBe(0);
|
|
227
241
|
expect(m.c).toBe(0);
|
|
228
242
|
});
|
|
243
|
+
|
|
244
|
+
it('maps a nonzero content-bounds origin to the selected alignment', () => {
|
|
245
|
+
const m = createMatrix();
|
|
246
|
+
computeScene2DFitTransform(
|
|
247
|
+
m,
|
|
248
|
+
fit({ align: 'topleft', root: makeNodeWithBounds(100, 50, 10, -20), scaleMode: 'exactfit' }),
|
|
249
|
+
200,
|
|
250
|
+
100,
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
expect(m.a).toBe(2);
|
|
254
|
+
expect(m.d).toBe(2);
|
|
255
|
+
expect(m.tx).toBe(-20);
|
|
256
|
+
expect(m.ty).toBe(40);
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
it('uses scaled width before subtracting a nonzero origin on a centered horizontal axis', () => {
|
|
260
|
+
const m = createMatrix();
|
|
261
|
+
computeScene2DFitTransform(
|
|
262
|
+
m,
|
|
263
|
+
fit({ align: 'top', root: makeNodeWithBounds(100, 50, 10, -20), scaleMode: 'exactfit' }),
|
|
264
|
+
200,
|
|
265
|
+
100,
|
|
266
|
+
);
|
|
267
|
+
|
|
268
|
+
expect(m.tx).toBe(-20);
|
|
269
|
+
expect(m.ty).toBe(40);
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
it('uses scaled height before subtracting a nonzero origin on a centered vertical axis', () => {
|
|
273
|
+
const m = createMatrix();
|
|
274
|
+
computeScene2DFitTransform(
|
|
275
|
+
m,
|
|
276
|
+
fit({ align: 'left', root: makeNodeWithBounds(100, 50, 10, -20), scaleMode: 'exactfit' }),
|
|
277
|
+
200,
|
|
278
|
+
100,
|
|
279
|
+
);
|
|
280
|
+
|
|
281
|
+
expect(m.tx).toBe(-20);
|
|
282
|
+
expect(m.ty).toBe(40);
|
|
283
|
+
});
|
|
229
284
|
});
|
package/src/traversal.test.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type { Node, NodeTraits } from '@flighthq/types/contract';
|
|
1
|
+
import type { Node, NodeRuntime, NodeTraits } from '@flighthq/types/contract';
|
|
2
2
|
import { NodeKind } from '@flighthq/types/contract';
|
|
3
3
|
|
|
4
4
|
import { addNodeChild } from './hierarchy';
|
|
5
|
-
import { createNode } from './node';
|
|
5
|
+
import { createNode, getNodeRuntime } from './node';
|
|
6
6
|
import {
|
|
7
7
|
findNode,
|
|
8
8
|
findNodeByName,
|
|
@@ -112,7 +112,6 @@ describe('forEachNodeAncestor', () => {
|
|
|
112
112
|
const visited: Node<NodeTraits>[] = [];
|
|
113
113
|
forEachNodeAncestor(grandchild, (n) => {
|
|
114
114
|
visited.push(n);
|
|
115
|
-
return true;
|
|
116
115
|
});
|
|
117
116
|
expect(visited).toEqual([childA, root]);
|
|
118
117
|
});
|
|
@@ -191,6 +190,18 @@ describe('getNodeNextSibling', () => {
|
|
|
191
190
|
it('returns the next sibling', () => {
|
|
192
191
|
expect(getNodeNextSibling(childA)).toBe(childB);
|
|
193
192
|
});
|
|
193
|
+
|
|
194
|
+
it('returns null for a stale parent whose child list is absent', () => {
|
|
195
|
+
(getNodeRuntime(root) as NodeRuntime).children = null;
|
|
196
|
+
|
|
197
|
+
expect(getNodeNextSibling(childA)).toBeNull();
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
it('returns null for a stale parent whose nonempty child list omits the source', () => {
|
|
201
|
+
(getNodeRuntime(root) as NodeRuntime).children = [childB];
|
|
202
|
+
|
|
203
|
+
expect(getNodeNextSibling(childA)).toBeNull();
|
|
204
|
+
});
|
|
194
205
|
});
|
|
195
206
|
|
|
196
207
|
describe('getNodePreviousSibling', () => {
|
|
@@ -205,6 +216,12 @@ describe('getNodePreviousSibling', () => {
|
|
|
205
216
|
it('returns the previous sibling', () => {
|
|
206
217
|
expect(getNodePreviousSibling(childB)).toBe(childA);
|
|
207
218
|
});
|
|
219
|
+
|
|
220
|
+
it('returns null for a stale parent whose child list is absent', () => {
|
|
221
|
+
(getNodeRuntime(root) as NodeRuntime).children = null;
|
|
222
|
+
|
|
223
|
+
expect(getNodePreviousSibling(childB)).toBeNull();
|
|
224
|
+
});
|
|
208
225
|
});
|
|
209
226
|
|
|
210
227
|
describe('walkNodeDescendants', () => {
|
|
@@ -243,4 +260,16 @@ describe('walkNodeDescendants', () => {
|
|
|
243
260
|
expect(visited).not.toContain(grandchild);
|
|
244
261
|
expect(visited).not.toContain(childB);
|
|
245
262
|
});
|
|
263
|
+
|
|
264
|
+
it('propagates an early stop from a nested descendant', () => {
|
|
265
|
+
const visited: Node<NodeTraits>[] = [];
|
|
266
|
+
|
|
267
|
+
const result = walkNodeDescendants(root, (node) => {
|
|
268
|
+
visited.push(node);
|
|
269
|
+
return node !== grandchild;
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
expect(result).toBe(false);
|
|
273
|
+
expect(visited).toEqual([childA, grandchild]);
|
|
274
|
+
});
|
|
246
275
|
});
|