@flighthq/materials 0.1.0 → 0.2.1-next.410.a23f189
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/customShaderMaterial.d.ts +3 -0
- package/dist/customShaderMaterial.d.ts.map +1 -0
- package/dist/customShaderMaterial.js +15 -0
- package/dist/customShaderMaterial.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/material.d.ts.map +1 -1
- package/dist/material.js +6 -19
- package/dist/material.js.map +1 -1
- package/dist/pbrMaterials.d.ts.map +1 -1
- package/dist/pbrMaterials.js +1 -1
- package/dist/pbrMaterials.js.map +1 -1
- package/dist/phongToPbr.d.ts +6 -0
- package/dist/phongToPbr.d.ts.map +1 -0
- package/dist/phongToPbr.js +53 -0
- package/dist/phongToPbr.js.map +1 -0
- package/dist/unlitMaterials.d.ts.map +1 -1
- package/dist/unlitMaterials.js +4 -2
- package/dist/unlitMaterials.js.map +1 -1
- package/package.json +9 -3
- package/src/colorTransform.test.ts +66 -9
- package/src/customShaderMaterial.test.ts +20 -0
- package/src/material.test.ts +14 -38
- package/src/phongToPbr.test.ts +67 -0
- package/dist/color.d.ts +0 -23
- package/dist/color.d.ts.map +0 -1
- package/dist/color.js +0 -291
- package/dist/color.js.map +0 -1
- package/dist/colorTransformMaterial.d.ts +0 -4
- package/dist/colorTransformMaterial.d.ts.map +0 -1
- package/dist/colorTransformMaterial.js +0 -18
- package/dist/colorTransformMaterial.js.map +0 -1
- package/src/color.test.ts +0 -383
- package/src/colorTransformMaterial.test.ts +0 -26
package/src/color.test.ts
DELETED
|
@@ -1,383 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
computeRgbHexString,
|
|
3
|
-
createHslColor,
|
|
4
|
-
createHsvColor,
|
|
5
|
-
createLinearColor,
|
|
6
|
-
getColorContrastRatio,
|
|
7
|
-
getColorLuminance,
|
|
8
|
-
hslToRgb,
|
|
9
|
-
hsvToRgb,
|
|
10
|
-
lerpColor,
|
|
11
|
-
lerpLinearColor,
|
|
12
|
-
linearChannelToSrgb,
|
|
13
|
-
packColor,
|
|
14
|
-
packLinearToColor,
|
|
15
|
-
premultiplyColorAlpha,
|
|
16
|
-
rgbToHsl,
|
|
17
|
-
rgbToHsv,
|
|
18
|
-
unpackColorRgba,
|
|
19
|
-
unpackColorToLinear,
|
|
20
|
-
unpremultiplyColorAlpha,
|
|
21
|
-
} from './color';
|
|
22
|
-
|
|
23
|
-
describe('computeRgbHexString', () => {
|
|
24
|
-
it('returns a 6-digit hex string of the lower 24 bits', () => {
|
|
25
|
-
expect(computeRgbHexString(0x00ff0000)).toBe('#ff0000');
|
|
26
|
-
expect(computeRgbHexString(0x0000ff00)).toBe('#00ff00');
|
|
27
|
-
expect(computeRgbHexString(0x000000ff)).toBe('#0000ff');
|
|
28
|
-
expect(computeRgbHexString(0x00000000)).toBe('#000000');
|
|
29
|
-
expect(computeRgbHexString(0x00ffffff)).toBe('#ffffff');
|
|
30
|
-
});
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
describe('createHslColor', () => {
|
|
34
|
-
it('allocates a zeroed three-component HSL color', () => {
|
|
35
|
-
expect(createHslColor()).toEqual([0, 0, 0]);
|
|
36
|
-
});
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
describe('createHsvColor', () => {
|
|
40
|
-
it('allocates a zeroed three-component HSV color', () => {
|
|
41
|
-
expect(createHsvColor()).toEqual([0, 0, 0]);
|
|
42
|
-
});
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
describe('createLinearColor', () => {
|
|
46
|
-
it('allocates a zeroed four-component color', () => {
|
|
47
|
-
expect(createLinearColor()).toEqual([0, 0, 0, 0]);
|
|
48
|
-
});
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
describe('getColorContrastRatio', () => {
|
|
52
|
-
it('returns 21 for black on white', () => {
|
|
53
|
-
expect(getColorContrastRatio(0xffffffff, 0x000000ff)).toBeCloseTo(21, 0);
|
|
54
|
-
});
|
|
55
|
-
it('returns 1 for identical colors', () => {
|
|
56
|
-
expect(getColorContrastRatio(0x808080ff, 0x808080ff)).toBeCloseTo(1, 3);
|
|
57
|
-
});
|
|
58
|
-
it('is symmetric — order of arguments does not matter', () => {
|
|
59
|
-
const ratio1 = getColorContrastRatio(0xffffffff, 0x808080ff);
|
|
60
|
-
const ratio2 = getColorContrastRatio(0x808080ff, 0xffffffff);
|
|
61
|
-
expect(ratio1).toBeCloseTo(ratio2, 8);
|
|
62
|
-
});
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
describe('getColorLuminance', () => {
|
|
66
|
-
it('returns 1 for white', () => {
|
|
67
|
-
expect(getColorLuminance(0xffffffff)).toBeCloseTo(1, 5);
|
|
68
|
-
});
|
|
69
|
-
it('returns 0 for black', () => {
|
|
70
|
-
expect(getColorLuminance(0x000000ff)).toBeCloseTo(0, 5);
|
|
71
|
-
});
|
|
72
|
-
it('ignores the alpha channel', () => {
|
|
73
|
-
expect(getColorLuminance(0xffffffff)).toBe(getColorLuminance(0xffffff00));
|
|
74
|
-
});
|
|
75
|
-
it('matches expected Rec. 709 value for mid-gray sRGB', () => {
|
|
76
|
-
// sRGB 0x808080 → linear ≈ 0.21586; Rec.709: 0.2126*R + 0.7152*G + 0.0722*B = R (equal channels)
|
|
77
|
-
expect(getColorLuminance(0x808080ff)).toBeCloseTo(0.21586, 4);
|
|
78
|
-
});
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
describe('hslToRgb', () => {
|
|
82
|
-
it('produces achromatic RGB for saturation = 0', () => {
|
|
83
|
-
const out: [number, number, number, number] = [0, 0, 0, 1];
|
|
84
|
-
hslToRgb(out, 0, 0, 0.5);
|
|
85
|
-
expect(out[0]).toBeCloseTo(0.5);
|
|
86
|
-
expect(out[1]).toBeCloseTo(0.5);
|
|
87
|
-
expect(out[2]).toBeCloseTo(0.5);
|
|
88
|
-
});
|
|
89
|
-
it('round-trips with rgbToHsl for a primary color', () => {
|
|
90
|
-
// Red: hue=0, s=1, l=0.5 → RGB (1, 0, 0)
|
|
91
|
-
const out: [number, number, number, number] = [0, 0, 0, 1];
|
|
92
|
-
hslToRgb(out, 0, 1, 0.5);
|
|
93
|
-
expect(out[0]).toBeCloseTo(1, 5);
|
|
94
|
-
expect(out[1]).toBeCloseTo(0, 5);
|
|
95
|
-
expect(out[2]).toBeCloseTo(0, 5);
|
|
96
|
-
});
|
|
97
|
-
it('does not modify out[3] (alpha)', () => {
|
|
98
|
-
const out: [number, number, number, number] = [0, 0, 0, 0.75];
|
|
99
|
-
hslToRgb(out, 120, 1, 0.5);
|
|
100
|
-
expect(out[3]).toBe(0.75);
|
|
101
|
-
});
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
describe('hsvToRgb', () => {
|
|
105
|
-
it('produces achromatic RGB for saturation = 0', () => {
|
|
106
|
-
const out: [number, number, number, number] = [0, 0, 0, 1];
|
|
107
|
-
hsvToRgb(out, 0, 0, 0.6);
|
|
108
|
-
expect(out[0]).toBeCloseTo(0.6);
|
|
109
|
-
expect(out[1]).toBeCloseTo(0.6);
|
|
110
|
-
expect(out[2]).toBeCloseTo(0.6);
|
|
111
|
-
});
|
|
112
|
-
it('produces pure red for h=0, s=1, v=1', () => {
|
|
113
|
-
const out: [number, number, number, number] = [0, 0, 0, 1];
|
|
114
|
-
hsvToRgb(out, 0, 1, 1);
|
|
115
|
-
expect(out[0]).toBeCloseTo(1, 5);
|
|
116
|
-
expect(out[1]).toBeCloseTo(0, 5);
|
|
117
|
-
expect(out[2]).toBeCloseTo(0, 5);
|
|
118
|
-
});
|
|
119
|
-
it('round-trips with rgbToHsv for a pure green', () => {
|
|
120
|
-
const out: [number, number, number, number] = [0, 0, 0, 1];
|
|
121
|
-
hsvToRgb(out, 120, 1, 1);
|
|
122
|
-
expect(out[0]).toBeCloseTo(0, 5);
|
|
123
|
-
expect(out[1]).toBeCloseTo(1, 5);
|
|
124
|
-
expect(out[2]).toBeCloseTo(0, 5);
|
|
125
|
-
});
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
describe('lerpColor', () => {
|
|
129
|
-
it('returns start at t=0', () => {
|
|
130
|
-
expect(lerpColor(0xff0000ff, 0x0000ffff, 0)).toBe(0xff0000ff);
|
|
131
|
-
});
|
|
132
|
-
it('returns end at t=1', () => {
|
|
133
|
-
expect(lerpColor(0xff0000ff, 0x0000ffff, 1)).toBe(0x0000ffff);
|
|
134
|
-
});
|
|
135
|
-
it('clamps t below 0 to 0', () => {
|
|
136
|
-
expect(lerpColor(0xff0000ff, 0x0000ffff, -1)).toBe(0xff0000ff);
|
|
137
|
-
});
|
|
138
|
-
it('clamps t above 1 to 1', () => {
|
|
139
|
-
expect(lerpColor(0xff0000ff, 0x0000ffff, 2)).toBe(0x0000ffff);
|
|
140
|
-
});
|
|
141
|
-
it('midpoint between black and white is approximately mid-gray', () => {
|
|
142
|
-
// Black (linear 0) and white (linear 1) — midpoint in linear is 0.5, which in sRGB ≈ 0xbc
|
|
143
|
-
const mid = lerpColor(0x000000ff, 0xffffffff, 0.5);
|
|
144
|
-
const r = (mid >>> 24) & 0xff;
|
|
145
|
-
// linear 0.5 → sRGB ≈ 0xbc (188)
|
|
146
|
-
expect(r).toBeCloseTo(0xbc, -1);
|
|
147
|
-
});
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
describe('lerpLinearColor', () => {
|
|
151
|
-
it('returns start at t=0', () => {
|
|
152
|
-
const out = createLinearColor();
|
|
153
|
-
const start: [number, number, number, number] = [1, 0, 0, 1];
|
|
154
|
-
const end: [number, number, number, number] = [0, 0, 1, 1];
|
|
155
|
-
lerpLinearColor(out, start, end, 0);
|
|
156
|
-
expect(out).toEqual(start);
|
|
157
|
-
});
|
|
158
|
-
it('returns end at t=1', () => {
|
|
159
|
-
const out = createLinearColor();
|
|
160
|
-
const start: [number, number, number, number] = [1, 0, 0, 1];
|
|
161
|
-
const end: [number, number, number, number] = [0, 0, 1, 1];
|
|
162
|
-
lerpLinearColor(out, start, end, 1);
|
|
163
|
-
expect(out).toEqual(end);
|
|
164
|
-
});
|
|
165
|
-
it('is alias-safe when out is the same object as start', () => {
|
|
166
|
-
const start: [number, number, number, number] = [1, 0, 0, 1];
|
|
167
|
-
const end: [number, number, number, number] = [0, 0, 1, 1];
|
|
168
|
-
const out = start;
|
|
169
|
-
lerpLinearColor(out, out, end, 0.5);
|
|
170
|
-
expect(out[0]).toBeCloseTo(0.5, 8);
|
|
171
|
-
expect(out[2]).toBeCloseTo(0.5, 8);
|
|
172
|
-
});
|
|
173
|
-
it('returns the out instance', () => {
|
|
174
|
-
const out = createLinearColor();
|
|
175
|
-
const start: [number, number, number, number] = [0, 0, 0, 0];
|
|
176
|
-
const end: [number, number, number, number] = [1, 1, 1, 1];
|
|
177
|
-
expect(lerpLinearColor(out, start, end, 0.5)).toBe(out);
|
|
178
|
-
});
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
describe('linearChannelToSrgb', () => {
|
|
182
|
-
it('maps 0 → 0', () => {
|
|
183
|
-
expect(linearChannelToSrgb(0)).toBe(0);
|
|
184
|
-
});
|
|
185
|
-
it('maps 1 → 1', () => {
|
|
186
|
-
expect(linearChannelToSrgb(1)).toBeCloseTo(1, 8);
|
|
187
|
-
});
|
|
188
|
-
it('is the inverse of the sRGB decode for a round-trip', () => {
|
|
189
|
-
// A known linear value near the breakpoint.
|
|
190
|
-
const linear = 0.5;
|
|
191
|
-
const srgb = linearChannelToSrgb(linear);
|
|
192
|
-
// Re-encode: sRGB(linear(x)) should round-trip within floating-point tolerance.
|
|
193
|
-
const roundTrip = srgb <= 0.04045 ? srgb / 12.92 : ((srgb + 0.055) / 1.055) ** 2.4;
|
|
194
|
-
expect(roundTrip).toBeCloseTo(linear, 5);
|
|
195
|
-
});
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
describe('packColor', () => {
|
|
199
|
-
it('packs white components to 0xffffffff', () => {
|
|
200
|
-
expect(packColor(1, 1, 1, 1)).toBe(0xffffffff);
|
|
201
|
-
});
|
|
202
|
-
it('packs black components to 0x000000ff', () => {
|
|
203
|
-
expect(packColor(0, 0, 0, 1)).toBe(0x000000ff);
|
|
204
|
-
});
|
|
205
|
-
it('clamps out-of-range values', () => {
|
|
206
|
-
expect(packColor(2, -1, 0.5, 1)).toBe(packColor(1, 0, 0.5, 1));
|
|
207
|
-
});
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
describe('packLinearToColor', () => {
|
|
211
|
-
it('is the inverse of unpackColorToLinear for white', () => {
|
|
212
|
-
const out = createLinearColor();
|
|
213
|
-
unpackColorToLinear(out, 0xffffffff);
|
|
214
|
-
expect(packLinearToColor(out)).toBe(0xffffffff);
|
|
215
|
-
});
|
|
216
|
-
it('is the inverse of unpackColorToLinear for black (opaque)', () => {
|
|
217
|
-
const out = createLinearColor();
|
|
218
|
-
unpackColorToLinear(out, 0x000000ff);
|
|
219
|
-
expect(packLinearToColor(out)).toBe(0x000000ff);
|
|
220
|
-
});
|
|
221
|
-
it('passes alpha through without gamma encoding', () => {
|
|
222
|
-
// Alpha 0x80 / 0xff ≈ 0.502; should round-trip through linear-space alpha.
|
|
223
|
-
const color = 0x000000_80 >>> 0;
|
|
224
|
-
const out = createLinearColor();
|
|
225
|
-
unpackColorToLinear(out, color);
|
|
226
|
-
const repacked = packLinearToColor(out);
|
|
227
|
-
expect(repacked & 0xff).toBe(0x80);
|
|
228
|
-
});
|
|
229
|
-
it('round-trips a mid-gray color within 1 LSB', () => {
|
|
230
|
-
const color = 0x808080ff >>> 0;
|
|
231
|
-
const out = createLinearColor();
|
|
232
|
-
unpackColorToLinear(out, color);
|
|
233
|
-
const repacked = packLinearToColor(out);
|
|
234
|
-
const origR = (color >>> 24) & 0xff;
|
|
235
|
-
const repR = (repacked >>> 24) & 0xff;
|
|
236
|
-
expect(Math.abs(repR - origR)).toBeLessThanOrEqual(1);
|
|
237
|
-
});
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
describe('premultiplyColorAlpha', () => {
|
|
241
|
-
it('returns fully-opaque color unchanged (alpha=1)', () => {
|
|
242
|
-
expect(premultiplyColorAlpha(0xff0000ff)).toBe(0xff0000ff);
|
|
243
|
-
});
|
|
244
|
-
it('multiplies RGB by alpha and preserves the alpha channel', () => {
|
|
245
|
-
// Red at 50% alpha: R=255*0.502≈128, A unchanged=0x80
|
|
246
|
-
const result = premultiplyColorAlpha(0xff00007f);
|
|
247
|
-
expect((result >>> 24) & 0xff).toBeCloseTo(0x7f, -1);
|
|
248
|
-
expect(result & 0xff).toBe(0x7f);
|
|
249
|
-
});
|
|
250
|
-
it('returns black-with-alpha-0 for fully transparent color', () => {
|
|
251
|
-
const result = premultiplyColorAlpha(0xff000000);
|
|
252
|
-
expect(result & 0xff).toBe(0);
|
|
253
|
-
expect((result >>> 24) & 0xff).toBe(0);
|
|
254
|
-
});
|
|
255
|
-
it('round-trips with unpremultiplyColorAlpha for fully-opaque', () => {
|
|
256
|
-
const color = 0xab3456ff;
|
|
257
|
-
expect(unpremultiplyColorAlpha(premultiplyColorAlpha(color))).toBe(color);
|
|
258
|
-
});
|
|
259
|
-
});
|
|
260
|
-
|
|
261
|
-
describe('rgbToHsl', () => {
|
|
262
|
-
it('converts pure red to hue=0, s=1, l=0.5', () => {
|
|
263
|
-
const out: [number, number, number] = [0, 0, 0];
|
|
264
|
-
rgbToHsl(out, 0xff0000ff);
|
|
265
|
-
expect(out[0]).toBeCloseTo(0, 3);
|
|
266
|
-
expect(out[1]).toBeCloseTo(1, 5);
|
|
267
|
-
expect(out[2]).toBeCloseTo(0.5, 5);
|
|
268
|
-
});
|
|
269
|
-
it('converts white to hue=0, s=0, l=1', () => {
|
|
270
|
-
const out: [number, number, number] = [0, 0, 0];
|
|
271
|
-
rgbToHsl(out, 0xffffffff);
|
|
272
|
-
expect(out[0]).toBe(0);
|
|
273
|
-
expect(out[1]).toBe(0);
|
|
274
|
-
expect(out[2]).toBeCloseTo(1, 5);
|
|
275
|
-
});
|
|
276
|
-
it('converts black to hue=0, s=0, l=0', () => {
|
|
277
|
-
const out: [number, number, number] = [0, 0, 0];
|
|
278
|
-
rgbToHsl(out, 0x000000ff);
|
|
279
|
-
expect(out[0]).toBe(0);
|
|
280
|
-
expect(out[1]).toBe(0);
|
|
281
|
-
expect(out[2]).toBeCloseTo(0, 5);
|
|
282
|
-
});
|
|
283
|
-
it('returns the out instance', () => {
|
|
284
|
-
const out: [number, number, number] = [0, 0, 0];
|
|
285
|
-
expect(rgbToHsl(out, 0xff0000ff)).toBe(out);
|
|
286
|
-
});
|
|
287
|
-
it('round-trips with hslToRgb within 1 LSB', () => {
|
|
288
|
-
const hsl: [number, number, number] = [0, 0, 0];
|
|
289
|
-
rgbToHsl(hsl, 0x3c8ab5ff);
|
|
290
|
-
const rgb: [number, number, number, number] = [0, 0, 0, 1];
|
|
291
|
-
hslToRgb(rgb, hsl[0], hsl[1], hsl[2]);
|
|
292
|
-
const repacked =
|
|
293
|
-
(Math.round(rgb[0] * 0xff) << 24) | (Math.round(rgb[1] * 0xff) << 16) | (Math.round(rgb[2] * 0xff) << 8) | 0xff;
|
|
294
|
-
const orig = 0x3c8ab5ff >>> 0;
|
|
295
|
-
expect(Math.abs(((repacked >>> 24) & 0xff) - ((orig >>> 24) & 0xff))).toBeLessThanOrEqual(1);
|
|
296
|
-
});
|
|
297
|
-
});
|
|
298
|
-
|
|
299
|
-
describe('rgbToHsv', () => {
|
|
300
|
-
it('converts pure red to hue=0, s=1, v=1', () => {
|
|
301
|
-
const out: [number, number, number] = [0, 0, 0];
|
|
302
|
-
rgbToHsv(out, 0xff0000ff);
|
|
303
|
-
expect(out[0]).toBeCloseTo(0, 3);
|
|
304
|
-
expect(out[1]).toBeCloseTo(1, 5);
|
|
305
|
-
expect(out[2]).toBeCloseTo(1, 5);
|
|
306
|
-
});
|
|
307
|
-
it('converts black to s=0, v=0', () => {
|
|
308
|
-
const out: [number, number, number] = [0, 0, 0];
|
|
309
|
-
rgbToHsv(out, 0x000000ff);
|
|
310
|
-
expect(out[1]).toBe(0);
|
|
311
|
-
expect(out[2]).toBeCloseTo(0, 5);
|
|
312
|
-
});
|
|
313
|
-
it('converts white to s=0, v=1', () => {
|
|
314
|
-
const out: [number, number, number] = [0, 0, 0];
|
|
315
|
-
rgbToHsv(out, 0xffffffff);
|
|
316
|
-
expect(out[1]).toBe(0);
|
|
317
|
-
expect(out[2]).toBeCloseTo(1, 5);
|
|
318
|
-
});
|
|
319
|
-
it('returns the out instance', () => {
|
|
320
|
-
const out: [number, number, number] = [0, 0, 0];
|
|
321
|
-
expect(rgbToHsv(out, 0xff0000ff)).toBe(out);
|
|
322
|
-
});
|
|
323
|
-
});
|
|
324
|
-
|
|
325
|
-
describe('unpackColorRgba', () => {
|
|
326
|
-
it('extracts white as all-ones', () => {
|
|
327
|
-
const out: [number, number, number, number] = [0, 0, 0, 0];
|
|
328
|
-
unpackColorRgba(out, 0xffffffff);
|
|
329
|
-
expect(out[0]).toBeCloseTo(1, 5);
|
|
330
|
-
expect(out[1]).toBeCloseTo(1, 5);
|
|
331
|
-
expect(out[2]).toBeCloseTo(1, 5);
|
|
332
|
-
expect(out[3]).toBeCloseTo(1, 5);
|
|
333
|
-
});
|
|
334
|
-
it('does NOT gamma-decode — mid sRGB stays above the linear midpoint', () => {
|
|
335
|
-
const out: [number, number, number, number] = [0, 0, 0, 0];
|
|
336
|
-
unpackColorRgba(out, 0x808080ff);
|
|
337
|
-
// Without gamma decode, 0x80/0xff ≈ 0.502 — above the linear midpoint.
|
|
338
|
-
expect(out[0]).toBeCloseTo(0x80 / 0xff, 4);
|
|
339
|
-
});
|
|
340
|
-
});
|
|
341
|
-
|
|
342
|
-
describe('unpackColorToLinear', () => {
|
|
343
|
-
it('decodes white and black exactly at the endpoints', () => {
|
|
344
|
-
const out = createLinearColor();
|
|
345
|
-
expect(unpackColorToLinear(out, 0xffffffff)).toEqual([1, 1, 1, 1]);
|
|
346
|
-
expect(unpackColorToLinear(out, 0x000000ff)).toEqual([0, 0, 0, 1]);
|
|
347
|
-
});
|
|
348
|
-
|
|
349
|
-
it('passes alpha through linearly without gamma decode', () => {
|
|
350
|
-
const out = createLinearColor();
|
|
351
|
-
unpackColorToLinear(out, 0x00000080);
|
|
352
|
-
expect(out[3]).toBeCloseTo(0x80 / 0xff, 6);
|
|
353
|
-
});
|
|
354
|
-
|
|
355
|
-
it('gamma-decodes RGB so mid sRgb is below the linear midpoint', () => {
|
|
356
|
-
const out = createLinearColor();
|
|
357
|
-
unpackColorToLinear(out, 0x808080ff);
|
|
358
|
-
expect(out[0]).toBeCloseTo(0.21586, 4);
|
|
359
|
-
expect(out[0]).toBe(out[1]);
|
|
360
|
-
expect(out[1]).toBe(out[2]);
|
|
361
|
-
});
|
|
362
|
-
|
|
363
|
-
it('returns the same out instance it was given', () => {
|
|
364
|
-
const out = createLinearColor();
|
|
365
|
-
expect(unpackColorToLinear(out, 0xff0000ff)).toBe(out);
|
|
366
|
-
});
|
|
367
|
-
});
|
|
368
|
-
|
|
369
|
-
describe('unpremultiplyColorAlpha', () => {
|
|
370
|
-
it('returns fully-opaque color unchanged', () => {
|
|
371
|
-
expect(unpremultiplyColorAlpha(0xff0000ff)).toBe(0xff0000ff);
|
|
372
|
-
});
|
|
373
|
-
it('divides RGB by alpha and preserves the alpha channel', () => {
|
|
374
|
-
// Premultiplied: R=128 (≈0x80), A=0x80 → straight: R ≈ 255
|
|
375
|
-
const premul = premultiplyColorAlpha((0xff0000ff & (0xffffff80 >>> 0)) | 0x80);
|
|
376
|
-
const result = unpremultiplyColorAlpha(premul);
|
|
377
|
-
expect(result & 0xff).toBe(0x80);
|
|
378
|
-
});
|
|
379
|
-
it('returns the input unchanged for alpha=0', () => {
|
|
380
|
-
const color = 0xff000000;
|
|
381
|
-
expect(unpremultiplyColorAlpha(color)).toBe(color);
|
|
382
|
-
});
|
|
383
|
-
});
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { ColorTransformMaterialKind, UniformColorTransformMaterialKind } from '@flighthq/types';
|
|
2
|
-
|
|
3
|
-
import { createColorTransform } from './colorTransform';
|
|
4
|
-
import { createColorTransformMaterial, createUniformColorTransformMaterial } from './colorTransformMaterial';
|
|
5
|
-
|
|
6
|
-
describe('createColorTransformMaterial', () => {
|
|
7
|
-
it('creates a material with the per-instance color transform kind and no value', () => {
|
|
8
|
-
const material = createColorTransformMaterial();
|
|
9
|
-
expect(material.kind).toBe(ColorTransformMaterialKind);
|
|
10
|
-
expect((material as { colorTransform?: unknown }).colorTransform).toBeUndefined();
|
|
11
|
-
});
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
describe('createUniformColorTransformMaterial', () => {
|
|
15
|
-
it('defaults to an identity color transform', () => {
|
|
16
|
-
const material = createUniformColorTransformMaterial();
|
|
17
|
-
expect(material.kind).toBe(UniformColorTransformMaterialKind);
|
|
18
|
-
expect(material.colorTransform.redMultiplier).toBe(1);
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
it('carries the provided color transform', () => {
|
|
22
|
-
const ct = createColorTransform({ greenMultiplier: 0.5 });
|
|
23
|
-
const material = createUniformColorTransformMaterial(ct);
|
|
24
|
-
expect(material.colorTransform).toBe(ct);
|
|
25
|
-
});
|
|
26
|
-
});
|