@photivra/engine 2.12.0 → 2.14.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 +144 -1
- package/CITATION.cff +1 -1
- package/README.md +7 -7
- package/SECURITY.md +1 -1
- package/THIRD_PARTY.md +3 -0
- package/dist/camera/lens-projection.js +2 -1
- package/dist/camera/lens-projection.js.map +1 -1
- package/dist/capture/dng-import.js +1 -1
- package/dist/capture/dng-import.js.map +1 -1
- package/dist/capture/export-jpeg.d.ts +1 -1
- package/dist/capture/export-jpeg.js +6 -4
- package/dist/capture/export-jpeg.js.map +1 -1
- package/dist/capture/host-yield-cadence.js +1 -1
- package/dist/capture/host-yield-cadence.js.map +1 -1
- package/dist/capture/native-environment-optical-stage.js +1 -1
- package/dist/capture/native-environment-optical-stage.js.map +1 -1
- package/dist/capture/native-raw.js +1 -1
- package/dist/capture/native-raw.js.map +1 -1
- package/dist/capture/packed-sensor-raw.js +3 -3
- package/dist/capture/packed-sensor-raw.js.map +1 -1
- package/dist/capture/sensor-raw-producer.js +1 -1
- package/dist/capture/sensor-raw-producer.js.map +1 -1
- package/dist/color/capture-color.js +3 -3
- package/dist/color/capture-color.js.map +1 -1
- package/dist/color/chromatic-adaptation.js +2 -2
- package/dist/color/chromatic-adaptation.js.map +1 -1
- package/dist/color/color-matching-functions.js +1 -1
- package/dist/color/color-matching-functions.js.map +1 -1
- package/dist/color/colorimetry-math.js +2 -2
- package/dist/color/colorimetry-math.js.map +1 -1
- package/dist/color/illuminants.js +3 -3
- package/dist/color/illuminants.js.map +1 -1
- package/dist/color/rgb-color-spaces.js +1 -1
- package/dist/color/rgb-color-spaces.js.map +1 -1
- package/dist/color/sensor-color-development.js +1 -1
- package/dist/color/sensor-color-development.js.map +1 -1
- package/dist/core/record-validation.js +3 -3
- package/dist/core/record-validation.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/equipment/generic-prime-lenses.js +3 -3
- package/dist/equipment/generic-prime-lenses.js.map +1 -1
- package/dist/equipment/generic-tier-assets.d.ts +23 -1
- package/dist/equipment/generic-tier-assets.js +58 -30
- package/dist/equipment/generic-tier-assets.js.map +1 -1
- package/dist/equipment/generic-tier-presets.d.ts +6 -3
- package/dist/equipment/generic-tier-presets.js +26 -13
- package/dist/equipment/generic-tier-presets.js.map +1 -1
- package/dist/exposure/auto-iso-slowest-shutter.d.ts +120 -0
- package/dist/exposure/auto-iso-slowest-shutter.js +217 -0
- package/dist/exposure/auto-iso-slowest-shutter.js.map +1 -0
- package/dist/exposure/exposure-mode-resolver.d.ts +57 -1
- package/dist/exposure/exposure-mode-resolver.js +238 -33
- package/dist/exposure/exposure-mode-resolver.js.map +1 -1
- package/dist/exposure/flash-ttl.js +1 -1
- package/dist/exposure/flash-ttl.js.map +1 -1
- package/dist/exposure/flash.js +1 -1
- package/dist/exposure/flash.js.map +1 -1
- package/dist/facade/chart-material-layers.js +2 -2
- package/dist/facade/chart-material-layers.js.map +1 -1
- package/dist/facade/chart-radiance-batch.js +1 -1
- package/dist/facade/chart-radiance-batch.js.map +1 -1
- package/dist/facade/chart-scenes.d.ts +45 -8
- package/dist/facade/chart-scenes.js +101 -44
- package/dist/facade/chart-scenes.js.map +1 -1
- package/dist/facade/color-patches.d.ts +129 -0
- package/dist/facade/color-patches.js +209 -0
- package/dist/facade/color-patches.js.map +1 -0
- package/dist/facade/diffraction-provider.js +12 -13
- package/dist/facade/diffraction-provider.js.map +1 -1
- package/dist/facade/display-view-gpu.js +1 -1
- package/dist/facade/display-view-gpu.js.map +1 -1
- package/dist/facade/display-view.d.ts +27 -0
- package/dist/facade/display-view.js +234 -65
- package/dist/facade/display-view.js.map +1 -1
- package/dist/facade/scene-radiance.js +2 -2
- package/dist/facade/scene-radiance.js.map +1 -1
- package/dist/facade/simulate-capture.d.ts +139 -12
- package/dist/facade/simulate-capture.js +209 -36
- package/dist/facade/simulate-capture.js.map +1 -1
- package/dist/facade/simulate-display-view.js +2 -2
- package/dist/facade/simulate-display-view.js.map +1 -1
- package/dist/gpu/chart-composite-gpu.js +2 -2
- package/dist/gpu/chart-composite-gpu.js.map +1 -1
- package/dist/gpu/display-view-composite-material-module.js +1 -1
- package/dist/gpu/display-view-composite-material-module.js.map +1 -1
- package/dist/gpu/display-view-quality-gpu.d.ts +2 -1
- package/dist/gpu/display-view-quality-gpu.js +24 -14
- package/dist/gpu/display-view-quality-gpu.js.map +1 -1
- package/dist/gpu/image-kernel-execution.js +2 -2
- package/dist/gpu/image-kernel-execution.js.map +1 -1
- package/dist/gpu/temporal-sum-gpu.js +23 -23
- package/dist/gpu/webgpu-runtime.js +1 -1
- package/dist/gpu/webgpu-runtime.js.map +1 -1
- package/dist/gpu/wgsl-chart-composite-kernel.js +318 -318
- package/dist/gpu/wgsl-chart-composite-kernel.js.map +1 -1
- package/dist/gpu/wgsl-chart-kernel.js +117 -117
- package/dist/gpu/wgsl-chart-kernel.js.map +1 -1
- package/dist/gpu/wgsl-display-view-quality-kernel.d.ts +13 -1
- package/dist/gpu/wgsl-display-view-quality-kernel.js +522 -488
- package/dist/gpu/wgsl-display-view-quality-kernel.js.map +1 -1
- package/dist/gpu/wgsl-display-view-speed-kernel.js +248 -248
- package/dist/gpu/wgsl-display-view-speed-kernel.js.map +1 -1
- package/dist/gpu/wgsl-kernels.js +203 -203
- package/dist/gpu/wgsl-kernels.js.map +1 -1
- package/dist/gpu/wgsl-sensor-kernel.js +302 -302
- package/dist/gpu/wgsl-sensor-kernel.js.map +1 -1
- package/dist/index.d.ts +8 -5
- package/dist/index.js +6 -3
- package/dist/index.js.map +1 -1
- package/dist/motion/temporal-exposure-sum.js +1 -1
- package/dist/motion/temporal-exposure-sum.js.map +1 -1
- package/dist/optics/diffraction-psf-profile.js +4 -4
- package/dist/optics/diffraction-psf-profile.js.map +1 -1
- package/dist/optics/front-of-lens-filter.js +1 -1
- package/dist/optics/front-of-lens-filter.js.map +1 -1
- package/dist/optics/irradiance-convolution.d.ts +10 -0
- package/dist/optics/irradiance-convolution.js +49 -14
- package/dist/optics/irradiance-convolution.js.map +1 -1
- package/dist/output/native-capture-sdr.js +1 -1
- package/dist/output/native-capture-sdr.js.map +1 -1
- package/dist/output/png-encoder.js +2 -2
- package/dist/output/png-encoder.js.map +1 -1
- package/dist/output/print-jpeg.js +13 -10
- package/dist/output/print-jpeg.js.map +1 -1
- package/dist/output/text-metadata.d.ts +22 -2
- package/dist/output/text-metadata.js +58 -9
- package/dist/output/text-metadata.js.map +1 -1
- package/dist/sensor/fixed-pattern-noise.js +1 -1
- package/dist/sensor/fixed-pattern-noise.js.map +1 -1
- package/dist/sensor/readout-exposure-linkage.js +1 -1
- package/dist/sensor/readout-exposure-linkage.js.map +1 -1
- package/docs/API_REFERENCE.md +49 -1
- package/docs/QUICK_START.md +104 -9
- package/docs/USAGE.md +241 -2
- package/docs/api/core-version.md +1 -1
- package/docs/api/equipment-generic-tier-assets.md +41 -1
- package/docs/api/equipment-generic-tier-presets.md +11 -3
- package/docs/api/exports.json +172 -2
- package/docs/api/exposure-auto-iso-slowest-shutter.md +139 -0
- package/docs/api/exposure-exposure-mode-resolver.md +68 -0
- package/docs/api/facade-chart-scenes.md +36 -5
- package/docs/api/facade-color-patches.md +150 -0
- package/docs/api/facade-simulate-capture.md +140 -9
- package/docs/api/output-text-metadata.md +34 -0
- package/llms.txt +1 -0
- package/package.json +2 -2
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Procedural test charts for the capture facade, written for this engine. Every chart is a flat,
|
|
4
4
|
* Lambertian (matte) reflectance pattern, defined by formulas in normalized frame coordinates. No
|
|
5
|
-
* third-party chart data (for example ColorChecker reflectances) is bundled.
|
|
6
|
-
* spectra that the caller supplies.
|
|
5
|
+
* third-party chart data (for example ColorChecker reflectances) is bundled. Colored patches use
|
|
6
|
+
* spectra that the caller supplies or, from 2.14, Photivra's own analytic color patches
|
|
7
|
+
* (`color-patches.ts`, docs/COLOR_PATCHES.md).
|
|
7
8
|
*
|
|
8
9
|
* Frame coordinates: `u` runs from -1 (left edge of the framed chart) to +1 (right edge); `v` points up
|
|
9
10
|
* and has the same scale, so it runs from -aspect to +aspect where aspect = height/width of the frame.
|
|
@@ -23,35 +24,37 @@
|
|
|
23
24
|
* levels, like the grey ramp: the step (below 0.1 % of the range) is far below sensor noise.
|
|
24
25
|
* @see docs/QUICK_START.md for the chart catalogue and their analytic definitions.
|
|
25
26
|
*/
|
|
26
|
-
import { portablePow } from "../core/portable-math.js";
|
|
27
|
+
import { portableExp, portablePow } from "../core/portable-math.js";
|
|
27
28
|
import { InvalidConfigurationError } from "../core/configuration-error.js";
|
|
28
29
|
import { prepareColorimetricSpectrum } from "../color/color-matching-functions.js";
|
|
29
30
|
import { requireInputObject, requireInputKeys } from "../core/record-validation.js";
|
|
30
31
|
import { createDeterministicUniformStream } from "../sensor/stochastic-seed.js";
|
|
31
|
-
|
|
32
|
+
import { COLOR_PATCH_IDS, COLOR_PATCH_SET_ID, COLOR_PATCH_SET_VERSION, findColorPatch, prepareColorPatchSpectrum } from "./color-patches.js";
|
|
33
|
+
export const CHART_SCENE_PRESETS = /* @__PURE__ */ Object.freeze(["uniform", "slanted-edge", "siemens-star",
|
|
32
34
|
"step-wedge", "grey-ramp", "checkerboard", "point-grid", "patches", "dead-leaves", "bar-groups", "hyperbolic-wedge", "zone-plate",
|
|
33
|
-
"slanted-squares", "line-grid", "fiducial", "composite"]);
|
|
35
|
+
"slanted-squares", "line-grid", "fiducial", "color-patches", "composite"]);
|
|
34
36
|
/** Neutral levels that represent a continuous pattern (grey ramp; 2.3 sine star, zone plate and dead-leaves greys). */
|
|
35
37
|
export const CHART_CONTINUOUS_LEVELS = 1024;
|
|
36
38
|
/** Engine-chosen chart parameters, each reported when the caller omitted it. */
|
|
37
|
-
export const CHART_SCENE_DEFAULTS = Object.freeze({
|
|
38
|
-
uniform: Object.freeze({ reflectance: 0.18 }),
|
|
39
|
-
"slanted-edge": Object.freeze({ angleDegrees: 5, darkReflectance: 0.15, lightReflectance: 0.6 }),
|
|
40
|
-
"siemens-star": Object.freeze({ cycles: 36, radiusFraction: 0.9, darkReflectance: 0.05, lightReflectance: 0.8, backgroundReflectance: 0.18 }),
|
|
41
|
-
"step-wedge": Object.freeze({ steps: 11, minimumReflectance: 0.03, maximumReflectance: 0.9, spacing: "equal-density" }),
|
|
42
|
-
"grey-ramp": Object.freeze({ minimumReflectance: 0.03, maximumReflectance: 0.9 }),
|
|
43
|
-
checkerboard: Object.freeze({ squaresAcross: 8, darkReflectance: 0.05, lightReflectance: 0.8 }),
|
|
44
|
-
"point-grid": Object.freeze({ columns: 5, rows: 3, radius: 0.01, pointReflectance: 0.9, backgroundReflectance: 0.02 }),
|
|
45
|
-
patches: Object.freeze({ gapFraction: 0.1, backgroundReflectance: 0.18 }),
|
|
46
|
-
"dead-leaves": Object.freeze({ radiusMinimum: 0.01, radiusMaximum: 0.5, minimumReflectance: 0.2, maximumReflectance: 0.6, seed: 1 }),
|
|
47
|
-
"bar-groups": Object.freeze({ lineWidthsPerPictureHeight: Object.freeze([100, 200, 400, 800]), numerals: true, darkReflectance: 0.05, lightReflectance: 0.8 }),
|
|
48
|
-
"hyperbolic-wedge": Object.freeze({ orientation: "horizontal", lineCount: 5, minimumLineWidthsPerPictureHeight: 100,
|
|
39
|
+
export const CHART_SCENE_DEFAULTS = /* @__PURE__ */ Object.freeze({
|
|
40
|
+
uniform: /* @__PURE__ */ Object.freeze({ reflectance: 0.18 }),
|
|
41
|
+
"slanted-edge": /* @__PURE__ */ Object.freeze({ angleDegrees: 5, darkReflectance: 0.15, lightReflectance: 0.6 }),
|
|
42
|
+
"siemens-star": /* @__PURE__ */ Object.freeze({ cycles: 36, radiusFraction: 0.9, darkReflectance: 0.05, lightReflectance: 0.8, backgroundReflectance: 0.18 }),
|
|
43
|
+
"step-wedge": /* @__PURE__ */ Object.freeze({ steps: 11, minimumReflectance: 0.03, maximumReflectance: 0.9, spacing: "equal-density" }),
|
|
44
|
+
"grey-ramp": /* @__PURE__ */ Object.freeze({ minimumReflectance: 0.03, maximumReflectance: 0.9 }),
|
|
45
|
+
checkerboard: /* @__PURE__ */ Object.freeze({ squaresAcross: 8, darkReflectance: 0.05, lightReflectance: 0.8 }),
|
|
46
|
+
"point-grid": /* @__PURE__ */ Object.freeze({ columns: 5, rows: 3, radius: 0.01, pointReflectance: 0.9, backgroundReflectance: 0.02 }),
|
|
47
|
+
patches: /* @__PURE__ */ Object.freeze({ gapFraction: 0.1, backgroundReflectance: 0.18 }),
|
|
48
|
+
"dead-leaves": /* @__PURE__ */ Object.freeze({ radiusMinimum: 0.01, radiusMaximum: 0.5, minimumReflectance: 0.2, maximumReflectance: 0.6, seed: 1 }),
|
|
49
|
+
"bar-groups": /* @__PURE__ */ Object.freeze({ lineWidthsPerPictureHeight: /* @__PURE__ */ Object.freeze([100, 200, 400, 800]), numerals: true, darkReflectance: 0.05, lightReflectance: 0.8 }),
|
|
50
|
+
"hyperbolic-wedge": /* @__PURE__ */ Object.freeze({ orientation: "horizontal", lineCount: 5, minimumLineWidthsPerPictureHeight: 100,
|
|
49
51
|
maximumLineWidthsPerPictureHeight: 1000, darkReflectance: 0.05, lightReflectance: 0.8 }),
|
|
50
|
-
"zone-plate": Object.freeze({ radiusFraction: 0.9, maximumCyclesPerPictureHeight: 200, darkReflectance: 0.1, lightReflectance: 0.7, backgroundReflectance: 0.18 }),
|
|
51
|
-
"slanted-squares": Object.freeze({ columns: 3, rows: 2, sizeFraction: 0.5, angleDegrees: 5, darkReflectance: 0.15, lightReflectance: 0.6 }),
|
|
52
|
-
"line-grid": Object.freeze({ spacing: 1 / 3, lineWidth: 0.01, lineReflectance: 0.42, backgroundReflectance: 0.6 }),
|
|
53
|
-
fiducial: Object.freeze({ radiusFraction: 0.8, ringFraction: 0.18, inverted: false, darkReflectance: 0.05, lightReflectance: 0.8, backgroundReflectance: 0.18 }),
|
|
54
|
-
|
|
52
|
+
"zone-plate": /* @__PURE__ */ Object.freeze({ radiusFraction: 0.9, maximumCyclesPerPictureHeight: 200, darkReflectance: 0.1, lightReflectance: 0.7, backgroundReflectance: 0.18 }),
|
|
53
|
+
"slanted-squares": /* @__PURE__ */ Object.freeze({ columns: 3, rows: 2, sizeFraction: 0.5, angleDegrees: 5, darkReflectance: 0.15, lightReflectance: 0.6 }),
|
|
54
|
+
"line-grid": /* @__PURE__ */ Object.freeze({ spacing: 1 / 3, lineWidth: 0.01, lineReflectance: 0.42, backgroundReflectance: 0.6 }),
|
|
55
|
+
fiducial: /* @__PURE__ */ Object.freeze({ radiusFraction: 0.8, ringFraction: 0.18, inverted: false, darkReflectance: 0.05, lightReflectance: 0.8, backgroundReflectance: 0.18 }),
|
|
56
|
+
"color-patches": /* @__PURE__ */ Object.freeze({ patchIds: COLOR_PATCH_IDS, gapFraction: 0.1, backgroundReflectance: 0.18 }),
|
|
57
|
+
composite: /* @__PURE__ */ Object.freeze({ background: /* @__PURE__ */ Object.freeze({ preset: "uniform", reflectance: 0.18 }) })
|
|
55
58
|
});
|
|
56
59
|
function reflectance(value, name) {
|
|
57
60
|
if (typeof value !== "number" || !Number.isFinite(value) || value < 0 || value > 1)
|
|
@@ -69,11 +72,23 @@ function finiteIn(value, name, minimum, maximum, open = false) {
|
|
|
69
72
|
return value;
|
|
70
73
|
}
|
|
71
74
|
/** Wavelength range every chart spectrum must cover (the facade's sensor quadrature range). */
|
|
72
|
-
export const CHART_SPECTRAL_RANGE_NANOMETERS = Object.freeze({ minimum: 380, maximum: 780 });
|
|
73
|
-
/**
|
|
74
|
-
|
|
75
|
+
export const CHART_SPECTRAL_RANGE_NANOMETERS = /* @__PURE__ */ Object.freeze({ minimum: 380, maximum: 780 });
|
|
76
|
+
/**
|
|
77
|
+
* Internal: validates a chart reflectance input (a neutral number, a reflectance spectrum covering 380–780 nm or a
|
|
78
|
+
* built-in color patch) as a material.
|
|
79
|
+
*/
|
|
80
|
+
export function resolveChartMaterial(value, name, exp) {
|
|
75
81
|
if (typeof value === "number")
|
|
76
82
|
return { kind: "neutral", reflectance: reflectance(value, name) };
|
|
83
|
+
if (typeof value === "object" && value !== null && value.kind === "color-patch") {
|
|
84
|
+
for (const key of Object.keys(value))
|
|
85
|
+
if (key !== "kind" && key !== "patchId")
|
|
86
|
+
throw new InvalidConfigurationError(name + " has unknown field \"" + key + "\".");
|
|
87
|
+
const patch = findColorPatch(value.patchId);
|
|
88
|
+
if (!patch)
|
|
89
|
+
throw new InvalidConfigurationError(name + ".patchId must be one of the built-in color patches (COLOR_PATCH_IDS).");
|
|
90
|
+
return { kind: "spectrum", spectrum: prepareColorPatchSpectrum(patch, exp) };
|
|
91
|
+
}
|
|
77
92
|
let spectrum;
|
|
78
93
|
try {
|
|
79
94
|
spectrum = prepareColorimetricSpectrum(value, name);
|
|
@@ -88,6 +103,26 @@ export function resolveChartMaterial(value, name) {
|
|
|
88
103
|
reflectance(spectrum.evaluate(nm), name + " values");
|
|
89
104
|
return { kind: "spectrum", spectrum };
|
|
90
105
|
}
|
|
106
|
+
/** How a uniform chart reports its reflectance: the number, the color-patch record, or `"caller-spectrum"`. */
|
|
107
|
+
function reflectanceReport(raw) {
|
|
108
|
+
if (typeof raw === "number")
|
|
109
|
+
return raw;
|
|
110
|
+
const record = raw;
|
|
111
|
+
return record.kind === "color-patch" ? { kind: "color-patch", patchId: record.patchId } : "caller-spectrum";
|
|
112
|
+
}
|
|
113
|
+
/** Material at a point of a row-major patch grid (`patches`, 2.14 `color-patches`): a patch index or `backgroundIndex`. */
|
|
114
|
+
function patchGridMaterialAt(columns, rows, count, gap, aspect, backgroundIndex) {
|
|
115
|
+
const cellW = 2 / columns, cellH = 2 * aspect / rows;
|
|
116
|
+
return (u, v) => {
|
|
117
|
+
const fx = (u + 1) / cellW, fy = (aspect - v) / cellH, i = Math.floor(fx), j = Math.floor(fy);
|
|
118
|
+
if (i < 0 || j < 0 || i >= columns || j >= rows)
|
|
119
|
+
return backgroundIndex;
|
|
120
|
+
const index = j * columns + i, x = fx - i, y = fy - j;
|
|
121
|
+
if (index >= count || x < gap / 2 || x > 1 - gap / 2 || y < gap / 2 || y > 1 - gap / 2)
|
|
122
|
+
return backgroundIndex;
|
|
123
|
+
return index;
|
|
124
|
+
};
|
|
125
|
+
}
|
|
91
126
|
/** Raw caller spectrum inputs per material (undefined for neutral materials), for the facade's scene digest. */
|
|
92
127
|
const MATERIAL_INPUTS = new WeakMap();
|
|
93
128
|
/**
|
|
@@ -136,12 +171,13 @@ export function resolveChartScene(input) {
|
|
|
136
171
|
}
|
|
137
172
|
/**
|
|
138
173
|
* Internal (not exported from the package): {@link resolveChartScene} with the step wedge's geometric levels computed
|
|
139
|
-
* with the portable power (`portablePow`, within 1 ulp) instead of `**`,
|
|
140
|
-
*
|
|
174
|
+
* with the portable power (`portablePow`, within 1 ulp) instead of `**`, and (2.14) the built-in color patches with the
|
|
175
|
+
* portable exponential (`portableExp`) instead of `Math.exp`, whose last bits differ between JavaScript engines. Every
|
|
176
|
+
* other value, the geometry and the material order are the same. The display view uses its materials.
|
|
141
177
|
*/
|
|
142
178
|
export function resolvePortableChartScene(input) {
|
|
143
179
|
requireInputObject(input, "resolveChartScene input");
|
|
144
|
-
return resolveScene(input.scene, input.aspect, { pictureHeight: 2 * input.aspect, nested: false, pow: portablePow });
|
|
180
|
+
return resolveScene(input.scene, input.aspect, { pictureHeight: 2 * input.aspect, nested: false, pow: portablePow, exp: portableExp });
|
|
145
181
|
}
|
|
146
182
|
function resolveScene(scene, aspect, context) {
|
|
147
183
|
requireInputObject(scene, "chart scene");
|
|
@@ -152,7 +188,7 @@ function resolveScene(scene, aspect, context) {
|
|
|
152
188
|
throw new InvalidConfigurationError("A composite chart cannot contain another composite.");
|
|
153
189
|
const record = scene;
|
|
154
190
|
const defaults = CHART_SCENE_DEFAULTS[preset];
|
|
155
|
-
const extra = preset === "patches" ? ["patches", "columns"] : preset === "composite" ? ["elements"] : preset === "siemens-star" ? ["profile"] : [];
|
|
191
|
+
const extra = preset === "patches" ? ["patches", "columns"] : preset === "color-patches" ? ["columns"] : preset === "composite" ? ["elements"] : preset === "siemens-star" ? ["profile"] : [];
|
|
156
192
|
const allowed = ["preset", ...Object.keys(defaults), ...extra];
|
|
157
193
|
for (const key of Object.keys(record))
|
|
158
194
|
if (!allowed.includes(key))
|
|
@@ -183,8 +219,8 @@ function resolveScene(scene, aspect, context) {
|
|
|
183
219
|
};
|
|
184
220
|
switch (preset) {
|
|
185
221
|
case "uniform": {
|
|
186
|
-
const raw = get("reflectance"), m = resolveChartMaterial(raw, "reflectance");
|
|
187
|
-
return done({ reflectance:
|
|
222
|
+
const raw = get("reflectance"), m = resolveChartMaterial(raw, "reflectance", context.exp);
|
|
223
|
+
return done({ reflectance: reflectanceReport(raw) }, [m], () => 0, [typeof raw === "number" ? undefined : raw], { kind: "uniform" });
|
|
188
224
|
}
|
|
189
225
|
case "slanted-edge": {
|
|
190
226
|
const angle = finiteIn(get("angleDegrees"), "angleDegrees", -45, 45, true), dark = neutral("darkReflectance"), light = neutral("lightReflectance");
|
|
@@ -270,7 +306,7 @@ function resolveScene(scene, aspect, context) {
|
|
|
270
306
|
if (typeof p.patchId !== "string" || p.patchId.trim() === "" || ids.has(p.patchId))
|
|
271
307
|
throw new InvalidConfigurationError("Each patch needs a unique non-empty patchId.");
|
|
272
308
|
ids.add(p.patchId);
|
|
273
|
-
materials.push(resolveChartMaterial(p.reflectance, "patches[" + index + "].reflectance"));
|
|
309
|
+
materials.push(resolveChartMaterial(p.reflectance, "patches[" + index + "].reflectance", context.exp));
|
|
274
310
|
inputs.push(typeof p.reflectance === "number" ? undefined : p.reflectance);
|
|
275
311
|
}
|
|
276
312
|
const columnsRaw = record.columns === undefined ? Math.ceil(Math.sqrt(list.length)) : record.columns;
|
|
@@ -284,15 +320,35 @@ function resolveScene(scene, aspect, context) {
|
|
|
284
320
|
materials.push({ kind: "neutral", reflectance: background });
|
|
285
321
|
inputs.push(undefined);
|
|
286
322
|
const cellW = 2 / columns, cellH = 2 * aspect / rows;
|
|
287
|
-
return done({ patchIds: list.map((p) => p.patchId), columns, rows, gapFraction: gap, backgroundReflectance: background }, materials, (
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
323
|
+
return done({ patchIds: list.map((p) => p.patchId), columns, rows, gapFraction: gap, backgroundReflectance: background }, materials, patchGridMaterialAt(columns, rows, list.length, gap, aspect, backgroundIndex), inputs, { kind: "patches", columns, rows, count: list.length, gap, cellW, cellH, backgroundIndex });
|
|
324
|
+
}
|
|
325
|
+
case "color-patches": {
|
|
326
|
+
const ids = get("patchIds");
|
|
327
|
+
if (!Array.isArray(ids) || ids.length < 1 || ids.length > COLOR_PATCH_IDS.length)
|
|
328
|
+
throw new InvalidConfigurationError("patchIds must list 1 to " + COLOR_PATCH_IDS.length + " built-in color patch ids.");
|
|
329
|
+
const seen = new Set(), materials = [], inputs = [];
|
|
330
|
+
for (const [index, id] of ids.entries()) {
|
|
331
|
+
if (!findColorPatch(id))
|
|
332
|
+
throw new InvalidConfigurationError("patchIds[" + index + "] must be one of the built-in color patches (COLOR_PATCH_IDS).");
|
|
333
|
+
if (seen.has(id))
|
|
334
|
+
throw new InvalidConfigurationError("patchIds[" + index + "] repeats " + JSON.stringify(id) + ".");
|
|
335
|
+
seen.add(id);
|
|
336
|
+
const input = Object.freeze({ kind: "color-patch", patchId: id });
|
|
337
|
+
materials.push(resolveChartMaterial(input, "patchIds[" + index + "]", context.exp));
|
|
338
|
+
inputs.push(input);
|
|
339
|
+
}
|
|
340
|
+
const columnsRaw = record.columns === undefined ? Math.min(6, ids.length) : record.columns;
|
|
341
|
+
if (record.columns === undefined)
|
|
342
|
+
defaulted.push("columns");
|
|
343
|
+
const columns = integerIn(columnsRaw, "columns", 1, ids.length), rows = Math.ceil(ids.length / columns);
|
|
344
|
+
const gap = finiteIn(get("gapFraction"), "gapFraction", 0, 0.5), background = neutral("backgroundReflectance");
|
|
345
|
+
if (gap >= 0.5)
|
|
346
|
+
throw new InvalidConfigurationError("gapFraction must be below 0.5.");
|
|
347
|
+
const backgroundIndex = materials.length;
|
|
348
|
+
materials.push({ kind: "neutral", reflectance: background });
|
|
349
|
+
inputs.push(undefined);
|
|
350
|
+
const cellW = 2 / columns, cellH = 2 * aspect / rows;
|
|
351
|
+
return done({ patchSet: COLOR_PATCH_SET_ID, patchSetVersion: COLOR_PATCH_SET_VERSION, patchIds: [...ids], columns, rows, gapFraction: gap, backgroundReflectance: background }, materials, patchGridMaterialAt(columns, rows, ids.length, gap, aspect, backgroundIndex), inputs, { kind: "patches", columns, rows, count: ids.length, gap, cellW, cellH, backgroundIndex });
|
|
296
352
|
}
|
|
297
353
|
case "dead-leaves": {
|
|
298
354
|
const rMin = finiteIn(get("radiusMinimum"), "radiusMinimum", DEAD_LEAVES_MINIMUM_RADIUS, 2), rMax = finiteIn(get("radiusMaximum"), "radiusMaximum", 0, 2, true);
|
|
@@ -410,7 +466,8 @@ function resolveScene(scene, aspect, context) {
|
|
|
410
466
|
const list = record.elements;
|
|
411
467
|
if (!Array.isArray(list) || list.length < 1 || list.length > 256)
|
|
412
468
|
throw new InvalidConfigurationError("elements must list 1 to 256 chart elements.");
|
|
413
|
-
const
|
|
469
|
+
const portable = { ...(context.pow ? { pow: context.pow } : {}), ...(context.exp ? { exp: context.exp } : {}) };
|
|
470
|
+
const nested = { pictureHeight: context.pictureHeight, nested: true, ...portable };
|
|
414
471
|
const background = resolveScene(get("background"), aspect, nested);
|
|
415
472
|
const materials = [...background.materials], inputs = [...chartMaterialInputs(background)];
|
|
416
473
|
for (const key of background.defaultedParameters)
|
|
@@ -430,7 +487,7 @@ function resolveScene(scene, aspect, context) {
|
|
|
430
487
|
if (e.mask === undefined)
|
|
431
488
|
defaulted.push("elements." + index + ".placement.mask");
|
|
432
489
|
// The element's own frame: u = ±1 across its width; frequencies keep the composite's picture height.
|
|
433
|
-
const chart = resolveScene(e.scene, height / width, { pictureHeight: context.pictureHeight * 2 / width, nested: true, ...
|
|
490
|
+
const chart = resolveScene(e.scene, height / width, { pictureHeight: context.pictureHeight * 2 / width, nested: true, ...portable });
|
|
434
491
|
for (const key of chart.defaultedParameters)
|
|
435
492
|
defaulted.push("elements." + index + ".parameters." + key);
|
|
436
493
|
const offset = materials.length;
|
|
@@ -574,7 +631,7 @@ function barGroupLayout(list, numerals, aspect, pictureHeight) {
|
|
|
574
631
|
/** Engine-drawn seven-segment numerals: a 0.6 × 1 cell per digit (baseline at 0, y up), stroke 0.16, advance 0.82. */
|
|
575
632
|
export const NUMERAL_WIDTH = 0.6, NUMERAL_ADVANCE = 0.82;
|
|
576
633
|
/** Internal: segment rectangles [x0, y0, x1, y1] of the seven-segment numerals and the segments each digit lights. */
|
|
577
|
-
export const SEGMENTS = Object.freeze({
|
|
634
|
+
export const SEGMENTS = /* @__PURE__ */ Object.freeze({
|
|
578
635
|
a: [0.08, 0.84, 0.52, 1], b: [0.44, 0.5, 0.6, 1], c: [0.44, 0, 0.6, 0.5], d: [0.08, 0, 0.52, 0.16], e: [0, 0, 0.16, 0.5], f: [0, 0.5, 0.16, 1], g: [0.08, 0.42, 0.52, 0.58]
|
|
579
636
|
});
|
|
580
637
|
export const DIGIT_SEGMENTS = ["abcdef", "bc", "abged", "abgcd", "fgbc", "afgcd", "afgedc", "abc", "abcdefg", "abcdfg"];
|