@flighthq/lighting 0.2.0 → 0.2.1-next.423.3350e53
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/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/lightIntensity.d.ts +5 -0
- package/dist/lightIntensity.d.ts.map +1 -0
- package/dist/lightIntensity.js +47 -0
- package/dist/lightIntensity.js.map +1 -0
- package/package.json +4 -4
- package/src/lightIntensity.test.ts +83 -0
- package/dist/colorFromKelvin.d.ts +0 -2
- package/dist/colorFromKelvin.d.ts.map +0 -1
- package/dist/colorFromKelvin.js +0 -44
- package/dist/colorFromKelvin.js.map +0 -1
- package/src/colorFromKelvin.test.ts +0 -48
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
export * from './ambientLight';
|
|
2
2
|
export * from './areaLight';
|
|
3
|
-
export * from './colorFromKelvin';
|
|
4
3
|
export * from './directionalLight';
|
|
5
4
|
export * from './environment';
|
|
6
5
|
export * from './hemisphereLight';
|
|
7
6
|
export * from './lightAnalysis';
|
|
7
|
+
export * from './lightIntensity';
|
|
8
8
|
export * from './pointLight';
|
|
9
9
|
export * from './sceneLights';
|
|
10
10
|
export * from './spotLight';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC;AAC9B,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
export * from './ambientLight';
|
|
2
2
|
export * from './areaLight';
|
|
3
|
-
export * from './colorFromKelvin';
|
|
4
3
|
export * from './directionalLight';
|
|
5
4
|
export * from './environment';
|
|
6
5
|
export * from './hemisphereLight';
|
|
7
6
|
export * from './lightAnalysis';
|
|
7
|
+
export * from './lightIntensity';
|
|
8
8
|
export * from './pointLight';
|
|
9
9
|
export * from './sceneLights';
|
|
10
10
|
export * from './spotLight';
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC;AAC9B,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { LightUnit } from '@flighthq/types';
|
|
2
|
+
export declare function applyLightExposure(intensity: number, ev: number): number;
|
|
3
|
+
export declare function convertLightIntensity(fromUnit: LightUnit, toUnit: LightUnit, value: number): number;
|
|
4
|
+
export declare function getLightLinearIntensity(unit: LightUnit, value: number): number;
|
|
5
|
+
//# sourceMappingURL=lightIntensity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lightIntensity.d.ts","sourceRoot":"","sources":["../src/lightIntensity.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AASjD,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAExE;AAOD,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAEnG;AAgBD,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAE9E"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { CandelaLightUnit, LumenLightUnit, LuxLightUnit, UnitlessLightUnit } from '@flighthq/types';
|
|
2
|
+
// Scales a light intensity by an exposure value in photographic stops: each +1 EV doubles the
|
|
3
|
+
// intensity, each -1 halves it (`intensity * 2**ev`). This is the porting dial for the linear-HDR
|
|
4
|
+
// contract described in this package's status.md: scene-gl multiplies LINEAR radiance and defers
|
|
5
|
+
// tonemap/gamma to the effect resolve pass, so intensities authored in a gamma-space (sRgb/LDR)
|
|
6
|
+
// engine read too dark here. Nudge them up in stops — directional lights empirically need ~+1.5 to
|
|
7
|
+
// +3 EV (~3-8x), ambient ~+0.5 to +1 EV (~1.5-2x) — without hand-editing every descriptor.
|
|
8
|
+
export function applyLightExposure(intensity, ev) {
|
|
9
|
+
return intensity * 2 ** ev;
|
|
10
|
+
}
|
|
11
|
+
// Restates a photometric `value` given in `fromUnit` as the equivalent reading in `toUnit`, pivoting
|
|
12
|
+
// through the renderer's native linear scale. Round-trips: converting a value out to another unit and
|
|
13
|
+
// back returns the original. Because the pivot is Flight's linear multiplier, this is a superset of
|
|
14
|
+
// getLightLinearIntensity (which is the fromUnit -> Unitless case). See getLightLinearIntensity for
|
|
15
|
+
// the reference-normalization anchors and their deliberate limits.
|
|
16
|
+
export function convertLightIntensity(fromUnit, toUnit, value) {
|
|
17
|
+
return getLightLinearIntensity(fromUnit, value) / LINEAR_PER_UNIT[toUnit];
|
|
18
|
+
}
|
|
19
|
+
// Maps a `value` expressed in a photometric `unit` to the dimensionless linear multiplier the scene
|
|
20
|
+
// shaders expect (the `intensity` field on light descriptors). `Unitless` passes through 1:1 — it IS
|
|
21
|
+
// the renderer's native scale. The three photometric units divide by a documented reference anchor:
|
|
22
|
+
// the physical reading that corresponds to linear 1.0.
|
|
23
|
+
//
|
|
24
|
+
// The anchors are renderer-normalization defaults, NOT physically exact conversions. A true
|
|
25
|
+
// photometric conversion needs geometry Flight does not have at this layer (illuminance depends on
|
|
26
|
+
// distance; luminous flux on emission solid angle) and an absolute exposure Flight deliberately
|
|
27
|
+
// defers to the tonemap pass. So: Lux (illuminance, for directional/sun lights) and Candela
|
|
28
|
+
// (luminous intensity, for point/spot lights) are each anchored independently at 100000 = linear 1.0
|
|
29
|
+
// (bright-daylight magnitude), and Lumen (luminous flux) derives from Candela via the ONE genuine
|
|
30
|
+
// physical relationship encodable without geometry — an isotropic point source emits
|
|
31
|
+
// `lumen = candela * 4*PI` over the full sphere. Revisit these anchors if a physically-based exposure
|
|
32
|
+
// model lands; they are chosen to be memorable and to land typical values near 1, not to be exact.
|
|
33
|
+
export function getLightLinearIntensity(unit, value) {
|
|
34
|
+
return value * LINEAR_PER_UNIT[unit];
|
|
35
|
+
}
|
|
36
|
+
// The renderer-linear multiplier produced by one unit of each LightUnit. Unitless is the native
|
|
37
|
+
// scale (1:1). Lux and Candela anchor at 1 / 100000 (100000 physical units == linear 1.0). Lumen
|
|
38
|
+
// follows Candela through the isotropic-point-source identity lumen = candela * 4*PI, so one lumen is
|
|
39
|
+
// 1 / (4*PI) of a candela's linear contribution. See getLightLinearIntensity for the rationale.
|
|
40
|
+
const REFERENCE_PHOTOMETRIC_LEVEL = 100000;
|
|
41
|
+
const LINEAR_PER_UNIT = {
|
|
42
|
+
[CandelaLightUnit]: 1 / REFERENCE_PHOTOMETRIC_LEVEL,
|
|
43
|
+
[LumenLightUnit]: 1 / (REFERENCE_PHOTOMETRIC_LEVEL * 4 * Math.PI),
|
|
44
|
+
[LuxLightUnit]: 1 / REFERENCE_PHOTOMETRIC_LEVEL,
|
|
45
|
+
[UnitlessLightUnit]: 1,
|
|
46
|
+
};
|
|
47
|
+
//# sourceMappingURL=lightIntensity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lightIntensity.js","sourceRoot":"","sources":["../src/lightIntensity.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEpG,8FAA8F;AAC9F,kGAAkG;AAClG,iGAAiG;AACjG,gGAAgG;AAChG,mGAAmG;AACnG,2FAA2F;AAC3F,MAAM,UAAU,kBAAkB,CAAC,SAAiB,EAAE,EAAU;IAC9D,OAAO,SAAS,GAAG,CAAC,IAAI,EAAE,CAAC;AAC7B,CAAC;AAED,qGAAqG;AACrG,sGAAsG;AACtG,oGAAoG;AACpG,oGAAoG;AACpG,mEAAmE;AACnE,MAAM,UAAU,qBAAqB,CAAC,QAAmB,EAAE,MAAiB,EAAE,KAAa;IACzF,OAAO,uBAAuB,CAAC,QAAQ,EAAE,KAAK,CAAC,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;AAC5E,CAAC;AAED,oGAAoG;AACpG,qGAAqG;AACrG,oGAAoG;AACpG,uDAAuD;AACvD,EAAE;AACF,4FAA4F;AAC5F,mGAAmG;AACnG,gGAAgG;AAChG,4FAA4F;AAC5F,qGAAqG;AACrG,kGAAkG;AAClG,qFAAqF;AACrF,sGAAsG;AACtG,mGAAmG;AACnG,MAAM,UAAU,uBAAuB,CAAC,IAAe,EAAE,KAAa;IACpE,OAAO,KAAK,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC;AACvC,CAAC;AAED,gGAAgG;AAChG,iGAAiG;AACjG,sGAAsG;AACtG,gGAAgG;AAChG,MAAM,2BAA2B,GAAG,MAAM,CAAC;AAC3C,MAAM,eAAe,GAAwC;IAC3D,CAAC,gBAAgB,CAAC,EAAE,CAAC,GAAG,2BAA2B;IACnD,CAAC,cAAc,CAAC,EAAE,CAAC,GAAG,CAAC,2BAA2B,GAAG,CAAC,GAAG,IAAI,CAAC,EAAE,CAAC;IACjE,CAAC,YAAY,CAAC,EAAE,CAAC,GAAG,2BAA2B;IAC/C,CAAC,iBAAiB,CAAC,EAAE,CAAC;CACvB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flighthq/lighting",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1-next.423.3350e53",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/flighthq/flight.git",
|
|
@@ -32,9 +32,9 @@
|
|
|
32
32
|
"clean:dist": "tsx ../../scripts/clean-package-dist.ts"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@flighthq/entity": "0.2.
|
|
36
|
-
"@flighthq/geometry": "0.2.
|
|
37
|
-
"@flighthq/types": "0.2.
|
|
35
|
+
"@flighthq/entity": "0.2.1-next.423.3350e53",
|
|
36
|
+
"@flighthq/geometry": "0.2.1-next.423.3350e53",
|
|
37
|
+
"@flighthq/types": "0.2.1-next.423.3350e53"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"typescript": "^5.3.0"
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { CandelaLightUnit, LumenLightUnit, LuxLightUnit, UnitlessLightUnit } from '@flighthq/types';
|
|
2
|
+
|
|
3
|
+
import { applyLightExposure, convertLightIntensity, getLightLinearIntensity } from './lightIntensity';
|
|
4
|
+
|
|
5
|
+
describe('applyLightExposure', () => {
|
|
6
|
+
it('returns the intensity unchanged at 0 EV', () => {
|
|
7
|
+
expect(applyLightExposure(1, 0)).toBe(1);
|
|
8
|
+
expect(applyLightExposure(3.5, 0)).toBe(3.5);
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it('doubles per +1 EV stop', () => {
|
|
12
|
+
expect(applyLightExposure(1, 1)).toBe(2);
|
|
13
|
+
expect(applyLightExposure(1, 3)).toBe(8);
|
|
14
|
+
expect(applyLightExposure(2, 2)).toBe(8);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('halves per -1 EV stop', () => {
|
|
18
|
+
expect(applyLightExposure(1, -1)).toBe(0.5);
|
|
19
|
+
expect(applyLightExposure(8, -3)).toBe(1);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('accepts fractional stops', () => {
|
|
23
|
+
expect(applyLightExposure(1, 0.5)).toBeCloseTo(Math.SQRT2, 10);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('is symmetric: +ev then -ev round-trips', () => {
|
|
27
|
+
const boosted = applyLightExposure(1.7, 2.5);
|
|
28
|
+
expect(applyLightExposure(boosted, -2.5)).toBeCloseTo(1.7, 10);
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
describe('convertLightIntensity', () => {
|
|
33
|
+
it('is identity when fromUnit equals toUnit', () => {
|
|
34
|
+
expect(convertLightIntensity(LuxLightUnit, LuxLightUnit, 500)).toBeCloseTo(500, 10);
|
|
35
|
+
expect(convertLightIntensity(CandelaLightUnit, CandelaLightUnit, 42)).toBeCloseTo(42, 10);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('round-trips through an intermediate unit', () => {
|
|
39
|
+
const asCandela = convertLightIntensity(LuxLightUnit, CandelaLightUnit, 1234);
|
|
40
|
+
expect(convertLightIntensity(CandelaLightUnit, LuxLightUnit, asCandela)).toBeCloseTo(1234, 6);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('maps Lux and Candela identically (shared anchor)', () => {
|
|
44
|
+
expect(convertLightIntensity(LuxLightUnit, CandelaLightUnit, 777)).toBeCloseTo(777, 10);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('relates Lumen and Candela by the isotropic 4*PI factor', () => {
|
|
48
|
+
// A candela over the full sphere is 4*PI lumens.
|
|
49
|
+
expect(convertLightIntensity(CandelaLightUnit, LumenLightUnit, 1)).toBeCloseTo(4 * Math.PI, 10);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('agrees with getLightLinearIntensity for the Unitless target', () => {
|
|
53
|
+
expect(convertLightIntensity(CandelaLightUnit, UnitlessLightUnit, 50000)).toBeCloseTo(
|
|
54
|
+
getLightLinearIntensity(CandelaLightUnit, 50000),
|
|
55
|
+
10,
|
|
56
|
+
);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
describe('getLightLinearIntensity', () => {
|
|
61
|
+
it('passes Unitless through 1:1', () => {
|
|
62
|
+
expect(getLightLinearIntensity(UnitlessLightUnit, 1)).toBe(1);
|
|
63
|
+
expect(getLightLinearIntensity(UnitlessLightUnit, 6.25)).toBe(6.25);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('anchors 100000 lux to linear 1.0', () => {
|
|
67
|
+
expect(getLightLinearIntensity(LuxLightUnit, 100000)).toBeCloseTo(1, 10);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('anchors 100000 candela to linear 1.0', () => {
|
|
71
|
+
expect(getLightLinearIntensity(CandelaLightUnit, 100000)).toBeCloseTo(1, 10);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('scales linearly with the value', () => {
|
|
75
|
+
expect(getLightLinearIntensity(LuxLightUnit, 50000)).toBeCloseTo(0.5, 10);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('treats one lumen as 1 / (4*PI) of a candela contribution', () => {
|
|
79
|
+
const perCandela = getLightLinearIntensity(CandelaLightUnit, 1);
|
|
80
|
+
const perLumen = getLightLinearIntensity(LumenLightUnit, 1);
|
|
81
|
+
expect(perLumen).toBeCloseTo(perCandela / (4 * Math.PI), 12);
|
|
82
|
+
});
|
|
83
|
+
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"colorFromKelvin.d.ts","sourceRoot":"","sources":["../src/colorFromKelvin.ts"],"names":[],"mappings":"AAOA,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAqC5D"}
|
package/dist/colorFromKelvin.js
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
// Creates a packed sRgb-albedo RGBA color (0xrrggbbaa) from a color temperature in Kelvin.
|
|
2
|
-
// Uses the Tanner Helland piecewise approximation (accurate to within ±1% for 1000 K–40000 K),
|
|
3
|
-
// the same algorithm used in Blender, three.js, and Filament's color-temperature helper.
|
|
4
|
-
// Returns opaque white (0xffffffff) for temperatures outside the 1000–40000 K range.
|
|
5
|
-
//
|
|
6
|
-
// Common temperatures: 1800 K = candlelight, 3000 K = warm bulb, 5500 K = noon sunlight,
|
|
7
|
-
// 6500 K = D65 white, 10000 K = overcast sky.
|
|
8
|
-
export function createColorFromKelvin(kelvin) {
|
|
9
|
-
// Clamp to the valid range.
|
|
10
|
-
const temp = Math.max(1000, Math.min(40000, kelvin)) / 100;
|
|
11
|
-
let r;
|
|
12
|
-
let g;
|
|
13
|
-
let b;
|
|
14
|
-
// Red channel.
|
|
15
|
-
if (temp <= 66) {
|
|
16
|
-
r = 255;
|
|
17
|
-
}
|
|
18
|
-
else {
|
|
19
|
-
r = 329.698727446 * Math.pow(temp - 60, -0.1332047592);
|
|
20
|
-
}
|
|
21
|
-
// Green channel.
|
|
22
|
-
if (temp <= 66) {
|
|
23
|
-
g = 99.4708025861 * Math.log(temp) - 161.1195681661;
|
|
24
|
-
}
|
|
25
|
-
else {
|
|
26
|
-
g = 288.1221695283 * Math.pow(temp - 60, -0.0755148492);
|
|
27
|
-
}
|
|
28
|
-
// Blue channel.
|
|
29
|
-
if (temp >= 66) {
|
|
30
|
-
b = 255;
|
|
31
|
-
}
|
|
32
|
-
else if (temp <= 19) {
|
|
33
|
-
b = 0;
|
|
34
|
-
}
|
|
35
|
-
else {
|
|
36
|
-
b = 138.5177312231 * Math.log(temp - 10) - 305.0447927307;
|
|
37
|
-
}
|
|
38
|
-
const ri = Math.max(0, Math.min(255, Math.round(r)));
|
|
39
|
-
const gi = Math.max(0, Math.min(255, Math.round(g)));
|
|
40
|
-
const bi = Math.max(0, Math.min(255, Math.round(b)));
|
|
41
|
-
// Pack as 0xrrggbbaa with fully opaque alpha.
|
|
42
|
-
return ((ri << 24) | (gi << 16) | (bi << 8) | 0xff) >>> 0;
|
|
43
|
-
}
|
|
44
|
-
//# sourceMappingURL=colorFromKelvin.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"colorFromKelvin.js","sourceRoot":"","sources":["../src/colorFromKelvin.ts"],"names":[],"mappings":"AAAA,2FAA2F;AAC3F,+FAA+F;AAC/F,yFAAyF;AACzF,qFAAqF;AACrF,EAAE;AACF,yFAAyF;AACzF,8CAA8C;AAC9C,MAAM,UAAU,qBAAqB,CAAC,MAAc;IAClD,4BAA4B;IAC5B,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,GAAG,GAAG,CAAC;IAE3D,IAAI,CAAS,CAAC;IACd,IAAI,CAAS,CAAC;IACd,IAAI,CAAS,CAAC;IAEd,eAAe;IACf,IAAI,IAAI,IAAI,EAAE,EAAE,CAAC;QACf,CAAC,GAAG,GAAG,CAAC;IACV,CAAC;SAAM,CAAC;QACN,CAAC,GAAG,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG,EAAE,EAAE,CAAC,YAAY,CAAC,CAAC;IACzD,CAAC;IAED,iBAAiB;IACjB,IAAI,IAAI,IAAI,EAAE,EAAE,CAAC;QACf,CAAC,GAAG,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,cAAc,CAAC;IACtD,CAAC;SAAM,CAAC;QACN,CAAC,GAAG,cAAc,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG,EAAE,EAAE,CAAC,YAAY,CAAC,CAAC;IAC1D,CAAC;IAED,gBAAgB;IAChB,IAAI,IAAI,IAAI,EAAE,EAAE,CAAC;QACf,CAAC,GAAG,GAAG,CAAC;IACV,CAAC;SAAM,IAAI,IAAI,IAAI,EAAE,EAAE,CAAC;QACtB,CAAC,GAAG,CAAC,CAAC;IACR,CAAC;SAAM,CAAC;QACN,CAAC,GAAG,cAAc,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG,EAAE,CAAC,GAAG,cAAc,CAAC;IAC5D,CAAC;IAED,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACrD,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACrD,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAErD,8CAA8C;IAC9C,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;AAC5D,CAAC"}
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { createColorFromKelvin } from './colorFromKelvin';
|
|
2
|
-
|
|
3
|
-
describe('createColorFromKelvin', () => {
|
|
4
|
-
it('returns opaque alpha (0xff) for all temperatures', () => {
|
|
5
|
-
expect(createColorFromKelvin(3000) & 0xff).toBe(0xff);
|
|
6
|
-
expect(createColorFromKelvin(6500) & 0xff).toBe(0xff);
|
|
7
|
-
expect(createColorFromKelvin(10000) & 0xff).toBe(0xff);
|
|
8
|
-
});
|
|
9
|
-
|
|
10
|
-
it('returns white (0xffffffff) for D65 daylight (~6500 K)', () => {
|
|
11
|
-
const color = createColorFromKelvin(6500);
|
|
12
|
-
const r = (color >>> 24) & 0xff;
|
|
13
|
-
const g = (color >>> 16) & 0xff;
|
|
14
|
-
const b = (color >>> 8) & 0xff;
|
|
15
|
-
// D65 should be close to neutral white — all channels near 255.
|
|
16
|
-
expect(r).toBeGreaterThan(240);
|
|
17
|
-
expect(g).toBeGreaterThan(240);
|
|
18
|
-
expect(b).toBeGreaterThan(230);
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
it('produces warmer (more red, less blue) color for lower temperatures', () => {
|
|
22
|
-
const warm = createColorFromKelvin(2000);
|
|
23
|
-
const cool = createColorFromKelvin(10000);
|
|
24
|
-
const warmR = (warm >>> 24) & 0xff;
|
|
25
|
-
const warmB = (warm >>> 8) & 0xff;
|
|
26
|
-
const coolR = (cool >>> 24) & 0xff;
|
|
27
|
-
const coolB = (cool >>> 8) & 0xff;
|
|
28
|
-
// Warm candlelight should have more red and less blue than cool sky light.
|
|
29
|
-
expect(warmR).toBeGreaterThanOrEqual(coolR);
|
|
30
|
-
expect(warmB).toBeLessThanOrEqual(coolB);
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it('clamps inputs below 1000 K to 1000 K', () => {
|
|
34
|
-
expect(createColorFromKelvin(500)).toBe(createColorFromKelvin(1000));
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it('clamps inputs above 40000 K to 40000 K', () => {
|
|
38
|
-
expect(createColorFromKelvin(50000)).toBe(createColorFromKelvin(40000));
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
it('returns a valid packed RGBA integer (unsigned 32-bit)', () => {
|
|
42
|
-
const color = createColorFromKelvin(5500);
|
|
43
|
-
expect(color).toBeGreaterThanOrEqual(0);
|
|
44
|
-
expect(color).toBeLessThanOrEqual(0xffffffff);
|
|
45
|
-
// Must be an integer.
|
|
46
|
-
expect(color % 1).toBe(0);
|
|
47
|
-
});
|
|
48
|
-
});
|