@flighthq/effects 0.5.1-next.704.19c91ec → 0.5.1-next.730.f29a5aa

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 CHANGED
@@ -13,5 +13,5 @@ Import the supported application-facing API from `@flighthq/effects`. The `@flig
13
13
  This package is part of the locked-version Flight SDK graph. Applications may instead install and import `@flighthq/sdk` when package-level tree shaking is sufficient.
14
14
 
15
15
  - [Flight project](https://github.com/flighthq/flight)
16
- - [Source for @flighthq/effects@0.5.1-next.704.19c91ec](https://github.com/flighthq/flight/tree/19c91ec3e569d6b3b471682726465a79fba9f8cb/packages/effects)
17
- - [License](https://github.com/flighthq/flight/blob/19c91ec3e569d6b3b471682726465a79fba9f8cb/LICENSE.md)
16
+ - [Source for @flighthq/effects@0.5.1-next.730.f29a5aa](https://github.com/flighthq/flight/tree/f29a5aab5db192284ce5cadf08ffae2649a598ea/packages/effects)
17
+ - [License](https://github.com/flighthq/flight/blob/f29a5aab5db192284ce5cadf08ffae2649a598ea/LICENSE.md)
@@ -1,4 +1,5 @@
1
- import type { RenderEffect } from '@flighthq/types/contract';
1
+ import type { RenderEffect, RenderEffectFieldRoles } from '@flighthq/types/contract';
2
2
  export declare function canLerpRenderEffects(a: Readonly<RenderEffect>, b: Readonly<RenderEffect>): boolean;
3
- export declare function lerpRenderEffect(a: Readonly<RenderEffect>, b: Readonly<RenderEffect>, t: number, out: RenderEffect): boolean;
3
+ export declare const RENDER_EFFECT_FIELD_ROLES: RenderEffectFieldRoles;
4
+ export declare function lerpRenderEffect(a: Readonly<RenderEffect>, b: Readonly<RenderEffect>, t: number, out: RenderEffect, roles?: RenderEffectFieldRoles): boolean;
4
5
  //# sourceMappingURL=renderEffectInterpolation.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"renderEffectInterpolation.d.ts","sourceRoot":"","sources":["../src/renderEffectInterpolation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAO7D,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC,EAAE,QAAQ,CAAC,YAAY,CAAC,GAAG,OAAO,CAElG;AAWD,wBAAgB,gBAAgB,CAC9B,CAAC,EAAE,QAAQ,CAAC,YAAY,CAAC,EACzB,CAAC,EAAE,QAAQ,CAAC,YAAY,CAAC,EACzB,CAAC,EAAE,MAAM,EACT,GAAG,EAAE,YAAY,GAChB,OAAO,CAgDT"}
1
+ {"version":3,"file":"renderEffectInterpolation.d.ts","sourceRoot":"","sources":["../src/renderEffectInterpolation.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAOrF,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC,EAAE,QAAQ,CAAC,YAAY,CAAC,GAAG,OAAO,CAElG;AAMD,eAAO,MAAM,yBAAyB,EAAE,sBAWvC,CAAC;AAgBF,wBAAgB,gBAAgB,CAC9B,CAAC,EAAE,QAAQ,CAAC,YAAY,CAAC,EACzB,CAAC,EAAE,QAAQ,CAAC,YAAY,CAAC,EACzB,CAAC,EAAE,MAAM,EACT,GAAG,EAAE,YAAY,EACjB,KAAK,GAAE,sBAAkD,GACxD,OAAO,CAiDT"}
@@ -1,3 +1,4 @@
1
+ import { lerpColor } from '@flighthq/color/contract';
1
2
  // Animation-friendly interpolation for render-effect intents. Allows tween/timeline to animate
2
3
  // effect parameters smoothly across keyframes. All functions are alias-safe.
3
4
  // Returns true if `a` and `b` can be interpolated: they must be the same `kind`.
@@ -5,6 +6,22 @@
5
6
  export function canLerpRenderEffects(a, b) {
6
7
  return a.kind === b.kind;
7
8
  }
9
+ // Which numeric fields on each SDK effect kind are packed sRGB RGBA rather than scalars. Derived from
10
+ // the field declarations in @flighthq/types, where every one of these carries a "Packed sRGB RGBA"
11
+ // comment. A caller with vendor effect kinds spreads its own entries over this table; a kind that is
12
+ // absent is treated as all-scalar, which is the pre-existing behaviour rather than a new guess.
13
+ export const RENDER_EFFECT_FIELD_ROLES = {
14
+ BevelEffect: { highlightColor: 'packedColor', shadowColor: 'packedColor' },
15
+ ConvolutionEffect: { color: 'packedColor' },
16
+ DropShadowEffect: { color: 'packedColor' },
17
+ InnerGlowEffect: { color: 'packedColor' },
18
+ InnerShadowEffect: { color: 'packedColor' },
19
+ OuterGlowEffect: { color: 'packedColor' },
20
+ OutlineEffect: { color: 'packedColor' },
21
+ ScreenSpaceFogEffect: { color: 'packedColor' },
22
+ VignetteEffect: { color: 'packedColor' },
23
+ VolumetricLightEffect: { lightColor: 'packedColor' },
24
+ };
8
25
  // Linearly interpolates all numeric fields between two effects of the same kind, writing the
9
26
  // result into `out`. Boolean fields snap to `a`'s value (t < 0.5) or `b`'s value (t >= 0.5).
10
27
  // Enum/string fields snap at t = 0.5. Readonly arrays (e.g. mipWeights) are not interpolated —
@@ -14,7 +31,12 @@ export function canLerpRenderEffects(a, b) {
14
31
  // If `a.kind !== b.kind`, `out` is left unchanged and the function returns false.
15
32
  // Returns true on success, false on kind mismatch. Never throws.
16
33
  // Alias-safe: reads all values from `a` and `b` before writing to `out` (safe if out === a or out === b).
17
- export function lerpRenderEffect(a, b, t, out) {
34
+ //
35
+ // Fields declared `packedColor` by `roles` interpolate per channel through `lerpColor` — which unpacks,
36
+ // interpolates in linear space, and repacks — rather than as scalars. Lerping a packed integer borrows
37
+ // across byte boundaries: halfway between 0xff0000ff and 0x0000ffff is 0x7f8080ff, a grey-blue whose
38
+ // green channel neither endpoint has.
39
+ export function lerpRenderEffect(a, b, t, out, roles = RENDER_EFFECT_FIELD_ROLES) {
18
40
  if (a.kind !== b.kind)
19
41
  return false;
20
42
  const tc = Math.max(0, Math.min(1, t));
@@ -54,15 +76,15 @@ export function lerpRenderEffect(a, b, t, out) {
54
76
  }
55
77
  // Read snapshot from a and b, then write to out.
56
78
  const outRecord = out;
79
+ const kindRoles = roles[a.kind];
57
80
  for (const key of numericKeys) {
58
81
  const va = aRec[key];
59
82
  const vb = bRec[key];
60
- if (va !== undefined && vb !== undefined) {
61
- outRecord[key] = va + (vb - va) * tc;
62
- }
63
- else {
83
+ if (va === undefined || vb === undefined) {
64
84
  outRecord[key] = tc < 0.5 ? va : vb;
85
+ continue;
65
86
  }
87
+ outRecord[key] = kindRoles?.[key] === 'packedColor' ? lerpColor(va, vb, tc) : va + (vb - va) * tc;
66
88
  }
67
89
  for (const key of booleanKeys) {
68
90
  outRecord[key] = tc < 0.5 ? aRec[key] : bRec[key];
@@ -1 +1 @@
1
- {"version":3,"file":"renderEffectInterpolation.js","sourceRoot":"","sources":["../src/renderEffectInterpolation.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"renderEffectInterpolation.js","sourceRoot":"","sources":["../src/renderEffectInterpolation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAGrD,+FAA+F;AAC/F,6EAA6E;AAE7E,iFAAiF;AACjF,sGAAsG;AACtG,MAAM,UAAU,oBAAoB,CAAC,CAAyB,EAAE,CAAyB;IACvF,OAAO,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI,CAAC;AAC3B,CAAC;AAED,sGAAsG;AACtG,mGAAmG;AACnG,qGAAqG;AACrG,gGAAgG;AAChG,MAAM,CAAC,MAAM,yBAAyB,GAA2B;IAC/D,WAAW,EAAE,EAAE,cAAc,EAAE,aAAa,EAAE,WAAW,EAAE,aAAa,EAAE;IAC1E,iBAAiB,EAAE,EAAE,KAAK,EAAE,aAAa,EAAE;IAC3C,gBAAgB,EAAE,EAAE,KAAK,EAAE,aAAa,EAAE;IAC1C,eAAe,EAAE,EAAE,KAAK,EAAE,aAAa,EAAE;IACzC,iBAAiB,EAAE,EAAE,KAAK,EAAE,aAAa,EAAE;IAC3C,eAAe,EAAE,EAAE,KAAK,EAAE,aAAa,EAAE;IACzC,aAAa,EAAE,EAAE,KAAK,EAAE,aAAa,EAAE;IACvC,oBAAoB,EAAE,EAAE,KAAK,EAAE,aAAa,EAAE;IAC9C,cAAc,EAAE,EAAE,KAAK,EAAE,aAAa,EAAE;IACxC,qBAAqB,EAAE,EAAE,UAAU,EAAE,aAAa,EAAE;CACrD,CAAC;AAEF,6FAA6F;AAC7F,6FAA6F;AAC7F,+FAA+F;AAC/F,2DAA2D;AAC3D,EAAE;AACF,iGAAiG;AACjG,kFAAkF;AAClF,iEAAiE;AACjE,0GAA0G;AAC1G,EAAE;AACF,wGAAwG;AACxG,uGAAuG;AACvG,qGAAqG;AACrG,sCAAsC;AACtC,MAAM,UAAU,gBAAgB,CAC9B,CAAyB,EACzB,CAAyB,EACzB,CAAS,EACT,GAAiB,EACjB,QAAgC,yBAAyB;IAEzD,IAAI,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI;QAAE,OAAO,KAAK,CAAC;IACpC,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACvC,6DAA6D;IAC7D,MAAM,WAAW,GAAG,IAAI,GAAG,EAAU,CAAC;IACtC,MAAM,WAAW,GAAG,IAAI,GAAG,EAAU,CAAC;IACtC,MAAM,UAAU,GAAG,IAAI,GAAG,EAAU,CAAC;IACrC,MAAM,IAAI,GAAG,CAA4B,CAAC;IAC1C,MAAM,IAAI,GAAG,CAA4B,CAAC;IAC1C,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACpC,IAAI,GAAG,KAAK,MAAM;YAAE,SAAS;QAC7B,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC;QACrB,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC;QACrB,IAAI,OAAO,EAAE,KAAK,QAAQ,IAAI,OAAO,EAAE,KAAK,QAAQ,EAAE,CAAC;YACrD,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACvB,CAAC;aAAM,IAAI,OAAO,EAAE,KAAK,SAAS,IAAI,OAAO,EAAE,KAAK,SAAS,EAAE,CAAC;YAC9D,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACvB,CAAC;aAAM,IAAI,OAAO,EAAE,KAAK,QAAQ,IAAI,OAAO,EAAE,KAAK,QAAQ,EAAE,CAAC;YAC5D,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACtB,CAAC;IACH,CAAC;IACD,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACpC,IAAI,GAAG,KAAK,MAAM;YAAE,SAAS;QAC7B,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YAC3E,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC;YACrB,IAAI,OAAO,EAAE,KAAK,QAAQ;gBAAE,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;iBAC5C,IAAI,OAAO,EAAE,KAAK,SAAS;gBAAE,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;iBAClD,IAAI,OAAO,EAAE,KAAK,QAAQ;gBAAE,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACvD,CAAC;IACH,CAAC;IACD,iDAAiD;IACjD,MAAM,SAAS,GAAG,GAAyC,CAAC;IAC5D,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,IAAc,CAAC,CAAC;IAC1C,KAAK,MAAM,GAAG,IAAI,WAAW,EAAE,CAAC;QAC9B,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAuB,CAAC;QAC3C,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAuB,CAAC;QAC3C,IAAI,EAAE,KAAK,SAAS,IAAI,EAAE,KAAK,SAAS,EAAE,CAAC;YACzC,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACpC,SAAS;QACX,CAAC;QACD,SAAS,CAAC,GAAG,CAAC,GAAG,SAAS,EAAE,CAAC,GAAG,CAAC,KAAK,aAAa,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC;IACpG,CAAC;IACD,KAAK,MAAM,GAAG,IAAI,WAAW,EAAE,CAAC;QAC9B,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACpD,CAAC;IACD,KAAK,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;QAC7B,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACpD,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flighthq/effects",
3
- "version": "0.5.1-next.704.19c91ec",
3
+ "version": "0.5.1-next.730.f29a5aa",
4
4
  "author": "Joshua Granick and other contributors",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -38,12 +38,13 @@
38
38
  "clean:dist": "tsx ../../scripts/clean-package-dist.ts"
39
39
  },
40
40
  "dependencies": {
41
- "@flighthq/geometry": "0.5.1-next.704.19c91ec",
42
- "@flighthq/node": "0.5.1-next.704.19c91ec",
43
- "@flighthq/registry": "0.5.1-next.704.19c91ec",
44
- "@flighthq/render": "0.5.1-next.704.19c91ec",
45
- "@flighthq/signals": "0.5.1-next.704.19c91ec",
46
- "@flighthq/types": "0.5.1-next.704.19c91ec"
41
+ "@flighthq/color": "0.5.1-next.730.f29a5aa",
42
+ "@flighthq/geometry": "0.5.1-next.730.f29a5aa",
43
+ "@flighthq/node": "0.5.1-next.730.f29a5aa",
44
+ "@flighthq/registry": "0.5.1-next.730.f29a5aa",
45
+ "@flighthq/render": "0.5.1-next.730.f29a5aa",
46
+ "@flighthq/signals": "0.5.1-next.730.f29a5aa",
47
+ "@flighthq/types": "0.5.1-next.730.f29a5aa"
47
48
  },
48
49
  "devDependencies": {
49
50
  "typescript": "^5.3.0"
@@ -1,3 +1,4 @@
1
+ import { createBevelEffect } from './bevelEffect';
1
2
  import { createBloomEffect } from './bloomEffect';
2
3
  import { canLerpRenderEffects, lerpRenderEffect } from './renderEffectInterpolation';
3
4
  import { createVignetteEffect } from './vignetteEffect';
@@ -56,6 +57,79 @@ describe('lerpRenderEffect', () => {
56
57
  lerpRenderEffect(a, b, 0.5, a);
57
58
  expect(a.threshold).toBeCloseTo(0.5, 5);
58
59
  });
60
+ it('interpolates a packed colour per channel instead of lerping the integer', () => {
61
+ // 0xff0000ff to 0x0000ffff. Lerping the packed integers linearly gives 0x7f8080ff — a washed-out
62
+ // grey-blue that is in neither colour's hue, because the red byte's borrow runs into green.
63
+ // Per-channel interpolation keeps green at zero throughout.
64
+ const a = createVignetteEffect({ color: 0xff0000ff });
65
+ const b = createVignetteEffect({ color: 0x0000ffff });
66
+ const out = createVignetteEffect();
67
+ lerpRenderEffect(a, b, 0.5, out);
68
+ expect(channelOf(out.color as number, 1)).toBe(0);
69
+ expect(out.color).not.toBe(Math.round(0xff0000ff + (0x0000ffff - 0xff0000ff) * 0.5));
70
+ });
71
+
72
+ it('holds a packed colour steady when both endpoints are the same colour', () => {
73
+ // The integer lerp happens to be correct here, so this pins the property the fix must not break:
74
+ // a channel that does not move must come back bit-identical rather than drifting by rounding.
75
+ const a = createVignetteEffect({ color: 0x3366ccff });
76
+ const b = createVignetteEffect({ color: 0x3366ccff });
77
+ const out = createVignetteEffect();
78
+ lerpRenderEffect(a, b, 0.37, out);
79
+ expect(out.color).toBe(0x3366ccff);
80
+ });
81
+
82
+ it('returns each endpoint exactly at t=0 and t=1', () => {
83
+ const a = createVignetteEffect({ color: 0xff0000ff });
84
+ const b = createVignetteEffect({ color: 0x00ff00ff });
85
+ const out = createVignetteEffect();
86
+ lerpRenderEffect(a, b, 0, out);
87
+ expect(out.color).toBe(0xff0000ff);
88
+ lerpRenderEffect(a, b, 1, out);
89
+ expect(out.color).toBe(0x00ff00ff);
90
+ });
91
+
92
+ it('writes a whole packed integer, never the fraction a scalar lerp lands on', () => {
93
+ // Halfway between 0xff0000ff and 0x00ff0000 the scalar path computes 2147451007.5 — a colour field
94
+ // holding a fraction is corrupt whatever its channels say, and asserting only on channel ranges
95
+ // misses it because the truncated bytes still fall inside them.
96
+ const a = createVignetteEffect({ color: 0xff0000ff });
97
+ const b = createVignetteEffect({ color: 0x00ff0000 });
98
+ const out = createVignetteEffect();
99
+ lerpRenderEffect(a, b, 0.5, out);
100
+ expect(Number.isInteger(out.color)).toBe(true);
101
+ const alpha = channelOf(out.color as number, 3);
102
+ expect(alpha).toBeGreaterThan(0);
103
+ expect(alpha).toBeLessThan(255);
104
+ });
105
+
106
+ it('treats every colour field on a kind that declares more than one', () => {
107
+ // BevelEffect carries highlightColor AND shadowColor. A fix that special-cased a field named
108
+ // exactly "color" would pass every other test here and corrupt both of these.
109
+ const a = createBevelEffect({ highlightColor: 0xff0000ff, shadowColor: 0xff0000ff });
110
+ const b = createBevelEffect({ highlightColor: 0x0000ffff, shadowColor: 0x0000ffff });
111
+ const out = createBevelEffect();
112
+ lerpRenderEffect(a, b, 0.5, out);
113
+ expect(channelOf(out.highlightColor as number, 1)).toBe(0);
114
+ expect(channelOf(out.shadowColor as number, 1)).toBe(0);
115
+ });
116
+
117
+ it('still lerps an ordinary scalar linearly on a kind that also carries a colour', () => {
118
+ // The colour path must not swallow the scalars beside it.
119
+ const a = createVignetteEffect({ color: 0xff0000ff, intensity: 0 });
120
+ const b = createVignetteEffect({ color: 0x0000ffff, intensity: 1 });
121
+ const out = createVignetteEffect();
122
+ lerpRenderEffect(a, b, 0.25, out);
123
+ expect(out.intensity).toBeCloseTo(0.25, 5);
124
+ });
125
+
126
+ it('is alias-safe for a colour field when out === a', () => {
127
+ const a = createVignetteEffect({ color: 0xff0000ff });
128
+ const b = createVignetteEffect({ color: 0x0000ffff });
129
+ lerpRenderEffect(a, b, 0.5, a);
130
+ expect(channelOf(a.color as number, 1)).toBe(0);
131
+ });
132
+
59
133
  it('boolean fields snap at t=0.5 boundary', () => {
60
134
  const a = createVignetteEffect({ enabled: false } as never);
61
135
  const b = createVignetteEffect({ enabled: true } as never);
@@ -66,3 +140,8 @@ describe('lerpRenderEffect', () => {
66
140
  expect((out as unknown as Record<string, unknown>).enabled).toBe(true);
67
141
  });
68
142
  });
143
+
144
+ // Reads one 0-255 channel out of a packed sRGB RGBA integer. Index 0 is red, 3 is alpha.
145
+ function channelOf(color: number, index: number): number {
146
+ return (color >>> (24 - index * 8)) & 0xff;
147
+ }