@photivra/engine 2.4.1 → 2.6.0
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/CHANGELOG.md +53 -1
- package/CITATION.cff +1 -1
- package/README.md +6 -6
- package/SECURITY.md +1 -1
- package/THIRD_PARTY.md +1 -0
- package/dist/capture/native-environment-lean.d.ts +1 -1
- package/dist/capture/native-environment-lean.js +17 -3
- package/dist/capture/native-environment-lean.js.map +1 -1
- package/dist/capture/native-environment-raw.d.ts +0 -4
- package/dist/capture/native-environment-raw.js +6 -2
- package/dist/capture/native-environment-raw.js.map +1 -1
- package/dist/color/illuminants.d.ts +15 -0
- package/dist/color/illuminants.js +81 -24
- package/dist/color/illuminants.js.map +1 -1
- package/dist/color/srgb-transfer.d.ts +6 -0
- package/dist/color/srgb-transfer.js +9 -0
- package/dist/color/srgb-transfer.js.map +1 -1
- package/dist/color/white-balance.d.ts +6 -0
- package/dist/color/white-balance.js +15 -2
- package/dist/color/white-balance.js.map +1 -1
- package/dist/core/internal-radiance-evaluator.d.ts +6 -0
- package/dist/core/internal-radiance-evaluator.js +33 -0
- package/dist/core/internal-radiance-evaluator.js.map +1 -0
- package/dist/core/portable-math.d.ts +33 -0
- package/dist/core/portable-math.js +342 -0
- package/dist/core/portable-math.js.map +1 -1
- package/dist/core/version.d.ts +1 -1
- package/dist/core/version.js +1 -1
- package/dist/core/version.js.map +1 -1
- package/dist/facade/chart-scenes.d.ts +9 -0
- package/dist/facade/chart-scenes.js +17 -4
- package/dist/facade/chart-scenes.js.map +1 -1
- package/dist/facade/diffraction-provider.d.ts +10 -0
- package/dist/facade/diffraction-provider.js +38 -17
- package/dist/facade/diffraction-provider.js.map +1 -1
- package/dist/facade/display-view.d.ts +208 -0
- package/dist/facade/display-view.js +1461 -0
- package/dist/facade/display-view.js.map +1 -0
- package/dist/facade/scene-radiance.d.ts +7 -0
- package/dist/facade/scene-radiance.js +18 -5
- package/dist/facade/scene-radiance.js.map +1 -1
- package/dist/facade/simulate-capture.d.ts +229 -5
- package/dist/facade/simulate-capture.js +119 -35
- package/dist/facade/simulate-capture.js.map +1 -1
- package/dist/facade/simulate-display-view.d.ts +21 -0
- package/dist/facade/simulate-display-view.js +168 -0
- package/dist/facade/simulate-display-view.js.map +1 -0
- package/dist/facade/subject-framing.d.ts +78 -0
- package/dist/facade/subject-framing.js +69 -20
- package/dist/facade/subject-framing.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/optics/diffraction-psf-profile.d.ts +59 -3
- package/dist/optics/diffraction-psf-profile.js +147 -5
- package/dist/optics/diffraction-psf-profile.js.map +1 -1
- package/dist/optics/irradiance-convolution.js +1 -1
- package/dist/optics/irradiance-convolution.js.map +1 -1
- package/dist/optics/scene-to-sensor-irradiance.d.ts +7 -0
- package/dist/optics/scene-to-sensor-irradiance.js +10 -5
- package/dist/optics/scene-to-sensor-irradiance.js.map +1 -1
- package/dist/output/print-plan.d.ts +8 -1
- package/dist/output/print-plan.js +8 -1
- package/dist/output/print-plan.js.map +1 -1
- package/dist/schema/scene-radiance.d.ts +16 -0
- package/dist/schema/scene-radiance.js +25 -4
- package/dist/schema/scene-radiance.js.map +1 -1
- package/dist/sensor/raw-readout.d.ts +12 -0
- package/dist/sensor/raw-readout.js +131 -9
- package/dist/sensor/raw-readout.js.map +1 -1
- package/dist/sensor/stochastic-seed.d.ts +13 -0
- package/dist/sensor/stochastic-seed.js +42 -8
- package/dist/sensor/stochastic-seed.js.map +1 -1
- package/docs/AGENT_GUIDE.md +1 -0
- package/docs/API_REFERENCE.md +16 -1
- package/docs/QUICK_START.md +245 -9
- package/docs/USAGE.md +15 -0
- package/docs/api/core-version.md +1 -1
- package/docs/api/exports.json +37 -2
- package/docs/api/facade-simulate-capture.md +70 -2
- package/docs/api/facade-simulate-display-view.md +18 -0
- package/docs/api/facade-subject-framing.md +83 -0
- package/docs/api/optics-diffraction-psf-profile.md +8 -1
- package/docs/api/output-print-plan.md +8 -1
- package/package.json +3 -2
|
@@ -33,6 +33,7 @@ import { COLORIMETRY_MODEL_VERSION, integrateSampledXyz, prepareColorMatchingFun
|
|
|
33
33
|
import { chromaticityOfUv, chromaticityOfXyz, parseChromaticity, requireFiniteNumber, requirePositiveNumber, requireRecordValue, uvOfChromaticity } from "./colorimetry-math.js";
|
|
34
34
|
import { rejectUnknownFields, requireInputKeys } from "../core/record-validation.js";
|
|
35
35
|
import { InvalidConfigurationError } from "../core/configuration-error.js";
|
|
36
|
+
import { portableExpm1, portableHypot, portablePow } from "../core/portable-math.js";
|
|
36
37
|
/** Exact SI defining constants (BIPM SI Brochure, 9th edition, 2019). */
|
|
37
38
|
export const PLANCK_CONSTANT_JOULE_SECONDS = 6.62607015e-34;
|
|
38
39
|
export const SPEED_OF_LIGHT_METERS_PER_SECOND = 299_792_458;
|
|
@@ -40,6 +41,9 @@ export const BOLTZMANN_CONSTANT_JOULES_PER_KELVIN = 1.380649e-23;
|
|
|
40
41
|
/** Second radiation constant c₂ = hc/k in m·K (derived from the exact constants). */
|
|
41
42
|
export const SECOND_RADIATION_CONSTANT_METER_KELVIN = (PLANCK_CONSTANT_JOULE_SECONDS * SPEED_OF_LIGHT_METERS_PER_SECOND) / BOLTZMANN_CONSTANT_JOULES_PER_KELVIN;
|
|
42
43
|
const FIRST_RADIATION_CONSTANT_RADIANCE = 2 * PLANCK_CONSTANT_JOULE_SECONDS * SPEED_OF_LIGHT_METERS_PER_SECOND ** 2;
|
|
44
|
+
/** The same constant with c² as one correctly rounded product (the portable evaluation below). */
|
|
45
|
+
const FIRST_RADIATION_CONSTANT_RADIANCE_PORTABLE = 2 * PLANCK_CONSTANT_JOULE_SECONDS * (SPEED_OF_LIGHT_METERS_PER_SECOND * SPEED_OF_LIGHT_METERS_PER_SECOND);
|
|
46
|
+
const PORTABLE = { pow: portablePow, expm1: portableExpm1, hypot: portableHypot };
|
|
43
47
|
/** Temperature range of the Planckian-locus models, K. */
|
|
44
48
|
export const PLANCKIAN_LOCUS_RANGE_KELVIN = Object.freeze({ minimum: 1000, maximum: 100000 });
|
|
45
49
|
/** CIE daylight-locus validity, K. */
|
|
@@ -57,14 +61,30 @@ export function calculatePlanckianSpectralRadiance(input) {
|
|
|
57
61
|
requireInputKeys(input, "calculatePlanckianSpectralRadiance", ["temperatureKelvin", "wavelengthNanometers"]);
|
|
58
62
|
const t = requirePositiveNumber(input.temperatureKelvin, "temperatureKelvin");
|
|
59
63
|
const nm = requirePositiveNumber(input.wavelengthNanometers, "wavelengthNanometers");
|
|
64
|
+
return planckianRadiancePerNanometer(t, nm, undefined);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Planck's law at temperature t (K) and wavelength nm. `portable` evaluates λ⁵ and e^x − 1 with the engine's portable
|
|
68
|
+
* functions (`portablePow`, `portableExpm1`; within 1 and 2 ulps) instead of `**` and Math.expm1, whose last bits differ
|
|
69
|
+
* between JavaScript engines (V8 12 and V8 13 differ in about 10 % of λ⁵); the display view uses it.
|
|
70
|
+
*/
|
|
71
|
+
function planckianRadiancePerNanometer(t, nm, portable) {
|
|
60
72
|
const lambda = nm * 1e-9;
|
|
61
73
|
const exponent = SECOND_RADIATION_CONSTANT_METER_KELVIN / (lambda * t);
|
|
62
74
|
// expm1 keeps precision in the Rayleigh–Jeans limit; overflow gives radiance 0.
|
|
63
|
-
const radiancePerMeter =
|
|
75
|
+
const radiancePerMeter = portable ? FIRST_RADIATION_CONSTANT_RADIANCE_PORTABLE / portable.pow(lambda, 5) / portable.expm1(exponent)
|
|
76
|
+
: FIRST_RADIATION_CONSTANT_RADIANCE / lambda ** 5 / Math.expm1(exponent);
|
|
64
77
|
return radiancePerMeter * 1e-9;
|
|
65
78
|
}
|
|
66
79
|
/** Planckian spectral radiance (W·m⁻²·sr⁻¹·nm⁻¹) as a function spectrum over the given range. */
|
|
67
80
|
export function createPlanckianSpectrum(input) {
|
|
81
|
+
return planckianSpectrum(input, undefined);
|
|
82
|
+
}
|
|
83
|
+
/** Internal (not exported from the package): {@link createPlanckianSpectrum} evaluated with the portable functions (the display view). */
|
|
84
|
+
export function createPortablePlanckianSpectrum(input) {
|
|
85
|
+
return planckianSpectrum(input, PORTABLE);
|
|
86
|
+
}
|
|
87
|
+
function planckianSpectrum(input, portable) {
|
|
68
88
|
requireInputKeys(input, "createPlanckianSpectrum", ["temperatureKelvin", "wavelengthRangeNanometers"]);
|
|
69
89
|
const temperatureKelvin = requirePositiveNumber(input.temperatureKelvin, "temperatureKelvin");
|
|
70
90
|
const range = requireRecordValue(input.wavelengthRangeNanometers, "wavelengthRangeNanometers");
|
|
@@ -72,7 +92,8 @@ export function createPlanckianSpectrum(input) {
|
|
|
72
92
|
const spectrum = {
|
|
73
93
|
kind: "function",
|
|
74
94
|
wavelengthRangeNanometers: { minimum: range.minimum, maximum: range.maximum },
|
|
75
|
-
evaluate: (wavelengthNanometers) =>
|
|
95
|
+
evaluate: portable ? (wavelengthNanometers) => planckianRadiancePerNanometer(temperatureKelvin, requirePositiveNumber(wavelengthNanometers, "wavelengthNanometers"), portable)
|
|
96
|
+
: (wavelengthNanometers) => calculatePlanckianSpectralRadiance({ temperatureKelvin, wavelengthNanometers })
|
|
76
97
|
};
|
|
77
98
|
prepareColorimetricSpectrum(spectrum, "planckianSpectrum");
|
|
78
99
|
return spectrum;
|
|
@@ -85,17 +106,25 @@ export const CIE_ILLUMINANT_A_RANGE_NANOMETERS = Object.freeze({ minimum: 300, m
|
|
|
85
106
|
* current c₂). Valid 300–830 nm.
|
|
86
107
|
*/
|
|
87
108
|
export function calculateCieIlluminantARelativeSpectralPower(wavelengthNanometers) {
|
|
109
|
+
return cieIlluminantA(wavelengthNanometers, undefined);
|
|
110
|
+
}
|
|
111
|
+
/** CIE illuminant A; `portable` evaluates the power and e^x − 1 with the portable functions (see planckianRadiancePerNanometer). */
|
|
112
|
+
function cieIlluminantA(wavelengthNanometers, portable) {
|
|
88
113
|
const nm = requireFiniteNumber(wavelengthNanometers, "wavelengthNanometers");
|
|
89
114
|
if (nm < CIE_ILLUMINANT_A_RANGE_NANOMETERS.minimum || nm > CIE_ILLUMINANT_A_RANGE_NANOMETERS.maximum) {
|
|
90
115
|
throw new InvalidScientificInputError("CIE illuminant A is defined from 300 nm to 830 nm.");
|
|
91
116
|
}
|
|
92
117
|
const c = 1.435e7 / 2848; // nm·K / K
|
|
93
|
-
return 100 * (560 / nm) ** 5 * (Math.expm1(c / 560) / Math.expm1(c / nm));
|
|
118
|
+
return portable ? 100 * portable.pow(560 / nm, 5) * (portable.expm1(c / 560) / portable.expm1(c / nm)) : 100 * (560 / nm) ** 5 * (Math.expm1(c / 560) / Math.expm1(c / nm));
|
|
94
119
|
}
|
|
95
120
|
/** CIE illuminant A as a function spectrum over its defining range. */
|
|
96
121
|
export function createCieIlluminantASpectrum() {
|
|
97
122
|
return { kind: "function", wavelengthRangeNanometers: { ...CIE_ILLUMINANT_A_RANGE_NANOMETERS }, evaluate: calculateCieIlluminantARelativeSpectralPower };
|
|
98
123
|
}
|
|
124
|
+
/** Internal (not exported from the package): {@link createCieIlluminantASpectrum} evaluated with the portable functions (the display view). */
|
|
125
|
+
export function createPortableCieIlluminantASpectrum() {
|
|
126
|
+
return { kind: "function", wavelengthRangeNanometers: { ...CIE_ILLUMINANT_A_RANGE_NANOMETERS }, evaluate: (nm) => cieIlluminantA(nm, PORTABLE) };
|
|
127
|
+
}
|
|
99
128
|
function requireTemperatureIn(t, range, what) {
|
|
100
129
|
const value = requirePositiveNumber(t, what);
|
|
101
130
|
if (value < range.minimum || value > range.maximum) {
|
|
@@ -103,10 +132,11 @@ function requireTemperatureIn(t, range, what) {
|
|
|
103
132
|
}
|
|
104
133
|
return value;
|
|
105
134
|
}
|
|
106
|
-
function daylightChromaticity(t) {
|
|
135
|
+
function daylightChromaticity(t, portable) {
|
|
136
|
+
const t3 = portable ? portable.pow(t, 3) : t ** 3, t2 = portable ? t * t : t ** 2;
|
|
107
137
|
const x = t <= 7000
|
|
108
|
-
? -4.6070e9 /
|
|
109
|
-
: -2.0064e9 /
|
|
138
|
+
? -4.6070e9 / t3 + 2.9678e6 / t2 + 0.09911e3 / t + 0.244063
|
|
139
|
+
: -2.0064e9 / t3 + 1.9018e6 / t2 + 0.24748e3 / t + 0.237040;
|
|
110
140
|
return { x, y: -3.000 * x * x + 2.870 * x - 0.275 };
|
|
111
141
|
}
|
|
112
142
|
/**
|
|
@@ -117,12 +147,15 @@ function daylightChromaticity(t) {
|
|
|
117
147
|
* The temperature is used as given: D65's nominal 6500 K corresponds to 6504 K here.
|
|
118
148
|
*/
|
|
119
149
|
export function calculateCieDaylightComponentWeights(input) {
|
|
150
|
+
return cieDaylightComponentWeights(input, undefined);
|
|
151
|
+
}
|
|
152
|
+
function cieDaylightComponentWeights(input, portable) {
|
|
120
153
|
requireInputKeys(input, "calculateCieDaylightComponentWeights", ["correlatedColorTemperatureKelvin", "coefficientRounding"]);
|
|
121
154
|
const t = requireTemperatureIn(input.correlatedColorTemperatureKelvin, CIE_DAYLIGHT_LOCUS_RANGE_KELVIN, "correlatedColorTemperatureKelvin");
|
|
122
155
|
if (input.coefficientRounding !== "none" && input.coefficientRounding !== "cie-three-decimals") {
|
|
123
156
|
throw new InvalidConfigurationError("coefficientRounding must be \"none\" or \"cie-three-decimals\".");
|
|
124
157
|
}
|
|
125
|
-
const xy = daylightChromaticity(t);
|
|
158
|
+
const xy = daylightChromaticity(t, portable);
|
|
126
159
|
const m = 0.0241 + 0.2562 * xy.x - 0.7341 * xy.y;
|
|
127
160
|
let m1 = (-1.3515 - 1.7703 * xy.x + 5.9114 * xy.y) / m;
|
|
128
161
|
let m2 = (0.0300 - 31.4424 * xy.x + 30.0717 * xy.y) / m;
|
|
@@ -137,11 +170,18 @@ export function calculateCieDaylightComponentWeights(input) {
|
|
|
137
170
|
* caller-supplied basis tables (S₁, S₂ linearly interpolated; fail closed outside range).
|
|
138
171
|
*/
|
|
139
172
|
export function createCieDaylightSpectrum(input) {
|
|
173
|
+
return cieDaylightSpectrum(input, undefined);
|
|
174
|
+
}
|
|
175
|
+
/** Internal (not exported from the package): {@link createCieDaylightSpectrum} with the portable daylight-locus powers (the display view). */
|
|
176
|
+
export function createPortableCieDaylightSpectrum(input) {
|
|
177
|
+
return cieDaylightSpectrum(input, PORTABLE);
|
|
178
|
+
}
|
|
179
|
+
function cieDaylightSpectrum(input, portable) {
|
|
140
180
|
requireInputKeys(input, "createCieDaylightSpectrum", ["correlatedColorTemperatureKelvin", "coefficientRounding", "basis"]);
|
|
141
|
-
const weights =
|
|
181
|
+
const weights = cieDaylightComponentWeights({
|
|
142
182
|
correlatedColorTemperatureKelvin: input.correlatedColorTemperatureKelvin,
|
|
143
183
|
coefficientRounding: input.coefficientRounding
|
|
144
|
-
}).value;
|
|
184
|
+
}, portable).value;
|
|
145
185
|
const basis = requireRecordValue(input.basis, "basis");
|
|
146
186
|
rejectUnknownFields(basis, ["s0", "s1", "s2", "datasetId"], "basis");
|
|
147
187
|
if (typeof basis.datasetId !== "string" || basis.datasetId.trim().length === 0) {
|
|
@@ -171,7 +211,9 @@ const CCT_RELATIVE_TOLERANCE = 1e-10;
|
|
|
171
211
|
* λᵢ⁻⁵ rᵢ, and exactly dr/dx = −a(r + r²) and d²r/dx² = a²(r + 3r² + 2r³) (differentiate
|
|
172
212
|
* r = 1/(E − 1), E = exp(ax), and use E = 1 + 1/r). They are summed in the same quadrature as X, Y, Z.
|
|
173
213
|
*/
|
|
174
|
-
function preparePlanckIntegrationLocus(cmfSource, quadratureNodes, model) {
|
|
214
|
+
function preparePlanckIntegrationLocus(cmfSource, quadratureNodes, model, portable) {
|
|
215
|
+
// `portable` (the display view): λ⁻⁵ and e^x − 1 from the portable functions, whose bits are the same in every engine.
|
|
216
|
+
const expm1 = portable ? portable.expm1 : Math.expm1;
|
|
175
217
|
const cmf = prepareColorMatchingFunctions(cmfSource);
|
|
176
218
|
const unit = prepareColorimetricSpectrum({ kind: "function", wavelengthRangeNanometers: cmf.wavelengthRangeNanometers, evaluate: () => 1 });
|
|
177
219
|
const basis = sampleColorimetricBasis(quadratureNodes, unit, cmf);
|
|
@@ -182,7 +224,7 @@ function preparePlanckIntegrationLocus(cmfSource, quadratureNodes, model) {
|
|
|
182
224
|
for (let i = 0; i < n; i++) {
|
|
183
225
|
const lambda = basis.nodes[i].wavelengthNanometers * 1e-9;
|
|
184
226
|
exponentPerKelvin[i] = SECOND_RADIATION_CONSTANT_METER_KELVIN / lambda;
|
|
185
|
-
inverseFifth[i] = 1 / lambda ** 5;
|
|
227
|
+
inverseFifth[i] = 1 / (portable ? portable.pow(lambda, 5) : lambda ** 5);
|
|
186
228
|
const w = basis.weights[i] * inverseFifth[i];
|
|
187
229
|
wx[i] = w * basis.xBar[i];
|
|
188
230
|
wy[i] = w * basis.yBar[i];
|
|
@@ -198,7 +240,7 @@ function preparePlanckIntegrationLocus(cmfSource, quadratureNodes, model) {
|
|
|
198
240
|
chromaticity(t) {
|
|
199
241
|
// expm1 keeps precision at long wavelengths; overflow at short ones gives 0, as in Planck's law.
|
|
200
242
|
for (let i = 0; i < n; i++)
|
|
201
|
-
factor[i] = inverseFifth[i] /
|
|
243
|
+
factor[i] = inverseFifth[i] / expm1(exponentPerKelvin[i] / t);
|
|
202
244
|
return chromaticityOfXyz(integrateSampledXyz(basis, factor));
|
|
203
245
|
},
|
|
204
246
|
sample(x) {
|
|
@@ -206,7 +248,7 @@ function preparePlanckIntegrationLocus(cmfSource, quadratureNodes, model) {
|
|
|
206
248
|
for (let i = 0; i < n; i++) {
|
|
207
249
|
const a = exponentPerKelvin[i];
|
|
208
250
|
// An overflowing expm1 gives r = 0 and zero derivatives, the exact limit.
|
|
209
|
-
const r = 1 /
|
|
251
|
+
const r = 1 / expm1(a * x), r2 = r * r;
|
|
210
252
|
const q1 = r + r2, k1 = -a * q1, k2 = a * a * (q1 + 2 * r2 * (1 + r));
|
|
211
253
|
const cx = wx[i], cy = wy[i], cz = wz[i];
|
|
212
254
|
sx += cx * r;
|
|
@@ -239,9 +281,13 @@ function preparePlanckIntegrationLocus(cmfSource, quadratureNodes, model) {
|
|
|
239
281
|
}
|
|
240
282
|
/** 1 nm nodes at 360, 361, …, 830 nm with Δλ = 1 nm (the CIE 15 summation convention). */
|
|
241
283
|
const DEFAULT_LOCUS_NODES = Object.freeze(Array.from({ length: 471 }, (_, i) => Object.freeze({ wavelengthNanometers: 360 + i, wavelengthMeasureNanometers: 1 })));
|
|
242
|
-
let defaultLocus;
|
|
243
|
-
/** The default locus, prepared on first use and shared (its scan cache is computed once). */
|
|
244
|
-
function analyticCmfLocus() {
|
|
284
|
+
let defaultLocus, defaultPortableLocus;
|
|
285
|
+
/** The default locus, prepared on first use and shared (its scan cache is computed once); `portable` for the display view. */
|
|
286
|
+
function analyticCmfLocus(portable) {
|
|
287
|
+
if (portable) {
|
|
288
|
+
defaultPortableLocus ??= preparePlanckIntegrationLocus({ kind: "analytic-cie-1931-multi-lobe-fit" }, DEFAULT_LOCUS_NODES, "planck-analytic-cmf", portable);
|
|
289
|
+
return defaultPortableLocus;
|
|
290
|
+
}
|
|
245
291
|
defaultLocus ??= preparePlanckIntegrationLocus({ kind: "analytic-cie-1931-multi-lobe-fit" }, DEFAULT_LOCUS_NODES, "planck-analytic-cmf");
|
|
246
292
|
return defaultLocus;
|
|
247
293
|
}
|
|
@@ -255,18 +301,18 @@ export function estimatePlanckianLocusChromaticity(input) {
|
|
|
255
301
|
const t = requireTemperatureIn(input.temperatureKelvin, PLANCKIAN_LOCUS_RANGE_KELVIN, "temperatureKelvin");
|
|
256
302
|
return approximationResult(analyticCmfLocus().chromaticity(t), "planckian-locus-planck-analytic-cmf", COLORIMETRY_MODEL_VERSION, ["Planck's law integrated against the engine's analytic CIE 1931 CMF fit, 1 nm nodes 360–830 nm; valid 1000–100000 K"]);
|
|
257
303
|
}
|
|
258
|
-
function prepareLocus(model) {
|
|
304
|
+
function prepareLocus(model, portable) {
|
|
259
305
|
const record = requireRecordValue(model, "locus");
|
|
260
306
|
if (record.kind === "planck-analytic-cmf") {
|
|
261
307
|
rejectUnknownFields(record, ["kind"], "locus");
|
|
262
|
-
return analyticCmfLocus();
|
|
308
|
+
return analyticCmfLocus(portable);
|
|
263
309
|
}
|
|
264
310
|
if (record.kind !== "planck-integration")
|
|
265
311
|
throw new InvalidConfigurationError("locus.kind is not supported.");
|
|
266
312
|
rejectUnknownFields(record, ["kind", "colorMatchingFunctions", "quadratureNodes"], "locus");
|
|
267
313
|
const m = model;
|
|
268
314
|
const cmf = prepareColorMatchingFunctions(m.colorMatchingFunctions);
|
|
269
|
-
return preparePlanckIntegrationLocus(m.colorMatchingFunctions, m.quadratureNodes, "planck-integration-" + cmf.sourceKind);
|
|
315
|
+
return preparePlanckIntegrationLocus(m.colorMatchingFunctions, m.quadratureNodes, "planck-integration-" + cmf.sourceKind, portable);
|
|
270
316
|
}
|
|
271
317
|
/** Planckian chromaticity at T using either locus model. */
|
|
272
318
|
export function calculatePlanckianChromaticity(input) {
|
|
@@ -297,8 +343,8 @@ export function estimateCorrelatedColorTemperatureMcCamy(input) {
|
|
|
297
343
|
return approximationResult({ correlatedColorTemperatureKelvin: cct }, "cct-mccamy-1992", COLORIMETRY_MODEL_VERSION, ["McCamy (1992) cubic; intended for chromaticities near the Planckian locus, about 2000–12500 K"]);
|
|
298
344
|
}
|
|
299
345
|
/** The "above-locus" unit normal at a sample: the increasing-T unit tangent (−du/dx, −dv/dx) turned by −90°. */
|
|
300
|
-
function locusNormal(s) {
|
|
301
|
-
const len = Math.hypot(s.du, s.dv);
|
|
346
|
+
function locusNormal(s, portable) {
|
|
347
|
+
const len = portable ? portable.hypot(s.du, s.dv) : Math.hypot(s.du, s.dv);
|
|
302
348
|
const tangent = { u: -s.du / len, v: -s.dv / len };
|
|
303
349
|
// u decreases as T rises, so turning the tangent by −90° points toward larger v (above the locus).
|
|
304
350
|
return { u: tangent.v, v: -tangent.u };
|
|
@@ -407,8 +453,19 @@ export function calculateCorrelatedColorTemperature(input) {
|
|
|
407
453
|
* (x ≥ 0, y > 0, x + y ≤ 1), for example 2000 K with Duv +0.02 (x + y ≈ 1.06).
|
|
408
454
|
*/
|
|
409
455
|
export function calculateChromaticityFromCorrelatedColorTemperature(input) {
|
|
456
|
+
return chromaticityFromCorrelatedColorTemperature(input, undefined);
|
|
457
|
+
}
|
|
458
|
+
/**
|
|
459
|
+
* Internal (not exported from the package): {@link calculateChromaticityFromCorrelatedColorTemperature} with the locus's
|
|
460
|
+
* λ⁻⁵, e^x − 1 and normal length and the daylight-locus powers from the portable functions (the display view's Kelvin
|
|
461
|
+
* white balance), so the result has the same bits in every JavaScript engine.
|
|
462
|
+
*/
|
|
463
|
+
export function calculatePortableChromaticityFromCorrelatedColorTemperature(input) {
|
|
464
|
+
return chromaticityFromCorrelatedColorTemperature(input, PORTABLE);
|
|
465
|
+
}
|
|
466
|
+
function chromaticityFromCorrelatedColorTemperature(input, portable) {
|
|
410
467
|
requireInputKeys(input, "calculateChromaticityFromCorrelatedColorTemperature", ["correlatedColorTemperatureKelvin", "duv", "whiteLocus", "locus"]);
|
|
411
|
-
const locus = prepareLocus(input.locus);
|
|
468
|
+
const locus = prepareLocus(input.locus, portable);
|
|
412
469
|
const duv = requireFiniteNumber(input.duv, "duv");
|
|
413
470
|
if (Math.abs(duv) > MAX_CORRELATED_COLOR_TEMPERATURE_DUV)
|
|
414
471
|
throw new InvalidScientificInputError("|duv| must not exceed 0.05.");
|
|
@@ -418,8 +475,8 @@ export function calculateChromaticityFromCorrelatedColorTemperature(input) {
|
|
|
418
475
|
? { minimum: Math.max(locus.range.minimum, CIE_DAYLIGHT_LOCUS_RANGE_KELVIN.minimum), maximum: Math.min(locus.range.maximum, CIE_DAYLIGHT_LOCUS_RANGE_KELVIN.maximum) }
|
|
419
476
|
: locus.range;
|
|
420
477
|
const t = requireTemperatureIn(input.correlatedColorTemperatureKelvin, range, "correlatedColorTemperatureKelvin");
|
|
421
|
-
const normal = locusNormal(locus.sample(1 / t));
|
|
422
|
-
const base = uvOfChromaticity(input.whiteLocus === "cie-daylight" ? daylightChromaticity(t) : locus.chromaticity(t));
|
|
478
|
+
const normal = locusNormal(locus.sample(1 / t), portable);
|
|
479
|
+
const base = uvOfChromaticity(input.whiteLocus === "cie-daylight" ? daylightChromaticity(t, portable) : locus.chromaticity(t));
|
|
423
480
|
const value = chromaticityOfUv({ u: base.u + duv * normal.u, v: base.v + duv * normal.v });
|
|
424
481
|
// The same domain parseChromaticity enforces on input, so every result can be passed back in.
|
|
425
482
|
if (!(value.x >= 0) || !(value.y > 0) || !(value.x + value.y <= 1)) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"illuminants.js","sourceRoot":"","sources":["../../src/color/illuminants.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Illuminant spectra and correlated-colour-temperature (CCT) helpers.\n *\n * - Planckian radiator: exact Planck law (BIPM Mise en pratique for the kelvin, Absolute Primary\n * Radiometry 2018, Eq. 1, free) with the exact SI defining constants (h, c, k; BIPM SI Brochure\n * 9th ed. 2019), so c₂ = hc/k = 1.438776877…e-2 m·K (NIST CODATA 2022). Wavelength is the vacuum\n * wavelength.\n * - CIE standard illuminant A: Planck's law relative to 560 nm with the CIE's defining constants\n * c₂ = 1.435e-2 m·K and T = 2848 K (also CIE 15:2018 Eq. 4.1 / ISO/CIE 11664-2, paid):\n * S_A(λ) = 100 (560/λ)^5 (exp(1.435e7/(2848·560)) − 1)/(exp(1.435e7/(2848 λ)) − 1)\n * (≈ 2856 K with the exact c₂). Computed from the formula, not a copied table; it reproduces the\n * free CIE 1 nm table (DOI 10.25039/CIE.DS.8jsxjrsn) to its six significant digits.\n * - CIE daylight: the CIE daylight-locus chromaticity polynomial and the M₁/M₂ weights (CIE 15:2018,\n * paid; no free text of the formulas found). With three-decimal rounding they reproduce the free\n * CIE D55 and D65 tables. The S₀/S₁/S₂ basis is a CIE dataset (CC BY-SA 4.0, not bundled);\n * callers supply it.\n * - Planckian locus: the chromaticity of a Planckian radiator at T, computed by integrating Planck's\n * law against colour-matching functions. The default uses the engine's analytic CIE 1931 fit with\n * 1 nm nodes from 360 to 830 nm (so it inherits that fit's approximation); callers may select\n * tabulated CMFs and their own nodes. Valid 1000–100000 K.\n * - CCT from chromaticity: McCamy (1992) cubic (approximation), or a nearest-point\n * search on the Planckian locus in the CIE 1960 UCS (CCT and signed Duv as defined in Smet et al.,\n * LEUKOS 2023, free archival copy from the US DOE; also CIE 15:2018). Duv sign: positive above the\n * locus (toward green), negative below (toward magenta), following Ohno, \"Calculation of CCT and Duv\n * and Practical Conversion Formulae\", CORM 2011 slides (free), slide 5; the engine documents and\n * tests it.\n * @see docs/COLORIMETRY.md for equations, coordinate/unit conventions and limits.\n */\n\nimport { approximationResult, calculatedResult, type CalculationResult } from \"../core/calculation-result.js\";\nimport type { SpectralWavelengthRangeNanometers } from \"../core/spectral.js\";\nimport { InvalidScientificInputError } from \"../core/validation.js\";\nimport {\n COLORIMETRY_MODEL_VERSION,\n integrateSampledXyz,\n prepareColorMatchingFunctions,\n prepareColorimetricSpectrum,\n sampleColorimetricBasis,\n type ColorMatchingFunctionsSource,\n type ColorimetricQuadratureNode,\n type ColorimetricSpectralSample,\n type ColorimetricSpectrum\n} from \"./color-matching-functions.js\";\nimport {\n chromaticityOfUv,\n chromaticityOfXyz,\n parseChromaticity,\n requireFiniteNumber,\n requirePositiveNumber,\n requireRecordValue,\n uvOfChromaticity,\n type Cie1960UvChromaticity,\n type CieXyChromaticity\n} from \"./colorimetry-math.js\";\nimport { rejectUnknownFields, requireInputKeys } from \"../core/record-validation.js\";\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\n\n/** Exact SI defining constants (BIPM SI Brochure, 9th edition, 2019). */\nexport const PLANCK_CONSTANT_JOULE_SECONDS = 6.62607015e-34;\nexport const SPEED_OF_LIGHT_METERS_PER_SECOND = 299_792_458;\nexport const BOLTZMANN_CONSTANT_JOULES_PER_KELVIN = 1.380649e-23;\n/** Second radiation constant c₂ = hc/k in m·K (derived from the exact constants). */\nexport const SECOND_RADIATION_CONSTANT_METER_KELVIN = (PLANCK_CONSTANT_JOULE_SECONDS * SPEED_OF_LIGHT_METERS_PER_SECOND) / BOLTZMANN_CONSTANT_JOULES_PER_KELVIN;\nconst FIRST_RADIATION_CONSTANT_RADIANCE = 2 * PLANCK_CONSTANT_JOULE_SECONDS * SPEED_OF_LIGHT_METERS_PER_SECOND ** 2;\n\n/** Temperature range of the Planckian-locus models, K. */\nexport const PLANCKIAN_LOCUS_RANGE_KELVIN = Object.freeze({ minimum: 1000, maximum: 100000 });\n/** CIE daylight-locus validity, K. */\nexport const CIE_DAYLIGHT_LOCUS_RANGE_KELVIN = Object.freeze({ minimum: 4000, maximum: 25000 });\n/**\n * Engine limit: no CCT beyond |Duv| = 0.05, the range over which Smet et al. (2023, free) evaluate CCT\n * methods for nominally white light (CIE 15:2018 §9.5, paid, gives the same limit).\n */\nexport const MAX_CORRELATED_COLOR_TEMPERATURE_DUV = 0.05;\n\n/**\n * Planck's law for blackbody spectral radiance per unit (vacuum) wavelength:\n * L(λ, T) = 2hc² / λ⁵ / (exp(hc/(λkT)) − 1). Returns W·m⁻²·sr⁻¹·nm⁻¹.\n */\nexport function calculatePlanckianSpectralRadiance(input: { temperatureKelvin: number; wavelengthNanometers: number }): number {\n requireInputKeys(input, \"calculatePlanckianSpectralRadiance\", [\"temperatureKelvin\", \"wavelengthNanometers\"]);\n const t = requirePositiveNumber(input.temperatureKelvin, \"temperatureKelvin\");\n const nm = requirePositiveNumber(input.wavelengthNanometers, \"wavelengthNanometers\");\n const lambda = nm * 1e-9;\n const exponent = SECOND_RADIATION_CONSTANT_METER_KELVIN / (lambda * t);\n // expm1 keeps precision in the Rayleigh–Jeans limit; overflow gives radiance 0.\n const radiancePerMeter = FIRST_RADIATION_CONSTANT_RADIANCE / lambda ** 5 / Math.expm1(exponent);\n return radiancePerMeter * 1e-9;\n}\n\n/** Planckian spectral radiance (W·m⁻²·sr⁻¹·nm⁻¹) as a function spectrum over the given range. */\nexport function createPlanckianSpectrum(input: { temperatureKelvin: number; wavelengthRangeNanometers: SpectralWavelengthRangeNanometers }): ColorimetricSpectrum {\n requireInputKeys(input, \"createPlanckianSpectrum\", [\"temperatureKelvin\", \"wavelengthRangeNanometers\"]);\n const temperatureKelvin = requirePositiveNumber(input.temperatureKelvin, \"temperatureKelvin\");\n const range = requireRecordValue(input.wavelengthRangeNanometers, \"wavelengthRangeNanometers\");\n rejectUnknownFields(range, [\"minimum\", \"maximum\"], \"wavelengthRangeNanometers\");\n const spectrum: ColorimetricSpectrum = {\n kind: \"function\",\n wavelengthRangeNanometers: { minimum: range.minimum as number, maximum: range.maximum as number },\n evaluate: (wavelengthNanometers) => calculatePlanckianSpectralRadiance({ temperatureKelvin, wavelengthNanometers })\n };\n prepareColorimetricSpectrum(spectrum, \"planckianSpectrum\");\n return spectrum;\n}\n\n/** CIE illuminant A defining range (nm). */\nexport const CIE_ILLUMINANT_A_RANGE_NANOMETERS = Object.freeze({ minimum: 300, maximum: 830 });\n\n/**\n * CIE standard illuminant A relative SPD from its defining formula (100 at 560 nm),\n * with c₂ = 1.435e-2 m·K and T = 2848 K as in the standard (≈ 2856 K with the\n * current c₂). Valid 300–830 nm.\n */\nexport function calculateCieIlluminantARelativeSpectralPower(wavelengthNanometers: number): number {\n const nm = requireFiniteNumber(wavelengthNanometers, \"wavelengthNanometers\");\n if (nm < CIE_ILLUMINANT_A_RANGE_NANOMETERS.minimum || nm > CIE_ILLUMINANT_A_RANGE_NANOMETERS.maximum) {\n throw new InvalidScientificInputError(\"CIE illuminant A is defined from 300 nm to 830 nm.\");\n }\n const c = 1.435e7 / 2848; // nm·K / K\n return 100 * (560 / nm) ** 5 * (Math.expm1(c / 560) / Math.expm1(c / nm));\n}\n\n/** CIE illuminant A as a function spectrum over its defining range. */\nexport function createCieIlluminantASpectrum(): ColorimetricSpectrum {\n return { kind: \"function\", wavelengthRangeNanometers: { ...CIE_ILLUMINANT_A_RANGE_NANOMETERS }, evaluate: calculateCieIlluminantARelativeSpectralPower };\n}\n\nfunction requireTemperatureIn(t: unknown, range: { minimum: number; maximum: number }, what: string): number {\n const value = requirePositiveNumber(t, what);\n if (value < range.minimum || value > range.maximum) {\n throw new InvalidScientificInputError(what + \" must lie in [\" + range.minimum + \", \" + range.maximum + \"] K.\");\n }\n return value;\n}\n\nfunction daylightChromaticity(t: number): CieXyChromaticity {\n const x = t <= 7000\n ? -4.6070e9 / t ** 3 + 2.9678e6 / t ** 2 + 0.09911e3 / t + 0.244063\n : -2.0064e9 / t ** 3 + 1.9018e6 / t ** 2 + 0.24748e3 / t + 0.237040;\n return { x, y: -3.000 * x * x + 2.870 * x - 0.275 };\n}\n\nexport interface CieDaylightComponentWeights {\n correlatedColorTemperatureKelvin: number;\n chromaticity: CieXyChromaticity;\n m1: number;\n m2: number;\n coefficientRounding: \"none\" | \"cie-three-decimals\";\n}\n\n/**\n * CIE daylight-locus chromaticity and S₁/S₂ weights for a CCT in 4000–25000 K\n * (CIE 15:2018 §4.1.2, paid; no free text of the formulas found). M₁, M₂ rounded to three decimals\n * reproduce the free CIE D55 and D65 tables (DOI 10.25039/CIE.DS.qewfb3kp, 10.25039/CIE.DS.hjfjmt59)\n * to their rounding; `coefficientRounding` makes that rounding explicit.\n * The temperature is used as given: D65's nominal 6500 K corresponds to 6504 K here.\n */\nexport function calculateCieDaylightComponentWeights(input: {\n correlatedColorTemperatureKelvin: number;\n coefficientRounding: \"none\" | \"cie-three-decimals\";\n}): CalculationResult<CieDaylightComponentWeights> {\n requireInputKeys(input, \"calculateCieDaylightComponentWeights\", [\"correlatedColorTemperatureKelvin\", \"coefficientRounding\"]);\n const t = requireTemperatureIn(input.correlatedColorTemperatureKelvin, CIE_DAYLIGHT_LOCUS_RANGE_KELVIN, \"correlatedColorTemperatureKelvin\");\n if (input.coefficientRounding !== \"none\" && input.coefficientRounding !== \"cie-three-decimals\") {\n throw new InvalidConfigurationError(\"coefficientRounding must be \\\"none\\\" or \\\"cie-three-decimals\\\".\");\n }\n const xy = daylightChromaticity(t);\n const m = 0.0241 + 0.2562 * xy.x - 0.7341 * xy.y;\n let m1 = (-1.3515 - 1.7703 * xy.x + 5.9114 * xy.y) / m;\n let m2 = (0.0300 - 31.4424 * xy.x + 30.0717 * xy.y) / m;\n if (input.coefficientRounding === \"cie-three-decimals\") {\n m1 = Math.round(m1 * 1000) / 1000;\n m2 = Math.round(m2 * 1000) / 1000;\n }\n return calculatedResult({ correlatedColorTemperatureKelvin: t, chromaticity: xy, m1, m2, coefficientRounding: input.coefficientRounding },\n \"cie-daylight-locus\", COLORIMETRY_MODEL_VERSION, [\"CIE daylight-locus polynomial and M1/M2 weights, checked against the free CIE D55 and D65 datasets (no free formula text found); valid 4000–25000 K\"]);\n}\n\n/** Caller-supplied CIE daylight basis S₀, S₁, S₂ (tabulated; CIE dataset not bundled). */\nexport interface CieDaylightBasis {\n s0: readonly ColorimetricSpectralSample[];\n s1: readonly ColorimetricSpectralSample[];\n s2: readonly ColorimetricSpectralSample[];\n /** Caller identity of the basis dataset (for example a DOI). */\n datasetId: string;\n}\n\n/**\n * Builds a CIE D-series relative SPD S(λ) = S₀ + M₁S₁ + M₂S₂ on the S₀ wavelengths from\n * caller-supplied basis tables (S₁, S₂ linearly interpolated; fail closed outside range).\n */\nexport function createCieDaylightSpectrum(input: {\n correlatedColorTemperatureKelvin: number;\n coefficientRounding: \"none\" | \"cie-three-decimals\";\n basis: CieDaylightBasis;\n}): ColorimetricSpectrum {\n requireInputKeys(input, \"createCieDaylightSpectrum\", [\"correlatedColorTemperatureKelvin\", \"coefficientRounding\", \"basis\"]);\n const weights = calculateCieDaylightComponentWeights({\n correlatedColorTemperatureKelvin: input.correlatedColorTemperatureKelvin,\n coefficientRounding: input.coefficientRounding\n }).value;\n const basis = requireRecordValue(input.basis, \"basis\");\n rejectUnknownFields(basis, [\"s0\", \"s1\", \"s2\", \"datasetId\"], \"basis\");\n if (typeof basis.datasetId !== \"string\" || basis.datasetId.trim().length === 0) {\n throw new InvalidConfigurationError(\"basis.datasetId must be a non-empty string.\");\n }\n const s0 = prepareColorimetricSpectrum({ kind: \"tabulated\", samples: input.basis.s0 }, \"basis.s0\");\n const s1 = prepareColorimetricSpectrum({ kind: \"tabulated\", samples: input.basis.s1 }, \"basis.s1\");\n const s2 = prepareColorimetricSpectrum({ kind: \"tabulated\", samples: input.basis.s2 }, \"basis.s2\");\n const samples = input.basis.s0.map((sample) => {\n const w = sample.wavelengthNanometers;\n return { wavelengthNanometers: w, value: s0.evaluate(w) + weights.m1 * s1.evaluate(w) + weights.m2 * s2.evaluate(w) };\n });\n return { kind: \"tabulated\", samples };\n}\n\n/** Selects how the Planckian locus is evaluated. */\nexport type PlanckianLocusModel =\n | {\n /** Planck's law integrated against the engine's analytic CIE 1931 CMFs, 1 nm nodes 360–830 nm (the default). */\n kind: \"planck-analytic-cmf\";\n }\n | {\n kind: \"planck-integration\";\n colorMatchingFunctions: ColorMatchingFunctionsSource;\n quadratureNodes: readonly ColorimetricQuadratureNode[];\n };\n\n/** Number of intervals of the coarse reciprocal-temperature scan that brackets the CCT search. */\nconst CCT_SCAN_STEPS = 200;\n/**\n * Convergence tolerance of the CCT root-find: it stops once a Newton (or bisection) step changes T by\n * at most this fraction of T (1e-6 K at 10000 K).\n */\nconst CCT_RELATIVE_TOLERANCE = 1e-10;\n\n/**\n * Locus uv and its first and second derivatives with respect to the reciprocal temperature x = 1/T\n * (K⁻¹), from the analytic derivatives of Planck's law integrated in the locus quadrature.\n */\ninterface LocusSample {\n u: number;\n v: number;\n du: number;\n dv: number;\n d2u: number;\n d2v: number;\n}\n\ninterface PreparedLocus {\n range: { minimum: number; maximum: number };\n approximation: boolean;\n model: string;\n chromaticity(t: number): CieXyChromaticity;\n /** uv and its x = 1/T derivatives at reciprocal temperature x. */\n sample(x: number): LocusSample;\n /** CIE 1960 uv at the coarse scan temperatures; computed once per prepared locus. */\n scan(): readonly Cie1960UvChromaticity[];\n}\n\n/**\n * Prepares a Planck-integration locus: the CMF values and 1/λ⁵ factors are sampled once, so each\n * temperature costs one exponential per node. Planck's constant factor 2hc² cancels in chromaticity.\n *\n * Derivatives: with x = 1/T, aᵢ = c₂/λᵢ and rᵢ = 1/(exp(aᵢx) − 1), Planck's law is proportional to\n * λᵢ⁻⁵ rᵢ, and exactly dr/dx = −a(r + r²) and d²r/dx² = a²(r + 3r² + 2r³) (differentiate\n * r = 1/(E − 1), E = exp(ax), and use E = 1 + 1/r). They are summed in the same quadrature as X, Y, Z.\n */\nfunction preparePlanckIntegrationLocus(cmfSource: ColorMatchingFunctionsSource, quadratureNodes: readonly ColorimetricQuadratureNode[], model: string): PreparedLocus {\n const cmf = prepareColorMatchingFunctions(cmfSource);\n const unit = prepareColorimetricSpectrum({ kind: \"function\", wavelengthRangeNanometers: cmf.wavelengthRangeNanometers, evaluate: () => 1 });\n const basis = sampleColorimetricBasis(quadratureNodes, unit, cmf);\n const n = basis.nodes.length;\n const exponentPerKelvin = new Float64Array(n), inverseFifth = new Float64Array(n);\n // Per-node constants of the derivative sums: weight × CMF × λ⁻⁵ for X, Y and Z.\n const wx = new Float64Array(n), wy = new Float64Array(n), wz = new Float64Array(n);\n for (let i = 0; i < n; i++) {\n const lambda = basis.nodes[i]!.wavelengthNanometers * 1e-9;\n exponentPerKelvin[i] = SECOND_RADIATION_CONSTANT_METER_KELVIN / lambda;\n inverseFifth[i] = 1 / lambda ** 5;\n const w = basis.weights[i]! * inverseFifth[i]!;\n wx[i] = w * basis.xBar[i]!; wy[i] = w * basis.yBar[i]!; wz[i] = w * basis.zBar[i]!;\n }\n const factor = new Float64Array(n);\n const range = PLANCKIAN_LOCUS_RANGE_KELVIN;\n let scanCache: readonly Cie1960UvChromaticity[] | undefined;\n const locus: PreparedLocus = {\n range,\n approximation: cmf.scientificStatus === \"approximation\",\n model,\n chromaticity(t: number): CieXyChromaticity {\n // expm1 keeps precision at long wavelengths; overflow at short ones gives 0, as in Planck's law.\n for (let i = 0; i < n; i++) factor[i] = inverseFifth[i]! / Math.expm1(exponentPerKelvin[i]! / t);\n return chromaticityOfXyz(integrateSampledXyz(basis, factor));\n },\n sample(x: number): LocusSample {\n let sx = 0, sy = 0, sz = 0, sx1 = 0, sy1 = 0, sz1 = 0, sx2 = 0, sy2 = 0, sz2 = 0;\n for (let i = 0; i < n; i++) {\n const a = exponentPerKelvin[i]!;\n // An overflowing expm1 gives r = 0 and zero derivatives, the exact limit.\n const r = 1 / Math.expm1(a * x), r2 = r * r;\n const q1 = r + r2, k1 = -a * q1, k2 = a * a * (q1 + 2 * r2 * (1 + r));\n const cx = wx[i]!, cy = wy[i]!, cz = wz[i]!;\n sx += cx * r; sy += cy * r; sz += cz * r;\n sx1 += cx * k1; sy1 += cy * k1; sz1 += cz * k1;\n sx2 += cx * k2; sy2 += cy * k2; sz2 += cz * k2;\n }\n // u = 4X/d, v = 6Y/d, d = X + 15Y + 3Z; quotient rule: q' = (N' − q d')/d, q'' = (N'' − 2q'd' − q d'')/d.\n const d = sx + 15 * sy + 3 * sz, d1 = sx1 + 15 * sy1 + 3 * sz1, d2 = sx2 + 15 * sy2 + 3 * sz2;\n if (!(d > 0) || !Number.isFinite(d)) throw new InvalidScientificInputError(\"Chromaticity requires X + Y + Z > 0.\");\n const u = (4 * sx) / d, v = (6 * sy) / d;\n const du = (4 * sx1 - u * d1) / d, dv = (6 * sy1 - v * d1) / d;\n return { u, v, du, dv, d2u: (4 * sx2 - 2 * du * d1 - u * d2) / d, d2v: (6 * sy2 - 2 * dv * d1 - v * d2) / d };\n },\n scan(): readonly Cie1960UvChromaticity[] {\n if (scanCache === undefined) {\n const lo = 1e6 / range.maximum, hi = 1e6 / range.minimum;\n scanCache = Array.from({ length: CCT_SCAN_STEPS + 1 }, (_, i) => uvOfChromaticity(locus.chromaticity(1e6 / (lo + ((hi - lo) * i) / CCT_SCAN_STEPS))));\n }\n return scanCache;\n }\n };\n return locus;\n}\n\n/** 1 nm nodes at 360, 361, …, 830 nm with Δλ = 1 nm (the CIE 15 summation convention). */\nconst DEFAULT_LOCUS_NODES: readonly ColorimetricQuadratureNode[] = Object.freeze(\n Array.from({ length: 471 }, (_, i) => Object.freeze({ wavelengthNanometers: 360 + i, wavelengthMeasureNanometers: 1 }))\n);\nlet defaultLocus: PreparedLocus | undefined;\n\n/** The default locus, prepared on first use and shared (its scan cache is computed once). */\nfunction analyticCmfLocus(): PreparedLocus {\n defaultLocus ??= preparePlanckIntegrationLocus({ kind: \"analytic-cie-1931-multi-lobe-fit\" }, DEFAULT_LOCUS_NODES, \"planck-analytic-cmf\");\n return defaultLocus;\n}\n\n/**\n * Planckian-locus chromaticity at T (1000–100000 K): Planck's law integrated against the engine's\n * analytic CIE 1931 CMFs with 1 nm nodes from 360 to 830 nm. An approximation, because the CMFs are\n * an analytic fit; with tabulated CMFs use {@link calculatePlanckianChromaticity}.\n */\nexport function estimatePlanckianLocusChromaticity(input: { temperatureKelvin: number }): CalculationResult<CieXyChromaticity> {\n requireInputKeys(input, \"estimatePlanckianLocusChromaticity\", [\"temperatureKelvin\"]);\n const t = requireTemperatureIn(input.temperatureKelvin, PLANCKIAN_LOCUS_RANGE_KELVIN, \"temperatureKelvin\");\n return approximationResult(analyticCmfLocus().chromaticity(t), \"planckian-locus-planck-analytic-cmf\", COLORIMETRY_MODEL_VERSION,\n [\"Planck's law integrated against the engine's analytic CIE 1931 CMF fit, 1 nm nodes 360–830 nm; valid 1000–100000 K\"]);\n}\n\nfunction prepareLocus(model: PlanckianLocusModel): PreparedLocus {\n const record = requireRecordValue(model, \"locus\");\n if (record.kind === \"planck-analytic-cmf\") {\n rejectUnknownFields(record, [\"kind\"], \"locus\");\n return analyticCmfLocus();\n }\n if (record.kind !== \"planck-integration\") throw new InvalidConfigurationError(\"locus.kind is not supported.\");\n rejectUnknownFields(record, [\"kind\", \"colorMatchingFunctions\", \"quadratureNodes\"], \"locus\");\n const m = model as Extract<PlanckianLocusModel, { kind: \"planck-integration\" }>;\n const cmf = prepareColorMatchingFunctions(m.colorMatchingFunctions);\n return preparePlanckIntegrationLocus(m.colorMatchingFunctions, m.quadratureNodes, \"planck-integration-\" + cmf.sourceKind);\n}\n\n/** Planckian chromaticity at T using either locus model. */\nexport function calculatePlanckianChromaticity(input: { temperatureKelvin: number; locus: PlanckianLocusModel }): CalculationResult<CieXyChromaticity> {\n requireInputKeys(input, \"calculatePlanckianChromaticity\", [\"temperatureKelvin\", \"locus\"]);\n const locus = prepareLocus(input.locus);\n const t = requireTemperatureIn(input.temperatureKelvin, locus.range, \"temperatureKelvin\");\n const value = locus.chromaticity(t);\n const assumptions = [\"Planckian locus model: \" + locus.model];\n return locus.approximation\n ? approximationResult(value, \"planckian-chromaticity\", COLORIMETRY_MODEL_VERSION, assumptions)\n : calculatedResult(value, \"planckian-chromaticity\", COLORIMETRY_MODEL_VERSION, assumptions);\n}\n\n/**\n * McCamy (1992) cubic estimate of CCT from CIE 1931 xy:\n * n = (x − 0.3320)/(0.1858 − y), CCT = 449 n³ + 3525 n² + 6823.3 n + 5520.33.\n * An approximation that carries no Duv. Measured for this engine (October 17, 2026) on points of a\n * Planckian locus integrated from the CIE 1931 2° 1 nm table (360–830 nm, exact c₂): it deviates by up\n * to 10 K over 2856–6504 K (largest near 3500 K), 19 K at 2000 K, 17 K at 8000 K, 118 K at 10000 K\n * and 442 K at 12500 K. See docs/COLORIMETRY.md.\n */\nexport function estimateCorrelatedColorTemperatureMcCamy(input: { chromaticity: CieXyChromaticity }): CalculationResult<{ correlatedColorTemperatureKelvin: number }> {\n requireInputKeys(input, \"estimateCorrelatedColorTemperatureMcCamy\", [\"chromaticity\"]);\n const xy = parseChromaticity(input.chromaticity, \"chromaticity\");\n const n = (xy.x - 0.3320) / (0.1858 - xy.y);\n const cct = 449 * n ** 3 + 3525 * n ** 2 + 6823.3 * n + 5520.33;\n if (!Number.isFinite(cct) || cct <= 0) throw new InvalidScientificInputError(\"Chromaticity is outside the McCamy approximation domain.\");\n return approximationResult({ correlatedColorTemperatureKelvin: cct }, \"cct-mccamy-1992\", COLORIMETRY_MODEL_VERSION,\n [\"McCamy (1992) cubic; intended for chromaticities near the Planckian locus, about 2000–12500 K\"]);\n}\n\n/** The \"above-locus\" unit normal at a sample: the increasing-T unit tangent (−du/dx, −dv/dx) turned by −90°. */\nfunction locusNormal(s: LocusSample): Cie1960UvChromaticity {\n const len = Math.hypot(s.du, s.dv);\n const tangent = { u: -s.du / len, v: -s.dv / len };\n // u decreases as T rises, so turning the tangent by −90° points toward larger v (above the locus).\n return { u: tangent.v, v: -tangent.u };\n}\n\n/**\n * Nearest locus point to `target` in CIE 1960 uv: the root of the derivative of the squared distance\n * F = (u − u₀)² + (v − v₀)². Roots of dF/dT and of dF/dx (x = 1/T) coincide, since dx/dT = −1/T² ≠ 0;\n * the root-find runs in x with g = ½ dF/dx = (u − u₀)u′ + (v − v₀)v′ and\n * g′ = u′² + v′² + (u − u₀)u″ + (v − v₀)v″ (analytic, see {@link preparePlanckIntegrationLocus}).\n * The cached coarse scan brackets the minimum; a Newton step falls back to bisection whenever it would\n * leave the bracket. Returns the root and the locus sample there, or undefined when the nearest point\n * lies beyond the model's range (by more than the tolerance).\n */\nfunction nearestLocusPoint(locus: PreparedLocus, target: Cie1960UvChromaticity): { x: number; sample: LocusSample } | undefined {\n const steps = CCT_SCAN_STEPS;\n const lo = 1e6 / locus.range.maximum, hi = 1e6 / locus.range.minimum, step = (hi - lo) / steps;\n const gridX = (i: number): number => (lo + step * i) * 1e-6;\n const scan = locus.scan();\n const f = (i: number): number => (scan[i]!.u - target.u) ** 2 + (scan[i]!.v - target.v) ** 2;\n let best = 0, bestValue = Infinity;\n for (let i = 0; i <= steps; i++) {\n const value = f(i);\n if (value < bestValue) { bestValue = value; best = i; }\n }\n const tolerance = (x: number): number => CCT_RELATIVE_TOLERANCE * x;\n const derivatives = (s: LocusSample): { g: number; gp: number } => {\n const eu = s.u - target.u, ev = s.v - target.v;\n return { g: eu * s.du + ev * s.dv, gp: s.du * s.du + s.dv * s.dv + eu * s.d2u + ev * s.d2v };\n };\n let a: number, b: number, x: number;\n if (best === 0 || best === steps) {\n // The scan minimum is a range limit: the nearest point is inside only if F still falls inward.\n x = gridX(best);\n const s = locus.sample(x), { g, gp } = derivatives(s);\n if (best === 0 ? g >= 0 : g <= 0) {\n // At the limit within the tolerance (one Newton step estimates the distance), or beyond it.\n return g === 0 || (gp > 0 && Math.abs(g / gp) <= tolerance(x)) ? { x, sample: s } : undefined;\n }\n a = best === 0 ? x : gridX(steps - 1);\n b = best === 0 ? gridX(1) : x;\n } else {\n // A discrete minimum between its neighbours brackets a local minimum; start at the parabola vertex.\n a = gridX(best - 1); b = gridX(best + 1);\n const fm = f(best - 1), fp = f(best + 1), curvature = fm - 2 * bestValue + fp;\n x = gridX(best) + (curvature > 0 ? (step * 1e-6 * (fm - fp)) / (2 * curvature) : 0);\n }\n let s = locus.sample(x);\n for (let i = 0; i < 100; i++) {\n const { g, gp } = derivatives(s);\n if (g === 0) break;\n if (g < 0) a = x; else b = x;\n let next = x - g / gp;\n if (!(gp > 0) || !(next > a && next < b)) next = 0.5 * (a + b);\n const converged = Math.abs(next - x) <= tolerance(next) || b - a <= tolerance(next);\n x = next;\n s = locus.sample(x);\n if (converged) break;\n }\n return { x, sample: s };\n}\n\nexport interface CorrelatedColorTemperatureResult {\n correlatedColorTemperatureKelvin: number;\n /** Signed CIE 1960 uv distance from the locus; positive above (greenish), negative below (the engine's convention). */\n duv: number;\n}\n\n/**\n * CCT and Duv of a chromaticity as the nearest point of the Planckian locus in the CIE\n * 1960 UCS (Smet et al. 2023; also CIE 15:2018). Finds the root of the analytic temperature derivative\n * of the squared uv distance (safeguarded Newton with bisection fallback, bracketed by a cached coarse\n * scan in reciprocal temperature), converged until a step changes T by at most 1e-10 of T. Throws\n * (fails closed) when the nearest point lies beyond the model's range, or when |Duv| > 0.05 (see\n * {@link MAX_CORRELATED_COLOR_TEMPERATURE_DUV}). Duv is positive above the locus (toward green) and\n * negative below it, the engine's documented sign convention.\n */\nexport function calculateCorrelatedColorTemperature(input: { chromaticity: CieXyChromaticity; locus: PlanckianLocusModel }): CalculationResult<CorrelatedColorTemperatureResult> {\n requireInputKeys(input, \"calculateCorrelatedColorTemperature\", [\"chromaticity\", \"locus\"]);\n const xy = parseChromaticity(input.chromaticity, \"chromaticity\");\n const locus = prepareLocus(input.locus);\n const target = uvOfChromaticity(xy);\n const nearest = nearestLocusPoint(locus, target);\n if (nearest === undefined) throw new InvalidScientificInputError(\"Chromaticity's nearest Planckian point lies outside the locus model range.\");\n const t = Math.min(locus.range.maximum, Math.max(locus.range.minimum, 1 / nearest.x));\n const point = nearest.sample, normal = locusNormal(point);\n const duv = (target.u - point.u) * normal.u + (target.v - point.v) * normal.v;\n if (Math.abs(duv) > MAX_CORRELATED_COLOR_TEMPERATURE_DUV) {\n throw new InvalidScientificInputError(\"|Duv| exceeds 0.05; no CCT is reported this far from the Planckian locus (the range evaluated by Smet et al. 2023).\");\n }\n const value = { correlatedColorTemperatureKelvin: t, duv };\n const assumptions = [\"Nearest point on the Planckian locus in CIE 1960 uv; locus model: \" + locus.model, \"Duv sign: positive above the locus (toward green), negative below (engine convention)\"];\n return locus.approximation\n ? approximationResult(value, \"cct-duv-locus-search\", COLORIMETRY_MODEL_VERSION, assumptions)\n : calculatedResult(value, \"cct-duv-locus-search\", COLORIMETRY_MODEL_VERSION, assumptions);\n}\n\n/** Locus whose temperature defines a white for CCT + Duv. */\nexport type WhiteLocus = \"planckian\" | \"cie-daylight\";\n\n/**\n * Chromaticity for CCT and Duv: the locus point (Planckian or CIE daylight) offset by\n * Duv along the unit normal of the Planckian locus at the same temperature in the CIE\n * 1960 UCS. For `cie-daylight`, Duv = 0 returns the daylight-locus point itself.\n * Throws InvalidScientificInputError when the offset point is not a physical chromaticity\n * (x ≥ 0, y > 0, x + y ≤ 1), for example 2000 K with Duv +0.02 (x + y ≈ 1.06).\n */\nexport function calculateChromaticityFromCorrelatedColorTemperature(input: {\n correlatedColorTemperatureKelvin: number;\n duv: number;\n whiteLocus: WhiteLocus;\n locus: PlanckianLocusModel;\n}): CalculationResult<CieXyChromaticity> {\n requireInputKeys(input, \"calculateChromaticityFromCorrelatedColorTemperature\", [\"correlatedColorTemperatureKelvin\", \"duv\", \"whiteLocus\", \"locus\"]);\n const locus = prepareLocus(input.locus);\n const duv = requireFiniteNumber(input.duv, \"duv\");\n if (Math.abs(duv) > MAX_CORRELATED_COLOR_TEMPERATURE_DUV) throw new InvalidScientificInputError(\"|duv| must not exceed 0.05.\");\n if (input.whiteLocus !== \"planckian\" && input.whiteLocus !== \"cie-daylight\") throw new InvalidConfigurationError(\"whiteLocus is not supported.\");\n const range = input.whiteLocus === \"cie-daylight\"\n ? { minimum: Math.max(locus.range.minimum, CIE_DAYLIGHT_LOCUS_RANGE_KELVIN.minimum), maximum: Math.min(locus.range.maximum, CIE_DAYLIGHT_LOCUS_RANGE_KELVIN.maximum) }\n : locus.range;\n const t = requireTemperatureIn(input.correlatedColorTemperatureKelvin, range, \"correlatedColorTemperatureKelvin\");\n const normal = locusNormal(locus.sample(1 / t));\n const base = uvOfChromaticity(input.whiteLocus === \"cie-daylight\" ? daylightChromaticity(t) : locus.chromaticity(t));\n const value = chromaticityOfUv({ u: base.u + duv * normal.u, v: base.v + duv * normal.v });\n // The same domain parseChromaticity enforces on input, so every result can be passed back in.\n if (!(value.x >= 0) || !(value.y > 0) || !(value.x + value.y <= 1)) {\n throw new InvalidScientificInputError(\"correlatedColorTemperatureKelvin and duv give a chromaticity outside x >= 0, y > 0, x + y <= 1.\");\n }\n const assumptions = [\"White locus: \" + input.whiteLocus + \"; Duv offset along the Planckian-locus normal (\" + locus.model + \") in CIE 1960 uv\"];\n const exactDaylightPoint = input.whiteLocus === \"cie-daylight\" && duv === 0;\n return locus.approximation && !exactDaylightPoint\n ? approximationResult(value, \"chromaticity-from-cct-duv\", COLORIMETRY_MODEL_VERSION, assumptions)\n : calculatedResult(value, \"chromaticity-from-cct-duv\", COLORIMETRY_MODEL_VERSION, assumptions);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"illuminants.js","sourceRoot":"","sources":["../../src/color/illuminants.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Illuminant spectra and correlated-colour-temperature (CCT) helpers.\n *\n * - Planckian radiator: exact Planck law (BIPM Mise en pratique for the kelvin, Absolute Primary\n * Radiometry 2018, Eq. 1, free) with the exact SI defining constants (h, c, k; BIPM SI Brochure\n * 9th ed. 2019), so c₂ = hc/k = 1.438776877…e-2 m·K (NIST CODATA 2022). Wavelength is the vacuum\n * wavelength.\n * - CIE standard illuminant A: Planck's law relative to 560 nm with the CIE's defining constants\n * c₂ = 1.435e-2 m·K and T = 2848 K (also CIE 15:2018 Eq. 4.1 / ISO/CIE 11664-2, paid):\n * S_A(λ) = 100 (560/λ)^5 (exp(1.435e7/(2848·560)) − 1)/(exp(1.435e7/(2848 λ)) − 1)\n * (≈ 2856 K with the exact c₂). Computed from the formula, not a copied table; it reproduces the\n * free CIE 1 nm table (DOI 10.25039/CIE.DS.8jsxjrsn) to its six significant digits.\n * - CIE daylight: the CIE daylight-locus chromaticity polynomial and the M₁/M₂ weights (CIE 15:2018,\n * paid; no free text of the formulas found). With three-decimal rounding they reproduce the free\n * CIE D55 and D65 tables. The S₀/S₁/S₂ basis is a CIE dataset (CC BY-SA 4.0, not bundled);\n * callers supply it.\n * - Planckian locus: the chromaticity of a Planckian radiator at T, computed by integrating Planck's\n * law against colour-matching functions. The default uses the engine's analytic CIE 1931 fit with\n * 1 nm nodes from 360 to 830 nm (so it inherits that fit's approximation); callers may select\n * tabulated CMFs and their own nodes. Valid 1000–100000 K.\n * - CCT from chromaticity: McCamy (1992) cubic (approximation), or a nearest-point\n * search on the Planckian locus in the CIE 1960 UCS (CCT and signed Duv as defined in Smet et al.,\n * LEUKOS 2023, free archival copy from the US DOE; also CIE 15:2018). Duv sign: positive above the\n * locus (toward green), negative below (toward magenta), following Ohno, \"Calculation of CCT and Duv\n * and Practical Conversion Formulae\", CORM 2011 slides (free), slide 5; the engine documents and\n * tests it.\n * @see docs/COLORIMETRY.md for equations, coordinate/unit conventions and limits.\n */\n\nimport { approximationResult, calculatedResult, type CalculationResult } from \"../core/calculation-result.js\";\nimport type { SpectralWavelengthRangeNanometers } from \"../core/spectral.js\";\nimport { InvalidScientificInputError } from \"../core/validation.js\";\nimport {\n COLORIMETRY_MODEL_VERSION,\n integrateSampledXyz,\n prepareColorMatchingFunctions,\n prepareColorimetricSpectrum,\n sampleColorimetricBasis,\n type ColorMatchingFunctionsSource,\n type ColorimetricQuadratureNode,\n type ColorimetricSpectralSample,\n type ColorimetricSpectrum\n} from \"./color-matching-functions.js\";\nimport {\n chromaticityOfUv,\n chromaticityOfXyz,\n parseChromaticity,\n requireFiniteNumber,\n requirePositiveNumber,\n requireRecordValue,\n uvOfChromaticity,\n type Cie1960UvChromaticity,\n type CieXyChromaticity\n} from \"./colorimetry-math.js\";\nimport { rejectUnknownFields, requireInputKeys } from \"../core/record-validation.js\";\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\nimport { portableExpm1, portableHypot, portablePow } from \"../core/portable-math.js\";\n\n/** Exact SI defining constants (BIPM SI Brochure, 9th edition, 2019). */\nexport const PLANCK_CONSTANT_JOULE_SECONDS = 6.62607015e-34;\nexport const SPEED_OF_LIGHT_METERS_PER_SECOND = 299_792_458;\nexport const BOLTZMANN_CONSTANT_JOULES_PER_KELVIN = 1.380649e-23;\n/** Second radiation constant c₂ = hc/k in m·K (derived from the exact constants). */\nexport const SECOND_RADIATION_CONSTANT_METER_KELVIN = (PLANCK_CONSTANT_JOULE_SECONDS * SPEED_OF_LIGHT_METERS_PER_SECOND) / BOLTZMANN_CONSTANT_JOULES_PER_KELVIN;\nconst FIRST_RADIATION_CONSTANT_RADIANCE = 2 * PLANCK_CONSTANT_JOULE_SECONDS * SPEED_OF_LIGHT_METERS_PER_SECOND ** 2;\n/** The same constant with c² as one correctly rounded product (the portable evaluation below). */\nconst FIRST_RADIATION_CONSTANT_RADIANCE_PORTABLE = 2 * PLANCK_CONSTANT_JOULE_SECONDS * (SPEED_OF_LIGHT_METERS_PER_SECOND * SPEED_OF_LIGHT_METERS_PER_SECOND);\n/**\n * The portable functions the display view's evaluations use. They are passed as an argument rather than called directly\n * from the shared code, so a bundle that never calls a portable variant (every capture) does not contain them.\n */\ninterface PortableOps { pow(x: number, y: number): number; expm1(x: number): number; hypot(x: number, y: number): number }\nconst PORTABLE: PortableOps = { pow: portablePow, expm1: portableExpm1, hypot: portableHypot };\n\n/** Temperature range of the Planckian-locus models, K. */\nexport const PLANCKIAN_LOCUS_RANGE_KELVIN = Object.freeze({ minimum: 1000, maximum: 100000 });\n/** CIE daylight-locus validity, K. */\nexport const CIE_DAYLIGHT_LOCUS_RANGE_KELVIN = Object.freeze({ minimum: 4000, maximum: 25000 });\n/**\n * Engine limit: no CCT beyond |Duv| = 0.05, the range over which Smet et al. (2023, free) evaluate CCT\n * methods for nominally white light (CIE 15:2018 §9.5, paid, gives the same limit).\n */\nexport const MAX_CORRELATED_COLOR_TEMPERATURE_DUV = 0.05;\n\n/**\n * Planck's law for blackbody spectral radiance per unit (vacuum) wavelength:\n * L(λ, T) = 2hc² / λ⁵ / (exp(hc/(λkT)) − 1). Returns W·m⁻²·sr⁻¹·nm⁻¹.\n */\nexport function calculatePlanckianSpectralRadiance(input: { temperatureKelvin: number; wavelengthNanometers: number }): number {\n requireInputKeys(input, \"calculatePlanckianSpectralRadiance\", [\"temperatureKelvin\", \"wavelengthNanometers\"]);\n const t = requirePositiveNumber(input.temperatureKelvin, \"temperatureKelvin\");\n const nm = requirePositiveNumber(input.wavelengthNanometers, \"wavelengthNanometers\");\n return planckianRadiancePerNanometer(t, nm, undefined);\n}\n/**\n * Planck's law at temperature t (K) and wavelength nm. `portable` evaluates λ⁵ and e^x − 1 with the engine's portable\n * functions (`portablePow`, `portableExpm1`; within 1 and 2 ulps) instead of `**` and Math.expm1, whose last bits differ\n * between JavaScript engines (V8 12 and V8 13 differ in about 10 % of λ⁵); the display view uses it.\n */\nfunction planckianRadiancePerNanometer(t: number, nm: number, portable: PortableOps | undefined): number {\n const lambda = nm * 1e-9;\n const exponent = SECOND_RADIATION_CONSTANT_METER_KELVIN / (lambda * t);\n // expm1 keeps precision in the Rayleigh–Jeans limit; overflow gives radiance 0.\n const radiancePerMeter = portable ? FIRST_RADIATION_CONSTANT_RADIANCE_PORTABLE / portable.pow(lambda, 5) / portable.expm1(exponent)\n : FIRST_RADIATION_CONSTANT_RADIANCE / lambda ** 5 / Math.expm1(exponent);\n return radiancePerMeter * 1e-9;\n}\n\n/** Planckian spectral radiance (W·m⁻²·sr⁻¹·nm⁻¹) as a function spectrum over the given range. */\nexport function createPlanckianSpectrum(input: { temperatureKelvin: number; wavelengthRangeNanometers: SpectralWavelengthRangeNanometers }): ColorimetricSpectrum {\n return planckianSpectrum(input, undefined);\n}\n/** Internal (not exported from the package): {@link createPlanckianSpectrum} evaluated with the portable functions (the display view). */\nexport function createPortablePlanckianSpectrum(input: { temperatureKelvin: number; wavelengthRangeNanometers: SpectralWavelengthRangeNanometers }): ColorimetricSpectrum {\n return planckianSpectrum(input, PORTABLE);\n}\nfunction planckianSpectrum(input: { temperatureKelvin: number; wavelengthRangeNanometers: SpectralWavelengthRangeNanometers }, portable: PortableOps | undefined): ColorimetricSpectrum {\n requireInputKeys(input, \"createPlanckianSpectrum\", [\"temperatureKelvin\", \"wavelengthRangeNanometers\"]);\n const temperatureKelvin = requirePositiveNumber(input.temperatureKelvin, \"temperatureKelvin\");\n const range = requireRecordValue(input.wavelengthRangeNanometers, \"wavelengthRangeNanometers\");\n rejectUnknownFields(range, [\"minimum\", \"maximum\"], \"wavelengthRangeNanometers\");\n const spectrum: ColorimetricSpectrum = {\n kind: \"function\",\n wavelengthRangeNanometers: { minimum: range.minimum as number, maximum: range.maximum as number },\n evaluate: portable ? (wavelengthNanometers): number => planckianRadiancePerNanometer(temperatureKelvin, requirePositiveNumber(wavelengthNanometers, \"wavelengthNanometers\"), portable)\n : (wavelengthNanometers): number => calculatePlanckianSpectralRadiance({ temperatureKelvin, wavelengthNanometers })\n };\n prepareColorimetricSpectrum(spectrum, \"planckianSpectrum\");\n return spectrum;\n}\n\n/** CIE illuminant A defining range (nm). */\nexport const CIE_ILLUMINANT_A_RANGE_NANOMETERS = Object.freeze({ minimum: 300, maximum: 830 });\n\n/**\n * CIE standard illuminant A relative SPD from its defining formula (100 at 560 nm),\n * with c₂ = 1.435e-2 m·K and T = 2848 K as in the standard (≈ 2856 K with the\n * current c₂). Valid 300–830 nm.\n */\nexport function calculateCieIlluminantARelativeSpectralPower(wavelengthNanometers: number): number {\n return cieIlluminantA(wavelengthNanometers, undefined);\n}\n/** CIE illuminant A; `portable` evaluates the power and e^x − 1 with the portable functions (see planckianRadiancePerNanometer). */\nfunction cieIlluminantA(wavelengthNanometers: number, portable: PortableOps | undefined): number {\n const nm = requireFiniteNumber(wavelengthNanometers, \"wavelengthNanometers\");\n if (nm < CIE_ILLUMINANT_A_RANGE_NANOMETERS.minimum || nm > CIE_ILLUMINANT_A_RANGE_NANOMETERS.maximum) {\n throw new InvalidScientificInputError(\"CIE illuminant A is defined from 300 nm to 830 nm.\");\n }\n const c = 1.435e7 / 2848; // nm·K / K\n return portable ? 100 * portable.pow(560 / nm, 5) * (portable.expm1(c / 560) / portable.expm1(c / nm)) : 100 * (560 / nm) ** 5 * (Math.expm1(c / 560) / Math.expm1(c / nm));\n}\n\n/** CIE illuminant A as a function spectrum over its defining range. */\nexport function createCieIlluminantASpectrum(): ColorimetricSpectrum {\n return { kind: \"function\", wavelengthRangeNanometers: { ...CIE_ILLUMINANT_A_RANGE_NANOMETERS }, evaluate: calculateCieIlluminantARelativeSpectralPower };\n}\n/** Internal (not exported from the package): {@link createCieIlluminantASpectrum} evaluated with the portable functions (the display view). */\nexport function createPortableCieIlluminantASpectrum(): ColorimetricSpectrum {\n return { kind: \"function\", wavelengthRangeNanometers: { ...CIE_ILLUMINANT_A_RANGE_NANOMETERS }, evaluate: (nm) => cieIlluminantA(nm, PORTABLE) };\n}\n\nfunction requireTemperatureIn(t: unknown, range: { minimum: number; maximum: number }, what: string): number {\n const value = requirePositiveNumber(t, what);\n if (value < range.minimum || value > range.maximum) {\n throw new InvalidScientificInputError(what + \" must lie in [\" + range.minimum + \", \" + range.maximum + \"] K.\");\n }\n return value;\n}\n\nfunction daylightChromaticity(t: number, portable?: PortableOps): CieXyChromaticity {\n const t3 = portable ? portable.pow(t, 3) : t ** 3, t2 = portable ? t * t : t ** 2;\n const x = t <= 7000\n ? -4.6070e9 / t3 + 2.9678e6 / t2 + 0.09911e3 / t + 0.244063\n : -2.0064e9 / t3 + 1.9018e6 / t2 + 0.24748e3 / t + 0.237040;\n return { x, y: -3.000 * x * x + 2.870 * x - 0.275 };\n}\n\nexport interface CieDaylightComponentWeights {\n correlatedColorTemperatureKelvin: number;\n chromaticity: CieXyChromaticity;\n m1: number;\n m2: number;\n coefficientRounding: \"none\" | \"cie-three-decimals\";\n}\n\n/**\n * CIE daylight-locus chromaticity and S₁/S₂ weights for a CCT in 4000–25000 K\n * (CIE 15:2018 §4.1.2, paid; no free text of the formulas found). M₁, M₂ rounded to three decimals\n * reproduce the free CIE D55 and D65 tables (DOI 10.25039/CIE.DS.qewfb3kp, 10.25039/CIE.DS.hjfjmt59)\n * to their rounding; `coefficientRounding` makes that rounding explicit.\n * The temperature is used as given: D65's nominal 6500 K corresponds to 6504 K here.\n */\nexport function calculateCieDaylightComponentWeights(input: {\n correlatedColorTemperatureKelvin: number;\n coefficientRounding: \"none\" | \"cie-three-decimals\";\n}): CalculationResult<CieDaylightComponentWeights> {\n return cieDaylightComponentWeights(input, undefined);\n}\nfunction cieDaylightComponentWeights(input: { correlatedColorTemperatureKelvin: number; coefficientRounding: \"none\" | \"cie-three-decimals\" },\n portable: PortableOps | undefined): CalculationResult<CieDaylightComponentWeights> {\n requireInputKeys(input, \"calculateCieDaylightComponentWeights\", [\"correlatedColorTemperatureKelvin\", \"coefficientRounding\"]);\n const t = requireTemperatureIn(input.correlatedColorTemperatureKelvin, CIE_DAYLIGHT_LOCUS_RANGE_KELVIN, \"correlatedColorTemperatureKelvin\");\n if (input.coefficientRounding !== \"none\" && input.coefficientRounding !== \"cie-three-decimals\") {\n throw new InvalidConfigurationError(\"coefficientRounding must be \\\"none\\\" or \\\"cie-three-decimals\\\".\");\n }\n const xy = daylightChromaticity(t, portable);\n const m = 0.0241 + 0.2562 * xy.x - 0.7341 * xy.y;\n let m1 = (-1.3515 - 1.7703 * xy.x + 5.9114 * xy.y) / m;\n let m2 = (0.0300 - 31.4424 * xy.x + 30.0717 * xy.y) / m;\n if (input.coefficientRounding === \"cie-three-decimals\") {\n m1 = Math.round(m1 * 1000) / 1000;\n m2 = Math.round(m2 * 1000) / 1000;\n }\n return calculatedResult({ correlatedColorTemperatureKelvin: t, chromaticity: xy, m1, m2, coefficientRounding: input.coefficientRounding },\n \"cie-daylight-locus\", COLORIMETRY_MODEL_VERSION, [\"CIE daylight-locus polynomial and M1/M2 weights, checked against the free CIE D55 and D65 datasets (no free formula text found); valid 4000–25000 K\"]);\n}\n\n/** Caller-supplied CIE daylight basis S₀, S₁, S₂ (tabulated; CIE dataset not bundled). */\nexport interface CieDaylightBasis {\n s0: readonly ColorimetricSpectralSample[];\n s1: readonly ColorimetricSpectralSample[];\n s2: readonly ColorimetricSpectralSample[];\n /** Caller identity of the basis dataset (for example a DOI). */\n datasetId: string;\n}\n\n/**\n * Builds a CIE D-series relative SPD S(λ) = S₀ + M₁S₁ + M₂S₂ on the S₀ wavelengths from\n * caller-supplied basis tables (S₁, S₂ linearly interpolated; fail closed outside range).\n */\nexport function createCieDaylightSpectrum(input: {\n correlatedColorTemperatureKelvin: number;\n coefficientRounding: \"none\" | \"cie-three-decimals\";\n basis: CieDaylightBasis;\n}): ColorimetricSpectrum {\n return cieDaylightSpectrum(input, undefined);\n}\n/** Internal (not exported from the package): {@link createCieDaylightSpectrum} with the portable daylight-locus powers (the display view). */\nexport function createPortableCieDaylightSpectrum(input: Parameters<typeof createCieDaylightSpectrum>[0]): ColorimetricSpectrum {\n return cieDaylightSpectrum(input, PORTABLE);\n}\nfunction cieDaylightSpectrum(input: Parameters<typeof createCieDaylightSpectrum>[0], portable: PortableOps | undefined): ColorimetricSpectrum {\n requireInputKeys(input, \"createCieDaylightSpectrum\", [\"correlatedColorTemperatureKelvin\", \"coefficientRounding\", \"basis\"]);\n const weights = cieDaylightComponentWeights({\n correlatedColorTemperatureKelvin: input.correlatedColorTemperatureKelvin,\n coefficientRounding: input.coefficientRounding\n }, portable).value;\n const basis = requireRecordValue(input.basis, \"basis\");\n rejectUnknownFields(basis, [\"s0\", \"s1\", \"s2\", \"datasetId\"], \"basis\");\n if (typeof basis.datasetId !== \"string\" || basis.datasetId.trim().length === 0) {\n throw new InvalidConfigurationError(\"basis.datasetId must be a non-empty string.\");\n }\n const s0 = prepareColorimetricSpectrum({ kind: \"tabulated\", samples: input.basis.s0 }, \"basis.s0\");\n const s1 = prepareColorimetricSpectrum({ kind: \"tabulated\", samples: input.basis.s1 }, \"basis.s1\");\n const s2 = prepareColorimetricSpectrum({ kind: \"tabulated\", samples: input.basis.s2 }, \"basis.s2\");\n const samples = input.basis.s0.map((sample) => {\n const w = sample.wavelengthNanometers;\n return { wavelengthNanometers: w, value: s0.evaluate(w) + weights.m1 * s1.evaluate(w) + weights.m2 * s2.evaluate(w) };\n });\n return { kind: \"tabulated\", samples };\n}\n\n/** Selects how the Planckian locus is evaluated. */\nexport type PlanckianLocusModel =\n | {\n /** Planck's law integrated against the engine's analytic CIE 1931 CMFs, 1 nm nodes 360–830 nm (the default). */\n kind: \"planck-analytic-cmf\";\n }\n | {\n kind: \"planck-integration\";\n colorMatchingFunctions: ColorMatchingFunctionsSource;\n quadratureNodes: readonly ColorimetricQuadratureNode[];\n };\n\n/** Number of intervals of the coarse reciprocal-temperature scan that brackets the CCT search. */\nconst CCT_SCAN_STEPS = 200;\n/**\n * Convergence tolerance of the CCT root-find: it stops once a Newton (or bisection) step changes T by\n * at most this fraction of T (1e-6 K at 10000 K).\n */\nconst CCT_RELATIVE_TOLERANCE = 1e-10;\n\n/**\n * Locus uv and its first and second derivatives with respect to the reciprocal temperature x = 1/T\n * (K⁻¹), from the analytic derivatives of Planck's law integrated in the locus quadrature.\n */\ninterface LocusSample {\n u: number;\n v: number;\n du: number;\n dv: number;\n d2u: number;\n d2v: number;\n}\n\ninterface PreparedLocus {\n range: { minimum: number; maximum: number };\n approximation: boolean;\n model: string;\n chromaticity(t: number): CieXyChromaticity;\n /** uv and its x = 1/T derivatives at reciprocal temperature x. */\n sample(x: number): LocusSample;\n /** CIE 1960 uv at the coarse scan temperatures; computed once per prepared locus. */\n scan(): readonly Cie1960UvChromaticity[];\n}\n\n/**\n * Prepares a Planck-integration locus: the CMF values and 1/λ⁵ factors are sampled once, so each\n * temperature costs one exponential per node. Planck's constant factor 2hc² cancels in chromaticity.\n *\n * Derivatives: with x = 1/T, aᵢ = c₂/λᵢ and rᵢ = 1/(exp(aᵢx) − 1), Planck's law is proportional to\n * λᵢ⁻⁵ rᵢ, and exactly dr/dx = −a(r + r²) and d²r/dx² = a²(r + 3r² + 2r³) (differentiate\n * r = 1/(E − 1), E = exp(ax), and use E = 1 + 1/r). They are summed in the same quadrature as X, Y, Z.\n */\nfunction preparePlanckIntegrationLocus(cmfSource: ColorMatchingFunctionsSource, quadratureNodes: readonly ColorimetricQuadratureNode[], model: string,\n portable?: PortableOps): PreparedLocus {\n // `portable` (the display view): λ⁻⁵ and e^x − 1 from the portable functions, whose bits are the same in every engine.\n const expm1 = portable ? portable.expm1 : Math.expm1;\n const cmf = prepareColorMatchingFunctions(cmfSource);\n const unit = prepareColorimetricSpectrum({ kind: \"function\", wavelengthRangeNanometers: cmf.wavelengthRangeNanometers, evaluate: () => 1 });\n const basis = sampleColorimetricBasis(quadratureNodes, unit, cmf);\n const n = basis.nodes.length;\n const exponentPerKelvin = new Float64Array(n), inverseFifth = new Float64Array(n);\n // Per-node constants of the derivative sums: weight × CMF × λ⁻⁵ for X, Y and Z.\n const wx = new Float64Array(n), wy = new Float64Array(n), wz = new Float64Array(n);\n for (let i = 0; i < n; i++) {\n const lambda = basis.nodes[i]!.wavelengthNanometers * 1e-9;\n exponentPerKelvin[i] = SECOND_RADIATION_CONSTANT_METER_KELVIN / lambda;\n inverseFifth[i] = 1 / (portable ? portable.pow(lambda, 5) : lambda ** 5);\n const w = basis.weights[i]! * inverseFifth[i]!;\n wx[i] = w * basis.xBar[i]!; wy[i] = w * basis.yBar[i]!; wz[i] = w * basis.zBar[i]!;\n }\n const factor = new Float64Array(n);\n const range = PLANCKIAN_LOCUS_RANGE_KELVIN;\n let scanCache: readonly Cie1960UvChromaticity[] | undefined;\n const locus: PreparedLocus = {\n range,\n approximation: cmf.scientificStatus === \"approximation\",\n model,\n chromaticity(t: number): CieXyChromaticity {\n // expm1 keeps precision at long wavelengths; overflow at short ones gives 0, as in Planck's law.\n for (let i = 0; i < n; i++) factor[i] = inverseFifth[i]! / expm1(exponentPerKelvin[i]! / t);\n return chromaticityOfXyz(integrateSampledXyz(basis, factor));\n },\n sample(x: number): LocusSample {\n let sx = 0, sy = 0, sz = 0, sx1 = 0, sy1 = 0, sz1 = 0, sx2 = 0, sy2 = 0, sz2 = 0;\n for (let i = 0; i < n; i++) {\n const a = exponentPerKelvin[i]!;\n // An overflowing expm1 gives r = 0 and zero derivatives, the exact limit.\n const r = 1 / expm1(a * x), r2 = r * r;\n const q1 = r + r2, k1 = -a * q1, k2 = a * a * (q1 + 2 * r2 * (1 + r));\n const cx = wx[i]!, cy = wy[i]!, cz = wz[i]!;\n sx += cx * r; sy += cy * r; sz += cz * r;\n sx1 += cx * k1; sy1 += cy * k1; sz1 += cz * k1;\n sx2 += cx * k2; sy2 += cy * k2; sz2 += cz * k2;\n }\n // u = 4X/d, v = 6Y/d, d = X + 15Y + 3Z; quotient rule: q' = (N' − q d')/d, q'' = (N'' − 2q'd' − q d'')/d.\n const d = sx + 15 * sy + 3 * sz, d1 = sx1 + 15 * sy1 + 3 * sz1, d2 = sx2 + 15 * sy2 + 3 * sz2;\n if (!(d > 0) || !Number.isFinite(d)) throw new InvalidScientificInputError(\"Chromaticity requires X + Y + Z > 0.\");\n const u = (4 * sx) / d, v = (6 * sy) / d;\n const du = (4 * sx1 - u * d1) / d, dv = (6 * sy1 - v * d1) / d;\n return { u, v, du, dv, d2u: (4 * sx2 - 2 * du * d1 - u * d2) / d, d2v: (6 * sy2 - 2 * dv * d1 - v * d2) / d };\n },\n scan(): readonly Cie1960UvChromaticity[] {\n if (scanCache === undefined) {\n const lo = 1e6 / range.maximum, hi = 1e6 / range.minimum;\n scanCache = Array.from({ length: CCT_SCAN_STEPS + 1 }, (_, i) => uvOfChromaticity(locus.chromaticity(1e6 / (lo + ((hi - lo) * i) / CCT_SCAN_STEPS))));\n }\n return scanCache;\n }\n };\n return locus;\n}\n\n/** 1 nm nodes at 360, 361, …, 830 nm with Δλ = 1 nm (the CIE 15 summation convention). */\nconst DEFAULT_LOCUS_NODES: readonly ColorimetricQuadratureNode[] = Object.freeze(\n Array.from({ length: 471 }, (_, i) => Object.freeze({ wavelengthNanometers: 360 + i, wavelengthMeasureNanometers: 1 }))\n);\nlet defaultLocus: PreparedLocus | undefined, defaultPortableLocus: PreparedLocus | undefined;\n\n/** The default locus, prepared on first use and shared (its scan cache is computed once); `portable` for the display view. */\nfunction analyticCmfLocus(portable?: PortableOps): PreparedLocus {\n if (portable) {\n defaultPortableLocus ??= preparePlanckIntegrationLocus({ kind: \"analytic-cie-1931-multi-lobe-fit\" }, DEFAULT_LOCUS_NODES, \"planck-analytic-cmf\", portable);\n return defaultPortableLocus;\n }\n defaultLocus ??= preparePlanckIntegrationLocus({ kind: \"analytic-cie-1931-multi-lobe-fit\" }, DEFAULT_LOCUS_NODES, \"planck-analytic-cmf\");\n return defaultLocus;\n}\n\n/**\n * Planckian-locus chromaticity at T (1000–100000 K): Planck's law integrated against the engine's\n * analytic CIE 1931 CMFs with 1 nm nodes from 360 to 830 nm. An approximation, because the CMFs are\n * an analytic fit; with tabulated CMFs use {@link calculatePlanckianChromaticity}.\n */\nexport function estimatePlanckianLocusChromaticity(input: { temperatureKelvin: number }): CalculationResult<CieXyChromaticity> {\n requireInputKeys(input, \"estimatePlanckianLocusChromaticity\", [\"temperatureKelvin\"]);\n const t = requireTemperatureIn(input.temperatureKelvin, PLANCKIAN_LOCUS_RANGE_KELVIN, \"temperatureKelvin\");\n return approximationResult(analyticCmfLocus().chromaticity(t), \"planckian-locus-planck-analytic-cmf\", COLORIMETRY_MODEL_VERSION,\n [\"Planck's law integrated against the engine's analytic CIE 1931 CMF fit, 1 nm nodes 360–830 nm; valid 1000–100000 K\"]);\n}\n\nfunction prepareLocus(model: PlanckianLocusModel, portable?: PortableOps): PreparedLocus {\n const record = requireRecordValue(model, \"locus\");\n if (record.kind === \"planck-analytic-cmf\") {\n rejectUnknownFields(record, [\"kind\"], \"locus\");\n return analyticCmfLocus(portable);\n }\n if (record.kind !== \"planck-integration\") throw new InvalidConfigurationError(\"locus.kind is not supported.\");\n rejectUnknownFields(record, [\"kind\", \"colorMatchingFunctions\", \"quadratureNodes\"], \"locus\");\n const m = model as Extract<PlanckianLocusModel, { kind: \"planck-integration\" }>;\n const cmf = prepareColorMatchingFunctions(m.colorMatchingFunctions);\n return preparePlanckIntegrationLocus(m.colorMatchingFunctions, m.quadratureNodes, \"planck-integration-\" + cmf.sourceKind, portable);\n}\n\n/** Planckian chromaticity at T using either locus model. */\nexport function calculatePlanckianChromaticity(input: { temperatureKelvin: number; locus: PlanckianLocusModel }): CalculationResult<CieXyChromaticity> {\n requireInputKeys(input, \"calculatePlanckianChromaticity\", [\"temperatureKelvin\", \"locus\"]);\n const locus = prepareLocus(input.locus);\n const t = requireTemperatureIn(input.temperatureKelvin, locus.range, \"temperatureKelvin\");\n const value = locus.chromaticity(t);\n const assumptions = [\"Planckian locus model: \" + locus.model];\n return locus.approximation\n ? approximationResult(value, \"planckian-chromaticity\", COLORIMETRY_MODEL_VERSION, assumptions)\n : calculatedResult(value, \"planckian-chromaticity\", COLORIMETRY_MODEL_VERSION, assumptions);\n}\n\n/**\n * McCamy (1992) cubic estimate of CCT from CIE 1931 xy:\n * n = (x − 0.3320)/(0.1858 − y), CCT = 449 n³ + 3525 n² + 6823.3 n + 5520.33.\n * An approximation that carries no Duv. Measured for this engine (October 17, 2026) on points of a\n * Planckian locus integrated from the CIE 1931 2° 1 nm table (360–830 nm, exact c₂): it deviates by up\n * to 10 K over 2856–6504 K (largest near 3500 K), 19 K at 2000 K, 17 K at 8000 K, 118 K at 10000 K\n * and 442 K at 12500 K. See docs/COLORIMETRY.md.\n */\nexport function estimateCorrelatedColorTemperatureMcCamy(input: { chromaticity: CieXyChromaticity }): CalculationResult<{ correlatedColorTemperatureKelvin: number }> {\n requireInputKeys(input, \"estimateCorrelatedColorTemperatureMcCamy\", [\"chromaticity\"]);\n const xy = parseChromaticity(input.chromaticity, \"chromaticity\");\n const n = (xy.x - 0.3320) / (0.1858 - xy.y);\n const cct = 449 * n ** 3 + 3525 * n ** 2 + 6823.3 * n + 5520.33;\n if (!Number.isFinite(cct) || cct <= 0) throw new InvalidScientificInputError(\"Chromaticity is outside the McCamy approximation domain.\");\n return approximationResult({ correlatedColorTemperatureKelvin: cct }, \"cct-mccamy-1992\", COLORIMETRY_MODEL_VERSION,\n [\"McCamy (1992) cubic; intended for chromaticities near the Planckian locus, about 2000–12500 K\"]);\n}\n\n/** The \"above-locus\" unit normal at a sample: the increasing-T unit tangent (−du/dx, −dv/dx) turned by −90°. */\nfunction locusNormal(s: LocusSample, portable?: PortableOps): Cie1960UvChromaticity {\n const len = portable ? portable.hypot(s.du, s.dv) : Math.hypot(s.du, s.dv);\n const tangent = { u: -s.du / len, v: -s.dv / len };\n // u decreases as T rises, so turning the tangent by −90° points toward larger v (above the locus).\n return { u: tangent.v, v: -tangent.u };\n}\n\n/**\n * Nearest locus point to `target` in CIE 1960 uv: the root of the derivative of the squared distance\n * F = (u − u₀)² + (v − v₀)². Roots of dF/dT and of dF/dx (x = 1/T) coincide, since dx/dT = −1/T² ≠ 0;\n * the root-find runs in x with g = ½ dF/dx = (u − u₀)u′ + (v − v₀)v′ and\n * g′ = u′² + v′² + (u − u₀)u″ + (v − v₀)v″ (analytic, see {@link preparePlanckIntegrationLocus}).\n * The cached coarse scan brackets the minimum; a Newton step falls back to bisection whenever it would\n * leave the bracket. Returns the root and the locus sample there, or undefined when the nearest point\n * lies beyond the model's range (by more than the tolerance).\n */\nfunction nearestLocusPoint(locus: PreparedLocus, target: Cie1960UvChromaticity): { x: number; sample: LocusSample } | undefined {\n const steps = CCT_SCAN_STEPS;\n const lo = 1e6 / locus.range.maximum, hi = 1e6 / locus.range.minimum, step = (hi - lo) / steps;\n const gridX = (i: number): number => (lo + step * i) * 1e-6;\n const scan = locus.scan();\n const f = (i: number): number => (scan[i]!.u - target.u) ** 2 + (scan[i]!.v - target.v) ** 2;\n let best = 0, bestValue = Infinity;\n for (let i = 0; i <= steps; i++) {\n const value = f(i);\n if (value < bestValue) { bestValue = value; best = i; }\n }\n const tolerance = (x: number): number => CCT_RELATIVE_TOLERANCE * x;\n const derivatives = (s: LocusSample): { g: number; gp: number } => {\n const eu = s.u - target.u, ev = s.v - target.v;\n return { g: eu * s.du + ev * s.dv, gp: s.du * s.du + s.dv * s.dv + eu * s.d2u + ev * s.d2v };\n };\n let a: number, b: number, x: number;\n if (best === 0 || best === steps) {\n // The scan minimum is a range limit: the nearest point is inside only if F still falls inward.\n x = gridX(best);\n const s = locus.sample(x), { g, gp } = derivatives(s);\n if (best === 0 ? g >= 0 : g <= 0) {\n // At the limit within the tolerance (one Newton step estimates the distance), or beyond it.\n return g === 0 || (gp > 0 && Math.abs(g / gp) <= tolerance(x)) ? { x, sample: s } : undefined;\n }\n a = best === 0 ? x : gridX(steps - 1);\n b = best === 0 ? gridX(1) : x;\n } else {\n // A discrete minimum between its neighbours brackets a local minimum; start at the parabola vertex.\n a = gridX(best - 1); b = gridX(best + 1);\n const fm = f(best - 1), fp = f(best + 1), curvature = fm - 2 * bestValue + fp;\n x = gridX(best) + (curvature > 0 ? (step * 1e-6 * (fm - fp)) / (2 * curvature) : 0);\n }\n let s = locus.sample(x);\n for (let i = 0; i < 100; i++) {\n const { g, gp } = derivatives(s);\n if (g === 0) break;\n if (g < 0) a = x; else b = x;\n let next = x - g / gp;\n if (!(gp > 0) || !(next > a && next < b)) next = 0.5 * (a + b);\n const converged = Math.abs(next - x) <= tolerance(next) || b - a <= tolerance(next);\n x = next;\n s = locus.sample(x);\n if (converged) break;\n }\n return { x, sample: s };\n}\n\nexport interface CorrelatedColorTemperatureResult {\n correlatedColorTemperatureKelvin: number;\n /** Signed CIE 1960 uv distance from the locus; positive above (greenish), negative below (the engine's convention). */\n duv: number;\n}\n\n/**\n * CCT and Duv of a chromaticity as the nearest point of the Planckian locus in the CIE\n * 1960 UCS (Smet et al. 2023; also CIE 15:2018). Finds the root of the analytic temperature derivative\n * of the squared uv distance (safeguarded Newton with bisection fallback, bracketed by a cached coarse\n * scan in reciprocal temperature), converged until a step changes T by at most 1e-10 of T. Throws\n * (fails closed) when the nearest point lies beyond the model's range, or when |Duv| > 0.05 (see\n * {@link MAX_CORRELATED_COLOR_TEMPERATURE_DUV}). Duv is positive above the locus (toward green) and\n * negative below it, the engine's documented sign convention.\n */\nexport function calculateCorrelatedColorTemperature(input: { chromaticity: CieXyChromaticity; locus: PlanckianLocusModel }): CalculationResult<CorrelatedColorTemperatureResult> {\n requireInputKeys(input, \"calculateCorrelatedColorTemperature\", [\"chromaticity\", \"locus\"]);\n const xy = parseChromaticity(input.chromaticity, \"chromaticity\");\n const locus = prepareLocus(input.locus);\n const target = uvOfChromaticity(xy);\n const nearest = nearestLocusPoint(locus, target);\n if (nearest === undefined) throw new InvalidScientificInputError(\"Chromaticity's nearest Planckian point lies outside the locus model range.\");\n const t = Math.min(locus.range.maximum, Math.max(locus.range.minimum, 1 / nearest.x));\n const point = nearest.sample, normal = locusNormal(point);\n const duv = (target.u - point.u) * normal.u + (target.v - point.v) * normal.v;\n if (Math.abs(duv) > MAX_CORRELATED_COLOR_TEMPERATURE_DUV) {\n throw new InvalidScientificInputError(\"|Duv| exceeds 0.05; no CCT is reported this far from the Planckian locus (the range evaluated by Smet et al. 2023).\");\n }\n const value = { correlatedColorTemperatureKelvin: t, duv };\n const assumptions = [\"Nearest point on the Planckian locus in CIE 1960 uv; locus model: \" + locus.model, \"Duv sign: positive above the locus (toward green), negative below (engine convention)\"];\n return locus.approximation\n ? approximationResult(value, \"cct-duv-locus-search\", COLORIMETRY_MODEL_VERSION, assumptions)\n : calculatedResult(value, \"cct-duv-locus-search\", COLORIMETRY_MODEL_VERSION, assumptions);\n}\n\n/** Locus whose temperature defines a white for CCT + Duv. */\nexport type WhiteLocus = \"planckian\" | \"cie-daylight\";\n\n/**\n * Chromaticity for CCT and Duv: the locus point (Planckian or CIE daylight) offset by\n * Duv along the unit normal of the Planckian locus at the same temperature in the CIE\n * 1960 UCS. For `cie-daylight`, Duv = 0 returns the daylight-locus point itself.\n * Throws InvalidScientificInputError when the offset point is not a physical chromaticity\n * (x ≥ 0, y > 0, x + y ≤ 1), for example 2000 K with Duv +0.02 (x + y ≈ 1.06).\n */\nexport function calculateChromaticityFromCorrelatedColorTemperature(input: {\n correlatedColorTemperatureKelvin: number;\n duv: number;\n whiteLocus: WhiteLocus;\n locus: PlanckianLocusModel;\n}): CalculationResult<CieXyChromaticity> {\n return chromaticityFromCorrelatedColorTemperature(input, undefined);\n}\n/**\n * Internal (not exported from the package): {@link calculateChromaticityFromCorrelatedColorTemperature} with the locus's\n * λ⁻⁵, e^x − 1 and normal length and the daylight-locus powers from the portable functions (the display view's Kelvin\n * white balance), so the result has the same bits in every JavaScript engine.\n */\nexport function calculatePortableChromaticityFromCorrelatedColorTemperature(input: Parameters<typeof calculateChromaticityFromCorrelatedColorTemperature>[0]):\n CalculationResult<CieXyChromaticity> {\n return chromaticityFromCorrelatedColorTemperature(input, PORTABLE);\n}\nfunction chromaticityFromCorrelatedColorTemperature(input: Parameters<typeof calculateChromaticityFromCorrelatedColorTemperature>[0], portable: PortableOps | undefined):\n CalculationResult<CieXyChromaticity> {\n requireInputKeys(input, \"calculateChromaticityFromCorrelatedColorTemperature\", [\"correlatedColorTemperatureKelvin\", \"duv\", \"whiteLocus\", \"locus\"]);\n const locus = prepareLocus(input.locus, portable);\n const duv = requireFiniteNumber(input.duv, \"duv\");\n if (Math.abs(duv) > MAX_CORRELATED_COLOR_TEMPERATURE_DUV) throw new InvalidScientificInputError(\"|duv| must not exceed 0.05.\");\n if (input.whiteLocus !== \"planckian\" && input.whiteLocus !== \"cie-daylight\") throw new InvalidConfigurationError(\"whiteLocus is not supported.\");\n const range = input.whiteLocus === \"cie-daylight\"\n ? { minimum: Math.max(locus.range.minimum, CIE_DAYLIGHT_LOCUS_RANGE_KELVIN.minimum), maximum: Math.min(locus.range.maximum, CIE_DAYLIGHT_LOCUS_RANGE_KELVIN.maximum) }\n : locus.range;\n const t = requireTemperatureIn(input.correlatedColorTemperatureKelvin, range, \"correlatedColorTemperatureKelvin\");\n const normal = locusNormal(locus.sample(1 / t), portable);\n const base = uvOfChromaticity(input.whiteLocus === \"cie-daylight\" ? daylightChromaticity(t, portable) : locus.chromaticity(t));\n const value = chromaticityOfUv({ u: base.u + duv * normal.u, v: base.v + duv * normal.v });\n // The same domain parseChromaticity enforces on input, so every result can be passed back in.\n if (!(value.x >= 0) || !(value.y > 0) || !(value.x + value.y <= 1)) {\n throw new InvalidScientificInputError(\"correlatedColorTemperatureKelvin and duv give a chromaticity outside x >= 0, y > 0, x + y <= 1.\");\n }\n const assumptions = [\"White locus: \" + input.whiteLocus + \"; Duv offset along the Planckian-locus normal (\" + locus.model + \") in CIE 1960 uv\"];\n const exactDaylightPoint = input.whiteLocus === \"cie-daylight\" && duv === 0;\n return locus.approximation && !exactDaylightPoint\n ? approximationResult(value, \"chromaticity-from-cct-duv\", COLORIMETRY_MODEL_VERSION, assumptions)\n : calculatedResult(value, \"chromaticity-from-cct-duv\", COLORIMETRY_MODEL_VERSION, assumptions);\n}\n"]}
|
|
@@ -1,3 +1,9 @@
|
|
|
1
1
|
/** Internal sRGB transfer function for one component. Shared by SDR rendering and the staged Print filter. */
|
|
2
2
|
export declare function encodeSrgbComponent(sample: number): number;
|
|
3
|
+
/**
|
|
4
|
+
* The same sRGB transfer function with the engine's portable power function (`portablePow`, within 1 ulp of the
|
|
5
|
+
* exact power) instead of `**`, so the result has the same bits in every JavaScript engine. Used by the display
|
|
6
|
+
* view; captures keep `encodeSrgbComponent`.
|
|
7
|
+
*/
|
|
8
|
+
export declare function encodeSrgbComponentPortable(sample: number): number;
|
|
3
9
|
export declare function decodeSrgbComponent(sample: number): number;
|
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
import { portablePow } from "../core/portable-math.js";
|
|
2
3
|
/** Internal sRGB transfer function for one component. Shared by SDR rendering and the staged Print filter. */
|
|
3
4
|
export function encodeSrgbComponent(sample) {
|
|
4
5
|
return sample === 1 ? 1 : sample <= .0031308 ? sample * 12.92 : 1.055 * sample ** (1 / 2.4) - .055;
|
|
5
6
|
}
|
|
7
|
+
/**
|
|
8
|
+
* The same sRGB transfer function with the engine's portable power function (`portablePow`, within 1 ulp of the
|
|
9
|
+
* exact power) instead of `**`, so the result has the same bits in every JavaScript engine. Used by the display
|
|
10
|
+
* view; captures keep `encodeSrgbComponent`.
|
|
11
|
+
*/
|
|
12
|
+
export function encodeSrgbComponentPortable(sample) {
|
|
13
|
+
return sample === 1 ? 1 : sample <= .0031308 ? sample * 12.92 : 1.055 * portablePow(sample, 1 / 2.4) - .055;
|
|
14
|
+
}
|
|
6
15
|
export function decodeSrgbComponent(sample) {
|
|
7
16
|
return sample === 1 ? 1 : sample <= .04045 ? sample / 12.92 : ((sample + .055) / 1.055) ** 2.4;
|
|
8
17
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"srgb-transfer.js","sourceRoot":"","sources":["../../src/color/srgb-transfer.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,8GAA8G;AAC9G,MAAM,UAAU,mBAAmB,CAAC,MAAa;IAC/C,OAAO,MAAM,KAAG,CAAC,CAAA,CAAC,CAAA,CAAC,CAAA,CAAC,CAAA,MAAM,IAAE,QAAQ,CAAA,CAAC,CAAA,MAAM,GAAC,KAAK,CAAA,CAAC,CAAA,KAAK,GAAC,MAAM,IAAE,CAAC,CAAC,GAAC,GAAG,CAAC,GAAC,IAAI,CAAC;AAC/E,CAAC;AACD,MAAM,UAAU,mBAAmB,CAAC,MAAa;IAC/C,OAAO,MAAM,KAAG,CAAC,CAAA,CAAC,CAAA,CAAC,CAAA,CAAC,CAAA,MAAM,IAAE,MAAM,CAAA,CAAC,CAAA,MAAM,GAAC,KAAK,CAAA,CAAC,CAAA,CAAC,CAAC,MAAM,GAAC,IAAI,CAAC,GAAC,KAAK,CAAC,IAAE,GAAG,CAAC;AAC7E,CAAC","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n/** Internal sRGB transfer function for one component. Shared by SDR rendering and the staged Print filter. */\nexport function encodeSrgbComponent(sample:number):number {\n return sample===1?1:sample<=.0031308?sample*12.92:1.055*sample**(1/2.4)-.055;\n}\nexport function decodeSrgbComponent(sample:number):number {\n return sample===1?1:sample<=.04045?sample/12.92:((sample+.055)/1.055)**2.4;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"srgb-transfer.js","sourceRoot":"","sources":["../../src/color/srgb-transfer.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,8GAA8G;AAC9G,MAAM,UAAU,mBAAmB,CAAC,MAAa;IAC/C,OAAO,MAAM,KAAG,CAAC,CAAA,CAAC,CAAA,CAAC,CAAA,CAAC,CAAA,MAAM,IAAE,QAAQ,CAAA,CAAC,CAAA,MAAM,GAAC,KAAK,CAAA,CAAC,CAAA,KAAK,GAAC,MAAM,IAAE,CAAC,CAAC,GAAC,GAAG,CAAC,GAAC,IAAI,CAAC;AAC/E,CAAC;AACD;;;;GAIG;AACH,MAAM,UAAU,2BAA2B,CAAC,MAAa;IACvD,OAAO,MAAM,KAAG,CAAC,CAAA,CAAC,CAAA,CAAC,CAAA,CAAC,CAAA,MAAM,IAAE,QAAQ,CAAA,CAAC,CAAA,MAAM,GAAC,KAAK,CAAA,CAAC,CAAA,KAAK,GAAC,WAAW,CAAC,MAAM,EAAC,CAAC,GAAC,GAAG,CAAC,GAAC,IAAI,CAAC;AACzF,CAAC;AACD,MAAM,UAAU,mBAAmB,CAAC,MAAa;IAC/C,OAAO,MAAM,KAAG,CAAC,CAAA,CAAC,CAAA,CAAC,CAAA,CAAC,CAAA,MAAM,IAAE,MAAM,CAAA,CAAC,CAAA,MAAM,GAAC,KAAK,CAAA,CAAC,CAAA,CAAC,CAAC,MAAM,GAAC,IAAI,CAAC,GAAC,KAAK,CAAC,IAAE,GAAG,CAAC;AAC7E,CAAC","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\nimport { portablePow } from \"../core/portable-math.js\";\n/** Internal sRGB transfer function for one component. Shared by SDR rendering and the staged Print filter. */\nexport function encodeSrgbComponent(sample:number):number {\n return sample===1?1:sample<=.0031308?sample*12.92:1.055*sample**(1/2.4)-.055;\n}\n/**\n * The same sRGB transfer function with the engine's portable power function (`portablePow`, within 1 ulp of the\n * exact power) instead of `**`, so the result has the same bits in every JavaScript engine. Used by the display\n * view; captures keep `encodeSrgbComponent`.\n */\nexport function encodeSrgbComponentPortable(sample:number):number {\n return sample===1?1:sample<=.0031308?sample*12.92:1.055*portablePow(sample,1/2.4)-.055;\n}\nexport function decodeSrgbComponent(sample:number):number {\n return sample===1?1:sample<=.04045?sample/12.92:((sample+.055)/1.055)**2.4;\n}\n"]}
|
|
@@ -190,6 +190,12 @@ export declare function createColorTemperatureWhiteBalanceIntent(input: CreateCo
|
|
|
190
190
|
* the locus model.
|
|
191
191
|
*/
|
|
192
192
|
export declare function resolveColorTemperatureWhiteBalance(input: ResolveColorTemperatureWhiteBalanceInput): ResolvedWhiteBalanceState;
|
|
193
|
+
/**
|
|
194
|
+
* Internal (not exported from the package): {@link resolveColorTemperatureWhiteBalance} with the white point from
|
|
195
|
+
* `calculatePortableChromaticityFromCorrelatedColorTemperature`, so the gains have the same bits in every JavaScript
|
|
196
|
+
* engine. The display view uses it for a Kelvin white balance; captures keep {@link resolveColorTemperatureWhiteBalance}.
|
|
197
|
+
*/
|
|
198
|
+
export declare function resolvePortableColorTemperatureWhiteBalance(input: ResolveColorTemperatureWhiteBalanceInput): ResolvedWhiteBalanceState;
|
|
193
199
|
/**
|
|
194
200
|
* Resolves a user-declared neutral/gray custom-WB measurement.
|
|
195
201
|
*
|